1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "taskwindow.h"
6#include "taskmodel.h"
7#include "taskfiltermodel.h"
8#include "timelinewidget.h"
9
10#include <QDir>
11#include <QPainter>
12#include <QStyledItemDelegate>
13#include <QMenu>
14#include <QLabel>
15#include <QLineEdit>
16#include <QToolButton>
17#include <QScrollBar>
18#include <QVBoxLayout>
19#include <QPushButton>
20#include <QComboBox>
21#include <QListView>
22#include <QTextLayout>
23#include <QTextLine>
24#include <QtDebug>
25
26namespace {
27const int ELLIPSIS_GRADIENT_WIDTH = 16;
28}
29
30namespace ReverseDebugger {
31namespace Internal {
32
33class TaskView : public QListView
34{
35public:
36 TaskView(QWidget *parent = nullptr);
37 ~TaskView();
38 void resizeEvent(QResizeEvent *e);
39 void contextMenuEvent(QContextMenuEvent*);
40private:
41};
42
43class TaskWidget : public QWidget
44{
45 // Q_OBJECT
46public:
47 TaskWidget(QWidget* parent = nullptr)
48 : QWidget(parent)
49 {
50 }
51 ~TaskWidget()
52 {
53 }
54
55 void setup(TimelineWidget *timeline, TaskView *listview)
56 {
57 QVBoxLayout *layout = new QVBoxLayout(this);
58 layout->setMargin(0);
59 layout->setSpacing(0);
60 setLayout(layout);
61 layout->addWidget(timeline);
62 layout->addWidget(listview);
63 }
64};
65
66class TaskDelegate : public QStyledItemDelegate
67{
68 Q_OBJECT
69
70 friend class TaskView; // for using Positions::minimumSize()
71
72public:
73 TaskDelegate(QObject * parent = nullptr);
74 ~TaskDelegate();
75 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
76 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
77
78 // TaskView uses this method if the size of the taskview changes
79 void emitSizeHintChanged(const QModelIndex &index);
80
81public slots:
82 void currentChanged(const QModelIndex &current, const QModelIndex &previous);
83
84private:
85 void generateGradientPixmap(int width, int height, QColor color, bool selected) const;
86
87 mutable int m_cachedHeight;
88 mutable QFont m_cachedFont;
89
90 /*
91 Collapsed:
92 +----------------------------------------------------------------------------------------------------+
93 | TASKICONAREA TEXTAREA TIMEAREA DURATIONAREA RETURNAREA TIDAREA THREADSAREA|
94 +----------------------------------------------------------------------------------------------------+
95
96 Expanded:
97 +----------------------------------------------------------------------------------------------------+
98 | TASKICONICON TEXTAREA TIMEAREA DURATIONAREA RETURNAREA TIDAREA THREADSAREA|
99 | more text -------------------------------------------------------------------------> |
100 +----------------------------------------------------------------------------------------------------+
101 */
102 class Positions
103 {
104 public:
105 Positions(const QStyleOptionViewItem &options, TaskModel *model) :
106 totalWidth(options.rect.width()),
107 maxLineLength(model->getSizeOfLineNumber(options.font)),
108 iTop(options.rect.top()),
109 iBottom(options.rect.bottom())
110 {
111 fontHeight = QFontMetrics(options.font).height();
112 }
113
114 int top() const { return iTop + ITEM_MARGIN; }
115 int left() const { return ITEM_MARGIN; }
116 int right() const { return totalWidth - ITEM_MARGIN; }
117 int bottom() const { return iBottom; }
118 int firstLineHeight() const { return fontHeight + 1; }
119 static int minimumHeight() { return taskIconHeight() + 2 * ITEM_MARGIN; }
120
121 int taskIconLeft() const { return left(); }
122 static int taskIconWidth() { return TASK_ICON_SIZE; }
123 static int taskIconHeight() { return TASK_ICON_SIZE; }
124 int taskIconRight() const { return taskIconLeft() + taskIconWidth(); }
125 QRect taskIcon() const { return QRect(taskIconLeft(), top(), taskIconWidth(), taskIconHeight()); }
126
127 int textAreaLeft() const { return taskIconRight() + ITEM_SPACING; }
128 int textAreaWidth() const { return textAreaRight() - textAreaLeft(); }
129 int textAreaRight() const { return timeAreaLeft() - ITEM_SPACING; }
130 QRect textArea() const { return QRect(textAreaLeft(), top(), textAreaWidth(), firstLineHeight()); }
131
132 int timeAreaLeft() const { return timeAreaRight() - timeAreaWidth(); }
133 int timeAreaWidth() const { return maxLineLength*5; }
134 int timeAreaRight() const { return durationAreaLeft() - ITEM_SPACING; }
135 QRect timeArea() const { return QRect(timeAreaLeft(), top(), timeAreaWidth(), firstLineHeight()); }
136
137 int durationAreaLeft() const { return durationAreaRight() - durationAreaWidth(); }
138 int durationAreaWidth() const { return maxLineLength*2; }
139 int durationAreaRight() const { return returnAreaLeft() - ITEM_SPACING; }
140 QRect durationArea() const { return QRect(durationAreaLeft(), top(), durationAreaWidth(), firstLineHeight()); }
141
142 int returnAreaLeft() const { return returnAreaRight() - returnAreaWidth(); }
143 int returnAreaWidth() const { return maxLineLength*3; }
144 int returnAreaRight() const { return tidAreaLeft() - ITEM_SPACING; }
145 QRect returnArea() const { return QRect(returnAreaLeft(), top(), returnAreaWidth(), firstLineHeight()); }
146
147 int tidAreaLeft() const { return tidAreaRight() - tidAreaWidth(); }
148 int tidAreaWidth() const { return maxLineLength; }
149 int tidAreaRight() const { return threadsAreaLeft() - ITEM_SPACING; }
150 QRect tidArea() const { return QRect(tidAreaLeft(), top(), tidAreaWidth(), firstLineHeight()); }
151
152 int threadsAreaLeft() const { return threadsAreaRight() - threadsAreaWidth(); }
153 int threadsAreaWidth() const { return maxLineLength; }
154 int threadsAreaRight() const { return right(); }
155 QRect threadsArea() const { return QRect(threadsAreaLeft(), top(), threadsAreaWidth(), firstLineHeight()); }
156
157 private:
158 int totalWidth = 0;
159 int maxLineLength = 0;
160 int iTop = 0;
161 int iBottom = 0;
162 int fontHeight = 0;
163
164 static const int TASK_ICON_SIZE = 16;
165 static const int ITEM_MARGIN = 2;
166 static const int ITEM_SPACING = 2 * ITEM_MARGIN;
167 };
168};
169
170TaskView::TaskView(QWidget *parent)
171 : QListView(parent)
172{
173 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
174 setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
175
176 QFontMetrics fm(font());
177 int vStepSize = fm.height() + 3;
178 if (vStepSize < TaskDelegate::Positions::minimumHeight())
179 vStepSize = TaskDelegate::Positions::minimumHeight();
180
181 verticalScrollBar()->setSingleStep(vStepSize);
182}
183
184TaskView::~TaskView()
185{ }
186
187void TaskView::resizeEvent(QResizeEvent *e)
188{
189 Q_UNUSED(e)
190 static_cast<TaskDelegate *>(itemDelegate())->emitSizeHintChanged(selectionModel()->currentIndex());
191}
192
193void TaskView::contextMenuEvent(QContextMenuEvent* event)
194{
195 // disable right context-menu
196 Q_UNUSED(event)
197}
198
199/////
200// TaskWindow
201/////
202
203#define TIMELINE_WIDGET_HEIGHT 60
204#define STR_CURRENT_EVENT tr(" Current Event [")
205
206class TaskWindowPrivate
207{
208public:
209 Internal::TaskModel *taskModel = nullptr;
210 Internal::TaskFilterModel *filter = nullptr;
211 Internal::TaskView *listview = nullptr;
212 Internal::TimelineWidget *timeline = nullptr;
213 Internal::TaskWidget *widget = nullptr;
214 QMenu *contextMenu = nullptr;
215 QToolButton *categoriesButton = nullptr;
216 QLabel* eventLabel = nullptr;
217 QLineEdit* commandLine = nullptr;
218 QComboBox* sortCombo = nullptr;
219 QPushButton* zoomIn = nullptr;
220 QPushButton* zoomOut = nullptr;
221 QPushButton* zoomFit = nullptr;
222 QPushButton* backward = nullptr;
223 QPushButton* forward = nullptr;
224 QMenu *categoriesMenu = nullptr;
225 QList<QAction *> actions;
226 int currentEvent = -1;
227};
228
229TaskWindow::TaskWindow()
230 : d(new TaskWindowPrivate)
231{
232 setupUi();
233}
234
235TaskWindow::~TaskWindow()
236{
237 delete d->widget;
238 delete d->filter;
239 delete d->taskModel;
240 delete d;
241}
242
243void TaskWindow::delayedInitialization()
244{
245}
246
247int TaskWindow::taskCount(const QString &category) const
248{
249 return d->taskModel->taskCount(category);
250}
251
252QWidget *TaskWindow::outputWidget() const
253{
254 return d->widget;
255}
256
257QList<QWidget *> TaskWindow::toolBarWidgets() const
258{
259 return QList<QWidget*>() << d->categoriesButton
260 << d->zoomIn
261 << d->zoomOut
262 << d->zoomFit
263 << d->backward
264 << d->forward
265 << d->sortCombo
266 << d->commandLine
267 << d->eventLabel;
268}
269
270int TaskWindow::priorityInStatusBar() const
271{
272 return 90;
273}
274
275void TaskWindow::clearContents()
276{
277 // clear all tasks in all displays
278 // Yeah we are that special
279 d->taskModel->clearTasks();
280 d->eventLabel->setText(STR_CURRENT_EVENT + QLatin1String("...]"));
281 emit tasksCleared();
282}
283
284void TaskWindow::visibilityChanged(bool visible)
285{
286 if (visible)
287 delayedInitialization();
288}
289
290bool TaskWindow::canFocus() const
291{
292 return d->filter->rowCount();
293}
294
295bool TaskWindow::hasFocus() const
296{
297 return d->listview->window()->focusWidget() == d->listview;
298}
299
300void TaskWindow::setFocus()
301{
302 if (d->filter->rowCount()) {
303 d->listview->setFocus();
304 if (d->listview->currentIndex() == QModelIndex())
305 d->listview->setCurrentIndex(d->filter->index(0,0, QModelIndex()));
306 }
307}
308
309bool TaskWindow::canNavigate() const
310{
311 return true;
312}
313
314bool TaskWindow::canNext() const
315{
316 return d->filter->rowCount();
317}
318
319bool TaskWindow::canPrevious() const
320{
321 return d->filter->rowCount();
322}
323
324void TaskWindow::goToNext()
325{
326 if (d->currentEvent + 1 < d->taskModel->rowCount())
327 goTo(d->currentEvent + 1);
328}
329
330void TaskWindow::goToPrev()
331{
332 if (d->currentEvent > 0) goTo(d->currentEvent - 1);
333}
334
335void TaskWindow::goTo(int index)
336{
337 Task task = d->taskModel->task(index);
338 d->eventLabel->setText( STR_CURRENT_EVENT +
339 task.description + QLatin1Char(']'));
340 d->currentEvent = index;
341 emit coredumpChanged(index);
342}
343
344void TaskWindow::addTask(const Task &task)
345{
346 d->taskModel->addTask(task);
347
348 emit tasksChanged();
349 navigateStateChanged();
350}
351
352void TaskWindow::removeTask(const Task &task)
353{
354 d->taskModel->removeTask(task);
355
356 emit tasksChanged();
357 navigateStateChanged();
358}
359
360void TaskWindow::addCategory(const QString &categoryId, const QString &displayName, bool visible)
361{
362 d->taskModel->addCategory(categoryId, displayName);
363 if (!visible) {
364 QList<QString> filters = d->filter->filteredCategories();
365 filters += categoryId;
366 d->filter->setFilteredCategories(filters);
367 d->timeline->setFilteredCategories(filters);
368 }
369}
370
371void TaskWindow::updateTimeline(void *timeline, int count)
372{
373 d->timeline->setData(this, timeline, count);
374
375 if (!timeline || !count) {
376 d->taskModel->clearTasks();
377 d->eventLabel->setText(STR_CURRENT_EVENT + QLatin1String("...]"));
378 }
379}
380
381static bool get_range(const QString& range, int* begin, int* end)
382{
383 bool ok;
384 int pos = range.indexOf(QLatin1Char(','));
385 if (pos > 0) {
386 *begin = range.left(pos).toInt(&ok, 10);
387 if (!ok) return false;
388 *end = range.mid(pos+1).toInt(&ok, 10);
389 if (!ok) return false;
390 }
391 else {
392 *begin = range.toInt(&ok, 10);
393 if (!ok) return false;
394 *end = *begin;
395 }
396
397 return (*begin >= 0 && *begin <= *end);
398}
399
400void TaskWindow::execCommand(void)
401{
402 int begin = -1, end = -1;
403 QString cmd = d->commandLine->text();
404
405 if (0 == cmd.indexOf(QLatin1String("sys "))) {
406 if (get_range(cmd.mid(4), &begin, &end)) {
407 d->filter->setEventRange(begin, end);
408 d->timeline->setEventRange(begin, end);
409 }
410 }
411 else if (0 == cmd.indexOf(QLatin1String("sig "))) {
412 if (get_range(cmd.mid(4), &begin, &end)) {
413 d->filter->setEventRange(
414 begin + DUMP_REASON_signal,
415 end + DUMP_REASON_signal);
416 d->timeline->setEventRange(
417 begin + DUMP_REASON_signal,
418 end + DUMP_REASON_signal);
419 }
420 }
421 else if (0 == cmd.indexOf(QLatin1String("x11 "))) {
422 if (get_range(cmd.mid(4), &begin, &end)) {
423 d->filter->setEventRange(
424 begin + DUMP_REASON_x11,
425 end + DUMP_REASON_x11);
426 d->timeline->setEventRange(
427 begin + DUMP_REASON_x11,
428 end + DUMP_REASON_x11);
429 }
430 }
431 else if (0 == cmd.indexOf(QLatin1String("dbus "))) {
432 if (get_range(cmd.mid(5), &begin, &end)) {
433 d->filter->setEventRange(
434 begin + DUMP_REASON_dbus,
435 end + DUMP_REASON_dbus);
436 d->timeline->setEventRange(
437 begin + DUMP_REASON_dbus,
438 end + DUMP_REASON_dbus);
439 }
440 }
441 else if (0 == cmd.indexOf(QLatin1String("list "))) {
442 if (get_range(cmd.mid(5), &begin, &end)) {
443 d->filter->setEventIndexRange(begin, end);
444 d->timeline->setEventIndexRange(begin, end);
445 }
446 }
447 else if (0 == cmd.indexOf(QLatin1String("tid "))) {
448 if (get_range(cmd.mid(4), &begin, &end)) {
449 d->filter->setEventTid(begin);
450 d->timeline->setEventTid(begin);
451 }
452 }
453 else if (cmd.isEmpty()) {
454 d->filter->setEventIndexRange(-1, -1);
455 d->timeline->setEventIndexRange(-1, -1);
456 }
457}
458
459void TaskWindow::sortEvent(int index)
460{
461 d->filter->setSortType(index);
462}
463
464void TaskWindow::setupUi()
465{
466 d->taskModel = new Internal::TaskModel(this);
467 d->filter = new Internal::TaskFilterModel(d->taskModel);
468 d->listview = new Internal::TaskView;
469
470 d->listview->setModel(d->filter);
471 d->listview->setFrameStyle(QFrame::NoFrame);
472 d->listview->setWindowTitle(tr("Events list"));
473 d->listview->setSelectionMode(QAbstractItemView::SingleSelection);
474 Internal::TaskDelegate *tld = new Internal::TaskDelegate(this);
475 d->listview->setItemDelegate(tld);
476 d->listview->setContextMenuPolicy(Qt::DefaultContextMenu);
477 d->listview->setAttribute(Qt::WA_MacShowFocusRect, false);
478
479 connect(d->listview->selectionModel(), &QItemSelectionModel::currentChanged,
480 tld, &TaskDelegate::currentChanged);
481
482 connect(d->listview->selectionModel(), &QItemSelectionModel::currentChanged,
483 this, &TaskWindow::currentChanged);
484 connect(d->listview, &QAbstractItemView::activated,
485 this, &TaskWindow::triggerDefaultHandler);
486 connect(d->listview, &QAbstractItemView::clicked,
487 this, &TaskWindow::clickItem);
488
489 d->contextMenu = new QMenu(d->listview);
490
491 d->widget = new TaskWidget();
492 d->listview->setParent(d->widget);
493 d->timeline = new TimelineWidget(d->widget);
494 d->timeline->setMinimumHeight(TIMELINE_WIDGET_HEIGHT);
495 d->widget->setup(d->timeline, d->listview);
496 d->taskModel->setTimelinePtr(d->timeline); // mozart added.
497
498 d->sortCombo = new QComboBox();
499 d->sortCombo->addItem(tr("sort by index"));
500 d->sortCombo->addItem(tr("sort by duration"));
501 d->sortCombo->addItem(tr("sort by result"));
502 d->sortCombo->addItem(tr("sort by number of threads"));
503 connect(d->sortCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(sortEvent(int)));
504
505 d->commandLine = new QLineEdit();
506 d->commandLine->setPlaceholderText("[sys sig x11] begin[,end]");
507 connect(d->commandLine, SIGNAL(returnPressed()), this, SLOT(execCommand()));
508
509 d->zoomIn = new QPushButton(tr("zoom in"));
510 connect(d->zoomIn, &QAbstractButton::clicked, d->timeline, &TimelineWidget::zoomIn);
511
512 d->zoomOut = new QPushButton(tr("zoom out"));
513 connect(d->zoomOut, &QAbstractButton::clicked, d->timeline, &TimelineWidget::zoomOut);
514
515 d->zoomFit = new QPushButton(tr("zoom fit"));
516 connect(d->zoomFit, &QAbstractButton::clicked, d->timeline, &TimelineWidget::zoomFit);
517
518 d->zoomIn->setFlat(true);
519 d->zoomOut->setFlat(true);
520 d->zoomFit->setFlat(true);
521
522 d->backward = new QPushButton();
523 auto backIcon = QIcon(":/resource/images/backward_press@2x");
524 d->backward->setIcon(backIcon);
525 d->backward->setFlat(true);
526 d->backward->setToolTip(tr("Previous Item"));
527 d->backward->setFixedSize(backIcon.actualSize(backIcon.availableSizes().first()));
528
529 connect(d->backward, &QAbstractButton::clicked, this, &TaskWindow::goToNext);
530
531 d->forward = new QPushButton();
532 auto forwardIcon = QIcon(":/resource/images/forward_press@2x");
533 d->forward->setIcon(forwardIcon);
534 d->forward->setFlat(true);
535 d->forward->setToolTip(tr("Next Item"));
536 d->forward->setFixedSize(backIcon.actualSize(backIcon.availableSizes().first()));
537 connect(d->forward, &QAbstractButton::clicked, this, &TaskWindow::goToPrev);
538
539 d->eventLabel = new QLabel(STR_CURRENT_EVENT + QLatin1String("...]"));
540 d->categoriesButton = new QToolButton;
541 d->categoriesButton->setIcon(QIcon(":/resource/images/filter_normal"));
542 d->categoriesButton->setToolTip(tr("Filter by categories"));
543 d->categoriesButton->setProperty("noArrow", true);
544 d->categoriesButton->setAutoRaise(true);
545 d->categoriesButton->setPopupMode(QToolButton::InstantPopup);
546
547 d->categoriesMenu = new QMenu(d->categoriesButton);
548 connect(d->categoriesMenu, &QMenu::aboutToShow, this, &TaskWindow::updateCategoriesMenu);
549
550 d->categoriesButton->setMenu(d->categoriesMenu);
551
552 connect(d->filter, &TaskFilterModel::rowsRemoved,
553 [this]() { emit setBadgeNumber(d->filter->rowCount()); });
554 connect(d->filter, &TaskFilterModel::rowsInserted,
555 [this]() { emit setBadgeNumber(d->filter->rowCount()); });
556 connect(d->filter, &TaskFilterModel::modelReset,
557 [this]() { emit setBadgeNumber(d->filter->rowCount()); });
558}
559
560void TaskWindow::showTask(unsigned int id)
561{
562 int sourceRow = d->taskModel->rowForId(id);
563 QModelIndex sourceIdx = d->taskModel->index(sourceRow, 0);
564 QModelIndex filterIdx = d->filter->mapFromSource(sourceIdx);
565 d->listview->setCurrentIndex(filterIdx);
566// popup(Core::IOutputPane::ModeSwitch);
567}
568
569void TaskWindow::openTask(unsigned int id)
570{
571 int sourceRow = d->taskModel->rowForId(id);
572 QModelIndex sourceIdx = d->taskModel->index(sourceRow, 0);
573 QModelIndex filterIdx = d->filter->mapFromSource(sourceIdx);
574 triggerDefaultHandler(filterIdx);
575}
576
577void TaskWindow::clearTasks(const QString &categoryId)
578{
579 d->taskModel->clearTasks(categoryId);
580
581 emit tasksChanged();
582 emit tasksCleared();
583 navigateStateChanged();
584}
585
586void TaskWindow::setCategoryVisibility(const QString &categoryId, bool visible)
587{
588 if (categoryId.isEmpty())
589 return;
590
591 QList<QString> categories = d->filter->filteredCategories();
592
593 if (visible)
594 categories.removeOne(categoryId);
595 else
596 categories.append(categoryId);
597
598 d->filter->setFilteredCategories(categories);
599 d->timeline->setFilteredCategories(categories);
600}
601
602void TaskWindow::currentChanged(const QModelIndex &index)
603{
604 qDebug() << "currentChanged " << index.row();
605}
606
607void TaskWindow::saveSettings()
608{
609 // do something.
610}
611
612void TaskWindow::loadSettings()
613{
614 // do something.
615}
616
617void TaskWindow::triggerDefaultHandler(const QModelIndex &index)
618{
619 bool ok;
620 int i = 0;
621 Task task = d->filter->task(index);
622 for (; i < task.description.size(); ++i) {
623 if (task.description[i] > QLatin1Char('9')) {
624 break;
625 }
626 }
627
628 if (i > 0) {
629 i = task.description.left(i).toInt(&ok, 10);
630 if (ok) {
631 d->eventLabel->setText( STR_CURRENT_EVENT +
632 task.description + QLatin1Char(']'));
633 d->currentEvent = i;
634 emit coredumpChanged(i);
635 }
636 }
637}
638
639void TaskWindow::clickItem(const QModelIndex &index)
640{
641 qDebug() << "clickItem " << index.row();
642}
643
644void TaskWindow::actionTriggered()
645{
646 // do something.
647}
648
649void TaskWindow::updateCategoriesMenu()
650{
651 typedef QMap<QString, QString>::ConstIterator NameToIdsConstIt;
652
653 d->categoriesMenu->clear();
654
655 const QList<QString> filteredCategories = d->filter->filteredCategories();
656
657 QMap<QString, QString> nameToIds;
658 foreach (QString categoryId, d->taskModel->categoryIds())
659 nameToIds.insert(d->taskModel->categoryDisplayName(categoryId), categoryId);
660
661 const NameToIdsConstIt cend = nameToIds.constEnd();
662 for (NameToIdsConstIt it = nameToIds.constBegin(); it != cend; ++it) {
663 const QString &displayName = it.key();
664 const QString categoryId = it.value();
665 QAction *action = new QAction(d->categoriesMenu);
666 action->setCheckable(true);
667 action->setText(displayName);
668 action->setChecked(!filteredCategories.contains(categoryId));
669 connect(action, &QAction::triggered, this, [this, action, categoryId] {
670 setCategoryVisibility(categoryId, action->isChecked());
671 });
672 d->categoriesMenu->addAction(action);
673 }
674}
675
676/////
677// Delegate
678/////
679TaskDelegate::TaskDelegate(QObject *parent) :
680 QStyledItemDelegate(parent),
681 m_cachedHeight(0)
682{ }
683
684TaskDelegate::~TaskDelegate()
685{
686}
687
688QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
689{
690 QStyleOptionViewItem opt = option;
691 initStyleOption(&opt, index);
692
693 const QAbstractItemView * view = qobject_cast<const QAbstractItemView *>(opt.widget);
694 const bool selected = (view->selectionModel()->currentIndex() == index);
695 QSize s;
696 s.setWidth(option.rect.width());
697
698 if (!selected && option.font == m_cachedFont && m_cachedHeight > 0) {
699 s.setHeight(m_cachedHeight);
700 return s;
701 }
702
703 QFontMetrics fm(option.font);
704 int fontHeight = fm.height();
705 int fontLeading = fm.leading();
706
707 TaskModel *model = static_cast<TaskFilterModel *>(view->model())->taskModel();
708 Positions positions(option, model);
709
710 if (selected) {
711 QString description = index.data(TaskModel::Description).toString();
712 description += QLatin1Char('\n');
713 description += index.data(TaskModel::ExtraInfo).toString();
714 // Layout the description
715 int leading = fontLeading;
716 int height = 0;
717 description.replace(QLatin1Char('\n'), QChar::LineSeparator);
718 QTextLayout tl(description);
719 // TODO: tl.setAdditionalFormats(...);
720 tl.beginLayout();
721 while (true) {
722 QTextLine line = tl.createLine();
723 if (!line.isValid())
724 break;
725 line.setLineWidth(positions.textAreaWidth());
726 height += leading;
727 line.setPosition(QPoint(0, height));
728 height += static_cast<int>(line.height());
729 }
730 tl.endLayout();
731
732 s.setHeight(height + leading + fontHeight + 3);
733 } else {
734 s.setHeight(fontHeight + 3);
735 }
736 if (s.height() < positions.minimumHeight())
737 s.setHeight(positions.minimumHeight());
738
739 if (!selected) {
740 m_cachedHeight = s.height();
741 m_cachedFont = option.font;
742 }
743
744 return s;
745}
746
747void TaskDelegate::emitSizeHintChanged(const QModelIndex &index)
748{
749 emit sizeHintChanged(index);
750}
751
752void TaskDelegate::currentChanged(const QModelIndex &current, const QModelIndex &previous)
753{
754 emit sizeHintChanged(current);
755 emit sizeHintChanged(previous);
756}
757
758void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
759{
760 QStyleOptionViewItem opt = option;
761 initStyleOption(&opt, index);
762 painter->save();
763
764 QFontMetrics fm(opt.font);
765 QColor backgroundColor;
766 QColor textColor;
767
768 const QAbstractItemView * view = qobject_cast<const QAbstractItemView *>(opt.widget);
769 bool selected = view->selectionModel()->currentIndex() == index;
770
771 if (selected) {
772 painter->setBrush(opt.palette.highlight().color());
773 backgroundColor = opt.palette.highlight().color();
774 } else {
775 painter->setBrush(opt.palette.window().color());
776 backgroundColor = opt.palette.window().color();
777 }
778 painter->setPen(Qt::NoPen);
779 painter->drawRect(opt.rect);
780
781 // Set Text Color
782 if (selected)
783 textColor = opt.palette.highlightedText().color();
784 else
785 textColor = opt.palette.text().color();
786
787 painter->setPen(textColor);
788
789 TaskModel *model = static_cast<TaskFilterModel *>(view->model())->taskModel();
790 Positions positions(opt, model);
791
792 // Paint TaskIconArea:
793 QIcon icon = index.data(TaskModel::Icon).value<QIcon>();
794 painter->drawPixmap(positions.left(), positions.top(),
795 icon.pixmap(positions.taskIconWidth(), positions.taskIconHeight()));
796
797 // Paint TextArea:
798 if (!selected) {
799 // in small mode we lay out differently
800 QString bottom = index.data(TaskModel::Description).toString();
801 painter->setClipRect(positions.textArea());
802 painter->drawText(positions.textAreaLeft(), positions.top() + fm.ascent(), bottom);
803 if (fm.horizontalAdvance(bottom) > positions.textAreaWidth()) {
804 // draw a gradient to mask the text
805 int gradientStart = positions.textAreaRight() - ELLIPSIS_GRADIENT_WIDTH + 1;
806 QLinearGradient lg(gradientStart, 0, gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0);
807 lg.setColorAt(0, Qt::transparent);
808 lg.setColorAt(1, backgroundColor);
809 painter->fillRect(gradientStart, positions.top(), ELLIPSIS_GRADIENT_WIDTH, positions.firstLineHeight(), lg);
810 }
811 } else {
812 // Description
813 QString description = index.data(TaskModel::Description).toString();
814 description += QLatin1Char('\n');
815 description += index.data(TaskModel::ExtraInfo).toString();
816
817 // Layout the description
818 int leading = fm.leading();
819 int height = 0;
820 description.replace(QLatin1Char('\n'), QChar::LineSeparator);
821 QTextLayout tl(description);
822 // TODO: tl.setAdditionalFormats(...);
823 tl.beginLayout();
824 while (true) {
825 QTextLine line = tl.createLine();
826 if (!line.isValid())
827 break;
828 line.setLineWidth(positions.textAreaWidth());
829 height += leading;
830 line.setPosition(QPoint(0, height));
831 height += static_cast<int>(line.height());
832 }
833 tl.endLayout();
834 tl.draw(painter, QPoint(positions.textAreaLeft(), positions.top()));
835
836 QColor mix;
837 mix.setRgb( static_cast<int>(0.7 * textColor.red() + 0.3 * backgroundColor.red()),
838 static_cast<int>(0.7 * textColor.green() + 0.3 * backgroundColor.green()),
839 static_cast<int>(0.7 * textColor.blue() + 0.3 * backgroundColor.blue()));
840 painter->setPen(mix);
841 }
842 painter->setPen(textColor);
843
844 // Paint time
845 double curtime = index.data(TaskModel::Time).toDouble();
846 time_t seconds = static_cast<time_t>(curtime/1000.0);
847 struct tm* cur_tm = localtime(&seconds);
848 QString result;
849 if (cur_tm) {
850 result = QString::asprintf("%d/%02d/%02d %02d:%02d:%02d.%03d",
851 cur_tm->tm_year + 1900, cur_tm->tm_mon + 1, cur_tm->tm_mday,
852 cur_tm->tm_hour, cur_tm->tm_min, cur_tm->tm_sec,
853 int(curtime - seconds*1000.0));
854 }
855 int realWidth = fm.horizontalAdvance(result);
856 painter->setClipRect(positions.timeArea());
857 painter->drawText(qMin(positions.timeAreaLeft(), positions.timeAreaRight() - realWidth),
858 positions.top() + fm.ascent(), result);
859
860 // Paint duration
861 double duration = index.data(TaskModel::Duration).toDouble();
862 result = QString::asprintf("%.3f ms", duration);
863 realWidth = fm.horizontalAdvance(result);
864 painter->setClipRect(positions.durationArea());
865 painter->drawText(qMin(positions.durationAreaLeft(), positions.durationAreaRight() - realWidth),
866 positions.top() + fm.ascent(), result);
867
868 // Paint syscall return
869 long syscall = index.data(TaskModel::Return).toLongLong();
870 if (syscall < 0xffff) {
871 result = QString::asprintf("%ld", syscall);
872 }
873 else {
874 result = QString::asprintf("%p", (void*)syscall);
875 }
876 realWidth = fm.horizontalAdvance(result);
877 painter->setClipRect(positions.returnArea());
878 painter->drawText(qMin(positions.returnAreaLeft(), positions.returnAreaRight() - realWidth),
879 positions.top() + fm.ascent(), result);
880 if (realWidth > positions.returnAreaWidth()) {
881 // draw a gradient to mask the text
882 int gradientStart = positions.returnAreaLeft() - 1;
883 QLinearGradient lg(gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0, gradientStart, 0);
884 lg.setColorAt(0, Qt::transparent);
885 lg.setColorAt(1, backgroundColor);
886 painter->fillRect(gradientStart, positions.top(), ELLIPSIS_GRADIENT_WIDTH, positions.firstLineHeight(), lg);
887 }
888
889 // Paint current tid
890 int tid = index.data(TaskModel::Tid).toInt();
891 result = QString::number(tid);
892 painter->setClipRect(positions.tidArea());
893 realWidth = fm.horizontalAdvance(result);
894 painter->drawText(positions.tidAreaRight() - realWidth, positions.top() + fm.ascent(), result);
895
896 // Paint thread num
897 int threads = index.data(TaskModel::ThreadNum).toInt();
898 result = QString::number(threads);
899 painter->setClipRect(positions.threadsArea());
900 realWidth = fm.horizontalAdvance(result);
901 painter->drawText(positions.threadsAreaRight() - realWidth, positions.top() + fm.ascent(), result);
902
903 painter->setClipRect(opt.rect);
904
905 // Separator lines
906 painter->setPen(QColor::fromRgb(150,150,150));
907 painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
908 painter->restore();
909}
910
911#include "taskwindow.moc"
912} // namespace Internal
913} // namespace ReverseDebugger
914