diff --git a/appcast-snapshots.xml.in b/appcast-snapshots.xml.in
new file mode 100644
index 00000000..31035655
--- /dev/null
+++ b/appcast-snapshots.xml.in
@@ -0,0 +1,18 @@
+
+
+
+ OpenSCAD Development Snapshots
+ http://openscad.org/appcast-snapshots.xml
+ en
+ -
+ OpenSCAD @VERSION@
+ @VERSIONDATE@
+ https://raw.github.com/openscad/openscad/master/RELEASE_NOTES
+
+
+
+
diff --git a/appcast.xml.in b/appcast.xml.in
new file mode 100644
index 00000000..e375fae7
--- /dev/null
+++ b/appcast.xml.in
@@ -0,0 +1,18 @@
+
+
+
+ OpenSCAD Updates
+ http://openscad.org/appcast.xml
+ en
+ -
+ OpenSCAD @VERSION@
+ @VERSIONDATE@
+ https://raw.github.com/openscad/openscad/openscad-@VERSION@/RELEASE_NOTES
+
+
+
+
diff --git a/doc/release-checklist.txt b/doc/release-checklist.txt
index c51c919c..db5b6965 100644
--- a/doc/release-checklist.txt
+++ b/doc/release-checklist.txt
@@ -44,7 +44,8 @@ o Notify package managers
Build and Upload Release Binaries
---------------------------------
-$ export VERSION=
+$ export VERSIONDATE=
+$ export VERSION= # If development snapshot, you don't need version is the same as VERSIONDATE
$ tar xzf openscad-$VERSION.src.tar.gz
$ cd openscad-$VERSION
diff --git a/icons/prefsUpdate.png b/icons/prefsUpdate.png
new file mode 100644
index 00000000..a7dc02ee
Binary files /dev/null and b/icons/prefsUpdate.png differ
diff --git a/mjau.gdb b/mjau.gdb
index 7b8e0296..6d394114 100644
--- a/mjau.gdb
+++ b/mjau.gdb
@@ -1 +1,2 @@
set environment DYLD_LIBRARY_PATH=/Users/kintel/code/OpenSCAD/libraries/install/lib
+set environment DYLD_FRAMEWORK_PATH=/Users/kintel/code/OpenSCAD/libraries/install/lib
diff --git a/openscad.pro b/openscad.pro
index a7088ecc..c624b2e2 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -68,7 +68,7 @@ macx {
APP_RESOURCES.path = Contents/Resources
APP_RESOURCES.files = OpenSCAD.sdef
QMAKE_BUNDLE_DATA += APP_RESOURCES
- LIBS += -framework Carbon
+ LIBS += -framework Cocoa -framework Sparkle
}
else {
TARGET = openscad
@@ -320,8 +320,14 @@ SOURCES += src/cgalutils.cc \
macx {
HEADERS += src/AppleEvents.h \
- src/EventFilter.h
- SOURCES += src/AppleEvents.cc
+ src/EventFilter.h \
+ src/AutoUpdater.h \
+ src/SparkleAutoUpdater.h \
+ src/CocoaInitializer.h
+ SOURCES += src/AppleEvents.cc \
+ src/AutoUpdater.cc
+ OBJECTIVE_SOURCES += src/SparkleAutoUpdater.mm \
+ src/CocoaInitializer.mm
}
isEmpty(PREFIX):PREFIX = /usr/local
diff --git a/openscad.qrc b/openscad.qrc
index 84745e9f..28b6a72d 100644
--- a/openscad.qrc
+++ b/openscad.qrc
@@ -4,6 +4,7 @@
icons/prefsAdvanced.png
icons/prefs3DView.png
icons/prefsEditor.png
+ icons/prefsUpdate.png
icons/flattr.png
src/AboutDialog.html
diff --git a/scripts/publish-macosx.sh b/scripts/publish-macosx.sh
index e22e5bdd..f9c693ba 100755
--- a/scripts/publish-macosx.sh
+++ b/scripts/publish-macosx.sh
@@ -1,10 +1,13 @@
#!/bin/sh
-# NB! To build a release build, the VERSION environment variable needs to be set.
+# NB! To build a release build, the VERSION and VERSIONDATE environment variables needs to be set.
# See doc/release-checklist.txt
+if test -z "$VERSIONDATE"; then
+ VERSIONDATE=`date "+%Y.%m.%d"`
+fi
if test -z "$VERSION"; then
- VERSION=`date "+%Y.%m.%d"`
+ VERSION=$VERSIONDATE
COMMIT=-c
SNAPSHOT=true
fi
@@ -18,21 +21,33 @@ export OPENSCAD_LIBRARIES=$PWD/../libraries/install
# Make sure that the correct Qt tools are used
export PATH=$OPENSCAD_LIBRARIES/bin:$PATH
-`dirname $0`/release-common.sh -v $VERSION $COMMIT
-if [[ $? != 0 ]]; then
- exit 1
-fi
+#`dirname $0`/release-common.sh -v $VERSION $COMMIT
+#if [[ $? != 0 ]]; then
+# exit 1
+#fi
echo "Sanity check of the app bundle..."
-`dirname $0`/macosx-sanity-check.py OpenSCAD.app/Contents/MacOS/OpenSCAD
-if [[ $? != 0 ]]; then
- exit 1
+#`dirname $0`/macosx-sanity-check.py OpenSCAD.app/Contents/MacOS/OpenSCAD
+#if [[ $? != 0 ]]; then
+# exit 1
+#fi
+
+if [[ $VERSION == $VERSIONDATE ]]; then
+ APPCASTFILE=appcast-snapshots.xml
+else
+ APPCASTFILE=appcast.xml
+fi
+echo "Creating appcast $APPCASTFILE..."
+sed -e "s,@VERSION@,$VERSION,g" -e "s,@VERSIONDATE@,$VERSIONDATE,g" -e "s,@FILESIZE@,$(stat -f "%z" OpenSCAD-$VERSION.dmg),g" $APPCASTFILE.in > $APPCASTFILE
+cp $APPCASTFILE ../openscad.github.com
+if [[ $VERSION == $VERSIONDATE ]]; then
+ cp $APPCASTFILE ../openscad.github.com/appcast-snapshots.xml
fi
echo "Uploading..."
-LABELS=OpSys-OSX,Type-Executable
-if ! $SNAPSHOT; then LABELS=$LABELS,Featured; fi
-`dirname $0`/googlecode_upload.py -s 'Mac OS X Snapshot' -p openscad OpenSCAD-$VERSION.dmg -l $LABELS
+#LABELS=OpSys-OSX,Type-Executable
+#if ! $SNAPSHOT; then LABELS=$LABELS,Featured; fi
+#`dirname $0`/googlecode_upload.py -s 'Mac OS X Snapshot' -p openscad OpenSCAD-$VERSION.dmg -l $LABELS
# Update snapshot filename on wab page
-`dirname $0`/update-web.sh OpenSCAD-$VERSION.dmg
+#`dirname $0`/update-web.sh OpenSCAD-$VERSION.dmg
diff --git a/scripts/release-common.sh b/scripts/release-common.sh
index de14cb17..a30d43a8 100755
--- a/scripts/release-common.sh
+++ b/scripts/release-common.sh
@@ -9,10 +9,12 @@
#
# Usage: release-common.sh [-v ] [-c] [-x32]
# -v Version string (e.g. -v 2010.01)
+# -d Version date (e.g. -d 2010.01.23)
# -c Build with commit info
# -mingw32 Cross-compile for win32 using MXE
#
-# If no version string is given, todays date will be used (YYYY-MM-DD)
+# If no version string or version date is given, todays date will be used (YYYY-MM-DD)
+# If only verion date is given, it will be used also as version string.
# If no make target is given, release will be used on Windows, none one Mac OS X
#
# The commit info will extracted from git and be passed to qmake as OPENSCAD_COMMIT
@@ -23,7 +25,7 @@
printUsage()
{
- echo "Usage: $0 -v -c -mingw32
+ echo "Usage: $0 -v -d -c -mingw32
echo
echo " Example: $0 -v 2010.01
}
@@ -59,16 +61,20 @@ else
exit
fi
-while getopts 'v:c' c
+while getopts 'v:d:c' c
do
case $c in
v) VERSION=$OPTARG;;
+ d) VERSIONDATE=$OPTARG;;
c) OPENSCAD_COMMIT=`git log -1 --pretty=format:"%h"`
esac
done
+if test -z "$VERSIONDATE"; then
+ VERSIONDATE=`date "+%Y.%m.%d"`
+fi
if test -z "$VERSION"; then
- VERSION=`date "+%Y.%m.%d"`
+ VERSION=$VERSIONDATE
fi
@@ -102,7 +108,7 @@ if [ -d .git ]; then
git submodule update
fi
-echo "Building openscad-$VERSION $CONFIGURATION..."
+echo "Building openscad-$VERSION ($VERSIONDATE) $CONFIGURATION..."
case $OS in
LINUX|MACOSX)
@@ -230,6 +236,7 @@ echo "Creating archive.."
case $OS in
MACOSX)
+ /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $VERSIONDATE" OpenSCAD.app/Contents/Info.plist
macdeployqt OpenSCAD.app -dmg -no-strip
mv OpenSCAD.dmg OpenSCAD-$VERSION.dmg
hdiutil internet-enable -yes -quiet OpenSCAD-$VERSION.dmg
diff --git a/setenv_mjau.sh b/setenv_mjau.sh
index 943ae6b2..7976c609 100644
--- a/setenv_mjau.sh
+++ b/setenv_mjau.sh
@@ -1,5 +1,6 @@
export OPENSCAD_LIBRARIES=$PWD/../libraries/install
export DYLD_LIBRARY_PATH=$OPENSCAD_LIBRARIES/lib
+export DYLD_FRAMEWORK_PATH=$OPENSCAD_LIBRARIES/lib
export QMAKESPEC=macx-g++
#export OPENCSGDIR=$PWD/../OpenCSG-1.3.0
diff --git a/src/AutoUpdater.cc b/src/AutoUpdater.cc
new file mode 100644
index 00000000..b64cc827
--- /dev/null
+++ b/src/AutoUpdater.cc
@@ -0,0 +1,3 @@
+#include "AutoUpdater.h"
+
+AutoUpdater *AutoUpdater::updater_instance = NULL;
diff --git a/src/AutoUpdater.h b/src/AutoUpdater.h
new file mode 100644
index 00000000..18527c8f
--- /dev/null
+++ b/src/AutoUpdater.h
@@ -0,0 +1,25 @@
+#ifndef AUTOUPDATER_H_
+#define AUTOUPDATER_H_
+
+#include
+
+class AutoUpdater
+{
+public:
+ virtual ~AutoUpdater() {}
+
+ virtual void checkForUpdates() = 0;
+ virtual void setAutomaticallyChecksForUpdates(bool on) = 0;
+ virtual bool automaticallyChecksForUpdates() = 0;
+ virtual void setEnableSnapshots(bool on) = 0;
+ virtual bool enableSnapshots() = 0;
+ virtual QString lastUpdateCheckDate() = 0;
+
+ static AutoUpdater *updater() { return updater_instance; }
+ static void setUpdater(AutoUpdater *updater) { updater_instance = updater; }
+
+protected:
+ static AutoUpdater *updater_instance;
+};
+
+#endif
diff --git a/src/MainWindow.h b/src/MainWindow.h
index a4835c24..033ef498 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -91,6 +91,7 @@ private:
class QMessageBox *openglbox;
private slots:
+ void actionUpdateCheck();
void actionNew();
void actionOpen();
void actionOpenRecent();
diff --git a/src/MainWindow.ui b/src/MainWindow.ui
index f71ac96c..4b1639bf 100644
--- a/src/MainWindow.ui
+++ b/src/MainWindow.ui
@@ -223,6 +223,7 @@
+
@@ -611,6 +612,9 @@
+
+ true
+
About
@@ -676,6 +680,14 @@
Library info
+
+
+ false
+
+
+ Check for Update..
+
+
diff --git a/src/Preferences.cc b/src/Preferences.cc
index ec660940..fe7462ad 100644
--- a/src/Preferences.cc
+++ b/src/Preferences.cc
@@ -29,7 +29,9 @@
#include
#include
#include
+#include
#include "PolySetCache.h"
+#include "AutoUpdater.h"
#ifdef ENABLE_CGAL
#include "CGALCache.h"
#endif
@@ -88,6 +90,7 @@ Preferences::Preferences(QWidget *parent) : QMainWindow(parent)
QActionGroup *group = new QActionGroup(this);
group->addAction(prefsAction3DView);
group->addAction(prefsActionEditor);
+ group->addAction(prefsActionUpdate);
group->addAction(prefsActionAdvanced);
connect(group, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*)));
@@ -155,6 +158,9 @@ Preferences::actionTriggered(QAction *action)
else if (action == this->prefsActionEditor) {
this->stackedWidget->setCurrentWidget(this->pageEditor);
}
+ else if (action == this->prefsActionUpdate) {
+ this->stackedWidget->setCurrentWidget(this->pageUpdate);
+ }
else if (action == this->prefsActionAdvanced) {
this->stackedWidget->setCurrentWidget(this->pageAdvanced);
}
@@ -186,6 +192,27 @@ void Preferences::on_fontSize_editTextChanged(const QString &size)
emit fontChanged(getValue("editor/fontfamily").toString(), intsize);
}
+void Preferences::on_updateCheckBox_toggled(bool on)
+{
+ if (AutoUpdater *updater =AutoUpdater::updater()) {
+ updater->setAutomaticallyChecksForUpdates(on);
+ }
+}
+
+void Preferences::on_snapshotCheckBox_toggled(bool on)
+{
+ if (AutoUpdater *updater =AutoUpdater::updater()) {
+ updater->setEnableSnapshots(on);
+ }
+}
+
+void Preferences::on_checkNowButton_clicked()
+{
+ if (AutoUpdater *updater =AutoUpdater::updater()) {
+ updater->checkForUpdates();
+ }
+}
+
void
Preferences::on_openCSGWarningBox_toggled(bool state)
{
@@ -289,6 +316,12 @@ void Preferences::updateGUI()
this->fontSize->setEditText(fontsize);
}
+ if (AutoUpdater *updater = AutoUpdater::updater()) {
+ this->updateCheckBox->setChecked(updater->automaticallyChecksForUpdates());
+ this->snapshotCheckBox->setChecked(updater->enableSnapshots());
+ this->lastCheckedLabel->setText(updater->lastUpdateCheckDate());
+ }
+
this->openCSGWarningBox->setChecked(getValue("advanced/opencsg_show_warning").toBool());
this->enableOpenCSGBox->setChecked(getValue("advanced/enable_opencsg_opengl1x").toBool());
this->cgalCacheSizeEdit->setText(getValue("advanced/cgalCacheSize").toString());
diff --git a/src/Preferences.h b/src/Preferences.h
index 48e07b48..46567939 100644
--- a/src/Preferences.h
+++ b/src/Preferences.h
@@ -30,6 +30,9 @@ public slots:
void on_polysetCacheSizeEdit_textChanged(const QString &);
void on_opencsgLimitEdit_textChanged(const QString &);
void on_forceGoldfeatherBox_toggled(bool);
+ void on_updateCheckBox_toggled(bool);
+ void on_snapshotCheckBox_toggled(bool);
+ void on_checkNowButton_clicked();
signals:
void requestRedraw() const;
diff --git a/src/Preferences.ui b/src/Preferences.ui
index 9b4671a4..d67db6a5 100644
--- a/src/Preferences.ui
+++ b/src/Preferences.ui
@@ -6,10 +6,16 @@
0
0
- 531
- 418
+ 473
+ 320
+
+
+ 0
+ 0
+
+
Preferences
@@ -173,6 +179,150 @@
+
+
+ -
+
+
-
+
+
-
+
+
+ Automatically check for updates
+
+
+ true
+
+
+
+ -
+
+
+ Include development snapshots
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Check Now
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 11
+
+
+
+ Last checked:
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 89
+
+
+
+
+
+
-
@@ -227,9 +377,6 @@
- openCSGWarningBox
- enableOpenCSGBox
- forceGoldfeatherBox
-
@@ -317,6 +464,7 @@
+
@@ -355,6 +503,18 @@
Editor
+
+
+ true
+
+
+
+ :/icons/prefsUpdate.png:/icons/prefsUpdate.png
+
+
+ Update
+
+
diff --git a/src/SparkleAutoUpdater.h b/src/SparkleAutoUpdater.h
new file mode 100644
index 00000000..786a190b
--- /dev/null
+++ b/src/SparkleAutoUpdater.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 Remko Troncon. BSD license
+ * Copyright (C) 2013 Marius Kintel. BSD license
+ */
+#ifndef SPARKLEAUTOUPDATER_H
+#define SPARKLEAUTOUPDATER_H
+
+#include
+
+#include "AutoUpdater.h"
+
+class SparkleAutoUpdater : public AutoUpdater
+{
+public:
+ SparkleAutoUpdater();
+ ~SparkleAutoUpdater();
+
+ void checkForUpdates();
+ void setAutomaticallyChecksForUpdates(bool on);
+ bool automaticallyChecksForUpdates();
+ void setEnableSnapshots(bool on);
+ bool enableSnapshots();
+ QString lastUpdateCheckDate();
+
+private:
+ void updateFeed();
+
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/src/SparkleAutoUpdater.mm b/src/SparkleAutoUpdater.mm
new file mode 100644
index 00000000..5176e80f
--- /dev/null
+++ b/src/SparkleAutoUpdater.mm
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 Remko Troncon. BSD license
+ * Copyright (C) 2013 Marius Kintel. BSD license
+ */
+
+#include "SparkleAutoUpdater.h"
+
+#include
+#include
+
+NSString *const SUEnableSnapshotsKey = @"SUEnableSnapshots";
+
+class SparkleAutoUpdater::Private
+{
+public:
+ SUUpdater* updater;
+};
+
+SparkleAutoUpdater::SparkleAutoUpdater()
+{
+ d = new Private;
+
+ d->updater = [SUUpdater sharedUpdater];
+ [d->updater retain];
+
+ updateFeed();
+}
+
+SparkleAutoUpdater::~SparkleAutoUpdater()
+{
+ [d->updater release];
+ delete d;
+}
+
+void SparkleAutoUpdater::checkForUpdates()
+{
+ [d->updater checkForUpdatesInBackground];
+}
+
+void SparkleAutoUpdater::setAutomaticallyChecksForUpdates(bool on)
+{
+ [d->updater setAutomaticallyChecksForUpdates:on];
+}
+
+bool SparkleAutoUpdater::automaticallyChecksForUpdates()
+{
+ return [d->updater automaticallyChecksForUpdates];
+}
+
+void SparkleAutoUpdater::setEnableSnapshots(bool on)
+{
+ [[NSUserDefaults standardUserDefaults] setBool:on forKey:SUEnableSnapshotsKey];
+ updateFeed();
+}
+
+bool SparkleAutoUpdater::enableSnapshots()
+{
+ return [[NSUserDefaults standardUserDefaults] boolForKey:SUEnableSnapshotsKey];
+}
+
+QString SparkleAutoUpdater::lastUpdateCheckDate()
+{
+ NSString *datestring = [NSString stringWithFormat:@"Last checked: %@",
+ [d->updater lastUpdateCheckDate]];
+ return QString::fromUtf8([datestring UTF8String]);
+}
+
+void SparkleAutoUpdater::updateFeed()
+{
+ NSString *urlstring = [NSString stringWithFormat:@"http://openscad.org/appcast%@.xml", enableSnapshots() ? @"-snapshots" : @""];
+ [d->updater setFeedURL:[NSURL URLWithString:urlstring]];
+}
diff --git a/src/mainwin.cc b/src/mainwin.cc
index 17b9ef7a..a05a4d4b 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -48,6 +48,7 @@
#include "ProgressWidget.h"
#include "ThrownTogetherRenderer.h"
#include "csgtermnormalizer.h"
+#include "AutoUpdater.h"
#include
#include
@@ -205,6 +206,16 @@ MainWindow::MainWindow(const QString &filename)
animate_panel->hide();
+ // Application menu
+#ifdef DEBUG
+ this->appActionUpdateCheck->setEnabled(false);
+#else
+#ifdef Q_OS_MAC
+ this->appActionUpdateCheck->setMenuRole(QAction::ApplicationSpecificRole);
+ this->appActionUpdateCheck->setEnabled(true);
+ connect(this->appActionUpdateCheck, SIGNAL(triggered()), this, SLOT(actionUpdateCheck()));
+#endif
+#endif
// File menu
connect(this->fileActionNew, SIGNAL(triggered()), this, SLOT(actionNew()));
connect(this->fileActionOpen, SIGNAL(triggered()), this, SLOT(actionOpen()));
@@ -781,6 +792,13 @@ void MainWindow::compileCSG(bool procevents)
}
}
+void MainWindow::actionUpdateCheck()
+{
+ if (AutoUpdater *updater =AutoUpdater::updater()) {
+ updater->checkForUpdates();
+ }
+}
+
void MainWindow::actionNew()
{
#ifdef ENABLE_MDI
diff --git a/src/openscad.cc b/src/openscad.cc
index 880aa0da..e0b4a680 100644
--- a/src/openscad.cc
+++ b/src/openscad.cc
@@ -54,6 +54,7 @@
#ifdef Q_WS_MAC
#include "EventFilter.h"
#include "AppleEvents.h"
+#include "SparkleAutoUpdater.h"
#endif
#include
@@ -386,6 +387,14 @@ int main(int argc, char **argv)
installAppleEventHandlers();
#endif
+#ifndef DEBUG
+#ifdef Q_WS_MAC
+ AutoUpdater *updater = new SparkleAutoUpdater;
+ AutoUpdater::setUpdater(updater);
+ if (updater->automaticallyChecksForUpdates()) updater->checkForUpdates();
+#endif
+#endif
+
QString qfilename;
if (filename) qfilename = QString::fromStdString(boosty::stringy(boosty::absolute(filename)));