// Squawk messenger. // Copyright (C) 2019 Yury Gubich // // This program 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 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "keydelegate.h" #include #include #include "ui/models/info/omemo/keys.h" #include constexpr uint8_t margin = 10; constexpr uint8_t partSize = 8; constexpr uint8_t maxSingleLineParts = 3; UI::KeyDelegate::KeyDelegate(QObject* parent): QStyledItemDelegate(parent), defaultFont(Shared::Global::getInstance()->defaultFont), fingerPrintFont(Shared::Global::getInstance()->monospaceFont), labelFont(Shared::Global::getInstance()->smallFont), defaultMetrics(Shared::Global::getInstance()->defaultFontMetrics), fingerPrintMetrics(Shared::Global::getInstance()->monospaceMetrics), labelMetrics(Shared::Global::getInstance()->smallFontMetrics), spaceWidth(fingerPrintMetrics.horizontalAdvance(" ")) {} UI::KeyDelegate::~KeyDelegate() {} void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); QRect r; int maxRight = 0; int leftOrigin = option.rect.left() + margin; QColor q = painter->pen().color(); q.setAlpha(180); QRect rect = option.rect; bool hover = option.state & QStyle::State_MouseOver; if (hover) { painter->save(); painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight)); painter->restore(); } QVariant dirtyV = index.data(Models::Keys::Dirty); if (dirtyV.isValid() && dirtyV.toBool()) { painter->save(); rect.setWidth(margin / 2); painter->fillRect(rect, option.palette.brush(QPalette::Active, QPalette::Highlight)); rect.setWidth(option.rect.width()); painter->restore(); } rect.adjust(margin, margin, -margin, -margin); QVariant labelV = index.data(Models::Keys::Label); if (labelV.isValid()) { QString label = labelV.toString(); if (label.size() > 0) { painter->save(); painter->setFont(labelFont); painter->setPen(q); painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, label, &r); rect.adjust(0, labelMetrics.lineSpacing(), 0, 0); maxRight = std::max(r.width(), maxRight); painter->restore(); } } QVariant fingerPrintV = index.data(Models::Keys::FingerPrint); if (fingerPrintV.isValid()) { painter->save(); painter->setFont(fingerPrintFont); QByteArray fingerPrint = fingerPrintV.toByteArray(); std::vector parts = getParts(fingerPrint); uint8_t partsLength = parts.size(); uint8_t firstLine; if (partsLength > maxSingleLineParts) firstLine = partsLength / 2 + partsLength % 2; else firstLine = partsLength; for (uint8_t i = 0; i < partsLength; ++i) { if (i == firstLine) { maxRight = std::max(rect.left() - leftOrigin - margin, maxRight); rect.setLeft(leftOrigin); rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0); } painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, parts[i], &r); rect.adjust(r.width() + spaceWidth ,0, 0, 0); } maxRight = std::max(rect.left() - leftOrigin - margin, maxRight); rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0); rect.setLeft(leftOrigin); painter->restore(); } QVariant lastV = index.data(Models::Keys::LastInteraction); if (lastV.isValid()) { QDateTime last = lastV.toDateTime(); if (last.isValid()) { painter->save(); painter->setFont(labelFont); painter->setPen(q); painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, last.toString(), &r); rect.adjust(0, labelMetrics.lineSpacing(), 0, 0); maxRight = std::max(r.width(), maxRight); painter->restore(); } } QVariant levelV = index.data(Models::Keys::TrustLevel); if (levelV.isValid()) { Shared::TrustLevel level = static_cast(levelV.toUInt()); QString levelName = Shared::Global::getName(level); if (maxRight > 0) maxRight += margin; rect.setLeft(leftOrigin + maxRight); rect.setTop(option.rect.top() + maxRight); rect.setBottom(option.rect.bottom() - maxRight); painter->setFont(defaultFont); painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, levelName, &r); } painter->restore(); } QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { QSize size = QStyledItemDelegate::sizeHint(option, index); int mh = margin * 2; int mw = margin * 2; int width = 0; QVariant labelV = index.data(Models::Keys::Label); if (labelV.isValid()) { QString label = labelV.toString(); if (label.size() > 0) { mh += labelMetrics.lineSpacing(); width = labelMetrics.horizontalAdvance(label); } } QVariant fingerPrintV = index.data(Models::Keys::FingerPrint); if (fingerPrintV.isValid()) { QString hex = fingerPrintV.toByteArray().toHex(); uint8_t parts = hex.size() / partSize; mh += fingerPrintMetrics.height(); if (parts > maxSingleLineParts) mh += fingerPrintMetrics.height() + fingerPrintMetrics.leading(); uint8_t firstLine; if (parts > maxSingleLineParts) firstLine = parts / 2 + parts % 2; else firstLine = parts; width = std::max(width, firstLine * fingerPrintMetrics.horizontalAdvance(hex, partSize) + (firstLine - 1) * spaceWidth); width += 1; //there is a mistake somewhere, this the cheapest way to compensate it } QVariant lastV = index.data(Models::Keys::LastInteraction); if (lastV.isValid()) { QDateTime last = lastV.toDateTime(); if (last.isValid()) { mh += labelMetrics.lineSpacing(); QString dt = last.toString(); width = std::max(labelMetrics.horizontalAdvance(dt), width); } } QVariant levelV = index.data(Models::Keys::TrustLevel); if (levelV.isValid()) { Shared::TrustLevel level = static_cast(levelV.toUInt()); QString levelName = Shared::Global::getName(level); if (width > 0) width += margin; width += defaultMetrics.horizontalAdvance(levelName); width += 1; //there is a mistake somewhere, this the cheapest way to compensate it } mw += width; if (size.width() < mw) size.setWidth(mw); if (size.height() < mh) size.setHeight(mh); return size; } std::vector UI::KeyDelegate::getParts(const QByteArray& data) { QString hex = data.toHex(); uint8_t parts = hex.size() / partSize; std::vector result(parts); for (uint8_t i = 0; i < parts; ++i) { uint16_t index = i * partSize; uint8_t length = partSize; if (index + length > hex.size()) length = hex.size() - index; QStringRef part(&hex, index, length); result[i] = part.toString(); } return result; }