diff --git a/src/CondFormat.cpp b/src/CondFormat.cpp index db3699f5..39776d1e 100644 --- a/src/CondFormat.cpp +++ b/src/CondFormat.cpp @@ -2,6 +2,8 @@ #include "Settings.h" #include "Data.h" +#include + 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. diff --git a/src/CondFormat.h b/src/CondFormat.h index 07fe275d..2bedfd66 100644 --- a/src/CondFormat.h +++ b/src/CondFormat.h @@ -4,6 +4,9 @@ #include #include #include +#include + +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()); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 3f407208..595d9e1a 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -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(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(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"); diff --git a/src/TableBrowser.cpp b/src/TableBrowser.cpp index cf90499d..45dad07c 100644 --- a/src/TableBrowser.cpp +++ b/src/TableBrowser.cpp @@ -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(&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(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 ¤t, 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& 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 emptyCondFormatVector = std::vector(); - 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& 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(column), Qt::Horizontal).toString())); + arg(m_model->headerData(static_cast(column), Qt::Horizontal, Qt::EditRole).toString())); if (condFormatDialog.exec()) { std::vector 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 columns, std::function changeFunction) +void TableBrowser::modifySingleFormat(const bool isRowIdFormat, const QString& filter, const QModelIndex refIndex, std::function changeFunction) { - for (size_t column : columns) { - std::vector& columnFormats = m_settings[currentlyBrowsedTableName()].condFormats[column]; + BrowseDataTableSettings& settings = m_settings[currentlyBrowsedTableName()]; + std::vector& 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(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 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& columns = ui->dataTable->selectedCols(); + if (columns.size() > 0) { + for (size_t column : columns) { + const QString& filter = m_settings[currentlyBrowsedTableName()].filterValues.value(static_cast(column)); + modifySingleFormat(false, filter, currentIndex().sibling(currentIndex().row(), static_cast(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(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 diff --git a/src/TableBrowser.h b/src/TableBrowser.h index 022fc118..5220482e 100644 --- a/src/TableBrowser.h +++ b/src/TableBrowser.h @@ -25,10 +25,12 @@ class TableBrowser; struct BrowseDataTableSettings { + using CondFormatMap = QMap>; sqlb::Query query; // NOTE: We only store the sort order in here (for now) QMap columnWidths; QMap filterValues; - QMap> condFormats; + CondFormatMap condFormats; + CondFormatMap rowIdFormats; QMap 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 columns, std::function changeFunction); + void modifySingleFormat(const bool isRowIdFormat, const QString& filter, const QModelIndex refIndex, + std::function changeFunction); + void modifyFormat(std::function changeFunction); }; #endif diff --git a/src/TableBrowser.ui b/src/TableBrowser.ui index 7efe720d..2be465d1 100644 --- a/src/TableBrowser.ui +++ b/src/TableBrowser.ui @@ -875,6 +875,9 @@ Edit Conditional Formats... + + Edit conditional formats for the current column + @@ -885,7 +888,13 @@ Clear Format - Clear All Conditional Formats + Clear All Formats + + + Clear all cell formatting from selected cells and all conditional formats from selected columns + + + Clear all cell formatting from selected cells and all conditional formats from selected columns diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index d5e6004a..bb904efa 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -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>& 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 SqliteTableModel::getColumns(std::shared_ptr p return listColumns; } -void SqliteTableModel::addCondFormat(size_t column, const CondFormat& condFormat) +void addCondFormatToMap(std::map>& 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& condFormats) +void SqliteTableModel::setCondFormats(const bool isRowIdFormat, size_t column, const std::vector& condFormats) { - m_mCondFormats[column] = condFormats; + if(isRowIdFormat) + m_mRowIdFormats[column] = condFormats; + else + m_mCondFormats[column] = condFormats; emit layoutChanged(); } diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index b673baf5..1f6a902d 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -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& 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& 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>& mCondFormats, size_t column, const QString& value, int role) const; DBBrowserDB& m_db; @@ -199,6 +203,7 @@ private: QString m_sQuery; std::vector m_vDataTypes; std::map> m_mCondFormats; + std::map> m_mRowIdFormats; sqlb::Query m_query; QString m_encoding;