- clean working copy for leaving SVN
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user