mirror of https://gitee.com/openkylin/quarkai.git
996 lines
35 KiB
C++
996 lines
35 KiB
C++
/*
|
|
* Copyright (C) 2013 ~ 2018 National University of Defense Technology(NUDT) & Tianjin Kylin Ltd.
|
|
*
|
|
* Authors:
|
|
* Kobe Lee xiangli@ubuntukylin.com/kobe24_lixiang@126.com
|
|
*
|
|
* 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; version 3.
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "processlistwidget.h"
|
|
#include <QTimer>
|
|
#include <QApplication>
|
|
#include <QDebug>
|
|
#include <QEvent>
|
|
#include <QMenu>
|
|
#include <QStyleFactory>
|
|
#include <QWheelEvent>
|
|
#include <QPainter>
|
|
#include <QtMath>
|
|
#include <QPen>
|
|
|
|
ProcessListWidget::ProcessListWidget(QList<bool> toBeDisplayedColumns, QWidget *parent) : QWidget(parent)
|
|
,m_titlePadding(10)
|
|
,m_titleHeight(36)
|
|
,m_rowHeight(36)
|
|
,m_offSet(0)
|
|
,m_origOffset(0)
|
|
,m_scrollbarWidth(10)
|
|
,m_titleHoverColumn(-1)
|
|
,m_titlePressColumn(-1)
|
|
,m_mouseAtScrollArea(false)
|
|
,m_mouseDragScrollbar(false)
|
|
{
|
|
this->m_searchFunc = NULL;
|
|
this->m_searchText = "";
|
|
this->m_lastItem = NULL;
|
|
this->m_listItems = new QList<ProcessListItem*>();
|
|
this->m_searchedItems = new QList<ProcessListItem*>();
|
|
this->m_selectedItems = new QList<ProcessListItem*>();
|
|
|
|
this->m_sortFuncList = new QList<SortFunction>();
|
|
this->m_isSortList = new QList<bool>();
|
|
|
|
this->m_upArrowNormalPixmap = QPixmap(":/res/arrow_up_normal.png");
|
|
this->m_upArrowHoverPixmap = QPixmap(":/res/arrow_up_hover.png");
|
|
this->m_upArrowPressPixmap = QPixmap(":/res/arrow_up_press.png");
|
|
this->m_downArrowNormalPixmap = QPixmap(":/res/arrow_down_normal.png");
|
|
this->m_downArrowHoverPixmap = QPixmap(":/res/arrow_down_hover.png");
|
|
this->m_downArrowPressPixmap = QPixmap(":/res/arrow_down_press.png");
|
|
|
|
this->columnTitles << tr("Process Name") << tr("User") << tr("Status") << tr("CPU") << tr("ID") << tr("Command Line") << tr("Memory") << tr("Priority");
|
|
QList<int> widths;
|
|
widths << 180 << 80 << 80 << 50 << 50 << -1 << 80 << 80;//-1时让改行填充所有剩余空间
|
|
|
|
QFont font;
|
|
font.setPointSize(9);//需要和填充所有剩余空间的那个的文字字体大小一致
|
|
QFontMetrics fm(font);
|
|
|
|
this->m_columnWidths.clear();
|
|
for (int i = 0; i < widths.length(); i++) {
|
|
if (widths[i] == -1) {
|
|
this->m_columnWidths << widths[i];
|
|
} else {//-1时让改行填充所有剩余空间
|
|
int maxWidth = fm.width(this->columnTitles[i]) + this->m_titlePadding + m_upArrowNormalPixmap.width() / m_upArrowNormalPixmap.devicePixelRatio() + 2 * 2;
|
|
this->m_columnWidths << std::max(widths[i], maxWidth);
|
|
}
|
|
}
|
|
|
|
this->m_columnVisibles.clear();
|
|
for (int i = 0; i < toBeDisplayedColumns.count(); i++) {
|
|
this->m_columnVisibles.append(toBeDisplayedColumns[i]);
|
|
}
|
|
|
|
this->setFocus();
|
|
}
|
|
|
|
ProcessListWidget::~ProcessListWidget()
|
|
{
|
|
delete this->m_lastItem;
|
|
delete this->m_listItems;
|
|
delete this->m_searchedItems;
|
|
delete this->m_selectedItems;
|
|
delete this->m_sortFuncList;
|
|
delete this->m_isSortList;
|
|
delete this->m_hideScrollbarTimer;
|
|
}
|
|
|
|
void ProcessListWidget::setProcessSortFunctions(QList<SortFunction> *list, int currentSortIndex, bool isSort)
|
|
{
|
|
this->m_sortFuncList = list;
|
|
|
|
for (int i = 0; i < this->m_sortFuncList->count(); i++) {
|
|
this->m_isSortList->append(false);
|
|
}
|
|
this->m_currentSortIndex = currentSortIndex;
|
|
this->m_isSort = isSort;
|
|
}
|
|
|
|
void ProcessListWidget::setSearchFunction(SearchFunction func)
|
|
{
|
|
this->m_searchFunc = func;
|
|
}
|
|
|
|
void ProcessListWidget::addItems(QList<ProcessListItem*> items)
|
|
{
|
|
this->m_listItems->append(items);
|
|
QList<ProcessListItem*> s_items = this->getSearchedItems(items);
|
|
this->m_searchedItems->append(s_items);
|
|
if (this->m_currentSortIndex != -1) {
|
|
this->sortItemsByColumn(this->m_currentSortIndex, this->m_isSort);
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::clearItems()
|
|
{
|
|
qDeleteAll(this->m_listItems->begin(), this->m_listItems->end());
|
|
this->m_listItems->clear();
|
|
this->m_searchedItems->clear();
|
|
}
|
|
|
|
void ProcessListWidget::addSelectedItems(QList<ProcessListItem*> items, bool recordLastItem)
|
|
{
|
|
this->m_selectedItems->append(items);
|
|
|
|
if (recordLastItem && this->m_selectedItems->count() > 0) {
|
|
this->m_lastItem = this->m_selectedItems->last();
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::clearSelectedItems(bool clearLast)
|
|
{
|
|
this->m_selectedItems->clear();
|
|
if (clearLast) {
|
|
this->m_lastItem = NULL;
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::refreshItems(QList<ProcessListItem*> items)
|
|
{
|
|
QList<ProcessListItem*> *allItems = new QList<ProcessListItem*>();
|
|
ProcessListItem *newLastItem = NULL;
|
|
|
|
for (ProcessListItem *item:items) {
|
|
for (ProcessListItem *selectionItem:*this->m_selectedItems) {
|
|
if (item->isSameItem(selectionItem)) {
|
|
allItems->append(item);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (this->m_lastItem != NULL) {
|
|
for (ProcessListItem *item:items) {
|
|
if (item->isSameItem(this->m_lastItem)) {
|
|
newLastItem = item;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
clearItems();
|
|
this->m_listItems->append(items);
|
|
QList<ProcessListItem*> s_items = this->getSearchedItems(items);
|
|
this->m_searchedItems->append(s_items);
|
|
|
|
if (this->m_currentSortIndex != -1) {
|
|
this->sortItemsByColumn(this->m_currentSortIndex, this->m_isSort);
|
|
}
|
|
|
|
clearSelectedItems();
|
|
addSelectedItems(*allItems, false);
|
|
|
|
this->m_lastItem = newLastItem;
|
|
this->m_offSet = setOffset(this->m_offSet);
|
|
|
|
repaint();
|
|
}
|
|
|
|
void ProcessListWidget::doSearch(QString text)
|
|
{
|
|
if (text == "" && this->m_searchText != text) {
|
|
this->m_searchText = text;
|
|
this->m_searchedItems->clear();
|
|
this->m_searchedItems->append(*this->m_listItems);
|
|
} else {
|
|
this->m_searchText = text;
|
|
QList<ProcessListItem*> s_items = this->getSearchedItems(*this->m_listItems);
|
|
this->m_searchedItems->clear();
|
|
this->m_searchedItems->append(s_items);
|
|
}
|
|
|
|
repaint();
|
|
}
|
|
|
|
void ProcessListWidget::selectTheFirstItem()
|
|
{
|
|
this->m_origOffset = this->m_offSet;
|
|
|
|
clearSelectedItems();
|
|
|
|
QList<ProcessListItem*> items = QList<ProcessListItem*>();
|
|
items << this->m_searchedItems->first();
|
|
addSelectedItems(items);
|
|
|
|
this->m_offSet = 0;
|
|
|
|
repaint();
|
|
}
|
|
|
|
void ProcessListWidget::selectTheLastItem()
|
|
{
|
|
this->m_origOffset = this->m_offSet;
|
|
|
|
clearSelectedItems();
|
|
|
|
QList<ProcessListItem*> items = QList<ProcessListItem*>();
|
|
items << this->m_searchedItems->last();
|
|
addSelectedItems(items);
|
|
|
|
this->m_offSet = getBottomOffset();
|
|
|
|
repaint();
|
|
}
|
|
|
|
void ProcessListWidget::selectThePrevItem(int offset)
|
|
{
|
|
this->m_origOffset = this->m_offSet;
|
|
|
|
if (this->m_selectedItems->empty()) {
|
|
selectTheFirstItem();
|
|
} else {
|
|
int firstIndex = this->m_searchedItems->count();
|
|
for (ProcessListItem *item:*this->m_selectedItems) {
|
|
int index = this->m_searchedItems->indexOf(item);
|
|
if (index < firstIndex) {
|
|
firstIndex = index;
|
|
}
|
|
}
|
|
|
|
if (firstIndex != -1) {
|
|
firstIndex = std::max(0, firstIndex - offset);
|
|
clearSelectedItems();
|
|
QList<ProcessListItem*> items = QList<ProcessListItem*>();
|
|
items << (*this->m_searchedItems)[firstIndex];
|
|
addSelectedItems(items);
|
|
int itemIndex = firstIndex - 1;
|
|
int itemOffset = setOffset(itemIndex * this->m_rowHeight + this->m_titleHeight);
|
|
if ((this->m_offSet / this->m_rowHeight) > itemIndex) {
|
|
this->m_offSet = itemOffset;
|
|
}
|
|
repaint();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::selectTheNextItem(int offset)
|
|
{
|
|
this->m_origOffset = this->m_offSet;
|
|
|
|
if (this->m_selectedItems->empty()) {
|
|
selectTheFirstItem();
|
|
} else {
|
|
int lastIndex = 0;
|
|
for (ProcessListItem *item:*this->m_selectedItems) {
|
|
int index = this->m_searchedItems->indexOf(item);
|
|
if (index > lastIndex) {
|
|
lastIndex = index;
|
|
}
|
|
}
|
|
|
|
if (lastIndex != -1) {
|
|
lastIndex = std::min(this->m_searchedItems->count() - 1, lastIndex + offset);
|
|
|
|
clearSelectedItems(false);
|
|
|
|
QList<ProcessListItem*> items = QList<ProcessListItem*>();
|
|
items << (*this->m_searchedItems)[lastIndex];
|
|
|
|
addSelectedItems(items);
|
|
|
|
int itemIndex = lastIndex + 1;
|
|
int itemOffset = setOffset(itemIndex * this->m_rowHeight - rect().height() + this->m_titleHeight);
|
|
if (((this->m_offSet + getTheScrollAreaHeight()) / this->m_rowHeight) < itemIndex) {
|
|
this->m_offSet = itemOffset;
|
|
}
|
|
|
|
repaint();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::shiftToHomeItem()
|
|
{
|
|
if (this->m_selectedItems->empty()) {
|
|
selectTheFirstItem();
|
|
}
|
|
else {
|
|
int lastSelectionIndex = this->m_searchedItems->indexOf(this->m_lastItem);
|
|
shiftToSelectedItems(0, lastSelectionIndex);
|
|
this->m_offSet = 0;
|
|
repaint();
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::shiftToEndItem()
|
|
{
|
|
if (this->m_selectedItems->empty()) {
|
|
selectTheLastItem();
|
|
}
|
|
else {
|
|
shiftToSelectedItems(this->m_searchedItems->indexOf(this->m_lastItem), this->m_searchedItems->count() - 1);
|
|
this->m_offSet = getBottomOffset();
|
|
repaint();
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::shiftToPrevItem(int offset)
|
|
{
|
|
this->m_origOffset = this->m_offSet;
|
|
if (this->m_selectedItems->empty()) {
|
|
selectTheFirstItem();
|
|
}
|
|
else {
|
|
int firstIndex = this->m_searchedItems->count();
|
|
int lastIndex = 0;
|
|
for (ProcessListItem *item : *this->m_selectedItems) {
|
|
int index = this->m_searchedItems->indexOf(item);
|
|
if (index < firstIndex) {
|
|
firstIndex = index;
|
|
}
|
|
if (index > lastIndex) {
|
|
lastIndex = index;
|
|
}
|
|
}
|
|
|
|
if (firstIndex != -1) {
|
|
int lastSelectionIndex = this->m_searchedItems->indexOf(this->m_lastItem);
|
|
int selectionStartIndex, selectionEndIndex;
|
|
if (lastIndex == lastSelectionIndex) {
|
|
selectionStartIndex = std::max(0, firstIndex - offset);
|
|
selectionEndIndex = lastSelectionIndex;
|
|
} else {
|
|
selectionStartIndex = firstIndex;
|
|
selectionEndIndex = std::max(0, lastIndex - offset);
|
|
}
|
|
shiftToSelectedItems(selectionStartIndex, selectionEndIndex);
|
|
if (this->m_offSet / this->m_rowHeight >= selectionStartIndex) {
|
|
this->m_offSet = setOffset((selectionStartIndex - 1) * this->m_rowHeight + this->m_titleHeight);
|
|
}
|
|
repaint();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::shiftToNextItem(int offset)
|
|
{
|
|
this->m_origOffset = this->m_offSet;
|
|
|
|
if (this->m_selectedItems->empty()) {
|
|
selectTheFirstItem();
|
|
} else {
|
|
int firstIndex = this->m_searchedItems->count();
|
|
int lastIndex = 0;
|
|
for (ProcessListItem *item:*this->m_selectedItems) {
|
|
int index = this->m_searchedItems->indexOf(item);
|
|
|
|
if (index < firstIndex) {
|
|
firstIndex = index;
|
|
}
|
|
|
|
if (index > lastIndex) {
|
|
lastIndex = index;
|
|
}
|
|
}
|
|
|
|
if (firstIndex != -1) {
|
|
int lastSelectionIndex = this->m_searchedItems->indexOf(this->m_lastItem);
|
|
int selectionStartIndex, selectionEndIndex;
|
|
|
|
if (firstIndex == lastSelectionIndex) {
|
|
selectionStartIndex = firstIndex;
|
|
selectionEndIndex = std::min(this->m_searchedItems->count() - 1, lastIndex + offset);
|
|
} else {
|
|
selectionStartIndex = std::min(this->m_searchedItems->count() - 1, firstIndex + offset);
|
|
selectionEndIndex = lastIndex;
|
|
}
|
|
|
|
shiftToSelectedItems(selectionStartIndex, selectionEndIndex);
|
|
|
|
if ((this->m_offSet + rect().height()) / this->m_rowHeight <= selectionEndIndex + 1) {
|
|
this->m_offSet = setOffset((selectionEndIndex + 1) * this->m_rowHeight + this->m_titleHeight - rect().height());
|
|
}
|
|
|
|
|
|
repaint();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::shiftToSelectedItems(int start, int end)
|
|
{
|
|
clearSelectedItems(false);
|
|
QList<ProcessListItem*> items = QList<ProcessListItem*>();
|
|
int index = 0;
|
|
for (ProcessListItem *item:*this->m_searchedItems) {
|
|
if (index >= start && index <= end) {
|
|
items << item;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
addSelectedItems(items, false);
|
|
}
|
|
|
|
void ProcessListWidget::leaveEvent(QEvent * event)
|
|
{
|
|
hideScrollbar();
|
|
QWidget::leaveEvent(event);
|
|
}
|
|
|
|
void ProcessListWidget::hideScrollbar()
|
|
{
|
|
this->m_mouseAtScrollArea = false;
|
|
this->m_origOffset = this->m_offSet;
|
|
|
|
repaint();
|
|
}
|
|
|
|
void ProcessListWidget::keyPressEvent(QKeyEvent *keyEvent)
|
|
{
|
|
if (keyEvent->key() == Qt::Key_Up) {
|
|
if (keyEvent->modifiers() == Qt::ShiftModifier)
|
|
shiftToPrevItem(1);
|
|
else
|
|
selectThePrevItem(1);
|
|
}
|
|
else if (keyEvent->key() == Qt::Key_Down) {
|
|
if (keyEvent->modifiers() == Qt::ShiftModifier)
|
|
shiftToNextItem(1);
|
|
else
|
|
selectTheNextItem(1);
|
|
}
|
|
else if (keyEvent->key() == Qt::Key_Home) {
|
|
if (keyEvent->modifiers() == Qt::ControlModifier) {
|
|
this->m_offSet = 0;
|
|
repaint();
|
|
}
|
|
else if (keyEvent->modifiers() == Qt::ShiftModifier) {
|
|
shiftToHomeItem();
|
|
}
|
|
else {
|
|
selectTheFirstItem();
|
|
}
|
|
} else if (keyEvent->key() == Qt::Key_End) {
|
|
if (keyEvent->modifiers() == Qt::ControlModifier) {
|
|
this->m_offSet = getBottomOffset();
|
|
repaint();
|
|
}
|
|
else if (keyEvent->modifiers() == Qt::ShiftModifier) {
|
|
shiftToEndItem();
|
|
}
|
|
else {
|
|
selectTheLastItem();
|
|
}
|
|
}
|
|
else if (keyEvent->key() == Qt::Key_PageUp) {
|
|
if (keyEvent->modifiers() == Qt::ControlModifier) {
|
|
this->m_offSet = setOffset(this->m_offSet - getTheScrollAreaHeight());
|
|
repaint();
|
|
}
|
|
else if (keyEvent->modifiers() == Qt::ShiftModifier) {
|
|
shiftToPrevItem(getTheScrollAreaHeight() / this->m_rowHeight);
|
|
}
|
|
else {
|
|
selectThePrevItem(getTheScrollAreaHeight() / this->m_rowHeight);
|
|
}
|
|
}
|
|
else if (keyEvent->key() == Qt::Key_PageDown) {
|
|
if (keyEvent->modifiers() == Qt::ControlModifier) {
|
|
this->m_offSet = setOffset(this->m_offSet + getTheScrollAreaHeight());
|
|
repaint();
|
|
}
|
|
else if (keyEvent->modifiers() == Qt::ShiftModifier) {
|
|
shiftToNextItem(getTheScrollAreaHeight() / this->m_rowHeight);
|
|
}
|
|
else {
|
|
selectTheNextItem(getTheScrollAreaHeight() / this->m_rowHeight);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::mouseMoveEvent(QMouseEvent *mouseEvent)
|
|
{
|
|
if (this->m_mouseDragScrollbar) {
|
|
this->m_offSet = setOffset((mouseEvent->y() - getScrollbarHeight() / 2 - this->m_titleHeight) / (getTheScrollAreaHeight()) * this->getItemsTotalHeight());
|
|
repaint();
|
|
}
|
|
else if (mouseAtScrollArea(mouseEvent->x()) != this->m_mouseAtScrollArea) {
|
|
this->m_mouseAtScrollArea = mouseAtScrollArea(mouseEvent->x());
|
|
repaint();
|
|
}
|
|
else {
|
|
bool isTitleArea = mouseAtTitleArea(mouseEvent->y());
|
|
if (isTitleArea) {
|
|
int currentHoverIndex = -1;
|
|
if (this->m_sortFuncList->count() != 0 && this->m_sortFuncList->count() == this->columnTitles.count() && this->m_isSortList->count() == this->columnTitles.count()) {
|
|
QList<int> titleItemsWidths = getTitleItemsWidths();
|
|
int counter = 0;
|
|
int pos_X = 0;
|
|
for (int t_width : titleItemsWidths) {
|
|
if (t_width > 0) {
|
|
if (mouseEvent->x() > pos_X && mouseEvent->x() < pos_X + t_width) {
|
|
currentHoverIndex = counter;
|
|
break;
|
|
}
|
|
pos_X += t_width;
|
|
}
|
|
counter++;
|
|
}
|
|
}
|
|
if (currentHoverIndex != this->m_titleHoverColumn) {
|
|
this->m_titleHoverColumn = currentHoverIndex;
|
|
repaint();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::mousePressEvent(QMouseEvent *mouseEvent)
|
|
{
|
|
setFocus();
|
|
|
|
bool isTitleArea = mouseAtTitleArea(mouseEvent->y());
|
|
bool isScrollArea = mouseAtScrollArea(mouseEvent->x());
|
|
|
|
if (isTitleArea) {//点击列表的标题栏区域
|
|
if (mouseEvent->button() == Qt::LeftButton) {
|
|
if (this->m_sortFuncList->count() != 0 && this->m_sortFuncList->count() == this->columnTitles.count() && this->m_isSortList->count() == this->columnTitles.count()) {
|
|
QList<int> titleItemsWidths = getTitleItemsWidths();
|
|
int counter = 0;
|
|
int posX = 0;
|
|
for (int t_width : titleItemsWidths) {
|
|
if (t_width > 0) {
|
|
if (mouseEvent->x() > posX && mouseEvent->x() < posX + t_width) {
|
|
if (counter != this->m_currentSortIndex) {
|
|
(*this->m_isSortList)[counter] = true;
|
|
}
|
|
else {
|
|
(*this->m_isSortList)[counter] = !(*this->m_isSortList)[counter];
|
|
}
|
|
this->m_currentSortIndex = counter;
|
|
this->m_isSort = (*this->m_isSortList)[counter];
|
|
emit this->changeSortStatus(this->m_currentSortIndex, this->m_isSort);
|
|
this->sortItemsByColumn(counter, (*this->m_isSortList)[counter]);
|
|
if (counter != this->m_titlePressColumn) {
|
|
this->m_titlePressColumn = counter;
|
|
}
|
|
repaint();
|
|
break;
|
|
}
|
|
posX += t_width;
|
|
}
|
|
counter++;
|
|
}
|
|
}
|
|
}
|
|
else if (mouseEvent->button() == Qt::RightButton) {
|
|
if (m_columnVisibles.count() == this->columnTitles.count()) {
|
|
QMenu *menu = new QMenu();
|
|
menu->setObjectName("MonitorMenu");
|
|
for (int i = 0; i < m_columnVisibles.count(); i++) {
|
|
if (i != 0) {//让第一行总是显示,不可以设置显示或者不显示,其他行可以设置
|
|
QAction *action = new QAction(menu);
|
|
action->setText(this->columnTitles[i]);
|
|
action->setCheckable(true);
|
|
action->setChecked(m_columnVisibles[i]);
|
|
connect(action, &QAction::triggered, this, [this, action, i] {
|
|
m_columnVisibles[i] = !m_columnVisibles[i];
|
|
emit this->changeColumnVisible(i, m_columnVisibles[i], m_columnVisibles);
|
|
repaint();
|
|
});
|
|
menu->addAction(action);
|
|
}
|
|
}
|
|
menu->exec(this->mapToGlobal(mouseEvent->pos()));
|
|
delete menu;
|
|
}
|
|
}
|
|
}
|
|
else if (isScrollArea) {//点击滚动条区域
|
|
int barHeight = getScrollbarHeight();
|
|
int barY = getScrollbarY();
|
|
if (mouseEvent->y() > barY && mouseEvent->y() < barY + barHeight) {
|
|
this->m_mouseDragScrollbar = true;
|
|
}
|
|
else {
|
|
this->m_offSet = setOffset((mouseEvent->y() - barHeight / 2 - this->m_titleHeight) / (getTheScrollAreaHeight()) * this->getItemsTotalHeight());
|
|
repaint();
|
|
}
|
|
}
|
|
else {
|
|
int pressedItemIndex = (this->m_offSet + mouseEvent->y() - this->m_titleHeight) / this->m_rowHeight;
|
|
if (pressedItemIndex >= this->m_searchedItems->count()) {
|
|
clearSelectedItems();
|
|
|
|
repaint();
|
|
} else {
|
|
if (mouseEvent->button() == Qt::LeftButton) {
|
|
if (pressedItemIndex < this->m_searchedItems->count()) {
|
|
if (mouseEvent->modifiers() == Qt::ControlModifier) {
|
|
ProcessListItem *item = (*this->m_searchedItems)[pressedItemIndex];
|
|
if (this->m_selectedItems->contains(item)) {
|
|
this->m_selectedItems->removeOne(item);
|
|
} else {
|
|
QList<ProcessListItem*> items = QList<ProcessListItem*>();
|
|
items << item;
|
|
addSelectedItems(items);
|
|
}
|
|
}
|
|
else if ((mouseEvent->modifiers() == Qt::ShiftModifier) && !this->m_selectedItems->empty()) {
|
|
int lastSelectionIndex = this->m_searchedItems->indexOf(this->m_lastItem);
|
|
int selectionStartIndex = std::min(pressedItemIndex, lastSelectionIndex);
|
|
int selectionEndIndex = std::max(pressedItemIndex, lastSelectionIndex);
|
|
shiftToSelectedItems(selectionStartIndex, selectionEndIndex);
|
|
}
|
|
else {
|
|
clearSelectedItems();
|
|
|
|
QList<ProcessListItem*> items = QList<ProcessListItem*>();
|
|
items << (*this->m_searchedItems)[pressedItemIndex];
|
|
addSelectedItems(items);
|
|
}
|
|
repaint();
|
|
}
|
|
} else if (mouseEvent->button() == Qt::RightButton) {
|
|
ProcessListItem *pressItem = (*this->m_searchedItems)[pressedItemIndex];
|
|
bool pressInSelectionArea = false;
|
|
for (ProcessListItem *item : *this->m_selectedItems) {
|
|
if (item == pressItem) {
|
|
pressInSelectionArea = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!pressInSelectionArea && pressedItemIndex < this->m_searchedItems->length()) {
|
|
clearSelectedItems();
|
|
QList<ProcessListItem*> items = QList<ProcessListItem*>();
|
|
items << (*this->m_searchedItems)[pressedItemIndex];
|
|
addSelectedItems(items);
|
|
repaint();
|
|
}
|
|
if (this->m_selectedItems->length() > 0) {
|
|
emit this->rightMouseClickedItems(this->mapToGlobal(mouseEvent->pos()), *this->m_selectedItems);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::mouseReleaseEvent(QMouseEvent *)
|
|
{
|
|
if (this->m_mouseDragScrollbar) {
|
|
this->m_mouseDragScrollbar = false;
|
|
repaint();
|
|
}
|
|
else {
|
|
if (this->m_titlePressColumn != -1) {
|
|
this->m_titlePressColumn = -1;
|
|
repaint();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::wheelEvent(QWheelEvent *event)
|
|
{
|
|
if (event->orientation() == Qt::Vertical) {
|
|
this->m_origOffset = this->m_offSet;
|
|
qreal scrollStep = event->angleDelta().y() / 100.0;
|
|
this->m_offSet = setOffset(this->m_offSet - scrollStep * this->m_rowHeight);
|
|
repaint();
|
|
}
|
|
event->accept();
|
|
}
|
|
|
|
void ProcessListWidget::paintEvent(QPaintEvent *)
|
|
{
|
|
QPainter painter(this);
|
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
QList<int> titleItemsWidths = getTitleItemsWidths();
|
|
|
|
painter.setOpacity(0.05);
|
|
|
|
int penWidth = 1;
|
|
QPainterPath framePath;
|
|
framePath.addRoundedRect(QRect(rect().x() + penWidth, rect().y() + penWidth, rect().width() - penWidth * 2, rect().height() - penWidth * 2), 4, 4);//背景弧度
|
|
painter.setClipPath(framePath);
|
|
|
|
//标题的背景
|
|
if (this->m_titleHeight > 0) {
|
|
QPainterPath titlePath;
|
|
titlePath.addRect(QRectF(rect().x(), rect().y(), rect().width(), this->m_titleHeight));
|
|
painter.setOpacity(0.02);
|
|
painter.fillPath(titlePath, QColor("#ffffff"));
|
|
}
|
|
|
|
int title_Y = 0;
|
|
int title_Height = 0;
|
|
if (this->m_titleHeight > 0) {
|
|
int counter = 0;
|
|
int posX = 0;
|
|
for (int itemWidth:titleItemsWidths) {
|
|
if (itemWidth > 0) {
|
|
painter.setOpacity(1);
|
|
QFont font = painter.font();
|
|
font.setPointSize(10);
|
|
painter.setFont(font);
|
|
//标题文字
|
|
painter.setPen(QPen(QColor("#000000")));
|
|
painter.drawText(QRect(posX + this->m_titlePadding, 0, itemWidth, this->m_titleHeight), Qt::AlignVCenter | Qt::AlignLeft, this->columnTitles[counter]);
|
|
posX += itemWidth;
|
|
if (counter < titleItemsWidths.size() - 1) {//垂直分割线
|
|
painter.setOpacity(0.1);
|
|
QPainterPath separatorPath;
|
|
separatorPath.addRect(QRectF(rect().x() + posX - 1, rect().y() + 5, 1, this->m_titleHeight - 5*2));
|
|
painter.fillPath(separatorPath, QColor("#000000"));
|
|
}
|
|
|
|
//标题文字右侧的排序箭头图标
|
|
if (this->m_currentSortIndex == counter) {
|
|
painter.setOpacity(1);
|
|
int arrowX = rect().x() + posX - 2 - m_upArrowNormalPixmap.width() / m_upArrowNormalPixmap.devicePixelRatio();
|
|
int arrowY = rect().y() + (this->m_titleHeight - m_downArrowNormalPixmap.height() / m_upArrowNormalPixmap.devicePixelRatio()) / 2;
|
|
|
|
if (this->m_isSort) {
|
|
if (this->m_titlePressColumn == this->m_currentSortIndex) {
|
|
painter.drawPixmap(QPoint(arrowX, arrowY), m_downArrowPressPixmap);
|
|
} else if (this->m_titleHoverColumn == this->m_currentSortIndex) {
|
|
painter.drawPixmap(QPoint(arrowX, arrowY), m_downArrowHoverPixmap);
|
|
} else {
|
|
painter.drawPixmap(QPoint(arrowX, arrowY), m_downArrowNormalPixmap);
|
|
}
|
|
} else {
|
|
if (this->m_titlePressColumn == this->m_currentSortIndex) {
|
|
painter.drawPixmap(QPoint(arrowX, arrowY), m_upArrowPressPixmap);
|
|
} else if (this->m_titleHoverColumn == this->m_currentSortIndex) {
|
|
painter.drawPixmap(QPoint(arrowX, arrowY), m_upArrowHoverPixmap);
|
|
} else {
|
|
painter.drawPixmap(QPoint(arrowX, arrowY), m_upArrowNormalPixmap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
counter++;
|
|
}
|
|
|
|
title_Y += this->m_titleHeight;
|
|
title_Height += this->m_titleHeight;
|
|
}
|
|
|
|
//去掉列表标题栏后的列表显示区域的背景
|
|
painter.setOpacity(0.05);
|
|
QPainterPath backgroundPath;
|
|
backgroundPath.addRect(QRectF(rect().x(), rect().y() + this->m_titleHeight, rect().width(), rect().height() - this->m_titleHeight));
|
|
painter.fillPath(backgroundPath, QColor("#ffffff"));
|
|
|
|
//进程信息
|
|
QPainterPath scrollAreaPath;
|
|
scrollAreaPath.addRect(QRectF(rect().x(), rect().y() + this->m_titleHeight, rect().width(), getTheScrollAreaHeight()));
|
|
|
|
int rowCounter = 0;
|
|
for (ProcessListItem *item:*this->m_searchedItems) {
|
|
if (rowCounter > ((this->m_offSet - this->m_rowHeight) / this->m_rowHeight)) {
|
|
QPainterPath itemPath;
|
|
itemPath.addRect(QRect(0, title_Y + rowCounter * this->m_rowHeight - this->m_offSet, rect().width(), this->m_rowHeight));
|
|
painter.setClipPath((framePath.intersected(scrollAreaPath)).intersected(itemPath));
|
|
|
|
bool isSelect = this->m_selectedItems->contains(item);
|
|
painter.save();
|
|
item->drawBackground(QRect(0, title_Y + rowCounter * this->m_rowHeight - this->m_offSet, rect().width(), this->m_rowHeight), &painter, rowCounter, isSelect);
|
|
painter.restore();
|
|
|
|
int columnCounter = 0;
|
|
int columnTitleX = 0;
|
|
for (int titleItemWidth : titleItemsWidths) {
|
|
if (titleItemWidth > 0) {
|
|
painter.save();
|
|
item->drawForeground(QRect(columnTitleX, title_Y + rowCounter * this->m_rowHeight - this->m_offSet, titleItemWidth, this->m_rowHeight), &painter, columnCounter, rowCounter, isSelect);
|
|
painter.restore();
|
|
columnTitleX += titleItemWidth;
|
|
}
|
|
columnCounter++;
|
|
}
|
|
title_Height += this->m_rowHeight;
|
|
if (title_Height > rect().height()) {
|
|
break;
|
|
}
|
|
}
|
|
rowCounter++;
|
|
}
|
|
painter.setClipPath(framePath);
|
|
|
|
//没有搜索结果时绘制提示文字
|
|
if (this->m_searchText != "" && this->m_searchedItems->size() == 0) {
|
|
painter.setOpacity(1);
|
|
painter.setPen(QPen(QColor("#666666")));
|
|
QFont font = painter.font() ;
|
|
font.setPointSize(22);
|
|
painter.setFont(font);
|
|
painter.drawText(QRect(rect().x(), rect().y() + this->m_titleHeight, rect().width(), rect().height() - this->m_titleHeight), Qt::AlignCenter, tr("No search result"));
|
|
}
|
|
|
|
//背景
|
|
// QPen framePen;
|
|
// framePen.setColor(QColor("#F5F5F5"));
|
|
// painter.setPen(framePen);
|
|
painter.setOpacity(0.2);
|
|
painter.drawPath(framePath);
|
|
|
|
//垂直滚动条
|
|
if (this->m_mouseAtScrollArea) {
|
|
paintScrollbar(&painter);
|
|
} else if (this->m_origOffset != this->m_offSet) {
|
|
paintScrollbar(&painter);
|
|
startScrollbarHideTimer();
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::paintScrollbar(QPainter *painter)
|
|
{
|
|
if (this->getItemsTotalHeight() > getTheScrollAreaHeight()) {
|
|
qreal opacitry = 0;
|
|
if (this->m_mouseDragScrollbar) {
|
|
opacitry = 0.8;
|
|
}
|
|
else {
|
|
if (this->m_mouseAtScrollArea)
|
|
opacitry = 0.7;
|
|
else
|
|
opacitry = 0.5;
|
|
}
|
|
|
|
int barWidth = (this->m_mouseAtScrollArea || this->m_mouseDragScrollbar) ? this->m_scrollbarWidth : 6;
|
|
int barY = getScrollbarY();
|
|
int barHeight = getScrollbarHeight();
|
|
painter->setOpacity(opacitry);
|
|
QPainterPath path;
|
|
path.addRoundedRect(
|
|
QRectF(rect().x() + rect().width() - barWidth - 4,
|
|
barY + 2,
|
|
barWidth,
|
|
barHeight - 2 * 2), 2, 2);
|
|
painter->fillPath(path, QColor("#0B95D7"));
|
|
|
|
QPen pen;
|
|
pen.setColor(QColor("#0B95D7"));
|
|
pen.setWidth(1);
|
|
painter->setOpacity(0);
|
|
painter->setPen(pen);
|
|
painter->drawPath(path);
|
|
}
|
|
}
|
|
|
|
QList<int> ProcessListWidget::getTitleItemsWidths()
|
|
{
|
|
QList<int> titleItemsWidths;
|
|
if (this->m_columnWidths.length() > 0) {
|
|
if (this->m_columnWidths.contains(-1)) {
|
|
for (int i = 0; i < this->m_columnWidths.count(); i++) {
|
|
if (this->m_columnWidths[i] != -1) {
|
|
if (m_columnVisibles[i]) {
|
|
titleItemsWidths << this->m_columnWidths[i];
|
|
} else {
|
|
titleItemsWidths << 0;
|
|
}
|
|
} else {
|
|
if (m_columnVisibles[i]) {
|
|
int totalWidth = 0;
|
|
for (int j = 0; j < this->m_columnWidths.count(); j++) {
|
|
if (this->m_columnWidths[j] != -1 && m_columnVisibles[j]) {
|
|
totalWidth += this->m_columnWidths[j];
|
|
}
|
|
}
|
|
titleItemsWidths << rect().width() - totalWidth;
|
|
}
|
|
else {
|
|
titleItemsWidths << 0;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (int i = 0; i < this->m_columnWidths.count(); i++) {
|
|
if (m_columnVisibles[i]) {
|
|
titleItemsWidths << this->m_columnWidths[i];
|
|
}
|
|
else {
|
|
titleItemsWidths << 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
titleItemsWidths << rect().width();
|
|
}
|
|
|
|
return titleItemsWidths;
|
|
}
|
|
|
|
bool ProcessListWidget::mouseAtScrollArea(int x)
|
|
{
|
|
return (x > rect().x() + rect().width() - this->m_scrollbarWidth) && (x < rect().x() + rect().width());
|
|
}
|
|
|
|
bool ProcessListWidget::mouseAtTitleArea(int y)
|
|
{
|
|
return (y > rect().y() && y < rect().y() + this->m_titleHeight);
|
|
}
|
|
|
|
int ProcessListWidget::setOffset(int offset)
|
|
{
|
|
return std::max(0, std::min(offset, getBottomOffset()));
|
|
}
|
|
|
|
int ProcessListWidget::getItemsTotalHeight()
|
|
{
|
|
return m_searchedItems->count() * m_rowHeight;
|
|
}
|
|
|
|
int ProcessListWidget::getTheScrollAreaHeight()
|
|
{
|
|
return this->rect().height() - this->m_titleHeight;
|
|
}
|
|
|
|
int ProcessListWidget::getScrollbarY()
|
|
{
|
|
return static_cast<int>((this->m_offSet / (this->getItemsTotalHeight())) * getTheScrollAreaHeight() + this->m_titleHeight);
|
|
}
|
|
|
|
int ProcessListWidget::getScrollbarHeight()
|
|
{
|
|
return std::max(static_cast<int>(getTheScrollAreaHeight() / (this->getItemsTotalHeight()) * rect().height()), this->m_rowHeight);
|
|
}
|
|
|
|
QList<ProcessListItem*> ProcessListWidget::getSearchedItems(QList<ProcessListItem*> items)
|
|
{
|
|
if (m_searchText == "" || m_searchFunc == NULL) {
|
|
return items;
|
|
} else {
|
|
QList<ProcessListItem*> *search_items = new QList<ProcessListItem*>();
|
|
|
|
for (ProcessListItem *item : items) {
|
|
if (m_searchFunc(item, m_searchText)) {
|
|
search_items->append(item);
|
|
}
|
|
}
|
|
return *search_items;
|
|
}
|
|
}
|
|
|
|
int ProcessListWidget::getBottomOffset()
|
|
{
|
|
int itemsHeight = this->getItemsTotalHeight();
|
|
if (itemsHeight > rect().height() - this->m_titleHeight) {
|
|
return this->getItemsTotalHeight() - rect().height() + this->m_titleHeight;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::sortItemsByColumn(int column, bool isSort)
|
|
{
|
|
if (m_sortFuncList->count() != 0 && m_sortFuncList->count() == columnTitles.count() && m_isSortList->count() == columnTitles.count()) {
|
|
qSort(m_searchedItems->begin(), m_searchedItems->end(), [&](const ProcessListItem *item1, const ProcessListItem *item2) {
|
|
return (*m_sortFuncList)[column](item1, item2, isSort);
|
|
});
|
|
}
|
|
}
|
|
|
|
void ProcessListWidget::startScrollbarHideTimer()
|
|
{
|
|
if (this->m_hideScrollbarTimer) {
|
|
this->m_hideScrollbarTimer->stop();
|
|
}
|
|
this->m_hideScrollbarTimer = new QTimer();
|
|
connect(this->m_hideScrollbarTimer, SIGNAL(timeout()), this, SLOT(hideScrollbar()));
|
|
this->m_hideScrollbarTimer->start(2000);
|
|
}
|