diff --git a/src/widgets/taskswidget.cpp b/src/widgets/taskswidget.cpp index 848b04a..de2966d 100644 --- a/src/widgets/taskswidget.cpp +++ b/src/widgets/taskswidget.cpp @@ -1,389 +1,389 @@ /* * Copyright (c) 2019 Alexander Potashev * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 "taskswidget.h" #include #include #include #include #include #include #include #include #include #include "ktimetracker.h" #include "ktt_debug.h" #include "model/task.h" #include "model/tasksmodel.h" bool readBoolEntry(const QString& key) { return KSharedConfig::openConfig()->group(QString()).readEntry(key, true); } void writeEntry(const QString& key, bool value) { KConfigGroup config = KSharedConfig::openConfig()->group(QString()); config.writeEntry(key, value); config.sync(); } //BEGIN ProgressColumnDelegate (custom painting of the progress column) class ProgressColumnDelegate : public QStyledItemDelegate { public: explicit ProgressColumnDelegate(QObject *parent) : QStyledItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if (index.column () == 6) { QApplication::style()->drawControl( QStyle::CE_ItemViewItem, &option, painter ); int rX = option.rect.x() + 2; int rY = option.rect.y() + 2; int rWidth = option.rect.width() - 4; int rHeight = option.rect.height() - 4; int value = index.model()->data( index ).toInt(); int newWidth = (int)(rWidth * (value / 100.)); if(QApplication::isLeftToRight()) { int mid = rY + rHeight / 2; int width = rWidth / 2; QLinearGradient gradient1( rX, mid, rX + width, mid); gradient1.setColorAt( 0, Qt::red ); gradient1.setColorAt( 1, Qt::yellow ); painter->fillRect( rX, rY, (newWidth < width) ? newWidth : width, rHeight, gradient1 ); if (newWidth > width) { QLinearGradient gradient2( rX + width, mid, rX + 2 * width, mid); gradient2.setColorAt( 0, Qt::yellow ); gradient2.setColorAt( 1, Qt::green ); painter->fillRect( rX + width, rY, newWidth - width, rHeight, gradient2 ); } painter->setPen( option.state & QStyle::State_Selected ? option.palette.highlight().color() : option.palette.window().color() ); for (int x = rHeight; x < newWidth; x += rHeight) { painter->drawLine( rX + x, rY, rX + x, rY + rHeight - 1 ); } } else { int mid = option.rect.height() - rHeight / 2; int width = rWidth / 2; QLinearGradient gradient1( rX, mid, rX + width, mid); gradient1.setColorAt( 0, Qt::red ); gradient1.setColorAt( 1, Qt::yellow ); painter->fillRect( option.rect.height(), rY, (newWidth < width) ? newWidth : width, rHeight, gradient1 ); if (newWidth > width) { QLinearGradient gradient2( rX + width, mid, rX + 2 * width, mid); gradient2.setColorAt( 0, Qt::yellow ); gradient2.setColorAt( 1, Qt::green ); painter->fillRect( rX + width, rY, newWidth - width, rHeight, gradient2 ); } painter->setPen( option.state & QStyle::State_Selected ? option.palette.highlight().color() : option.palette.window().color() ); for (int x = rWidth- rHeight; x > newWidth; x -= rHeight) { painter->drawLine( rWidth - x, rY, rWidth - x, rY + rHeight - 1 ); } } painter->setPen( Qt::black ); painter->drawText( option.rect, Qt::AlignCenter | Qt::AlignVCenter, QString::number(value) + " %" ); } else { QStyledItemDelegate::paint( painter, option, index ); } } }; //END TasksWidget::TasksWidget(QWidget *parent, QSortFilterProxyModel *filterProxyModel, TasksModel *tasksModel) : QTreeView(parent) , m_filterProxyModel(filterProxyModel) , m_tasksModel(tasksModel) , m_popupPercentageMenu(nullptr) , m_popupPriorityMenu(nullptr) { setModel(filterProxyModel); connect(this, &QTreeView::expanded, this, &TasksWidget::itemStateChanged); connect(this, &QTreeView::collapsed, this, &TasksWidget::itemStateChanged); setWindowFlags(windowFlags() | Qt::WindowContextHelpButtonHint); setAllColumnsShowFocus(true); setSortingEnabled(true); setAlternatingRowColors(true); setDragDropMode(QAbstractItemView::InternalMove); setItemDelegateForColumn(6, new ProgressColumnDelegate(this)); // Context menu for task progress percentage m_popupPercentageMenu = new QMenu(this); for (int i = 0; i <= 100; i += 10) { QString label = i18nc("@item:inmenu Task progress", "%1 %", i); m_percentage[m_popupPercentageMenu->addAction(label)] = i; } connect(m_popupPercentageMenu, &QMenu::triggered, this, &TasksWidget::slotSetPercentage); // Context menu for task priority m_popupPriorityMenu = new QMenu(this); for (int i = 0; i <= 9; ++i) { QString label; switch (i) { case 0: label = i18nc("@item:inmenu Task priority", "unspecified"); break; case 1: label = i18nc("@item:inmenu Task priority", "1 (highest)"); break; case 5: label = i18nc("@item:inmenu Task priority", "5 (medium)"); break; case 9: label = i18nc("@item:inmenu Task priority", "9 (lowest)"); break; default: label = QString("%1").arg(i); break; } m_priority[m_popupPriorityMenu->addAction(label)] = i; } connect(m_popupPriorityMenu, &QMenu::triggered, this, &TasksWidget::slotSetPriority); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &TasksWidget::customContextMenuRequested, this, &TasksWidget::slotCustomContextMenuRequested); sortByColumn(0, Qt::AscendingOrder); } void TasksWidget::itemStateChanged(const QModelIndex &index) { Task *task = taskAtViewIndex(index); if (!task) { return; } qCDebug(KTT_LOG) <<"TaskView::itemStateChanged()" <<" uid=" << task->uid() <<" state=" << isExpanded(index); writeEntry(task->uid(), isExpanded(index)); } void TasksWidget::slotCustomContextMenuRequested(const QPoint& pos) { QPoint newPos = viewport()->mapToGlobal(pos); int column = columnAt(pos.x()); switch (column) { case 6: /* percentage */ m_popupPercentageMenu->popup(newPos); break; case 5: /* priority */ m_popupPriorityMenu->popup(newPos); break; default: emit contextMenuRequested(newPos); break; } } void TasksWidget::slotSetPercentage(QAction* action) { if (currentItem()) { currentItem()->setPercentComplete(m_percentage[action]); emit updateButtons(); } } void TasksWidget::slotSetPriority(QAction* action) { if (currentItem()) { currentItem()->setPriority(m_priority[action]); } } void TasksWidget::mouseMoveEvent(QMouseEvent *event) { QModelIndex index = indexAt(event->pos()); if (index.isValid() && index.column() == 6 && event->buttons() == Qt::LeftButton) { int newValue = (int)((event->pos().x() - visualRect(index).x()) / (double)(visualRect(index).width()) * 101); if (newValue > 100) { newValue = 100; } if ( event->modifiers() & Qt::ShiftModifier ) { int delta = newValue % 10; if ( delta >= 5 ) { newValue += (10 - delta); } else { newValue -= delta; } } if (selectionModel()->isSelected(index)) { Task *task = taskAtViewIndex(index); if (task) { task->setPercentComplete(newValue); emit updateButtons(); } } } else { QTreeView::mouseMoveEvent(event); } } bool TasksWidget::mousePositionInsideCheckbox(QMouseEvent *event) const { QModelIndex index = indexAt(event->pos()); return index.isValid() && index.column() == 0 && visualRect(index).x() <= event->pos().x() && event->pos().x() < visualRect(index).x() + 19; } void TasksWidget::mousePressEvent(QMouseEvent *event) { qCDebug(KTT_LOG) << "Entering function, event->button()=" << event->button(); - if (mousePositionInsideCheckbox(event)) { + if (mousePositionInsideCheckbox(event) && event->buttons() == Qt::LeftButton) { // if the user toggles a task as complete/incomplete QModelIndex index = indexAt(event->pos()); Task *task = taskAtViewIndex(index); if (task) { if (task->isComplete()) { task->setPercentComplete(0); } else { task->setPercentComplete(100); } emit updateButtons(); } } else { // the user did not mark a task as complete/incomplete if (KTimeTrackerSettings::configPDA()) { // if you have a touchscreen, you cannot right-click. So, display context menu on any click. QPoint newPos = viewport()->mapToGlobal(event->pos()); emit contextMenuRequested(newPos); } QTreeView::mousePressEvent(event); } } void TasksWidget::mouseDoubleClickEvent(QMouseEvent *event) { qCDebug(KTT_LOG) << "Entering function, event->button()=" << event->button(); QModelIndex index = indexAt(event->pos()); // if the user toggles a task as complete/incomplete if (index.isValid() && !mousePositionInsideCheckbox(event)) { Task *task = taskAtViewIndex(index); if (task) { emit taskDoubleClicked(task); } } else { QTreeView::mouseDoubleClickEvent(event); } } void TasksWidget::currentChanged(const QModelIndex& /*current*/, const QModelIndex& /*previous*/) { emit updateButtons(); } void TasksWidget::restoreItemState() { qCDebug(KTT_LOG) << "Entering function"; if (m_tasksModel->topLevelItemCount() > 0) { for (auto *item : m_tasksModel->getAllItems()) { auto *task = dynamic_cast(item); setExpanded(m_filterProxyModel->mapFromSource( m_tasksModel->index(task, 0)), readBoolEntry(task->uid())); } } qCDebug(KTT_LOG) << "Leaving function"; } Task* TasksWidget::taskAtViewIndex(QModelIndex viewIndex) { // if (!m_storage->isLoaded()) { // return nullptr; // } if (!m_tasksModel) { return nullptr; } QModelIndex index = m_filterProxyModel->mapToSource(viewIndex); return dynamic_cast(m_tasksModel->item(index)); } void TasksWidget::setSourceModel(TasksModel *tasksModel) { m_tasksModel = tasksModel; } Task* TasksWidget::currentItem() { return taskAtViewIndex(QTreeView::currentIndex()); } void TasksWidget::setFilterText(const QString &text) { m_filterProxyModel->setFilterFixedString(text); } void TasksWidget::refresh() { // remove root decoration if there is no more child. // int i = 0; // while (itemAt(++i) && itemAt(i)->depth() == 0){}; //setRootIsDecorated( itemAt( i ) && ( itemAt( i )->depth() != 0 ) ); // FIXME workaround? seems that the QItemDelegate for the percent column only // works properly if rootIsDecorated == true. setRootIsDecorated(true); emit updateButtons(); qCDebug(KTT_LOG) << "exiting TaskView::refresh()"; } void TasksWidget::reconfigure() { /* Adapt columns */ setColumnHidden(1, !KTimeTrackerSettings::displaySessionTime()); setColumnHidden(2, !KTimeTrackerSettings::displayTime()); setColumnHidden(3, !KTimeTrackerSettings::displayTotalSessionTime()); setColumnHidden(4, !KTimeTrackerSettings::displayTotalTime()); setColumnHidden(5, !KTimeTrackerSettings::displayPriority()); setColumnHidden(6, !KTimeTrackerSettings::displayPercentComplete()); refresh(); }