Basic model/view action works
This commit is contained in:
parent
752f270cf8
commit
2deadc6cfb
15 changed files with 439 additions and 133 deletions
|
|
@ -14,8 +14,8 @@ SQLiteSaveFile::SQLiteSaveFile(QObject *parent) :
|
|||
|
||||
bool SQLiteSaveFile::open(const QString &filename)
|
||||
{
|
||||
qDebug() << "open";
|
||||
{
|
||||
auto dbg = qDebug() << "open";
|
||||
{ /* Emit signals only after unlocking mutex to allow accesses by receivers */
|
||||
QMutexLocker l(&m_dbMut);
|
||||
QFile f(filename);
|
||||
if (!f.exists()) {
|
||||
|
|
@ -44,15 +44,16 @@ bool SQLiteSaveFile::open(const QString &filename)
|
|||
m_dirty = false;
|
||||
m_open = true;
|
||||
m_image = q.value(0).toByteArray();
|
||||
dbg << QString("Loaded %1 byte image").arg(m_image.size());
|
||||
}
|
||||
fileReload(); /* Call after unlocking mutex to allow accesses by receivers */
|
||||
fileReload();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::clearNew()
|
||||
{
|
||||
qDebug() << "clearNew";
|
||||
{
|
||||
{ /* Emit signals only after unlocking mutex to allow accesses by receivers */
|
||||
QMutexLocker l(&m_dbMut);
|
||||
|
||||
QSqlDatabase new_db(QSqlDatabase::addDatabase("QSQLITE", QUuid::createUuid().toString()));
|
||||
|
|
@ -148,7 +149,7 @@ bool SQLiteSaveFile::saveAs(const QString &filename)
|
|||
|
||||
QList<Tag> SQLiteSaveFile::getAllTags()
|
||||
{
|
||||
qDebug() << "getAllTags";
|
||||
auto dbg = qDebug() << "getAllTags()";
|
||||
QMutexLocker l(&m_dbMut);
|
||||
resetError();
|
||||
QList<Tag> rv;
|
||||
|
|
@ -170,56 +171,134 @@ QList<Tag> SQLiteSaveFile::getAllTags()
|
|||
if (!setDatabaseError(q))
|
||||
return QList<Tag>();
|
||||
|
||||
dbg << QString("%1 tags").arg(rv.size());
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::updateTag(Tag tag)
|
||||
{
|
||||
QMutexLocker l(&m_dbMut);
|
||||
if (!runSql("UPDATE tags SET name=?, anchor_x=?, anchor_y=?, meta=? WHERE id=?", {
|
||||
tag.name, tag.anchor.x(), tag.anchor.y(), QJsonDocument::fromVariant(tag.metadata).toJson(), tag.id
|
||||
}))
|
||||
return false;
|
||||
auto dbg = qDebug() << "updating tag" << tag.id << tag.name;
|
||||
{
|
||||
QMutexLocker l(&m_dbMut);
|
||||
QSqlQuery q(m_db);
|
||||
q.prepare("SELECT 1 FROM tags WHERE id=? AND name=?");
|
||||
q.addBindValue(tag.id);
|
||||
q.addBindValue(tag.name);
|
||||
if (!q.exec()) {
|
||||
setDatabaseError(q);
|
||||
return false;
|
||||
}
|
||||
bool nameChanged = !q.next();
|
||||
if (!setDatabaseError(q))
|
||||
return false;
|
||||
|
||||
m_dirty = true;
|
||||
if (nameChanged)
|
||||
setMetaLocked("lastTagName", tag.name);
|
||||
|
||||
if (!runSql("UPDATE tags SET name=?, anchor_x=?, anchor_y=?, meta=? WHERE id=?", {
|
||||
tag.name, tag.anchor.x(), tag.anchor.y(), QJsonDocument::fromVariant(tag.metadata).toJson(), tag.id
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
dbg << "calling handlers";
|
||||
tagChange(TagChange::CHANGED, tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::deleteTag(Tag tag)
|
||||
{
|
||||
QMutexLocker l(&m_dbMut);
|
||||
if (!runSql("DELETE FROM tags WHERE id=?", {tag.id}))
|
||||
return false;
|
||||
{
|
||||
QMutexLocker l(&m_dbMut);
|
||||
if (!runSql("DELETE FROM tags WHERE id=?", {tag.id}))
|
||||
return false;
|
||||
|
||||
m_dirty = true;
|
||||
m_dirty = true;
|
||||
}
|
||||
tagChange(TagChange::DELETED, tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::createTag(Tag tag)
|
||||
{
|
||||
qDebug() << "createTag";
|
||||
QMutexLocker l(&m_dbMut);
|
||||
resetError();
|
||||
QSqlQuery q(m_db);
|
||||
q.prepare("INSERT INTO tags(name, anchor_x, anchor_y, meta) VALUES (?, ?, ?, ?)");
|
||||
q.addBindValue(tag.name);
|
||||
q.addBindValue(tag.anchor.x());
|
||||
q.addBindValue(tag.anchor.y());
|
||||
q.addBindValue(QJsonDocument::fromVariant(tag.metadata).toJson());
|
||||
auto dbg = qDebug() << "createTag";
|
||||
{
|
||||
QMutexLocker l(&m_dbMut);
|
||||
resetError();
|
||||
if (!runSql("INSERT INTO tags(name, anchor_x, anchor_y, meta) VALUES (?, ?, ?, ?)", {
|
||||
tag.name,
|
||||
tag.anchor.x(),
|
||||
tag.anchor.y(),
|
||||
QJsonDocument::fromVariant(tag.metadata).toJson()
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
m_dirty = true;
|
||||
}
|
||||
auto id = q.lastInsertId().toLongLong();
|
||||
dbg << "id" << id;
|
||||
tagChange(TagChange::CREATED, Tag(id, tag));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!setDatabaseError(q))
|
||||
QString SQLiteSaveFile::getNextAutoTagName()
|
||||
{
|
||||
QVariant lookupResult = getMeta("lastTagName");
|
||||
QString lastTagName = "U0";
|
||||
if (lookupResult.isValid() && !lookupResult.toString().isNull())
|
||||
lastTagName = lookupResult.toString();
|
||||
QString newName = "U1";
|
||||
|
||||
QRegularExpression name_re("^(.*?)(\\d+)$");
|
||||
auto res = name_re.match(lastTagName);
|
||||
if (res.hasMatch()) {
|
||||
bool ok = false;
|
||||
int numericSuffix = res.captured(2).toInt(&ok);
|
||||
QString stringPrefix = res.captured(1);
|
||||
qDebug() << "Name has match" << stringPrefix << numericSuffix;
|
||||
|
||||
if (ok) {
|
||||
do {
|
||||
numericSuffix ++;
|
||||
newName = QString("%1%2").arg(stringPrefix).arg(numericSuffix);
|
||||
} while (numericSuffix<10000 && !tagNameIsFree(newName));
|
||||
}
|
||||
}
|
||||
return newName;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::createTagAt(const QPointF &anchor)
|
||||
{
|
||||
QString newName = getNextAutoTagName();
|
||||
if (!setMeta("lastTagName", newName))
|
||||
return false;
|
||||
return createTag(Tag(newName, anchor));
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::tagNameIsFree(const QString &name)
|
||||
{
|
||||
QMutexLocker l(&m_dbMut);
|
||||
QSqlQuery q(m_db);
|
||||
q.prepare("SELECT name FROM tags WHERE name=?");
|
||||
q.addBindValue(name);
|
||||
if (!q.exec()) {
|
||||
setDatabaseError(q);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (q.next())
|
||||
return false;
|
||||
|
||||
Tag created_tag(q.lastInsertId().toLongLong(), tag);
|
||||
m_dirty = true;
|
||||
tagChange(TagChange::CREATED, created_tag);
|
||||
setDatabaseError(q);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::setMetaLocked(const QString &key, const QVariant &value)
|
||||
{
|
||||
qDebug() << QString("setMeta: %1=%2").arg(key).arg(value.toString());
|
||||
return runSql("INSERT OR REPLACE INTO metadata(key, value) VALUES (?, ?)", {key, value});
|
||||
}
|
||||
|
||||
|
|
@ -232,15 +311,15 @@ bool SQLiteSaveFile::setMetaLocked(std::initializer_list<QPair<QString, QVariant
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::setMeta(const QString &key, const QVariant &value) {
|
||||
bool SQLiteSaveFile::setMeta(const QString &key, const QVariant &value, bool setDirty) {
|
||||
QMutexLocker l(&m_dbMut);
|
||||
m_dirty = true;
|
||||
m_dirty = m_dirty || setDirty;
|
||||
return setMetaLocked(key, value);
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::setMeta(std::initializer_list<QPair<QString, QVariant>> metas) {
|
||||
bool SQLiteSaveFile::setMeta(std::initializer_list<QPair<QString, QVariant>> metas, bool setDirty) {
|
||||
QMutexLocker l(&m_dbMut);
|
||||
m_dirty = true;
|
||||
m_dirty = m_dirty || setDirty;
|
||||
return setMetaLocked(metas);
|
||||
}
|
||||
|
||||
|
|
@ -251,7 +330,6 @@ const QVariant SQLiteSaveFile::getMeta(const QString &key) const {
|
|||
|
||||
const QVariant SQLiteSaveFile::getMetaLocked(const QString &key) const
|
||||
{
|
||||
qDebug() << "getMeta " << key;
|
||||
resetError();
|
||||
QSqlQuery q(m_db);
|
||||
q.prepare("SELECT value FROM metadata WHERE key=?");
|
||||
|
|
@ -267,6 +345,7 @@ const QVariant SQLiteSaveFile::getMetaLocked(const QString &key) const
|
|||
return QVariant();
|
||||
}
|
||||
|
||||
qDebug() << QString("getMeta: %1=%2").arg(key).arg(q.value(0).toString());
|
||||
return q.value(0);
|
||||
}
|
||||
|
||||
|
|
@ -288,29 +367,35 @@ bool SQLiteSaveFile::runSql(QString query, std::initializer_list<QVariant> bindi
|
|||
|
||||
bool SQLiteSaveFile::loadImageFromDisk(const QString &filename)
|
||||
{
|
||||
QMutexLocker l(&m_dbMut);
|
||||
QFile f(filename);
|
||||
resetError();
|
||||
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
setError(ImageOpenError, QString("Failed to open image: %1").arg(f.errorString()));
|
||||
return false;
|
||||
{
|
||||
QMutexLocker l(&m_dbMut);
|
||||
QFile f(filename);
|
||||
resetError();
|
||||
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
setError(ImageOpenError, QString("Failed to open image: %1").arg(f.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_image = f.readAll();
|
||||
if (f.error() != QFileDevice::NoError) {
|
||||
setError(ImageReadError, QString("Failed to read image: %1").arg(f.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setMetaLocked({
|
||||
{"imagePathOriginal", f.fileName()},
|
||||
{"imagePathAbsolute", QFileInfo(f).absoluteFilePath()},
|
||||
{"imageLoadedTime", QDateTime::currentDateTimeUtc().toMSecsSinceEpoch()}}))
|
||||
return false;
|
||||
|
||||
m_dirty = true;
|
||||
if (!runSql("INSERT OR REPLACE INTO blobs(name, data) VALUES ('image', ?)", {m_image}))
|
||||
return false;
|
||||
}
|
||||
|
||||
m_image = f.readAll();
|
||||
if (f.error() != QFileDevice::NoError) {
|
||||
setError(ImageReadError, QString("Failed to read image: %1").arg(f.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setMetaLocked({
|
||||
{"imagePathOriginal", f.fileName()},
|
||||
{"imagePathAbsolute", QFileInfo(f).absoluteFilePath()},
|
||||
{"imageLoadedTime", QDateTime::currentDateTimeUtc().toMSecsSinceEpoch()}}))
|
||||
return false;
|
||||
|
||||
m_dirty = true;
|
||||
return runSql("INSERT OR REPLACE INTO blobs(name, data) VALUES ('image', ?)", {m_image});
|
||||
/* Emit signal with mutex unlocked */
|
||||
imageLoaded(m_image);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::reloadImageFromDisk()
|
||||
|
|
@ -344,6 +429,15 @@ Tag::Tag(long long id, const Tag &other)
|
|||
{
|
||||
}
|
||||
|
||||
Tag::Tag(QString name, const QPointF &anchor, const QVariantMap metadata)
|
||||
: id(-1)
|
||||
, name(name)
|
||||
, anchor(anchor)
|
||||
, metadata(metadata)
|
||||
, valid(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool SQLiteSaveFile::setDatabaseError(const QSqlQuery &q) const
|
||||
{
|
||||
if (!q.lastError().isValid())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue