/*************************************************************************** * Copyright (C) 2006, 2007 by Niklas Knutsson * * nq@altern.org * * * * 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 2 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, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "categoriescomparisonchart.h" #include "budget.h" #include "account.h" #include "transaction.h" #include "recurrence.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern double monthsBetweenDates(const QDate &date1, const QDate &date2); extern QDate addMonths(const QDate &date, int nmonths); extern QDate addYears(const QDate &date, int nyears); CategoriesComparisonChart::CategoriesComparisonChart(Budget *budg, QWidget *parent) : QWidget(parent), budget(budg) { setWFlags(getWFlags() | Qt::WDestructiveClose); const KCalendarSystem *calSys = KGlobal::locale()->calendar(); QDate first_date; Transaction *trans = budget->transactions.first(); while(trans) { if(trans->fromAccount()->type() != ACCOUNT_TYPE_ASSETS || trans->toAccount()->type() != ACCOUNT_TYPE_ASSETS) { first_date = trans->date(); break; } trans = budget->transactions.next(); } to_date = QDate::currentDate(); from_date = calSys->addDays(addYears(to_date, -1), 1); if(first_date.isNull()) { from_date = to_date; } else if(from_date < first_date) { from_date = first_date; } QVBoxLayout *layout = new QVBoxLayout(this, 6, 6); KButtonBox *buttons = new KButtonBox(this); buttons->addStretch(1); saveButton = buttons->addButton(KStdGuiItem::saveAs()); printButton = buttons->addButton(KStdGuiItem::print()); buttons->layout(); layout->addWidget(buttons); canvas = NULL; view = new QCanvasView(this); layout->addWidget(view); QGroupBox *settingsGroup = new QGroupBox(1, Qt::Horizontal, i18n("Options"), this); QWidget *settingsWidget = new QWidget(settingsGroup); QVBoxLayout *settingsLayout = new QVBoxLayout(settingsWidget, 0, 6); QHBoxLayout *choicesLayout = new QHBoxLayout(settingsLayout); fromButton = new QCheckBox(i18n("From"), settingsWidget); fromButton->setChecked(true); choicesLayout->addWidget(fromButton); fromEdit = new KDateEdit(settingsWidget); fromEdit->setDate(from_date); choicesLayout->addWidget(fromEdit); choicesLayout->addWidget(new QLabel(i18n("To"), settingsWidget)); toEdit = new KDateEdit(settingsWidget); toEdit->setDate(to_date); choicesLayout->addWidget(toEdit); prevYearButton = new KPushButton(KApplication::kApplication()->iconLoader()->loadIconSet("2leftarrow", KIcon::Small, KIcon::SizeSmall), "", settingsWidget); choicesLayout->addWidget(prevYearButton); prevMonthButton = new KPushButton(KApplication::kApplication()->iconLoader()->loadIconSet("1leftarrow", KIcon::Small, KIcon::SizeSmall), "", settingsWidget); choicesLayout->addWidget(prevMonthButton); nextMonthButton = new KPushButton(KApplication::kApplication()->iconLoader()->loadIconSet("1rightarrow", KIcon::Small, KIcon::SizeSmall), "", settingsWidget); choicesLayout->addWidget(nextMonthButton); nextYearButton = new KPushButton(KApplication::kApplication()->iconLoader()->loadIconSet("2rightarrow", KIcon::Small, KIcon::SizeSmall), "", settingsWidget); choicesLayout->addWidget(nextYearButton); choicesLayout->addStretch(1); QHBoxLayout *typeLayout = new QHBoxLayout(settingsLayout); typeLayout->addWidget(new QLabel(i18n("Source:"), settingsWidget)); sourceCombo = new KComboBox(settingsWidget); sourceCombo->setEditable(false); sourceCombo->insertItem(i18n("All Expenses")); sourceCombo->insertItem(i18n("All Incomes")); sourceCombo->insertItem(i18n("All Accounts")); Account *account = budget->expensesAccounts.first(); while(account) { sourceCombo->insertItem(i18n("Expenses: %1").arg(account->name())); account = budget->expensesAccounts.next(); } account = budget->incomesAccounts.first(); while(account) { sourceCombo->insertItem(i18n("Incomes: %1").arg(account->name())); account = budget->incomesAccounts.next(); } typeLayout->addWidget(sourceCombo); typeLayout->addStretch(1); current_account = NULL; layout->addWidget(settingsGroup); connect(sourceCombo, SIGNAL(activated(int)), this, SLOT(sourceChanged(int))); connect(prevMonthButton, SIGNAL(clicked()), this, SLOT(prevMonth())); connect(nextMonthButton, SIGNAL(clicked()), this, SLOT(nextMonth())); connect(prevYearButton, SIGNAL(clicked()), this, SLOT(prevYear())); connect(nextYearButton, SIGNAL(clicked()), this, SLOT(nextYear())); connect(fromButton, SIGNAL(toggled(bool)), fromEdit, SLOT(setEnabled(bool))); connect(fromButton, SIGNAL(toggled(bool)), this, SLOT(updateDisplay())); connect(fromEdit, SIGNAL(dateChanged(const QDate&)), this, SLOT(fromChanged(const QDate&))); connect(toEdit, SIGNAL(dateChanged(const QDate&)), this, SLOT(toChanged(const QDate&))); connect(saveButton, SIGNAL(clicked()), this, SLOT(save())); connect(printButton, SIGNAL(clicked()), this, SLOT(print())); } CategoriesComparisonChart::~CategoriesComparisonChart() { } void CategoriesComparisonChart::sourceChanged(int index) { fromButton->setEnabled(index != 2); fromEdit->setEnabled(index != 2); updateDisplay(); } void CategoriesComparisonChart::saveConfig() { ((KDialogBase*) parent())->saveDialogSize(*kapp->config(), "Categories Comparison Chart"); } void CategoriesComparisonChart::toChanged(const QDate &date) { bool error = false; if(!date.isValid()) { KMessageBox::error(this, i18n("Invalid date.")); error = true; } if(!error && fromEdit->date() > date) { if(fromButton->isChecked() && fromButton->isEnabled()) { KMessageBox::error(this, i18n("To date is before from date.")); } from_date = date; fromEdit->blockSignals(true); fromEdit->setDate(from_date); fromEdit->blockSignals(false); } if(error) { toEdit->setFocus(); toEdit->blockSignals(true); toEdit->setDate(to_date); toEdit->blockSignals(false); toEdit->lineEdit()->selectAll(); return; } to_date = date; updateDisplay(); } void CategoriesComparisonChart::fromChanged(const QDate &date) { bool error = false; if(!date.isValid()) { KMessageBox::error(this, i18n("Invalid date.")); error = true; } if(!error && date > toEdit->date()) { KMessageBox::error(this, i18n("From date is after to date.")); to_date = date; toEdit->blockSignals(true); toEdit->setDate(to_date); toEdit->blockSignals(false); } if(error) { fromEdit->setFocus(); fromEdit->blockSignals(true); fromEdit->setDate(from_date); fromEdit->blockSignals(false); fromEdit->lineEdit()->selectAll(); return; } from_date = date; if(fromButton->isChecked()) updateDisplay(); } void CategoriesComparisonChart::prevMonth() { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); fromEdit->blockSignals(true); toEdit->blockSignals(true); from_date = addMonths(from_date, -1); fromEdit->setDate(from_date); if(calSys->day(to_date) == calSys->daysInMonth(to_date)) { to_date = addMonths(to_date, -1); calSys->setYMD(to_date, calSys->year(to_date), calSys->month(to_date), calSys->daysInMonth(to_date)); } else { to_date = addMonths(to_date, -1); } toEdit->setDate(to_date); fromEdit->blockSignals(false); toEdit->blockSignals(false); updateDisplay(); } void CategoriesComparisonChart::nextMonth() { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); fromEdit->blockSignals(true); toEdit->blockSignals(true); from_date = addMonths(from_date, 1); fromEdit->setDate(from_date); if(calSys->day(to_date) == calSys->daysInMonth(to_date)) { to_date = addMonths(to_date, 1); calSys->setYMD(to_date, calSys->year(to_date), calSys->month(to_date), calSys->daysInMonth(to_date)); } else { to_date = addMonths(to_date, 1); } toEdit->setDate(to_date); fromEdit->blockSignals(false); toEdit->blockSignals(false); updateDisplay(); } void CategoriesComparisonChart::prevYear() { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); fromEdit->blockSignals(true); toEdit->blockSignals(true); from_date = addYears(from_date, -1); fromEdit->setDate(from_date); if(calSys->day(to_date) == calSys->daysInMonth(to_date)) { to_date = addYears(to_date, -1); calSys->setYMD(to_date, calSys->year(to_date), calSys->month(to_date), calSys->daysInMonth(to_date)); } else { to_date = addYears(to_date, -1); } toEdit->setDate(to_date); fromEdit->blockSignals(false); toEdit->blockSignals(false); updateDisplay(); } void CategoriesComparisonChart::nextYear() { const KCalendarSystem *calSys = KGlobal::locale()->calendar(); fromEdit->blockSignals(true); toEdit->blockSignals(true); from_date = addYears(from_date, 1); fromEdit->setDate(from_date); if(calSys->day(to_date) == calSys->daysInMonth(to_date)) { to_date = addYears(to_date, 1); calSys->setYMD(to_date, calSys->year(to_date), calSys->month(to_date), calSys->daysInMonth(to_date)); } else { to_date = addYears(to_date, 1); } toEdit->setDate(to_date); fromEdit->blockSignals(false); toEdit->blockSignals(false); updateDisplay(); } void CategoriesComparisonChart::save() { if(!canvas) return; KFileDialog *dialog = new KFileDialog(QString::null, QString::null, this, NULL, true); QStringList filter; filter << "image/png"; if(QImageIO::outputFormats().contains("GIF")) { filter << "image/gif"; } if(QImageIO::outputFormats().contains("JPEG")) { filter << "image/jpeg"; } filter << "image/x-bmp"; filter << "image/x-xbm"; filter << "image/x-xpm"; filter << "image/x-portable-pixmap"; dialog->setMimeFilter(filter, "image/png"); dialog->setOperationMode(KFileDialog::Saving); dialog->setMode(KFile::File); if(dialog->exec() != QDialog::Accepted) {dialog->deleteLater(); return;} KURL url = dialog->selectedURL(); QString current_filter = dialog->currentFilter(); dialog->deleteLater(); if(url.isEmpty() && url.isValid()) return; if(url.isLocalFile()) { if(QFile::exists(url.path())) { if(KMessageBox::warningYesNo(this, i18n("The selected file already exists. Would you like to overwrite the old copy?")) != KMessageBox::Yes) return; } QFileInfo info(url.path()); if(info.isDir()) { KMessageBox::error(this, i18n("You selected a directory!")); return; } KSaveFile ofile(url.path(), 0660); if(ofile.status()) { ofile.abort(); KMessageBox::error(this, i18n("Couldn't open file for writing.")); return; } QPixmap pixmap(min_width, min_height); QPainter p(&pixmap); canvas->drawArea(QRect(0, 0, min_width, min_height), &p); if(current_filter == "image/png") {pixmap.save(ofile.file(), "PNG");} else if(current_filter == "image/x-bmp") {pixmap.save(ofile.file(), "BMP");} else if(current_filter == "image/x-xbm") {pixmap.save(ofile.file(), "XBM");} else if(current_filter == "image/x-xpm") {pixmap.save(ofile.file(), "XPM");} else if(current_filter == "image/x-portable-pixmap") {pixmap.save(ofile.file(), "PPM");} else if(current_filter == "image/gif") {pixmap.save(ofile.file(), "GIF");} else if(current_filter == "image/jpeg") {pixmap.save(ofile.file(), "JPEG");} if(ofile.status()) { ofile.abort(); KMessageBox::error(this, i18n("Error while writing file; file was not saved.")); return; } ofile.close(); if(ofile.status()) { KMessageBox::error(this, i18n("Error after saving file; data may not have been saved.")); return; } return; } KTempFile tf; tf.setAutoDelete(true); QPixmap pixmap(min_width, min_height); QPainter p(&pixmap); canvas->drawArea(QRect(0, 0, min_width, min_height), &p); if(current_filter == "image/png") {pixmap.save(tf.file(), "PNG");} else if(current_filter == "image/x-bmp") {pixmap.save(tf.file(), "BMP");} else if(current_filter == "image/x-xbm") {pixmap.save(tf.file(), "XBM");} else if(current_filter == "image/x-xpm") {pixmap.save(tf.file(), "XPM");} else if(current_filter == "image/x-portable-pixmap") {pixmap.save(tf.file(), "PPM");} else if(current_filter == "image/gif") {pixmap.save(tf.file(), "GIF");} else if(current_filter == "image/jpeg") {pixmap.save(tf.file(), "JPEG");} KIO::NetAccess::upload(tf.name(), url, this); } void CategoriesComparisonChart::print() { if(!canvas) return; KPrinter pr; if(pr.setup(this)) { QPainter p(&pr); canvas->drawArea(QRect(0, 0, min_width, min_height), &p); } } QColor getColor(int index) { switch(index) { case 0: {return Qt::red;} case 1: {return Qt::green;} case 2: {return Qt::blue;} case 3: {return Qt::cyan;} case 4: {return Qt::magenta;} case 5: {return Qt::yellow;} case 6: {return Qt::darkRed;} case 7: {return Qt::darkGreen;} case 8: {return Qt::darkBlue;} case 9: {return Qt::darkCyan;} case 10: {return Qt::darkMagenta;} case 11: {return Qt::darkYellow;} default: {return Qt::gray;} } } QBrush getBrush(int index) { QBrush brush; switch(index / 12) { case 0: {brush.setStyle(Qt::SolidPattern); break;} case 1: {brush.setStyle(Qt::HorPattern); break;} case 2: {brush.setStyle(Qt::VerPattern); break;} case 3: {brush.setStyle(Qt::Dense5Pattern); break;} case 4: {brush.setStyle(Qt::DiagCrossPattern); break;} default: {} } brush.setColor(getColor(index % 12)); return brush; } void CategoriesComparisonChart::updateDisplay() { QMap values; QMap counts; QMap desc_values; QMap desc_counts; double value_count = 0.0; double value = 0.0; current_account = NULL; AccountType type; switch(sourceCombo->currentItem()) { case 0: { type = ACCOUNT_TYPE_EXPENSES; Account *account = budget->expensesAccounts.first(); while(account) { values[account] = 0.0; counts[account] = 0.0; account = budget->expensesAccounts.next(); } break; } case 1: { type = ACCOUNT_TYPE_INCOMES; Account *account = budget->incomesAccounts.first(); while(account) { values[account] = 0.0; counts[account] = 0.0; account = budget->incomesAccounts.next(); } break; } case 2: { type = ACCOUNT_TYPE_ASSETS; AssetsAccount *account = budget->assetsAccounts.first(); while(account) { if(account != budget->balancingAccount) { if(account->accountType() == ASSETS_TYPE_SECURITIES) { values[account] = 0.0; } else { values[account] = account->initialBalance(); value += account->initialBalance(); } counts[account] = 0.0; } account = budget->assetsAccounts.next(); } break; } default: { type = ACCOUNT_TYPE_EXPENSES; int i = sourceCombo->currentItem() - 3; if(i < (int) budget->expensesAccounts.count()) current_account = budget->expensesAccounts.at(i); else current_account = budget->incomesAccounts.at(i - budget->expensesAccounts.count()); if(current_account) { type = current_account->type(); Transaction *trans = budget->transactions.first(); while(trans) { if((trans->fromAccount() == current_account || trans->toAccount() == current_account)) { desc_values[trans->description()] = 0.0; desc_counts[trans->description()] = 0.0; } trans = budget->transactions.next(); } } } } QDate first_date; bool first_date_reached = false; if(type == ACCOUNT_TYPE_ASSETS) { first_date_reached = true; } else if(fromButton->isChecked()) { first_date = from_date; } else { Transaction *trans = budget->transactions.first(); while(trans) { if(trans->fromAccount()->type() != ACCOUNT_TYPE_ASSETS || trans->toAccount()->type() != ACCOUNT_TYPE_ASSETS) { first_date = trans->date(); break; } trans = budget->transactions.next(); } if(first_date.isNull()) first_date = QDate::currentDate(); if(first_date > to_date) first_date = to_date; } Transaction *trans = budget->transactions.first(); while(trans) { if(!first_date_reached && trans->date() >= first_date) first_date_reached = true; else if(first_date_reached && trans->date() > to_date) break; if(first_date_reached) { if(current_account) { if(trans->fromAccount() == current_account) { if(type == ACCOUNT_TYPE_EXPENSES) {desc_values[trans->description()] -= trans->value(); value -= trans->value();} else {desc_values[trans->description()] += trans->value(); value += trans->value();} desc_counts[trans->description()] += trans->quantity(); value_count += trans->quantity(); } else if(trans->toAccount() == current_account) { if(type == ACCOUNT_TYPE_EXPENSES) {desc_values[trans->description()] += trans->value(); value += trans->value();} else {desc_values[trans->description()] -= trans->value(); value -= trans->value();} desc_counts[trans->description()] += trans->quantity(); value_count += trans->quantity(); } } else if(type == ACCOUNT_TYPE_ASSETS) { if(trans->fromAccount()->type() == ACCOUNT_TYPE_ASSETS && trans->fromAccount() != budget->balancingAccount) { if(((AssetsAccount*) trans->fromAccount())->accountType() != ASSETS_TYPE_SECURITIES) { values[trans->fromAccount()] -= trans->value(); value -= trans->value(); } if(trans->toAccount() != budget->balancingAccount) { counts[trans->fromAccount()] += trans->quantity(); value_count += trans->quantity(); } } if(trans->toAccount()->type() == ACCOUNT_TYPE_ASSETS && trans->toAccount() != budget->balancingAccount) { if(((AssetsAccount*) trans->toAccount())->accountType() != ASSETS_TYPE_SECURITIES) { values[trans->toAccount()] += trans->value(); value += trans->value(); } if(trans->fromAccount() != budget->balancingAccount) { counts[trans->toAccount()] += trans->quantity(); value_count += trans->quantity(); } } } else if(type == ACCOUNT_TYPE_EXPENSES) { if(trans->fromAccount()->type() == ACCOUNT_TYPE_EXPENSES) { values[trans->fromAccount()] -= trans->value(); value -= trans->value(); counts[trans->fromAccount()] += trans->quantity(); value_count += trans->quantity(); } else if(trans->toAccount()->type() == ACCOUNT_TYPE_EXPENSES) { values[trans->toAccount()] += trans->value(); value += trans->value(); counts[trans->toAccount()] += trans->quantity(); value_count += trans->quantity(); } } else { if(trans->fromAccount()->type() == ACCOUNT_TYPE_INCOMES) { values[trans->fromAccount()] += trans->value(); value += trans->value(); counts[trans->fromAccount()] += trans->quantity(); value_count += trans->quantity(); } else if(trans->toAccount()->type() == ACCOUNT_TYPE_INCOMES) { values[trans->toAccount()] -= trans->value(); value -= trans->value(); counts[trans->toAccount()] += trans->quantity(); value_count += trans->quantity(); } } } trans = budget->transactions.next(); } first_date_reached = false; ScheduledTransaction *strans = budget->scheduledTransactions.first(); while(strans) { trans = strans->transaction(); if(!first_date_reached && trans->date() >= first_date) first_date_reached = true; else if(first_date_reached && trans->date() > to_date) break; if(first_date_reached) { if(current_account) { if(trans->fromAccount() == current_account) { int count = strans->recurrence() ? strans->recurrence()->countOccurrences(first_date, to_date) : 1; if(type == ACCOUNT_TYPE_EXPENSES) {desc_values[trans->description()] -= trans->value() * count; value -= trans->value() * count;} else {desc_values[trans->description()] += trans->value() * count; value += trans->value() * count;} desc_counts[trans->description()] += count * trans->quantity(); value_count += count * trans->quantity(); } else if(trans->toAccount() == current_account) { int count = strans->recurrence() ? strans->recurrence()->countOccurrences(first_date, to_date) : 1; if(type == ACCOUNT_TYPE_EXPENSES) {desc_values[trans->description()] += trans->value() * count; value += trans->value() * count;} else {desc_values[trans->description()] -= trans->value() * count; value -= trans->value() * count;} desc_counts[trans->description()] += count * trans->quantity(); value_count += count * trans->quantity(); } } else if(type == ACCOUNT_TYPE_ASSETS) { if(trans->fromAccount()->type() == ACCOUNT_TYPE_ASSETS && trans->fromAccount() != budget->balancingAccount) { int count = strans->recurrence() ? strans->recurrence()->countOccurrences(to_date) : 1; if(trans->toAccount() != budget->balancingAccount) { counts[trans->fromAccount()] += count * trans->quantity(); value_count += count * trans->quantity(); } if(((AssetsAccount*) trans->fromAccount())->accountType() != ASSETS_TYPE_SECURITIES) { values[trans->fromAccount()] -= trans->value() * count; value -= trans->value() * count; } } if(trans->toAccount()->type() == ACCOUNT_TYPE_ASSETS && trans->toAccount() != budget->balancingAccount) { int count = strans->recurrence() ? strans->recurrence()->countOccurrences(to_date) : 1; if(trans->fromAccount() != budget->balancingAccount) { counts[trans->toAccount()] += count * trans->quantity(); value_count += count * trans->quantity(); } if(((AssetsAccount*) trans->toAccount())->accountType() != ASSETS_TYPE_SECURITIES) { values[trans->toAccount()] += trans->value() * count; value += trans->value() * count; } } } else if(type == ACCOUNT_TYPE_EXPENSES) { if(trans->fromAccount()->type() == ACCOUNT_TYPE_EXPENSES) { int count = strans->recurrence() ? strans->recurrence()->countOccurrences(first_date, to_date) : 1; counts[trans->fromAccount()] += count * trans->quantity(); values[trans->fromAccount()] -= trans->value() * count; value_count += count * trans->quantity(); value -= trans->value() * count; } else if(trans->toAccount()->type() == ACCOUNT_TYPE_EXPENSES) { int count = strans->recurrence() ? strans->recurrence()->countOccurrences(first_date, to_date) : 1; counts[trans->toAccount()] += count * trans->quantity(); values[trans->toAccount()] += trans->value() * count; value_count += count * trans->quantity(); value += trans->value() * count; } } else { if(trans->fromAccount()->type() == ACCOUNT_TYPE_INCOMES) { int count = strans->recurrence() ? strans->recurrence()->countOccurrences(first_date, to_date) : 1; counts[trans->fromAccount()] += count; values[trans->fromAccount()] += trans->value() * count; value_count += count; value += trans->value() * count; } else if(trans->toAccount()->type() == ACCOUNT_TYPE_INCOMES) { int count = strans->recurrence() ? strans->recurrence()->countOccurrences(first_date, to_date) : 1; counts[trans->toAccount()] += count; values[trans->toAccount()] -= trans->value() * count; value_count += count; value -= trans->value() * count; } } } strans = budget->scheduledTransactions.next(); } if(type == ACCOUNT_TYPE_ASSETS) { Security *security = budget->securities.first(); while(security) { double val = security->value(to_date); values[security->account()] += val; value += val; security = budget->securities.next(); } } /*int days = first_date.daysTo(to_date) + 1; double months = monthsBetweenDates(first_date, to_date), years = yearsBetweenDates(first_date, to_date);*/ QCanvas *oldcanvas = canvas; canvas = new QCanvas(this); canvas->setBackgroundColor(Qt::white); QFont legend_font = font(); QFontMetrics fm(legend_font); int fh = fm.height(); int diameter = 430; int margin = 35; int legend_x = diameter + margin * 2; Account *account = NULL; QMap::iterator it_desc = desc_values.begin(); QMap::iterator it_desc_end = desc_values.end(); int n = 0; if(current_account) { n = desc_values.count(); } else if(type == ACCOUNT_TYPE_ASSETS) { account = budget->assetsAccounts.first(); if(account == budget->balancingAccount) account = budget->assetsAccounts.next(); n = budget->assetsAccounts.count() - 1; } else if(type == ACCOUNT_TYPE_EXPENSES) { account = budget->expensesAccounts.first(); n = budget->expensesAccounts.count(); } else { account = budget->incomesAccounts.first(); n = budget->incomesAccounts.count(); } int index = 0; int text_width = 0; double value_bak = value; while((current_account && it_desc != it_desc_end) || account) { bool b_empty = false; QString legend_string; double legend_value = 0.0; if(current_account) { if(it_desc.key().isEmpty()) { legend_string = i18n("No description"); } else { legend_string = it_desc.key(); } legend_value = it_desc.data(); } else { legend_string = account->name(); legend_value = values[account]; } legend_value = (legend_value * 100) / value_bak; int deci = 0; if(legend_value < 10.0 && legend_value > -10.0) { legend_value = round(legend_value * 10.0) / 10.0; deci = 1; } else { legend_value = round(legend_value); } QCanvasText *legend_text = new QCanvasText(QString("%1 (%2\%)").arg(legend_string).arg(KGlobal::locale()->formatNumber(legend_value, deci)), legend_font, canvas); int index_bak = index; if(b_empty) index = n - 1; if(legend_text->boundingRect().width() > text_width) text_width = legend_text->boundingRect().width(); legend_text->setColor(Qt::black); legend_text->move(legend_x + 10 + fh + 5, margin + 10 + (fh + 5) * index); legend_text->show(); if(current_account) { if(it_desc.data() < 0.0) { value -= it_desc.data(); *it_desc = 0.0; } } else { if(values[account] < 0.0) { value -= values[account]; values[account] = 0.0; } } if(current_account) { ++it_desc; } else if(type == ACCOUNT_TYPE_ASSETS) { account = budget->assetsAccounts.next(); if(account == budget->balancingAccount) account = budget->assetsAccounts.next(); } else if(type == ACCOUNT_TYPE_EXPENSES) account = budget->expensesAccounts.next(); else account = budget->incomesAccounts.next(); if(b_empty) index = index_bak; else index++; } if(current_account) { } else if(type == ACCOUNT_TYPE_ASSETS) { account = budget->assetsAccounts.first(); if(account == budget->balancingAccount) account = budget->assetsAccounts.next(); } else if(type == ACCOUNT_TYPE_EXPENSES) { account = budget->expensesAccounts.first(); } else { account = budget->incomesAccounts.first(); } it_desc = desc_values.begin(); it_desc_end = desc_values.end(); index = 0; double current_value = 0.0; int prev_end = 0; while((current_account && it_desc != it_desc_end) || account) { if(current_account) current_value += it_desc.data(); else current_value += values[account]; int next_end = (int) lround((current_value * 360 * 16) / value); int length = (next_end - prev_end); QCanvasEllipse *ellipse = new QCanvasEllipse(diameter, diameter, prev_end, length, canvas); prev_end = next_end; bool b_empty = false; if(current_account && it_desc.key().isEmpty()) {b_empty = true;} int index_bak = index; if(b_empty) index = n - 1; ellipse->setBrush(getBrush(index)); ellipse->move(diameter / 2 + margin, diameter / 2 + margin); ellipse->show(); QCanvasRectangle *legend_box = new QCanvasRectangle(legend_x + 10, margin + 10 + (fh + 5) * index, fh, fh, canvas); legend_box->setPen(QPen(Qt::black)); legend_box->setBrush(getBrush(index)); legend_box->show(); if(current_account) { ++it_desc; } else if(type == ACCOUNT_TYPE_ASSETS) { account = budget->assetsAccounts.next(); if(account == budget->balancingAccount) account = budget->assetsAccounts.next(); } else if(type == ACCOUNT_TYPE_EXPENSES) account = budget->expensesAccounts.next(); else account = budget->incomesAccounts.next(); if(b_empty) index = index_bak; else index++; } QCanvasRectangle *legend_outline = new QCanvasRectangle(legend_x, margin, 10 + fh + 5 + text_width + 10, 10 + ((fh + 5) * n) + 5, canvas); legend_outline->setPen(QPen(Qt::black)); legend_outline->show(); double ratio = (double) view->visibleWidth() / (double) view->visibleHeight(); min_width = (int) ceil(legend_outline->x() + legend_outline->width() + margin); min_height = (int) ceil(legend_outline->y() + legend_outline->height() + margin); if(min_height < diameter + margin * 2) min_height = diameter + margin * 2; int height, width; if(floor(min_height * ratio) < min_width) { height = (int) floor(min_width / ratio); width = min_width; } else { height = min_height; width = (int) floor(min_height * ratio); } canvas->resize(width, height); canvas->update(); QWMatrix mx; mx.scale((double) view->visibleWidth() / (double) width, (double) view->visibleHeight() / (double) height); view->setWorldMatrix(mx); view->setCanvas(canvas); if(oldcanvas) { delete oldcanvas; } } void CategoriesComparisonChart::resizeEvent(QResizeEvent*) { if(canvas) { double ratio = (double) view->visibleWidth() / (double) view->visibleHeight(); int height, width; if(floor(min_height * ratio) < min_width) { height = (int) floor(min_width / ratio); width = min_width; } else { height = min_height; width = (int) floor(min_height * ratio); } canvas->resize(width, height); QWMatrix mx; mx.scale((double) view->visibleWidth() / (double) width, (double) view->visibleHeight() / (double) height); view->setWorldMatrix(mx); } } void CategoriesComparisonChart::updateTransactions() { updateDisplay(); } void CategoriesComparisonChart::updateAccounts() { int curindex = sourceCombo->currentItem(); if(curindex > 2) { curindex = 0; } sourceCombo->blockSignals(true); sourceCombo->clear(); sourceCombo->insertItem(i18n("All Expenses")); sourceCombo->insertItem(i18n("All Incomes")); sourceCombo->insertItem(i18n("All Accounts")); int i = 3; Account *account = budget->expensesAccounts.first(); while(account) { sourceCombo->insertItem(i18n("Expenses: %1").arg(account->name())); if(account == current_account) curindex = i; account = budget->expensesAccounts.next(); i++; } account = budget->incomesAccounts.first(); while(account) { sourceCombo->insertItem(i18n("Incomes: %1").arg(account->name())); if(account == current_account) curindex = i; account = budget->incomesAccounts.next(); i++; } if(curindex < sourceCombo->count()) sourceCombo->setCurrentItem(curindex); sourceCombo->blockSignals(false); updateDisplay(); } #include "categoriescomparisonchart.moc"