File indexing completed on 2024-05-12 16:43:52
0001 /* 0002 SPDX-FileCopyrightText: 2015 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "ledgerdelegate.h" 0007 0008 // ---------------------------------------------------------------------------- 0009 // QT Includes 0010 0011 #include <QApplication> 0012 #include <QScrollBar> 0013 #include <QPainter> 0014 #include <QDebug> 0015 #include <QDate> 0016 0017 // ---------------------------------------------------------------------------- 0018 // KDE Includes 0019 0020 #include <KLocalizedString> 0021 #include <KColorScheme> 0022 0023 // ---------------------------------------------------------------------------- 0024 // Project Includes 0025 0026 #include "ledgerview.h" 0027 #include "ledgermodel.h" 0028 #include "newtransactioneditor.h" 0029 0030 static unsigned char attentionSign[] = { 0031 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0032 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0033 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0034 0x08, 0x06, 0x00, 0x00, 0x00, 0x8D, 0x89, 0x1D, 0035 0x0D, 0x00, 0x00, 0x00, 0x04, 0x73, 0x42, 0x49, 0036 0x54, 0x08, 0x08, 0x08, 0x08, 0x7C, 0x08, 0x64, 0037 0x88, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0038 0x74, 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0039 0x65, 0x00, 0x77, 0x77, 0x77, 0x2E, 0x69, 0x6E, 0040 0x6B, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2E, 0x6F, 0041 0x72, 0x67, 0x9B, 0xEE, 0x3C, 0x1A, 0x00, 0x00, 0042 0x02, 0x05, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8D, 0043 0xAD, 0x95, 0xBF, 0x4B, 0x5B, 0x51, 0x14, 0xC7, 0044 0x3F, 0x2F, 0xBC, 0x97, 0x97, 0x97, 0x97, 0x77, 0045 0xF3, 0xF2, 0x1C, 0xA4, 0x54, 0x6B, 0x70, 0x10, 0046 0x44, 0x70, 0x2A, 0x91, 0x2E, 0x52, 0x02, 0x55, 0047 0x8A, 0xB5, 0xA3, 0xAB, 0x38, 0x08, 0x66, 0xCC, 0048 0xEE, 0xE0, 0xE2, 0x20, 0xB8, 0x38, 0xB8, 0xB8, 0049 0xF8, 0x1F, 0x38, 0x29, 0xA5, 0x29, 0x74, 0x90, 0050 0x0E, 0x0D, 0x0E, 0x22, 0x1D, 0x44, 0xA8, 0xD0, 0051 0xD4, 0xB4, 0x58, 0x4B, 0x09, 0xF9, 0xF1, 0x4A, 0052 0x3B, 0xD4, 0xD3, 0xE1, 0x55, 0xD3, 0x34, 0xAF, 0053 0x49, 0x6C, 0x3D, 0xF0, 0x85, 0x7B, 0xCF, 0xFD, 0054 0x9E, 0xEF, 0x3D, 0xE7, 0xFE, 0xD4, 0x44, 0x84, 0055 0xDB, 0xB4, 0x48, 0x2F, 0xA4, 0x94, 0xAB, 0xE5, 0056 0x52, 0xAE, 0x96, 0xEB, 0x49, 0x51, 0x44, 0x3A, 0057 0x02, 0x18, 0x88, 0xC7, 0xF1, 0xE3, 0x71, 0x7C, 0058 0x60, 0xA0, 0x1B, 0xBF, 0x6B, 0x86, 0x49, 0xC5, 0059 0x46, 0x3E, 0x47, 0x34, 0x9F, 0x23, 0x9A, 0x54, 0060 0x6C, 0xFC, 0x57, 0x86, 0x40, 0xC6, 0x4B, 0xE1, 0061 0x37, 0xCA, 0x48, 0xA3, 0x8C, 0x78, 0x29, 0x7C, 0062 0x20, 0xD3, 0x31, 0xA6, 0xD3, 0xA0, 0x52, 0x1C, 0063 0x6D, 0x6F, 0x72, 0xD9, 0x28, 0x23, 0xFE, 0x07, 0064 0x64, 0x7B, 0x93, 0x4B, 0xA5, 0x38, 0xFA, 0x27, 0065 0x41, 0x60, 0x6E, 0x74, 0x84, 0x7A, 0xE5, 0x1D, 0066 0x92, 0x54, 0x88, 0xE7, 0x22, 0xD5, 0x12, 0x32, 0067 0x3A, 0x42, 0x1D, 0x98, 0xBB, 0x91, 0x20, 0x60, 0068 0xDA, 0x36, 0x17, 0xFB, 0x7B, 0xC8, 0xC1, 0x4B, 0069 0x04, 0x02, 0xBC, 0x7E, 0x81, 0xEC, 0xEF, 0x21, 0070 0xB6, 0xCD, 0x05, 0x60, 0xF6, 0x2C, 0x68, 0x9A, 0071 0x2C, 0xCF, 0x4C, 0xE1, 0x4B, 0x05, 0x39, 0x3F, 0072 0x69, 0x0A, 0xBE, 0x7F, 0x83, 0x48, 0x05, 0x99, 0073 0x99, 0xC2, 0x37, 0x4D, 0x96, 0x7B, 0x12, 0x04, 0074 0xFA, 0x2D, 0x8B, 0xC6, 0xE9, 0x61, 0x10, 0x2C, 0075 0x15, 0xC4, 0x8A, 0x21, 0x86, 0x8E, 0xFC, 0xF8, 0076 0x12, 0xF4, 0x4F, 0x0F, 0x11, 0xCB, 0xA2, 0x01, 0077 0xF4, 0x77, 0x3D, 0x36, 0x4E, 0x82, 0xF5, 0xA5, 0078 0x05, 0x8C, 0xE1, 0x74, 0xD3, 0x37, 0x34, 0x18, 0079 0x20, 0xF2, 0x8B, 0x3D, 0x9C, 0x86, 0xA5, 0x05, 0080 0x0C, 0x27, 0xC1, 0x7A, 0xC7, 0x63, 0x03, 0x8C, 0081 0x2B, 0x07, 0xBF, 0x5A, 0x6A, 0x66, 0x27, 0x15, 0082 0x64, 0x3A, 0x8B, 0x3C, 0x7A, 0xD8, 0xEA, 0xAB, 0083 0x96, 0x10, 0xE5, 0xE0, 0x03, 0xE3, 0x7F, 0xCD, 0084 0x50, 0x39, 0x6C, 0xAD, 0xAD, 0x10, 0x53, 0xAA, 0085 0x75, 0xD2, 0xF4, 0xBD, 0x00, 0x2D, 0x5C, 0x05, 0086 0x6B, 0x2B, 0xC4, 0x94, 0xC3, 0xD6, 0xEF, 0xFE, 0087 0x6B, 0x41, 0x4D, 0xD3, 0x66, 0xFB, 0x3C, 0xC6, 0088 0x16, 0xE7, 0xDB, 0x97, 0x61, 0xE2, 0x3E, 0x3C, 0089 0xC8, 0xB4, 0x15, 0xC7, 0xE2, 0x3C, 0x91, 0x3E, 0090 0x8F, 0x31, 0x4D, 0xD3, 0x66, 0x5B, 0x4A, 0x06, 0091 0x8C, 0x84, 0xCD, 0x59, 0x61, 0xA7, 0xB5, 0xAC, 0092 0x2B, 0x9C, 0x1C, 0x04, 0x08, 0x1B, 0x2B, 0xEC, 0093 0x20, 0x09, 0x9B, 0x33, 0xC0, 0xB8, 0xDE, 0x65, 0094 0x43, 0x27, 0x9F, 0x9D, 0xA4, 0x1E, 0x16, 0xF0, 0095 0xF9, 0x6D, 0xB0, 0xC3, 0x86, 0x1E, 0xB4, 0xC3, 0096 0x38, 0xD9, 0x49, 0xEA, 0x86, 0x4E, 0xFE, 0xEA, 0097 0x29, 0xF4, 0x2C, 0x8B, 0xDA, 0x71, 0x31, 0x9C, 0098 0xFC, 0xF5, 0x23, 0x32, 0x34, 0x88, 0xDC, 0xBD, 0099 0x13, 0x5C, 0xBF, 0x30, 0xCE, 0x71, 0x11, 0xB1, 0100 0x2C, 0x6A, 0x80, 0xA7, 0xDB, 0x36, 0xAB, 0x4F, 0101 0xA6, 0x89, 0xBA, 0x49, 0x38, 0xFF, 0xD4, 0xBE, 0102 0x4E, 0x00, 0xAF, 0x9E, 0x81, 0x08, 0xD4, 0xEA, 0103 0x01, 0xFE, 0x34, 0x37, 0x09, 0x4F, 0x1F, 0x13, 0104 0xDD, 0x7D, 0xCE, 0xAA, 0x96, 0x72, 0x29, 0x7C, 0105 0xFB, 0xCE, 0x44, 0xB8, 0xD4, 0xCD, 0x2C, 0x66, 0106 0x52, 0xD4, 0x6E, 0xFB, 0x0B, 0xF8, 0x09, 0x63, 0107 0x63, 0x31, 0xE4, 0x85, 0x76, 0x2E, 0x0E, 0x00, 0108 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0109 0x42, 0x60, 0x82 0110 }; 0111 0112 QColor LedgerDelegate::m_erroneousColor = QColor(Qt::red); 0113 QColor LedgerDelegate::m_importedColor = QColor(Qt::yellow); 0114 QColor LedgerDelegate::m_separatorColor = QColor(0xff, 0xf2, 0x9b); 0115 0116 0117 class LedgerSeparatorDate : public LedgerSeparator 0118 { 0119 public: 0120 LedgerSeparatorDate(eLedgerModel::Role role); 0121 virtual ~LedgerSeparatorDate() {} 0122 0123 virtual bool rowHasSeparator(const QModelIndex& index) const override; 0124 virtual QString separatorText(const QModelIndex& index) const override; 0125 virtual void adjustBackgroundScheme(QPalette& palette, const QModelIndex& index) const override; 0126 0127 protected: 0128 QString getEntry(const QModelIndex& index, const QModelIndex& nextIndex) const; 0129 QMap<QDate, QString> m_entries; 0130 }; 0131 0132 class LedgerSeparatorOnlineBalance : public LedgerSeparatorDate 0133 { 0134 public: 0135 LedgerSeparatorOnlineBalance(eLedgerModel::Role role); 0136 virtual ~LedgerSeparatorOnlineBalance() {} 0137 0138 virtual bool rowHasSeparator(const QModelIndex& index) const override; 0139 virtual QString separatorText(const QModelIndex& index) const override; 0140 virtual void adjustBackgroundScheme(QPalette& palette, const QModelIndex& index) const override; 0141 0142 void setSeparatorData(const QDate& date, const MyMoneyMoney& amount, int fraction); 0143 0144 private: 0145 QString m_balanceAmount; 0146 }; 0147 0148 0149 0150 QDate LedgerSeparator::firstFiscalDate; 0151 bool LedgerSeparator::showFiscalDate = true; 0152 bool LedgerSeparator::showFancyDate = true; 0153 0154 0155 void LedgerSeparator::setFirstFiscalDate(int firstMonth, int firstDay) 0156 { 0157 firstFiscalDate = QDate(QDate::currentDate().year(), firstMonth, firstDay); 0158 if (QDate::currentDate() < firstFiscalDate) 0159 firstFiscalDate = firstFiscalDate.addYears(-1); 0160 } 0161 0162 QModelIndex LedgerSeparator::nextIndex(const QModelIndex& index) const 0163 { 0164 const int nextRow = index.row() + 1; 0165 if (index.isValid() && (nextRow < index.model()->rowCount(QModelIndex()))) { 0166 const QAbstractItemModel* model = index.model(); 0167 return model->index(nextRow, 0, QModelIndex()); 0168 } 0169 return QModelIndex(); 0170 } 0171 0172 LedgerSeparatorDate::LedgerSeparatorDate(eLedgerModel::Role role) 0173 : LedgerSeparator(role) 0174 { 0175 const QDate today = QDate::currentDate(); 0176 const QDate thisMonth(today.year(), today.month(), 1); 0177 const QDate lastMonth = thisMonth.addMonths(-1); 0178 const QDate yesterday = today.addDays(-1); 0179 // a = QDate::dayOfWeek() todays weekday (1 = Monday, 7 = Sunday) 0180 // b = QLocale().firstDayOfWeek() first day of week (1 = Monday, 7 = Sunday) 0181 int weekStartOfs = today.dayOfWeek() - QLocale().firstDayOfWeek(); 0182 if (weekStartOfs < 0) { 0183 weekStartOfs = 7 + weekStartOfs; 0184 } 0185 const QDate thisWeek = today.addDays(-weekStartOfs); 0186 const QDate lastWeek = thisWeek.addDays(-7); 0187 const QDate thisYear(today.year(), 1, 1); 0188 0189 m_entries[thisYear] = i18n("This year"); 0190 m_entries[lastMonth] = i18n("Last month"); 0191 m_entries[thisMonth] = i18n("This month"); 0192 m_entries[lastWeek] = i18n("Last week"); 0193 m_entries[thisWeek] = i18n("This week"); 0194 m_entries[yesterday] = i18n("Yesterday"); 0195 m_entries[today] = i18n("Today"); 0196 m_entries[today.addDays(1)] = i18n("Future transactions"); 0197 m_entries[thisWeek.addDays(7)] = i18n("Next week"); 0198 m_entries[thisMonth.addMonths(1)] = i18n("Next month"); 0199 if (showFiscalDate && firstFiscalDate.isValid()) { 0200 m_entries[firstFiscalDate] = i18n("Current fiscal year"); 0201 m_entries[firstFiscalDate.addYears(-1)] = i18n("Previous fiscal year"); 0202 m_entries[firstFiscalDate.addYears(1)] = i18n("Next fiscal year"); 0203 } 0204 } 0205 0206 0207 QString LedgerSeparatorDate::getEntry(const QModelIndex& index, const QModelIndex& nextIndex) const 0208 { 0209 Q_ASSERT(index.isValid()); 0210 Q_ASSERT(nextIndex.isValid()); 0211 Q_ASSERT(index.model() == nextIndex.model()); 0212 0213 const QAbstractItemModel* model = index.model(); 0214 QString rc; 0215 if(!m_entries.isEmpty()) { 0216 if (model->data(index, (int)m_role).toDate() != model->data(nextIndex, (int)m_role).toDate()) { 0217 const QDate key = model->data(index, (int)m_role).toDate(); 0218 const QDate endKey = model->data(nextIndex, (int)m_role).toDate(); 0219 QMap<QDate, QString>::const_iterator it = m_entries.upperBound(key); 0220 while((it != m_entries.cend()) && (it.key() <= endKey)) { 0221 rc = *it; 0222 ++it; 0223 } 0224 } 0225 } 0226 return rc; 0227 } 0228 0229 bool LedgerSeparatorDate::rowHasSeparator(const QModelIndex& index) const 0230 { 0231 bool rc = false; 0232 if(!m_entries.isEmpty()) { 0233 QModelIndex nextIdx = nextIndex(index); 0234 if(nextIdx.isValid() ) { 0235 const QString id = nextIdx.model()->data(nextIdx, (int)eLedgerModel::Role::TransactionSplitId).toString(); 0236 // For a new transaction the id is completely empty, for a split view the transaction 0237 // part is filled but the split id is empty and the string ends with a dash 0238 // and we never draw a separator in front of that row 0239 if(!id.isEmpty() && !id.endsWith('-')) { 0240 rc = !getEntry(index, nextIdx).isEmpty(); 0241 } 0242 } 0243 } 0244 return rc; 0245 } 0246 0247 QString LedgerSeparatorDate::separatorText(const QModelIndex& index) const 0248 { 0249 QModelIndex nextIdx = nextIndex(index); 0250 if(nextIdx.isValid()) { 0251 return getEntry(index, nextIdx); 0252 } 0253 return QString(); 0254 } 0255 0256 void LedgerSeparatorDate::adjustBackgroundScheme(QPalette& palette, const QModelIndex& index) const 0257 { 0258 Q_UNUSED(index); 0259 KColorScheme::adjustBackground(palette, KColorScheme::ActiveBackground, QPalette::Base, KColorScheme::Button, KSharedConfigPtr()); 0260 } 0261 0262 0263 0264 LedgerSeparatorOnlineBalance::LedgerSeparatorOnlineBalance(eLedgerModel::Role role) 0265 : LedgerSeparatorDate(role) 0266 { 0267 // we don't need the standard values 0268 m_entries.clear(); 0269 } 0270 0271 void LedgerSeparatorOnlineBalance::setSeparatorData(const QDate& date, const MyMoneyMoney& amount, int fraction) 0272 { 0273 m_entries.clear(); 0274 if (date.isValid()) { 0275 m_balanceAmount = amount.formatMoney(fraction); 0276 m_entries[date] = i18n("Online statement balance: %1", m_balanceAmount); 0277 } 0278 } 0279 0280 bool LedgerSeparatorOnlineBalance::rowHasSeparator(const QModelIndex& index) const 0281 { 0282 bool rc = false; 0283 if(!m_entries.isEmpty()) { 0284 QModelIndex nextIdx = nextIndex(index); 0285 const QAbstractItemModel* model = index.model(); 0286 const QDate date = model->data(index, (int)m_role).toDate(); 0287 // only a real transaction can have an online balance separator 0288 if(model->data(index, (int) eLedgerModel::Role::ScheduleId).toString().isEmpty()) { 0289 // if this is not the last entry and not a schedule? 0290 if(nextIdx.isValid()) { 0291 // index points to the last entry of a date 0292 rc = (date != model->data(nextIdx, (int)m_role).toDate()); 0293 if (!rc) { 0294 // in case it's the same date, we need to check if this is the last real transaction 0295 // and the next one is a scheduled transaction 0296 if(!model->data(nextIdx, (int) eLedgerModel::Role::ScheduleId).toString().isEmpty() ) { 0297 rc = (date <= m_entries.firstKey()); 0298 } 0299 } else { 0300 // check if this the spot for the online balance data 0301 rc &= ((date <= m_entries.firstKey()) 0302 && (model->data(nextIdx, (int)m_role).toDate() > m_entries.firstKey())); 0303 } 0304 } else { 0305 rc = (date <= m_entries.firstKey()); 0306 } 0307 } 0308 } 0309 return rc; 0310 } 0311 0312 QString LedgerSeparatorOnlineBalance::separatorText(const QModelIndex& index) const 0313 { 0314 if(rowHasSeparator(index)) { 0315 return m_entries.first(); 0316 } 0317 return QString(); 0318 } 0319 0320 void LedgerSeparatorOnlineBalance::adjustBackgroundScheme(QPalette& palette, const QModelIndex& index) const 0321 { 0322 const QAbstractItemModel* model = index.model(); 0323 QModelIndex amountIndex = model->index(index.row(), (int)eLedgerModel::Column::Balance); 0324 QString amount = model->data(amountIndex).toString(); 0325 KColorScheme::BackgroundRole role = KColorScheme::PositiveBackground; 0326 0327 if (!m_entries.isEmpty()) { 0328 if(amount != m_balanceAmount) { 0329 role = KColorScheme::NegativeBackground; 0330 } 0331 } 0332 KColorScheme::adjustBackground(palette, role, QPalette::Base, KColorScheme::Button, KSharedConfigPtr()); 0333 } 0334 0335 0336 0337 0338 class LedgerDelegate::Private 0339 { 0340 public: 0341 Private() 0342 : m_editor(0) 0343 , m_view(0) 0344 , m_editorRow(-1) 0345 , m_separator(0) 0346 , m_onlineBalanceSeparator(0) 0347 {} 0348 0349 ~Private() 0350 { 0351 delete m_separator; 0352 } 0353 0354 inline bool displaySeparator(const QModelIndex& index) const 0355 { 0356 return m_separator && m_separator->rowHasSeparator(index); 0357 } 0358 0359 inline bool displayOnlineBalanceSeparator(const QModelIndex& index) const 0360 { 0361 return m_onlineBalanceSeparator && m_onlineBalanceSeparator->rowHasSeparator(index); 0362 } 0363 0364 NewTransactionEditor* m_editor; 0365 LedgerView* m_view; 0366 int m_editorRow; 0367 LedgerSeparator* m_separator; 0368 LedgerSeparatorOnlineBalance* m_onlineBalanceSeparator; 0369 }; 0370 0371 0372 LedgerDelegate::LedgerDelegate(LedgerView* parent) 0373 : QStyledItemDelegate(parent) 0374 , d(new Private) 0375 { 0376 d->m_view = parent; 0377 } 0378 0379 LedgerDelegate::~LedgerDelegate() 0380 { 0381 delete d; 0382 } 0383 0384 void LedgerDelegate::setSortRole(eLedgerModel::Role role) 0385 { 0386 delete d->m_separator; 0387 delete d->m_onlineBalanceSeparator; 0388 d->m_separator = 0; 0389 d->m_onlineBalanceSeparator = 0; 0390 0391 switch(role) { 0392 case eLedgerModel::Role::PostDate: 0393 d->m_separator = new LedgerSeparatorDate(role); 0394 d->m_onlineBalanceSeparator = new LedgerSeparatorOnlineBalance(role); 0395 break; 0396 default: 0397 qDebug() << "LedgerDelegate::setSortRole role" << (int)role << "not implemented"; 0398 break; 0399 } 0400 } 0401 0402 void LedgerDelegate::setErroneousColor(const QColor& color) 0403 { 0404 m_erroneousColor = color; 0405 } 0406 0407 void LedgerDelegate::setOnlineBalance(const QDate& date, const MyMoneyMoney& amount, int fraction) 0408 { 0409 if(d->m_onlineBalanceSeparator) { 0410 d->m_onlineBalanceSeparator->setSeparatorData(date, amount, fraction); 0411 } 0412 } 0413 0414 QWidget* LedgerDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const 0415 { 0416 Q_UNUSED(option); 0417 0418 if(index.isValid()) { 0419 if(d->m_view->selectionModel()->selectedRows().count() > 1) { 0420 qDebug() << "Editing multiple transactions at once is not yet supported"; 0421 0422 /** 0423 * @todo replace the following three lines with the creation of a special 0424 * editor that can handle multiple transactions at once 0425 */ 0426 d->m_editor = 0; 0427 LedgerDelegate* that = const_cast<LedgerDelegate*>(this); 0428 emit that->closeEditor(d->m_editor, NoHint); 0429 0430 } else { 0431 d->m_editor = new NewTransactionEditor(parent, d->m_view->accountId()); 0432 } 0433 0434 if(d->m_editor) { 0435 d->m_editorRow = index.row(); 0436 connect(d->m_editor, SIGNAL(done()), this, SLOT(endEdit())); 0437 emit sizeHintChanged(index); 0438 } 0439 0440 } else { 0441 qFatal("LedgerDelegate::createEditor(): we should never end up here"); 0442 } 0443 return d->m_editor; 0444 } 0445 0446 int LedgerDelegate::editorRow() const 0447 { 0448 return d->m_editorRow; 0449 } 0450 0451 void LedgerDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const 0452 { 0453 QStyleOptionViewItem opt = option; 0454 initStyleOption(&opt, index); 0455 0456 // never change the background of the cell the mouse is hovering over 0457 opt.state &= ~QStyle::State_MouseOver; 0458 0459 // show the focus only on the detail column 0460 opt.state &= ~QStyle::State_HasFocus; 0461 0462 // if selected, always show as active, so that the 0463 // background does not change when the editor is shown 0464 if (opt.state & QStyle::State_Selected) { 0465 opt.state |= QStyle::State_Active; 0466 } 0467 0468 painter->save(); 0469 0470 QAbstractItemView* view = qobject_cast< QAbstractItemView* >(parent()); 0471 const bool editWidgetIsVisible = d->m_view && d->m_view->indexWidget(index); 0472 const bool rowHasSeparator = d->displaySeparator(index); 0473 const bool rowHasOnlineBalance = d->displayOnlineBalanceSeparator(index); 0474 0475 // Background 0476 QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); 0477 const int margin = style->pixelMetric(QStyle::PM_FocusFrameHMargin); 0478 const int lineHeight = opt.fontMetrics.lineSpacing() + 2; 0479 0480 if (rowHasSeparator) { 0481 // don't draw over the separator space 0482 opt.rect.setHeight(opt.rect.height() - lineHeight ); 0483 } 0484 if (rowHasOnlineBalance) { 0485 // don't draw over the online balance space 0486 opt.rect.setHeight(opt.rect.height() - lineHeight ); 0487 } 0488 0489 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); 0490 0491 QPalette::ColorGroup cg; 0492 0493 // Do not paint text if the edit widget is shown 0494 if (!editWidgetIsVisible) { 0495 if(view && (index.column() == (int)eLedgerModel::Column::Detail)) { 0496 if(view->currentIndex().row() == index.row()) { 0497 opt.state |= QStyle::State_HasFocus; 0498 } 0499 } 0500 const QRect textArea = QRect(opt.rect.x() + margin, opt.rect.y() + margin, opt.rect.width() - 2 * margin, opt.rect.height() - 2 * margin); 0501 const bool selected = opt.state & QStyle::State_Selected; 0502 0503 QStringList lines; 0504 if(index.column() == (int)eLedgerModel::Column::Detail) { 0505 lines << index.model()->data(index, (int)eLedgerModel::Role::PayeeName).toString(); 0506 if(selected) { 0507 lines << index.model()->data(index, (int)eLedgerModel::Role::CounterAccount).toString(); 0508 lines << index.model()->data(index, (int)eLedgerModel::Role::SingleLineMemo).toString(); 0509 0510 } else { 0511 if(lines.at(0).isEmpty()) { 0512 lines.clear(); 0513 lines << index.model()->data(index, (int)eLedgerModel::Role::SingleLineMemo).toString(); 0514 } 0515 if(lines.at(0).isEmpty()) { 0516 lines << index.model()->data(index, (int)eLedgerModel::Role::CounterAccount).toString(); 0517 } 0518 } 0519 lines.removeAll(QString()); 0520 } 0521 0522 const bool erroneous = index.model()->data(index, (int)eLedgerModel::Role::Erroneous).toBool(); 0523 0524 // draw the text items 0525 if(!opt.text.isEmpty() || !lines.isEmpty()) { 0526 0527 // check if it is a scheduled transaction and display it as inactive 0528 if(!index.model()->data(index, (int)eLedgerModel::Role::ScheduleId).toString().isEmpty()) { 0529 opt.state &= ~QStyle::State_Enabled; 0530 } 0531 0532 cg = (opt.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; 0533 0534 if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) { 0535 cg = QPalette::Inactive; 0536 } 0537 if (selected) { 0538 // always use the normal palette since the background is also in normal 0539 painter->setPen(opt.palette.color(QPalette::ColorGroup(QPalette::Normal), QPalette::HighlightedText)); 0540 0541 } else if (erroneous) { 0542 painter->setPen(m_erroneousColor); 0543 0544 } else { 0545 painter->setPen(opt.palette.color(cg, QPalette::Text)); 0546 } 0547 0548 if (opt.state & QStyle::State_Editing) { 0549 painter->setPen(opt.palette.color(cg, QPalette::Text)); 0550 painter->drawRect(textArea.adjusted(0, 0, -1, -1)); 0551 } 0552 0553 // collect data for the various columns 0554 if(index.column() == (int)eLedgerModel::Column::Detail) { 0555 for(int i = 0; i < lines.count(); ++i) { 0556 painter->drawText(textArea.adjusted(0, lineHeight * i, 0, 0), opt.displayAlignment, lines[i]); 0557 } 0558 0559 } else { 0560 painter->drawText(textArea, opt.displayAlignment, opt.text); 0561 } 0562 } 0563 0564 // draw the focus rect 0565 if(opt.state & QStyle::State_HasFocus) { 0566 QStyleOptionFocusRect o; 0567 o.QStyleOption::operator=(opt); 0568 o.rect = style->proxy()->subElementRect(QStyle::SE_ItemViewItemFocusRect, &opt, opt.widget); 0569 o.state |= QStyle::State_KeyboardFocusChange; 0570 o.state |= QStyle::State_Item; 0571 0572 cg = (opt.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; 0573 o.backgroundColor = opt.palette.color(cg, (opt.state & QStyle::State_Selected) 0574 ? QPalette::Highlight : QPalette::Window); 0575 style->proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, opt.widget); 0576 } 0577 0578 // draw the attention mark 0579 if((index.column() == (int)eLedgerModel::Column::Detail) 0580 && erroneous) { 0581 QPixmap attention; 0582 attention.loadFromData(attentionSign, sizeof(attentionSign), 0, 0); 0583 style->proxy()->drawItemPixmap(painter, option.rect, Qt::AlignRight | Qt::AlignTop, attention); 0584 } 0585 } 0586 0587 // draw a separator if any 0588 if (rowHasOnlineBalance) { 0589 opt.rect.setY(opt.rect.y() + opt.rect.height()); 0590 opt.rect.setHeight(lineHeight); 0591 d->m_onlineBalanceSeparator->adjustBackgroundScheme(opt.palette, index); 0592 opt.backgroundBrush = opt.palette.base(); 0593 0594 // never draw it as selected but always enabled 0595 opt.state &= ~QStyle::State_Selected; 0596 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); 0597 0598 // when the editor is shown, the row has only a single column 0599 // so we need to paint the separator if we get here in this casee 0600 bool needPaint = editWidgetIsVisible; 0601 0602 if(!needPaint && (index.column() == (int)eLedgerModel::Column::Detail)) { 0603 needPaint = true; 0604 // adjust the rect to cover all columns 0605 if(view && view->viewport()) { 0606 opt.rect.setX(0); 0607 opt.rect.setWidth(view->viewport()->width()); 0608 } 0609 } 0610 0611 if(needPaint) { 0612 painter->setPen(opt.palette.color(QPalette::Normal, QPalette::Text)); 0613 painter->drawText(opt.rect, Qt::AlignCenter, d->m_onlineBalanceSeparator->separatorText(index)); 0614 } 0615 } 0616 0617 if (rowHasSeparator) { 0618 opt.rect.setY(opt.rect.y() + opt.rect.height()); 0619 opt.rect.setHeight(lineHeight); 0620 d->m_separator->adjustBackgroundScheme(opt.palette, index); 0621 opt.backgroundBrush = opt.palette.base(); 0622 0623 // never draw it as selected but always enabled 0624 opt.state &= ~QStyle::State_Selected; 0625 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); 0626 0627 // when the editor is shown, the row has only a single column 0628 // so we need to paint the separator if we get here in this casee 0629 bool needPaint = editWidgetIsVisible; 0630 0631 if(!needPaint && (index.column() == (int)eLedgerModel::Column::Detail)) { 0632 needPaint = true; 0633 // adjust the rect to cover all columns 0634 if(view && view->viewport()) { 0635 opt.rect.setX(0); 0636 opt.rect.setWidth(view->viewport()->width()); 0637 } 0638 } 0639 0640 if(needPaint) { 0641 painter->setPen(opt.palette.foreground().color()); 0642 painter->drawText(opt.rect, Qt::AlignCenter, d->m_separator->separatorText(index)); 0643 } 0644 } 0645 painter->restore(); 0646 } 0647 0648 QSize LedgerDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const 0649 { 0650 bool fullDisplay = false; 0651 if(d->m_view) { 0652 QModelIndex currentIndex = d->m_view->currentIndex(); 0653 if(currentIndex.isValid()) { 0654 QString currentId = currentIndex.model()->data(currentIndex, (int)eLedgerModel::Role::TransactionSplitId).toString(); 0655 QString myId = index.model()->data(index, (int)eLedgerModel::Role::TransactionSplitId).toString(); 0656 fullDisplay = (currentId == myId); 0657 } 0658 } 0659 0660 QSize size; 0661 QStyleOptionViewItem opt = option; 0662 int rows = 1; 0663 initStyleOption(&opt, index); 0664 0665 QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); 0666 const int margin = style->pixelMetric(QStyle::PM_FocusFrameHMargin); 0667 const int lineHeight = opt.fontMetrics.lineSpacing(); 0668 0669 if(index.isValid()) { 0670 // check if we are showing the edit widget 0671 // const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(opt.widget); 0672 if (d->m_view) { 0673 QModelIndex editIndex = d->m_view->model()->index(index.row(), 0); 0674 if(editIndex.isValid()) { 0675 QWidget* editor = d->m_view->indexWidget(editIndex); 0676 if(editor) { 0677 size = editor->minimumSizeHint(); 0678 if(d->displaySeparator(index)) { 0679 // don't draw over the separator space 0680 size += QSize(0, lineHeight + margin); 0681 } 0682 if(d->displayOnlineBalanceSeparator(index)) { 0683 // don't draw over the separator space 0684 size += QSize(0, lineHeight + margin); 0685 } 0686 return size; 0687 } 0688 } 0689 } 0690 } 0691 0692 size = QSize(100, lineHeight + 2*margin); 0693 0694 if(fullDisplay) { 0695 auto payee = index.data((int)eLedgerModel::Role::PayeeName).toString(); 0696 auto counterAccount = index.data((int)eLedgerModel::Role::CounterAccount).toString(); 0697 auto memo = index.data((int)eLedgerModel::Role::SingleLineMemo).toString(); 0698 0699 rows = (payee.length() > 0 ? 1 : 0) + (counterAccount.length() > 0 ? 1 : 0) + (memo.length() > 0 ? 1 : 0); 0700 // make sure we show at least one row 0701 if(!rows) { 0702 rows = 1; 0703 } 0704 // leave a few pixels as margin for each space between rows 0705 size.setHeight((size.height() * rows) - (margin * (rows - 1))); 0706 0707 } 0708 0709 if (d->m_separator && d->m_separator->rowHasSeparator(index)) { 0710 size.setHeight(size.height() + lineHeight + margin); 0711 } 0712 if (d->m_onlineBalanceSeparator && d->m_onlineBalanceSeparator->rowHasSeparator(index)) { 0713 size.setHeight(size.height() + lineHeight + margin); 0714 } 0715 return size; 0716 } 0717 0718 void LedgerDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const 0719 { 0720 Q_UNUSED(index); 0721 0722 QStyle *style = option.widget ? option.widget->style() : QApplication::style(); 0723 const int margin = style->pixelMetric(QStyle::PM_FocusFrameHMargin); 0724 const int lineHeight = option.fontMetrics.lineSpacing(); 0725 0726 int ofs = 8; 0727 if(d->m_view) { 0728 if(d->m_view->verticalScrollBar()->isVisible()) { 0729 ofs += d->m_view->verticalScrollBar()->width(); 0730 } 0731 } 0732 0733 QRect r(option.rect); 0734 if (option.widget) 0735 r.setWidth(option.widget->width() - ofs); 0736 0737 if(d->displaySeparator(index)) { 0738 // consider the separator space 0739 r.setHeight(r.height() - lineHeight - margin); 0740 } 0741 if(d->displayOnlineBalanceSeparator(index)) { 0742 // consider the separator space 0743 r.setHeight(r.height() - lineHeight - margin); 0744 } 0745 editor->setGeometry(r); 0746 editor->update(); 0747 } 0748 0749 void LedgerDelegate::endEdit() 0750 { 0751 if(d->m_editor) { 0752 if(d->m_editor->accepted()) { 0753 emit commitData(d->m_editor); 0754 } 0755 emit closeEditor(d->m_editor, NoHint); 0756 d->m_editorRow = -1; 0757 } 0758 } 0759 0760 /** 0761 * This eventfilter seems to do nothing but it prevents that selecting a 0762 * different row with the mouse closes the editor 0763 */ 0764 bool LedgerDelegate::eventFilter(QObject* o, QEvent* event) 0765 { 0766 return QAbstractItemDelegate::eventFilter(o, event); 0767 } 0768 0769 void LedgerDelegate::setEditorData(QWidget* editWidget, const QModelIndex& index) const 0770 { 0771 NewTransactionEditor* editor = qobject_cast<NewTransactionEditor*>(editWidget); 0772 if(editor) { 0773 editor->loadTransaction(index.model()->data(index, (int)eLedgerModel::Role::TransactionSplitId).toString()); 0774 } 0775 } 0776 0777 void LedgerDelegate::setModelData(QWidget* editWidget, QAbstractItemModel* model, const QModelIndex& index) const 0778 { 0779 Q_UNUSED(model) 0780 Q_UNUSED(index) 0781 0782 NewTransactionEditor* editor = qobject_cast<NewTransactionEditor*>(editWidget); 0783 if(editor) { 0784 editor->saveTransaction(); 0785 } 0786 }