File indexing completed on 2024-05-12 16:43:42
0001 /* 0002 SPDX-FileCopyrightText: 2015-2019 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "journaldelegate.h" 0007 0008 // ---------------------------------------------------------------------------- 0009 // QT Includes 0010 0011 #include <QApplication> 0012 #include <QScrollBar> 0013 #include <QPainter> 0014 #include <QDebug> 0015 #include <QDate> 0016 #include <QSortFilterProxyModel> 0017 0018 // ---------------------------------------------------------------------------- 0019 // KDE Includes 0020 0021 #include <KLocalizedString> 0022 #include <KColorScheme> 0023 0024 // ---------------------------------------------------------------------------- 0025 // Project Includes 0026 0027 #include "mymoneyfile.h" 0028 #include "ledgerview.h" 0029 #include "journalmodel.h" 0030 #include "schedulesjournalmodel.h" 0031 #include "accountsmodel.h" 0032 #include "payeesmodel.h" 0033 #include "newtransactioneditor.h" 0034 #include "investtransactioneditor.h" 0035 #include "mymoneyutils.h" 0036 #include "mymoneysecurity.h" 0037 0038 // clang-format off 0039 static unsigned char attentionSign[] = { 0040 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0041 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0042 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0043 0x08, 0x06, 0x00, 0x00, 0x00, 0x8D, 0x89, 0x1D, 0044 0x0D, 0x00, 0x00, 0x00, 0x04, 0x73, 0x42, 0x49, 0045 0x54, 0x08, 0x08, 0x08, 0x08, 0x7C, 0x08, 0x64, 0046 0x88, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0047 0x74, 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0048 0x65, 0x00, 0x77, 0x77, 0x77, 0x2E, 0x69, 0x6E, 0049 0x6B, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2E, 0x6F, 0050 0x72, 0x67, 0x9B, 0xEE, 0x3C, 0x1A, 0x00, 0x00, 0051 0x02, 0x05, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8D, 0052 0xAD, 0x95, 0xBF, 0x4B, 0x5B, 0x51, 0x14, 0xC7, 0053 0x3F, 0x2F, 0xBC, 0x97, 0x97, 0x97, 0x97, 0x77, 0054 0xF3, 0xF2, 0x1C, 0xA4, 0x54, 0x6B, 0x70, 0x10, 0055 0x44, 0x70, 0x2A, 0x91, 0x2E, 0x52, 0x02, 0x55, 0056 0x8A, 0xB5, 0xA3, 0xAB, 0x38, 0x08, 0x66, 0xCC, 0057 0xEE, 0xE0, 0xE2, 0x20, 0xB8, 0x38, 0xB8, 0xB8, 0058 0xF8, 0x1F, 0x38, 0x29, 0xA5, 0x29, 0x74, 0x90, 0059 0x0E, 0x0D, 0x0E, 0x22, 0x1D, 0x44, 0xA8, 0xD0, 0060 0xD4, 0xB4, 0x58, 0x4B, 0x09, 0xF9, 0xF1, 0x4A, 0061 0x3B, 0xD4, 0xD3, 0xE1, 0x55, 0xD3, 0x34, 0xAF, 0062 0x49, 0x6C, 0x3D, 0xF0, 0x85, 0x7B, 0xCF, 0xFD, 0063 0x9E, 0xEF, 0x3D, 0xE7, 0xFE, 0xD4, 0x44, 0x84, 0064 0xDB, 0xB4, 0x48, 0x2F, 0xA4, 0x94, 0xAB, 0xE5, 0065 0x52, 0xAE, 0x96, 0xEB, 0x49, 0x51, 0x44, 0x3A, 0066 0x02, 0x18, 0x88, 0xC7, 0xF1, 0xE3, 0x71, 0x7C, 0067 0x60, 0xA0, 0x1B, 0xBF, 0x6B, 0x86, 0x49, 0xC5, 0068 0x46, 0x3E, 0x47, 0x34, 0x9F, 0x23, 0x9A, 0x54, 0069 0x6C, 0xFC, 0x57, 0x86, 0x40, 0xC6, 0x4B, 0xE1, 0070 0x37, 0xCA, 0x48, 0xA3, 0x8C, 0x78, 0x29, 0x7C, 0071 0x20, 0xD3, 0x31, 0xA6, 0xD3, 0xA0, 0x52, 0x1C, 0072 0x6D, 0x6F, 0x72, 0xD9, 0x28, 0x23, 0xFE, 0x07, 0073 0x64, 0x7B, 0x93, 0x4B, 0xA5, 0x38, 0xFA, 0x27, 0074 0x41, 0x60, 0x6E, 0x74, 0x84, 0x7A, 0xE5, 0x1D, 0075 0x92, 0x54, 0x88, 0xE7, 0x22, 0xD5, 0x12, 0x32, 0076 0x3A, 0x42, 0x1D, 0x98, 0xBB, 0x91, 0x20, 0x60, 0077 0xDA, 0x36, 0x17, 0xFB, 0x7B, 0xC8, 0xC1, 0x4B, 0078 0x04, 0x02, 0xBC, 0x7E, 0x81, 0xEC, 0xEF, 0x21, 0079 0xB6, 0xCD, 0x05, 0x60, 0xF6, 0x2C, 0x68, 0x9A, 0080 0x2C, 0xCF, 0x4C, 0xE1, 0x4B, 0x05, 0x39, 0x3F, 0081 0x69, 0x0A, 0xBE, 0x7F, 0x83, 0x48, 0x05, 0x99, 0082 0x99, 0xC2, 0x37, 0x4D, 0x96, 0x7B, 0x12, 0x04, 0083 0xFA, 0x2D, 0x8B, 0xC6, 0xE9, 0x61, 0x10, 0x2C, 0084 0x15, 0xC4, 0x8A, 0x21, 0x86, 0x8E, 0xFC, 0xF8, 0085 0x12, 0xF4, 0x4F, 0x0F, 0x11, 0xCB, 0xA2, 0x01, 0086 0xF4, 0x77, 0x3D, 0x36, 0x4E, 0x82, 0xF5, 0xA5, 0087 0x05, 0x8C, 0xE1, 0x74, 0xD3, 0x37, 0x34, 0x18, 0088 0x20, 0xF2, 0x8B, 0x3D, 0x9C, 0x86, 0xA5, 0x05, 0089 0x0C, 0x27, 0xC1, 0x7A, 0xC7, 0x63, 0x03, 0x8C, 0090 0x2B, 0x07, 0xBF, 0x5A, 0x6A, 0x66, 0x27, 0x15, 0091 0x64, 0x3A, 0x8B, 0x3C, 0x7A, 0xD8, 0xEA, 0xAB, 0092 0x96, 0x10, 0xE5, 0xE0, 0x03, 0xE3, 0x7F, 0xCD, 0093 0x50, 0x39, 0x6C, 0xAD, 0xAD, 0x10, 0x53, 0xAA, 0094 0x75, 0xD2, 0xF4, 0xBD, 0x00, 0x2D, 0x5C, 0x05, 0095 0x6B, 0x2B, 0xC4, 0x94, 0xC3, 0xD6, 0xEF, 0xFE, 0096 0x6B, 0x41, 0x4D, 0xD3, 0x66, 0xFB, 0x3C, 0xC6, 0097 0x16, 0xE7, 0xDB, 0x97, 0x61, 0xE2, 0x3E, 0x3C, 0098 0xC8, 0xB4, 0x15, 0xC7, 0xE2, 0x3C, 0x91, 0x3E, 0099 0x8F, 0x31, 0x4D, 0xD3, 0x66, 0x5B, 0x4A, 0x06, 0100 0x8C, 0x84, 0xCD, 0x59, 0x61, 0xA7, 0xB5, 0xAC, 0101 0x2B, 0x9C, 0x1C, 0x04, 0x08, 0x1B, 0x2B, 0xEC, 0102 0x20, 0x09, 0x9B, 0x33, 0xC0, 0xB8, 0xDE, 0x65, 0103 0x43, 0x27, 0x9F, 0x9D, 0xA4, 0x1E, 0x16, 0xF0, 0104 0xF9, 0x6D, 0xB0, 0xC3, 0x86, 0x1E, 0xB4, 0xC3, 0105 0x38, 0xD9, 0x49, 0xEA, 0x86, 0x4E, 0xFE, 0xEA, 0106 0x29, 0xF4, 0x2C, 0x8B, 0xDA, 0x71, 0x31, 0x9C, 0107 0xFC, 0xF5, 0x23, 0x32, 0x34, 0x88, 0xDC, 0xBD, 0108 0x13, 0x5C, 0xBF, 0x30, 0xCE, 0x71, 0x11, 0xB1, 0109 0x2C, 0x6A, 0x80, 0xA7, 0xDB, 0x36, 0xAB, 0x4F, 0110 0xA6, 0x89, 0xBA, 0x49, 0x38, 0xFF, 0xD4, 0xBE, 0111 0x4E, 0x00, 0xAF, 0x9E, 0x81, 0x08, 0xD4, 0xEA, 0112 0x01, 0xFE, 0x34, 0x37, 0x09, 0x4F, 0x1F, 0x13, 0113 0xDD, 0x7D, 0xCE, 0xAA, 0x96, 0x72, 0x29, 0x7C, 0114 0xFB, 0xCE, 0x44, 0xB8, 0xD4, 0xCD, 0x2C, 0x66, 0115 0x52, 0xD4, 0x6E, 0xFB, 0x0B, 0xF8, 0x09, 0x63, 0116 0x63, 0x31, 0xE4, 0x85, 0x76, 0x2E, 0x0E, 0x00, 0117 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0118 0x42, 0x60, 0x82 0119 }; 0120 // clang-format on 0121 0122 QColor JournalDelegate::m_erroneousColor = QColor(Qt::red); 0123 QColor JournalDelegate::m_importedColor = QColor(Qt::yellow); 0124 QColor JournalDelegate::m_separatorColor = QColor(0xff, 0xf2, 0x9b); 0125 0126 0127 0128 0129 class JournalDelegate::Private 0130 { 0131 public: 0132 Private() 0133 : m_editor(nullptr) 0134 , m_view(nullptr) 0135 , m_editorRow(-1) 0136 , m_singleLineRole(eMyMoney::Model::SplitPayeeRole) 0137 , m_lineHeight(-1) 0138 , m_margin(2) 0139 0140 {} 0141 0142 ~Private() 0143 { 0144 } 0145 0146 QStringList displayString(const QModelIndex& index, const QStyleOptionViewItem& opt) 0147 { 0148 QStringList lines; 0149 if(index.column() == JournalModel::Column::Detail) { 0150 if (index.data(eMyMoney::Model::TransactionIsInvestmentRole).toBool()) { 0151 if(opt.state & QStyle::State_Selected) { 0152 lines << index.data(eMyMoney::Model::SplitActivityRole).toString(); 0153 lines << index.data(eMyMoney::Model::TransactionBrokerageAccountRole).toString(); 0154 lines << index.data(eMyMoney::Model::TransactionInterestCategoryRole).toString(); 0155 lines << index.data(eMyMoney::Model::TransactionFeesCategoryRole).toString(); 0156 lines << index.data(eMyMoney::Model::SplitSingleLineMemoRole).toString(); 0157 } else { 0158 lines << index.data(eMyMoney::Model::SplitActivityRole).toString(); 0159 } 0160 0161 } else { 0162 lines << index.data(m_singleLineRole).toString(); 0163 if(opt.state & QStyle::State_Selected) { 0164 lines.clear(); 0165 lines << index.data(eMyMoney::Model::Roles::SplitPayeeRole).toString(); 0166 lines << index.data(eMyMoney::Model::Roles::TransactionCounterAccountRole).toString(); 0167 lines << index.data(eMyMoney::Model::Roles::SplitSingleLineMemoRole).toString(); 0168 0169 } else { 0170 if(lines.at(0).isEmpty()) { 0171 lines.clear(); 0172 lines << index.data(eMyMoney::Model::Roles::SplitSingleLineMemoRole).toString(); 0173 } 0174 if(lines.at(0).isEmpty()) { 0175 lines << index.data(eMyMoney::Model::Roles::TransactionCounterAccountRole).toString(); 0176 } 0177 } 0178 } 0179 lines.removeAll(QString()); 0180 0181 } else if(index.column() == JournalModel::Column::Quantity) { 0182 if (index.data(eMyMoney::Model::TransactionIsInvestmentRole).toBool()) { 0183 lines << opt.text; 0184 if(opt.state & QStyle::State_Selected) { 0185 // we have to pay attention here as later on empty items will be removed 0186 // from the lines all together. Since we use the column detail as label 0187 // we have to make that we are not off. Therefor, if the detail column 0188 // is filled, we add a simple blank here instead of an empty line. 0189 // The first line is always present, so we make sure it is not empty in this column. 0190 if (lines[0].isEmpty()) 0191 lines[0] = QStringLiteral(" "); 0192 lines << (index.data(eMyMoney::Model::TransactionBrokerageAccountRole).toString().isEmpty() ? QString() : QStringLiteral(" ")); 0193 0194 MyMoneySecurity currency = MyMoneyFile::instance()->currency(index.data(eMyMoney::Model::TransactionCommodityRole).toString()); 0195 0196 if (index.data(eMyMoney::Model::TransactionInterestSplitPresentRole).toBool()) { 0197 const auto value = index.data(eMyMoney::Model::TransactionInterestValueRole).value<MyMoneyMoney>(); 0198 lines << (index.data(eMyMoney::Model::TransactionInterestCategoryRole).toString().isEmpty() ? QString() : MyMoneyUtils::formatMoney(value.abs(), currency)); 0199 } 0200 0201 if (index.data(eMyMoney::Model::TransactionFeeSplitPresentRole).toBool()) { 0202 const auto value = index.data(eMyMoney::Model::TransactionFeesValueRole).value<MyMoneyMoney>(); 0203 lines << (index.data(eMyMoney::Model::TransactionFeesCategoryRole).toString().isEmpty() ? QString() : MyMoneyUtils::formatMoney(value.abs(), currency)); 0204 } 0205 } else { 0206 lines << opt.text; 0207 } 0208 } 0209 lines.removeAll(QString()); 0210 0211 } else { 0212 lines << opt.text; 0213 } 0214 return lines; 0215 } 0216 0217 TransactionEditorBase* m_editor; 0218 LedgerView* m_view; 0219 int m_editorRow; 0220 eMyMoney::Model::Roles m_singleLineRole; 0221 int m_lineHeight; 0222 int m_margin; 0223 int m_editorWidthOfs; 0224 }; 0225 0226 0227 JournalDelegate::JournalDelegate(LedgerView* parent) 0228 : KMMStyledItemDelegate(parent) 0229 , d(new Private) 0230 { 0231 d->m_view = parent; 0232 } 0233 0234 JournalDelegate::~JournalDelegate() 0235 { 0236 delete d; 0237 } 0238 0239 void JournalDelegate::setErroneousColor(const QColor& color) 0240 { 0241 m_erroneousColor = color; 0242 } 0243 0244 void JournalDelegate::setSingleLineRole(eMyMoney::Model::Roles role) 0245 { 0246 d->m_singleLineRole = role; 0247 } 0248 0249 QWidget* JournalDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const 0250 { 0251 Q_UNUSED(option); 0252 0253 if(index.isValid()) { 0254 if(d->m_view->selectionModel()->selectedRows().count() > 1) { 0255 qDebug() << "Editing multiple transactions at once is not yet supported"; 0256 0257 /** 0258 * @todo replace the following three lines with the creation of a special 0259 * editor that can handle multiple transactions at once 0260 */ 0261 d->m_editor = nullptr; 0262 JournalDelegate* that = const_cast<JournalDelegate*>(this); 0263 emit that->closeEditor(d->m_editor, NoHint); 0264 0265 } else { 0266 auto accountId = index.data(eMyMoney::Model::SplitAccountIdRole).toString(); 0267 if (accountId.isEmpty() || (accountId == MyMoneyFile::instance()->journalModel()->fakeId())) { 0268 accountId = d->m_view->accountId(); 0269 } 0270 if (!accountId.isEmpty()) { 0271 // now determine which editor to use. In case we have no transaction (yet) 0272 // we use the account type 0273 if (index.data(eMyMoney::Model::JournalTransactionIdRole).toString().isEmpty()) { 0274 const auto acc = MyMoneyFile::instance()->accountsModel()->itemById(accountId); 0275 if (acc.accountType() == eMyMoney::Account::Type::Investment) { 0276 d->m_editor = new InvestTransactionEditor(parent, accountId); 0277 } else { 0278 d->m_editor = new NewTransactionEditor(parent, accountId); 0279 } 0280 } else { 0281 if (index.data(eMyMoney::Model::TransactionIsInvestmentRole).toBool()) { 0282 // in case of an investment transaction we need to use 0283 // the parent account of the security account and pass 0284 // it to the editor. 0285 accountId = index.data(eMyMoney::Model::TransactionInvestmentAccountIdRole).toString(); 0286 d->m_editor = new InvestTransactionEditor(parent, accountId); 0287 } else { 0288 d->m_editor = new NewTransactionEditor(parent, accountId); 0289 } 0290 } 0291 d->m_editorWidthOfs = 8; 0292 if(d->m_view) { 0293 if(d->m_view->verticalScrollBar()->isVisible()) { 0294 d->m_editorWidthOfs += d->m_view->verticalScrollBar()->width(); 0295 } 0296 } 0297 0298 } else { 0299 qDebug() << "Unable to determine account for editing"; 0300 0301 d->m_editor = nullptr; 0302 JournalDelegate* that = const_cast<JournalDelegate*>(this); 0303 emit that->closeEditor(d->m_editor, NoHint); 0304 } 0305 } 0306 0307 if(d->m_editor) { 0308 d->m_editorRow = index.row(); 0309 connect(d->m_editor, &TransactionEditorBase::done, this, &JournalDelegate::endEdit); 0310 JournalDelegate* that = const_cast<JournalDelegate*>(this); 0311 emit that->sizeHintChanged(index); 0312 } 0313 0314 } else { 0315 qFatal("JournalDelegate::createEditor(): we should never end up here"); 0316 } 0317 return d->m_editor; 0318 } 0319 0320 int JournalDelegate::editorRow() const 0321 { 0322 return d->m_editorRow; 0323 } 0324 0325 void JournalDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const 0326 { 0327 QStyleOptionViewItem opt = option; 0328 initStyleOption(&opt, index); 0329 0330 // never change the background of the cell the mouse is hovering over 0331 opt.state &= ~QStyle::State_MouseOver; 0332 0333 // show the focus only on the detail column 0334 opt.state &= ~QStyle::State_HasFocus; 0335 0336 // if selected, always show as active, so that the 0337 // background does not change when the editor is shown 0338 if (opt.state & QStyle::State_Selected) { 0339 opt.state |= QStyle::State_Active; 0340 } 0341 0342 painter->save(); 0343 0344 QAbstractItemView* view = qobject_cast< QAbstractItemView* >(parent()); 0345 const bool editWidgetIsVisible = d->m_view && d->m_view->indexWidget(index); 0346 0347 // Background 0348 QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); 0349 const int margin = style->pixelMetric(QStyle::PM_FocusFrameHMargin); 0350 const int lineHeight = opt.fontMetrics.lineSpacing() + 2; 0351 0352 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); 0353 0354 QPalette::ColorGroup cg; 0355 0356 // Do not paint text if the edit widget is shown 0357 if (!editWidgetIsVisible) { 0358 bool isOverdue = false; 0359 if(view && (index.column() == JournalModel::Column::Detail)) { 0360 if(view->currentIndex().row() == index.row()) { 0361 opt.state |= QStyle::State_HasFocus; 0362 } 0363 } 0364 const QRect textArea = QRect(opt.rect.x() + margin, opt.rect.y() + margin, opt.rect.width() - 2 * margin, opt.rect.height() - 2 * margin); 0365 const bool selected = opt.state & QStyle::State_Selected; 0366 0367 QStringList lines = d->displayString(index, opt); 0368 0369 const bool erroneous = index.data(eMyMoney::Model::Roles::TransactionErroneousRole).toBool(); 0370 0371 // draw the text items 0372 if(!opt.text.isEmpty() || !lines.isEmpty()) { 0373 0374 // check if it is a scheduled transaction and display it as inactive 0375 if (MyMoneyFile::baseModel()->baseModel(index) == MyMoneyFile::instance()->schedulesJournalModel()) { 0376 opt.state &= ~QStyle::State_Enabled; 0377 isOverdue = index.data(eMyMoney::Model::ScheduleIsOverdueRole).toBool(); 0378 } 0379 cg = (opt.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; 0380 0381 if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) { 0382 cg = QPalette::Inactive; 0383 } 0384 if (selected) { 0385 // always use the normal palette since the background is also in normal 0386 painter->setPen(opt.palette.color(QPalette::ColorGroup(QPalette::Normal), QPalette::HighlightedText)); 0387 0388 } else if (erroneous) { 0389 painter->setPen(m_erroneousColor); 0390 0391 } else { 0392 painter->setPen(opt.palette.color(cg, QPalette::Text)); 0393 } 0394 0395 if (opt.state & QStyle::State_Editing) { 0396 painter->setPen(opt.palette.color(cg, QPalette::Text)); 0397 painter->drawRect(textArea.adjusted(0, 0, -1, -1)); 0398 } 0399 0400 // collect data for the various columns 0401 for(int i = 0; i < lines.count(); ++i) { 0402 painter->drawText(textArea.adjusted(0, lineHeight * i, 0, 0), opt.displayAlignment, lines[i]); 0403 } 0404 } 0405 0406 // draw the focus rect 0407 if(opt.state & QStyle::State_HasFocus) { 0408 QStyleOptionFocusRect o; 0409 o.QStyleOption::operator=(opt); 0410 o.rect = style->proxy()->subElementRect(QStyle::SE_ItemViewItemFocusRect, &opt, opt.widget); 0411 o.state |= QStyle::State_KeyboardFocusChange; 0412 o.state |= QStyle::State_Item; 0413 0414 cg = (opt.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; 0415 o.backgroundColor = opt.palette.color(cg, (opt.state & QStyle::State_Selected) 0416 ? QPalette::Highlight : QPalette::Window); 0417 style->proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, opt.widget); 0418 } 0419 0420 // draw the attention mark 0421 if((index.column() == JournalModel::Column::Detail) 0422 && (erroneous || isOverdue)) { 0423 QPixmap attention; 0424 attention.loadFromData(attentionSign, sizeof(attentionSign), 0, 0); 0425 style->proxy()->drawItemPixmap(painter, option.rect, Qt::AlignRight | Qt::AlignTop, attention); 0426 } 0427 } 0428 0429 painter->restore(); 0430 } 0431 0432 QSize JournalDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const 0433 { 0434 // get parameters only once per update to speed things up 0435 if (d->m_lineHeight == -1) { 0436 QStyleOptionViewItem opt = option; 0437 initStyleOption(&opt, index); 0438 QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); 0439 d->m_margin = style->pixelMetric(QStyle::PM_FocusFrameHMargin); 0440 d->m_lineHeight = opt.fontMetrics.lineSpacing(); 0441 } 0442 int rows = 1; 0443 0444 if(index.isValid()) { 0445 // check if we are showing the edit widget 0446 // const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(opt.widget); 0447 if (d->m_view) { 0448 QModelIndex editIndex = d->m_view->model()->index(index.row(), 0); 0449 if(editIndex.isValid()) { 0450 QWidget* editor = d->m_view->indexWidget(editIndex); 0451 if(editor) { 0452 return editor->minimumSizeHint(); 0453 } 0454 } 0455 } 0456 } 0457 0458 QSize size(10, d->m_lineHeight + 2 * d->m_margin); 0459 0460 if(option.state & QStyle::State_Selected) { 0461 rows = d->displayString(index, option).count(); 0462 0463 // make sure we show at least one row 0464 if(!rows) { 0465 rows = 1; 0466 } 0467 // leave a few pixels as margin for each space between rows 0468 size.setHeight((size.height() * rows) - (d->m_margin * (rows - 1))); 0469 } 0470 return size; 0471 } 0472 0473 void JournalDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const 0474 { 0475 Q_UNUSED(index); 0476 0477 QRect r(option.rect); 0478 // respect the vertical scrollbar if visible 0479 if (option.widget 0480 && d->m_view 0481 && d->m_view->verticalScrollBar()->isVisible() ) { 0482 r.setWidth(option.widget->width() - d->m_view->verticalScrollBar()->width()); 0483 } 0484 editor->setGeometry(r); 0485 editor->update(); 0486 } 0487 0488 void JournalDelegate::endEdit() 0489 { 0490 if(d->m_editor) { 0491 if(d->m_editor->accepted()) { 0492 emit commitData(d->m_editor); 0493 } 0494 emit closeEditor(d->m_editor, NoHint); 0495 d->m_editorRow = -1; 0496 d->m_editor = nullptr; 0497 } 0498 } 0499 0500 /** 0501 * This eventfilter seems to do nothing but it prevents that selecting a 0502 * different row with the mouse closes the editor 0503 */ 0504 bool JournalDelegate::eventFilter(QObject* o, QEvent* event) 0505 { 0506 return QAbstractItemDelegate::eventFilter(o, event); 0507 } 0508 0509 void JournalDelegate::setEditorData(QWidget* editWidget, const QModelIndex& index) const 0510 { 0511 auto* editor = qobject_cast<TransactionEditorBase*>(editWidget); 0512 if(editor) { 0513 editor->loadTransaction(index); 0514 } 0515 } 0516 0517 void JournalDelegate::setModelData(QWidget* editWidget, QAbstractItemModel* model, const QModelIndex& index) const 0518 { 0519 Q_UNUSED(model) 0520 Q_UNUSED(index) 0521 0522 auto* editor = qobject_cast<TransactionEditorBase*>(editWidget); 0523 if(editor) { 0524 // saving the transaction may move the selected transaction(s) around 0525 // we keep the transaction IDs here and take care of them when we return 0526 const auto selection = d->m_view->selectedTransactions(); 0527 0528 editor->saveTransaction(); 0529 0530 d->m_view->setSelectedTransactions(selection); 0531 } 0532 }