Formats conditioned to row-id values to allow free cell formatting

The conditional formatting is extended to cover free single-cell
formatting. The row-id formats have precedence over regular conditional
formats.

In the styling toolbar, when single cells are selected, row-id formats are
created or updated. When entire columns are selected, regular conditional
formats are instead. Clearing formats for entire columns, remove both. For
single cells, only corresponding row-id formats.

New row-id formats are also saved to project files and loaded.

See issue #1976
This commit is contained in:
mgrojo
2019-10-27 01:19:11 +02:00
parent bf62a6e441
commit 5aeca230fc
8 changed files with 258 additions and 126 deletions

View File

@@ -2,6 +2,8 @@
#include "Settings.h"
#include "Data.h"
#include <QAbstractTableModel>
CondFormat::Alignment CondFormat::fromCombinedAlignment(Qt::Alignment align)
{
if (align.testFlag(Qt::AlignLeft))
@@ -30,6 +32,22 @@ CondFormat::CondFormat(const QString& filter,
m_sqlCondition = filterToSqlCondition(filter, encoding);
}
CondFormat::CondFormat(const QString& filter,
const QAbstractTableModel* model,
const QModelIndex index,
const QString& encoding)
: m_filter(filter)
{
if (!filter.isEmpty())
m_sqlCondition = filterToSqlCondition(filter, encoding);
m_bgColor = QColor(model->data(index, Qt::BackgroundRole).toString());
m_fgColor = QColor(model->data(index, Qt::ForegroundRole).toString());
m_align = fromCombinedAlignment(Qt::Alignment(model->data(index, Qt::TextAlignmentRole).toInt()));
m_font.fromString(model->data(index, Qt::FontRole).toString());
}
QString CondFormat::filterToSqlCondition(const QString& value, const QString& encoding)
{
// Check for any special comparison operators at the beginning of the value string. If there are none default to LIKE.

View File

@@ -4,6 +4,9 @@
#include <QString>
#include <QColor>
#include <QFont>
#include <QModelIndex>
class QAbstractTableModel;
// Conditional formatting for given format to table cells based on a specified condition.
class CondFormat
@@ -27,12 +30,18 @@ public:
static Alignment fromCombinedAlignment(Qt::Alignment align);
CondFormat() {}
explicit CondFormat(const QString& filter,
const QColor& foreground,
const QColor& background,
const QFont& font,
const Alignment alignment = AlignLeft,
const QString& encoding = QString());
CondFormat(const QString& filter,
const QColor& foreground,
const QColor& background,
const QFont& font,
const Alignment alignment = AlignLeft,
const QString& encoding = QString());
// Create a new CondFormat from values obtained from the model
CondFormat(const QString& filter,
const QAbstractTableModel* model,
const QModelIndex index,
const QString& encoding = QString());
static QString filterToSqlCondition(const QString& value, const QString& encoding = QString());

View File

@@ -2195,6 +2195,38 @@ void MainWindow::on_actionDonatePatreon_triggered()
QDesktopServices::openUrl(QUrl("https://www.patreon.com/bePatron?u=11578749"));
}
static void loadCondFormatMap(BrowseDataTableSettings::CondFormatMap& condFormats, QXmlStreamReader& xml, const QString& encoding)
{
const QStringRef name = xml.name();
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != name) {
if (xml.name() == "column") {
size_t index = xml.attributes().value("index").toUInt();
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "column") {
if(xml.name() == "format") {
QFont font;
if (xml.attributes().hasAttribute("font"))
font.fromString(xml.attributes().value("font").toString());
else
Settings::getValue("databrowser", "font").toString();
CondFormat::Alignment align;
if (xml.attributes().hasAttribute("align"))
align = static_cast<CondFormat::Alignment>(xml.attributes().value("align").toInt());
else
align = CondFormat::AlignLeft;
condFormats[index].emplace_back(xml.attributes().value("condition").toString(),
QColor(xml.attributes().value("foreground").toString()),
QColor(xml.attributes().value("background").toString()),
font, align, encoding);
xml.skipCurrentElement();
}
}
}
}
}
static void loadBrowseDataTableSettings(BrowseDataTableSettings& settings, QXmlStreamReader& xml)
{
// TODO Remove this in the near future. This file format was only created temporarily by the nightlies from the late 3.11 development period.
@@ -2240,32 +2272,9 @@ static void loadBrowseDataTableSettings(BrowseDataTableSettings& settings, QXmlS
}
}
} else if(xml.name() == "conditional_formats") {
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "conditional_formats") {
if (xml.name() == "column") {
size_t index = xml.attributes().value("index").toUInt();
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "column") {
if(xml.name() == "format") {
QFont font;
if (xml.attributes().hasAttribute("font"))
font.fromString(xml.attributes().value("font").toString());
else
Settings::getValue("databrowser", "font").toString();
CondFormat::Alignment align;
if (xml.attributes().hasAttribute("align"))
align = static_cast<CondFormat::Alignment>(xml.attributes().value("align").toInt());
else
align = CondFormat::AlignLeft;
settings.condFormats[index].emplace_back(xml.attributes().value("condition").toString(),
QColor(xml.attributes().value("foreground").toString()),
QColor(xml.attributes().value("background").toString()),
font, align, settings.encoding);
xml.skipCurrentElement();
}
}
}
}
loadCondFormatMap(settings.condFormats, xml, settings.encoding);
} else if(xml.name() == "row_id_formats") {
loadCondFormatMap(settings.rowIdFormats, xml, settings.encoding);
} else if(xml.name() == "display_formats") {
while(xml.readNext() != QXmlStreamReader::EndElement && xml.name() != "display_formats") {
if (xml.name() == "column") {
@@ -2556,6 +2565,26 @@ static void saveDbTreeState(const QTreeView* tree, QXmlStreamWriter& xml, QModel
}
}
static void saveCondFormatMap(const QString& elementName, const BrowseDataTableSettings::CondFormatMap& condFormats, QXmlStreamWriter& xml)
{
xml.writeStartElement(elementName);
for(auto iter=condFormats.constBegin(); iter!=condFormats.constEnd(); ++iter) {
xml.writeStartElement("column");
xml.writeAttribute("index", QString::number(iter.key()));
for(auto format : iter.value()) {
xml.writeStartElement("format");
xml.writeAttribute("condition", format.filter());
xml.writeAttribute("background", format.backgroundColor().name());
xml.writeAttribute("foreground", format.foregroundColor().name());
xml.writeAttribute("font", format.font().toString());
xml.writeAttribute("align", QString().setNum(format.alignment()));
xml.writeEndElement();
}
xml.writeEndElement();
}
xml.writeEndElement();
}
static void saveBrowseDataTableSettings(const BrowseDataTableSettings& object, QXmlStreamWriter& xml)
{
xml.writeAttribute("show_row_id", QString::number(object.showRowid));
@@ -2589,22 +2618,8 @@ static void saveBrowseDataTableSettings(const BrowseDataTableSettings& object, Q
xml.writeEndElement();
}
xml.writeEndElement();
xml.writeStartElement("conditional_formats");
for(auto iter=object.condFormats.constBegin(); iter!=object.condFormats.constEnd(); ++iter) {
xml.writeStartElement("column");
xml.writeAttribute("index", QString::number(iter.key()));
for(auto format : iter.value()) {
xml.writeStartElement("format");
xml.writeAttribute("condition", format.filter());
xml.writeAttribute("background", format.backgroundColor().name());
xml.writeAttribute("foreground", format.foregroundColor().name());
xml.writeAttribute("font", format.font().toString());
xml.writeAttribute("align", QString().setNum(format.alignment()));
xml.writeEndElement();
}
xml.writeEndElement();
}
xml.writeEndElement();
saveCondFormatMap("conditional_formats", object.condFormats, xml);
saveCondFormatMap("row_id_formats", object.rowIdFormats, xml);
xml.writeStartElement("display_formats");
for(auto iter=object.displayFormats.constBegin(); iter!=object.displayFormats.constEnd(); ++iter) {
xml.writeStartElement("column");

View File

@@ -92,7 +92,7 @@ TableBrowser::TableBrowser(QWidget* parent) :
// Set up filters
connect(ui->dataTable->filterHeader(), &FilterTableHeader::filterChanged, this, &TableBrowser::updateFilter);
connect(ui->dataTable->filterHeader(), &FilterTableHeader::addCondFormat, this, &TableBrowser::addCondFormat);
connect(ui->dataTable->filterHeader(), &FilterTableHeader::addCondFormat, this, &TableBrowser::addCondFormatFromFilter);
connect(ui->dataTable->filterHeader(), &FilterTableHeader::allCondFormatsCleared, this, &TableBrowser::clearAllCondFormats);
connect(ui->dataTable->filterHeader(), &FilterTableHeader::condFormatsEdited, this, &TableBrowser::editCondFormats);
connect(ui->dataTable, &ExtendedTableWidget::editCondFormats, this, &TableBrowser::editCondFormats);
@@ -130,32 +130,32 @@ TableBrowser::TableBrowser(QWidget* parent) :
connect(ui->dataTable, &ExtendedTableWidget::selectedRowsToBeDeleted, this, &TableBrowser::deleteRecord);
connect(ui->fontComboBox, &QFontComboBox::currentFontChanged, this, [this](const QFont &font) {
modifyColumnFormat(ui->dataTable->colsInSelection(), [font](CondFormat& format) { format.setFontFamily(font.family()); });
modifyFormat([font](CondFormat& format) { format.setFontFamily(font.family()); });
});
connect(ui->fontSizeBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
[this](int pointSize) {
modifyColumnFormat(ui->dataTable->colsInSelection(), [pointSize](CondFormat& format) { format.setFontPointSize(pointSize); });
modifyFormat([pointSize](CondFormat& format) { format.setFontPointSize(pointSize); });
});
connect(ui->actionFontColor, &QAction::triggered, this, [this]() {
QColor color = QColorDialog::getColor(QColor(m_model->data(currentIndex(), Qt::ForegroundRole).toString()));
if(color.isValid())
modifyColumnFormat(ui->dataTable->colsInSelection(), [color](CondFormat& format) { format.setForegroundColor(color); });
modifyFormat([color](CondFormat& format) { format.setForegroundColor(color); });
});
connect(ui->actionBackgroundColor, &QAction::triggered, this, [this]() {
QColor color = QColorDialog::getColor(QColor(m_model->data(currentIndex(), Qt::BackgroundRole).toString()));
if(color.isValid())
modifyColumnFormat(ui->dataTable->colsInSelection(), [color](CondFormat& format) { format.setBackgroundColor(color); });
modifyFormat([color](CondFormat& format) { format.setBackgroundColor(color); });
});
connect(ui->actionBold, &QAction::toggled, this, [this](bool checked) {
modifyColumnFormat(ui->dataTable->colsInSelection(), [checked](CondFormat& format) { format.setBold(checked); });
modifyFormat([checked](CondFormat& format) { format.setBold(checked); });
});
connect(ui->actionItalic, &QAction::toggled, this, [this](bool checked) {
modifyColumnFormat(ui->dataTable->colsInSelection(), [checked](CondFormat& format) { format.setItalic(checked); });
modifyFormat([checked](CondFormat& format) { format.setItalic(checked); });
});
connect(ui->actionUnderline, &QAction::toggled, this, [this](bool checked) {
modifyColumnFormat(ui->dataTable->colsInSelection(), [checked](CondFormat& format) { format.setUnderline(checked); });
modifyFormat([checked](CondFormat& format) { format.setUnderline(checked); });
});
connect(ui->actionLeftAlign, &QAction::triggered, this, [this]() {
@@ -163,34 +163,37 @@ TableBrowser::TableBrowser(QWidget* parent) :
ui->actionRightAlign->setChecked(false);
ui->actionCenter->setChecked(false);
ui->actionJustify->setChecked(false);
modifyColumnFormat(ui->dataTable->colsInSelection(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignLeft); });
modifyFormat([](CondFormat& format) { format.setAlignment(CondFormat::AlignLeft); });
});
connect(ui->actionRightAlign, &QAction::triggered, this, [this]() {
ui->actionLeftAlign->setChecked(false);
ui->actionRightAlign->setChecked(true);
ui->actionCenter->setChecked(false);
ui->actionJustify->setChecked(false);
modifyColumnFormat(ui->dataTable->colsInSelection(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignRight); });
modifyFormat([](CondFormat& format) { format.setAlignment(CondFormat::AlignRight); });
});
connect(ui->actionCenter, &QAction::triggered, this, [this]() {
ui->actionLeftAlign->setChecked(false);
ui->actionRightAlign->setChecked(false);
ui->actionCenter->setChecked(true);
ui->actionJustify->setChecked(false);
modifyColumnFormat(ui->dataTable->colsInSelection(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignCenter); });
modifyFormat([](CondFormat& format) { format.setAlignment(CondFormat::AlignCenter); });
});
connect(ui->actionJustify, &QAction::triggered, this, [this]() {
ui->actionLeftAlign->setChecked(false);
ui->actionRightAlign->setChecked(false);
ui->actionCenter->setChecked(false);
ui->actionJustify->setChecked(true);
modifyColumnFormat(ui->dataTable->colsInSelection(), [](CondFormat& format) { format.setAlignment(CondFormat::AlignJustify); });
modifyFormat([](CondFormat& format) { format.setAlignment(CondFormat::AlignJustify); });
});
connect(ui->actionEditCondFormats, &QAction::triggered, this, [this]() { editCondFormats(static_cast<size_t>(currentIndex().column())); });
connect(ui->actionClearFormat, &QAction::triggered, this, [this]() {
for (size_t column : ui->dataTable->colsInSelection())
for (const size_t column : ui->dataTable->selectedCols())
clearAllCondFormats(column);
for (const QModelIndex& index : ui->dataTable->selectionModel()->selectedIndexes())
clearRowIdFormats(index);
});
connect(ui->dataTable, &ExtendedTableWidget::currentIndexChanged, this, [this](const QModelIndex &current, const QModelIndex &) {
@@ -582,7 +585,7 @@ void TableBrowser::updateFilter(size_t column, const QString& value)
applySettings(settings, true);
}
void TableBrowser::addCondFormat(size_t column, const QString& value)
void TableBrowser::addCondFormatFromFilter(size_t column, const QString& value)
{
QFont font = QFont(Settings::getValue("databrowser", "font").toString());
font.setPointSize(Settings::getValue("databrowser", "fontsize").toInt());
@@ -594,59 +597,86 @@ void TableBrowser::addCondFormat(size_t column, const QString& value)
font,
CondFormat::AlignLeft,
m_model->encoding());
m_model->addCondFormat(column, newCondFormat);
m_settings[currentlyBrowsedTableName()].condFormats[column].push_back(newCondFormat);
addCondFormat(false, column, newCondFormat);
}
void TableBrowser::addCondFormat(bool isRowIdFormat, size_t column, const CondFormat& newCondFormat)
{
BrowseDataTableSettings& settings = m_settings[currentlyBrowsedTableName()];
std::vector<CondFormat>& formats = isRowIdFormat ? settings.rowIdFormats[column] : settings.condFormats[column];
m_model->addCondFormat(isRowIdFormat, column, newCondFormat);
// Conditionless formats are pushed back and others inserted at the begining, so they aren't eclipsed.
if (newCondFormat.filter().isEmpty())
formats.push_back(newCondFormat);
else
formats.insert(formats.begin(), newCondFormat);
}
void TableBrowser::clearAllCondFormats(size_t column)
{
std::vector<CondFormat> emptyCondFormatVector = std::vector<CondFormat>();
m_model->setCondFormats(column, emptyCondFormatVector);
m_model->setCondFormats(false, column, emptyCondFormatVector);
m_settings[currentlyBrowsedTableName()].condFormats[column].clear();
emit projectModified();
}
void TableBrowser::clearRowIdFormats(const QModelIndex index)
{
std::vector<CondFormat>& rowIdFormats = m_settings[currentlyBrowsedTableName()].rowIdFormats[index.column()];
rowIdFormats.erase(std::remove_if(rowIdFormats.begin(), rowIdFormats.end(), [&](const CondFormat& format) {
return format.filter() == QString("=%1").arg(m_model->data(index.sibling(index.row(), 0)).toString());
}), rowIdFormats.end());
m_model->setCondFormats(true, index.column(), rowIdFormats);
emit projectModified();
}
void TableBrowser::editCondFormats(size_t column)
{
CondFormatManager condFormatDialog(m_settings[currentlyBrowsedTableName()].condFormats[column],
m_model->encoding(), this);
condFormatDialog.setWindowTitle(tr("Conditional formats for \"%1\"").
arg(m_model->headerData(static_cast<int>(column), Qt::Horizontal).toString()));
arg(m_model->headerData(static_cast<int>(column), Qt::Horizontal, Qt::EditRole).toString()));
if (condFormatDialog.exec()) {
std::vector<CondFormat> condFormatVector = condFormatDialog.getCondFormats();
m_model->setCondFormats(column, condFormatVector);
m_model->setCondFormats(false, column, condFormatVector);
m_settings[currentlyBrowsedTableName()].condFormats[column] = condFormatVector;
emit projectModified();
}
}
void TableBrowser::modifyColumnFormat(std::unordered_set<size_t> columns, std::function<void(CondFormat&)> changeFunction)
void TableBrowser::modifySingleFormat(const bool isRowIdFormat, const QString& filter, const QModelIndex refIndex, std::function<void(CondFormat&)> changeFunction)
{
for (size_t column : columns) {
std::vector<CondFormat>& columnFormats = m_settings[currentlyBrowsedTableName()].condFormats[column];
BrowseDataTableSettings& settings = m_settings[currentlyBrowsedTableName()];
std::vector<CondFormat>& formats = isRowIdFormat ? settings.rowIdFormats[refIndex.column()] : settings.condFormats[refIndex.column()];
auto it = std::find_if(formats.begin(), formats.end(), [&filter](const CondFormat& format) {
return format.filter() == filter;
});
if(it != formats.end()) {
changeFunction(*it);
m_model->addCondFormat(isRowIdFormat, refIndex.column(), *it);
} else {
// Create a new conditional format based on the current reference index and then modify it as requested using the passed function.
CondFormat newCondFormat(filter, m_model, refIndex, m_model->encoding());
changeFunction(newCondFormat);
addCondFormat(isRowIdFormat, refIndex.column(), newCondFormat);
}
}
auto it = std::find_if(columnFormats.begin(), columnFormats.end(), [](const CondFormat& format) {
return format.sqlCondition().isEmpty();
});
if(it != columnFormats.end()) {
changeFunction(*it);
m_model->addCondFormat(column, *it);
} else {
// Create a new conditional format based on defaults and then modify it as requested using the passed function.
// Alignment is get from the current column since the default is different from text and numbers.
QFont font = QFont(Settings::getValue("databrowser", "font").toString());
font.setPointSize(Settings::getValue("databrowser", "fontsize").toInt());
Qt::Alignment align = Qt::Alignment(m_model->data(currentIndex().sibling(currentIndex().row(), static_cast<int>(column)),
Qt::TextAlignmentRole).toInt());
CondFormat newCondFormat(QString(""), QColor(Settings::getValue("databrowser", "reg_fg_colour").toString()),
QColor(Settings::getValue("databrowser", "reg_bg_colour").toString()),
font,
CondFormat::fromCombinedAlignment(align),
m_model->encoding());
changeFunction(newCondFormat);
m_model->addCondFormat(column, newCondFormat);
m_settings[currentlyBrowsedTableName()].condFormats[column].push_back(newCondFormat);
void TableBrowser::modifyFormat(std::function<void(CondFormat&)> changeFunction)
{
// Modify the conditional formats from entirely selected columns having the current filter value,
// or modify the row-id formats of selected cells, when the selection does not cover entire columns.
const std::unordered_set<size_t>& columns = ui->dataTable->selectedCols();
if (columns.size() > 0) {
for (size_t column : columns) {
const QString& filter = m_settings[currentlyBrowsedTableName()].filterValues.value(static_cast<int>(column));
modifySingleFormat(false, filter, currentIndex().sibling(currentIndex().row(), static_cast<int>(column)), changeFunction);
}
} else {
for(const QModelIndex& index : ui->dataTable->selectionModel()->selectedIndexes()) {
const QString filter = QString("=%1").arg(m_model->data(index.sibling(index.row(), 0)).toString());
modifySingleFormat(true, filter, index, changeFunction);
}
}
emit projectModified();
@@ -721,19 +751,23 @@ void TableBrowser::applySettings(const BrowseDataTableSettings& storedData, bool
for(auto filterIt=storedData.filterValues.constBegin();filterIt!=storedData.filterValues.constEnd();++filterIt)
filterHeader->setFilter(static_cast<size_t>(filterIt.key()), filterIt.value());
// Conditional formats
// Regular conditional formats
for(auto formatIt=storedData.condFormats.constBegin(); formatIt!=storedData.condFormats.constEnd(); ++formatIt)
m_model->setCondFormats(formatIt.key(), formatIt.value());
m_model->setCondFormats(false, formatIt.key(), formatIt.value());
filterHeader->blockSignals(oldState);
// Row Id formats
for(auto formatIt=storedData.rowIdFormats.constBegin(); formatIt!=storedData.rowIdFormats.constEnd(); ++formatIt)
m_model->setCondFormats(true, formatIt.key(), formatIt.value());
ui->editGlobalFilter->blockSignals(true);
QString text;
for(const auto& f : storedData.globalFilters)
text += f + " ";
text.chop(1);
ui->editGlobalFilter->setText(text);
ui->editGlobalFilter->blockSignals(false);
filterHeader->blockSignals(oldState);
ui->editGlobalFilter->blockSignals(true);
QString text;
for(const auto& f : storedData.globalFilters)
text += f + " ";
text.chop(1);
ui->editGlobalFilter->setText(text);
ui->editGlobalFilter->blockSignals(false);
}
// Encoding

View File

@@ -25,10 +25,12 @@ class TableBrowser;
struct BrowseDataTableSettings
{
using CondFormatMap = QMap<size_t, std::vector<CondFormat>>;
sqlb::Query query; // NOTE: We only store the sort order in here (for now)
QMap<int, int> columnWidths;
QMap<int, QString> filterValues;
QMap<size_t, std::vector<CondFormat>> condFormats;
CondFormatMap condFormats;
CondFormatMap rowIdFormats;
QMap<int, QString> displayFormats;
bool showRowid;
QString encoding;
@@ -121,8 +123,10 @@ signals:
private slots:
void clear();
void updateFilter(size_t column, const QString& value);
void addCondFormat(size_t column, const QString& value);
void addCondFormatFromFilter(size_t column, const QString& value);
void addCondFormat(bool isRowIdFormat, size_t column, const CondFormat& newCondFormat);
void clearAllCondFormats(size_t column);
void clearRowIdFormats(const QModelIndex index);
void editCondFormats(size_t column);
void applySettings(const BrowseDataTableSettings& storedData, bool skipFilters = false);
void enableEditing(bool enable_edit);
@@ -176,7 +180,9 @@ private:
Palette m_condFormatPalette;
void modifyColumnFormat(std::unordered_set<size_t> columns, std::function<void(CondFormat&)> changeFunction);
void modifySingleFormat(const bool isRowIdFormat, const QString& filter, const QModelIndex refIndex,
std::function<void(CondFormat&)> changeFunction);
void modifyFormat(std::function<void(CondFormat&)> changeFunction);
};
#endif

View File

@@ -875,6 +875,9 @@
<property name="toolTip">
<string>Edit Conditional Formats...</string>
</property>
<property name="statusTip">
<string>Edit conditional formats for the current column</string>
</property>
</action>
<action name="actionClearFormat">
<property name="icon">
@@ -885,7 +888,13 @@
<string>Clear Format</string>
</property>
<property name="toolTip">
<string>Clear All Conditional Formats</string>
<string>Clear All Formats</string>
</property>
<property name="statusTip">
<string>Clear all cell formatting from selected cells and all conditional formats from selected columns</string>
</property>
<property name="whatsThis">
<string>Clear all cell formatting from selected cells and all conditional formats from selected columns</string>
</property>
</action>
<action name="actionFontColor">

View File

@@ -112,6 +112,7 @@ void SqliteTableModel::reset()
m_headers.clear();
m_vDataTypes.clear();
m_mCondFormats.clear();
m_mRowIdFormats.clear();
endResetModel();
}
@@ -256,9 +257,9 @@ QVariant SqliteTableModel::headerData(int section, Qt::Orientation orientation,
return QString::number(section + 1);
}
QVariant SqliteTableModel::getMatchingCondFormat(size_t column, const QString& value, int role) const
QVariant SqliteTableModel::getMatchingCondFormat(const std::map<size_t, std::vector<CondFormat>>& mCondFormats, size_t column, const QString& value, int role) const
{
if (!m_mCondFormats.count(column))
if (!mCondFormats.count(column))
return QVariant();
bool isNumber;
@@ -267,7 +268,7 @@ QVariant SqliteTableModel::getMatchingCondFormat(size_t column, const QString& v
// For each conditional format for this column,
// if the condition matches the current data, return the associated format.
for (const CondFormat& eachCondFormat : m_mCondFormats.at(column)) {
for (const CondFormat& eachCondFormat : mCondFormats.at(column)) {
if (isNumber && !eachCondFormat.sqlCondition().contains("'"))
sql = QString("SELECT %1 %2").arg(value, eachCondFormat.sqlCondition());
else
@@ -290,6 +291,26 @@ QVariant SqliteTableModel::getMatchingCondFormat(size_t column, const QString& v
return QVariant();
}
QVariant SqliteTableModel::getMatchingCondFormat(size_t row, size_t column, const QString& value, int role) const
{
QVariant format;
// Check first for a row-id format and when there is none, for a conditional format.
if (m_mRowIdFormats.count(column)) {
QMutexLocker lock(&m_mutexDataCache);
const bool row_available = m_cache.count(row);
const QByteArray blank_data("");
const QByteArray& row_id_data = row_available ? m_cache.at(row).at(0) : blank_data;
lock.unlock();
format = getMatchingCondFormat(m_mRowIdFormats, column, row_id_data, role);
if (format.isValid())
return format;
}
if (m_mCondFormats.count(column))
return getMatchingCondFormat(m_mCondFormats, column, value, role);
else
return QVariant();
}
QVariant SqliteTableModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
@@ -335,7 +356,7 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const
else {
// Unlock before querying from DB
lock.unlock();
QVariant condFormatFont = getMatchingCondFormat(column, data, role);
QVariant condFormatFont = getMatchingCondFormat(row, column, data, role);
if (condFormatFont.isValid())
return condFormatFont;
}
@@ -347,10 +368,10 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const
return m_nullFgColour;
else if (isBinary(data))
return m_binFgColour;
else if (m_mCondFormats.count(column)) {
else {
// Unlock before querying from DB
lock.unlock();
QVariant condFormatColor = getMatchingCondFormat(column, data, role);
QVariant condFormatColor = getMatchingCondFormat(row, column, data, role);
if (condFormatColor.isValid())
return condFormatColor;
}
@@ -363,10 +384,10 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const
return m_nullBgColour;
else if (isBinary(data))
return m_binBgColour;
else if (m_mCondFormats.count(column)) {
else {
// Unlock before querying from DB
lock.unlock();
QVariant condFormatColor = getMatchingCondFormat(column, data, role);
QVariant condFormatColor = getMatchingCondFormat(row, column, data, role);
if (condFormatColor.isValid())
return condFormatColor;
}
@@ -383,7 +404,7 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const
// Align horizontally according to conditional format or default (left for text and right for numbers)
// Align vertically to the center, which displays better.
lock.unlock();
QVariant condFormat = getMatchingCondFormat(column, data, role);
QVariant condFormat = getMatchingCondFormat(row, column, data, role);
if (condFormat.isValid())
return condFormat;
bool isNumber;
@@ -799,23 +820,38 @@ std::vector<std::string> SqliteTableModel::getColumns(std::shared_ptr<sqlite3> p
return listColumns;
}
void SqliteTableModel::addCondFormat(size_t column, const CondFormat& condFormat)
void addCondFormatToMap(std::map<size_t, std::vector<CondFormat>>& mCondFormats, size_t column, const CondFormat& condFormat)
{
// If the condition is already present in the vector, update that entry and respect the order, since two entries with the same
// condition do not make sense.
auto it = std::find_if(m_mCondFormats[column].begin(), m_mCondFormats[column].end(), [condFormat](const CondFormat& format) {
auto it = std::find_if(mCondFormats[column].begin(), mCondFormats[column].end(), [condFormat](const CondFormat& format) {
return format.sqlCondition() == condFormat.sqlCondition();
});
if(it != m_mCondFormats[column].end()) {
// Replace cond-format if present. push it back if it's a conditionless format (apply to every cell in column) or insert
// as first element otherwise.
if(it != mCondFormats[column].end()) {
*it = condFormat;
} else
m_mCondFormats[column].push_back(condFormat);
} else if (condFormat.filter().isEmpty())
mCondFormats[column].push_back(condFormat);
else
mCondFormats[column].insert(mCondFormats[column].begin(), condFormat);
}
void SqliteTableModel::addCondFormat(const bool isRowIdFormat, size_t column, const CondFormat& condFormat)
{
if(isRowIdFormat)
addCondFormatToMap(m_mRowIdFormats, column, condFormat);
else
addCondFormatToMap(m_mCondFormats, column, condFormat);
emit layoutChanged();
}
void SqliteTableModel::setCondFormats(size_t column, const std::vector<CondFormat>& condFormats)
void SqliteTableModel::setCondFormats(const bool isRowIdFormat, size_t column, const std::vector<CondFormat>& condFormats)
{
m_mCondFormats[column] = condFormats;
if(isRowIdFormat)
m_mRowIdFormats[column] = condFormats;
else
m_mCondFormats[column] = condFormats;
emit layoutChanged();
}

View File

@@ -113,8 +113,11 @@ public:
// Helper function for removing all comments from a SQL query
static void removeCommentsFromQuery(QString& query);
void addCondFormat(size_t column, const CondFormat& condFormat);
void setCondFormats(size_t column, const std::vector<CondFormat>& condFormats);
// Conditional formats are of two kinds: regular conditional formats (including condition-free formats applying to any value in the
// column) and formats applying to a particular row-id and which have always precedence over the first kind and whose filter apply
// to the row-id column.
void addCondFormat(const bool isRowIdFormat, size_t column, const CondFormat& condFormat);
void setCondFormats(const bool isRowIdFormat, size_t column, const std::vector<CondFormat>& condFormats);
// Search for the specified expression in the given cells. This intended as a replacement for QAbstractItemModel::match() even though
// it does not override it, which - because of the different parameters - is not possible.
@@ -166,7 +169,8 @@ private:
// Return matching conditional format color/font or invalid value, otherwise.
// Only format roles are expected in role (Qt::ItemDataRole)
QVariant getMatchingCondFormat(size_t column, const QString& value, int role) const;
QVariant getMatchingCondFormat(size_t row, size_t column, const QString& value, int role) const;
QVariant getMatchingCondFormat(const std::map<size_t, std::vector<CondFormat>>& mCondFormats, size_t column, const QString& value, int role) const;
DBBrowserDB& m_db;
@@ -199,6 +203,7 @@ private:
QString m_sQuery;
std::vector<int> m_vDataTypes;
std::map<size_t, std::vector<CondFormat>> m_mCondFormats;
std::map<size_t, std::vector<CondFormat>> m_mRowIdFormats;
sqlb::Query m_query;
QString m_encoding;