File indexing completed on 2024-05-19 05:01:24
0001 /* 0002 This file is part of the KDE project. 0003 0004 SPDX-FileCopyrightText: 2020 Stefano Crocco <posta@stefanocrocco.it> 0005 0006 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 #include "webfieldsdataview.h" 0010 0011 #include <KLocalizedString> 0012 0013 #include <QEvent> 0014 #include <QHeaderView> 0015 0016 using WebForm = WebEngineWallet::WebForm; 0017 using WebFormList = WebEngineWallet::WebFormList; 0018 using WebField = WebEngineWallet::WebForm::WebField; 0019 using WebFieldType = WebEngineWallet::WebForm::WebFieldType; 0020 0021 WebFieldsDataViewPasswordDelegate::WebFieldsDataViewPasswordDelegate(QObject* parent): QStyledItemDelegate(parent) 0022 { 0023 } 0024 0025 bool WebFieldsDataViewPasswordDelegate::isPassword(const QModelIndex& idx) 0026 { 0027 return idx.data(WebFieldsDataModel::PasswordRole).toBool(); 0028 } 0029 0030 QString WebFieldsDataViewPasswordDelegate::passwordReplacement(const QStyleOptionViewItem& option, const QModelIndex& index) 0031 { 0032 const QWidget *w = option.widget; 0033 QStyle *s = w->style(); 0034 QChar passwdChar(s->styleHint(QStyle::StyleHint::SH_LineEdit_PasswordCharacter, &option, w)); 0035 return QString(index.data().toString().length(), passwdChar); 0036 } 0037 0038 void WebFieldsDataViewPasswordDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const 0039 { 0040 if (!isPassword(index)) { 0041 QStyledItemDelegate::paint(painter, option, index); 0042 } else { 0043 QString str = passwordReplacement(option, index); 0044 option.widget->style()->drawItemText(painter, option.rect, index.data(Qt::TextAlignmentRole).toInt(), option.palette, true, str); 0045 } 0046 } 0047 0048 QSize WebFieldsDataViewPasswordDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const 0049 { 0050 if (!isPassword(index)) { 0051 return QStyledItemDelegate::sizeHint(option, index); 0052 } else { 0053 QString str = passwordReplacement(option, index); 0054 return option.widget->style()->itemTextRect(option.fontMetrics, option.rect, option.displayAlignment, true, str).size(); 0055 } 0056 } 0057 0058 WebFieldsDataModel::WebFieldsDataModel(bool checkableItems, QObject* parent): QStandardItemModel(parent), m_checkableItems(checkableItems) 0059 { 0060 setHorizontalHeaderLabels({"", 0061 i18nc("Label of a web field", "Field name"), 0062 i18nc("Value of a web field", "Field value"), 0063 i18nc("Name attribute of a web field", "Internal field name"), 0064 i18nc("Type of a web field", "Field type"), 0065 i18nc("The id of a web field", "Field id"), 0066 i18nc("Other details about a web field", "Details")}); 0067 } 0068 0069 WebFieldsDataModel::~WebFieldsDataModel() 0070 { 0071 } 0072 0073 void WebFieldsDataModel::setForms(const WebEngineWallet::WebFormList &forms) 0074 { 0075 m_forms = forms; 0076 removeRows(0, rowCount()); 0077 for (int i = 0; i < m_forms.size(); ++i) { 0078 const WebForm &form = m_forms.at(i); 0079 for (int j = 0; j < form.fields.size(); ++j) { 0080 const WebField &field = form.fields.at(j); 0081 appendRow(createRowForField(field, i, j)); 0082 } 0083 } 0084 } 0085 0086 void WebFieldsDataModel::clearForms() 0087 { 0088 m_forms.clear(); 0089 removeRows(0, rowCount()); 0090 } 0091 0092 WebEngineWallet::WebFormList WebFieldsDataModel::checkedFields() const 0093 { 0094 if (!m_checkableItems) { 0095 return {}; 0096 } 0097 QMap<int, QVector<int>> fields; 0098 for (int i = 0; i < rowCount(); ++i) { 0099 QStandardItem *it = item(i, ChosenCol); 0100 if (it->checkState() == Qt::Checked) { 0101 fields[it->data(FormRole).toInt()].append(it->data(FieldRole).toInt()); 0102 } 0103 } 0104 WebFormList lst; 0105 for (QMap<int, QVector<int>>::const_iterator it = fields.constBegin(); it != fields.constEnd(); ++it) { 0106 if (it.value().isEmpty()) { 0107 continue; 0108 } 0109 const WebForm &oldForm = m_forms.at(it.key()); 0110 WebForm form(oldForm); 0111 form.fields.clear(); 0112 for (int i : it.value()) { 0113 form.fields.append(oldForm.fields.at(i)); 0114 } 0115 lst.append(form); 0116 } 0117 return lst; 0118 } 0119 0120 QList<QStandardItem *> WebFieldsDataModel::createRowForField(const WebEngineWallet::WebForm::WebField& field, int formIndex, int fieldIndex) 0121 { 0122 QString type = WebForm::fieldNameFromType(field.type, true); 0123 QStringList notes; 0124 if (field.readOnly) { 0125 notes << i18nc("web field has the readonly attribute", "read only"); 0126 } 0127 if (!field.autocompleteAllowed) { 0128 notes << i18nc("web field has the autocomplete attribute set to off", "auto-completion off"); 0129 } 0130 if (field.disabled) { 0131 notes << i18nc("web field is disabled", "disabled"); 0132 } 0133 QString label = !field.label.isEmpty() ? field.label : field.name; 0134 QStringList contents{QString(), label, field.value, field.name, type, field.id, notes.join(", ")}; 0135 QList<QStandardItem*> row; 0136 row.reserve(contents.size()); 0137 auto itemFromString = [](const QString &s){ 0138 QStandardItem *it = new QStandardItem(s); 0139 it->setTextAlignment(Qt::AlignCenter); 0140 return it; 0141 }; 0142 std::transform(contents.constBegin(), contents.constEnd(), std::back_inserter(row), itemFromString); 0143 row[ValueCol]->setData(field.type == WebFieldType::Password, PasswordRole); 0144 if (m_checkableItems) { 0145 row[ChosenCol]->setCheckable(true); 0146 } 0147 QString toolTip = toolTipForField(field); 0148 row[LabelCol]->setToolTip(toolTip); 0149 row[ValueCol]->setToolTip(toolTip); 0150 QStandardItem *chosen = row.at(ChosenCol); 0151 chosen->setData(formIndex, FormRole); 0152 chosen->setData(fieldIndex, FieldRole); 0153 return row; 0154 } 0155 0156 QString WebFieldsDataModel::toolTipForField(const WebEngineWallet::WebForm::WebField& field) 0157 { 0158 QString type = WebForm::fieldNameFromType(field.type, true); 0159 const QString yes = i18nc("A statement about a field is true", "yes"); 0160 const QString no = i18nc("A statement about a field is false", "no"); 0161 auto boolToYesNo = [yes, no](bool val){return val ? yes : no;}; 0162 QString toolTip = i18n( 0163 "<ul><li><b>Field internal name: </b>%1</li>" 0164 "<li><b>Field type: </b>%2</li>" 0165 "<li><b>Field id: </b>%3</li>" 0166 "<li><b>Field is read only: </b>%4</li>" 0167 "<li><b>Field is enabled: </b>%5</li>" 0168 "<li><b>Autocompletion is enabled: </b>%6</li>" 0169 "</ul>", 0170 field.name, type, field.id, boolToYesNo(field.readOnly), boolToYesNo(!field.disabled), boolToYesNo(field.autocompleteAllowed)); 0171 return toolTip; 0172 } 0173 0174 WebFieldsDataView::WebFieldsDataView(QWidget* parent): QTableView(parent), 0175 m_passwordDelegate(new WebFieldsDataViewPasswordDelegate(this)), m_showPasswords(false), 0176 m_showDetails(false), m_showToolTips(true) 0177 { 0178 setItemDelegateForColumn(WebFieldsDataModel::ValueCol, m_passwordDelegate); 0179 setEditTriggers(QAbstractItemView::NoEditTriggers); 0180 verticalHeader()->hide(); 0181 } 0182 0183 WebFieldsDataView::~WebFieldsDataView() 0184 { 0185 } 0186 0187 void WebFieldsDataView::setModel(QAbstractItemModel* model) 0188 { 0189 QTableView::setModel(model); 0190 setDetailsVisible(m_showDetails); 0191 horizontalHeader()->setStretchLastSection(true); 0192 horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); 0193 WebFieldsDataModel *m = qobject_cast<WebFieldsDataModel*>(model); 0194 if (m) { 0195 setColumnHidden(WebFieldsDataModel::ChosenCol, !m->areItemsCheckable()); 0196 } 0197 } 0198 0199 void WebFieldsDataView::togglePasswords(bool show) 0200 { 0201 if (m_showPasswords == show) { 0202 return; 0203 } 0204 m_showPasswords = show; 0205 setItemDelegateForColumn(WebFieldsDataModel::ValueCol, show ? itemDelegate() : m_passwordDelegate); 0206 } 0207 0208 void WebFieldsDataView::toggleDetails(bool show) 0209 { 0210 if (m_showDetails == show) { 0211 return; 0212 } 0213 setDetailsVisible(show); 0214 } 0215 0216 void WebFieldsDataView::setDetailsVisible(bool visible) 0217 { 0218 m_showDetails = visible; 0219 for (int i = WebFieldsDataModel::InternalNameCol; i <= WebFieldsDataModel::DetailsCol; ++i) { 0220 setColumnHidden(i, !visible); 0221 } 0222 } 0223 0224 void WebFieldsDataView::toggleToolTips(bool show) 0225 { 0226 m_showToolTips = show; 0227 } 0228 0229 bool WebFieldsDataView::viewportEvent(QEvent* e) 0230 { 0231 if (!m_showToolTips && (e->type() == QEvent::ToolTip || e->type() == QEvent::ToolTipChange)) { 0232 e->accept(); 0233 return true; 0234 } else { 0235 return QTableView::viewportEvent(e); 0236 } 0237 } 0238 0239 QSize WebFieldsDataView::sizeHint() const 0240 { 0241 QSize hint = QTableView::sizeHint(); 0242 int h = 2*frameWidth(); 0243 if (horizontalHeader()->isVisible()) { 0244 h += horizontalHeader()->height(); 0245 } 0246 if (model() && model()->rowCount() != 0) { 0247 h += rowHeight(0) * model()->rowCount(); 0248 } 0249 hint.setHeight(h); 0250 return hint; 0251 }