- clean working copy for leaving SVN
This commit is contained in:
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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_Application.h"
|
||||
#include "jucer_AppearanceSettings.h"
|
||||
|
||||
namespace AppearanceColours
|
||||
{
|
||||
struct ColourInfo
|
||||
{
|
||||
const char* name;
|
||||
int colourID;
|
||||
bool mustBeOpaque;
|
||||
bool applyToEditorOnly;
|
||||
};
|
||||
|
||||
static const ColourInfo colours[] =
|
||||
{
|
||||
{ "Main Window Bkgd", mainBackgroundColourId, true, false },
|
||||
{ "Treeview Highlight", TreeView::selectedItemBackgroundColourId, false, false },
|
||||
|
||||
{ "Code Background", CodeEditorComponent::backgroundColourId, true, false },
|
||||
{ "Line Number Bkgd", CodeEditorComponent::lineNumberBackgroundId, false, false },
|
||||
{ "Line Numbers", CodeEditorComponent::lineNumberTextId, false, false },
|
||||
{ "Plain Text", CodeEditorComponent::defaultTextColourId, false, false },
|
||||
{ "Selected Text Bkgd", CodeEditorComponent::highlightColourId, false, false },
|
||||
{ "Caret", CaretComponent::caretColourId, false, true }
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
numColours = sizeof (AppearanceColours::colours) / sizeof (AppearanceColours::colours[0])
|
||||
};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
AppearanceSettings::AppearanceSettings (bool updateAppWhenChanged)
|
||||
: settings ("COLOUR_SCHEME")
|
||||
{
|
||||
if (! IntrojucerApp::getApp().isRunningCommandLine)
|
||||
{
|
||||
IntrojucerLookAndFeel lf;
|
||||
|
||||
for (int i = 0; i < AppearanceColours::numColours; ++i)
|
||||
getColourValue (AppearanceColours::colours[i].name) = lf.findColour (AppearanceColours::colours[i].colourID).toString();
|
||||
|
||||
CodeDocument doc;
|
||||
CPlusPlusCodeTokeniser tokeniser;
|
||||
CodeEditorComponent editor (doc, &tokeniser);
|
||||
|
||||
const CodeEditorComponent::ColourScheme cs (editor.getColourScheme());
|
||||
|
||||
for (int i = cs.types.size(); --i >= 0;)
|
||||
{
|
||||
CodeEditorComponent::ColourScheme::TokenType& t = cs.types.getReference(i);
|
||||
getColourValue (t.name) = t.colour.toString();
|
||||
}
|
||||
|
||||
getCodeFontValue() = getDefaultCodeFont().toString();
|
||||
|
||||
if (updateAppWhenChanged)
|
||||
settings.addListener (this);
|
||||
}
|
||||
}
|
||||
|
||||
File AppearanceSettings::getSchemesFolder()
|
||||
{
|
||||
File f (getGlobalProperties().getFile().getSiblingFile ("Schemes"));
|
||||
f.createDirectory();
|
||||
return f;
|
||||
}
|
||||
|
||||
void AppearanceSettings::writeDefaultSchemeFile (const String& xmlString, const String& name)
|
||||
{
|
||||
const File file (getSchemesFolder().getChildFile (name).withFileExtension (getSchemeFileSuffix()));
|
||||
|
||||
AppearanceSettings settings (false);
|
||||
|
||||
ScopedPointer<XmlElement> xml (XmlDocument::parse (xmlString));
|
||||
if (xml != nullptr)
|
||||
settings.readFromXML (*xml);
|
||||
|
||||
settings.writeToFile (file);
|
||||
}
|
||||
|
||||
void AppearanceSettings::refreshPresetSchemeList()
|
||||
{
|
||||
writeDefaultSchemeFile (BinaryData::colourscheme_dark_xml, "Default (Dark)");
|
||||
writeDefaultSchemeFile (BinaryData::colourscheme_light_xml, "Default (Light)");
|
||||
|
||||
Array<File> newSchemes;
|
||||
getSchemesFolder().findChildFiles (newSchemes, File::findFiles, false, String ("*") + getSchemeFileSuffix());
|
||||
|
||||
if (newSchemes != presetSchemeFiles)
|
||||
{
|
||||
presetSchemeFiles.swapWith (newSchemes);
|
||||
IntrojucerApp::getCommandManager().commandStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
StringArray AppearanceSettings::getPresetSchemes()
|
||||
{
|
||||
StringArray s;
|
||||
for (int i = 0; i < presetSchemeFiles.size(); ++i)
|
||||
s.add (presetSchemeFiles.getReference(i).getFileNameWithoutExtension());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void AppearanceSettings::selectPresetScheme (int index)
|
||||
{
|
||||
readFromFile (presetSchemeFiles [index]);
|
||||
}
|
||||
|
||||
bool AppearanceSettings::readFromXML (const XmlElement& xml)
|
||||
{
|
||||
if (xml.hasTagName (settings.getType().toString()))
|
||||
{
|
||||
const ValueTree newSettings (ValueTree::fromXml (xml));
|
||||
|
||||
// we'll manually copy across the new properties to the existing tree so that
|
||||
// any open editors will be kept up to date..
|
||||
settings.copyPropertiesFrom (newSettings, nullptr);
|
||||
|
||||
for (int i = settings.getNumChildren(); --i >= 0;)
|
||||
{
|
||||
ValueTree c (settings.getChild (i));
|
||||
|
||||
const ValueTree newValue (newSettings.getChildWithProperty (Ids::name, c.getProperty (Ids::name)));
|
||||
|
||||
if (newValue.isValid())
|
||||
c.copyPropertiesFrom (newValue, nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppearanceSettings::readFromFile (const File& file)
|
||||
{
|
||||
const ScopedPointer<XmlElement> xml (XmlDocument::parse (file));
|
||||
return xml != nullptr && readFromXML (*xml);
|
||||
}
|
||||
|
||||
bool AppearanceSettings::writeToFile (const File& file) const
|
||||
{
|
||||
const ScopedPointer<XmlElement> xml (settings.createXml());
|
||||
return xml != nullptr && xml->writeToFile (file, String::empty);
|
||||
}
|
||||
|
||||
Font AppearanceSettings::getDefaultCodeFont()
|
||||
{
|
||||
return Font (Font::getDefaultMonospacedFontName(), Font::getDefaultStyle(), 13.0f);
|
||||
}
|
||||
|
||||
StringArray AppearanceSettings::getColourNames() const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < settings.getNumChildren(); ++i)
|
||||
{
|
||||
const ValueTree c (settings.getChild(i));
|
||||
|
||||
if (c.hasType ("COLOUR"))
|
||||
s.add (c [Ids::name]);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void AppearanceSettings::updateColourScheme()
|
||||
{
|
||||
applyToLookAndFeel (LookAndFeel::getDefaultLookAndFeel());
|
||||
IntrojucerApp::getApp().mainWindowList.sendLookAndFeelChange();
|
||||
}
|
||||
|
||||
void AppearanceSettings::applyToLookAndFeel (LookAndFeel& lf) const
|
||||
{
|
||||
for (int i = 0; i < AppearanceColours::numColours; ++i)
|
||||
{
|
||||
Colour col;
|
||||
if (getColour (AppearanceColours::colours[i].name, col))
|
||||
{
|
||||
if (AppearanceColours::colours[i].mustBeOpaque)
|
||||
col = Colours::white.overlaidWith (col);
|
||||
|
||||
if (! AppearanceColours::colours[i].applyToEditorOnly)
|
||||
lf.setColour (AppearanceColours::colours[i].colourID, col);
|
||||
}
|
||||
}
|
||||
|
||||
lf.setColour (ScrollBar::thumbColourId, lf.findColour (mainBackgroundColourId).contrasting().withAlpha (0.13f));
|
||||
}
|
||||
|
||||
void AppearanceSettings::applyToCodeEditor (CodeEditorComponent& editor) const
|
||||
{
|
||||
CodeEditorComponent::ColourScheme cs (editor.getColourScheme());
|
||||
|
||||
for (int i = cs.types.size(); --i >= 0;)
|
||||
{
|
||||
CodeEditorComponent::ColourScheme::TokenType& t = cs.types.getReference(i);
|
||||
getColour (t.name, t.colour);
|
||||
}
|
||||
|
||||
editor.setColourScheme (cs);
|
||||
editor.setFont (getCodeFont());
|
||||
|
||||
for (int i = 0; i < AppearanceColours::numColours; ++i)
|
||||
{
|
||||
if (AppearanceColours::colours[i].applyToEditorOnly)
|
||||
{
|
||||
Colour col;
|
||||
if (getColour (AppearanceColours::colours[i].name, col))
|
||||
editor.setColour (AppearanceColours::colours[i].colourID, col);
|
||||
}
|
||||
}
|
||||
|
||||
editor.setColour (ScrollBar::thumbColourId, editor.findColour (CodeEditorComponent::backgroundColourId)
|
||||
.contrasting()
|
||||
.withAlpha (0.13f));
|
||||
}
|
||||
|
||||
Font AppearanceSettings::getCodeFont() const
|
||||
{
|
||||
const String fontString (settings [Ids::font].toString());
|
||||
|
||||
if (fontString.isEmpty())
|
||||
return getDefaultCodeFont();
|
||||
|
||||
return Font::fromString (fontString);
|
||||
}
|
||||
|
||||
Value AppearanceSettings::getCodeFontValue()
|
||||
{
|
||||
return settings.getPropertyAsValue (Ids::font, nullptr);
|
||||
}
|
||||
|
||||
Value AppearanceSettings::getColourValue (const String& colourName)
|
||||
{
|
||||
ValueTree c (settings.getChildWithProperty (Ids::name, colourName));
|
||||
|
||||
if (! c.isValid())
|
||||
{
|
||||
c = ValueTree ("COLOUR");
|
||||
c.setProperty (Ids::name, colourName, nullptr);
|
||||
settings.addChild (c, -1, nullptr);
|
||||
}
|
||||
|
||||
return c.getPropertyAsValue (Ids::colour, nullptr);
|
||||
}
|
||||
|
||||
bool AppearanceSettings::getColour (const String& name, Colour& result) const
|
||||
{
|
||||
const ValueTree colour (settings.getChildWithProperty (Ids::name, name));
|
||||
|
||||
if (colour.isValid())
|
||||
{
|
||||
result = Colour::fromString (colour [Ids::colour].toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct AppearanceEditor
|
||||
{
|
||||
class FontScanPanel : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
FontScanPanel()
|
||||
{
|
||||
fontsToScan = Font::findAllTypefaceNames();
|
||||
startTimer (1);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (Colours::darkgrey);
|
||||
|
||||
g.setFont (14.0f);
|
||||
g.setColour (Colours::white);
|
||||
g.drawFittedText ("Scanning for fonts..", getLocalBounds(), Justification::centred, 2);
|
||||
|
||||
const int size = 30;
|
||||
getLookAndFeel().drawSpinningWaitAnimation (g, Colours::white, (getWidth() - size) / 2, getHeight() / 2 - 50, size, size);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
repaint();
|
||||
|
||||
if (fontsToScan.size() == 0)
|
||||
{
|
||||
getAppSettings().monospacedFontNames = fontsFound;
|
||||
|
||||
if (DialogWindow* w = findParentComponentOfClass<DialogWindow>())
|
||||
w->setContentOwned (new EditorPanel(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isMonospacedTypeface (fontsToScan[0]))
|
||||
fontsFound.add (fontsToScan[0]);
|
||||
|
||||
fontsToScan.remove (0);
|
||||
}
|
||||
}
|
||||
|
||||
// A rather hacky trick to select only the fixed-pitch fonts..
|
||||
// This is unfortunately a bit slow, but will work on all platforms.
|
||||
static bool isMonospacedTypeface (const String& name)
|
||||
{
|
||||
const Font font (name, 20.0f, Font::plain);
|
||||
|
||||
const int width = font.getStringWidth ("....");
|
||||
|
||||
return width == font.getStringWidth ("WWWW")
|
||||
&& width == font.getStringWidth ("0000")
|
||||
&& width == font.getStringWidth ("1111")
|
||||
&& width == font.getStringWidth ("iiii");
|
||||
}
|
||||
|
||||
StringArray fontsToScan, fontsFound;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class EditorPanel : public Component,
|
||||
private ButtonListener
|
||||
{
|
||||
public:
|
||||
EditorPanel()
|
||||
: loadButton ("Load Scheme..."),
|
||||
saveButton ("Save Scheme...")
|
||||
{
|
||||
rebuildProperties();
|
||||
addAndMakeVisible (panel);
|
||||
|
||||
loadButton.setColour (TextButton::buttonColourId, Colours::lightgrey.withAlpha (0.5f));
|
||||
saveButton.setColour (TextButton::buttonColourId, Colours::lightgrey.withAlpha (0.5f));
|
||||
loadButton.setColour (TextButton::textColourOffId, Colours::white);
|
||||
saveButton.setColour (TextButton::textColourOffId, Colours::white);
|
||||
|
||||
addAndMakeVisible (loadButton);
|
||||
addAndMakeVisible (saveButton);
|
||||
|
||||
loadButton.addListener (this);
|
||||
saveButton.addListener (this);
|
||||
}
|
||||
|
||||
void rebuildProperties()
|
||||
{
|
||||
AppearanceSettings& scheme = getAppSettings().appearance;
|
||||
|
||||
Array <PropertyComponent*> props;
|
||||
Value fontValue (scheme.getCodeFontValue());
|
||||
props.add (FontNameValueSource::createProperty ("Code Editor Font", fontValue));
|
||||
props.add (FontSizeValueSource::createProperty ("Font Size", fontValue));
|
||||
|
||||
const StringArray colourNames (scheme.getColourNames());
|
||||
|
||||
for (int i = 0; i < colourNames.size(); ++i)
|
||||
props.add (new ColourPropertyComponent (nullptr, colourNames[i],
|
||||
scheme.getColourValue (colourNames[i]),
|
||||
Colours::white, false));
|
||||
|
||||
panel.clear();
|
||||
panel.addProperties (props);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
Rectangle<int> r (getLocalBounds());
|
||||
panel.setBounds (r.removeFromTop (getHeight() - 28).reduced (4, 2));
|
||||
loadButton.setBounds (r.removeFromLeft (getWidth() / 2).reduced (10, 4));
|
||||
saveButton.setBounds (r.reduced (10, 3));
|
||||
}
|
||||
|
||||
private:
|
||||
PropertyPanel panel;
|
||||
TextButton loadButton, saveButton;
|
||||
|
||||
void buttonClicked (Button* b) override
|
||||
{
|
||||
if (b == &loadButton)
|
||||
loadScheme();
|
||||
else
|
||||
saveScheme();
|
||||
}
|
||||
|
||||
void saveScheme()
|
||||
{
|
||||
FileChooser fc ("Select a file in which to save this colour-scheme...",
|
||||
getAppSettings().appearance.getSchemesFolder()
|
||||
.getNonexistentChildFile ("Scheme", AppearanceSettings::getSchemeFileSuffix()),
|
||||
AppearanceSettings::getSchemeFileWildCard());
|
||||
|
||||
if (fc.browseForFileToSave (true))
|
||||
{
|
||||
File file (fc.getResult().withFileExtension (AppearanceSettings::getSchemeFileSuffix()));
|
||||
getAppSettings().appearance.writeToFile (file);
|
||||
getAppSettings().appearance.refreshPresetSchemeList();
|
||||
}
|
||||
}
|
||||
|
||||
void loadScheme()
|
||||
{
|
||||
FileChooser fc ("Please select a colour-scheme file to load...",
|
||||
getAppSettings().appearance.getSchemesFolder(),
|
||||
AppearanceSettings::getSchemeFileWildCard());
|
||||
|
||||
if (fc.browseForFileToOpen())
|
||||
{
|
||||
if (getAppSettings().appearance.readFromFile (fc.getResult()))
|
||||
rebuildProperties();
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (EditorPanel)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class FontNameValueSource : public ValueSourceFilter
|
||||
{
|
||||
public:
|
||||
FontNameValueSource (const Value& source) : ValueSourceFilter (source) {}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return Font::fromString (sourceValue.toString()).getTypefaceName();
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
Font font (Font::fromString (sourceValue.toString()));
|
||||
font.setTypefaceName (newValue.toString().isEmpty() ? Font::getDefaultMonospacedFontName()
|
||||
: newValue.toString());
|
||||
sourceValue = font.toString();
|
||||
}
|
||||
|
||||
static ChoicePropertyComponent* createProperty (const String& title, const Value& value)
|
||||
{
|
||||
StringArray fontNames = getAppSettings().monospacedFontNames;
|
||||
|
||||
Array<var> values;
|
||||
values.add (Font::getDefaultMonospacedFontName());
|
||||
values.add (var());
|
||||
|
||||
for (int i = 0; i < fontNames.size(); ++i)
|
||||
values.add (fontNames[i]);
|
||||
|
||||
StringArray names;
|
||||
names.add ("<Default Monospaced>");
|
||||
names.add (String::empty);
|
||||
names.addArray (getAppSettings().monospacedFontNames);
|
||||
|
||||
return new ChoicePropertyComponent (Value (new FontNameValueSource (value)),
|
||||
title, names, values);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class FontSizeValueSource : public ValueSourceFilter
|
||||
{
|
||||
public:
|
||||
FontSizeValueSource (const Value& source) : ValueSourceFilter (source) {}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return Font::fromString (sourceValue.toString()).getHeight();
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
sourceValue = Font::fromString (sourceValue.toString()).withHeight (newValue).toString();
|
||||
}
|
||||
|
||||
static PropertyComponent* createProperty (const String& title, const Value& value)
|
||||
{
|
||||
return new SliderPropertyComponent (Value (new FontSizeValueSource (value)),
|
||||
title, 5.0, 40.0, 0.1, 0.5);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void AppearanceSettings::showEditorWindow (ScopedPointer<Component>& ownerPointer)
|
||||
{
|
||||
if (ownerPointer != nullptr)
|
||||
{
|
||||
ownerPointer->toFront (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Component* content;
|
||||
if (getAppSettings().monospacedFontNames.size() == 0)
|
||||
content = new AppearanceEditor::FontScanPanel();
|
||||
else
|
||||
content = new AppearanceEditor::EditorPanel();
|
||||
|
||||
const int width = 350;
|
||||
new FloatingToolWindow ("Appearance Settings",
|
||||
"colourSchemeEditorPos",
|
||||
content, ownerPointer,
|
||||
width, 500,
|
||||
width, 200, width, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
IntrojucerLookAndFeel::IntrojucerLookAndFeel()
|
||||
{
|
||||
setColour (mainBackgroundColourId, Colour::greyLevel (0.8f));
|
||||
}
|
||||
|
||||
int IntrojucerLookAndFeel::getTabButtonBestWidth (TabBarButton&, int) { return 120; }
|
||||
|
||||
static Colour getTabBackgroundColour (TabBarButton& button)
|
||||
{
|
||||
const Colour bkg (button.findColour (mainBackgroundColourId).contrasting (0.15f));
|
||||
|
||||
if (button.isFrontTab())
|
||||
return bkg.overlaidWith (Colours::yellow.withAlpha (0.5f));
|
||||
|
||||
return bkg;
|
||||
}
|
||||
|
||||
void IntrojucerLookAndFeel::drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown)
|
||||
{
|
||||
const Rectangle<int> activeArea (button.getActiveArea());
|
||||
|
||||
const Colour bkg (getTabBackgroundColour (button));
|
||||
|
||||
g.setGradientFill (ColourGradient (bkg.brighter (0.1f), 0, (float) activeArea.getY(),
|
||||
bkg.darker (0.1f), 0, (float) activeArea.getBottom(), false));
|
||||
g.fillRect (activeArea);
|
||||
|
||||
g.setColour (button.findColour (mainBackgroundColourId).darker (0.3f));
|
||||
g.drawRect (activeArea);
|
||||
|
||||
const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f;
|
||||
const Colour col (bkg.contrasting().withMultipliedAlpha (alpha));
|
||||
|
||||
TextLayout textLayout;
|
||||
LookAndFeel_V3::createTabTextLayout (button, (float) activeArea.getWidth(), (float) activeArea.getHeight(), col, textLayout);
|
||||
|
||||
textLayout.draw (g, button.getTextArea().toFloat());
|
||||
}
|
||||
|
||||
static Range<float> getBrightnessRange (const Image& im)
|
||||
{
|
||||
float minB = 1.0f, maxB = 0;
|
||||
const int w = im.getWidth();
|
||||
const int h = im.getHeight();
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const float b = im.getPixelAt (x, y).getBrightness();
|
||||
minB = jmin (minB, b);
|
||||
maxB = jmax (maxB, b);
|
||||
}
|
||||
}
|
||||
|
||||
return Range<float> (minB, maxB);
|
||||
}
|
||||
|
||||
void IntrojucerLookAndFeel::fillWithBackgroundTexture (Graphics& g)
|
||||
{
|
||||
const Colour bkg (findColour (mainBackgroundColourId));
|
||||
|
||||
if (backgroundTextureBaseColour != bkg)
|
||||
{
|
||||
backgroundTextureBaseColour = bkg;
|
||||
|
||||
const Image original (ImageCache::getFromMemory (BinaryData::background_tile_png,
|
||||
BinaryData::background_tile_pngSize));
|
||||
const int w = original.getWidth();
|
||||
const int h = original.getHeight();
|
||||
|
||||
backgroundTexture = Image (Image::RGB, w, h, false);
|
||||
|
||||
const Range<float> brightnessRange (getBrightnessRange (original));
|
||||
const float brightnessOffset = (brightnessRange.getStart() + brightnessRange.getEnd()) / 2.0f;
|
||||
const float brightnessScale = 0.025f / brightnessRange.getLength();
|
||||
const float bkgB = bkg.getBrightness();
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const float b = (original.getPixelAt (x, y).getBrightness() - brightnessOffset) * brightnessScale;
|
||||
backgroundTexture.setPixelAt (x, y, bkg.withBrightness (jlimit (0.0f, 1.0f, bkgB + b)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.setTiledImageFill (backgroundTexture, 0, 0, 1.0f);
|
||||
g.fillAll();
|
||||
}
|
||||
|
||||
void IntrojucerLookAndFeel::fillWithBackgroundTexture (Component& c, Graphics& g)
|
||||
{
|
||||
dynamic_cast<IntrojucerLookAndFeel&> (c.getLookAndFeel()).fillWithBackgroundTexture (g);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_APPEARANCESETTINGS_JUCEHEADER__
|
||||
#define __JUCER_APPEARANCESETTINGS_JUCEHEADER__
|
||||
|
||||
|
||||
class AppearanceSettings : private ValueTree::Listener
|
||||
{
|
||||
public:
|
||||
AppearanceSettings (bool updateAppWhenChanged);
|
||||
|
||||
bool readFromFile (const File& file);
|
||||
bool readFromXML (const XmlElement&);
|
||||
bool writeToFile (const File& file) const;
|
||||
|
||||
void updateColourScheme();
|
||||
void applyToCodeEditor (CodeEditorComponent& editor) const;
|
||||
|
||||
StringArray getColourNames() const;
|
||||
Value getColourValue (const String& colourName);
|
||||
bool getColour (const String& name, Colour& resultIfFound) const;
|
||||
|
||||
Font getCodeFont() const;
|
||||
Value getCodeFontValue();
|
||||
|
||||
ValueTree settings;
|
||||
|
||||
static File getSchemesFolder();
|
||||
StringArray getPresetSchemes();
|
||||
void refreshPresetSchemeList();
|
||||
void selectPresetScheme (int index);
|
||||
|
||||
static Font getDefaultCodeFont();
|
||||
|
||||
static void showEditorWindow (ScopedPointer<Component>& ownerPointer);
|
||||
|
||||
static const char* getSchemeFileSuffix() { return ".scheme"; }
|
||||
static const char* getSchemeFileWildCard() { return "*.scheme"; }
|
||||
|
||||
private:
|
||||
|
||||
Array<File> presetSchemeFiles;
|
||||
|
||||
static void writeDefaultSchemeFile (const String& xml, const String& name);
|
||||
|
||||
void applyToLookAndFeel (LookAndFeel&) const;
|
||||
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { updateColourScheme(); }
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override { updateColourScheme(); }
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&) override { updateColourScheme(); }
|
||||
void valueTreeChildOrderChanged (ValueTree&) override { updateColourScheme(); }
|
||||
void valueTreeParentChanged (ValueTree&) override { updateColourScheme(); }
|
||||
void valueTreeRedirected (ValueTree&) override { updateColourScheme(); }
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AppearanceSettings)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class IntrojucerLookAndFeel : public LookAndFeel_V3
|
||||
{
|
||||
public:
|
||||
IntrojucerLookAndFeel();
|
||||
|
||||
void fillWithBackgroundTexture (Graphics&);
|
||||
static void fillWithBackgroundTexture (Component&, Graphics&);
|
||||
|
||||
void drawTabButton (TabBarButton& button, Graphics&, bool isMouseOver, bool isMouseDown) override;
|
||||
void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int, int) override {}
|
||||
|
||||
int getTabButtonBestWidth (TabBarButton&, int tabDepth) override;
|
||||
|
||||
private:
|
||||
Image backgroundTexture;
|
||||
Colour backgroundTextureBaseColour;
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_APPEARANCESETTINGS_JUCEHEADER__
|
||||
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_APPLICATION_JUCEHEADER__
|
||||
#define __JUCER_APPLICATION_JUCEHEADER__
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "jucer_MainWindow.h"
|
||||
#include "jucer_CommandLine.h"
|
||||
#include "../Project/jucer_Module.h"
|
||||
#include "jucer_AutoUpdater.h"
|
||||
#include "../Code Editor/jucer_SourceCodeEditor.h"
|
||||
|
||||
void createGUIEditorMenu (PopupMenu&);
|
||||
void handleGUIEditorMenuCommand (int);
|
||||
void registerGUIEditorCommands();
|
||||
|
||||
//==============================================================================
|
||||
class IntrojucerApp : public JUCEApplication
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
IntrojucerApp() : isRunningCommandLine (false) {}
|
||||
|
||||
//==============================================================================
|
||||
void initialise (const String& commandLine) override
|
||||
{
|
||||
LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
|
||||
settings = new StoredSettings();
|
||||
|
||||
if (commandLine.isNotEmpty())
|
||||
{
|
||||
const int appReturnCode = performCommandLine (commandLine);
|
||||
|
||||
if (appReturnCode != commandLineNotPerformed)
|
||||
{
|
||||
isRunningCommandLine = true;
|
||||
setApplicationReturnValue (appReturnCode);
|
||||
quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sendCommandLineToPreexistingInstance())
|
||||
{
|
||||
DBG ("Another instance is running - quitting...");
|
||||
quit();
|
||||
return;
|
||||
}
|
||||
|
||||
initialiseLogger ("log_");
|
||||
|
||||
icons = new Icons();
|
||||
|
||||
initCommandManager();
|
||||
|
||||
menuModel = new MainMenuModel();
|
||||
|
||||
doExtraInitialisation();
|
||||
|
||||
settings->appearance.refreshPresetSchemeList();
|
||||
|
||||
ImageCache::setCacheTimeout (30 * 1000);
|
||||
|
||||
if (commandLine.trim().isNotEmpty() && ! commandLine.trim().startsWithChar ('-'))
|
||||
anotherInstanceStarted (commandLine);
|
||||
else
|
||||
mainWindowList.reopenLastProjects();
|
||||
|
||||
mainWindowList.createWindowIfNoneAreOpen();
|
||||
|
||||
#if JUCE_MAC
|
||||
MenuBarModel::setMacMainMenu (menuModel, nullptr, "Open Recent");
|
||||
#endif
|
||||
|
||||
versionChecker = new LatestVersionChecker();
|
||||
}
|
||||
|
||||
void shutdown() override
|
||||
{
|
||||
versionChecker = nullptr;
|
||||
appearanceEditorWindow = nullptr;
|
||||
utf8Window = nullptr;
|
||||
svgPathWindow = nullptr;
|
||||
|
||||
mainWindowList.forceCloseAllWindows();
|
||||
openDocumentManager.clear();
|
||||
|
||||
#if JUCE_MAC
|
||||
MenuBarModel::setMacMainMenu (nullptr);
|
||||
#endif
|
||||
|
||||
menuModel = nullptr;
|
||||
commandManager = nullptr;
|
||||
settings = nullptr;
|
||||
|
||||
LookAndFeel::setDefaultLookAndFeel (nullptr);
|
||||
|
||||
if (! isRunningCommandLine)
|
||||
Logger::writeToLog ("Shutdown");
|
||||
|
||||
deleteLogger();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void systemRequestedQuit() override
|
||||
{
|
||||
if (ModalComponentManager::getInstance()->cancelAllModalComponents())
|
||||
{
|
||||
new AsyncQuitRetrier();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (closeAllMainWindows())
|
||||
quit();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const String getApplicationName() override { return "Introjucer"; }
|
||||
const String getApplicationVersion() override { return ProjectInfo::versionString; }
|
||||
|
||||
bool moreThanOneInstanceAllowed() override
|
||||
{
|
||||
return true; // this is handled manually in initialise()
|
||||
}
|
||||
|
||||
void anotherInstanceStarted (const String& commandLine) override
|
||||
{
|
||||
openFile (File (commandLine.unquoted()));
|
||||
}
|
||||
|
||||
static IntrojucerApp& getApp()
|
||||
{
|
||||
IntrojucerApp* const app = dynamic_cast<IntrojucerApp*> (JUCEApplication::getInstance());
|
||||
jassert (app != nullptr);
|
||||
return *app;
|
||||
}
|
||||
|
||||
static ApplicationCommandManager& getCommandManager()
|
||||
{
|
||||
ApplicationCommandManager* cm = IntrojucerApp::getApp().commandManager;
|
||||
jassert (cm != nullptr);
|
||||
return *cm;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class MainMenuModel : public MenuBarModel
|
||||
{
|
||||
public:
|
||||
MainMenuModel()
|
||||
{
|
||||
setApplicationCommandManagerToWatch (&getCommandManager());
|
||||
}
|
||||
|
||||
StringArray getMenuBarNames() override
|
||||
{
|
||||
return getApp().getMenuNames();
|
||||
}
|
||||
|
||||
PopupMenu getMenuForIndex (int /*topLevelMenuIndex*/, const String& menuName) override
|
||||
{
|
||||
PopupMenu menu;
|
||||
getApp().createMenu (menu, menuName);
|
||||
return menu;
|
||||
}
|
||||
|
||||
void menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/) override
|
||||
{
|
||||
getApp().handleMainMenuCommand (menuItemID);
|
||||
}
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
recentProjectsBaseID = 100,
|
||||
activeDocumentsBaseID = 300,
|
||||
colourSchemeBaseID = 1000
|
||||
};
|
||||
|
||||
virtual StringArray getMenuNames()
|
||||
{
|
||||
const char* const names[] = { "File", "Edit", "View", "Window", "GUI Editor", "Tools", nullptr };
|
||||
return StringArray (names);
|
||||
}
|
||||
|
||||
virtual void createMenu (PopupMenu& menu, const String& menuName)
|
||||
{
|
||||
if (menuName == "File") createFileMenu (menu);
|
||||
else if (menuName == "Edit") createEditMenu (menu);
|
||||
else if (menuName == "View") createViewMenu (menu);
|
||||
else if (menuName == "Window") createWindowMenu (menu);
|
||||
else if (menuName == "Tools") createToolsMenu (menu);
|
||||
else if (menuName == "GUI Editor") createGUIEditorMenu (menu);
|
||||
else jassertfalse; // names have changed?
|
||||
}
|
||||
|
||||
virtual void createFileMenu (PopupMenu& menu)
|
||||
{
|
||||
menu.addCommandItem (commandManager, CommandIDs::newProject);
|
||||
menu.addSeparator();
|
||||
menu.addCommandItem (commandManager, CommandIDs::open);
|
||||
|
||||
PopupMenu recentFiles;
|
||||
settings->recentFiles.createPopupMenuItems (recentFiles, recentProjectsBaseID, true, true);
|
||||
menu.addSubMenu ("Open Recent", recentFiles);
|
||||
|
||||
menu.addSeparator();
|
||||
menu.addCommandItem (commandManager, CommandIDs::closeDocument);
|
||||
menu.addCommandItem (commandManager, CommandIDs::saveDocument);
|
||||
menu.addCommandItem (commandManager, CommandIDs::saveDocumentAs);
|
||||
menu.addCommandItem (commandManager, CommandIDs::saveAll);
|
||||
menu.addSeparator();
|
||||
menu.addCommandItem (commandManager, CommandIDs::closeProject);
|
||||
menu.addCommandItem (commandManager, CommandIDs::saveProject);
|
||||
menu.addSeparator();
|
||||
menu.addCommandItem (commandManager, CommandIDs::openInIDE);
|
||||
menu.addCommandItem (commandManager, CommandIDs::saveAndOpenInIDE);
|
||||
|
||||
#if ! JUCE_MAC
|
||||
menu.addSeparator();
|
||||
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void createEditMenu (PopupMenu& menu)
|
||||
{
|
||||
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::undo);
|
||||
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::redo);
|
||||
menu.addSeparator();
|
||||
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
|
||||
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);
|
||||
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::paste);
|
||||
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
|
||||
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::selectAll);
|
||||
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::deselectAll);
|
||||
menu.addSeparator();
|
||||
menu.addCommandItem (commandManager, CommandIDs::showFindPanel);
|
||||
menu.addCommandItem (commandManager, CommandIDs::findSelection);
|
||||
menu.addCommandItem (commandManager, CommandIDs::findNext);
|
||||
menu.addCommandItem (commandManager, CommandIDs::findPrevious);
|
||||
}
|
||||
|
||||
virtual void createViewMenu (PopupMenu& menu)
|
||||
{
|
||||
menu.addCommandItem (commandManager, CommandIDs::showFilePanel);
|
||||
menu.addCommandItem (commandManager, CommandIDs::showConfigPanel);
|
||||
menu.addCommandItem (commandManager, CommandIDs::showProjectSettings);
|
||||
menu.addCommandItem (commandManager, CommandIDs::showProjectModules);
|
||||
menu.addSeparator();
|
||||
createColourSchemeItems (menu);
|
||||
}
|
||||
|
||||
void createColourSchemeItems (PopupMenu& menu)
|
||||
{
|
||||
menu.addCommandItem (commandManager, CommandIDs::showAppearanceSettings);
|
||||
|
||||
const StringArray presetSchemes (settings->appearance.getPresetSchemes());
|
||||
|
||||
if (presetSchemes.size() > 0)
|
||||
{
|
||||
PopupMenu schemes;
|
||||
|
||||
for (int i = 0; i < presetSchemes.size(); ++i)
|
||||
schemes.addItem (colourSchemeBaseID + i, presetSchemes[i]);
|
||||
|
||||
menu.addSubMenu ("Colour Scheme", schemes);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void createWindowMenu (PopupMenu& menu)
|
||||
{
|
||||
menu.addCommandItem (commandManager, CommandIDs::closeWindow);
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addCommandItem (commandManager, CommandIDs::goToPreviousDoc);
|
||||
menu.addCommandItem (commandManager, CommandIDs::goToNextDoc);
|
||||
menu.addCommandItem (commandManager, CommandIDs::goToCounterpart);
|
||||
menu.addSeparator();
|
||||
|
||||
const int numDocs = jmin (50, openDocumentManager.getNumOpenDocuments());
|
||||
|
||||
for (int i = 0; i < numDocs; ++i)
|
||||
{
|
||||
OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument(i);
|
||||
menu.addItem (activeDocumentsBaseID + i, doc->getName());
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
menu.addCommandItem (commandManager, CommandIDs::closeAllDocuments);
|
||||
}
|
||||
|
||||
virtual void createToolsMenu (PopupMenu& menu)
|
||||
{
|
||||
menu.addCommandItem (commandManager, CommandIDs::showUTF8Tool);
|
||||
menu.addCommandItem (commandManager, CommandIDs::showSVGPathTool);
|
||||
menu.addCommandItem (commandManager, CommandIDs::showTranslationTool);
|
||||
}
|
||||
|
||||
virtual void handleMainMenuCommand (int menuItemID)
|
||||
{
|
||||
if (menuItemID >= recentProjectsBaseID && menuItemID < recentProjectsBaseID + 100)
|
||||
{
|
||||
// open a file from the "recent files" menu
|
||||
openFile (settings->recentFiles.getFile (menuItemID - recentProjectsBaseID));
|
||||
}
|
||||
else if (menuItemID >= activeDocumentsBaseID && menuItemID < activeDocumentsBaseID + 200)
|
||||
{
|
||||
if (OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument (menuItemID - activeDocumentsBaseID))
|
||||
mainWindowList.openDocument (doc, true);
|
||||
else
|
||||
jassertfalse;
|
||||
}
|
||||
else if (menuItemID >= colourSchemeBaseID && menuItemID < colourSchemeBaseID + 200)
|
||||
{
|
||||
settings->appearance.selectPresetScheme (menuItemID - colourSchemeBaseID);
|
||||
}
|
||||
else
|
||||
{
|
||||
handleGUIEditorMenuCommand (menuItemID);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void getAllCommands (Array <CommandID>& commands) override
|
||||
{
|
||||
JUCEApplication::getAllCommands (commands);
|
||||
|
||||
const CommandID ids[] = { CommandIDs::newProject,
|
||||
CommandIDs::open,
|
||||
CommandIDs::closeAllDocuments,
|
||||
CommandIDs::saveAll,
|
||||
CommandIDs::showAppearanceSettings,
|
||||
CommandIDs::showUTF8Tool,
|
||||
CommandIDs::showSVGPathTool };
|
||||
|
||||
commands.addArray (ids, numElementsInArray (ids));
|
||||
}
|
||||
|
||||
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
|
||||
{
|
||||
switch (commandID)
|
||||
{
|
||||
case CommandIDs::newProject:
|
||||
result.setInfo ("New Project...", "Creates a new Jucer project", CommandCategories::general, 0);
|
||||
result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::open:
|
||||
result.setInfo ("Open...", "Opens a Jucer project", CommandCategories::general, 0);
|
||||
result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::showAppearanceSettings:
|
||||
result.setInfo ("Fonts and Colours...", "Shows the appearance settings window.", CommandCategories::general, 0);
|
||||
break;
|
||||
|
||||
case CommandIDs::closeAllDocuments:
|
||||
result.setInfo ("Close All Documents", "Closes all open documents", CommandCategories::general, 0);
|
||||
result.setActive (openDocumentManager.getNumOpenDocuments() > 0);
|
||||
break;
|
||||
|
||||
case CommandIDs::saveAll:
|
||||
result.setInfo ("Save All", "Saves all open documents", CommandCategories::general, 0);
|
||||
result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::altModifier, 0));
|
||||
break;
|
||||
|
||||
case CommandIDs::showUTF8Tool:
|
||||
result.setInfo ("UTF-8 String-Literal Helper", "Shows the UTF-8 string literal utility", CommandCategories::general, 0);
|
||||
break;
|
||||
|
||||
case CommandIDs::showSVGPathTool:
|
||||
result.setInfo ("SVG Path Helper", "Shows the SVG->Path data conversion utility", CommandCategories::general, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
JUCEApplication::getCommandInfo (commandID, result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool perform (const InvocationInfo& info) override
|
||||
{
|
||||
switch (info.commandID)
|
||||
{
|
||||
case CommandIDs::newProject: createNewProject(); break;
|
||||
case CommandIDs::open: askUserToOpenFile(); break;
|
||||
case CommandIDs::saveAll: openDocumentManager.saveAll(); break;
|
||||
case CommandIDs::closeAllDocuments: closeAllDocuments (true); break;
|
||||
case CommandIDs::showUTF8Tool: showUTF8ToolWindow (utf8Window); break;
|
||||
case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow (svgPathWindow); break;
|
||||
|
||||
case CommandIDs::showAppearanceSettings: AppearanceSettings::showEditorWindow (appearanceEditorWindow); break;
|
||||
default: return JUCEApplication::perform (info);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void createNewProject()
|
||||
{
|
||||
MainWindow* mw = mainWindowList.getOrCreateEmptyWindow();
|
||||
mw->showNewProjectWizard();
|
||||
mainWindowList.avoidSuperimposedWindows (mw);
|
||||
}
|
||||
|
||||
virtual void updateNewlyOpenedProject (Project&) {}
|
||||
|
||||
void askUserToOpenFile()
|
||||
{
|
||||
FileChooser fc ("Open File");
|
||||
|
||||
if (fc.browseForFileToOpen())
|
||||
openFile (fc.getResult());
|
||||
}
|
||||
|
||||
bool openFile (const File& file)
|
||||
{
|
||||
return mainWindowList.openFile (file);
|
||||
}
|
||||
|
||||
bool closeAllDocuments (bool askUserToSave)
|
||||
{
|
||||
return openDocumentManager.closeAll (askUserToSave);
|
||||
}
|
||||
|
||||
virtual bool closeAllMainWindows()
|
||||
{
|
||||
return mainWindowList.askAllWindowsToClose();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void initialiseLogger (const char* filePrefix)
|
||||
{
|
||||
if (logger == nullptr)
|
||||
{
|
||||
logger = FileLogger::createDateStampedLogger (getLogFolderName(), filePrefix, ".txt",
|
||||
getApplicationName() + " " + getApplicationVersion()
|
||||
+ " --- Build date: " __DATE__);
|
||||
Logger::setCurrentLogger (logger);
|
||||
}
|
||||
}
|
||||
|
||||
struct FileWithTime
|
||||
{
|
||||
FileWithTime (const File& f) : file (f), time (f.getLastModificationTime()) {}
|
||||
FileWithTime() {}
|
||||
|
||||
bool operator< (const FileWithTime& other) const { return time < other.time; }
|
||||
bool operator== (const FileWithTime& other) const { return time == other.time; }
|
||||
|
||||
File file;
|
||||
Time time;
|
||||
};
|
||||
|
||||
void deleteLogger()
|
||||
{
|
||||
const int maxNumLogFilesToKeep = 50;
|
||||
|
||||
Logger::setCurrentLogger (nullptr);
|
||||
|
||||
if (logger != nullptr)
|
||||
{
|
||||
Array<File> logFiles;
|
||||
logger->getLogFile().getParentDirectory().findChildFiles (logFiles, File::findFiles, false);
|
||||
|
||||
if (logFiles.size() > maxNumLogFilesToKeep)
|
||||
{
|
||||
Array <FileWithTime> files;
|
||||
|
||||
for (int i = 0; i < logFiles.size(); ++i)
|
||||
files.addUsingDefaultSort (logFiles.getReference(i));
|
||||
|
||||
for (int i = 0; i < files.size() - maxNumLogFilesToKeep; ++i)
|
||||
files.getReference(i).file.deleteFile();
|
||||
}
|
||||
}
|
||||
|
||||
logger = nullptr;
|
||||
}
|
||||
|
||||
virtual void doExtraInitialisation() {}
|
||||
virtual void addExtraConfigItems (Project&, TreeViewItem&) {}
|
||||
|
||||
#if JUCE_LINUX
|
||||
virtual String getLogFolderName() const { return "~/.config/Introjucer/Logs"; }
|
||||
#else
|
||||
virtual String getLogFolderName() const { return "com.juce.introjucer"; }
|
||||
#endif
|
||||
|
||||
virtual PropertiesFile::Options getPropertyFileOptionsFor (const String& filename)
|
||||
{
|
||||
PropertiesFile::Options options;
|
||||
options.applicationName = filename;
|
||||
options.filenameSuffix = "settings";
|
||||
options.osxLibrarySubFolder = "Application Support";
|
||||
#if JUCE_LINUX
|
||||
options.folderName = "~/.config/Introjucer";
|
||||
#else
|
||||
options.folderName = "Introjucer";
|
||||
#endif
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
virtual Component* createProjectContentComponent() const
|
||||
{
|
||||
return new ProjectContentComponent();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
IntrojucerLookAndFeel lookAndFeel;
|
||||
|
||||
ScopedPointer<StoredSettings> settings;
|
||||
ScopedPointer<Icons> icons;
|
||||
|
||||
ScopedPointer<MainMenuModel> menuModel;
|
||||
|
||||
MainWindowList mainWindowList;
|
||||
OpenDocumentManager openDocumentManager;
|
||||
ScopedPointer<ApplicationCommandManager> commandManager;
|
||||
|
||||
ScopedPointer<Component> appearanceEditorWindow, utf8Window, svgPathWindow;
|
||||
|
||||
ScopedPointer<FileLogger> logger;
|
||||
|
||||
bool isRunningCommandLine;
|
||||
|
||||
private:
|
||||
ScopedPointer<LatestVersionChecker> versionChecker;
|
||||
|
||||
class AsyncQuitRetrier : private Timer
|
||||
{
|
||||
public:
|
||||
AsyncQuitRetrier() { startTimer (500); }
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
delete this;
|
||||
|
||||
if (JUCEApplicationBase* app = JUCEApplicationBase::getInstance())
|
||||
app->systemRequestedQuit();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AsyncQuitRetrier)
|
||||
};
|
||||
|
||||
void initCommandManager()
|
||||
{
|
||||
commandManager = new ApplicationCommandManager();
|
||||
commandManager->registerAllCommandsForTarget (this);
|
||||
|
||||
{
|
||||
CodeDocument doc;
|
||||
CppCodeEditorComponent ed (File::nonexistent, doc);
|
||||
commandManager->registerAllCommandsForTarget (&ed);
|
||||
}
|
||||
|
||||
registerGUIEditorCommands();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_APPLICATION_JUCEHEADER__
|
||||
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCER_AUTOUPDATER_H_INCLUDED
|
||||
#define JUCER_AUTOUPDATER_H_INCLUDED
|
||||
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class LatestVersionChecker : private Thread,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
LatestVersionChecker() : Thread ("Updater"),
|
||||
hasAttemptedToReadWebsite (false)
|
||||
{
|
||||
startTimer (2000);
|
||||
}
|
||||
|
||||
~LatestVersionChecker()
|
||||
{
|
||||
stopThread (20000);
|
||||
}
|
||||
|
||||
static URL getLatestVersionURL()
|
||||
{
|
||||
return URL ("http://www.juce.com/juce/updates/updatelist.php");
|
||||
}
|
||||
|
||||
void checkForNewVersion()
|
||||
{
|
||||
hasAttemptedToReadWebsite = true;
|
||||
|
||||
{
|
||||
const ScopedPointer<InputStream> in (getLatestVersionURL().createInputStream (false));
|
||||
|
||||
if (in == nullptr || threadShouldExit())
|
||||
return; // can't connect: fail silently.
|
||||
|
||||
jsonReply = JSON::parse (in->readEntireStreamAsString());
|
||||
}
|
||||
|
||||
if (threadShouldExit())
|
||||
return;
|
||||
|
||||
if (jsonReply.isArray() || jsonReply.isObject())
|
||||
startTimer (100);
|
||||
}
|
||||
|
||||
void processResult (var reply)
|
||||
{
|
||||
if (reply.isArray())
|
||||
{
|
||||
askUserAboutNewVersion (VersionInfo (reply[0]));
|
||||
}
|
||||
else if (reply.isObject())
|
||||
{
|
||||
// In the far-distant future, this may be contacting a defunct
|
||||
// URL, so hopefully the website will contain a helpful message
|
||||
// for the user..
|
||||
String message = reply.getProperty ("message", var()).toString();
|
||||
|
||||
if (message.isNotEmpty())
|
||||
{
|
||||
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
|
||||
TRANS("JUCE Updater"),
|
||||
message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct VersionInfo
|
||||
{
|
||||
VersionInfo (var v)
|
||||
{
|
||||
version = v.getProperty ("version", var()).toString().trim();
|
||||
|
||||
url = v.getProperty (
|
||||
#if JUCE_MAC
|
||||
"url_osx",
|
||||
#elif JUCE_WINDOWS
|
||||
"url_win",
|
||||
#elif JUCE_LINUX
|
||||
"url_linux",
|
||||
#endif
|
||||
var()).toString();
|
||||
}
|
||||
|
||||
bool isDifferentVersionToCurrent() const
|
||||
{
|
||||
return version != JUCE_STRINGIFY(JUCE_MAJOR_VERSION)
|
||||
"." JUCE_STRINGIFY(JUCE_MINOR_VERSION)
|
||||
"." JUCE_STRINGIFY(JUCE_BUILDNUMBER)
|
||||
&& version.containsChar ('.')
|
||||
&& version.length() > 2;
|
||||
}
|
||||
|
||||
String version;
|
||||
URL url;
|
||||
};
|
||||
|
||||
void askUserAboutNewVersion (const VersionInfo& info)
|
||||
{
|
||||
if (info.isDifferentVersionToCurrent())
|
||||
{
|
||||
File appParentFolder (File::getSpecialLocation (File::currentApplicationFile).getParentDirectory());
|
||||
|
||||
if (isZipFolder (appParentFolder))
|
||||
{
|
||||
int result = AlertWindow::showYesNoCancelBox (AlertWindow::InfoIcon,
|
||||
TRANS("Download JUCE version 123?").replace ("123", info.version),
|
||||
TRANS("A new version of JUCE is available - would you like to overwrite the folder:\n\n"
|
||||
"xfldrx\n\n"
|
||||
" ..with the latest version from juce.com?\n\n"
|
||||
"(Please note that this will overwrite everything in that folder!)")
|
||||
.replace ("xfldrx", appParentFolder.getFullPathName()),
|
||||
TRANS("Overwrite"),
|
||||
TRANS("Choose another folder..."),
|
||||
TRANS("Cancel"));
|
||||
|
||||
if (result == 1)
|
||||
DownloadNewVersionThread::performDownload (info.url, appParentFolder);
|
||||
|
||||
if (result == 2)
|
||||
askUserForLocationToDownload (info);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AlertWindow::showOkCancelBox (AlertWindow::InfoIcon,
|
||||
TRANS("Download JUCE version 123?").replace ("123", info.version),
|
||||
TRANS("A new version of JUCE is available - would you like to download it?")))
|
||||
askUserForLocationToDownload (info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void askUserForLocationToDownload (const VersionInfo& info)
|
||||
{
|
||||
File targetFolder (findDefaultModulesFolder());
|
||||
|
||||
if (isJuceModulesFolder (targetFolder))
|
||||
targetFolder = targetFolder.getParentDirectory();
|
||||
|
||||
FileChooser chooser (TRANS("Please select the location into which you'd like to install the new version"),
|
||||
targetFolder);
|
||||
|
||||
if (chooser.browseForDirectory())
|
||||
{
|
||||
targetFolder = chooser.getResult();
|
||||
|
||||
if (isJuceModulesFolder (targetFolder))
|
||||
targetFolder = targetFolder.getParentDirectory();
|
||||
|
||||
if (targetFolder.getChildFile ("JUCE").isDirectory())
|
||||
targetFolder = targetFolder.getChildFile ("JUCE");
|
||||
|
||||
if (targetFolder.getChildFile (".git").isDirectory())
|
||||
{
|
||||
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
|
||||
TRANS ("Downloading new JUCE version"),
|
||||
TRANS ("This folder is a GIT repository!\n\n"
|
||||
"You should use a \"git pull\" to update it to the latest version. "
|
||||
"Or to use the Introjucer to get an update, you should select an empty "
|
||||
"folder into which you'd like to download the new code."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isJuceFolder (targetFolder))
|
||||
{
|
||||
if (! AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
|
||||
TRANS("Overwrite existing JUCE folder?"),
|
||||
TRANS("Do you want to overwrite the folder:\n\n"
|
||||
"xfldrx\n\n"
|
||||
" ..with the latest version from juce.com?\n\n"
|
||||
"(Please note that this will overwrite everything in that folder!)")
|
||||
.replace ("xfldrx", targetFolder.getFullPathName())))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
targetFolder = targetFolder.getChildFile ("JUCE").getNonexistentSibling();
|
||||
}
|
||||
|
||||
DownloadNewVersionThread::performDownload (info.url, targetFolder);
|
||||
}
|
||||
}
|
||||
|
||||
static bool isZipFolder (const File& f)
|
||||
{
|
||||
return f.getChildFile ("modules").isDirectory()
|
||||
&& f.getChildFile ("extras").isDirectory()
|
||||
&& f.getChildFile ("examples").isDirectory()
|
||||
&& ! f.getChildFile (".git").isDirectory();
|
||||
}
|
||||
|
||||
private:
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
if (hasAttemptedToReadWebsite)
|
||||
processResult (jsonReply);
|
||||
else
|
||||
startThread (3);
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
checkForNewVersion();
|
||||
}
|
||||
|
||||
var jsonReply;
|
||||
bool hasAttemptedToReadWebsite;
|
||||
URL newVersionToDownload;
|
||||
|
||||
//==============================================================================
|
||||
class DownloadNewVersionThread : public ThreadWithProgressWindow
|
||||
{
|
||||
public:
|
||||
DownloadNewVersionThread (URL u, File target)
|
||||
: ThreadWithProgressWindow ("Downloading New Version", true, true),
|
||||
result (Result::ok()),
|
||||
url (u), targetFolder (target)
|
||||
{
|
||||
}
|
||||
|
||||
static void performDownload (URL u, File targetFolder)
|
||||
{
|
||||
DownloadNewVersionThread d (u, targetFolder);
|
||||
|
||||
if (d.runThread())
|
||||
{
|
||||
if (d.result.failed())
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
"Installation Failed",
|
||||
d.result.getErrorMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
new RelaunchTimer (targetFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
setProgress (-1.0);
|
||||
|
||||
MemoryBlock zipData;
|
||||
result = download (zipData);
|
||||
|
||||
if (result.wasOk() && ! threadShouldExit())
|
||||
result = unzip (zipData);
|
||||
}
|
||||
|
||||
Result download (MemoryBlock& dest)
|
||||
{
|
||||
setStatusMessage ("Downloading...");
|
||||
|
||||
const ScopedPointer<InputStream> in (url.createInputStream (false, nullptr, nullptr, String::empty, 10000));
|
||||
|
||||
if (in != nullptr)
|
||||
{
|
||||
int64 total = 0;
|
||||
MemoryOutputStream mo (dest, true);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (threadShouldExit())
|
||||
return Result::fail ("cancel");
|
||||
|
||||
int64 written = mo.writeFromInputStream (*in, 8192);
|
||||
|
||||
if (written == 0)
|
||||
break;
|
||||
|
||||
total += written;
|
||||
|
||||
setStatusMessage (String (TRANS ("Downloading... (123)"))
|
||||
.replace ("123", File::descriptionOfSizeInBytes (total)));
|
||||
}
|
||||
|
||||
return Result::ok();
|
||||
}
|
||||
|
||||
return Result::fail ("Failed to download from: " + url.toString (false));
|
||||
}
|
||||
|
||||
Result unzip (const MemoryBlock& data)
|
||||
{
|
||||
setStatusMessage ("Installing...");
|
||||
|
||||
File unzipTarget;
|
||||
bool isUsingTempFolder = false;
|
||||
|
||||
{
|
||||
MemoryInputStream input (data, false);
|
||||
ZipFile zip (input);
|
||||
|
||||
if (zip.getNumEntries() == 0)
|
||||
return Result::fail ("The downloaded file wasn't a valid JUCE file!");
|
||||
|
||||
unzipTarget = targetFolder;
|
||||
|
||||
if (unzipTarget.exists())
|
||||
{
|
||||
isUsingTempFolder = true;
|
||||
unzipTarget = targetFolder.getNonexistentSibling();
|
||||
|
||||
if (! unzipTarget.createDirectory())
|
||||
return Result::fail ("Couldn't create a folder to unzip the new version!");
|
||||
}
|
||||
|
||||
Result r (zip.uncompressTo (unzipTarget));
|
||||
|
||||
if (r.failed())
|
||||
{
|
||||
if (isUsingTempFolder)
|
||||
unzipTarget.deleteRecursively();
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (isUsingTempFolder)
|
||||
{
|
||||
File oldFolder (targetFolder.getSiblingFile (targetFolder.getFileNameWithoutExtension() + "_old").getNonexistentSibling());
|
||||
|
||||
if (! targetFolder.moveFileTo (oldFolder))
|
||||
{
|
||||
unzipTarget.deleteRecursively();
|
||||
return Result::fail ("Could not remove the existing folder!");
|
||||
}
|
||||
|
||||
if (! unzipTarget.moveFileTo (targetFolder))
|
||||
{
|
||||
unzipTarget.deleteRecursively();
|
||||
return Result::fail ("Could not overwrite the existing folder!");
|
||||
}
|
||||
}
|
||||
|
||||
return Result::ok();
|
||||
}
|
||||
|
||||
Result result;
|
||||
URL url;
|
||||
File targetFolder;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DownloadNewVersionThread)
|
||||
};
|
||||
|
||||
struct RelaunchTimer : private Timer
|
||||
{
|
||||
RelaunchTimer (const File& f) : parentFolder (f)
|
||||
{
|
||||
startTimer (1500);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
File app = parentFolder.getChildFile (
|
||||
#if JUCE_MAC
|
||||
"Introjucer.app");
|
||||
#elif JUCE_WINDOWS
|
||||
"Introjucer.exe");
|
||||
#elif JUCE_LINUX
|
||||
"Introjucer");
|
||||
#endif
|
||||
|
||||
JUCEApplication::quit();
|
||||
|
||||
if (app.exists())
|
||||
{
|
||||
app.setExecutePermission (true);
|
||||
|
||||
#if JUCE_MAC
|
||||
app.getChildFile("Contents")
|
||||
.getChildFile("MacOS")
|
||||
.getChildFile("Introjucer").setExecutePermission (true);
|
||||
#endif
|
||||
|
||||
app.startAsProcess();
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
File parentFolder;
|
||||
};
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LatestVersionChecker)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCER_AUTOUPDATER_H_INCLUDED
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
A namespace to hold all the possible command IDs.
|
||||
*/
|
||||
namespace CommandIDs
|
||||
{
|
||||
enum
|
||||
{
|
||||
newProject = 0x200010,
|
||||
open = 0x200020,
|
||||
closeDocument = 0x200030,
|
||||
saveDocument = 0x200040,
|
||||
saveDocumentAs = 0x200041,
|
||||
|
||||
closeProject = 0x200051,
|
||||
saveProject = 0x200060,
|
||||
saveAll = 0x200080,
|
||||
openInIDE = 0x200072,
|
||||
saveAndOpenInIDE = 0x200073,
|
||||
|
||||
showUTF8Tool = 0x200076,
|
||||
showAppearanceSettings = 0x200077,
|
||||
showConfigPanel = 0x200074,
|
||||
showFilePanel = 0x200078,
|
||||
showTranslationTool = 0x200079,
|
||||
showProjectSettings = 0x20007a,
|
||||
showProjectModules = 0x20007b,
|
||||
showSVGPathTool = 0x20007c,
|
||||
|
||||
closeWindow = 0x201001,
|
||||
closeAllDocuments = 0x201000,
|
||||
goToPreviousDoc = 0x201002,
|
||||
goToNextDoc = 0x201003,
|
||||
goToCounterpart = 0x201004,
|
||||
deleteSelectedItem = 0x201005,
|
||||
|
||||
showFindPanel = 0x2010a0,
|
||||
findSelection = 0x2010a1,
|
||||
findNext = 0x2010a2,
|
||||
findPrevious = 0x2010a3
|
||||
};
|
||||
}
|
||||
|
||||
namespace CommandCategories
|
||||
{
|
||||
static const char* const general = "General";
|
||||
static const char* const editing = "Editing";
|
||||
static const char* const view = "View";
|
||||
static const char* const windows = "Windows";
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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 "../Project/jucer_Project.h"
|
||||
#include "../Project/jucer_Module.h"
|
||||
#include "jucer_CommandLine.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
namespace
|
||||
{
|
||||
static void hideDockIcon()
|
||||
{
|
||||
#if JUCE_MAC
|
||||
Process::setDockIconVisible (false);
|
||||
#endif
|
||||
}
|
||||
|
||||
static File getFile (const String& filename)
|
||||
{
|
||||
return File::getCurrentWorkingDirectory().getChildFile (filename.unquoted());
|
||||
}
|
||||
|
||||
static bool matchArgument (const String& arg, const String& possible)
|
||||
{
|
||||
return arg == possible
|
||||
|| arg == "-" + possible
|
||||
|| arg == "--" + possible;
|
||||
}
|
||||
|
||||
static bool checkArgumentCount (const StringArray& args, int minNumArgs)
|
||||
{
|
||||
if (args.size() < minNumArgs)
|
||||
{
|
||||
std::cout << "Not enough arguments!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct LoadedProject
|
||||
{
|
||||
LoadedProject()
|
||||
{
|
||||
hideDockIcon();
|
||||
}
|
||||
|
||||
int load (const File& projectFile)
|
||||
{
|
||||
hideDockIcon();
|
||||
|
||||
if (! projectFile.exists())
|
||||
{
|
||||
std::cout << "The file " << projectFile.getFullPathName() << " doesn't exist!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (! projectFile.hasFileExtension (Project::projectFileExtension))
|
||||
{
|
||||
std::cout << projectFile.getFullPathName() << " isn't a valid jucer project file!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
project = new Project (projectFile);
|
||||
|
||||
if (! project->loadFrom (projectFile, true))
|
||||
{
|
||||
project = nullptr;
|
||||
std::cout << "Failed to load the project file: " << projectFile.getFullPathName() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int save (bool justSaveResources)
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
Result error (justSaveResources ? project->saveResourcesOnly (project->getFile())
|
||||
: project->saveProject (project->getFile(), true));
|
||||
|
||||
if (error.failed())
|
||||
{
|
||||
std::cout << "Error when saving: " << error.getErrorMessage() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
project = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ScopedPointer<Project> project;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/* Running a command-line of the form "introjucer --resave foobar.jucer" will try to load
|
||||
that project and re-export all of its targets.
|
||||
*/
|
||||
static int resaveProject (const StringArray& args, bool justSaveResources)
|
||||
{
|
||||
if (! checkArgumentCount (args, 2))
|
||||
return 1;
|
||||
|
||||
LoadedProject proj;
|
||||
|
||||
int res = proj.load (getFile (args[1]));
|
||||
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
std::cout << (justSaveResources ? "Re-saving project resources: "
|
||||
: "Re-saving file: ")
|
||||
<< proj.project->getFile().getFullPathName() << std::endl;
|
||||
|
||||
return proj.save (justSaveResources);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static int setVersion (const StringArray& args)
|
||||
{
|
||||
if (! checkArgumentCount (args, 3))
|
||||
return 1;
|
||||
|
||||
LoadedProject proj;
|
||||
|
||||
int res = proj.load (getFile (args[2]));
|
||||
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
String version (args[1].trim());
|
||||
|
||||
std::cout << "Setting project version: " << version << std::endl;
|
||||
|
||||
proj.project->getVersionValue() = version;
|
||||
|
||||
return proj.save (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static int bumpVersion (const StringArray& args)
|
||||
{
|
||||
if (! checkArgumentCount (args, 2))
|
||||
return 1;
|
||||
|
||||
LoadedProject proj;
|
||||
|
||||
int res = proj.load (getFile (args[1]));
|
||||
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
String version = proj.project->getVersionString();
|
||||
|
||||
version = version.upToLastOccurrenceOf (".", true, false)
|
||||
+ String (version.getTrailingIntValue() + 1);
|
||||
|
||||
std::cout << "Bumping project version to: " << version << std::endl;
|
||||
|
||||
proj.project->getVersionValue() = version;
|
||||
|
||||
return proj.save (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int showStatus (const StringArray& args)
|
||||
{
|
||||
hideDockIcon();
|
||||
|
||||
if (! checkArgumentCount (args, 2))
|
||||
return 1;
|
||||
|
||||
LoadedProject proj;
|
||||
|
||||
int res = proj.load (getFile (args[1]));
|
||||
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
std::cout << "Project file: " << proj.project->getFile().getFullPathName() << std::endl
|
||||
<< "Name: " << proj.project->getTitle() << std::endl
|
||||
<< "UID: " << proj.project->getProjectUID() << std::endl;
|
||||
|
||||
EnabledModuleList& modules = proj.project->getModules();
|
||||
|
||||
const int numModules = modules.getNumModules();
|
||||
if (numModules > 0)
|
||||
{
|
||||
std::cout << "Modules:" << std::endl;
|
||||
|
||||
for (int i = 0; i < numModules; ++i)
|
||||
std::cout << " " << modules.getModuleID (i) << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static String getModulePackageName (const LibraryModule& module)
|
||||
{
|
||||
return module.getID() + ".jucemodule";
|
||||
}
|
||||
|
||||
static int zipModule (const File& targetFolder, const File& moduleFolder)
|
||||
{
|
||||
jassert (targetFolder.isDirectory());
|
||||
|
||||
const File moduleFolderParent (moduleFolder.getParentDirectory());
|
||||
LibraryModule module (moduleFolder.getChildFile (ModuleDescription::getManifestFileName()));
|
||||
|
||||
if (! module.isValid())
|
||||
{
|
||||
std::cout << moduleFolder.getFullPathName() << " is not a valid module folder!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const File targetFile (targetFolder.getChildFile (getModulePackageName (module)));
|
||||
|
||||
ZipFile::Builder zip;
|
||||
|
||||
{
|
||||
DirectoryIterator i (moduleFolder, true, "*", File::findFiles);
|
||||
|
||||
while (i.next())
|
||||
if (! i.getFile().isHidden())
|
||||
zip.addFile (i.getFile(), 9, i.getFile().getRelativePathFrom (moduleFolderParent));
|
||||
}
|
||||
|
||||
std::cout << "Writing: " << targetFile.getFullPathName() << std::endl;
|
||||
|
||||
TemporaryFile temp (targetFile);
|
||||
ScopedPointer<FileOutputStream> out (temp.getFile().createOutputStream());
|
||||
|
||||
bool ok = out != nullptr && zip.writeToStream (*out, nullptr);
|
||||
out = nullptr;
|
||||
ok = ok && temp.overwriteTargetFileWithTemporary();
|
||||
|
||||
if (! ok)
|
||||
{
|
||||
std::cout << "Failed to write to the target file: " << targetFile.getFullPathName() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int buildModules (const StringArray& args, const bool buildAllWithIndex)
|
||||
{
|
||||
hideDockIcon();
|
||||
|
||||
if (! checkArgumentCount (args, 3))
|
||||
return 1;
|
||||
|
||||
const File targetFolder (getFile (args[1]));
|
||||
|
||||
if (! targetFolder.isDirectory())
|
||||
{
|
||||
std::cout << "The first argument must be the directory to put the result." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (buildAllWithIndex)
|
||||
{
|
||||
const File folderToSearch (getFile (args[2]));
|
||||
DirectoryIterator i (folderToSearch, false, "*", File::findDirectories);
|
||||
var infoList;
|
||||
|
||||
while (i.next())
|
||||
{
|
||||
LibraryModule module (i.getFile().getChildFile (ModuleDescription::getManifestFileName()));
|
||||
|
||||
if (module.isValid())
|
||||
{
|
||||
const int result = zipModule (targetFolder, i.getFile());
|
||||
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
var moduleInfo (new DynamicObject());
|
||||
moduleInfo.getDynamicObject()->setProperty ("file", getModulePackageName (module));
|
||||
moduleInfo.getDynamicObject()->setProperty ("info", module.moduleInfo.moduleInfo);
|
||||
infoList.append (moduleInfo);
|
||||
}
|
||||
}
|
||||
|
||||
const File indexFile (targetFolder.getChildFile ("modulelist"));
|
||||
std::cout << "Writing: " << indexFile.getFullPathName() << std::endl;
|
||||
indexFile.replaceWithText (JSON::toString (infoList), false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 2; i < args.size(); ++i)
|
||||
{
|
||||
const int result = zipModule (targetFolder, getFile (args[i]));
|
||||
|
||||
if (result != 0)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static int showHelp()
|
||||
{
|
||||
hideDockIcon();
|
||||
|
||||
std::cout << "The Introjucer!" << std::endl
|
||||
<< std::endl
|
||||
<< "Usage: " << std::endl
|
||||
<< std::endl
|
||||
<< " introjucer --resave project_file" << std::endl
|
||||
<< " Resaves all files and resources in a project." << std::endl
|
||||
<< std::endl
|
||||
<< " introjucer --resave-resources project_file" << std::endl
|
||||
<< " Resaves just the binary resources for a project." << std::endl
|
||||
<< std::endl
|
||||
<< " introjucer --set-version version_number project_file" << std::endl
|
||||
<< " Updates the version number in a project." << std::endl
|
||||
<< std::endl
|
||||
<< " introjucer --bump-version project_file" << std::endl
|
||||
<< " Updates the minor version number in a project by 1." << std::endl
|
||||
<< std::endl
|
||||
<< " introjucer --status project_file" << std::endl
|
||||
<< " Displays information about a project." << std::endl
|
||||
<< std::endl
|
||||
<< " introjucer --buildmodule target_folder module_folder" << std::endl
|
||||
<< " Zips a module into a downloadable file format." << std::endl
|
||||
<< std::endl
|
||||
<< " introjucer --buildallmodules target_folder module_folder" << std::endl
|
||||
<< " Zips all modules in a given folder and creates an index for them." << std::endl
|
||||
<< std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int performCommandLine (const String& commandLine)
|
||||
{
|
||||
StringArray args;
|
||||
args.addTokens (commandLine, true);
|
||||
args.trim();
|
||||
|
||||
if (matchArgument (args[0], "help")) return showHelp();
|
||||
if (matchArgument (args[0], "resave")) return resaveProject (args, false);
|
||||
if (matchArgument (args[0], "resave-resources")) return resaveProject (args, true);
|
||||
if (matchArgument (args[0], "set-version")) return setVersion (args);
|
||||
if (matchArgument (args[0], "bump-version")) return bumpVersion (args);
|
||||
if (matchArgument (args[0], "buildmodule")) return buildModules (args, false);
|
||||
if (matchArgument (args[0], "buildallmodules")) return buildModules (args, true);
|
||||
if (matchArgument (args[0], "status")) return showStatus (args);
|
||||
|
||||
return commandLineNotPerformed;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_COMMANDLINE_JUCEHEADER__
|
||||
#define __JUCER_COMMANDLINE_JUCEHEADER__
|
||||
|
||||
|
||||
int performCommandLine (const String& commandLine);
|
||||
|
||||
enum { commandLineNotPerformed = 0x72346231 };
|
||||
|
||||
|
||||
#endif // __JUCER_COMMANDLINE_JUCEHEADER__
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_COMMONHEADERS_JUCEHEADER__
|
||||
#define __JUCER_COMMONHEADERS_JUCEHEADER__
|
||||
|
||||
|
||||
#include "../Utility/jucer_StoredSettings.h"
|
||||
#include "../Utility/jucer_Icons.h"
|
||||
#include "../Utility/jucer_MiscUtilities.h"
|
||||
#include "../Utility/jucer_CodeHelpers.h"
|
||||
#include "../Utility/jucer_FileHelpers.h"
|
||||
#include "../Utility/jucer_RelativePath.h"
|
||||
#include "../Utility/jucer_ValueSourceHelpers.h"
|
||||
#include "../Utility/jucer_PresetIDs.h"
|
||||
#include "jucer_CommandIDs.h"
|
||||
|
||||
//==============================================================================
|
||||
const char* const projectItemDragType = "Project Items";
|
||||
const char* const drawableItemDragType = "Drawable Items";
|
||||
const char* const componentItemDragType = "Components";
|
||||
|
||||
const char* const sourceFileExtensions = "cpp;mm;m;c;cc;cxx;s;asm";
|
||||
const char* const headerFileExtensions = "h;hpp;hxx;hh;inl";
|
||||
const char* const cOrCppFileExtensions = "cpp;cc;cxx;c";
|
||||
const char* const cppFileExtensions = "cpp;cc;cxx";
|
||||
const char* const objCFileExtensions = "mm;m";
|
||||
const char* const asmFileExtensions = "s;S;asm";
|
||||
const char* const sourceOrHeaderFileExtensions = "cpp;mm;m;c;cc;cxx;s;S;asm;h;hpp;hxx;hh;inl";
|
||||
const char* const fileTypesToCompileByDefault = "cpp;mm;c;m;cc;cxx;s;S;asm;r";
|
||||
|
||||
enum ColourIds
|
||||
{
|
||||
mainBackgroundColourId = 0x2340000,
|
||||
};
|
||||
|
||||
#endif // __JUCER_COMMONHEADERS_JUCEHEADER__
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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_DocumentEditorComponent.h"
|
||||
#include "../Project/jucer_ProjectContentComponent.h"
|
||||
#include "../Application/jucer_Application.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
DocumentEditorComponent::DocumentEditorComponent (OpenDocumentManager::Document* doc)
|
||||
: document (doc)
|
||||
{
|
||||
IntrojucerApp::getApp().openDocumentManager.addListener (this);
|
||||
}
|
||||
|
||||
DocumentEditorComponent::~DocumentEditorComponent()
|
||||
{
|
||||
IntrojucerApp::getApp().openDocumentManager.removeListener (this);
|
||||
}
|
||||
|
||||
bool DocumentEditorComponent::documentAboutToClose (OpenDocumentManager::Document* closingDoc)
|
||||
{
|
||||
if (document == closingDoc)
|
||||
{
|
||||
jassert (document != nullptr);
|
||||
|
||||
if (ProjectContentComponent* pcc = findParentComponentOfClass<ProjectContentComponent>())
|
||||
pcc->hideDocument (document);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DocumentEditorComponent::setEditedState (bool /*hasBeenEdited*/)
|
||||
{
|
||||
if (ProjectContentComponent* pcc = findParentComponentOfClass<ProjectContentComponent>())
|
||||
pcc->updateMainWindowTitle();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_DOCUMENTEDITORCOMPONENT_JUCEHEADER__
|
||||
#define __JUCER_DOCUMENTEDITORCOMPONENT_JUCEHEADER__
|
||||
|
||||
#include "jucer_OpenDocumentManager.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class DocumentEditorComponent : public Component,
|
||||
public OpenDocumentManager::DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DocumentEditorComponent (OpenDocumentManager::Document* document);
|
||||
~DocumentEditorComponent();
|
||||
|
||||
OpenDocumentManager::Document* getDocument() const { return document; }
|
||||
|
||||
bool documentAboutToClose (OpenDocumentManager::Document*) override;
|
||||
|
||||
protected:
|
||||
OpenDocumentManager::Document* document;
|
||||
|
||||
void setEditedState (bool hasBeenEdited);
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DocumentEditorComponent)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_DOCUMENTEDITORCOMPONENT_JUCEHEADER__
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_FILEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
#define __JUCER_FILEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class ItemPreviewComponent : public Component
|
||||
{
|
||||
public:
|
||||
ItemPreviewComponent (const File& f) : file (f)
|
||||
{
|
||||
setOpaque (true);
|
||||
tryToLoadImage();
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
IntrojucerLookAndFeel::fillWithBackgroundTexture (*this, g);
|
||||
|
||||
if (drawable != nullptr)
|
||||
{
|
||||
Rectangle<float> contentBounds (drawable->getDrawableBounds());
|
||||
|
||||
if (DrawableComposite* dc = dynamic_cast<DrawableComposite*> (drawable.get()))
|
||||
{
|
||||
Rectangle<float> r (dc->getContentArea().resolve (nullptr));
|
||||
|
||||
if (! r.isEmpty())
|
||||
contentBounds = r;
|
||||
}
|
||||
|
||||
Rectangle<float> area = RectanglePlacement (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize)
|
||||
.appliedTo (contentBounds, Rectangle<float> (4.0f, 22.0f, getWidth() - 8.0f, getHeight() - 26.0f));
|
||||
|
||||
Path p;
|
||||
p.addRectangle (area);
|
||||
DropShadow (Colours::black.withAlpha (0.5f), 6, Point<int> (0, 1)).drawForPath (g, p);
|
||||
|
||||
g.fillCheckerBoard (area.getSmallestIntegerContainer(), 24, 24,
|
||||
Colour (0xffffffff), Colour (0xffeeeeee));
|
||||
|
||||
drawable->draw (g, 1.0f, RectanglePlacement (RectanglePlacement::stretchToFit)
|
||||
.getTransformToFit (contentBounds, area.toFloat()));
|
||||
}
|
||||
|
||||
g.setFont (Font (14.0f, Font::bold));
|
||||
g.setColour (findColour (mainBackgroundColourId).contrasting());
|
||||
g.drawMultiLineText (facts.joinIntoString ("\n"), 10, 15, getWidth() - 16);
|
||||
}
|
||||
|
||||
private:
|
||||
StringArray facts;
|
||||
File file;
|
||||
ScopedPointer<Drawable> drawable;
|
||||
|
||||
void tryToLoadImage()
|
||||
{
|
||||
facts.clear();
|
||||
facts.add (file.getFullPathName());
|
||||
drawable = nullptr;
|
||||
|
||||
{
|
||||
ScopedPointer<InputStream> input (file.createInputStream());
|
||||
|
||||
if (input != nullptr)
|
||||
{
|
||||
const int64 totalSize = input->getTotalLength();
|
||||
|
||||
String formatName;
|
||||
if (ImageFileFormat* format = ImageFileFormat::findImageFormatForStream (*input))
|
||||
formatName = " " + format->getFormatName();
|
||||
|
||||
input = nullptr;
|
||||
|
||||
Image image (ImageCache::getFromFile (file));
|
||||
|
||||
if (image.isValid())
|
||||
{
|
||||
DrawableImage* d = new DrawableImage();
|
||||
d->setImage (image);
|
||||
drawable = d;
|
||||
|
||||
facts.add (String (image.getWidth()) + " x " + String (image.getHeight()) + formatName);
|
||||
}
|
||||
|
||||
if (totalSize > 0)
|
||||
facts.add (File::descriptionOfSizeInBytes (totalSize));
|
||||
}
|
||||
}
|
||||
|
||||
if (drawable == nullptr)
|
||||
{
|
||||
ScopedPointer<XmlElement> svg (XmlDocument::parse (file));
|
||||
|
||||
if (svg != nullptr)
|
||||
drawable = Drawable::createFromSVG (*svg);
|
||||
}
|
||||
|
||||
facts.removeEmptyStrings (true);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemPreviewComponent)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_FILEPREVIEWCOMPONENT_JUCEHEADER__
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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_Application.h"
|
||||
|
||||
START_JUCE_APPLICATION (IntrojucerApp)
|
||||
@@ -0,0 +1,581 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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_Application.h"
|
||||
#include "jucer_MainWindow.h"
|
||||
#include "jucer_OpenDocumentManager.h"
|
||||
#include "../Code Editor/jucer_SourceCodeEditor.h"
|
||||
#include "../Wizards/jucer_NewProjectWizardClasses.h"
|
||||
#include "../Utility/jucer_JucerTreeViewBase.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
MainWindow::MainWindow()
|
||||
: DocumentWindow (IntrojucerApp::getApp().getApplicationName(),
|
||||
Colour::greyLevel (0.6f),
|
||||
DocumentWindow::allButtons,
|
||||
false)
|
||||
{
|
||||
setUsingNativeTitleBar (true);
|
||||
|
||||
#if ! JUCE_MAC
|
||||
setMenuBar (IntrojucerApp::getApp().menuModel);
|
||||
#endif
|
||||
|
||||
createProjectContentCompIfNeeded();
|
||||
|
||||
setResizable (true, false);
|
||||
centreWithSize (800, 600);
|
||||
|
||||
ApplicationCommandManager& commandManager = IntrojucerApp::getCommandManager();
|
||||
|
||||
// Register all the app commands..
|
||||
commandManager.registerAllCommandsForTarget (this);
|
||||
commandManager.registerAllCommandsForTarget (getProjectContentComponent());
|
||||
|
||||
// update key mappings..
|
||||
{
|
||||
commandManager.getKeyMappings()->resetToDefaultMappings();
|
||||
|
||||
ScopedPointer<XmlElement> keys (getGlobalProperties().getXmlValue ("keyMappings"));
|
||||
|
||||
if (keys != nullptr)
|
||||
commandManager.getKeyMappings()->restoreFromXml (*keys);
|
||||
|
||||
addKeyListener (commandManager.getKeyMappings());
|
||||
}
|
||||
|
||||
// don't want the window to take focus when the title-bar is clicked..
|
||||
setWantsKeyboardFocus (false);
|
||||
|
||||
getLookAndFeel().setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
|
||||
|
||||
setResizeLimits (600, 500, 32000, 32000);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
#if ! JUCE_MAC
|
||||
setMenuBar (nullptr);
|
||||
#endif
|
||||
|
||||
removeKeyListener (IntrojucerApp::getCommandManager().getKeyMappings());
|
||||
|
||||
// save the current size and position to our settings file..
|
||||
getGlobalProperties().setValue ("lastMainWindowPos", getWindowStateAsString());
|
||||
|
||||
clearContentComponent();
|
||||
currentProject = nullptr;
|
||||
}
|
||||
|
||||
void MainWindow::createProjectContentCompIfNeeded()
|
||||
{
|
||||
if (getProjectContentComponent() == nullptr)
|
||||
{
|
||||
clearContentComponent();
|
||||
setContentOwned (IntrojucerApp::getApp().createProjectContentComponent(), false);
|
||||
jassert (getProjectContentComponent() != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::makeVisible()
|
||||
{
|
||||
restoreWindowPosition();
|
||||
setVisible (true);
|
||||
addToDesktop(); // (must add before restoring size so that fullscreen will work)
|
||||
restoreWindowPosition();
|
||||
|
||||
getContentComponent()->grabKeyboardFocus();
|
||||
}
|
||||
|
||||
ProjectContentComponent* MainWindow::getProjectContentComponent() const
|
||||
{
|
||||
return dynamic_cast<ProjectContentComponent*> (getContentComponent());
|
||||
}
|
||||
|
||||
void MainWindow::closeButtonPressed()
|
||||
{
|
||||
IntrojucerApp::getApp().mainWindowList.closeWindow (this);
|
||||
}
|
||||
|
||||
bool MainWindow::closeProject (Project* project)
|
||||
{
|
||||
jassert (project == currentProject && project != nullptr);
|
||||
|
||||
if (project == nullptr)
|
||||
return true;
|
||||
|
||||
project->getStoredProperties().setValue (getProjectWindowPosName(), getWindowStateAsString());
|
||||
|
||||
if (ProjectContentComponent* const pcc = getProjectContentComponent())
|
||||
{
|
||||
pcc->saveTreeViewState();
|
||||
pcc->saveOpenDocumentList();
|
||||
pcc->hideEditor();
|
||||
}
|
||||
|
||||
if (! IntrojucerApp::getApp().openDocumentManager.closeAllDocumentsUsingProject (*project, true))
|
||||
return false;
|
||||
|
||||
FileBasedDocument::SaveResult r = project->saveIfNeededAndUserAgrees();
|
||||
|
||||
if (r == FileBasedDocument::savedOk)
|
||||
{
|
||||
setProject (nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MainWindow::closeCurrentProject()
|
||||
{
|
||||
return currentProject == nullptr || closeProject (currentProject);
|
||||
}
|
||||
|
||||
void MainWindow::setProject (Project* newProject)
|
||||
{
|
||||
createProjectContentCompIfNeeded();
|
||||
getProjectContentComponent()->setProject (newProject);
|
||||
currentProject = newProject;
|
||||
getProjectContentComponent()->updateMainWindowTitle();
|
||||
IntrojucerApp::getCommandManager().commandStatusChanged();
|
||||
}
|
||||
|
||||
void MainWindow::restoreWindowPosition()
|
||||
{
|
||||
String windowState;
|
||||
|
||||
if (currentProject != nullptr)
|
||||
windowState = currentProject->getStoredProperties().getValue (getProjectWindowPosName());
|
||||
|
||||
if (windowState.isEmpty())
|
||||
windowState = getGlobalProperties().getValue ("lastMainWindowPos");
|
||||
|
||||
restoreWindowStateFromString (windowState);
|
||||
}
|
||||
|
||||
bool MainWindow::canOpenFile (const File& file) const
|
||||
{
|
||||
return (! file.isDirectory())
|
||||
&& (file.hasFileExtension (Project::projectFileExtension)
|
||||
|| IntrojucerApp::getApp().openDocumentManager.canOpenFile (file));
|
||||
}
|
||||
|
||||
bool MainWindow::openFile (const File& file)
|
||||
{
|
||||
createProjectContentCompIfNeeded();
|
||||
|
||||
if (file.hasFileExtension (Project::projectFileExtension))
|
||||
{
|
||||
ScopedPointer<Project> newDoc (new Project (file));
|
||||
|
||||
Result result (newDoc->loadFrom (file, true));
|
||||
|
||||
if (result.wasOk() && closeCurrentProject())
|
||||
{
|
||||
setProject (newDoc);
|
||||
newDoc.release()->setChangedFlag (false);
|
||||
|
||||
jassert (getProjectContentComponent() != nullptr);
|
||||
getProjectContentComponent()->reloadLastOpenDocuments();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (file.exists())
|
||||
{
|
||||
return getProjectContentComponent()->showEditorForFile (file, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MainWindow::isInterestedInFileDrag (const StringArray& filenames)
|
||||
{
|
||||
for (int i = filenames.size(); --i >= 0;)
|
||||
if (canOpenFile (File (filenames[i])))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::filesDropped (const StringArray& filenames, int /*mouseX*/, int /*mouseY*/)
|
||||
{
|
||||
for (int i = filenames.size(); --i >= 0;)
|
||||
{
|
||||
const File f (filenames[i]);
|
||||
|
||||
if (canOpenFile (f) && openFile (f))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails,
|
||||
StringArray& files, bool& canMoveFiles)
|
||||
{
|
||||
if (TreeView* tv = dynamic_cast<TreeView*> (sourceDetails.sourceComponent.get()))
|
||||
{
|
||||
Array<JucerTreeViewBase*> selected;
|
||||
|
||||
for (int i = tv->getNumSelectedItems(); --i >= 0;)
|
||||
if (JucerTreeViewBase* b = dynamic_cast<JucerTreeViewBase*> (tv->getSelectedItem(i)))
|
||||
selected.add (b);
|
||||
|
||||
if (selected.size() > 0)
|
||||
{
|
||||
for (int i = selected.size(); --i >= 0;)
|
||||
{
|
||||
if (JucerTreeViewBase* jtvb = selected.getUnchecked(i))
|
||||
{
|
||||
const File f (jtvb->getDraggableFile());
|
||||
|
||||
if (f.existsAsFile())
|
||||
files.add (f.getFullPathName());
|
||||
}
|
||||
}
|
||||
|
||||
canMoveFiles = false;
|
||||
return files.size() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::activeWindowStatusChanged()
|
||||
{
|
||||
DocumentWindow::activeWindowStatusChanged();
|
||||
|
||||
if (ProjectContentComponent* const pcc = getProjectContentComponent())
|
||||
pcc->updateMissingFileStatuses();
|
||||
|
||||
IntrojucerApp::getApp().openDocumentManager.reloadModifiedFiles();
|
||||
}
|
||||
|
||||
void MainWindow::updateTitle (const String& documentName)
|
||||
{
|
||||
String name (IntrojucerApp::getApp().getApplicationName());
|
||||
|
||||
if (currentProject != nullptr)
|
||||
name << " - " << currentProject->getDocumentTitle();
|
||||
|
||||
if (documentName.isNotEmpty())
|
||||
name << " - " << documentName;
|
||||
|
||||
setName (name);
|
||||
}
|
||||
|
||||
void MainWindow::showNewProjectWizard()
|
||||
{
|
||||
jassert (currentProject == nullptr);
|
||||
setContentOwned (createNewProjectWizardComponent(), true);
|
||||
centreWithSize (900, 630);
|
||||
setVisible (true);
|
||||
addToDesktop();
|
||||
getContentComponent()->grabKeyboardFocus();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* MainWindow::getNextCommandTarget()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MainWindow::getAllCommands (Array <CommandID>& commands)
|
||||
{
|
||||
const CommandID ids[] = { CommandIDs::closeWindow };
|
||||
|
||||
commands.addArray (ids, numElementsInArray (ids));
|
||||
}
|
||||
|
||||
void MainWindow::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
|
||||
{
|
||||
switch (commandID)
|
||||
{
|
||||
case CommandIDs::closeWindow:
|
||||
result.setInfo ("Close Window", "Closes the current window", CommandCategories::general, 0);
|
||||
result.defaultKeypresses.add (KeyPress ('w', ModifierKeys::commandModifier, 0));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::perform (const InvocationInfo& info)
|
||||
{
|
||||
switch (info.commandID)
|
||||
{
|
||||
case CommandIDs::closeWindow:
|
||||
closeButtonPressed();
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
MainWindowList::MainWindowList()
|
||||
{
|
||||
}
|
||||
|
||||
void MainWindowList::forceCloseAllWindows()
|
||||
{
|
||||
windows.clear();
|
||||
}
|
||||
|
||||
bool MainWindowList::askAllWindowsToClose()
|
||||
{
|
||||
saveCurrentlyOpenProjectList();
|
||||
|
||||
while (windows.size() > 0)
|
||||
{
|
||||
if (! windows[0]->closeCurrentProject())
|
||||
return false;
|
||||
|
||||
windows.remove (0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWindowList::createWindowIfNoneAreOpen()
|
||||
{
|
||||
if (windows.size() == 0)
|
||||
createNewMainWindow()->showNewProjectWizard();
|
||||
}
|
||||
|
||||
void MainWindowList::closeWindow (MainWindow* w)
|
||||
{
|
||||
jassert (windows.contains (w));
|
||||
|
||||
#if ! JUCE_MAC
|
||||
if (windows.size() == 1)
|
||||
{
|
||||
JUCEApplicationBase::getInstance()->systemRequestedQuit();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (w->closeCurrentProject())
|
||||
{
|
||||
windows.removeObject (w);
|
||||
saveCurrentlyOpenProjectList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindowList::openDocument (OpenDocumentManager::Document* doc, bool grabFocus)
|
||||
{
|
||||
getOrCreateFrontmostWindow()->getProjectContentComponent()->showDocument (doc, grabFocus);
|
||||
}
|
||||
|
||||
bool MainWindowList::openFile (const File& file)
|
||||
{
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
{
|
||||
MainWindow* const w = windows.getUnchecked(i);
|
||||
|
||||
if (w->getProject() != nullptr && w->getProject()->getFile() == file)
|
||||
{
|
||||
w->toFront (true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (file.hasFileExtension (Project::projectFileExtension))
|
||||
{
|
||||
MainWindow* const w = getOrCreateEmptyWindow();
|
||||
bool ok = w->openFile (file);
|
||||
|
||||
w->makeVisible();
|
||||
avoidSuperimposedWindows (w);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
if (file.exists())
|
||||
return getOrCreateFrontmostWindow()->openFile (file);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MainWindow* MainWindowList::createNewMainWindow()
|
||||
{
|
||||
MainWindow* const w = new MainWindow();
|
||||
windows.add (w);
|
||||
w->restoreWindowPosition();
|
||||
avoidSuperimposedWindows (w);
|
||||
return w;
|
||||
}
|
||||
|
||||
MainWindow* MainWindowList::getOrCreateFrontmostWindow()
|
||||
{
|
||||
if (windows.size() == 0)
|
||||
{
|
||||
MainWindow* w = createNewMainWindow();
|
||||
avoidSuperimposedWindows (w);
|
||||
w->makeVisible();
|
||||
return w;
|
||||
}
|
||||
|
||||
for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
|
||||
{
|
||||
MainWindow* mw = dynamic_cast<MainWindow*> (Desktop::getInstance().getComponent (i));
|
||||
if (windows.contains (mw))
|
||||
return mw;
|
||||
}
|
||||
|
||||
return windows.getLast();
|
||||
}
|
||||
|
||||
MainWindow* MainWindowList::getOrCreateEmptyWindow()
|
||||
{
|
||||
if (windows.size() == 0)
|
||||
return createNewMainWindow();
|
||||
|
||||
for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
|
||||
{
|
||||
MainWindow* mw = dynamic_cast<MainWindow*> (Desktop::getInstance().getComponent (i));
|
||||
if (windows.contains (mw) && mw->getProject() == nullptr)
|
||||
return mw;
|
||||
}
|
||||
|
||||
return createNewMainWindow();
|
||||
}
|
||||
|
||||
void MainWindowList::updateAllWindowTitles()
|
||||
{
|
||||
for (int i = 0; i < windows.size(); ++i)
|
||||
if (ProjectContentComponent* pc = windows.getUnchecked(i)->getProjectContentComponent())
|
||||
pc->updateMainWindowTitle();
|
||||
}
|
||||
|
||||
void MainWindowList::avoidSuperimposedWindows (MainWindow* const mw)
|
||||
{
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
{
|
||||
MainWindow* const other = windows.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindowList::saveCurrentlyOpenProjectList()
|
||||
{
|
||||
Array<File> projects;
|
||||
|
||||
Desktop& desktop = Desktop::getInstance();
|
||||
for (int i = 0; i < desktop.getNumComponents(); ++i)
|
||||
{
|
||||
if (MainWindow* const mw = dynamic_cast<MainWindow*> (desktop.getComponent(i)))
|
||||
if (Project* p = mw->getProject())
|
||||
projects.add (p->getFile());
|
||||
}
|
||||
|
||||
getAppSettings().setLastProjects (projects);
|
||||
}
|
||||
|
||||
void MainWindowList::reopenLastProjects()
|
||||
{
|
||||
Array<File> projects (getAppSettings().getLastProjects());
|
||||
|
||||
for (int i = 0; i < projects.size(); ++ i)
|
||||
openFile (projects.getReference(i));
|
||||
}
|
||||
|
||||
void MainWindowList::sendLookAndFeelChange()
|
||||
{
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
windows.getUnchecked(i)->sendLookAndFeelChange();
|
||||
}
|
||||
|
||||
Project* MainWindowList::getFrontmostProject()
|
||||
{
|
||||
Desktop& desktop = Desktop::getInstance();
|
||||
|
||||
for (int i = desktop.getNumComponents(); --i >= 0;)
|
||||
if (MainWindow* const mw = dynamic_cast<MainWindow*> (desktop.getComponent(i)))
|
||||
if (Project* p = mw->getProject())
|
||||
return p;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
File findDefaultModulesFolder (bool mustContainJuceCoreModule)
|
||||
{
|
||||
const MainWindowList& windows = IntrojucerApp::getApp().mainWindowList;
|
||||
|
||||
for (int i = windows.windows.size(); --i >= 0;)
|
||||
{
|
||||
if (Project* p = windows.windows.getUnchecked (i)->getProject())
|
||||
{
|
||||
const File f (EnabledModuleList::findDefaultModulesFolder (*p));
|
||||
|
||||
if (isJuceModulesFolder (f) || (f.isDirectory() && ! mustContainJuceCoreModule))
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
if (mustContainJuceCoreModule)
|
||||
return findDefaultModulesFolder (false);
|
||||
|
||||
File f (File::getSpecialLocation (File::currentApplicationFile));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
File parent (f.getParentDirectory());
|
||||
|
||||
if (parent == f || ! parent.isDirectory())
|
||||
break;
|
||||
|
||||
if (isJuceFolder (parent))
|
||||
return parent.getChildFile ("modules");
|
||||
|
||||
f = parent;
|
||||
}
|
||||
|
||||
return File::nonexistent;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_MAINWINDOW_JUCEHEADER__
|
||||
#define __JUCER_MAINWINDOW_JUCEHEADER__
|
||||
|
||||
#include "../Project/jucer_ProjectContentComponent.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
The big top-level window where everything happens.
|
||||
*/
|
||||
class MainWindow : public DocumentWindow,
|
||||
public ApplicationCommandTarget,
|
||||
public FileDragAndDropTarget,
|
||||
public DragAndDropContainer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
MainWindow();
|
||||
~MainWindow();
|
||||
|
||||
//==============================================================================
|
||||
void closeButtonPressed() override;
|
||||
|
||||
//==============================================================================
|
||||
bool canOpenFile (const File& file) const;
|
||||
bool openFile (const File& file);
|
||||
void setProject (Project* newProject);
|
||||
Project* getProject() const { return currentProject; }
|
||||
|
||||
void makeVisible();
|
||||
void restoreWindowPosition();
|
||||
bool closeProject (Project* project);
|
||||
bool closeCurrentProject();
|
||||
|
||||
void showNewProjectWizard();
|
||||
|
||||
bool isInterestedInFileDrag (const StringArray& files) override;
|
||||
void filesDropped (const StringArray& filenames, int mouseX, int mouseY) override;
|
||||
|
||||
void activeWindowStatusChanged() override;
|
||||
|
||||
void updateTitle (const String& documentName);
|
||||
|
||||
ProjectContentComponent* getProjectContentComponent() const;
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* getNextCommandTarget() override;
|
||||
void getAllCommands (Array <CommandID>& commands) override;
|
||||
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override;
|
||||
bool perform (const InvocationInfo& info) override;
|
||||
|
||||
bool shouldDropFilesWhenDraggedExternally (const DragAndDropTarget::SourceDetails& sourceDetails,
|
||||
StringArray& files, bool& canMoveFiles) override;
|
||||
private:
|
||||
ScopedPointer <Project> currentProject;
|
||||
|
||||
static const char* getProjectWindowPosName() { return "projectWindowPos"; }
|
||||
void createProjectContentCompIfNeeded();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class MainWindowList
|
||||
{
|
||||
public:
|
||||
MainWindowList();
|
||||
|
||||
void forceCloseAllWindows();
|
||||
bool askAllWindowsToClose();
|
||||
void closeWindow (MainWindow*);
|
||||
|
||||
void createWindowIfNoneAreOpen();
|
||||
void openDocument (OpenDocumentManager::Document*, bool grabFocus);
|
||||
bool openFile (const File& file);
|
||||
|
||||
MainWindow* createNewMainWindow();
|
||||
MainWindow* getOrCreateFrontmostWindow();
|
||||
MainWindow* getOrCreateEmptyWindow();
|
||||
|
||||
Project* getFrontmostProject();
|
||||
|
||||
void reopenLastProjects();
|
||||
void saveCurrentlyOpenProjectList();
|
||||
|
||||
void updateAllWindowTitles();
|
||||
|
||||
void avoidSuperimposedWindows (MainWindow*);
|
||||
|
||||
void sendLookAndFeelChange();
|
||||
|
||||
OwnedArray<MainWindow> windows;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindowList)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_MAINWINDOW_JUCEHEADER__
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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_OpenDocumentManager.h"
|
||||
#include "jucer_FilePreviewComponent.h"
|
||||
#include "../Code Editor/jucer_SourceCodeEditor.h"
|
||||
#include "jucer_Application.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class UnknownDocument : public OpenDocumentManager::Document
|
||||
{
|
||||
public:
|
||||
UnknownDocument (Project* p, const File& f)
|
||||
: project (p), file (f)
|
||||
{
|
||||
reloadFromFile();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct Type : public OpenDocumentManager::DocumentType
|
||||
{
|
||||
bool canOpenFile (const File&) override { return true; }
|
||||
Document* openFile (Project* p, const File& f) override { return new UnknownDocument (p, f); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
bool loadedOk() const override { return true; }
|
||||
bool isForFile (const File& f) const override { return file == f; }
|
||||
bool isForNode (const ValueTree&) const override { return false; }
|
||||
bool refersToProject (Project& p) const override { return project == &p; }
|
||||
Project* getProject() const override { return project; }
|
||||
bool needsSaving() const override { return false; }
|
||||
bool save() override { return true; }
|
||||
bool saveAs() override { return false; }
|
||||
bool hasFileBeenModifiedExternally() override { return fileModificationTime != file.getLastModificationTime(); }
|
||||
void reloadFromFile() override { fileModificationTime = file.getLastModificationTime(); }
|
||||
String getName() const override { return file.getFileName(); }
|
||||
File getFile() const override { return file; }
|
||||
Component* createEditor() override { return new ItemPreviewComponent (file); }
|
||||
Component* createViewer() override { return createEditor(); }
|
||||
void fileHasBeenRenamed (const File& newFile) override { file = newFile; }
|
||||
String getState() const override { return String::empty; }
|
||||
void restoreState (const String&) override {}
|
||||
|
||||
String getType() const override
|
||||
{
|
||||
if (file.getFileExtension().isNotEmpty())
|
||||
return file.getFileExtension() + " file";
|
||||
|
||||
jassertfalse;
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private:
|
||||
Project* const project;
|
||||
File file;
|
||||
Time fileModificationTime;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnknownDocument)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
OpenDocumentManager::DocumentType* createGUIDocumentType();
|
||||
|
||||
OpenDocumentManager::OpenDocumentManager()
|
||||
{
|
||||
registerType (new UnknownDocument::Type());
|
||||
registerType (new SourceCodeDocument::Type());
|
||||
registerType (createGUIDocumentType());
|
||||
}
|
||||
|
||||
OpenDocumentManager::~OpenDocumentManager()
|
||||
{
|
||||
}
|
||||
|
||||
void OpenDocumentManager::clear()
|
||||
{
|
||||
documents.clear();
|
||||
types.clear();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void OpenDocumentManager::registerType (DocumentType* type)
|
||||
{
|
||||
types.add (type);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void OpenDocumentManager::addListener (DocumentCloseListener* listener)
|
||||
{
|
||||
listeners.addIfNotAlreadyThere (listener);
|
||||
}
|
||||
|
||||
void OpenDocumentManager::removeListener (DocumentCloseListener* listener)
|
||||
{
|
||||
listeners.removeFirstMatchingValue (listener);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool OpenDocumentManager::canOpenFile (const File& file)
|
||||
{
|
||||
for (int i = types.size(); --i >= 0;)
|
||||
if (types.getUnchecked(i)->canOpenFile (file))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
OpenDocumentManager::Document* OpenDocumentManager::openFile (Project* project, const File& file)
|
||||
{
|
||||
for (int i = documents.size(); --i >= 0;)
|
||||
if (documents.getUnchecked(i)->isForFile (file))
|
||||
return documents.getUnchecked(i);
|
||||
|
||||
Document* d = nullptr;
|
||||
|
||||
for (int i = types.size(); --i >= 0 && d == nullptr;)
|
||||
{
|
||||
if (types.getUnchecked(i)->canOpenFile (file))
|
||||
{
|
||||
d = types.getUnchecked(i)->openFile (project, file);
|
||||
jassert (d != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
jassert (d != nullptr); // should always at least have been picked up by UnknownDocument
|
||||
|
||||
documents.add (d);
|
||||
IntrojucerApp::getCommandManager().commandStatusChanged();
|
||||
return d;
|
||||
}
|
||||
|
||||
int OpenDocumentManager::getNumOpenDocuments() const
|
||||
{
|
||||
return documents.size();
|
||||
}
|
||||
|
||||
OpenDocumentManager::Document* OpenDocumentManager::getOpenDocument (int index) const
|
||||
{
|
||||
return documents.getUnchecked (index);
|
||||
}
|
||||
|
||||
FileBasedDocument::SaveResult OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Document* doc)
|
||||
{
|
||||
if (! doc->needsSaving())
|
||||
return FileBasedDocument::savedOk;
|
||||
|
||||
const int r = AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon,
|
||||
TRANS("Closing document..."),
|
||||
TRANS("Do you want to save the changes to \"")
|
||||
+ doc->getName() + "\"?",
|
||||
TRANS("Save"),
|
||||
TRANS("Discard changes"),
|
||||
TRANS("Cancel"));
|
||||
|
||||
if (r == 1) // save changes
|
||||
return doc->save() ? FileBasedDocument::savedOk
|
||||
: FileBasedDocument::failedToWriteToFile;
|
||||
|
||||
if (r == 2) // discard changes
|
||||
return FileBasedDocument::savedOk;
|
||||
|
||||
return FileBasedDocument::userCancelledSave;
|
||||
}
|
||||
|
||||
|
||||
bool OpenDocumentManager::closeDocument (int index, bool saveIfNeeded)
|
||||
{
|
||||
if (Document* doc = documents [index])
|
||||
{
|
||||
if (saveIfNeeded)
|
||||
if (saveIfNeededAndUserAgrees (doc) != FileBasedDocument::savedOk)
|
||||
return false;
|
||||
|
||||
bool canClose = true;
|
||||
|
||||
for (int i = listeners.size(); --i >= 0;)
|
||||
if (DocumentCloseListener* l = listeners[i])
|
||||
if (! l->documentAboutToClose (doc))
|
||||
canClose = false;
|
||||
|
||||
if (! canClose)
|
||||
return false;
|
||||
|
||||
documents.remove (index);
|
||||
IntrojucerApp::getCommandManager().commandStatusChanged();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenDocumentManager::closeDocument (Document* document, bool saveIfNeeded)
|
||||
{
|
||||
return closeDocument (documents.indexOf (document), saveIfNeeded);
|
||||
}
|
||||
|
||||
void OpenDocumentManager::closeFile (const File& f, bool saveIfNeeded)
|
||||
{
|
||||
for (int i = documents.size(); --i >= 0;)
|
||||
if (Document* d = documents[i])
|
||||
if (d->isForFile (f))
|
||||
closeDocument (i, saveIfNeeded);
|
||||
}
|
||||
|
||||
bool OpenDocumentManager::closeAll (bool askUserToSave)
|
||||
{
|
||||
for (int i = getNumOpenDocuments(); --i >= 0;)
|
||||
if (! closeDocument (i, askUserToSave))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded)
|
||||
{
|
||||
for (int i = documents.size(); --i >= 0;)
|
||||
if (Document* d = documents[i])
|
||||
if (d->refersToProject (project))
|
||||
if (! closeDocument (i, saveIfNeeded))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenDocumentManager::anyFilesNeedSaving() const
|
||||
{
|
||||
for (int i = documents.size(); --i >= 0;)
|
||||
if (documents.getUnchecked (i)->needsSaving())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenDocumentManager::saveAll()
|
||||
{
|
||||
for (int i = documents.size(); --i >= 0;)
|
||||
{
|
||||
if (! documents.getUnchecked (i)->save())
|
||||
return false;
|
||||
|
||||
IntrojucerApp::getApp().mainWindowList.updateAllWindowTitles();
|
||||
IntrojucerApp::getCommandManager().commandStatusChanged();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenDocumentManager::reloadModifiedFiles()
|
||||
{
|
||||
for (int i = documents.size(); --i >= 0;)
|
||||
{
|
||||
Document* d = documents.getUnchecked (i);
|
||||
|
||||
if (d->hasFileBeenModifiedExternally())
|
||||
d->reloadFromFile();
|
||||
}
|
||||
}
|
||||
|
||||
void OpenDocumentManager::fileHasBeenRenamed (const File& oldFile, const File& newFile)
|
||||
{
|
||||
for (int i = documents.size(); --i >= 0;)
|
||||
{
|
||||
Document* d = documents.getUnchecked (i);
|
||||
|
||||
if (d->isForFile (oldFile))
|
||||
d->fileHasBeenRenamed (newFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
RecentDocumentList::RecentDocumentList()
|
||||
{
|
||||
IntrojucerApp::getApp().openDocumentManager.addListener (this);
|
||||
}
|
||||
|
||||
RecentDocumentList::~RecentDocumentList()
|
||||
{
|
||||
IntrojucerApp::getApp().openDocumentManager.removeListener (this);
|
||||
}
|
||||
|
||||
void RecentDocumentList::clear()
|
||||
{
|
||||
previousDocs.clear();
|
||||
nextDocs.clear();
|
||||
}
|
||||
|
||||
void RecentDocumentList::newDocumentOpened (OpenDocumentManager::Document* document)
|
||||
{
|
||||
if (document != nullptr && document != getCurrentDocument())
|
||||
{
|
||||
nextDocs.clear();
|
||||
previousDocs.add (document);
|
||||
}
|
||||
}
|
||||
|
||||
bool RecentDocumentList::canGoToPrevious() const
|
||||
{
|
||||
return previousDocs.size() > 1;
|
||||
}
|
||||
|
||||
bool RecentDocumentList::canGoToNext() const
|
||||
{
|
||||
return nextDocs.size() > 0;
|
||||
}
|
||||
|
||||
OpenDocumentManager::Document* RecentDocumentList::getPrevious()
|
||||
{
|
||||
if (! canGoToPrevious())
|
||||
return nullptr;
|
||||
|
||||
nextDocs.insert (0, previousDocs.remove (previousDocs.size() - 1));
|
||||
return previousDocs.getLast();
|
||||
}
|
||||
|
||||
OpenDocumentManager::Document* RecentDocumentList::getNext()
|
||||
{
|
||||
if (! canGoToNext())
|
||||
return nullptr;
|
||||
|
||||
OpenDocumentManager::Document* d = nextDocs.remove (0);
|
||||
previousDocs.add (d);
|
||||
return d;
|
||||
}
|
||||
|
||||
OpenDocumentManager::Document* RecentDocumentList::getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const
|
||||
{
|
||||
for (int i = previousDocs.size(); --i >= 0;)
|
||||
if (previousDocs.getUnchecked(i) != oneToAvoid)
|
||||
return previousDocs.getUnchecked(i);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool RecentDocumentList::documentAboutToClose (OpenDocumentManager::Document* document)
|
||||
{
|
||||
previousDocs.removeAllInstancesOf (document);
|
||||
nextDocs.removeAllInstancesOf (document);
|
||||
|
||||
jassert (! previousDocs.contains (document));
|
||||
jassert (! nextDocs.contains (document));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void restoreDocList (Project& project, Array <OpenDocumentManager::Document*>& list, const XmlElement* xml)
|
||||
{
|
||||
if (xml != nullptr)
|
||||
{
|
||||
OpenDocumentManager& odm = IntrojucerApp::getApp().openDocumentManager;
|
||||
|
||||
forEachXmlChildElementWithTagName (*xml, e, "DOC")
|
||||
{
|
||||
const File file (e->getStringAttribute ("file"));
|
||||
|
||||
if (file.exists())
|
||||
{
|
||||
if (OpenDocumentManager::Document* doc = odm.openFile (&project, file))
|
||||
{
|
||||
doc->restoreState (e->getStringAttribute ("state"));
|
||||
|
||||
list.add (doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RecentDocumentList::restoreFromXML (Project& project, const XmlElement& xml)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (xml.hasTagName ("RECENT_DOCUMENTS"))
|
||||
{
|
||||
restoreDocList (project, previousDocs, xml.getChildByName ("PREVIOUS"));
|
||||
restoreDocList (project, nextDocs, xml.getChildByName ("NEXT"));
|
||||
}
|
||||
}
|
||||
|
||||
static void saveDocList (const Array <OpenDocumentManager::Document*>& list, XmlElement& xml)
|
||||
{
|
||||
for (int i = 0; i < list.size(); ++i)
|
||||
{
|
||||
const OpenDocumentManager::Document& doc = *list.getUnchecked(i);
|
||||
|
||||
XmlElement* e = xml.createNewChildElement ("DOC");
|
||||
|
||||
e->setAttribute ("file", doc.getFile().getFullPathName());
|
||||
e->setAttribute ("state", doc.getState());
|
||||
}
|
||||
}
|
||||
|
||||
XmlElement* RecentDocumentList::createXML() const
|
||||
{
|
||||
XmlElement* xml = new XmlElement ("RECENT_DOCUMENTS");
|
||||
|
||||
saveDocList (previousDocs, *xml->createNewChildElement ("PREVIOUS"));
|
||||
saveDocList (nextDocs, *xml->createNewChildElement ("NEXT"));
|
||||
|
||||
return xml;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software 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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_OPENDOCUMENTMANAGER_JUCEHEADER__
|
||||
#define __JUCER_OPENDOCUMENTMANAGER_JUCEHEADER__
|
||||
|
||||
#include "../Project/jucer_Project.h"
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
class OpenDocumentManager
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
OpenDocumentManager();
|
||||
~OpenDocumentManager();
|
||||
|
||||
//==============================================================================
|
||||
class Document
|
||||
{
|
||||
public:
|
||||
Document() {}
|
||||
virtual ~Document() {}
|
||||
|
||||
virtual bool loadedOk() const = 0;
|
||||
virtual bool isForFile (const File& file) const = 0;
|
||||
virtual bool isForNode (const ValueTree& node) const = 0;
|
||||
virtual bool refersToProject (Project& project) const = 0;
|
||||
virtual Project* getProject() const = 0;
|
||||
virtual String getName() const = 0;
|
||||
virtual String getType() const = 0;
|
||||
virtual File getFile() const = 0;
|
||||
virtual bool needsSaving() const = 0;
|
||||
virtual bool save() = 0;
|
||||
virtual bool saveAs() = 0;
|
||||
virtual bool hasFileBeenModifiedExternally() = 0;
|
||||
virtual void reloadFromFile() = 0;
|
||||
virtual Component* createEditor() = 0;
|
||||
virtual Component* createViewer() = 0;
|
||||
virtual void fileHasBeenRenamed (const File& newFile) = 0;
|
||||
virtual String getState() const = 0;
|
||||
virtual void restoreState (const String& state) = 0;
|
||||
virtual File getCounterpartFile() const { return File::nonexistent; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
int getNumOpenDocuments() const;
|
||||
Document* getOpenDocument (int index) const;
|
||||
void clear();
|
||||
|
||||
bool canOpenFile (const File& file);
|
||||
Document* openFile (Project* project, const File& file);
|
||||
bool closeDocument (int index, bool saveIfNeeded);
|
||||
bool closeDocument (Document* document, bool saveIfNeeded);
|
||||
bool closeAll (bool askUserToSave);
|
||||
bool closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded);
|
||||
void closeFile (const File& f, bool saveIfNeeded);
|
||||
bool anyFilesNeedSaving() const;
|
||||
bool saveAll();
|
||||
FileBasedDocument::SaveResult saveIfNeededAndUserAgrees (Document* doc);
|
||||
void reloadModifiedFiles();
|
||||
void fileHasBeenRenamed (const File& oldFile, const File& newFile);
|
||||
|
||||
//==============================================================================
|
||||
class DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
DocumentCloseListener() {}
|
||||
virtual ~DocumentCloseListener() {}
|
||||
|
||||
// return false to force it to stop.
|
||||
virtual bool documentAboutToClose (Document* document) = 0;
|
||||
};
|
||||
|
||||
void addListener (DocumentCloseListener*);
|
||||
void removeListener (DocumentCloseListener*);
|
||||
|
||||
//==============================================================================
|
||||
class DocumentType
|
||||
{
|
||||
public:
|
||||
DocumentType() {}
|
||||
virtual ~DocumentType() {}
|
||||
|
||||
virtual bool canOpenFile (const File& file) = 0;
|
||||
virtual Document* openFile (Project* project, const File& file) = 0;
|
||||
};
|
||||
|
||||
void registerType (DocumentType* type);
|
||||
|
||||
|
||||
private:
|
||||
OwnedArray<DocumentType> types;
|
||||
OwnedArray<Document> documents;
|
||||
Array<DocumentCloseListener*> listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenDocumentManager)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class RecentDocumentList : private OpenDocumentManager::DocumentCloseListener
|
||||
{
|
||||
public:
|
||||
RecentDocumentList();
|
||||
~RecentDocumentList();
|
||||
|
||||
void clear();
|
||||
|
||||
void newDocumentOpened (OpenDocumentManager::Document* document);
|
||||
|
||||
OpenDocumentManager::Document* getCurrentDocument() const { return previousDocs.getLast(); }
|
||||
|
||||
bool canGoToPrevious() const;
|
||||
bool canGoToNext() const;
|
||||
|
||||
OpenDocumentManager::Document* getPrevious();
|
||||
OpenDocumentManager::Document* getNext();
|
||||
|
||||
OpenDocumentManager::Document* getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const;
|
||||
|
||||
void restoreFromXML (Project& project, const XmlElement& xml);
|
||||
XmlElement* createXML() const;
|
||||
|
||||
private:
|
||||
bool documentAboutToClose (OpenDocumentManager::Document*);
|
||||
|
||||
Array<OpenDocumentManager::Document*> previousDocs, nextDocs;
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_OPENDOCUMENTMANAGER_JUCEHEADER__
|
||||
Reference in New Issue
Block a user