1114 lines
26 KiB
C++
1114 lines
26 KiB
C++
|
/*
|
||
|
|
||
|
Copyright 2014 S. Razi Alavizadeh
|
||
|
Copyright 2012-2018 Adam Reichold
|
||
|
Copyright 2012 Michał Trybus
|
||
|
Copyright 2018 Egor Zenkov
|
||
|
|
||
|
This file is part of qpdfview.
|
||
|
|
||
|
qpdfview is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 2 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
qpdfview 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 qpdfview. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "database.h"
|
||
|
|
||
|
#include <QApplication>
|
||
|
#include <QCryptographicHash>
|
||
|
#include <QDateTime>
|
||
|
#include <QDebug>
|
||
|
#include <QDir>
|
||
|
|
||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||
|
|
||
|
#include <QStandardPaths>
|
||
|
|
||
|
#else
|
||
|
|
||
|
#include <QDesktopServices>
|
||
|
|
||
|
#endif // QT_VERSION
|
||
|
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
#include <QSqlError>
|
||
|
#include <QSqlQuery>
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
|
||
|
#include "settings.h"
|
||
|
#include "documentview.h"
|
||
|
#include "bookmarkmodel.h"
|
||
|
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
namespace
|
||
|
{
|
||
|
|
||
|
using namespace qpdfview;
|
||
|
|
||
|
class Transaction
|
||
|
{
|
||
|
public:
|
||
|
Transaction(QSqlDatabase& database) :
|
||
|
m_database(database),
|
||
|
m_committed(false)
|
||
|
{
|
||
|
if(s_current != 0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!m_database.transaction())
|
||
|
{
|
||
|
throw m_database.lastError();
|
||
|
}
|
||
|
|
||
|
s_current = this;
|
||
|
}
|
||
|
|
||
|
~Transaction() throw()
|
||
|
{
|
||
|
if(s_current != this)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!m_committed)
|
||
|
{
|
||
|
m_database.rollback();
|
||
|
}
|
||
|
|
||
|
s_current = 0;
|
||
|
}
|
||
|
|
||
|
void commit()
|
||
|
{
|
||
|
if(s_current != this)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!m_database.commit())
|
||
|
{
|
||
|
throw m_database.lastError();
|
||
|
}
|
||
|
|
||
|
m_committed = true;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
Q_DISABLE_COPY(Transaction)
|
||
|
|
||
|
QSqlDatabase& m_database;
|
||
|
bool m_committed;
|
||
|
|
||
|
static Transaction* s_current;
|
||
|
|
||
|
};
|
||
|
|
||
|
Transaction* Transaction::s_current = 0;
|
||
|
|
||
|
class Query
|
||
|
{
|
||
|
private:
|
||
|
template< typename T >
|
||
|
class BindValue
|
||
|
{
|
||
|
private:
|
||
|
friend class Query;
|
||
|
|
||
|
explicit BindValue(const T& value) : m_value(value) {}
|
||
|
|
||
|
public:
|
||
|
inline operator QVariant() const
|
||
|
{
|
||
|
return Conversion< T >::convert(m_value);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const T& m_value;
|
||
|
|
||
|
template< typename S, bool Defined = QMetaTypeId2< S >::Defined >
|
||
|
struct Conversion
|
||
|
{
|
||
|
static inline QVariant convert(const S& value)
|
||
|
{
|
||
|
return QVariant(value);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template< typename S >
|
||
|
struct Conversion< S, false >
|
||
|
{
|
||
|
static inline QVariant convert(const S& value)
|
||
|
{
|
||
|
return QVariant(static_cast< uint >(value));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
public:
|
||
|
class Value
|
||
|
{
|
||
|
private:
|
||
|
friend class Query;
|
||
|
|
||
|
explicit Value(const QVariant& value) : m_value(value) {}
|
||
|
|
||
|
public:
|
||
|
template< typename T >
|
||
|
inline operator T() const
|
||
|
{
|
||
|
return Conversion< T >::convert(m_value);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const QVariant m_value;
|
||
|
|
||
|
template< typename T, bool Defined = QMetaTypeId2< T >::Defined >
|
||
|
struct Conversion
|
||
|
{
|
||
|
static inline T convert(const QVariant& value)
|
||
|
{
|
||
|
return value.value< T >();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template< typename T >
|
||
|
struct Conversion< T, false >
|
||
|
{
|
||
|
static inline T convert(const QVariant& value)
|
||
|
{
|
||
|
return static_cast< T >(value.value< uint >());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
public:
|
||
|
Query(QSqlDatabase& database) :
|
||
|
m_query(database),
|
||
|
m_bindValueIndex(0),
|
||
|
m_valueIndex(0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void prepare(const QString& query)
|
||
|
{
|
||
|
if(!m_query.prepare(query))
|
||
|
{
|
||
|
throw m_query.lastError();
|
||
|
}
|
||
|
|
||
|
m_bindValueIndex = 0;
|
||
|
}
|
||
|
|
||
|
void exec()
|
||
|
{
|
||
|
if(!m_query.exec())
|
||
|
{
|
||
|
throw m_query.lastError();
|
||
|
}
|
||
|
|
||
|
m_bindValueIndex = 0;
|
||
|
}
|
||
|
|
||
|
void exec(const QString& query)
|
||
|
{
|
||
|
if(!m_query.exec(query))
|
||
|
{
|
||
|
throw m_query.lastError();
|
||
|
}
|
||
|
|
||
|
m_bindValueIndex = 0;
|
||
|
}
|
||
|
|
||
|
Query& operator <<(const QVariant& value)
|
||
|
{
|
||
|
m_query.bindValue(m_bindValueIndex++, value);
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template< typename T >
|
||
|
Query& operator <<(const T& value)
|
||
|
{
|
||
|
m_query.bindValue(m_bindValueIndex++, BindValue< T >(value));
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
bool nextRecord()
|
||
|
{
|
||
|
if(!m_query.isActive())
|
||
|
{
|
||
|
throw m_query.lastError();
|
||
|
}
|
||
|
|
||
|
m_valueIndex = 0;
|
||
|
|
||
|
return m_query.next();
|
||
|
}
|
||
|
|
||
|
Value nextValue()
|
||
|
{
|
||
|
return Value(m_query.value(m_valueIndex++));
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
Q_DISABLE_COPY(Query)
|
||
|
|
||
|
QSqlQuery m_query;
|
||
|
int m_bindValueIndex;
|
||
|
int m_valueIndex;
|
||
|
|
||
|
};
|
||
|
|
||
|
inline QByteArray hashFilePath(const QString& filePath)
|
||
|
{
|
||
|
return QCryptographicHash::hash(filePath.toUtf8(), QCryptographicHash::Sha1).toBase64();
|
||
|
}
|
||
|
|
||
|
} // anonymous
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
|
||
|
namespace qpdfview
|
||
|
{
|
||
|
|
||
|
Database* Database::s_instance = 0;
|
||
|
|
||
|
Database* Database::instance()
|
||
|
{
|
||
|
if(s_instance == 0)
|
||
|
{
|
||
|
s_instance = new Database(qApp);
|
||
|
}
|
||
|
|
||
|
return s_instance;
|
||
|
}
|
||
|
|
||
|
Database::~Database()
|
||
|
{
|
||
|
s_instance = 0;
|
||
|
}
|
||
|
|
||
|
QStringList Database::knownInstanceNames()
|
||
|
{
|
||
|
QStringList instanceNames;
|
||
|
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.exec("SELECT DISTINCT(instanceName) FROM tabs_v5");
|
||
|
|
||
|
while(query.nextRecord())
|
||
|
{
|
||
|
const QString instanceName = query.nextValue();
|
||
|
|
||
|
if(!instanceName.isEmpty())
|
||
|
{
|
||
|
instanceNames.append(instanceName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
|
||
|
return instanceNames;
|
||
|
}
|
||
|
|
||
|
void Database::restoreTabs(const RestoreTab& restoreTab)
|
||
|
{
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.prepare("SELECT filePath,currentPage,continuousMode,layoutMode,rightToLeftMode,scaleMode,scaleFactor,rotation,renderFlags,firstPage"
|
||
|
" FROM tabs_v5 WHERE instanceName==? ORDER BY tabIndex");
|
||
|
|
||
|
query << instanceName();
|
||
|
|
||
|
query.exec();
|
||
|
|
||
|
while(query.nextRecord())
|
||
|
{
|
||
|
if(DocumentView* newTab = restoreTab(query.nextValue()))
|
||
|
{
|
||
|
const int page = query.nextValue();
|
||
|
|
||
|
newTab->setContinuousMode(query.nextValue());
|
||
|
newTab->setLayoutMode(query.nextValue());
|
||
|
newTab->setRightToLeftMode(query.nextValue());
|
||
|
|
||
|
newTab->setScaleMode(query.nextValue());
|
||
|
newTab->setScaleFactor(query.nextValue());
|
||
|
|
||
|
newTab->setRotation(query.nextValue());
|
||
|
newTab->setRenderFlags(query.nextValue());
|
||
|
|
||
|
newTab->setFirstPage(query.nextValue());
|
||
|
|
||
|
newTab->jumpToPage(page, false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
Q_UNUSED(restoreTab);
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
}
|
||
|
|
||
|
void Database::saveTabs(const QVector< DocumentView* >& tabs)
|
||
|
{
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.prepare("DELETE FROM tabs_v5 WHERE instanceName==?");
|
||
|
|
||
|
query << instanceName();
|
||
|
|
||
|
query.exec();
|
||
|
|
||
|
query.prepare("INSERT INTO tabs_v5"
|
||
|
" (filePath,instanceName,tabIndex,currentPage,continuousMode,layoutMode,rightToLeftMode,scaleMode,scaleFactor,rotation,renderFlags,firstPage)"
|
||
|
" VALUES (?,?,?,?,?,?,?,?,?,?,?,?)");
|
||
|
|
||
|
for(int tabIndex = 0; tabIndex < tabs.size(); ++tabIndex)
|
||
|
{
|
||
|
const DocumentView* const tab = tabs.at(tabIndex);
|
||
|
|
||
|
query << tab->fileInfo().absoluteFilePath()
|
||
|
<< instanceName()
|
||
|
<< tabIndex
|
||
|
<< tab->currentPage()
|
||
|
|
||
|
<< tab->continuousMode()
|
||
|
<< tab->layoutMode()
|
||
|
<< tab->rightToLeftMode()
|
||
|
|
||
|
<< tab->scaleMode()
|
||
|
<< tab->scaleFactor()
|
||
|
|
||
|
<< tab->rotation()
|
||
|
<< tab->renderFlags()
|
||
|
|
||
|
<< tab->firstPage();
|
||
|
|
||
|
query.exec();
|
||
|
}
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
Q_UNUSED(tabs);
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
}
|
||
|
|
||
|
void Database::clearTabs()
|
||
|
{
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.exec("DELETE FROM tabs_v5");
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
}
|
||
|
|
||
|
void Database::restoreBookmarks()
|
||
|
{
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query outerQuery(m_database);
|
||
|
Query innerQuery(m_database);
|
||
|
|
||
|
outerQuery.exec("SELECT DISTINCT(filePath) FROM bookmarks_v3");
|
||
|
|
||
|
innerQuery.prepare("SELECT page,label,comment,modified"
|
||
|
" FROM bookmarks_v3 WHERE filePath==?");
|
||
|
|
||
|
while(outerQuery.nextRecord())
|
||
|
{
|
||
|
const QString filePath = outerQuery.nextValue();
|
||
|
|
||
|
innerQuery << filePath;
|
||
|
|
||
|
innerQuery.exec();
|
||
|
|
||
|
BookmarkModel* model = BookmarkModel::fromPath(filePath, true);
|
||
|
|
||
|
while(innerQuery.nextRecord())
|
||
|
{
|
||
|
const int page = innerQuery.nextValue();
|
||
|
const QString label = innerQuery.nextValue();
|
||
|
const QString comment = innerQuery.nextValue();
|
||
|
const QDateTime modified = innerQuery.nextValue();
|
||
|
|
||
|
model->addBookmark(BookmarkItem(page, label, comment, modified));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
}
|
||
|
|
||
|
void Database::saveBookmarks()
|
||
|
{
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.exec("DELETE FROM bookmarks_v3");
|
||
|
|
||
|
if(Settings::instance()->mainWindow().restoreBookmarks())
|
||
|
{
|
||
|
query.prepare("INSERT INTO bookmarks_v3"
|
||
|
" (filePath,page,label,comment,modified)"
|
||
|
" VALUES (?,?,?,?,?)");
|
||
|
|
||
|
foreach(const QString& filePath, BookmarkModel::paths())
|
||
|
{
|
||
|
const BookmarkModel* model = BookmarkModel::fromPath(filePath);
|
||
|
|
||
|
for(int row = 0, rowCount = model->rowCount(); row < rowCount; ++row)
|
||
|
{
|
||
|
const QModelIndex index = model->index(row);
|
||
|
|
||
|
query << filePath
|
||
|
|
||
|
<< index.data(BookmarkModel::PageRole)
|
||
|
<< index.data(BookmarkModel::LabelRole)
|
||
|
<< index.data(BookmarkModel::CommentRole)
|
||
|
<< index.data(BookmarkModel::ModifiedRole);
|
||
|
|
||
|
query.exec();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
}
|
||
|
|
||
|
void Database::clearBookmarks()
|
||
|
{
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.exec("DELETE FROM bookmarks_v3");
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
}
|
||
|
|
||
|
void Database::restorePerFileSettings(DocumentView* tab)
|
||
|
{
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
if(!Settings::instance()->mainWindow().restorePerFileSettings())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
try
|
||
|
{
|
||
|
const QByteArray filePath = hashFilePath(tab->fileInfo().absoluteFilePath());
|
||
|
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.prepare("SELECT currentPage,continuousMode,layoutMode,rightToLeftMode,scaleMode,scaleFactor,rotation,renderFlags,firstPage"
|
||
|
" FROM perfilesettings_v4 WHERE filePath==?");
|
||
|
|
||
|
query << filePath;
|
||
|
|
||
|
query.exec();
|
||
|
|
||
|
if(query.nextRecord())
|
||
|
{
|
||
|
const int page = query.nextValue();
|
||
|
|
||
|
tab->setContinuousMode(query.nextValue());
|
||
|
tab->setLayoutMode(query.nextValue());
|
||
|
tab->setRightToLeftMode(query.nextValue());
|
||
|
|
||
|
tab->setScaleMode(query.nextValue());
|
||
|
tab->setScaleFactor(query.nextValue());
|
||
|
|
||
|
tab->setRotation(query.nextValue());
|
||
|
|
||
|
tab->setRenderFlags(query.nextValue());
|
||
|
|
||
|
tab->setFirstPage(query.nextValue());
|
||
|
|
||
|
tab->jumpToPage(page, false);
|
||
|
}
|
||
|
|
||
|
query.prepare("SELECT expandedPath FROM perfilesettings_outline_v1 WHERE filePath==?");
|
||
|
|
||
|
query << filePath;
|
||
|
|
||
|
query.exec();
|
||
|
|
||
|
QSet< QByteArray > expandedPaths;
|
||
|
|
||
|
while(query.nextRecord())
|
||
|
{
|
||
|
expandedPaths.insert(query.nextValue());
|
||
|
}
|
||
|
|
||
|
tab->restoreExpandedPaths(expandedPaths);
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
Q_UNUSED(tab);
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
}
|
||
|
|
||
|
void Database::savePerFileSettings(const DocumentView* tab)
|
||
|
{
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
if(!Settings::instance()->mainWindow().restorePerFileSettings())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
try
|
||
|
{
|
||
|
const QByteArray filePath = hashFilePath(tab->fileInfo().absoluteFilePath());
|
||
|
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.prepare("INSERT OR REPLACE INTO perfilesettings_v4"
|
||
|
" (lastUsed,filePath,currentPage,continuousMode,layoutMode,rightToLeftMode,scaleMode,scaleFactor,rotation,renderFlags,firstPage)"
|
||
|
" VALUES (strftime('%s','now'),?,?,?,?,?,?,?,?,?,?)");
|
||
|
|
||
|
query << filePath
|
||
|
<< tab->currentPage()
|
||
|
|
||
|
<< tab->continuousMode()
|
||
|
<< tab->layoutMode()
|
||
|
<< tab->rightToLeftMode()
|
||
|
|
||
|
<< tab->scaleMode()
|
||
|
<< tab->scaleFactor()
|
||
|
|
||
|
<< tab->rotation()
|
||
|
<< tab->renderFlags()
|
||
|
|
||
|
<< tab->firstPage();
|
||
|
|
||
|
query.exec();
|
||
|
|
||
|
query.prepare("DELETE FROM perfilesettings_outline_v1 WHERE filePath==?");
|
||
|
|
||
|
query << filePath;
|
||
|
|
||
|
query.exec();
|
||
|
|
||
|
query.prepare("INSERT INTO perfilesettings_outline_v1"
|
||
|
" (filePath,expandedPath)"
|
||
|
" VALUES (?,?)");
|
||
|
|
||
|
foreach(const QByteArray& expandedPath, tab->saveExpandedPaths())
|
||
|
{
|
||
|
query << filePath << expandedPath;
|
||
|
|
||
|
query.exec();
|
||
|
}
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
Q_UNUSED(tab);
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
}
|
||
|
|
||
|
Database::Database(QObject* parent) : QObject(parent)
|
||
|
{
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||
|
|
||
|
const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||
|
|
||
|
#else
|
||
|
|
||
|
const QString path = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
||
|
|
||
|
#endif // QT_VERSION
|
||
|
|
||
|
QDir().mkpath(path);
|
||
|
|
||
|
m_database = QSqlDatabase::addDatabase("QSQLITE");
|
||
|
m_database.setDatabaseName(QDir(path).filePath("database"));
|
||
|
|
||
|
if(!m_database.open())
|
||
|
{
|
||
|
qDebug() << m_database.lastError();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
try
|
||
|
{
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.exec("PRAGMA synchronous = OFF");
|
||
|
query.exec("PRAGMA journal_mode = MEMORY");
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
|
||
|
const QStringList tables = m_database.tables();
|
||
|
|
||
|
// tabs
|
||
|
|
||
|
if(!tables.contains("tabs_v5"))
|
||
|
{
|
||
|
if(prepareTabs_v5())
|
||
|
{
|
||
|
if(tables.contains("tabs_v4"))
|
||
|
{
|
||
|
migrateTabs_v4_v5();
|
||
|
}
|
||
|
else if(tables.contains("tabs_v3"))
|
||
|
{
|
||
|
migrateTabs_v3_v5();
|
||
|
}
|
||
|
else if(tables.contains("tabs_v2"))
|
||
|
{
|
||
|
migrateTabs_v2_v5();
|
||
|
}
|
||
|
else if(tables.contains("tabs_v1"))
|
||
|
{
|
||
|
migrateTabs_v1_v5();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// bookmarks
|
||
|
|
||
|
if(!tables.contains("bookmarks_v3"))
|
||
|
{
|
||
|
if(prepareBookmarks_v3())
|
||
|
{
|
||
|
if(tables.contains("bookmarks_v2"))
|
||
|
{
|
||
|
migrateBookmarks_v2_v3();
|
||
|
}
|
||
|
else if(tables.contains("bookmarks_v1"))
|
||
|
{
|
||
|
migrateBookmarks_v1_v3();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// per-file settings
|
||
|
|
||
|
if(!tables.contains("perfilesettings_v4"))
|
||
|
{
|
||
|
if(preparePerFileSettings_v4())
|
||
|
{
|
||
|
if(tables.contains("perfilesettings_v3"))
|
||
|
{
|
||
|
migratePerFileSettings_v3_v4();
|
||
|
}
|
||
|
else if(tables.contains("perfilesettings_v2"))
|
||
|
{
|
||
|
migratePerFileSettings_v2_v4();
|
||
|
}
|
||
|
else if(tables.contains("perfilesettings_v1"))
|
||
|
{
|
||
|
migratePerFileSettings_v1_v4();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!tables.contains("perfilesettings_outline_v1"))
|
||
|
{
|
||
|
preparePerFileSettings_Outline_v1();
|
||
|
}
|
||
|
|
||
|
limitPerFileSettings();
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
}
|
||
|
|
||
|
QString Database::instanceName()
|
||
|
{
|
||
|
QString instanceName = qApp->objectName();
|
||
|
|
||
|
if(instanceName.isNull())
|
||
|
{
|
||
|
instanceName = QLatin1String("");
|
||
|
}
|
||
|
|
||
|
return instanceName;
|
||
|
}
|
||
|
|
||
|
#ifdef WITH_SQL
|
||
|
|
||
|
bool Database::prepareTabs_v5()
|
||
|
{
|
||
|
return prepareTable("CREATE TABLE tabs_v5 ("
|
||
|
" filePath TEXT"
|
||
|
" ,instanceName TEXT"
|
||
|
" ,tabIndex INTEGER"
|
||
|
" ,currentPage INTEGER"
|
||
|
" ,continuousMode INTEGER"
|
||
|
" ,layoutMode INTEGER"
|
||
|
" ,rightToLeftMode INTEGER"
|
||
|
" ,scaleMode INTEGER"
|
||
|
" ,scaleFactor REAL"
|
||
|
" ,rotation INTEGER"
|
||
|
" ,renderFlags INTEGER"
|
||
|
" ,firstPage INTEGER"
|
||
|
" ,PRIMARY KEY (instanceName, tabIndex)"
|
||
|
" )");
|
||
|
}
|
||
|
|
||
|
bool Database::prepareBookmarks_v3()
|
||
|
{
|
||
|
return prepareTable("CREATE TABLE bookmarks_v3 ("
|
||
|
" filePath TEXT"
|
||
|
" ,page INTEGER"
|
||
|
" ,label TEXT"
|
||
|
" ,comment TEXT"
|
||
|
" ,modified DATETIME"
|
||
|
" )");
|
||
|
}
|
||
|
|
||
|
bool Database::preparePerFileSettings_v4()
|
||
|
{
|
||
|
return prepareTable("CREATE TABLE perfilesettings_v4 ("
|
||
|
" lastUsed INTEGER"
|
||
|
" ,filePath TEXT PRIMARY KEY"
|
||
|
" ,currentPage INTEGER"
|
||
|
" ,continuousMode INTEGER"
|
||
|
" ,layoutMode INTEGER"
|
||
|
" ,rightToLeftMode INTEGER"
|
||
|
" ,scaleMode INTEGER"
|
||
|
" ,scaleFactor REAL"
|
||
|
" ,rotation INTEGER"
|
||
|
" ,renderFlags INTEGER"
|
||
|
" ,firstPage INTEGER"
|
||
|
" )");
|
||
|
}
|
||
|
|
||
|
bool Database::preparePerFileSettings_Outline_v1()
|
||
|
{
|
||
|
return prepareTable("CREATE TABLE perfilesettings_outline_v1 ("
|
||
|
" filePath TEXT"
|
||
|
" ,expandedPath TEXT"
|
||
|
" ,FOREIGN KEY (filePath) REFERENCES perfilesettings_v4 (filePath) ON DELETE CASCADE"
|
||
|
" )");
|
||
|
}
|
||
|
|
||
|
void Database::migrateTabs_v4_v5()
|
||
|
{
|
||
|
migrateTable("INSERT INTO tabs_v5"
|
||
|
" SELECT filePath,instanceName,(SELECT MAX(tabIndex)+1 FROM tabs_v5 WHERE tabs_v5.instanceName=tabs_v4.instanceName),currentPage,continuousMode,layoutMode,rightToLeftMode,scaleMode,scaleFactor,rotation,0,-1"
|
||
|
" FROM tabs_v4",
|
||
|
|
||
|
"DROP TABLE tabs_v4",
|
||
|
|
||
|
"Migrated tabs from v4 to v5, dropping v4.");
|
||
|
}
|
||
|
|
||
|
void Database::migrateTabs_v3_v5()
|
||
|
{
|
||
|
migrateTable("INSERT INTO tabs_v5"
|
||
|
" SELECT filePath,instanceName,(SELECT MAX(tabIndex)+1 FROM tabs_v5 WHERE tabs_v5.instanceName=tabs_v3.instanceName),currentPage,continuousMode,layoutMode,rightToLeftMode,scaleMode,scaleFactor,rotation,0,-1"
|
||
|
" FROM tabs_v3",
|
||
|
|
||
|
"DROP TABLE tabs_v3",
|
||
|
|
||
|
"Migrated tabs from v3 to v5, dropping v3.");
|
||
|
}
|
||
|
|
||
|
void Database::migrateTabs_v2_v5()
|
||
|
{
|
||
|
migrateTable("INSERT INTO tabs_v5"
|
||
|
" SELECT filePath,instanceName,(SELECT MAX(tabIndex)+1 FROM tabs_v5 WHERE tabs_v5.instanceName=tabs_v2.instanceName),currentPage,continuousMode,layoutMode,0,scaleMode,scaleFactor,rotation,0,-1"
|
||
|
" FROM tabs_v2",
|
||
|
|
||
|
"DROP TABLE tabs_v2" ,
|
||
|
|
||
|
"Migrated tabs from v2 to v5, dropping v2.");
|
||
|
}
|
||
|
|
||
|
void Database::migrateTabs_v1_v5()
|
||
|
{
|
||
|
migrateTable("INSERT INTO tabs_v5"
|
||
|
" SELECT filePath,'',(SELECT MAX(tabIndex)+1 FROM tabs_v5 WHERE tabs_v5.instanceName=''),currentPage,continuousMode,layoutMode,0,scaleMode,scaleFactor,rotation,0,-1"
|
||
|
" FROM tabs_v1",
|
||
|
|
||
|
"DROP TABLE tabs_v1",
|
||
|
|
||
|
"Migrated tabs from v1 to v5, dropping v1.");
|
||
|
}
|
||
|
|
||
|
void Database::migrateBookmarks_v2_v3()
|
||
|
{
|
||
|
migrateTable("INSERT INTO bookmarks_v3"
|
||
|
" SELECT filePath,page,label,'',datetime('now')"
|
||
|
" FROM bookmarks_v2",
|
||
|
|
||
|
"DROP TABLE bookmarks_v2",
|
||
|
|
||
|
"Migrated bookmarks from v2 to v3, dropping v2.");
|
||
|
}
|
||
|
|
||
|
void Database::migrateBookmarks_v1_v3()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query outerQuery(m_database);
|
||
|
Query innerQuery(m_database);
|
||
|
|
||
|
outerQuery.exec("SELECT filePath,pages FROM bookmarks_v1");
|
||
|
|
||
|
innerQuery.prepare("INSERT INTO bookmarks_v3"
|
||
|
" (filePath,page,label,comment,modified)"
|
||
|
" VALUES (?,?,?,'',datetime('now'))");
|
||
|
|
||
|
while(outerQuery.nextRecord())
|
||
|
{
|
||
|
const QString filePath = outerQuery.nextValue();
|
||
|
const QString pages = outerQuery.nextValue();
|
||
|
|
||
|
foreach(const QString& page, pages.split(",", QString::SkipEmptyParts))
|
||
|
{
|
||
|
innerQuery << filePath
|
||
|
<< page
|
||
|
<< tr("Jump to page %1").arg(page);
|
||
|
|
||
|
innerQuery.exec();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qWarning() << "Migrated bookmarks from v1 to v3, dropping v1.";
|
||
|
|
||
|
outerQuery.exec("DROP TABLE bookmarks_v1");
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Database::migratePerFileSettings_v3_v4()
|
||
|
{
|
||
|
migrateTable("INSERT INTO perfilesettings_v4"
|
||
|
" SELECT lastUsed,filePath,currentPage,continuousMode,layoutMode,rightToLeftMode,scaleMode,scaleFactor,rotation,0,firstPage"
|
||
|
" FROM perfilesettings_v3",
|
||
|
|
||
|
"DROP TABLE perfilesettings_v3",
|
||
|
|
||
|
"Migrated per-file settings from v3 to v4, dropping v3.");
|
||
|
}
|
||
|
|
||
|
void Database::migratePerFileSettings_v2_v4()
|
||
|
{
|
||
|
migrateTable("INSERT INTO perfilesettings_v4"
|
||
|
" SELECT lastUsed,filePath,currentPage,continuousMode,layoutMode,rightToLeftMode,scaleMode,scaleFactor,rotation,0,-1"
|
||
|
" FROM perfilesettings_v2",
|
||
|
|
||
|
"DROP TABLE perfilesettings_v2",
|
||
|
|
||
|
"Migrated per-file settings from v2 to v4, dropping v2.");
|
||
|
}
|
||
|
|
||
|
void Database::migratePerFileSettings_v1_v4()
|
||
|
{
|
||
|
migrateTable("INSERT INTO perfilesettings_v4"
|
||
|
" SELECT lastUsed,filePath,currentPage,continuousMode,layoutMode,0,scaleMode,scaleFactor,rotation,0,-1"
|
||
|
" FROM perfilesettings_v1",
|
||
|
|
||
|
"DROP TABLE perfilesettings_v1",
|
||
|
|
||
|
"Migrated per-file settings from v1 to v4, dropping v1.");
|
||
|
}
|
||
|
|
||
|
bool Database::prepareTable(const QString& prepare)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.exec(prepare);
|
||
|
|
||
|
transaction.commit();
|
||
|
return true;
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Database::migrateTable(const QString& migrate, const QString& prune, const QString& warning)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
query.exec(migrate);
|
||
|
|
||
|
qWarning() << warning;
|
||
|
|
||
|
query.exec(prune);
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Database::limitPerFileSettings()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
Transaction transaction(m_database);
|
||
|
Query query(m_database);
|
||
|
|
||
|
if(Settings::instance()->mainWindow().restorePerFileSettings())
|
||
|
{
|
||
|
query.prepare("DELETE FROM perfilesettings_v4"
|
||
|
" WHERE filePath NOT IN ("
|
||
|
" SELECT filePath FROM perfilesettings_v4"
|
||
|
" ORDER BY lastUsed DESC LIMIT ?"
|
||
|
" )");
|
||
|
|
||
|
query << Settings::instance()->mainWindow().perFileSettingsLimit();
|
||
|
|
||
|
query.exec();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
query.exec("DELETE FROM perfilesettings_v4");
|
||
|
}
|
||
|
|
||
|
transaction.commit();
|
||
|
}
|
||
|
catch(QSqlError& error)
|
||
|
{
|
||
|
qDebug() << error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // WITH_SQL
|
||
|
|
||
|
} // qpdfview
|