Merge pull request #3741 from lasconic/fix-extension-master
fix #273032: implement packages to install soundfont, templates, etc.
This commit is contained in:
commit
5f9fde47a7
56 changed files with 1791 additions and 435 deletions
|
@ -21,6 +21,7 @@
|
|||
#include "synthesizer/event.h"
|
||||
#include "synthesizer/msynthesizer.h"
|
||||
#include "mscore/preferences.h"
|
||||
#include "mscore/extension.h"
|
||||
|
||||
#include "fluid.h"
|
||||
#include "sfont.h"
|
||||
|
@ -623,9 +624,7 @@ bool Fluid::loadSoundFonts(const QStringList& sl)
|
|||
locker.unlock();
|
||||
bool ok = true;
|
||||
|
||||
|
||||
QFileInfoList l = sfFiles();
|
||||
|
||||
for (int i = sl.size() - 1; i >= 0; --i) {
|
||||
QString s = sl[i];
|
||||
if (s.isEmpty())
|
||||
|
@ -896,6 +895,10 @@ QFileInfoList Fluid::sfFiles()
|
|||
QStringList pl = preferences.getString(PREF_APP_PATHS_MYSOUNDFONTS).split(";");
|
||||
pl.prepend(QFileInfo(QString("%1%2").arg(mscoreGlobalShare).arg("sound")).absoluteFilePath());
|
||||
|
||||
// append extensions directory
|
||||
QStringList extensionsDir = Ms::Extension::getDirectoriesByType(Ms::Extension::soundfontsDir);
|
||||
pl.append(extensionsDir);
|
||||
|
||||
foreach (const QString& s, pl) {
|
||||
QString ss(s);
|
||||
if (!s.isEmpty() && s[0] == '~')
|
||||
|
|
|
@ -52,6 +52,19 @@ static InstrumentGroup* searchInstrumentGroup(const QString& name)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// searchArticulation
|
||||
//---------------------------------------------------------
|
||||
|
||||
static MidiArticulation searchArticulation(const QString& name)
|
||||
{
|
||||
foreach(MidiArticulation a, articulation) {
|
||||
if (a.name == name)
|
||||
return a;
|
||||
}
|
||||
return MidiArticulation();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// readStaffIdx
|
||||
//---------------------------------------------------------
|
||||
|
@ -67,7 +80,7 @@ static int readStaffIdx(XmlReader& e)
|
|||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// readInstrumentGroup
|
||||
// read InstrumentGroup
|
||||
//---------------------------------------------------------
|
||||
|
||||
void InstrumentGroup::read(XmlReader& e)
|
||||
|
@ -108,6 +121,16 @@ void InstrumentGroup::read(XmlReader& e)
|
|||
id = name.toLower().replace(" ", "-");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// clear InstrumentGroup
|
||||
//---------------------------------------------------------
|
||||
|
||||
void InstrumentGroup::clear()
|
||||
{
|
||||
qDeleteAll(instrumentTemplates);
|
||||
instrumentTemplates.clear();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// InstrumentTemplate
|
||||
//---------------------------------------------------------
|
||||
|
@ -586,6 +609,21 @@ bool saveInstrumentTemplates1(const QString& instrTemplates)
|
|||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// clearInstrumentTemplates
|
||||
//---------------------------------------------------------
|
||||
|
||||
void clearInstrumentTemplates()
|
||||
{
|
||||
for (InstrumentGroup* g : instrumentGroups)
|
||||
g->clear();
|
||||
qDeleteAll(instrumentGroups);
|
||||
instrumentGroups.clear();
|
||||
qDeleteAll(instrumentGenres);
|
||||
instrumentGenres.clear();
|
||||
articulation.clear();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// loadInstrumentTemplates
|
||||
//---------------------------------------------------------
|
||||
|
@ -604,8 +642,8 @@ bool loadInstrumentTemplates(const QString& instrTemplates)
|
|||
while (e.readNextStartElement()) {
|
||||
const QStringRef& tag(e.name());
|
||||
if (tag == "instrument-group" || tag == "InstrumentGroup") {
|
||||
QString id(e.attribute("id"));
|
||||
InstrumentGroup* group = searchInstrumentGroup(id);
|
||||
QString idGroup(e.attribute("id"));
|
||||
InstrumentGroup* group = searchInstrumentGroup(idGroup);
|
||||
if (group == 0) {
|
||||
group = new InstrumentGroup;
|
||||
instrumentGroups.append(group);
|
||||
|
@ -614,13 +652,14 @@ bool loadInstrumentTemplates(const QString& instrTemplates)
|
|||
}
|
||||
else if (tag == "Articulation") {
|
||||
// read global articulation
|
||||
MidiArticulation a;
|
||||
QString name(e.attribute("name"));
|
||||
MidiArticulation a = searchArticulation(name);
|
||||
a.read(e);
|
||||
articulation.append(a);
|
||||
}
|
||||
else if (tag == "Genre") {
|
||||
QString id(e.attribute("id"));
|
||||
InstrumentGenre* genre = searchInstrumentGenre(id);
|
||||
QString idGenre(e.attribute("id"));
|
||||
InstrumentGenre* genre = searchInstrumentGenre(idGenre);
|
||||
if (!genre) {
|
||||
genre = new InstrumentGenre;
|
||||
instrumentGenres.append(genre);
|
||||
|
|
|
@ -111,6 +111,7 @@ struct InstrumentGroup {
|
|||
bool extended; // belongs to extended instruments set if true
|
||||
QList<InstrumentTemplate*> instrumentTemplates;
|
||||
void read(XmlReader&);
|
||||
void clear();
|
||||
|
||||
InstrumentGroup() { extended = false; }
|
||||
};
|
||||
|
@ -118,6 +119,7 @@ struct InstrumentGroup {
|
|||
extern QList<InstrumentGenre *> instrumentGenres;
|
||||
extern QList<MidiArticulation> articulation;
|
||||
extern QList<InstrumentGroup*> instrumentGroups;
|
||||
extern void clearInstrumentTemplates();
|
||||
extern bool loadInstrumentTemplates(const QString& instrTemplates);
|
||||
extern bool saveInstrumentTemplates(const QString& instrTemplates);
|
||||
extern InstrumentTemplate* searchTemplate(const QString& name);
|
||||
|
|
|
@ -781,7 +781,7 @@ Score::FileError MasterScore::loadCompressedMsc(QIODevice* io, bool ignoreVersio
|
|||
|
||||
QByteArray dbuf = uz.fileData(rootfile);
|
||||
if (dbuf.isEmpty()) {
|
||||
QList<MQZipReader::FileInfo> fil = uz.fileInfoList();
|
||||
QVector<MQZipReader::FileInfo> fil = uz.fileInfoList();
|
||||
foreach(const MQZipReader::FileInfo& fi, fil) {
|
||||
if (fi.filePath.endsWith(".mscx")) {
|
||||
dbuf = uz.fileData(fi.filePath);
|
||||
|
|
|
@ -669,6 +669,34 @@ int updateVersion()
|
|||
return _updateVersion;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// updateVersion
|
||||
/// Up to 4 digits X.X.X.X
|
||||
/// Each digit can be double XX.XX.XX.XX
|
||||
/// return true if v1 < v2
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool compareVersion(QString v1, QString v2)
|
||||
{
|
||||
auto v1l = v1.split(".");
|
||||
auto v2l = v2.split(".");
|
||||
int ma = qPow(100,qMax(v1l.size(), v2l.size()));
|
||||
int m = ma;
|
||||
int vv1 = 0;
|
||||
for (int i = 0; i < v1l.size(); i++) {
|
||||
vv1 += (m * v1l[i].toInt());
|
||||
m /= 100;
|
||||
}
|
||||
m = ma;
|
||||
int vv2 = 0;
|
||||
for (int i = 0; i < v2l.size(); i++) {
|
||||
vv2 += (m * v2l[i].toInt());
|
||||
m /= 100;
|
||||
}
|
||||
|
||||
return vv1 < vv2;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// diatonicUpDown
|
||||
// used to find the second note of a trill, mordent etc.
|
||||
|
|
|
@ -65,6 +65,7 @@ extern int version();
|
|||
extern int majorVersion();
|
||||
extern int minorVersion();
|
||||
extern int updateVersion();
|
||||
extern bool compareVersion(QString v1, QString v2);
|
||||
|
||||
extern Note* nextChordNote(Note* note);
|
||||
extern Note* prevChordNote(Note* note);
|
||||
|
|
|
@ -351,6 +351,7 @@ add_executable ( ${ExecutableName}
|
|||
abstractdialog.cpp abstractdialog.h
|
||||
toolbuttonmenu.cpp
|
||||
preferenceslistwidget.cpp preferenceslistwidget.h
|
||||
extension.cpp extension.h
|
||||
|
||||
${COCOABRIDGE}
|
||||
${OMR_FILES}
|
||||
|
|
|
@ -36,7 +36,6 @@ bool DownloadUtils::saveFile()
|
|||
void DownloadUtils::downloadFinished(QNetworkReply *data)
|
||||
{
|
||||
sdata = data->readAll();
|
||||
qDebug() << "size" << sdata.size();
|
||||
emit done();
|
||||
}
|
||||
|
||||
|
@ -45,16 +44,31 @@ QByteArray DownloadUtils::returnData()
|
|||
return sdata;
|
||||
}
|
||||
|
||||
void DownloadUtils::download()
|
||||
void DownloadUtils::download(bool showProgress)
|
||||
{
|
||||
QUrl url = QUrl::fromEncoded(_target.toLocal8Bit());
|
||||
QNetworkRequest request(url);
|
||||
QEventLoop loop;
|
||||
QNetworkReply* reply = manager.get(request);
|
||||
|
||||
QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
|
||||
QObject::connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*)));
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
|
||||
if (showProgress) {
|
||||
progressDialog = new QProgressDialog(static_cast<QWidget*>(parent()));
|
||||
progressDialog->setWindowFlags(Qt::WindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint));
|
||||
progressDialog->setWindowModality(Qt::ApplicationModal);
|
||||
progressDialog->setCancelButtonText(tr("Cancel"));
|
||||
progressDialog->setLabelText(tr("Downloading..."));
|
||||
progressDialog->setAutoClose(true);
|
||||
progressDialog->setAutoReset(true);
|
||||
QObject::connect(progressDialog, SIGNAL(canceled()), &loop, SLOT(quit()));
|
||||
progressDialog->show();
|
||||
}
|
||||
|
||||
loop.exec();
|
||||
|
||||
QObject::disconnect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
|
||||
QObject::disconnect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*)));
|
||||
QObject::disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
|
@ -62,7 +76,9 @@ void DownloadUtils::download()
|
|||
|
||||
void DownloadUtils::downloadProgress(qint64 received, qint64 total)
|
||||
{
|
||||
qDebug() << (double(received)/total)*100 << "%";
|
||||
double curVal = (double(received)/total)*100;
|
||||
if (progressDialog && progressDialog->isVisible())
|
||||
progressDialog->setValue(curVal);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ class DownloadUtils : public QObject
|
|||
QString _target;
|
||||
QString _localFile;
|
||||
|
||||
QProgressDialog* progressDialog = nullptr;
|
||||
|
||||
public:
|
||||
explicit DownloadUtils(QWidget *parent=0);
|
||||
|
||||
|
@ -36,7 +38,7 @@ class DownloadUtils : public QObject
|
|||
void done();
|
||||
|
||||
public slots:
|
||||
void download();
|
||||
void download(bool showProgress = false);
|
||||
void downloadFinished(QNetworkReply* data);
|
||||
void downloadProgress(qint64 received, qint64 total);
|
||||
};
|
||||
|
|
71
mscore/extension.cpp
Normal file
71
mscore/extension.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
//=============================================================================
|
||||
// MusE Score
|
||||
// Linux Music Score Editor
|
||||
// $Id:$
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer and others
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.
|
||||
//
|
||||
// This program 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//=============================================================================
|
||||
|
||||
#include "extension.h"
|
||||
#include "preferences.h"
|
||||
#include "libmscore/utils.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
//---------------------------------------------------------
|
||||
// getDirectoriesByType
|
||||
//---------------------------------------------------------
|
||||
|
||||
QStringList Extension::getDirectoriesByType(const char* type)
|
||||
{
|
||||
QStringList result;
|
||||
QDir d(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS));
|
||||
for (auto dd : d.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot| QDir::Readable | QDir::NoSymLinks)) {
|
||||
QDir extensionsDir(dd.absoluteFilePath());
|
||||
auto extDir = extensionsDir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot| QDir::Readable | QDir::NoSymLinks, QDir::Name);
|
||||
// take the most recent version only
|
||||
if (!extDir.isEmpty()) {
|
||||
QString typeDir = QString("%1/%2").arg(extDir.last().absoluteFilePath()).arg(type);
|
||||
if (QFileInfo(typeDir).exists())
|
||||
result.append(typeDir);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// isInstalled
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool Extension::isInstalled(QString extensionId)
|
||||
{
|
||||
QDir extensionDir(QString("%1/%2").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId));
|
||||
return extensionDir.exists();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// getLatestVersion
|
||||
//---------------------------------------------------------
|
||||
|
||||
QString Extension::getLatestVersion(QString extensionId)
|
||||
{
|
||||
QString result = "0.0";
|
||||
QDir extensionDir(QString("%1/%2").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId));
|
||||
auto extDir = extensionDir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot| QDir::Readable | QDir::NoSymLinks, QDir::Name);
|
||||
if (!extDir.isEmpty())
|
||||
result = extDir.last().fileName();
|
||||
return result;
|
||||
}
|
||||
}
|
47
mscore/extension.h
Normal file
47
mscore/extension.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
//=============================================================================
|
||||
// MusE Score
|
||||
// Linux Music Score Editor
|
||||
// $Id:$
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer and others
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2.
|
||||
//
|
||||
// This program 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.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//=============================================================================
|
||||
|
||||
#ifndef __EXTENSION_H__
|
||||
#define __EXTENSION_H__
|
||||
|
||||
namespace Ms {
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Extension
|
||||
//---------------------------------------------------------
|
||||
|
||||
class Extension {
|
||||
public:
|
||||
Extension() {}
|
||||
static constexpr const char* workspacesDir = "workspaces";
|
||||
static constexpr const char* sfzsDir = "sfzs";
|
||||
static constexpr const char* soundfontsDir = "soundfonts";
|
||||
static constexpr const char* templatesDir = "templates";
|
||||
static constexpr const char* instrumentsDir = "instruments";
|
||||
|
||||
static QStringList getDirectoriesByType(const char* type);
|
||||
static bool isInstalled(QString extensionId);
|
||||
static QString getLatestVersion(QString extensionId);
|
||||
};
|
||||
|
||||
|
||||
} // namespace Ms
|
||||
#endif
|
||||
|
|
@ -74,6 +74,7 @@
|
|||
#include "synthesizer/msynthesizer.h"
|
||||
#include "svggenerator.h"
|
||||
#include "scorePreview.h"
|
||||
#include "extension.h"
|
||||
|
||||
#ifdef OMR
|
||||
#include "omr/omr.h"
|
||||
|
@ -90,6 +91,7 @@
|
|||
namespace Ms {
|
||||
|
||||
extern void importSoundfont(QString name);
|
||||
|
||||
extern bool savePositions(Score*, const QString& name, bool segments);
|
||||
extern MasterSynthesizer* synti;
|
||||
|
||||
|
@ -2120,6 +2122,15 @@ void importSoundfont(QString name)
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// importExtension
|
||||
//---------------------------------------------------------
|
||||
|
||||
void importExtension(QString name)
|
||||
{
|
||||
mscore->importExtension(name);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// readScore
|
||||
/// Import file \a name
|
||||
|
@ -2143,6 +2154,10 @@ Score::FileError readScore(MasterScore* score, QString name, bool ignoreVersionE
|
|||
importSoundfont(name);
|
||||
return Score::FileError::FILE_IGNORE_ERROR;
|
||||
}
|
||||
else if (suffix == "muxt") {
|
||||
importExtension(name);
|
||||
return Score::FileError::FILE_IGNORE_ERROR;
|
||||
}
|
||||
else {
|
||||
// typedef Score::FileError (*ImportFunction)(MasterScore*, const QString&);
|
||||
struct ImportDef {
|
||||
|
|
|
@ -52,7 +52,8 @@ InstrumentsDialog::InstrumentsDialog(QWidget* parent)
|
|||
QAction* a = getAction("instruments");
|
||||
connect(a, SIGNAL(triggered()), SLOT(reject()));
|
||||
addAction(a);
|
||||
|
||||
saveButton->setVisible(false);
|
||||
loadButton->setVisible(false);
|
||||
readSettings();
|
||||
}
|
||||
|
||||
|
@ -172,6 +173,24 @@ QTreeWidget* InstrumentsDialog::partiturList()
|
|||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// buildInstrumentsList
|
||||
//---------------------------------------------------------
|
||||
|
||||
void InstrumentsDialog::buildInstrumentsList()
|
||||
{
|
||||
instrumentsWidget->buildTemplateList();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// updateInstrumentDialog
|
||||
//---------------------------------------------------------
|
||||
|
||||
void MuseScore::updateInstrumentDialog()
|
||||
{
|
||||
if (instrList)
|
||||
instrList->buildInstrumentsList();
|
||||
}
|
||||
//---------------------------------------------------------
|
||||
// editInstrList
|
||||
//---------------------------------------------------------
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ class InstrumentsDialog : public QDialog, public Ui::InstrumentsDialog {
|
|||
void writeSettings();
|
||||
void genPartList(Score*);
|
||||
QTreeWidget* partiturList();
|
||||
void buildInstrumentsList();
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
|
|
|
@ -413,7 +413,7 @@ void populateGenreCombo(QComboBox* combo)
|
|||
void populateInstrumentList(QTreeWidget* instrumentList)
|
||||
{
|
||||
instrumentList->clear();
|
||||
// TODO: memory leak
|
||||
// TODO: memory leak?
|
||||
foreach(InstrumentGroup* g, instrumentGroups) {
|
||||
InstrumentTemplateListItem* group = new InstrumentTemplateListItem(g->name, instrumentList);
|
||||
group->setFlags(Qt::ItemIsEnabled);
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
#include "libmscore/lasso.h"
|
||||
#include "libmscore/excerpt.h"
|
||||
#include "libmscore/synthesizerstate.h"
|
||||
#include "libmscore/utils.h"
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
|
@ -111,6 +112,8 @@
|
|||
#include "startcenter.h"
|
||||
#include "help.h"
|
||||
#include "awl/aslider.h"
|
||||
#include "extension.h"
|
||||
#include "thirdparty/qzip/qzipreader_p.h"
|
||||
|
||||
#ifdef USE_LAME
|
||||
#include "exportmp3.h"
|
||||
|
@ -163,6 +166,7 @@ static QString jsonFileName;
|
|||
static QString audioDriver;
|
||||
static QString pluginName;
|
||||
static QString styleFile;
|
||||
static QString extensionName;
|
||||
static bool scoresOnCommandline { false };
|
||||
|
||||
static QList<QTranslator*> translatorList;
|
||||
|
@ -465,6 +469,7 @@ void updateExternalValuesFromPreferences() {
|
|||
dir.mkpath(preferences.getString(PREF_APP_PATHS_MYSTYLES));
|
||||
dir.mkpath(preferences.getString(PREF_APP_PATHS_MYIMAGES));
|
||||
dir.mkpath(preferences.getString(PREF_APP_PATHS_MYTEMPLATES));
|
||||
dir.mkpath(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS));
|
||||
dir.mkpath(preferences.getString(PREF_APP_PATHS_MYPLUGINS));
|
||||
foreach (QString path, preferences.getString(PREF_APP_PATHS_MYSOUNDFONTS).split(";"))
|
||||
dir.mkpath(path);
|
||||
|
@ -551,6 +556,8 @@ void MuseScore::preferencesChanged()
|
|||
|
||||
delete newWizard;
|
||||
newWizard = 0;
|
||||
reloadInstrumentTemplates();
|
||||
updateInstrumentDialog();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -609,6 +616,291 @@ void MuseScore::populateNoteInputMenu()
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// onLongOperationFinished
|
||||
//---------------------------------------------------------
|
||||
|
||||
void MuseScore::onLongOperationFinished()
|
||||
{
|
||||
infoMsgBox->accept();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// importExtension
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool MuseScore::importExtension(QString path)
|
||||
{
|
||||
MQZipReader zipFile(path);
|
||||
// compute total unzipped size
|
||||
qint64 totalZipSize = 0;
|
||||
for (auto fi : zipFile.fileInfoList())
|
||||
totalZipSize += fi.size;
|
||||
|
||||
// check if extension path is writable and has enough space
|
||||
QStorageInfo storage = QStorageInfo(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS));
|
||||
if (storage.isReadOnly()) {
|
||||
if (!MScore::noGui)
|
||||
QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Cannot import extension on read-only storage: %1").arg(storage.displayName()));
|
||||
return false;
|
||||
}
|
||||
if (totalZipSize >= storage.bytesAvailable()) {
|
||||
if (!MScore::noGui)
|
||||
QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Cannot import extension: storage %1 is full").arg(storage.displayName()));
|
||||
return false;
|
||||
}
|
||||
// Check structure of the extension
|
||||
bool hasMetadata = false;
|
||||
bool hasAlienDirectory = false;
|
||||
bool hasAlienFiles = false;
|
||||
QSet<QString> acceptableFolders = { Extension::sfzsDir, Extension::soundfontsDir, Extension::templatesDir, Extension::instrumentsDir, Extension::workspacesDir };
|
||||
for (auto fi : zipFile.fileInfoList()) {
|
||||
if (fi.filePath == "metadata.json")
|
||||
hasMetadata = true;
|
||||
else {
|
||||
// get folders
|
||||
auto path = QDir::cleanPath(fi.filePath);
|
||||
QStringList folders(path);
|
||||
while ((path = QFileInfo(path).path()).length() < folders.last().length())
|
||||
folders << path;
|
||||
if (folders.size() < 2) {
|
||||
hasAlienFiles = true; // in root dir
|
||||
break;
|
||||
}
|
||||
QString rootDir = folders.at(folders.size() - 2);
|
||||
if (!acceptableFolders.contains(rootDir)) {
|
||||
hasAlienDirectory = true; // in root dir
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasMetadata) {
|
||||
if (!MScore::noGui)
|
||||
QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Corrupted extension: no metadata.json"));
|
||||
return false;
|
||||
}
|
||||
if (hasAlienDirectory) {
|
||||
if (!MScore::noGui)
|
||||
QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Corrupted extension: unsupported directories in root directory"));
|
||||
return false;
|
||||
}
|
||||
if (hasAlienFiles) {
|
||||
if (!MScore::noGui)
|
||||
QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Corrupted extension: unsupported files in root directory"));
|
||||
return false;
|
||||
}
|
||||
zipFile.close();
|
||||
|
||||
MQZipReader zipFile2(path);
|
||||
// get extension id from metadata.json
|
||||
QByteArray mdba = zipFile2.fileData("metadata.json");
|
||||
zipFile2.close();
|
||||
QJsonDocument loadDoc = QJsonDocument::fromJson(mdba);
|
||||
QJsonObject mdObject = loadDoc.object();
|
||||
QString extensionId = mdObject["id"].toString();
|
||||
QString version = mdObject["version"].toString();
|
||||
if (extensionId.isEmpty() || version.isEmpty()) {
|
||||
if (!MScore::noGui)
|
||||
QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Corrupted extension: corrupted metadata.json"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if extension is already installed, ask for uninstall
|
||||
QDir dir(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS));
|
||||
auto dirList = dir.entryList(QStringList(extensionId), QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
bool newerVersion = false;
|
||||
if (dirList.contains(extensionId)) {
|
||||
QString extDirName = QString("%1/%2").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId);
|
||||
QDir extDir(extDirName);
|
||||
auto versionDirList = extDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
if (versionDirList.size() > 0) {
|
||||
// potentially other versions
|
||||
// is there a more recent version?
|
||||
for (auto versionDir : versionDirList) {
|
||||
if (compareVersion(version, versionDir)) {
|
||||
qDebug() << "There is a newer version. We don't install";
|
||||
if (!MScore::noGui)
|
||||
QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("A newer version is already installed"));
|
||||
newerVersion = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!newerVersion) {
|
||||
qDebug() << "found already install extension without newer version: deleting it";
|
||||
QDir d(QString("%1/%2").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId));
|
||||
if (!d.removeRecursively()) {
|
||||
if (!MScore::noGui)
|
||||
QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Error while deleting previous version of the extension: %1").arg(extensionId));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//setup the message box
|
||||
infoMsgBox = new QMessageBox();
|
||||
infoMsgBox->setWindowModality(Qt::ApplicationModal);
|
||||
infoMsgBox->setWindowFlags(Qt::WindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint));
|
||||
infoMsgBox->setTextFormat(Qt::RichText);
|
||||
infoMsgBox->setMinimumSize(300, 100);
|
||||
infoMsgBox->setMaximumSize(300, 100);
|
||||
infoMsgBox->setStandardButtons(0);
|
||||
infoMsgBox->setText(QString("<p align='center'>") + tr("Please wait, unpacking extension...") + QString("</p>"));
|
||||
|
||||
//setup async run of long operations
|
||||
QFutureWatcher<bool> futureWatcherUnzip;
|
||||
connect(&futureWatcherUnzip, SIGNAL(finished()), this, SLOT(onLongOperationFinished()));
|
||||
|
||||
MQZipReader* zipFile3 = new MQZipReader(path);
|
||||
// Unzip the extension asynchronously
|
||||
QFuture<bool> futureUnzip = QtConcurrent::run(zipFile3, &MQZipReader::extractAll, QString("%1/%2/%3").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version));
|
||||
futureWatcherUnzip.setFuture(futureUnzip);
|
||||
if (!MScore::noGui)
|
||||
infoMsgBox->exec();
|
||||
bool unzipResult = futureUnzip.result();
|
||||
zipFile3->close();
|
||||
delete zipFile3;
|
||||
zipFile3 = nullptr;
|
||||
|
||||
if (!unzipResult) {
|
||||
if (!MScore::noGui)
|
||||
QMessageBox::critical(mscore, QWidget::tr("Import Extension File"), QWidget::tr("Unable to extract files from the extension"));
|
||||
return false;
|
||||
}
|
||||
|
||||
delete newWizard;
|
||||
newWizard = 0;
|
||||
mscore->reloadInstrumentTemplates();
|
||||
mscore->updateInstrumentDialog();
|
||||
|
||||
auto loadSoundFontAsync = [&]() {
|
||||
// After install: add sfz to zerberus
|
||||
QDir sfzDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::sfzsDir));
|
||||
if (sfzDir.exists()) {
|
||||
// get all sfz files
|
||||
QDirIterator it(sfzDir.absolutePath(), QStringList("*.sfz"), QDir::Files, QDirIterator::Subdirectories);
|
||||
Synthesizer* s = synti->synthesizer("Zerberus");
|
||||
QStringList sfzs;
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
sfzs.append(it.fileName());
|
||||
}
|
||||
sfzs.sort();
|
||||
for (int sfzNum = 0; sfzNum < sfzs.size(); ++sfzNum)
|
||||
s->addSoundFont(sfzs[sfzNum]);
|
||||
|
||||
if (!sfzs.isEmpty())
|
||||
synti->storeState();
|
||||
}
|
||||
|
||||
// After install: add soundfont to fluid
|
||||
QDir sfDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::soundfontsDir));
|
||||
if (sfDir.exists()) {
|
||||
// get all soundfont files
|
||||
QStringList filters("*.sf2");
|
||||
filters.append("*.sf3");
|
||||
QDirIterator it(sfzDir.absolutePath(), filters, QDir::Files, QDirIterator::Subdirectories);
|
||||
Synthesizer* s = synti->synthesizer("Fluid");
|
||||
QStringList sfs;
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
sfs.append(it.fileName());
|
||||
}
|
||||
sfs.sort();
|
||||
for (auto sf : sfs)
|
||||
s->addSoundFont(sf);
|
||||
if (!sfs.isEmpty())
|
||||
synti->storeState();
|
||||
}
|
||||
};
|
||||
if (!enableExperimental) {
|
||||
//load soundfonts async
|
||||
QFuture<void> futureLoadSFs = QtConcurrent::run(loadSoundFontAsync);
|
||||
QFutureWatcher<void> futureWatcherLoadSFs;
|
||||
futureWatcherLoadSFs.setFuture(futureLoadSFs);
|
||||
connect(&futureWatcherLoadSFs, SIGNAL(finished()), this, SLOT(onLongOperationFinished()));
|
||||
infoMsgBox->setText(QString("<p align='center'>") + tr("Please wait, loading soundfonts...") + QString("</p>"));
|
||||
if (!MScore::noGui)
|
||||
infoMsgBox->exec();
|
||||
else
|
||||
futureLoadSFs.waitForFinished();
|
||||
}
|
||||
|
||||
// after install: refresh workspaces if needed
|
||||
QDir workspacesDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::workspacesDir));
|
||||
if (workspacesDir.exists() && !MScore::noGui) {
|
||||
auto wsList = workspacesDir.entryInfoList(QStringList("*.workspace"), QDir::Files);
|
||||
if (!wsList.isEmpty()) {
|
||||
Workspace::refreshWorkspaces();
|
||||
paletteBox->updateWorkspaces();
|
||||
paletteBox->selectWorkspace(wsList.last().absoluteFilePath());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// uninstallExtension
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool MuseScore::uninstallExtension(QString extensionId)
|
||||
{
|
||||
QString version = Extension::getLatestVersion(extensionId);
|
||||
// Before install: remove sfz from zerberus
|
||||
QDir sfzDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::sfzsDir));
|
||||
if (sfzDir.exists()) {
|
||||
// get all sfz files
|
||||
QDirIterator it(sfzDir.absolutePath(), QStringList("*.sfz"), QDir::Files, QDirIterator::Subdirectories);
|
||||
Synthesizer* s = synti->synthesizer("Zerberus");
|
||||
bool found = it.hasNext();
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
s->removeSoundFont(it.fileInfo().absoluteFilePath());
|
||||
}
|
||||
if (found)
|
||||
synti->storeState();
|
||||
}
|
||||
// Before install: remove soundfont from fluid
|
||||
QDir sfDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::soundfontsDir));
|
||||
if (sfDir.exists()) {
|
||||
// get all soundfont files
|
||||
QStringList filters("*.sf2");
|
||||
filters.append("*.sf3");
|
||||
QDirIterator it(sfzDir.absolutePath(), filters, QDir::Files, QDirIterator::Subdirectories);
|
||||
Synthesizer* s = synti->synthesizer("Fluid");
|
||||
bool found = it.hasNext();
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
s->removeSoundFont(it.fileInfo().absoluteFilePath());
|
||||
}
|
||||
if (found)
|
||||
synti->storeState();
|
||||
}
|
||||
bool refreshWorkspaces = false;
|
||||
QDir workspacesDir(QString("%1/%2/%3/%4").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId).arg(version).arg(Extension::workspacesDir));
|
||||
if (workspacesDir.exists()) {
|
||||
auto wsList = workspacesDir.entryInfoList(QStringList("*.workspace"), QDir::Files);
|
||||
if (!wsList.isEmpty())
|
||||
refreshWorkspaces = true;
|
||||
}
|
||||
|
||||
// delete directories
|
||||
QDir extensionDir(QString("%1/%2").arg(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS)).arg(extensionId));
|
||||
extensionDir.removeRecursively();
|
||||
|
||||
// update UI
|
||||
delete newWizard;
|
||||
newWizard = 0;
|
||||
mscore->reloadInstrumentTemplates();
|
||||
mscore->updateInstrumentDialog();
|
||||
if (refreshWorkspaces) {
|
||||
Workspace::refreshWorkspaces();
|
||||
paletteBox->updateWorkspaces();
|
||||
paletteBox->selectWorkspace("Basic");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// MuseScore
|
||||
//---------------------------------------------------------
|
||||
|
@ -646,7 +938,8 @@ MuseScore::MuseScore()
|
|||
setWindowTitle(QString(MUSESCORE_NAME_VERSION));
|
||||
setIconSize(QSize(preferences.getInt(PREF_UI_THEME_ICONWIDTH) * guiScaling, preferences.getInt(PREF_UI_THEME_ICONHEIGHT) * guiScaling));
|
||||
|
||||
ucheck = new UpdateChecker();
|
||||
ucheck = new UpdateChecker(this);
|
||||
packUChecker = new ExtensionsUpdateChecker(this);
|
||||
|
||||
setAcceptDrops(true);
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
|
@ -1395,14 +1688,9 @@ MuseScore::MuseScore()
|
|||
|
||||
setCentralWidget(envelope);
|
||||
|
||||
// load cascading instrument templates
|
||||
loadInstrumentTemplates(preferences.getString(PREF_APP_PATHS_INSTRUMENTLIST1));
|
||||
QString instrList2 = preferences.getString(PREF_APP_PATHS_INSTRUMENTLIST2);
|
||||
if (!instrList2.isEmpty())
|
||||
loadInstrumentTemplates(instrList2);
|
||||
|
||||
if (!MScore::noGui)
|
||||
preferencesChanged();
|
||||
|
||||
if (seq) {
|
||||
connect(seq, SIGNAL(started()), SLOT(seqStarted()));
|
||||
connect(seq, SIGNAL(stopped()), SLOT(seqStopped()));
|
||||
|
@ -1818,6 +2106,31 @@ void MuseScore::openRecentMenu()
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// reloadInstrumentTemplates
|
||||
//---------------------------------------------------------
|
||||
|
||||
void MuseScore::reloadInstrumentTemplates()
|
||||
{
|
||||
clearInstrumentTemplates();
|
||||
// load cascading instrument templates
|
||||
loadInstrumentTemplates(preferences.getString(PREF_APP_PATHS_INSTRUMENTLIST1));
|
||||
QString list2 = preferences.getString(PREF_APP_PATHS_INSTRUMENTLIST2);
|
||||
if (!list2.isEmpty())
|
||||
loadInstrumentTemplates(list2);
|
||||
|
||||
// load instrument templates from extension
|
||||
QStringList extensionDir = Extension::getDirectoriesByType(Extension::instrumentsDir);
|
||||
QStringList filter("*.xml");
|
||||
for (QString s : extensionDir) {
|
||||
QDir extDir(s);
|
||||
extDir.setNameFilters(filter);
|
||||
auto instFiles = extDir.entryInfoList(QDir::Files | QDir::NoSymLinks | QDir::Readable);
|
||||
for (auto instFile : instFiles)
|
||||
loadInstrumentTemplates(instFile.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// setCurrentView
|
||||
//---------------------------------------------------------
|
||||
|
@ -2898,14 +3211,23 @@ static bool processNonGui(const QStringList& argv)
|
|||
if (!converterMode)
|
||||
return res;
|
||||
}
|
||||
bool rv = true;
|
||||
if (converterMode) {
|
||||
if (processJob)
|
||||
return doProcessJob(jsonFileName);
|
||||
else
|
||||
return convert(argv[0], outFileName);
|
||||
}
|
||||
return rv;
|
||||
if (!extensionName.isEmpty()) {
|
||||
QFileInfo fi(extensionName);
|
||||
QString suffix = fi.suffix().toLower();
|
||||
if (suffix == "muxt")
|
||||
return mscore->importExtension(extensionName);
|
||||
else {
|
||||
fprintf(stderr, "cannot install extension: <%s>\n", qPrintable(extensionName));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -3069,7 +3391,16 @@ bool MuseScore::hasToCheckForUpdate()
|
|||
if (ucheck)
|
||||
return ucheck->hasToCheck();
|
||||
else
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// hasToCheckForExtensionsUpdate
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool MuseScore::hasToCheckForExtensionsUpdate()
|
||||
{
|
||||
return packUChecker ? packUChecker->hasToCheck() : false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -3082,6 +3413,16 @@ void MuseScore::checkForUpdate()
|
|||
ucheck->check(version(), sender() != 0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// checkForExtensionsUpdate
|
||||
//---------------------------------------------------------
|
||||
|
||||
void MuseScore::checkForExtensionsUpdate()
|
||||
{
|
||||
if (packUChecker)
|
||||
packUChecker->check();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// readLanguages
|
||||
//---------------------------------------------------------
|
||||
|
@ -5501,6 +5842,8 @@ void MuseScore::showSearchDialog()
|
|||
_searchDialog->show();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef SCRIPT_INTERFACE
|
||||
void MuseScore::pluginTriggered(int) {}
|
||||
void MuseScore::loadPlugins() {}
|
||||
|
@ -6037,6 +6380,7 @@ int main(int argc, char* av[])
|
|||
parser.addOption(QCommandLineOption( "no-fallback-font", "Don't use Bravura as fallback musical font"));
|
||||
parser.addOption(QCommandLineOption({"f", "force"}, "Used with '-o <file>', ignore warnings reg. score being corrupted or from wrong version"));
|
||||
parser.addOption(QCommandLineOption({"b", "bitrate"}, "Used with '-o <file>.mp3', sets bitrate, in kbps", "bitrate"));
|
||||
parser.addOption(QCommandLineOption({"E", "install-extension"}, "Install an extension, load soundfont as default unless if -e is passed too", "extension file"));
|
||||
|
||||
parser.addPositionalArgument("scorefiles", "The files to open", "[scorefile...]");
|
||||
|
||||
|
@ -6083,6 +6427,10 @@ int main(int argc, char* av[])
|
|||
if (pluginName.isEmpty())
|
||||
parser.showHelp(EXIT_FAILURE);
|
||||
}
|
||||
if (parser.isSet("E")) {
|
||||
MScore::noGui = true;
|
||||
extensionName = parser.value("E");
|
||||
}
|
||||
MScore::saveTemplateMode = parser.isSet("template-mode");
|
||||
if (parser.isSet("r")) {
|
||||
QString temp = parser.value("r");
|
||||
|
@ -6278,7 +6626,6 @@ int main(int argc, char* av[])
|
|||
MScore::readDefaultStyle(preferences.getString(PREF_SCORE_STYLE_DEFAULTSTYLEFILE));
|
||||
|
||||
QSplashScreen* sc = 0;
|
||||
QTimer* stimer = 0;
|
||||
if (!MScore::noGui && preferences.getBool(PREF_UI_APP_STARTUP_SHOWSPLASHSCREEN)) {
|
||||
QPixmap pm(":/data/splash.png");
|
||||
sc = new QSplashScreen(pm);
|
||||
|
@ -6286,10 +6633,6 @@ int main(int argc, char* av[])
|
|||
#ifdef Q_OS_MAC // to have session dialog on top of splashscreen on mac
|
||||
sc->setWindowFlags(Qt::FramelessWindowHint);
|
||||
#endif
|
||||
// show splash screen for 5 sec
|
||||
stimer = new QTimer(0);
|
||||
qApp->connect(stimer, SIGNAL(timeout()), sc, SLOT(close()));
|
||||
stimer->start(5000);
|
||||
sc->show();
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
@ -6459,6 +6802,9 @@ int main(int argc, char* av[])
|
|||
mscore->checkForUpdate();
|
||||
#endif
|
||||
|
||||
if (mscore->hasToCheckForExtensionsUpdate())
|
||||
mscore->checkForExtensionsUpdate();
|
||||
|
||||
if (!scoresOnCommandline && preferences.getBool(PREF_UI_APP_STARTUP_SHOWSTARTCENTER) && (!restoredSession || mscore->scores().size() == 0)) {
|
||||
#ifdef Q_OS_MAC
|
||||
// ugly, but on mac we get an event when a file is open.
|
||||
|
@ -6481,6 +6827,7 @@ int main(int argc, char* av[])
|
|||
#endif
|
||||
}
|
||||
|
||||
sc->close();
|
||||
mscore->showPlayPanel(preferences.getBool(PREF_UI_APP_STARTUP_SHOWPLAYPANEL));
|
||||
QSettings settings;
|
||||
if (settings.value("synthControlVisible", false).toBool())
|
||||
|
|
|
@ -222,7 +222,8 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
|
|||
QSettings settings;
|
||||
ScoreView* cv { 0 };
|
||||
ScoreState _sstate;
|
||||
UpdateChecker* ucheck = nullptr;
|
||||
UpdateChecker* ucheck;
|
||||
ExtensionsUpdateChecker* packUChecker = nullptr;
|
||||
|
||||
static const std::list<const char*> _allNoteInputMenuEntries;
|
||||
static const std::list<const char*> _basicNoteInputMenuEntries;
|
||||
|
@ -409,6 +410,7 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
|
|||
|
||||
qreal _physicalDotsPerInch;
|
||||
|
||||
QMessageBox* infoMsgBox;
|
||||
//---------------------
|
||||
|
||||
virtual void closeEvent(QCloseEvent*);
|
||||
|
@ -511,6 +513,7 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
|
|||
void switchLayoutMode(int);
|
||||
void showMidiImportPanel();
|
||||
void changeWorkspace(QAction*);
|
||||
void onLongOperationFinished();
|
||||
|
||||
virtual QMenu* createPopupMenu() override;
|
||||
|
||||
|
@ -526,6 +529,7 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
|
|||
void setPlayState() { changeState(STATE_PLAY); }
|
||||
void setNoteEntryState() { changeState(STATE_NOTE_ENTRY); }
|
||||
void checkForUpdate();
|
||||
void checkForExtensionsUpdate();
|
||||
void midiNoteReceived(int channel, int pitch, int velo);
|
||||
void midiNoteReceived(int pitch, bool ctrl, int velo);
|
||||
void instrumentChanged();
|
||||
|
@ -608,6 +612,7 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
|
|||
QNetworkAccessManager* networkManager();
|
||||
virtual Score* openScore(const QString& fn);
|
||||
bool hasToCheckForUpdate();
|
||||
bool hasToCheckForExtensionsUpdate();
|
||||
static bool unstable();
|
||||
bool eventFilter(QObject *, QEvent *);
|
||||
void setMidiRecordId(int id) { _midiRecordId = id; }
|
||||
|
@ -752,6 +757,8 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
|
|||
QHelpEngine* helpEngine() const { return _helpEngine; }
|
||||
|
||||
virtual void updateInspector() override;
|
||||
void updateInstrumentDialog();
|
||||
void reloadInstrumentTemplates();
|
||||
void showSynthControl(bool);
|
||||
void showMixer(bool);
|
||||
|
||||
|
@ -777,6 +784,8 @@ class MuseScore : public QMainWindow, public MuseScoreCore {
|
|||
static void restoreGeometry(QWidget*const qw);
|
||||
|
||||
void updateWindowTitle(Score* score);
|
||||
bool importExtension(QString path);
|
||||
bool uninstallExtension(QString extensionId);
|
||||
};
|
||||
|
||||
extern MuseScore* mscore;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
//=============================================================================
|
||||
// MusE Score
|
||||
// Linux Music Score Editor
|
||||
|
@ -24,6 +25,7 @@
|
|||
#include "palette.h"
|
||||
#include "instrdialog.h"
|
||||
#include "scoreBrowser.h"
|
||||
#include "extension.h"
|
||||
|
||||
#include "libmscore/instrtemplate.h"
|
||||
#include "libmscore/score.h"
|
||||
|
@ -274,18 +276,9 @@ NewWizardPage4::NewWizardPage4(QWidget* parent)
|
|||
|
||||
templateFileBrowser = new ScoreBrowser;
|
||||
templateFileBrowser->setStripNumbers(true);
|
||||
QDir dir(mscoreGlobalShare + "/templates");
|
||||
QFileInfoList fil = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Dirs | QDir::Files, QDir::Name);
|
||||
if(fil.isEmpty()){
|
||||
fil.append(QFileInfo(QFile(":data/Empty_Score.mscz")));
|
||||
}
|
||||
|
||||
QDir myTemplatesDir(preferences.getString(PREF_APP_PATHS_MYTEMPLATES));
|
||||
fil.append(myTemplatesDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Dirs | QDir::Files, QDir::Name));
|
||||
|
||||
templateFileBrowser->setShowCustomCategory(true);
|
||||
templateFileBrowser->setScores(fil);
|
||||
templateFileBrowser->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
|
||||
buildTemplatesList();
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout;
|
||||
QHBoxLayout* searchLayout = new QHBoxLayout;
|
||||
|
@ -317,6 +310,31 @@ void NewWizardPage4::initializePage()
|
|||
path.clear();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// buildTemplatesList
|
||||
//---------------------------------------------------------
|
||||
|
||||
void NewWizardPage4::buildTemplatesList()
|
||||
{
|
||||
|
||||
QDir dir(mscoreGlobalShare + "/templates");
|
||||
QFileInfoList fil = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Dirs | QDir::Files, QDir::Name);
|
||||
if(fil.isEmpty()){
|
||||
fil.append(QFileInfo(QFile(":data/Empty_Score.mscz")));
|
||||
}
|
||||
|
||||
QDir myTemplatesDir(preferences.getString(PREF_APP_PATHS_MYTEMPLATES));
|
||||
fil.append(myTemplatesDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Dirs | QDir::Files, QDir::Name));
|
||||
|
||||
// append templates directories from extensions
|
||||
QStringList extensionsDir = Extension::getDirectoriesByType(Extension::templatesDir);
|
||||
for (QString extDir : extensionsDir) {
|
||||
QDir extTemplateDir(extDir);
|
||||
fil.append(extTemplateDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Dirs | QDir::Files, QDir::Name));
|
||||
}
|
||||
templateFileBrowser->setScores(fil);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// isComplete
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -145,6 +145,7 @@ class NewWizardPage4 : public QWizardPage {
|
|||
virtual bool isComplete() const override;
|
||||
QString templatePath() const;
|
||||
virtual void initializePage();
|
||||
void buildTemplatesList();
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -207,6 +208,7 @@ class NewWizard : public QWizard {
|
|||
double tempo() const { return p5->tempo(); }
|
||||
bool createTempo() const { return p5->createTempo(); }
|
||||
bool emptyScore() const;
|
||||
void updateValues() const;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -170,6 +170,16 @@ void PaletteBox::updateWorkspaces()
|
|||
workspaceList->setCurrentIndex(curIdx);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// selectWorkspace
|
||||
//---------------------------------------------------------
|
||||
|
||||
void PaletteBox::selectWorkspace(QString path)
|
||||
{
|
||||
int idx = workspaceList->findData(path);
|
||||
workspaceList->setCurrentIndex(idx);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// clear
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -68,6 +68,7 @@ class PaletteBox : public QDockWidget {
|
|||
bool eventFilter(QObject* obj, QEvent *event);
|
||||
void setKeyboardNavigation(bool val) { keyboardNavigation = val; }
|
||||
bool getKeyboardNavigation() { return keyboardNavigation; }
|
||||
void selectWorkspace(QString path);
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -32,7 +32,6 @@ void Preferences::init(bool storeInMemoryOnly)
|
|||
if (!storeInMemoryOnly) {
|
||||
if (_settings)
|
||||
delete _settings;
|
||||
|
||||
_settings = new QSettings();
|
||||
}
|
||||
|
||||
|
@ -40,8 +39,10 @@ void Preferences::init(bool storeInMemoryOnly)
|
|||
|
||||
#if defined(Q_OS_MAC) || (defined(Q_OS_WIN) && !defined(FOR_WINSTORE))
|
||||
bool checkUpdateStartup = true;
|
||||
bool checkExtensionsUpdateStartup = true;
|
||||
#else
|
||||
bool checkUpdateStartup = false;
|
||||
bool checkExtensionsUpdateStartup = false;
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
|
@ -51,7 +52,6 @@ void Preferences::init(bool storeInMemoryOnly)
|
|||
#else
|
||||
bool nativeDialogs = false; // don't use system native file dialogs
|
||||
#endif
|
||||
|
||||
bool defaultUsePortAudio = false;
|
||||
bool defaultUsePulseAudio = false;
|
||||
bool defaultUseJackAudio = false;
|
||||
|
@ -84,6 +84,7 @@ void Preferences::init(bool storeInMemoryOnly)
|
|||
{PREF_APP_PATHS_MYSHORTCUTS, new StringPreference(QFileInfo(QString("%1/%2").arg(wd).arg(QCoreApplication::translate("shortcuts_directory", "Shortcuts"))).absoluteFilePath(), false)},
|
||||
{PREF_APP_PATHS_MYSTYLES, new StringPreference(QFileInfo(QString("%1/%2").arg(wd).arg(QCoreApplication::translate("styles_directory", "Styles"))).absoluteFilePath(), false)},
|
||||
{PREF_APP_PATHS_MYTEMPLATES, new StringPreference(QFileInfo(QString("%1/%2").arg(wd).arg(QCoreApplication::translate("templates_directory", "Templates"))).absoluteFilePath(), false)},
|
||||
{PREF_APP_PATHS_MYEXTENSIONS, new StringPreference(QFileInfo(QString("%1/%2").arg(wd).arg(QCoreApplication::translate("extensions_directory", "Extensions"))).absoluteFilePath(), false)},
|
||||
{PREF_APP_PLAYBACK_FOLLOWSONG, new BoolPreference(true)},
|
||||
{PREF_APP_PLAYBACK_PANPLAYBACK, new BoolPreference(true)},
|
||||
{PREF_APP_PLAYBACK_PLAYREPEATS, new BoolPreference(true)},
|
||||
|
@ -150,6 +151,7 @@ void Preferences::init(bool storeInMemoryOnly)
|
|||
{PREF_UI_CANVAS_SCROLL_LIMITSCROLLAREA, new BoolPreference(false, false)},
|
||||
{PREF_UI_CANVAS_SCROLL_VERTICALORIENTATION, new BoolPreference(false, false)},
|
||||
{PREF_UI_APP_STARTUP_CHECKUPDATE, new BoolPreference(checkUpdateStartup, false)},
|
||||
{PREF_UI_APP_STARTUP_CHECK_EXTENSIONS_UPDATE, new BoolPreference(checkExtensionsUpdateStartup, false)},
|
||||
{PREF_UI_APP_STARTUP_SHOWNAVIGATOR, new BoolPreference(false, false)},
|
||||
{PREF_UI_APP_STARTUP_SHOWPLAYPANEL, new BoolPreference(false, false)},
|
||||
{PREF_UI_APP_STARTUP_SHOWSPLASHSCREEN, new BoolPreference(true, false)},
|
||||
|
@ -404,7 +406,6 @@ void Preferences::clearMidiRemote(int recordId)
|
|||
remove(baseKey);
|
||||
}
|
||||
|
||||
|
||||
Preference::Preference(QVariant defaultValue, QMetaType::Type type, bool showInAdvancedList)
|
||||
: _defaultValue(defaultValue),
|
||||
_showInAdvancedList(showInAdvancedList),
|
||||
|
|
|
@ -91,6 +91,7 @@ enum class MusicxmlExportBreaks : char {
|
|||
#define PREF_APP_PATHS_MYSOUNDFONTS "application/paths/mySoundfonts"
|
||||
#define PREF_APP_PATHS_MYSTYLES "application/paths/myStyles"
|
||||
#define PREF_APP_PATHS_MYTEMPLATES "application/paths/myTemplates"
|
||||
#define PREF_APP_PATHS_MYEXTENSIONS "application/paths/myExtensions"
|
||||
#define PREF_APP_PLAYBACK_FOLLOWSONG "application/playback/followSong"
|
||||
#define PREF_APP_PLAYBACK_PANPLAYBACK "application/playback/panPlayback"
|
||||
#define PREF_APP_PLAYBACK_PLAYREPEATS "application/playback/playRepeats"
|
||||
|
@ -158,6 +159,7 @@ enum class MusicxmlExportBreaks : char {
|
|||
#define PREF_UI_CANVAS_SCROLL_VERTICALORIENTATION "ui/canvas/scroll/verticalOrientation"
|
||||
#define PREF_UI_CANVAS_SCROLL_LIMITSCROLLAREA "ui/canvas/scroll/limitScrollArea"
|
||||
#define PREF_UI_APP_STARTUP_CHECKUPDATE "ui/application/startup/checkUpdate"
|
||||
#define PREF_UI_APP_STARTUP_CHECK_EXTENSIONS_UPDATE "ui/application/startup/checkExtensionsUpdate"
|
||||
#define PREF_UI_APP_STARTUP_SHOWNAVIGATOR "ui/application/startup/showNavigator"
|
||||
#define PREF_UI_APP_STARTUP_SHOWPLAYPANEL "ui/application/startup/showPlayPanel"
|
||||
#define PREF_UI_APP_STARTUP_SHOWSPLASHSCREEN "ui/application/startup/showSplashScreen"
|
||||
|
|
|
@ -153,6 +153,8 @@ PreferenceDialog::PreferenceDialog(QWidget* parent)
|
|||
connect(myPluginsButton, SIGNAL(clicked()), SLOT(selectPluginsDirectory()));
|
||||
connect(myImagesButton, SIGNAL(clicked()), SLOT(selectImagesDirectory()));
|
||||
connect(mySoundfontsButton, SIGNAL(clicked()), SLOT(changeSoundfontPaths()));
|
||||
connect(myExtensionsButton, SIGNAL(clicked()), SLOT(selectExtensionsDirectory()));
|
||||
|
||||
|
||||
connect(updateTranslation, SIGNAL(clicked()), SLOT(updateTranslationClicked()));
|
||||
|
||||
|
@ -532,6 +534,7 @@ void PreferenceDialog::updateValues(bool useDefaultValues)
|
|||
myTemplates->setText(preferences.getString(PREF_APP_PATHS_MYTEMPLATES));
|
||||
myPlugins->setText(preferences.getString(PREF_APP_PATHS_MYPLUGINS));
|
||||
mySoundfonts->setText(preferences.getString(PREF_APP_PATHS_MYSOUNDFONTS));
|
||||
myExtensions->setText(preferences.getString(PREF_APP_PATHS_MYEXTENSIONS));
|
||||
|
||||
index = exportAudioSampleRate->findData(preferences.getInt(PREF_EXPORT_AUDIO_SAMPLERATE));
|
||||
exportAudioSampleRate->setCurrentIndex(index);
|
||||
|
@ -898,6 +901,7 @@ void PreferenceDialog::apply()
|
|||
preferences.setPreference(PREF_APP_PATHS_MYSOUNDFONTS, mySoundfonts->text());
|
||||
preferences.setPreference(PREF_APP_PATHS_MYSTYLES, myStyles->text());
|
||||
preferences.setPreference(PREF_APP_PATHS_MYTEMPLATES, myTemplates->text());
|
||||
preferences.setPreference(PREF_APP_PATHS_MYEXTENSIONS, myExtensions->text());
|
||||
preferences.setPreference(PREF_APP_STARTUP_STARTSCORE, sessionScore->text());
|
||||
preferences.setPreference(PREF_EXPORT_AUDIO_SAMPLERATE, exportAudioSampleRate->currentData().toInt());
|
||||
preferences.setPreference(PREF_EXPORT_MP3_BITRATE, exportMp3BitRate->currentData().toInt());
|
||||
|
@ -1300,6 +1304,22 @@ void PreferenceDialog::changeSoundfontPaths()
|
|||
mySoundfonts->setText(pld.path());
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// selectExtensionsDirectory
|
||||
//---------------------------------------------------------
|
||||
|
||||
void PreferenceDialog::selectExtensionsDirectory()
|
||||
{
|
||||
QString s = QFileDialog::getExistingDirectory(
|
||||
this,
|
||||
tr("Choose Extensions Folder"),
|
||||
myExtensions->text(),
|
||||
QFileDialog::ShowDirsOnly | (preferences.getBool(PREF_UI_APP_USENATIVEDIALOGS) ? QFileDialog::Options() : QFileDialog::DontUseNativeDialog)
|
||||
);
|
||||
if (!s.isNull())
|
||||
myExtensions->setText(s);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// updateLanguagesClicked
|
||||
//---------------------------------------------------------
|
||||
|
@ -1307,6 +1327,7 @@ void PreferenceDialog::changeSoundfontPaths()
|
|||
void PreferenceDialog::updateTranslationClicked()
|
||||
{
|
||||
ResourceManager r(0);
|
||||
r.selectLanguagesTab();
|
||||
r.exec();
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ class PreferenceDialog : public AbstractDialog, private Ui::PrefsDialogBase {
|
|||
void selectTemplatesDirectory();
|
||||
void selectPluginsDirectory();
|
||||
void selectImagesDirectory();
|
||||
void selectExtensionsDirectory();
|
||||
void printShortcutsClicked();
|
||||
void filterShortcutsTextChanged(const QString &);
|
||||
void filterAdvancedPreferences(const QString&);
|
||||
|
|
|
@ -428,6 +428,19 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_extensions">
|
||||
<property name="text">
|
||||
<string>Extensions:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>5</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_86">
|
||||
<property name="text">
|
||||
|
@ -588,6 +601,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="myExtensions">
|
||||
<property name="accessibleName">
|
||||
<string>Extensions folder</string>
|
||||
</property>
|
||||
<property name="accessibleDescription">
|
||||
<string>Insert path to extensions folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QToolButton" name="myTemplatesButton">
|
||||
<property name="focusPolicy">
|
||||
|
@ -628,6 +651,26 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="2">
|
||||
<widget class="QToolButton" name="myExtensionsButton">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="accessibleName">
|
||||
<string>Extensions folder</string>
|
||||
</property>
|
||||
<property name="accessibleDescription">
|
||||
<string>Opens a folder dialog for selecting the extensions folder</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="musescore.qrc">
|
||||
<normaloff>:/data/icons/document-open.svg</normaloff>:/data/icons/document-open.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QToolButton" name="mySoundfontsButton">
|
||||
<property name="focusPolicy">
|
||||
|
@ -1047,6 +1090,7 @@
|
|||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
|
@ -4109,7 +4153,20 @@ Adjusting latency can help synchronize your MIDI hardware with MuseScore's inter
|
|||
<string>Automatic Update Check</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="1" column="0">
|
||||
<item row="4" column="0">
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_5">
|
||||
|
@ -4126,19 +4183,6 @@ Adjusting latency can help synchronize your MIDI hardware with MuseScore's inter
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkUpdateStartup">
|
||||
<property name="text">
|
||||
|
@ -4146,6 +4190,13 @@ Adjusting latency can help synchronize your MIDI hardware with MuseScore's inter
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="checkExtensionsUpdateStartup">
|
||||
<property name="text">
|
||||
<string>Check for new version of MuseScore extensions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -4261,6 +4312,8 @@ Adjusting latency can help synchronize your MIDI hardware with MuseScore's inter
|
|||
<tabstop>mySoundfontsButton</tabstop>
|
||||
<tabstop>myImages</tabstop>
|
||||
<tabstop>myImagesButton</tabstop>
|
||||
<tabstop>myExtensions</tabstop>
|
||||
<tabstop>myExtensionsButton</tabstop>
|
||||
<tabstop>language</tabstop>
|
||||
<tabstop>updateTranslation</tabstop>
|
||||
<tabstop>styleName</tabstop>
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
|
||||
#include "resourceManager.h"
|
||||
#include "musescore.h"
|
||||
#include "extension.h"
|
||||
#include "libmscore/utils.h"
|
||||
#include "stringutils.h"
|
||||
#include "ui_resourceManager.h"
|
||||
#include "thirdparty/qzip/qzipreader_p.h"
|
||||
|
||||
|
@ -29,28 +32,130 @@ ResourceManager::ResourceManager(QWidget *parent) :
|
|||
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
QDir dir;
|
||||
dir.mkpath(dataPath + "/locale");
|
||||
baseAddr = "http://extensions.musescore.org/2.3/";
|
||||
displayPlugins();
|
||||
displayExtensions();
|
||||
displayLanguages();
|
||||
languagesTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||||
languagesTable->verticalHeader()->hide();
|
||||
tabs->removeTab(tabs->indexOf(plugins));
|
||||
tabs->setCurrentIndex(tabs->indexOf(languages));
|
||||
extensionsTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||||
extensionsTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
|
||||
extensionsTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
|
||||
extensionsTable->verticalHeader()->hide();
|
||||
extensionsTable->setColumnWidth(1, 50);
|
||||
extensionsTable->setColumnWidth(1, 100);
|
||||
MuseScore::restoreGeometry(this);
|
||||
}
|
||||
|
||||
void ResourceManager::displayPlugins()
|
||||
|
||||
//---------------------------------------------------------
|
||||
// selectLanguagesTab
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ResourceManager::selectLanguagesTab()
|
||||
{
|
||||
textBrowser->setText("hello");
|
||||
tabs->setCurrentIndex(tabs->indexOf(languages));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// selectExtensionsTab
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ResourceManager::selectExtensionsTab()
|
||||
{
|
||||
tabs->setCurrentIndex(tabs->indexOf(extensions));
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// displayExtensions
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ResourceManager::displayExtensions()
|
||||
{
|
||||
DownloadUtils js(this);
|
||||
js.setTarget(baseAddr() + "extensions/details.json");
|
||||
js.download();
|
||||
QByteArray json = js.returnData();
|
||||
|
||||
// parse the json file
|
||||
QJsonParseError err;
|
||||
QJsonDocument result = QJsonDocument::fromJson(json, &err);
|
||||
if (err.error != QJsonParseError::NoError || !result.isObject()) {
|
||||
qDebug("An error occurred during parsing");
|
||||
return;
|
||||
}
|
||||
int rowCount = result.object().keys().size();
|
||||
rowCount -= 2; //version and type
|
||||
extensionsTable->setRowCount(rowCount);
|
||||
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
QPushButton* buttonInstall;
|
||||
QPushButton* buttonUninstall;
|
||||
extensionsTable->verticalHeader()->show();
|
||||
|
||||
QStringList extensions = result.object().keys();
|
||||
for (QString key : extensions) {
|
||||
if (!result.object().value(key).isObject())
|
||||
continue;
|
||||
QJsonObject value = result.object().value(key).toObject();
|
||||
col = 0;
|
||||
QString test = value.value("file_name").toString();
|
||||
if (test.length() == 0)
|
||||
continue;
|
||||
|
||||
QString filename = value.value("file_name").toString();
|
||||
QString name = value.value("name").toString();
|
||||
int fileSize = value.value("file_size").toInt();
|
||||
QString hashValue = value.value("hash").toString();
|
||||
QString version = value.value("version").toString();
|
||||
|
||||
extensionsTable->setItem(row, col++, new QTableWidgetItem(name));
|
||||
extensionsTable->setItem(row, col++, new QTableWidgetItem(version));
|
||||
extensionsTable->setItem(row, col++, new QTableWidgetItem(stringutils::convertFileSizeToHumanReadable(fileSize)));
|
||||
buttonInstall = new QPushButton(tr("Install"));
|
||||
buttonUninstall = new QPushButton(tr("Uninstall"));
|
||||
|
||||
connect(buttonInstall, SIGNAL(clicked()), this, SLOT(downloadExtension()));
|
||||
connect(buttonUninstall, SIGNAL(clicked()), this, SLOT(uninstallExtension()));
|
||||
buttonInstall->setProperty("path", "extensions/" + filename);
|
||||
buttonInstall->setProperty("hash", hashValue);
|
||||
buttonInstall->setProperty("rowId", row);
|
||||
buttonUninstall->setProperty("extensionId", key);
|
||||
buttonUninstall->setProperty("rowId", row);
|
||||
|
||||
// get the installed version of the extension if any
|
||||
if (Extension::isInstalled(key)) {
|
||||
buttonUninstall->setDisabled(false);
|
||||
QString installedVersion = Extension::getLatestVersion(key);
|
||||
if (compareVersion(installedVersion, version)) {
|
||||
buttonInstall->setText(tr("Update"));
|
||||
}
|
||||
else {
|
||||
buttonInstall->setText(tr("Updated"));
|
||||
buttonInstall->setDisabled(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
buttonUninstall->setDisabled(true);
|
||||
}
|
||||
extensionsTable->setIndexWidget(extensionsTable->model()->index(row, col++), buttonInstall);
|
||||
extensionsTable->setIndexWidget(extensionsTable->model()->index(row, col++), buttonUninstall);
|
||||
row++;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// displayLanguages
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ResourceManager::displayLanguages()
|
||||
{
|
||||
// Download details.json
|
||||
DownloadUtils *js = new DownloadUtils(this);
|
||||
js->setTarget(baseAddr + "languages/details.json");
|
||||
js->download();
|
||||
QByteArray json = js->returnData();
|
||||
DownloadUtils js(this);
|
||||
js.download();
|
||||
js.setTarget(baseAddr() + "languages/details.json");
|
||||
QByteArray json = js.returnData();
|
||||
qDebug() << json;
|
||||
|
||||
// parse the json file
|
||||
QJsonParseError err;
|
||||
|
@ -70,7 +175,7 @@ void ResourceManager::displayLanguages()
|
|||
languagesTable->verticalHeader()->show();
|
||||
|
||||
// move current language to first row
|
||||
QStringList languages = result.object().keys();
|
||||
QStringList languages = result.object().keys();
|
||||
QString lang = mscore->getLocaleISOCode();
|
||||
int index = languages.indexOf(lang);
|
||||
if (index < 0 && lang.size() > 2) {
|
||||
|
@ -98,12 +203,12 @@ void ResourceManager::displayLanguages()
|
|||
|
||||
languagesTable->setItem(row, col++, new QTableWidgetItem(name));
|
||||
languagesTable->setItem(row, col++, new QTableWidgetItem(filename));
|
||||
languagesTable->setItem(row, col++, new QTableWidgetItem(tr("%1 KB").arg(fileSize)));
|
||||
languagesTable->setItem(row, col++, new QTableWidgetItem(tr("%1 kB").arg(fileSize)));
|
||||
updateButtons[row] = new QPushButton(tr("Update"));
|
||||
|
||||
temp = updateButtons[row];
|
||||
buttonMap[temp] = "languages/" + filename;
|
||||
buttonHashMap[temp] = hashValue;
|
||||
languageButtonMap[temp] = "languages/" + filename;
|
||||
languageButtonHashMap[temp] = hashValue;
|
||||
|
||||
languagesTable->setIndexWidget(languagesTable->model()->index(row, col++), temp);
|
||||
|
||||
|
@ -121,16 +226,20 @@ void ResourceManager::displayLanguages()
|
|||
bool verifyInstruments = verifyLanguageFile(filenameInstruments, hashInstruments);
|
||||
|
||||
if (verifyMScore && verifyInstruments) { // compare local file with distant hash
|
||||
temp->setText(tr("No update"));
|
||||
temp->setText(tr("Updated"));
|
||||
temp->setDisabled(1);
|
||||
}
|
||||
else {
|
||||
connect(temp, SIGNAL(clicked()), this, SLOT(download()));
|
||||
connect(temp, SIGNAL(clicked()), this, SLOT(downloadLanguage()));
|
||||
}
|
||||
row++;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// verifyLanguageFile
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool ResourceManager::verifyLanguageFile(QString filename, QString hash)
|
||||
{
|
||||
QString local = dataPath + "/locale/" + filename;
|
||||
|
@ -143,21 +252,25 @@ bool ResourceManager::verifyLanguageFile(QString filename, QString hash)
|
|||
return verifyFile(local, hash);
|
||||
}
|
||||
|
||||
void ResourceManager::download()
|
||||
|
||||
//---------------------------------------------------------
|
||||
// downloadLanguage
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ResourceManager::downloadLanguage()
|
||||
{
|
||||
QPushButton *button = qobject_cast<QPushButton*>( sender() );
|
||||
QString data = buttonMap[button];
|
||||
QString hash = buttonHashMap[button];
|
||||
QPushButton *button = static_cast<QPushButton*>( sender() );
|
||||
QString data = languageButtonMap[button];
|
||||
QString hash = languageButtonHashMap[button];
|
||||
button->setText(tr("Updating"));
|
||||
button->setDisabled(1);
|
||||
QString baseAddress = baseAddr + data;
|
||||
DownloadUtils *dl = new DownloadUtils(this);
|
||||
dl->setTarget(baseAddress);
|
||||
qDebug() << baseAddress;
|
||||
button->setDisabled(true);
|
||||
QString baseAddress = baseAddr() + data;
|
||||
DownloadUtils dl(this);
|
||||
dl.setTarget(baseAddress);
|
||||
QString localPath = dataPath + "/locale/" + data.split('/')[1];
|
||||
dl->setLocalFile(localPath);
|
||||
dl->download();
|
||||
if( !dl->saveFile() || !verifyFile(localPath, hash)) {
|
||||
dl.setLocalFile(localPath);
|
||||
dl.download();
|
||||
if (!dl.saveFile() || !verifyFile(localPath, hash)) {
|
||||
button->setText(tr("Failed, try again"));
|
||||
button->setEnabled(1);
|
||||
}
|
||||
|
@ -166,7 +279,7 @@ void ResourceManager::download()
|
|||
MQZipReader zipFile(localPath);
|
||||
QFileInfo zfi(localPath);
|
||||
QString destinationDir(zfi.absolutePath());
|
||||
QList<MQZipReader::FileInfo> allFiles = zipFile.fileInfoList();
|
||||
QVector<MQZipReader::FileInfo> allFiles = zipFile.fileInfoList();
|
||||
bool result = true;
|
||||
foreach (MQZipReader::FileInfo fi, allFiles) {
|
||||
const QString absPath = destinationDir + "/" + fi.filePath;
|
||||
|
@ -186,7 +299,7 @@ void ResourceManager::download()
|
|||
QFile::remove(localPath);
|
||||
button->setText(tr("Updated"));
|
||||
// retranslate the UI if current language is updated
|
||||
if (data == buttonMap.first())
|
||||
if (data == languageButtonMap.first())
|
||||
setMscoreLocale(localeName);
|
||||
}
|
||||
else {
|
||||
|
@ -196,6 +309,79 @@ void ResourceManager::download()
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// downloadExtension
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ResourceManager::downloadExtension()
|
||||
{
|
||||
QPushButton* button = static_cast<QPushButton*>(sender());
|
||||
QString data = button->property("path").toString();
|
||||
QString hash = button->property("hash").toString();
|
||||
button->setText(tr("Updating"));
|
||||
button->setDisabled(true);
|
||||
QString baseAddress = baseAddr() + data;
|
||||
DownloadUtils dl(this);
|
||||
dl.setTarget(baseAddress);
|
||||
QString localPath = QDir::tempPath() + QDir::separator() + data.split('/')[1];
|
||||
QFile::remove(localPath);
|
||||
dl.setLocalFile(localPath);
|
||||
dl.download(true);
|
||||
bool saveFileRes = dl.saveFile();
|
||||
bool verifyFileRes = saveFileRes && verifyFile(localPath, hash);
|
||||
if(!verifyFileRes) {
|
||||
QFile::remove(localPath);
|
||||
button->setText(tr("Failed, try again"));
|
||||
button->setEnabled(true);
|
||||
|
||||
QMessageBox msgBox;
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setTextFormat(Qt::RichText);
|
||||
msgBox.setWindowTitle(tr("Extensions Installation Failed"));
|
||||
if (!saveFileRes) //failed to save file on disk
|
||||
msgBox.setText(tr("Unable to save the extension file on disk"));
|
||||
else //failed to verify package, so size or hash sum is incorrect
|
||||
msgBox.setText(tr("Unable to download, save and verify the package.\nCheck your internet connection."));
|
||||
msgBox.exec();
|
||||
}
|
||||
else {
|
||||
bool result = mscore->importExtension(localPath);
|
||||
if (result) {
|
||||
QFile::remove(localPath);
|
||||
button->setText(tr("Updated"));
|
||||
// find uninstall button and make it visible
|
||||
int rowId = button->property("rowId").toInt();
|
||||
QPushButton* uninstallButton = static_cast<QPushButton*>(extensionsTable->indexWidget(extensionsTable->model()->index(rowId, 4)));
|
||||
uninstallButton->setDisabled(false);
|
||||
}
|
||||
else {
|
||||
button->setText(tr("Failed, try again"));
|
||||
button->setEnabled(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// uninstallExtension
|
||||
//---------------------------------------------------------
|
||||
|
||||
void ResourceManager::uninstallExtension()
|
||||
{
|
||||
QPushButton* uninstallButton = static_cast<QPushButton*>(sender());
|
||||
QString extensionId = uninstallButton->property("extensionId").toString();
|
||||
if (mscore->uninstallExtension(extensionId)) {
|
||||
// find uninstall button and make it visible
|
||||
int rowId = uninstallButton->property("rowId").toInt();
|
||||
QPushButton* installButton = static_cast<QPushButton*>(extensionsTable->indexWidget(extensionsTable->model()->index(rowId, 3)));
|
||||
installButton->setText("Install");
|
||||
installButton->setDisabled(false);
|
||||
uninstallButton->setDisabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// verifyFile
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool ResourceManager::verifyFile(QString path, QString hash)
|
||||
{
|
||||
|
|
|
@ -23,21 +23,27 @@ class ResourceManager : public QDialog, public Ui::Resource
|
|||
Q_OBJECT
|
||||
|
||||
virtual void hideEvent(QHideEvent*);
|
||||
public:
|
||||
explicit ResourceManager(QWidget *parent = 0);
|
||||
QByteArray txt;
|
||||
void displayLanguages();
|
||||
void displayPlugins();
|
||||
void displayExtensions();
|
||||
bool verifyFile(QString path, QString hash);
|
||||
bool verifyLanguageFile(QString filename, QString hash);
|
||||
|
||||
private:
|
||||
QMap <QPushButton *, QString> buttonMap; // QPushButton -> filename
|
||||
QMap <QPushButton *, QString> buttonHashMap;// QPushButton -> hash of the file
|
||||
QString baseAddr;
|
||||
public:
|
||||
explicit ResourceManager(QWidget *parent = 0);
|
||||
void selectLanguagesTab();
|
||||
void selectExtensionsTab();
|
||||
|
||||
public slots:
|
||||
void download();
|
||||
static inline QString baseAddr() { return "http://extensions.musescore.org/2.3/"; }
|
||||
|
||||
private:
|
||||
QMap <QPushButton *, QString> languageButtonMap; // QPushButton -> filename
|
||||
QMap <QPushButton *, QString> languageButtonHashMap;// QPushButton -> hash of the file
|
||||
|
||||
private slots:
|
||||
void downloadLanguage();
|
||||
void downloadExtension();
|
||||
void uninstallExtension();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,71 @@
|
|||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="extensions">
|
||||
<attribute name="title">
|
||||
<string>Extensions</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_1">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="extensionsTable">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rowCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<row/>
|
||||
<row/>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Extension</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Version</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>File Size</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Install/Update</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Uninstall</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="languages">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
|
@ -57,7 +122,7 @@
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rowCount">
|
||||
<number>10</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>4</number>
|
||||
|
@ -67,14 +132,6 @@
|
|||
</attribute>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Language</string>
|
||||
|
@ -99,21 +156,6 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="plugins">
|
||||
<attribute name="title">
|
||||
<string>Plugins</string>
|
||||
</attribute>
|
||||
<widget class="QTextBrowser" name="textBrowser">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>251</width>
|
||||
<height>341</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -64,6 +64,7 @@ ScoreBrowser::ScoreBrowser(QWidget* parent)
|
|||
scoreList->layout()->setMargin(0);
|
||||
_noMatchedScoresLabel = new QLabel(tr("There are no templates matching the current search."));
|
||||
_noMatchedScoresLabel->setHidden(true);
|
||||
_noMatchedScoresLabel->setObjectName("noMatchedScoresLabel");
|
||||
scoreList->layout()->addWidget(_noMatchedScoresLabel);
|
||||
connect(preview, SIGNAL(doubleClicked(QString)), SIGNAL(scoreActivated(QString)));
|
||||
if (!_showPreview)
|
||||
|
@ -177,8 +178,16 @@ void ScoreBrowser::setScores(QFileInfoList& s)
|
|||
scoreLists.clear();
|
||||
|
||||
QVBoxLayout* l = static_cast<QVBoxLayout*>(scoreList->layout());
|
||||
while (l->count())
|
||||
l->removeItem(l->itemAt(0));
|
||||
QLayoutItem* child;
|
||||
while (l->count()) {
|
||||
child = l->takeAt(0);
|
||||
if (child->widget() != 0) {
|
||||
if (child->widget()->objectName() == "noMatchedScoresLabel") // do not delete
|
||||
continue;
|
||||
delete child->widget();
|
||||
}
|
||||
delete child;
|
||||
}
|
||||
|
||||
ScoreListWidget* sl = 0;
|
||||
|
||||
|
@ -271,14 +280,14 @@ void ScoreBrowser::selectLast()
|
|||
ScoreItem* item = static_cast<ScoreItem*>(w->item(w->count()-1));
|
||||
w->setCurrentItem(item);
|
||||
preview->setScore(item->info());
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// filter
|
||||
// filter which scores are visible based on searchString
|
||||
//---------------------------------------------------------
|
||||
void ScoreBrowser::filter(const QString &searchString)
|
||||
{
|
||||
{
|
||||
int numCategoriesWithMathingScores = 0;
|
||||
|
||||
for (ScoreListWidget* list : scoreLists) {
|
||||
|
@ -308,7 +317,7 @@ void ScoreBrowser::filter(const QString &searchString)
|
|||
}
|
||||
|
||||
_noMatchedScoresLabel->setHidden(numCategoriesWithMathingScores > 0);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// scoreChanged
|
||||
|
|
|
@ -95,4 +95,72 @@ QString stringutils::removeDiacritics(const QString& pre)
|
|||
return result;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// convertFileSizeToHumanReadable
|
||||
//---------------------------------------------------------
|
||||
|
||||
QString stringutils::convertFileSizeToHumanReadable(const qlonglong& bytes)
|
||||
{
|
||||
QString number;
|
||||
if (bytes < 0x400) {//If less than 1 KB, report in B
|
||||
number = QLocale::system().toString(bytes);
|
||||
number.append(" B");
|
||||
return number;
|
||||
}
|
||||
else {
|
||||
if (bytes >= 0x400 && bytes < 0x100000) { //If less than 1 MB, report in KB, unless rounded result is 1024 KB, then report in MB
|
||||
qlonglong result = (bytes + (0x400 / 2)) / 0x400;
|
||||
if(result < 0x400) {
|
||||
number = QLocale::system().toString(result);
|
||||
number.append(" kB");
|
||||
return number;
|
||||
}
|
||||
else {
|
||||
qlonglong result = (bytes + (0x100000 / 2)) / 0x100000;
|
||||
number = QLocale::system().toString(result);
|
||||
number.append(" MB");
|
||||
return number;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (bytes >= 0x100000 && bytes < 0x40000000) { //If less than 1 GB, report in MB, unless rounded result is 1024 MB, then report in GB
|
||||
qlonglong result = (bytes + (0x100000 / 2)) / 0x100000;
|
||||
if (result < 0x100000) {
|
||||
number = QLocale::system().toString(result);
|
||||
number.append(" MB");
|
||||
return number;
|
||||
}
|
||||
else {
|
||||
qlonglong result = (bytes + (0x40000000 / 2)) / 0x40000000;
|
||||
number = QLocale::system().toString(result);
|
||||
number.append(" GB");
|
||||
return number;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (bytes >= 0x40000000 && bytes < 0x10000000000) { //If less than 1 TB, report in GB, unless rounded result is 1024 GB, then report in TB
|
||||
qlonglong result = (bytes + (0x40000000 / 2)) / 0x40000000;
|
||||
if (result < 0x40000000) {
|
||||
number = QLocale::system().toString(result);
|
||||
number.append(" GB");
|
||||
return number;
|
||||
}
|
||||
else {
|
||||
qlonglong result = (bytes + (0x10000000000 / 2)) / 0x10000000000;
|
||||
number = QLocale::system().toString(result);
|
||||
number.append(" TB");
|
||||
return number;
|
||||
}
|
||||
}
|
||||
else {
|
||||
qlonglong result = (bytes + (0x10000000000 / 2)) / 0x10000000000; //If more than 1 TB, report in TB
|
||||
number = QLocale::system().toString(result);
|
||||
number.append(" TB");
|
||||
return number;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Ms
|
||||
|
|
|
@ -30,6 +30,7 @@ class stringutils : public QObject
|
|||
public:
|
||||
static QString removeLigatures(const QString& pre);
|
||||
static QString removeDiacritics(const QString& pre);
|
||||
static QString convertFileSizeToHumanReadable(const qlonglong & bytes);
|
||||
};
|
||||
|
||||
} // namespace Ms
|
||||
|
|
|
@ -320,16 +320,7 @@ void SynthControl::storeButtonClicked()
|
|||
qDebug("no score");
|
||||
return;
|
||||
}
|
||||
QString s(dataPath + "/synthesizer.xml");
|
||||
QFile f(s);
|
||||
if (!f.open(QIODevice::WriteOnly)) {
|
||||
qDebug("cannot write synthesizer settings <%s>", qPrintable(s));
|
||||
return;
|
||||
}
|
||||
XmlWriter xml(0, &f);
|
||||
xml.header();
|
||||
synti->state().write(xml);
|
||||
|
||||
synti->storeState();
|
||||
storeButton->setEnabled(false);
|
||||
recallButton->setEnabled(false);
|
||||
}
|
||||
|
|
|
@ -21,20 +21,35 @@
|
|||
#include "musescore.h"
|
||||
#include "libmscore/mscore.h"
|
||||
#include "preferences.h"
|
||||
#include "resourceManager.h"
|
||||
#include "extension.h"
|
||||
#include "libmscore/utils.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
UpdateChecker::UpdateChecker()
|
||||
//---------------------------------------------------------
|
||||
// default period
|
||||
//---------------------------------------------------------
|
||||
|
||||
static int defaultPeriod()
|
||||
{
|
||||
int result = 24;
|
||||
if(qApp->applicationName() == "MuseScore2"){ //avoid nightly cymbals
|
||||
if (MuseScore::unstable())
|
||||
result = 24;
|
||||
else
|
||||
result = 24; // yes, it's again the same but let's keep the logic for now
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
UpdateChecker::UpdateChecker(QObject* parent)
|
||||
: UpdateCheckerBase(parent)
|
||||
{
|
||||
manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onRequestFinished(QNetworkReply*)));
|
||||
}
|
||||
|
||||
UpdateChecker::~UpdateChecker()
|
||||
{
|
||||
delete manager;
|
||||
}
|
||||
|
||||
void UpdateChecker::onRequestFinished(QNetworkReply* reply)
|
||||
{
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
|
@ -54,7 +69,6 @@ void UpdateChecker::onRequestFinished(QNetworkReply* reply)
|
|||
QString downloadUrl;
|
||||
QString infoUrl;
|
||||
QString description;
|
||||
QString releaseType;
|
||||
|
||||
while (!reader.atEnd() && !reader.hasError()) {
|
||||
QXmlStreamReader::TokenType token = reader.readNext();
|
||||
|
@ -113,6 +127,16 @@ QString UpdateChecker::parseText(QXmlStreamReader& reader)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool UpdateChecker::getUpdatePrefValue()
|
||||
{
|
||||
return preferences.getBool(PREF_UI_APP_STARTUP_CHECKUPDATE);
|
||||
}
|
||||
|
||||
QString UpdateChecker::getUpdatePrefString()
|
||||
{
|
||||
return "lastUpdateDate";
|
||||
}
|
||||
|
||||
void UpdateChecker::check(QString currentVersion, bool m)
|
||||
{
|
||||
manual = m;
|
||||
|
@ -135,45 +159,110 @@ void UpdateChecker::check(QString currentVersion, bool m)
|
|||
qDebug("release type: %s", release.toLatin1().constData());
|
||||
if (!os.isEmpty() && !release.isEmpty() && release != "nightly") {
|
||||
_currentVersion = currentVersion;
|
||||
manager->get(QNetworkRequest(QUrl("http://update.musescore.org/update_"+os +"_" + release +".xml")));
|
||||
manager->get(QNetworkRequest(QUrl("http://update.musescore.org/update_" + os +"_" + release +".xml")));
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// default period
|
||||
//---------------------------------------------------------
|
||||
|
||||
int UpdateChecker::defaultPeriod()
|
||||
UpdateCheckerBase::UpdateCheckerBase(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
int result = 24;
|
||||
if (qApp->applicationName() == "MuseScore3") { //avoid nightly cymbals
|
||||
if (MuseScore::unstable())
|
||||
result = 24;
|
||||
else
|
||||
result = 24; // yes, it's again the same but let's keep the logic for now
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// default hasToCheck
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool UpdateChecker::hasToCheck()
|
||||
bool UpdateCheckerBase::hasToCheck()
|
||||
{
|
||||
if (!preferences.getBool(PREF_UI_APP_STARTUP_CHECKUPDATE))
|
||||
if(!getUpdatePrefValue())
|
||||
return false;
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup("Update");
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
QDateTime lastUpdate = s.value("lastUpdateDate", now).value<QDateTime>();
|
||||
QDateTime lastUpdate = s.value(getUpdatePrefString(), now).value<QDateTime>();
|
||||
|
||||
if (MScore::debugMode) {
|
||||
qDebug("preferences.checkUpdateStartup: %d" , preferences.getBool(PREF_UI_APP_STARTUP_CHECKUPDATE));
|
||||
qDebug("lastupdate: %s", qPrintable(lastUpdate.toString("dd.MM.yyyy hh:mm:ss.zzz")));
|
||||
qDebug(QString("preferences." + getUpdatePrefString() + ": %d").toStdString().c_str() , getUpdatePrefValue());
|
||||
qDebug(QString("last update for " + getUpdatePrefString() + ": %s").toStdString().c_str(), qPrintable(lastUpdate.toString("dd.MM.yyyy hh:mm:ss.zzz")));
|
||||
}
|
||||
s.endGroup();
|
||||
return now == lastUpdate || now > lastUpdate.addSecs(3600 * defaultPeriod()) ;
|
||||
}
|
||||
|
||||
ExtensionsUpdateChecker::ExtensionsUpdateChecker(QObject* parent)
|
||||
: UpdateCheckerBase(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ExtensionsUpdateChecker::check()
|
||||
{
|
||||
DownloadUtils *js = new DownloadUtils();
|
||||
js->setTarget(ResourceManager::baseAddr() + "extensions/details.json");
|
||||
js->download();
|
||||
QByteArray json = js->returnData();
|
||||
|
||||
// parse the json file
|
||||
QJsonParseError err;
|
||||
QJsonDocument result = QJsonDocument::fromJson(json, &err);
|
||||
if (err.error != QJsonParseError::NoError || !result.isObject()) {
|
||||
qDebug("An error occurred during parsing");
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList extensions = result.object().keys();
|
||||
for (QString key : extensions) {
|
||||
if (!result.object().value(key).isObject())
|
||||
continue;
|
||||
QJsonObject value = result.object().value(key).toObject();
|
||||
QString version = value.value("version").toString();
|
||||
|
||||
// get the installed version of the extension if any
|
||||
if (Extension::isInstalled(key)) {
|
||||
QString installedVersion = Extension::getLatestVersion(key);
|
||||
if (compareVersion(installedVersion, version)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Extension Updates Available"));
|
||||
msgBox.setText(tr("One or more installed extensions have updates available in Help / Resource Manager..."));
|
||||
msgBox.setTextFormat(Qt::RichText);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
||||
msgBox.setEscapeButton(QMessageBox::Cancel);
|
||||
int ret = msgBox.exec();
|
||||
switch (ret) {
|
||||
case QMessageBox::Ok: {
|
||||
ResourceManager r(static_cast<QWidget*>(this->parent()));
|
||||
r.selectExtensionsTab();
|
||||
r.exec();
|
||||
break;
|
||||
}
|
||||
case QMessageBox::Cancel: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qWarning() << "undefined action in ExtensionsUpdateChecker::check" << ret;
|
||||
}
|
||||
QSettings s;
|
||||
s.beginGroup("Update");
|
||||
s.setValue(getUpdatePrefString(), QDateTime::currentDateTime());
|
||||
s.endGroup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ExtensionsUpdateChecker::getUpdatePrefValue()
|
||||
{
|
||||
return preferences.getBool(PREF_UI_APP_STARTUP_CHECK_EXTENSIONS_UPDATE);
|
||||
}
|
||||
|
||||
QString ExtensionsUpdateChecker::getUpdatePrefString()
|
||||
{
|
||||
return "lastExtensionsUpdateDate";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,19 @@ namespace Ms {
|
|||
//---------------------------------------------------------
|
||||
// UpdateChecker
|
||||
//---------------------------------------------------------
|
||||
class UpdateCheckerBase: public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
class UpdateChecker : public QObject{
|
||||
public:
|
||||
UpdateCheckerBase(QObject* parent);
|
||||
virtual bool hasToCheck();
|
||||
|
||||
private:
|
||||
virtual bool getUpdatePrefValue() = 0;
|
||||
virtual QString getUpdatePrefString() = 0;
|
||||
};
|
||||
|
||||
class UpdateChecker : public UpdateCheckerBase {
|
||||
Q_OBJECT
|
||||
|
||||
QNetworkAccessManager* manager;
|
||||
|
@ -37,20 +48,29 @@ class UpdateChecker : public QObject{
|
|||
|
||||
public:
|
||||
void check(QString,bool);
|
||||
static bool hasToCheck();
|
||||
|
||||
|
||||
public slots:
|
||||
void onRequestFinished(QNetworkReply*);
|
||||
|
||||
private:
|
||||
QString parseText(QXmlStreamReader&);
|
||||
static int defaultPeriod();
|
||||
static int computeVersion(QString);
|
||||
virtual bool getUpdatePrefValue();
|
||||
virtual QString getUpdatePrefString();
|
||||
|
||||
public:
|
||||
UpdateChecker();
|
||||
~UpdateChecker();
|
||||
UpdateChecker(QObject* parent);
|
||||
};
|
||||
|
||||
class ExtensionsUpdateChecker : public UpdateCheckerBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ExtensionsUpdateChecker(QObject* parent);
|
||||
|
||||
void check();
|
||||
private:
|
||||
virtual bool getUpdatePrefValue();
|
||||
virtual QString getUpdatePrefString();
|
||||
};
|
||||
}
|
||||
#endif // UPDATECHECKER_H
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "preferences.h"
|
||||
#include "palette.h"
|
||||
#include "palettebox.h"
|
||||
#include "extension.h"
|
||||
|
||||
namespace Ms {
|
||||
|
||||
|
@ -458,9 +459,24 @@ void Workspace::save()
|
|||
QList<Workspace*>& Workspace::workspaces()
|
||||
{
|
||||
if (!workspacesRead) {
|
||||
// Remove all workspaces but Basic and Advanced
|
||||
QMutableListIterator<Workspace*> i(_workspaces);
|
||||
int index = 0;
|
||||
while (i.hasNext()) {
|
||||
Workspace* w = i.next();
|
||||
if (index >= 2) {
|
||||
delete w;
|
||||
i.remove();
|
||||
}
|
||||
index++;
|
||||
}
|
||||
QStringList path;
|
||||
path << mscoreGlobalShare + "workspaces";
|
||||
path << dataPath + "/workspaces";
|
||||
|
||||
QStringList extensionsDir = Extension::getDirectoriesByType(Extension::workspacesDir);
|
||||
path.append(extensionsDir);
|
||||
|
||||
QStringList nameFilters;
|
||||
nameFilters << "*.workspace";
|
||||
|
||||
|
@ -491,6 +507,16 @@ QList<Workspace*>& Workspace::workspaces()
|
|||
return _workspaces;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// refreshWorkspaces
|
||||
//---------------------------------------------------------
|
||||
|
||||
QList<Workspace*>& Workspace::refreshWorkspaces()
|
||||
{
|
||||
workspacesRead = false;
|
||||
return workspaces();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// createNewWorkspace
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -68,6 +68,7 @@ class Workspace : public QObject {
|
|||
static Workspace* createNewWorkspace(const QString& name);
|
||||
static bool workspacesRead;
|
||||
static void writeBuiltinWorkspace();
|
||||
static QList<Workspace*>& refreshWorkspaces();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -101,6 +101,7 @@ add_library(
|
|||
${PROJECT_SOURCE_DIR}/thirdparty/beatroot/AgentList.cpp # Required by importmidi.cpp
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/beatroot/BeatTracker.cpp # Required by importmidi.cpp
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/beatroot/Induction.cpp # Required by importmidi.cpp
|
||||
${PROJECT_SOURCE_DIR}/mscore/extension.cpp # required by zerberus tests
|
||||
${OMR_SRC}
|
||||
omr
|
||||
)
|
||||
|
@ -188,6 +189,7 @@ subdirs (
|
|||
libmscore/transpose
|
||||
libmscore/tuplet
|
||||
# libmscore/text work in progress...
|
||||
libmscore/utils
|
||||
importmidi
|
||||
capella
|
||||
biab
|
||||
|
|
17
mtest/libmscore/utils/CMakeLists.txt
Normal file
17
mtest/libmscore/utils/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
#=============================================================================
|
||||
# MuseScore
|
||||
# Music Composition & Notation
|
||||
# $Id:$
|
||||
#
|
||||
# Copyright (C) 2011 Werner Schweer
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2
|
||||
# as published by the Free Software Foundation and appearing in
|
||||
# the file LICENSE.GPL
|
||||
#=============================================================================
|
||||
|
||||
set(TARGET tst_utils)
|
||||
|
||||
include(${PROJECT_SOURCE_DIR}/mtest/cmake.inc)
|
||||
|
68
mtest/libmscore/utils/tst_utils.cpp
Normal file
68
mtest/libmscore/utils/tst_utils.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
//=============================================================================
|
||||
// MuseScore
|
||||
// Music Composition & Notation
|
||||
// $Id:$
|
||||
//
|
||||
// Copyright (C) 2018 Werner Schweer
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2
|
||||
// as published by the Free Software Foundation and appearing in
|
||||
// the file LICENCE.GPL
|
||||
//=============================================================================
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include "libmscore/utils.h"
|
||||
#include "mtest/testutils.h"
|
||||
|
||||
#define DIR QString("libmscore/utils/")
|
||||
|
||||
using namespace Ms;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// TestNote
|
||||
//---------------------------------------------------------
|
||||
|
||||
class TestUtils : public QObject, public MTest
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void tst_compareVersion();
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
// initTestCase
|
||||
//---------------------------------------------------------
|
||||
|
||||
void TestUtils::initTestCase()
|
||||
{
|
||||
initMTest();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
/// test_version
|
||||
//---------------------------------------------------------
|
||||
|
||||
void TestUtils::tst_compareVersion()
|
||||
{
|
||||
QVERIFY(compareVersion("0.22", "1.0") == true);
|
||||
QVERIFY(compareVersion("1", "2") == true);
|
||||
QVERIFY(compareVersion("1.0", "2.0") == true);
|
||||
QVERIFY(compareVersion("1.14", "1.16") == true);
|
||||
QVERIFY(compareVersion("1.16", "1.14") == false);
|
||||
QVERIFY(compareVersion("2.1", "2.0") == false);
|
||||
QVERIFY(compareVersion("2.0", "2.1") == true);
|
||||
QVERIFY(compareVersion("2.1.1.2", "2.0") == false);
|
||||
QVERIFY(compareVersion("2.0", "2.1.1.3") == true);
|
||||
QVERIFY(compareVersion("2.1.1.2", "2.1.1.3") == true);
|
||||
QVERIFY(compareVersion("test", "2.1") == true);
|
||||
QVERIFY(compareVersion("test1", "test") == false);
|
||||
}
|
||||
|
||||
QTEST_MAIN(TestUtils)
|
||||
|
||||
#include "tst_utils.moc"
|
||||
|
|
@ -19,4 +19,4 @@ include_directories(
|
|||
${SNDFILE_INCDIR}
|
||||
)
|
||||
|
||||
target_link_libraries(tst_sfzcomments zerberus synthesizer audiofile ${SNDFILE_LIB})
|
||||
target_link_libraries(tst_sfzcomments zerberus synthesizer audiofile ${SNDFILE_LIB} testutils)
|
||||
|
|
|
@ -19,4 +19,4 @@ include_directories(
|
|||
${SNDFILE_INCDIR}
|
||||
)
|
||||
|
||||
target_link_libraries(tst_sfzenvelopes zerberus synthesizer audiofile ${SNDFILE_LIB})
|
||||
target_link_libraries(tst_sfzenvelopes zerberus synthesizer audiofile ${SNDFILE_LIB} testutils)
|
||||
|
|
|
@ -19,4 +19,4 @@ include_directories(
|
|||
${SNDFILE_INCDIR}
|
||||
)
|
||||
|
||||
target_link_libraries(tst_sfzglobal zerberus synthesizer audiofile ${SNDFILE_LIB})
|
||||
target_link_libraries(tst_sfzglobal zerberus synthesizer audiofile ${SNDFILE_LIB} testutils)
|
||||
|
|
|
@ -19,4 +19,4 @@ include_directories(
|
|||
${SNDFILE_INCDIR}
|
||||
)
|
||||
|
||||
target_link_libraries(tst_sfzincludes zerberus synthesizer audiofile ${SNDFILE_LIB})
|
||||
target_link_libraries(tst_sfzincludes zerberus synthesizer audiofile ${SNDFILE_LIB} testutils)
|
||||
|
|
|
@ -19,4 +19,4 @@ include_directories(
|
|||
${SNDFILE_INCDIR}
|
||||
)
|
||||
|
||||
target_link_libraries(tst_sfzinputcontrols zerberus synthesizer audiofile ${SNDFILE_LIB})
|
||||
target_link_libraries(tst_sfzinputcontrols zerberus synthesizer audiofile ${SNDFILE_LIB} testutils)
|
||||
|
|
|
@ -19,4 +19,4 @@ include_directories(
|
|||
${SNDFILE_INCDIR}
|
||||
)
|
||||
|
||||
target_link_libraries(tst_sfzloop zerberus synthesizer audiofile ${SNDFILE_LIB})
|
||||
target_link_libraries(tst_sfzloop zerberus synthesizer audiofile ${SNDFILE_LIB} testutils)
|
||||
|
|
|
@ -19,4 +19,4 @@ include_directories(
|
|||
${SNDFILE_INCDIR}
|
||||
)
|
||||
|
||||
target_link_libraries(tst_sfzopcodes zerberus synthesizer audiofile ${SNDFILE_LIB})
|
||||
target_link_libraries(tst_sfzopcodes zerberus synthesizer audiofile ${SNDFILE_LIB} testutils)
|
||||
|
|
|
@ -367,6 +367,24 @@ SynthesizerState MasterSynthesizer::state() const
|
|||
return ss;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// storeState
|
||||
//---------------------------------------------------------
|
||||
|
||||
bool MasterSynthesizer::storeState()
|
||||
{
|
||||
QString s(dataPath + "/synthesizer.xml");
|
||||
QFile f(s);
|
||||
if (!f.open(QIODevice::WriteOnly)) {
|
||||
qDebug("cannot write synthesizer settings <%s>", qPrintable(s));
|
||||
return false;
|
||||
}
|
||||
XmlWriter xml(0, &f);
|
||||
xml.header();
|
||||
state().write(xml);
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// setGain
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -102,6 +102,8 @@ class MasterSynthesizer : public QObject {
|
|||
float gain() const { return _gain; }
|
||||
float boost() const { return _boost; }
|
||||
void setBoost(float v) { _boost = v; }
|
||||
|
||||
bool storeState();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
551
thirdparty/qzip/qzip.cpp
vendored
551
thirdparty/qzip/qzip.cpp
vendored
|
@ -1,39 +1,37 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtGui module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
|
@ -47,47 +45,9 @@
|
|||
|
||||
#include <zlib.h>
|
||||
|
||||
#if defined(Q_OS_WIN) or defined(Q_OS_ANDROID)
|
||||
# undef S_IFREG
|
||||
# define S_IFREG 0100000
|
||||
# ifndef S_IFDIR
|
||||
# define S_IFDIR 0040000
|
||||
# endif
|
||||
# ifndef S_ISDIR
|
||||
# define S_ISDIR(x) ((x) & S_IFDIR) > 0
|
||||
# endif
|
||||
# ifndef S_ISREG
|
||||
# define S_ISREG(x) ((x) & 0170000) == S_IFREG
|
||||
# endif
|
||||
# define S_IFLNK 020000
|
||||
# define S_ISLNK(x) ((x) & S_IFLNK) > 0
|
||||
# ifndef S_IRUSR
|
||||
# define S_IRUSR 0400
|
||||
# endif
|
||||
# ifndef S_IWUSR
|
||||
# define S_IWUSR 0200
|
||||
# endif
|
||||
# ifndef S_IXUSR
|
||||
# define S_IXUSR 0100
|
||||
# endif
|
||||
# define S_IRGRP 0040
|
||||
# define S_IWGRP 0020
|
||||
# define S_IXGRP 0010
|
||||
# define S_IROTH 0004
|
||||
# define S_IWOTH 0002
|
||||
# define S_IXOTH 0001
|
||||
#else
|
||||
// # ifndef S_IFDIR
|
||||
// # define S_IFDIR 0040000
|
||||
// # endif
|
||||
# ifndef S_ISDIR
|
||||
# define S_ISDIR(x) ((x) & S_IFDIR) > 0
|
||||
# endif
|
||||
# ifndef S_ISREG
|
||||
# define S_ISREG(x) ((x) & 0170000) == S_IFREG
|
||||
# endif
|
||||
# define S_ISLNK(x) ((x) & S_IFLNK) > 0
|
||||
#endif
|
||||
// Zip standard version for archives handled by this API
|
||||
// (actually, the only basic support of this version is implemented but it is enough for now)
|
||||
#define ZIP_VERSION 20
|
||||
|
||||
#if 0
|
||||
#define ZDEBUG qDebug
|
||||
|
@ -161,42 +121,12 @@ static void writeMSDosDate(uchar *dest, const QDateTime& dt)
|
|||
}
|
||||
}
|
||||
|
||||
static quint32 permissionsToMode(QFile::Permissions perms)
|
||||
{
|
||||
quint32 mode = 0;
|
||||
if (perms & QFile::ReadOwner)
|
||||
mode |= S_IRUSR;
|
||||
if (perms & QFile::WriteOwner)
|
||||
mode |= S_IWUSR;
|
||||
if (perms & QFile::ExeOwner)
|
||||
mode |= S_IXUSR;
|
||||
if (perms & QFile::ReadUser)
|
||||
mode |= S_IRUSR;
|
||||
if (perms & QFile::WriteUser)
|
||||
mode |= S_IWUSR;
|
||||
if (perms & QFile::ExeUser)
|
||||
mode |= S_IXUSR;
|
||||
if (perms & QFile::ReadGroup)
|
||||
mode |= S_IRGRP;
|
||||
if (perms & QFile::WriteGroup)
|
||||
mode |= S_IWGRP;
|
||||
if (perms & QFile::ExeGroup)
|
||||
mode |= S_IXGRP;
|
||||
if (perms & QFile::ReadOther)
|
||||
mode |= S_IROTH;
|
||||
if (perms & QFile::WriteOther)
|
||||
mode |= S_IWOTH;
|
||||
if (perms & QFile::ExeOther)
|
||||
mode |= S_IXOTH;
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
|
||||
{
|
||||
z_stream stream;
|
||||
int err;
|
||||
|
||||
stream.next_in = (Bytef*)source;
|
||||
stream.next_in = const_cast<Bytef*>(source);
|
||||
stream.avail_in = (uInt)sourceLen;
|
||||
if ((uLong)stream.avail_in != sourceLen)
|
||||
return Z_BUF_ERROR;
|
||||
|
@ -231,7 +161,7 @@ static int deflate (Bytef *dest, ulong *destLen, const Bytef *source, ulong sour
|
|||
z_stream stream;
|
||||
int err;
|
||||
|
||||
stream.next_in = (Bytef*)source;
|
||||
stream.next_in = const_cast<Bytef*>(source);
|
||||
stream.avail_in = (uInt)sourceLen;
|
||||
stream.next_out = dest;
|
||||
stream.avail_out = (uInt)*destLen;
|
||||
|
@ -255,36 +185,86 @@ static int deflate (Bytef *dest, ulong *destLen, const Bytef *source, ulong sour
|
|||
return err;
|
||||
}
|
||||
|
||||
|
||||
namespace WindowsFileAttributes {
|
||||
enum {
|
||||
Dir = 0x10, // FILE_ATTRIBUTE_DIRECTORY
|
||||
File = 0x80, // FILE_ATTRIBUTE_NORMAL
|
||||
TypeMask = 0x90,
|
||||
|
||||
ReadOnly = 0x01, // FILE_ATTRIBUTE_READONLY
|
||||
PermMask = 0x01
|
||||
};
|
||||
}
|
||||
|
||||
namespace UnixFileAttributes {
|
||||
enum {
|
||||
Dir = 0040000, // __S_IFDIR
|
||||
File = 0100000, // __S_IFREG
|
||||
SymLink = 0120000, // __S_IFLNK
|
||||
TypeMask = 0170000, // __S_IFMT
|
||||
|
||||
ReadUser = 0400, // __S_IRUSR
|
||||
WriteUser = 0200, // __S_IWUSR
|
||||
ExeUser = 0100, // __S_IXUSR
|
||||
ReadGroup = 0040, // __S_IRGRP
|
||||
WriteGroup = 0020, // __S_IWGRP
|
||||
ExeGroup = 0010, // __S_IXGRP
|
||||
ReadOther = 0004, // __S_IROTH
|
||||
WriteOther = 0002, // __S_IWOTH
|
||||
ExeOther = 0001, // __S_IXOTH
|
||||
PermMask = 0777
|
||||
};
|
||||
}
|
||||
|
||||
static QFile::Permissions modeToPermissions(quint32 mode)
|
||||
{
|
||||
QFile::Permissions ret;
|
||||
if (mode & S_IRUSR)
|
||||
ret |= QFile::ReadOwner;
|
||||
if (mode & S_IWUSR)
|
||||
ret |= QFile::WriteOwner;
|
||||
if (mode & S_IXUSR)
|
||||
ret |= QFile::ExeOwner;
|
||||
if (mode & S_IRUSR)
|
||||
ret |= QFile::ReadUser;
|
||||
if (mode & S_IWUSR)
|
||||
ret |= QFile::WriteUser;
|
||||
if (mode & S_IXUSR)
|
||||
ret |= QFile::ExeUser;
|
||||
if (mode & S_IRGRP)
|
||||
if (mode & UnixFileAttributes::ReadUser)
|
||||
ret |= QFile::ReadOwner | QFile::ReadUser;
|
||||
if (mode & UnixFileAttributes::WriteUser)
|
||||
ret |= QFile::WriteOwner | QFile::WriteUser;
|
||||
if (mode & UnixFileAttributes::ExeUser)
|
||||
ret |= QFile::ExeOwner | QFile::ExeUser;
|
||||
if (mode & UnixFileAttributes::ReadGroup)
|
||||
ret |= QFile::ReadGroup;
|
||||
if (mode & S_IWGRP)
|
||||
if (mode & UnixFileAttributes::WriteGroup)
|
||||
ret |= QFile::WriteGroup;
|
||||
if (mode & S_IXGRP)
|
||||
if (mode & UnixFileAttributes::ExeGroup)
|
||||
ret |= QFile::ExeGroup;
|
||||
if (mode & S_IROTH)
|
||||
if (mode & UnixFileAttributes::ReadOther)
|
||||
ret |= QFile::ReadOther;
|
||||
if (mode & S_IWOTH)
|
||||
if (mode & UnixFileAttributes::WriteOther)
|
||||
ret |= QFile::WriteOther;
|
||||
if (mode & S_IXOTH)
|
||||
if (mode & UnixFileAttributes::ExeOther)
|
||||
ret |= QFile::ExeOther;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static quint32 permissionsToMode(QFile::Permissions perms)
|
||||
{
|
||||
quint32 mode = 0;
|
||||
if (perms & (QFile::ReadOwner | QFile::ReadUser))
|
||||
mode |= UnixFileAttributes::ReadUser;
|
||||
if (perms & (QFile::WriteOwner | QFile::WriteUser))
|
||||
mode |= UnixFileAttributes::WriteUser;
|
||||
if (perms & (QFile::ExeOwner | QFile::ExeUser))
|
||||
mode |= UnixFileAttributes::WriteUser;
|
||||
if (perms & QFile::ReadGroup)
|
||||
mode |= UnixFileAttributes::ReadGroup;
|
||||
if (perms & QFile::WriteGroup)
|
||||
mode |= UnixFileAttributes::WriteGroup;
|
||||
if (perms & QFile::ExeGroup)
|
||||
mode |= UnixFileAttributes::ExeGroup;
|
||||
if (perms & QFile::ReadOther)
|
||||
mode |= UnixFileAttributes::ReadOther;
|
||||
if (perms & QFile::WriteOther)
|
||||
mode |= UnixFileAttributes::WriteOther;
|
||||
if (perms & QFile::ExeOther)
|
||||
mode |= UnixFileAttributes::ExeOther;
|
||||
return mode;
|
||||
}
|
||||
|
||||
static QDateTime readMSDosDate(const uchar *src)
|
||||
{
|
||||
uint dosDate = readUInt(src);
|
||||
|
@ -300,6 +280,71 @@ static QDateTime readMSDosDate(const uchar *src)
|
|||
return QDateTime(QDate(tm_year, tm_mon, tm_mday), QTime(tm_hour, tm_min, tm_sec));
|
||||
}
|
||||
|
||||
// for details, see http://www.pkware.com/documents/casestudies/APPNOTE.TXT
|
||||
|
||||
enum HostOS {
|
||||
HostFAT = 0,
|
||||
HostAMIGA = 1,
|
||||
HostVMS = 2, // VAX/VMS
|
||||
HostUnix = 3,
|
||||
HostVM_CMS = 4,
|
||||
HostAtari = 5, // what if it's a minix filesystem? [cjh]
|
||||
HostHPFS = 6, // filesystem used by OS/2 (and NT 3.x)
|
||||
HostMac = 7,
|
||||
HostZ_System = 8,
|
||||
HostCPM = 9,
|
||||
HostTOPS20 = 10, // pkzip 2.50 NTFS
|
||||
HostNTFS = 11, // filesystem used by Windows NT
|
||||
HostQDOS = 12, // SMS/QDOS
|
||||
HostAcorn = 13, // Archimedes Acorn RISC OS
|
||||
HostVFAT = 14, // filesystem used by Windows 95, NT
|
||||
HostMVS = 15,
|
||||
HostBeOS = 16, // hybrid POSIX/database filesystem
|
||||
HostTandem = 17,
|
||||
HostOS400 = 18,
|
||||
HostOSX = 19
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(HostOS, Q_PRIMITIVE_TYPE);
|
||||
|
||||
enum GeneralPurposeFlag {
|
||||
Encrypted = 0x01,
|
||||
AlgTune1 = 0x02,
|
||||
AlgTune2 = 0x04,
|
||||
HasDataDescriptor = 0x08,
|
||||
PatchedData = 0x20,
|
||||
StrongEncrypted = 0x40,
|
||||
Utf8Names = 0x0800,
|
||||
CentralDirectoryEncrypted = 0x2000
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(GeneralPurposeFlag, Q_PRIMITIVE_TYPE);
|
||||
|
||||
enum CompressionMethod {
|
||||
CompressionMethodStored = 0,
|
||||
CompressionMethodShrunk = 1,
|
||||
CompressionMethodReduced1 = 2,
|
||||
CompressionMethodReduced2 = 3,
|
||||
CompressionMethodReduced3 = 4,
|
||||
CompressionMethodReduced4 = 5,
|
||||
CompressionMethodImploded = 6,
|
||||
CompressionMethodReservedTokenizing = 7, // reserved for tokenizing
|
||||
CompressionMethodDeflated = 8,
|
||||
CompressionMethodDeflated64 = 9,
|
||||
CompressionMethodPKImploding = 10,
|
||||
|
||||
CompressionMethodBZip2 = 12,
|
||||
|
||||
CompressionMethodLZMA = 14,
|
||||
|
||||
CompressionMethodTerse = 18,
|
||||
CompressionMethodLz77 = 19,
|
||||
|
||||
CompressionMethodJpeg = 96,
|
||||
CompressionMethodWavPack = 97,
|
||||
CompressionMethodPPMd = 98,
|
||||
CompressionMethodWzAES = 99
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(CompressionMethod, Q_PRIMITIVE_TYPE);
|
||||
|
||||
struct LocalFileHeader
|
||||
{
|
||||
uchar signature[4]; // 0x04034b50
|
||||
|
@ -313,6 +358,7 @@ struct LocalFileHeader
|
|||
uchar file_name_length[2];
|
||||
uchar extra_field_length[2];
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(LocalFileHeader, Q_PRIMITIVE_TYPE);
|
||||
|
||||
struct DataDescriptor
|
||||
{
|
||||
|
@ -320,8 +366,9 @@ struct DataDescriptor
|
|||
uchar compressed_size[4];
|
||||
uchar uncompressed_size[4];
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(DataDescriptor, Q_PRIMITIVE_TYPE);
|
||||
|
||||
struct MCentralFileHeader
|
||||
struct CentralFileHeader
|
||||
{
|
||||
uchar signature[4]; // 0x02014b50
|
||||
uchar version_made[2];
|
||||
|
@ -341,6 +388,7 @@ struct MCentralFileHeader
|
|||
uchar offset_local_header[4];
|
||||
LocalFileHeader toLocalHeader() const;
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(CentralFileHeader, Q_PRIMITIVE_TYPE);
|
||||
|
||||
struct EndOfDirectory
|
||||
{
|
||||
|
@ -353,46 +401,16 @@ struct EndOfDirectory
|
|||
uchar dir_start_offset[4];
|
||||
uchar comment_length[2];
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(EndOfDirectory, Q_PRIMITIVE_TYPE);
|
||||
|
||||
struct FileHeader
|
||||
{
|
||||
MCentralFileHeader h;
|
||||
CentralFileHeader h;
|
||||
QByteArray file_name;
|
||||
QByteArray extra_field;
|
||||
QByteArray file_comment;
|
||||
};
|
||||
|
||||
MQZipReader::FileInfo::FileInfo()
|
||||
: isDir(false), isFile(false), isSymLink(false), crc32(0), size(0)
|
||||
{
|
||||
}
|
||||
|
||||
MQZipReader::FileInfo::~FileInfo()
|
||||
{
|
||||
}
|
||||
|
||||
MQZipReader::FileInfo::FileInfo(const FileInfo &other)
|
||||
{
|
||||
operator=(other);
|
||||
}
|
||||
|
||||
MQZipReader::FileInfo& MQZipReader::FileInfo::operator=(const FileInfo &other)
|
||||
{
|
||||
filePath = other.filePath;
|
||||
isDir = other.isDir;
|
||||
isFile = other.isFile;
|
||||
isSymLink = other.isSymLink;
|
||||
permissions = other.permissions;
|
||||
crc32 = other.crc32;
|
||||
size = other.size;
|
||||
lastModified = other.lastModified;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool MQZipReader::FileInfo::isValid() const
|
||||
{
|
||||
return isDir || isFile || isSymLink;
|
||||
}
|
||||
Q_DECLARE_TYPEINFO(FileHeader, Q_MOVABLE_TYPE);
|
||||
|
||||
class MQZipPrivate
|
||||
{
|
||||
|
@ -408,28 +426,78 @@ public:
|
|||
delete device;
|
||||
}
|
||||
|
||||
void fillFileInfo(int index, MQZipReader::FileInfo &fileInfo) const;
|
||||
MQZipReader::FileInfo fillFileInfo(int index) const;
|
||||
|
||||
QIODevice *device;
|
||||
bool ownDevice;
|
||||
bool dirtyFileTree;
|
||||
QList<FileHeader> fileHeaders;
|
||||
QVector<FileHeader> fileHeaders;
|
||||
QByteArray comment;
|
||||
uint start_of_directory;
|
||||
};
|
||||
|
||||
void MQZipPrivate::fillFileInfo(int index, MQZipReader::FileInfo &fileInfo) const
|
||||
MQZipReader::FileInfo MQZipPrivate::fillFileInfo(int index) const
|
||||
{
|
||||
MQZipReader::FileInfo fileInfo;
|
||||
FileHeader header = fileHeaders.at(index);
|
||||
fileInfo.filePath = QString::fromLocal8Bit(header.file_name);
|
||||
const quint32 mode = (qFromLittleEndian<quint32>(&header.h.external_file_attributes[0]) >> 16) & 0xFFFF;
|
||||
fileInfo.isDir = S_ISDIR(mode);
|
||||
fileInfo.isFile = S_ISREG(mode);
|
||||
fileInfo.isSymLink = S_ISLNK(mode);
|
||||
fileInfo.permissions = modeToPermissions(mode);
|
||||
fileInfo.crc32 = readUInt(header.h.crc_32);
|
||||
quint32 mode = readUInt(header.h.external_file_attributes);
|
||||
const HostOS hostOS = HostOS(readUShort(header.h.version_made) >> 8);
|
||||
switch (hostOS) {
|
||||
case HostUnix:
|
||||
mode = (mode >> 16) & 0xffff;
|
||||
switch (mode & UnixFileAttributes::TypeMask) {
|
||||
case UnixFileAttributes::SymLink:
|
||||
fileInfo.isSymLink = true;
|
||||
break;
|
||||
case UnixFileAttributes::Dir:
|
||||
fileInfo.isDir = true;
|
||||
break;
|
||||
case UnixFileAttributes::File:
|
||||
default: // ### just for the case; should we warn?
|
||||
fileInfo.isFile = true;
|
||||
break;
|
||||
}
|
||||
fileInfo.permissions = modeToPermissions(mode);
|
||||
break;
|
||||
case HostFAT:
|
||||
case HostNTFS:
|
||||
case HostHPFS:
|
||||
case HostVFAT:
|
||||
switch (mode & WindowsFileAttributes::TypeMask) {
|
||||
case WindowsFileAttributes::Dir:
|
||||
fileInfo.isDir = true;
|
||||
break;
|
||||
case WindowsFileAttributes::File:
|
||||
default:
|
||||
fileInfo.isFile = true;
|
||||
break;
|
||||
}
|
||||
fileInfo.permissions |= QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther;
|
||||
if ((mode & WindowsFileAttributes::ReadOnly) == 0)
|
||||
fileInfo.permissions |= QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther;
|
||||
if (fileInfo.isDir)
|
||||
fileInfo.permissions |= QFile::ExeOwner | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther;
|
||||
break;
|
||||
default:
|
||||
qWarning("QZip: Zip entry format at %d is not supported.", index);
|
||||
return fileInfo; // we don't support anything else
|
||||
}
|
||||
|
||||
ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
|
||||
// if bit 11 is set, the filename and comment fields must be encoded using UTF-8
|
||||
const bool inUtf8 = (general_purpose_bits & Utf8Names) != 0;
|
||||
fileInfo.filePath = inUtf8 ? QString::fromUtf8(header.file_name) : QString::fromLocal8Bit(header.file_name);
|
||||
fileInfo.crc = readUInt(header.h.crc_32);
|
||||
fileInfo.size = readUInt(header.h.uncompressed_size);
|
||||
fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
|
||||
|
||||
// fix the file path, if broken (convert separators, eat leading and trailing ones)
|
||||
fileInfo.filePath = QDir::fromNativeSeparators(fileInfo.filePath);
|
||||
while (!fileInfo.filePath.isEmpty() && (fileInfo.filePath.at(0) == QLatin1Char('.') || fileInfo.filePath.at(0) == QLatin1Char('/')))
|
||||
fileInfo.filePath = fileInfo.filePath.mid(1);
|
||||
while (!fileInfo.filePath.isEmpty() && fileInfo.filePath.at(fileInfo.filePath.size() - 1) == QLatin1Char('/'))
|
||||
fileInfo.filePath.chop(1);
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
class MQZipReaderPrivate : public MQZipPrivate
|
||||
|
@ -465,7 +533,7 @@ public:
|
|||
void addEntry(EntryType type, const QString &fileName, const QByteArray &contents);
|
||||
};
|
||||
|
||||
LocalFileHeader MCentralFileHeader::toLocalHeader() const
|
||||
LocalFileHeader CentralFileHeader::toLocalHeader() const
|
||||
{
|
||||
LocalFileHeader h;
|
||||
writeUInt(h.signature, 0x04034b50);
|
||||
|
@ -500,7 +568,7 @@ void MQZipReaderPrivate::scanFiles()
|
|||
uchar tmp[4];
|
||||
device->read((char *)tmp, 4);
|
||||
if (readUInt(tmp) != 0x04034b50) {
|
||||
qWarning() << "QZip: not a zip file!";
|
||||
qWarning("QZip: not a zip file!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -510,9 +578,9 @@ void MQZipReaderPrivate::scanFiles()
|
|||
int num_dir_entries = 0;
|
||||
EndOfDirectory eod;
|
||||
while (start_of_directory == -1) {
|
||||
int pos = device->size() - sizeof(EndOfDirectory) - i;
|
||||
const int pos = device->size() - int(sizeof(EndOfDirectory)) - i;
|
||||
if (pos < 0 || i > 65535) {
|
||||
qWarning() << "QZip: EndOfDirectory not found";
|
||||
qWarning("QZip: EndOfDirectory not found");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -529,39 +597,39 @@ void MQZipReaderPrivate::scanFiles()
|
|||
ZDEBUG("start_of_directory at %d, num_dir_entries=%d", start_of_directory, num_dir_entries);
|
||||
int comment_length = readUShort(eod.comment_length);
|
||||
if (comment_length != i)
|
||||
qWarning() << "QZip: failed to parse zip file.";
|
||||
qWarning("QZip: failed to parse zip file.");
|
||||
comment = device->read(qMin(comment_length, i));
|
||||
|
||||
|
||||
device->seek(start_of_directory);
|
||||
for (i = 0; i < num_dir_entries; ++i) {
|
||||
FileHeader header;
|
||||
int read = device->read((char *) &header.h, sizeof(MCentralFileHeader));
|
||||
if (read < (int)sizeof(MCentralFileHeader)) {
|
||||
qWarning() << "QZip: Failed to read complete header, index may be incomplete";
|
||||
int read = device->read((char *) &header.h, sizeof(CentralFileHeader));
|
||||
if (read < (int)sizeof(CentralFileHeader)) {
|
||||
qWarning("QZip: Failed to read complete header, index may be incomplete");
|
||||
break;
|
||||
}
|
||||
if (readUInt(header.h.signature) != 0x02014b50) {
|
||||
qWarning() << "QZip: invalid header signature, index may be incomplete";
|
||||
qWarning("QZip: invalid header signature, index may be incomplete");
|
||||
break;
|
||||
}
|
||||
|
||||
int l = readUShort(header.h.file_name_length);
|
||||
header.file_name = device->read(l);
|
||||
if (header.file_name.length() != l) {
|
||||
qWarning() << "QZip: Failed to read filename from zip index, index may be incomplete";
|
||||
qWarning("QZip: Failed to read filename from zip index, index may be incomplete");
|
||||
break;
|
||||
}
|
||||
l = readUShort(header.h.extra_field_length);
|
||||
header.extra_field = device->read(l);
|
||||
if (header.extra_field.length() != l) {
|
||||
qWarning() << "QZip: Failed to read extra field in zip file, skipping file, index may be incomplete";
|
||||
qWarning("QZip: Failed to read extra field in zip file, skipping file, index may be incomplete");
|
||||
break;
|
||||
}
|
||||
l = readUShort(header.h.file_comment_length);
|
||||
header.file_comment = device->read(l);
|
||||
if (header.file_comment.length() != l) {
|
||||
qWarning() << "QZip: Failed to read read file comment, index may be incomplete";
|
||||
qWarning("QZip: Failed to read read file comment, index may be incomplete");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -573,7 +641,7 @@ void MQZipReaderPrivate::scanFiles()
|
|||
void MQZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const QByteArray &contents/*, QFile::Permissions permissions, QZip::Method m*/)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
static const char *entryTypes[] = {
|
||||
static const char *const entryTypes[] = {
|
||||
"directory",
|
||||
"file ",
|
||||
"symlink " };
|
||||
|
@ -596,15 +664,15 @@ void MQZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const
|
|||
}
|
||||
|
||||
FileHeader header;
|
||||
memset(&header.h, 0, sizeof(MCentralFileHeader));
|
||||
memset(&header.h, 0, sizeof(CentralFileHeader));
|
||||
writeUInt(header.h.signature, 0x02014b50);
|
||||
|
||||
writeUShort(header.h.version_needed, 0x14);
|
||||
writeUShort(header.h.version_needed, ZIP_VERSION);
|
||||
writeUInt(header.h.uncompressed_size, contents.length());
|
||||
writeMSDosDate(header.h.last_mod_file, QDateTime::currentDateTime());
|
||||
QByteArray data = contents;
|
||||
if (compression == MQZipWriter::AlwaysCompress) {
|
||||
writeUShort(header.h.compression_method, 8);
|
||||
writeUShort(header.h.compression_method, CompressionMethodDeflated);
|
||||
|
||||
ulong len = contents.length();
|
||||
// shamelessly copied form zlib
|
||||
|
@ -634,22 +702,40 @@ void MQZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const
|
|||
crc_32 = ::crc32(crc_32, (const uchar *)contents.constData(), contents.length());
|
||||
writeUInt(header.h.crc_32, crc_32);
|
||||
|
||||
header.file_name = fileName.toUtf8();
|
||||
// if bit 11 is set, the filename and comment fields must be encoded using UTF-8
|
||||
ushort general_purpose_bits = Utf8Names; // always use utf-8
|
||||
writeUShort(header.h.general_purpose_bits, general_purpose_bits);
|
||||
|
||||
const bool inUtf8 = (general_purpose_bits & Utf8Names) != 0;
|
||||
header.file_name = inUtf8 ? fileName.toUtf8() : fileName.toLocal8Bit();
|
||||
if (header.file_name.size() > 0xffff) {
|
||||
qWarning("QZip: Filename too long, chopping it to 65535 characters");
|
||||
header.file_name = header.file_name.left(0xffff);
|
||||
qWarning("QZip: Filename is too long, chopping it to 65535 bytes");
|
||||
header.file_name = header.file_name.left(0xffff); // ### don't break the utf-8 sequence, if any
|
||||
}
|
||||
if (header.file_comment.size() + header.file_name.size() > 0xffff) {
|
||||
qWarning("QZip: File comment is too long, chopping it to 65535 bytes");
|
||||
header.file_comment.truncate(0xffff - header.file_name.size()); // ### don't break the utf-8 sequence, if any
|
||||
}
|
||||
writeUShort(header.h.file_name_length, header.file_name.length());
|
||||
//h.extra_field_length[2];
|
||||
|
||||
writeUShort(header.h.version_made, 3 << 8);
|
||||
writeUShort(header.h.version_made, HostUnix << 8);
|
||||
//uchar internal_file_attributes[2];
|
||||
//uchar external_file_attributes[4];
|
||||
quint32 mode = permissionsToMode(permissions);
|
||||
switch (type) {
|
||||
case File: mode |= S_IFREG; break;
|
||||
case Directory: mode |= S_IFDIR; break;
|
||||
case Symlink: mode |= S_IFLNK; break;
|
||||
case Symlink:
|
||||
mode |= UnixFileAttributes::SymLink;
|
||||
break;
|
||||
case Directory:
|
||||
mode |= UnixFileAttributes::Dir;
|
||||
break;
|
||||
case File:
|
||||
mode |= UnixFileAttributes::File;
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
writeUInt(header.h.external_file_attributes, mode << 16);
|
||||
writeUInt(header.h.offset_local_header, start_of_directory);
|
||||
|
@ -699,8 +785,8 @@ void MQZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const
|
|||
*/
|
||||
|
||||
/*!
|
||||
\variable FileInfo::crc32
|
||||
The calculated checksum as a crc32 type.
|
||||
\variable FileInfo::crc
|
||||
The calculated checksum as a crc type.
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
@ -708,12 +794,6 @@ void MQZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const
|
|||
The total size of the unpacked content.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable FileInfo::d
|
||||
\internal
|
||||
private pointer.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class QZipReader
|
||||
\internal
|
||||
|
@ -736,16 +816,17 @@ void MQZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const
|
|||
MQZipReader::MQZipReader(const QString &archive, QIODevice::OpenMode mode)
|
||||
{
|
||||
QScopedPointer<QFile> f(new QFile(archive));
|
||||
f->open(mode);
|
||||
const bool result = f->open(mode);
|
||||
MQZipReader::Status status;
|
||||
if (f->error() == QFile::NoError)
|
||||
const QFileDevice::FileError error = f->error();
|
||||
if (result && error == QFile::NoError) {
|
||||
status = NoError;
|
||||
else {
|
||||
if (f->error() == QFile::ReadError)
|
||||
} else {
|
||||
if (error == QFile::ReadError)
|
||||
status = FileReadError;
|
||||
else if (f->error() == QFile::OpenError)
|
||||
else if (error == QFile::OpenError)
|
||||
status = FileOpenError;
|
||||
else if (f->error() == QFile::PermissionsError)
|
||||
else if (error == QFile::PermissionsError)
|
||||
status = FilePermissionsError;
|
||||
else
|
||||
status = FileError;
|
||||
|
@ -785,7 +866,7 @@ QIODevice* MQZipReader::device() const
|
|||
}
|
||||
|
||||
/*!
|
||||
Returns true if the user can read the file; otherwise returns false.
|
||||
Returns \c true if the user can read the file; otherwise returns \c false.
|
||||
*/
|
||||
bool MQZipReader::isReadable() const
|
||||
{
|
||||
|
@ -793,7 +874,7 @@ bool MQZipReader::isReadable() const
|
|||
}
|
||||
|
||||
/*!
|
||||
Returns true if the file exists; otherwise returns false.
|
||||
Returns \c true if the file exists; otherwise returns \c false.
|
||||
*/
|
||||
bool MQZipReader::exists() const
|
||||
{
|
||||
|
@ -806,15 +887,14 @@ bool MQZipReader::exists() const
|
|||
/*!
|
||||
Returns the list of files the archive contains.
|
||||
*/
|
||||
QList<MQZipReader::FileInfo> MQZipReader::fileInfoList() const
|
||||
QVector<MQZipReader::FileInfo> MQZipReader::fileInfoList() const
|
||||
{
|
||||
d->scanFiles();
|
||||
QList<MQZipReader::FileInfo> files;
|
||||
for (int i = 0; i < d->fileHeaders.size(); ++i) {
|
||||
MQZipReader::FileInfo fi;
|
||||
d->fillFileInfo(i, fi);
|
||||
files.append(fi);
|
||||
}
|
||||
QVector<FileInfo> files;
|
||||
const int numFileHeaders = d->fileHeaders.size();
|
||||
files.reserve(numFileHeaders);
|
||||
for (int i = 0; i < numFileHeaders; ++i)
|
||||
files.append(d->fillFileInfo(i));
|
||||
return files;
|
||||
|
||||
}
|
||||
|
@ -838,10 +918,9 @@ int MQZipReader::count() const
|
|||
MQZipReader::FileInfo MQZipReader::entryInfoAt(int index) const
|
||||
{
|
||||
d->scanFiles();
|
||||
MQZipReader::FileInfo fi;
|
||||
if (index >= 0 && index < d->fileHeaders.count())
|
||||
d->fillFileInfo(index, fi);
|
||||
return fi;
|
||||
return d->fillFileInfo(index);
|
||||
return MQZipReader::FileInfo();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -860,6 +939,13 @@ QByteArray MQZipReader::fileData(const QString &fileName) const
|
|||
|
||||
FileHeader header = d->fileHeaders.at(i);
|
||||
|
||||
ushort version_needed = readUShort(header.h.version_needed);
|
||||
if (version_needed > ZIP_VERSION) {
|
||||
qWarning("QZip: .ZIP specification version %d implementationis needed to extract the data.", version_needed);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
|
||||
int compressed_size = readUInt(header.h.compressed_size);
|
||||
int uncompressed_size = readUInt(header.h.uncompressed_size);
|
||||
int start = readUInt(header.h.offset_local_header);
|
||||
|
@ -874,13 +960,18 @@ QByteArray MQZipReader::fileData(const QString &fileName) const
|
|||
int compression_method = readUShort(lh.compression_method);
|
||||
//qDebug("file=%s: compressed_size=%d, uncompressed_size=%d", fileName.toLocal8Bit().data(), compressed_size, uncompressed_size);
|
||||
|
||||
if ((general_purpose_bits & Encrypted) != 0) {
|
||||
qWarning("QZip: Unsupported encryption method is needed to extract the data.");
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
//qDebug("file at %lld", d->device->pos());
|
||||
QByteArray compressed = d->device->read(compressed_size);
|
||||
if (compression_method == 0) {
|
||||
if (compression_method == CompressionMethodStored) {
|
||||
// no compression
|
||||
compressed.truncate(uncompressed_size);
|
||||
return compressed;
|
||||
} else if (compression_method == 8) {
|
||||
} else if (compression_method == CompressionMethodDeflated) {
|
||||
// Deflate
|
||||
//qDebug("compressed=%d", compressed.size());
|
||||
compressed.truncate(compressed_size);
|
||||
|
@ -890,7 +981,7 @@ QByteArray MQZipReader::fileData(const QString &fileName) const
|
|||
do {
|
||||
baunzip.resize(len);
|
||||
res = inflate((uchar*)baunzip.data(), &len,
|
||||
(uchar*)compressed.constData(), compressed_size);
|
||||
(const uchar*)compressed.constData(), compressed_size);
|
||||
|
||||
switch (res) {
|
||||
case Z_OK:
|
||||
|
@ -910,7 +1001,8 @@ QByteArray MQZipReader::fileData(const QString &fileName) const
|
|||
} while (res == Z_BUF_ERROR);
|
||||
return baunzip;
|
||||
}
|
||||
qWarning() << "QZip: Unknown compression method";
|
||||
|
||||
qWarning("QZip: Unsupported compression method %d is needed to extract the data.", compression_method);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
|
@ -924,8 +1016,8 @@ bool MQZipReader::extractAll(const QString &destinationDir) const
|
|||
QDir baseDir(destinationDir);
|
||||
|
||||
// create directories first
|
||||
QList<FileInfo> allFiles = fileInfoList();
|
||||
foreach (FileInfo fi, allFiles) {
|
||||
const QVector<FileInfo> allFiles = fileInfoList();
|
||||
for (const FileInfo &fi : allFiles) {
|
||||
const QString absPath = destinationDir + QDir::separator() + fi.filePath;
|
||||
if (fi.isDir) {
|
||||
if (!baseDir.mkpath(fi.filePath))
|
||||
|
@ -936,7 +1028,7 @@ bool MQZipReader::extractAll(const QString &destinationDir) const
|
|||
}
|
||||
|
||||
// set up symlinks
|
||||
foreach (FileInfo fi, allFiles) {
|
||||
for (const FileInfo &fi : allFiles) {
|
||||
const QString absPath = destinationDir + QDir::separator() + fi.filePath;
|
||||
if (fi.isSymLink) {
|
||||
QString destination = QFile::decodeName(fileData(fi.filePath));
|
||||
|
@ -954,7 +1046,7 @@ bool MQZipReader::extractAll(const QString &destinationDir) const
|
|||
}
|
||||
}
|
||||
|
||||
foreach (FileInfo fi, allFiles) {
|
||||
for (const FileInfo &fi : allFiles) {
|
||||
const QString absPath = destinationDir + QDir::separator() + fi.filePath;
|
||||
if (fi.isFile) {
|
||||
QFile f(absPath);
|
||||
|
@ -1021,9 +1113,8 @@ void MQZipReader::close()
|
|||
MQZipWriter::MQZipWriter(const QString &fileName, QIODevice::OpenMode mode)
|
||||
{
|
||||
QScopedPointer<QFile> f(new QFile(fileName));
|
||||
f->open(mode);
|
||||
MQZipWriter::Status status;
|
||||
if (f->error() == QFile::NoError)
|
||||
if (f->open(mode) && f->error() == QFile::NoError)
|
||||
status = MQZipWriter::NoError;
|
||||
else {
|
||||
if (f->error() == QFile::WriteError)
|
||||
|
@ -1067,7 +1158,7 @@ QIODevice* MQZipWriter::device() const
|
|||
}
|
||||
|
||||
/*!
|
||||
Returns true if the user can write to the archive; otherwise returns false.
|
||||
Returns \c true if the user can write to the archive; otherwise returns \c false.
|
||||
*/
|
||||
bool MQZipWriter::isWritable() const
|
||||
{
|
||||
|
@ -1075,7 +1166,7 @@ bool MQZipWriter::isWritable() const
|
|||
}
|
||||
|
||||
/*!
|
||||
Returns true if the file exists; otherwise returns false.
|
||||
Returns \c true if the file exists; otherwise returns \c false.
|
||||
*/
|
||||
bool MQZipWriter::exists() const
|
||||
{
|
||||
|
@ -1175,7 +1266,7 @@ QFile::Permissions MQZipWriter::creationPermissions() const
|
|||
*/
|
||||
void MQZipWriter::addFile(const QString &fileName, const QByteArray &data)
|
||||
{
|
||||
d->addEntry(MQZipWriterPrivate::File, fileName, data);
|
||||
d->addEntry(MQZipWriterPrivate::File, QDir::fromNativeSeparators(fileName), data);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -1197,7 +1288,7 @@ void MQZipWriter::addFile(const QString &fileName, QIODevice *device)
|
|||
return;
|
||||
}
|
||||
}
|
||||
d->addEntry(MQZipWriterPrivate::File, fileName, device->readAll());
|
||||
d->addEntry(MQZipWriterPrivate::File, QDir::fromNativeSeparators(fileName), device->readAll());
|
||||
if (opened)
|
||||
device->close();
|
||||
}
|
||||
|
@ -1208,10 +1299,10 @@ void MQZipWriter::addFile(const QString &fileName, QIODevice *device)
|
|||
*/
|
||||
void MQZipWriter::addDirectory(const QString &dirName)
|
||||
{
|
||||
QString name = dirName;
|
||||
QString name(QDir::fromNativeSeparators(dirName));
|
||||
// separator is mandatory
|
||||
if (!name.endsWith(QDir::separator()))
|
||||
name.append(QDir::separator());
|
||||
if (!name.endsWith(QLatin1Char('/')))
|
||||
name.append(QLatin1Char('/'));
|
||||
d->addEntry(MQZipWriterPrivate::Directory, name, QByteArray());
|
||||
}
|
||||
|
||||
|
@ -1222,7 +1313,7 @@ void MQZipWriter::addDirectory(const QString &dirName)
|
|||
*/
|
||||
void MQZipWriter::addSymLink(const QString &fileName, const QString &destination)
|
||||
{
|
||||
d->addEntry(MQZipWriterPrivate::Symlink, fileName, QFile::encodeName(destination));
|
||||
d->addEntry(MQZipWriterPrivate::Symlink, QDir::fromNativeSeparators(fileName), QFile::encodeName(destination));
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -1240,7 +1331,7 @@ void MQZipWriter::close()
|
|||
// write new directory
|
||||
for (int i = 0; i < d->fileHeaders.size(); ++i) {
|
||||
const FileHeader &header = d->fileHeaders.at(i);
|
||||
d->device->write((const char *)&header.h, sizeof(MCentralFileHeader));
|
||||
d->device->write((const char *)&header.h, sizeof(CentralFileHeader));
|
||||
d->device->write(header.file_name);
|
||||
d->device->write(header.extra_field);
|
||||
d->device->write(header.file_comment);
|
||||
|
|
16
thirdparty/qzip/qzipreader_p.h
vendored
16
thirdparty/qzip/qzipreader_p.h
vendored
|
@ -78,23 +78,23 @@ public:
|
|||
|
||||
struct FileInfo
|
||||
{
|
||||
FileInfo();
|
||||
FileInfo(const FileInfo &other);
|
||||
~FileInfo();
|
||||
FileInfo &operator=(const FileInfo &other);
|
||||
bool isValid() const;
|
||||
FileInfo() Q_DECL_NOTHROW
|
||||
: isDir(false), isFile(false), isSymLink(false), crc(0), size(0)
|
||||
{}
|
||||
|
||||
bool isValid() const Q_DECL_NOTHROW { return isDir || isFile || isSymLink; }
|
||||
|
||||
QString filePath;
|
||||
uint isDir : 1;
|
||||
uint isFile : 1;
|
||||
uint isSymLink : 1;
|
||||
QFile::Permissions permissions;
|
||||
uint crc32;
|
||||
uint crc;
|
||||
qint64 size;
|
||||
QDateTime lastModified;
|
||||
void *d;
|
||||
};
|
||||
|
||||
QList<FileInfo> fileInfoList() const;
|
||||
QVector<FileInfo> fileInfoList() const;
|
||||
int count() const;
|
||||
|
||||
FileInfo entryInfoAt(int index) const;
|
||||
|
|
|
@ -43,7 +43,7 @@ Sample::~Sample()
|
|||
Sample* ZInstrument::readSample(const QString& s, MQZipReader* uz)
|
||||
{
|
||||
if (uz) {
|
||||
QList<MQZipReader::FileInfo> fi = uz->fileInfoList();
|
||||
QVector<MQZipReader::FileInfo> fi = uz->fileInfoList();
|
||||
|
||||
buf = uz->fileData(s);
|
||||
if (buf.isEmpty()) {
|
||||
|
|
|
@ -446,8 +446,8 @@ void SfzRegion::readOp(const QString& b, const QString& data, SfzControl &c)
|
|||
loop_mode = LoopMode::CONTINUOUS;
|
||||
else if (opcode_data == "loop_sustain")
|
||||
loop_mode = LoopMode::SUSTAIN;
|
||||
if (loop_mode != LoopMode::ONE_SHOT)
|
||||
qDebug("SfzRegion: loop_mode <%s>", qPrintable(opcode_data));
|
||||
//if (loop_mode != LoopMode::ONE_SHOT)
|
||||
// qDebug("SfzRegion: loop_mode <%s>", qPrintable(opcode_data));
|
||||
}
|
||||
else if(opcode == "loop_start")
|
||||
readLongLong(opcode_data, loopStart);
|
||||
|
|
|
@ -47,8 +47,8 @@ Zerberus::Zerberus()
|
|||
initialized = true;
|
||||
Voice::init();
|
||||
}
|
||||
for (int i = 0; i < MAX_VOICES; ++i)
|
||||
freeVoices.push(new Voice(this));
|
||||
|
||||
freeVoices.init(this);
|
||||
for (int i = 0; i < MAX_CHANNEL; ++i)
|
||||
_channel[i] = new Channel(this, i);
|
||||
busy = true; // no sf loaded yet
|
||||
|
|
|
@ -17,11 +17,14 @@
|
|||
#include <atomic>
|
||||
// #include <mutex>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include "synthesizer/synthesizer.h"
|
||||
#include "synthesizer/event.h"
|
||||
#include "voice.h"
|
||||
|
||||
|
||||
class Channel;
|
||||
class ZInstrument;
|
||||
enum class Trigger : char;
|
||||
|
@ -35,32 +38,33 @@ static const int MAX_TRIGGER = 512;
|
|||
//---------------------------------------------------------
|
||||
|
||||
class VoiceFifo {
|
||||
Voice* buffer[MAX_VOICES];
|
||||
std::atomic<int> n;
|
||||
int writeIdx = 0; // index of next slot to write
|
||||
int readIdx = 0; // index of slot to read
|
||||
std::queue<Voice*> buffer;
|
||||
std::vector< std::unique_ptr<Voice> > voices;
|
||||
|
||||
public:
|
||||
VoiceFifo() {
|
||||
n = 0;
|
||||
voices.resize(MAX_VOICES);
|
||||
}
|
||||
~VoiceFifo() {
|
||||
for (Voice* v : buffer)
|
||||
delete v;
|
||||
|
||||
void init(Zerberus* z) {
|
||||
for (int i = 0; i < MAX_VOICES; ++i) {
|
||||
voices.push_back(std::unique_ptr<Voice>(new Voice(z)));
|
||||
buffer.push(voices.back().get());
|
||||
}
|
||||
}
|
||||
|
||||
void push(Voice* v) {
|
||||
buffer[writeIdx++] = v;
|
||||
writeIdx %= MAX_VOICES;
|
||||
++n;
|
||||
buffer.push(v);
|
||||
}
|
||||
Voice* pop() {
|
||||
Q_ASSERT(n != 0);
|
||||
--n;
|
||||
Voice* v = buffer[readIdx++];
|
||||
readIdx %= MAX_VOICES;
|
||||
|
||||
Voice* pop() {
|
||||
Q_ASSERT(!buffer.empty());
|
||||
Voice* v = buffer.front();
|
||||
buffer.pop();
|
||||
return v;
|
||||
}
|
||||
bool empty() const { return n == 0; }
|
||||
|
||||
bool empty() const { return buffer.empty(); }
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "zerberusgui.h"
|
||||
|
||||
#include "mscore/preferences.h"
|
||||
#include "mscore/extension.h"
|
||||
|
||||
//---------------------------------------------------------
|
||||
// SfzListDialog
|
||||
|
@ -137,6 +138,10 @@ QFileInfoList Zerberus::sfzFiles()
|
|||
QStringList pl = Ms::preferences.getString(PREF_APP_PATHS_MYSOUNDFONTS).split(";");
|
||||
pl.prepend(QFileInfo(QString("%1%2").arg(Ms::mscoreGlobalShare).arg("sound")).absoluteFilePath());
|
||||
|
||||
// append extensions directory
|
||||
QStringList extensionsDir = Ms::Extension::getDirectoriesByType(Ms::Extension::sfzsDir);
|
||||
pl.append(extensionsDir);
|
||||
|
||||
foreach (const QString& s, pl) {
|
||||
QString ss(s);
|
||||
if (!s.isEmpty() && s[0] == '~')
|
||||
|
|
Loading…
Reference in a new issue