2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-10 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../jucer_Headers.h"
27 #include "jucer_JuceUpdater.h"
30 //==============================================================================
31 JuceUpdater::JuceUpdater()
32 : filenameComp ("Juce Folder", StoredSettings::getInstance()->getLastKnownJuceFolder(),
33 true, true, false, "*", String::empty
, "Select your Juce folder"),
34 checkNowButton ("Check Online for Available Updates...",
35 "Contacts the website to see if this version is up-to-date")
37 addAndMakeVisible (&label
);
38 addAndMakeVisible (&filenameComp
);
39 addAndMakeVisible (&checkNowButton
);
40 addAndMakeVisible (¤tVersionLabel
);
41 checkNowButton
.addListener (this);
42 filenameComp
.addListener (this);
44 currentVersionLabel
.setFont (Font (14.0f
, Font::italic
));
45 label
.setFont (Font (12.0f
));
46 label
.setText ("Destination folder:", false);
48 addAndMakeVisible (&availableVersionsList
);
49 availableVersionsList
.setModel (this);
54 JuceUpdater::~JuceUpdater()
56 checkNowButton
.removeListener (this);
57 filenameComp
.removeListener (this);
60 void JuceUpdater::show (Component
* mainWindow
)
63 DialogWindow::showModalDialog ("Juce Update...", &updater
, mainWindow
,
68 void JuceUpdater::resized()
70 filenameComp
.setBounds (20, 40, getWidth() - 40, 22);
71 label
.setBounds (filenameComp
.getX(), filenameComp
.getY() - 18, filenameComp
.getWidth(), 18);
72 currentVersionLabel
.setBounds (filenameComp
.getX(), filenameComp
.getBottom(), filenameComp
.getWidth(), 25);
73 checkNowButton
.changeWidthToFitText (20);
74 checkNowButton
.setCentrePosition (getWidth() / 2, filenameComp
.getBottom() + 40);
75 availableVersionsList
.setBounds (filenameComp
.getX(), checkNowButton
.getBottom() + 20, filenameComp
.getWidth(), getHeight() - (checkNowButton
.getBottom() + 20));
78 void JuceUpdater::paint (Graphics
& g
)
80 g
.fillAll (Colours::white
);
83 const String
findVersionNum (const String
& file
, const String
& token
)
85 return file
.fromFirstOccurrenceOf (token
, false, false)
86 .upToFirstOccurrenceOf ("\n", false, false)
90 const String
JuceUpdater::getCurrentVersion()
92 const String
header (filenameComp
.getCurrentFile()
93 .getChildFile ("src/core/juce_StandardHeader.h").loadFileAsString());
95 const String
v1 (findVersionNum (header
, "JUCE_MAJOR_VERSION"));
96 const String
v2 (findVersionNum (header
, "JUCE_MINOR_VERSION"));
97 const String
v3 (findVersionNum (header
, "JUCE_BUILDNUMBER"));
99 if ((v1
+ v2
+ v3
).isEmpty())
100 return String::empty
;
102 return v1
+ "." + v2
+ "." + v3
;
105 XmlElement
* JuceUpdater::downloadVersionList()
107 return URL ("http://www.rawmaterialsoftware.com/juce/downloads/juce_versions.php").readEntireXmlStream();
110 void JuceUpdater::updateVersions (const XmlElement
& xml
)
112 availableVersions
.clear();
114 forEachXmlChildElementWithTagName (xml
, v
, "VERSION")
116 VersionInfo
* vi
= new VersionInfo();
117 vi
->url
= URL (v
->getStringAttribute ("url"));
118 vi
->desc
= v
->getStringAttribute ("desc");
119 vi
->version
= v
->getStringAttribute ("version");
120 vi
->date
= v
->getStringAttribute ("date");
121 availableVersions
.add (vi
);
124 availableVersionsList
.updateContent();
127 void JuceUpdater::buttonClicked (Button
*)
129 ScopedPointer
<XmlElement
> xml (downloadVersionList());
131 if (xml
== nullptr || xml
->hasTagName ("html"))
133 AlertWindow::showMessageBox (AlertWindow::WarningIcon
, "Connection Problems...",
134 "Couldn't connect to the Raw Material Software website!");
139 if (! xml
->hasTagName ("JUCEVERSIONS"))
141 AlertWindow::showMessageBox (AlertWindow::WarningIcon
, "Update Problems...",
142 "This version of the Jucer may be too old to receive automatic updates!\n\n"
143 "Please visit www.rawmaterialsoftware.com and get the latest version manually!");
147 const String
currentVersion (getCurrentVersion());
149 OwnedArray
<VersionInfo
> versions
;
150 updateVersions (*xml
);
153 //==============================================================================
154 class NewVersionDownloader
: public ThreadWithProgressWindow
157 NewVersionDownloader (const String
& title
, const URL
& url_
, const File
& target_
)
158 : ThreadWithProgressWindow (title
, true, true),
159 url (url_
), target (target_
)
165 setStatusMessage ("Contacting website...");
167 ScopedPointer
<InputStream
> input (url
.createInputStream (false));
169 if (input
== nullptr)
171 error
= "Couldn't connect to the website...";
175 if (! target
.deleteFile())
177 error
= "Couldn't delete the destination file...";
181 ScopedPointer
<OutputStream
> output (target
.createOutputStream (32768));
183 if (output
== nullptr)
185 error
= "Couldn't write to the destination file...";
189 setStatusMessage ("Downloading...");
191 int totalBytes
= (int) input
->getTotalLength();
194 while (! (input
->isExhausted() || threadShouldExit()))
196 HeapBlock
<char> buffer (8192);
197 const int num
= input
->read (buffer
, 8192);
202 output
->write (buffer
, num
);
205 setProgress (totalBytes
> 0 ? bytesSoFar
/ (double) totalBytes
: -1.0);
216 //==============================================================================
217 class Unzipper
: public ThreadWithProgressWindow
220 Unzipper (ZipFile
& zipFile_
, const File
& targetDir_
)
221 : ThreadWithProgressWindow ("Unzipping...", true, true),
222 worked (true), zipFile (zipFile_
), targetDir (targetDir_
)
228 for (int i
= 0; i
< zipFile
.getNumEntries(); ++i
)
230 if (threadShouldExit())
233 const ZipFile::ZipEntry
* e
= zipFile
.getEntry (i
);
234 setStatusMessage ("Unzipping " + e
->filename
+ "...");
235 setProgress (i
/ (double) zipFile
.getNumEntries());
237 worked
= zipFile
.uncompressEntry (i
, targetDir
, true) && worked
;
248 //==============================================================================
249 void JuceUpdater::applyVersion (VersionInfo
* version
)
251 File
destDir (filenameComp
.getCurrentFile());
253 const bool destDirExisted
= destDir
.isDirectory();
255 if (destDirExisted
&& destDir
.getNumberOfChildFiles (File::findFilesAndDirectories
, "*") > 0)
257 int r
= AlertWindow::showYesNoCancelBox (AlertWindow::WarningIcon
, "Folder already exists",
258 "The folder " + destDir
.getFullPathName() + "\nalready contains some files...\n\n"
259 "Do you want to delete everything in the folder and replace it entirely, or just merge the new files into the existing folder?",
260 "Delete and replace entire folder",
261 "Add and overwrite existing files",
269 if (! destDir
.deleteRecursively())
271 AlertWindow::showMessageBox (AlertWindow::WarningIcon
, "Problems...",
272 "Couldn't delete the existing folder!");
278 if (! (destDir
.isDirectory() || destDir
.createDirectory()))
280 AlertWindow::showMessageBox (AlertWindow::WarningIcon
, "Problems...",
281 "Couldn't create that target folder..");
285 File
zipFile (destDir
.getNonexistentChildFile ("juce_download", ".tar.gz", false));
290 NewVersionDownloader
downloader ("Downloading Version " + version
->version
+ "...",
291 version
->url
, zipFile
);
292 worked
= downloader
.runThread();
297 ZipFile
zip (zipFile
);
298 Unzipper
unzipper (zip
, destDir
);
299 worked
= unzipper
.runThread() && unzipper
.worked
;
302 zipFile
.deleteFile();
304 if ((! destDirExisted
) && (destDir
.getNumberOfChildFiles (File::findFilesAndDirectories
, "*") == 0 || ! worked
))
305 destDir
.deleteRecursively();
307 filenameComponentChanged (&filenameComp
);
310 void JuceUpdater::filenameComponentChanged (FilenameComponent
*)
312 const String
version (getCurrentVersion());
314 if (version
.isEmpty())
315 currentVersionLabel
.setText ("(Not a Juce folder)", false);
317 currentVersionLabel
.setText ("(Current version in this folder: " + version
+ ")", false);
320 //==============================================================================
321 int JuceUpdater::getNumRows()
323 return availableVersions
.size();
326 void JuceUpdater::paintListBoxItem (int rowNumber
, Graphics
& g
, int width
, int height
, bool rowIsSelected
)
329 g
.fillAll (findColour (TextEditor::highlightColourId
));
332 Component
* JuceUpdater::refreshComponentForRow (int rowNumber
, bool isRowSelected
, Component
* existingComponentToUpdate
)
334 class UpdateListComponent
: public Component
,
335 public ButtonListener
338 UpdateListComponent (JuceUpdater
& updater_
)
339 : updater (updater_
),
341 applyButton ("Install this version...")
343 addAndMakeVisible (&applyButton
);
344 applyButton
.addListener (this);
345 setInterceptsMouseClicks (false, true);
348 ~UpdateListComponent()
350 applyButton
.removeListener (this);
353 void setVersion (VersionInfo
* v
)
363 void paint (Graphics
& g
)
365 if (version
!= nullptr)
367 g
.setColour (Colours::green
.withAlpha (0.12f
));
369 g
.fillRect (0, 1, getWidth(), getHeight() - 2);
370 g
.setColour (Colours::black
);
371 g
.setFont (getHeight() * 0.7f
);
374 s
<< "Version " << version
->version
<< " - " << version
->desc
<< " - " << version
->date
;
376 g
.drawText (s
, 4, 0, applyButton
.getX() - 4, getHeight(), Justification::centredLeft
, true);
382 applyButton
.changeWidthToFitText (getHeight() - 4);
383 applyButton
.setTopRightPosition (getWidth(), 2);
384 applyButton
.setVisible (version
!= nullptr);
387 void buttonClicked (Button
*)
389 updater
.applyVersion (version
);
393 JuceUpdater
& updater
;
394 VersionInfo
* version
;
395 TextButton applyButton
;
398 UpdateListComponent
* c
= dynamic_cast <UpdateListComponent
*> (existingComponentToUpdate
);
400 c
= new UpdateListComponent (*this);
402 c
->setVersion (availableVersions
[rowNumber
]);