]> git.cworth.org Git - apitrace/blob - gui/timelinewidget.cpp
b6a52f875980322c8306ef8ead5341b8f4583057
[apitrace] / gui / timelinewidget.cpp
1 #include "timelinewidget.h"
2 #include "trace_profiler.hpp"
3
4 #include <math.h>
5 #include <QColor>
6 #include <QPainter>
7 #include <QToolTip>
8 #include <QMouseEvent>
9 #include <QWheelEvent>
10 #include <QApplication>
11
12 typedef trace::Profile::Call Call;
13 typedef trace::Profile::Frame Frame;
14 typedef trace::Profile::Program Program;
15
16 TimelineWidget::TimelineWidget(QWidget *parent)
17     : QWidget(parent),
18       m_profile(NULL),
19       m_timeSelectionStart(0),
20       m_timeSelectionEnd(0),
21       m_rowHeight(20),
22       m_axisWidth(50),
23       m_axisHeight(30),
24       m_axisLine(QColor(240, 240, 240)),
25       m_axisBorder(Qt::black),
26       m_axisForeground(Qt::black),
27       m_axisBackground(QColor(210, 210, 210)),
28       m_itemBorder(Qt::red),
29       m_itemForeground(Qt::cyan),
30       m_itemBackground(Qt::red),
31       m_selectionBorder(Qt::green),
32       m_selectionBackground(QColor(100, 255, 100, 8)),
33       m_zoomBorder(QColor(255, 0, 255)),
34       m_zoomBackground(QColor(255, 0, 255, 30))
35 {
36     setBackgroundRole(QPalette::Base);
37     setAutoFillBackground(true);
38     setMouseTracking(true);
39 }
40
41
42 /**
43  * Update horizontal view scroll based on scroll value
44  */
45 void TimelineWidget::setHorizontalScrollValue(int scrollValue)
46 {
47     if (!m_profile) {
48         return;
49     }
50
51     /* Calculate time from scroll value */
52     double time = scrollValue;
53     time /= m_maxScrollX;
54     time *= (m_timeMax - m_timeWidth) - m_timeMin;
55     time += m_timeMin;
56
57     setTimeScroll(time, false);
58 }
59
60
61 /**
62  * Update vertical view scroll based on scroll value
63  */
64 void TimelineWidget::setVerticalScrollValue(int value)
65 {
66     if (!m_profile) {
67         return;
68     }
69
70     setRowScroll(value, false);
71 }
72
73
74 /**
75  * Update the time selection
76  */
77 void TimelineWidget::setSelection(int64_t start, int64_t end, bool notify)
78 {
79     m_timeSelectionStart = start;
80     m_timeSelectionEnd = end;
81
82     if (notify) {
83         emit selectionChanged(m_timeSelectionStart, m_timeSelectionEnd);
84     }
85
86     update();
87 }
88
89
90 /**
91  * Convert time to view position
92  */
93 double TimelineWidget::timeToPosition(int64_t time)
94 {
95     double pos = time;
96     pos -= m_time;
97     pos /= m_timeWidth;
98     pos *= m_viewWidth;
99     return pos;
100 }
101
102
103 /**
104  * Convert view position to time
105  */
106 int64_t TimelineWidget::positionToTime(int pos)
107 {
108     double time = pos;
109     time /= m_viewWidth;
110     time *= m_timeWidth;
111     time += m_time;
112     return (int64_t)time;
113 }
114
115
116 /**
117  * Binary Search for a time in start+durations
118  */
119 template<typename val_ty, int64_t val_ty::* mem_ptr_start, int64_t val_ty::* mem_ptr_dura>
120 typename std::vector<val_ty>::const_iterator binarySearchTimespan(
121         typename std::vector<val_ty>::const_iterator begin,
122         typename std::vector<val_ty>::const_iterator end,
123         int64_t time)
124 {
125     int lower = 0;
126     int upper = end - begin;
127     int pos = (lower + upper) / 2;
128     typename std::vector<val_ty>::const_iterator itr = begin + pos;
129
130     while (!((*itr).*mem_ptr_start <= time && (*itr).*mem_ptr_start + (*itr).*mem_ptr_dura > time) && (lower <= upper)) {
131         if ((*itr).*mem_ptr_start > time) {
132             upper = pos - 1;
133         } else {
134             lower = pos + 1;
135         }
136
137         pos = (lower + upper) / 2;
138         itr = begin + pos;
139     }
140
141     if (lower <= upper) {
142         return itr;
143     } else {
144         return end;
145     }
146 }
147
148
149 /**
150  * Binary Search for a time in start+durations on an array of indices
151  */
152 std::vector<unsigned>::const_iterator binarySearchTimespanIndexed(
153         const std::vector<Call>& calls,
154         std::vector<unsigned>::const_iterator begin,
155         std::vector<unsigned>::const_iterator end,
156         int64_t time)
157 {
158     int lower = 0;
159     int upper = end - begin;
160     int pos = (lower + upper) / 2;
161     std::vector<unsigned>::const_iterator itr = begin + pos;
162
163     while (lower <= upper) {
164         const Call& call = calls[*itr];
165
166         if (call.gpuStart <= time && call.gpuStart + call.gpuDuration > time) {
167             break;
168         }
169
170         if (call.gpuStart > time) {
171             upper = pos - 1;
172         } else {
173             lower = pos + 1;
174         }
175
176         pos = (lower + upper) / 2;
177         itr = begin + pos;
178     }
179
180     if (lower <= upper) {
181         return itr;
182     } else {
183         return end;
184     }
185 }
186
187
188 /**
189  * Find the frame at time
190  */
191 const Frame* TimelineWidget::frameAtTime(int64_t time)
192 {
193     if (!m_profile) {
194         return NULL;
195     }
196
197     std::vector<Frame>::const_iterator res
198             = binarySearchTimespan<Frame, &Frame::cpuStart, &Frame::cpuDuration>(
199                 m_profile->frames.begin(),
200                 m_profile->frames.end(),
201                 time);
202
203     if (res != m_profile->frames.end()) {
204         return &*res;
205     }
206
207     return NULL;
208 }
209
210
211 /**
212  * Find the CPU call at time
213  */
214 const Call* TimelineWidget::cpuCallAtTime(int64_t time)
215 {
216     if (!m_profile) {
217         return NULL;
218     }
219
220     std::vector<Call>::const_iterator res
221             = binarySearchTimespan<Call, &Call::cpuStart, &Call::cpuDuration>(
222                 m_profile->calls.begin(),
223                 m_profile->calls.end(),
224                 time);
225
226     if (res != m_profile->calls.end()) {
227         return &*res;
228     }
229
230     return NULL;
231 }
232
233
234 /**
235  * Find the draw call at time
236  */
237 const Call* TimelineWidget::drawCallAtTime(int64_t time, int program)
238 {
239     if (!m_profile) {
240         return NULL;
241     }
242
243     std::vector<unsigned>::const_iterator res
244             = binarySearchTimespanIndexed(
245                 m_profile->calls,
246                 m_profile->programs[program].calls.begin(),
247                 m_profile->programs[program].calls.end(),
248                 time);
249
250     if (res != m_profile->programs[program].calls.end()) {
251         return &m_profile->calls[*res];
252     }
253
254     return NULL;
255 }
256
257
258 /**
259  * Calculate the row order by total gpu time per shader
260  */
261 void TimelineWidget::calculateRows()
262 {
263     typedef QPair<uint64_t, unsigned> Pair;
264     std::vector<Pair> gpu;
265
266     /* Map shader to visible row */
267     for (std::vector<Program>::const_iterator itr = m_profile->programs.begin(); itr != m_profile->programs.end(); ++itr) {
268         const Program& program = *itr;
269         unsigned no = itr -  m_profile->programs.begin();
270
271         if (program.gpuTotal > 0) {
272             gpu.push_back(Pair(program.gpuTotal, no));
273         }
274     }
275
276     /* Sort the shaders by most used gpu */
277     qSort(gpu);
278
279     /* Create row order */
280     m_rowPrograms.clear();
281
282     for (std::vector<Pair>::const_reverse_iterator itr = gpu.rbegin(); itr != gpu.rend(); ++itr) {
283         m_rowPrograms.push_back(itr->second);
284     }
285
286     m_rowCount = m_rowPrograms.size();
287 }
288
289
290 /**
291  * Set the trace profile to use for the timeline
292  */
293 void TimelineWidget::setProfile(trace::Profile* profile)
294 {
295     if (!profile->frames.size())
296         return;
297
298     m_profile = profile;
299     calculateRows();
300
301     m_timeMin = m_profile->frames.front().cpuStart;
302     m_timeMax = m_profile->frames.back().cpuStart + m_profile->frames.back().cpuDuration;
303
304     m_time = m_timeMin;
305     m_timeWidth = m_timeMax - m_timeMin;
306
307     m_timeWidthMin = 1000;
308     m_timeWidthMax = m_timeWidth;
309
310     m_maxScrollX = 0;
311     m_maxScrollY = qMax(0, (m_rowCount * m_rowHeight) - m_viewHeight);
312
313     setTimeScroll(m_time);
314     setRowScroll(0);
315
316     update();
317 }
318
319
320 /**
321  * Set the horizontal scroll position to time
322  */
323 void TimelineWidget::setTimeScroll(int64_t time, bool notify)
324 {
325     time = qBound(m_timeMin, time, m_timeMax - m_timeWidth);
326
327     m_time = time;
328
329     if (m_timeWidth == m_timeWidthMax) {
330         m_maxScrollX = 0;
331     } else {
332         m_maxScrollX = 10000;
333     }
334
335     if (notify) {
336         double value = time - m_timeMin;
337         value /= m_timeMax - m_timeWidth - m_timeMin;
338         value *= m_maxScrollX;
339         m_scrollX = value;
340
341         emit horizontalScrollMaxChanged(m_maxScrollX);
342         emit horizontalScrollValueChanged(m_scrollX);
343     }
344
345     update();
346 }
347
348
349 /**
350  * Set the vertical scroll position to position
351  */
352 void TimelineWidget::setRowScroll(int position, bool notify)
353 {
354     position = qBound(0, position, m_maxScrollY);
355
356     m_scrollY = position;
357     m_row  = m_scrollY / m_rowHeight;
358
359     if (notify) {
360         emit verticalScrollMaxChanged(m_maxScrollY);
361         emit verticalScrollValueChanged(m_scrollY);
362     }
363
364     update();
365 }
366
367
368 void TimelineWidget::resizeEvent(QResizeEvent *e)
369 {
370     /* Update viewport size */
371     m_viewWidth = qMax(0, width() - m_axisWidth);
372     m_viewHeight = qMax(0, height() - m_axisHeight - m_rowHeight);
373
374     /* Update vertical scroll bar */
375     if (m_profile) {
376         m_maxScrollY = qMax(0, (m_rowCount * m_rowHeight) - m_viewHeight);
377         emit verticalScrollMaxChanged(m_maxScrollY);
378         setRowScroll(m_scrollY);
379     }
380 }
381
382
383 void TimelineWidget::mouseMoveEvent(QMouseEvent *e)
384 {
385     bool tooltip = false;
386     m_mousePosition = e->pos();
387
388     if (!m_profile) {
389         return;
390     }
391
392     /* Display tooltip if necessary */
393     if (e->buttons() == Qt::NoButton) {
394         if (m_mousePosition.x() > m_axisWidth && m_mousePosition.y() > m_axisHeight) {
395             int64_t time = positionToTime(m_mousePosition.x() - m_axisWidth);
396             int y = m_mousePosition.y() - m_axisHeight;
397
398             if (y < m_rowHeight) {
399                 const Call* call = cpuCallAtTime(time);
400
401                 if (call) {
402                     QString text;
403                     text  = QString::fromStdString(call->name);
404                     text += QString("\nCall: %1").arg(call->no);
405                     text += QString("\nCPU Start: %1").arg(call->cpuStart);
406                     text += QString("\nCPU Duration: %1").arg(call->cpuDuration);
407
408                     QToolTip::showText(e->globalPos(), text);
409                     tooltip = true;
410                 }
411             } else {
412                 int row = (y - m_rowHeight + m_scrollY) / m_rowHeight;
413
414                 if (row < m_rowPrograms.size()) {
415                     const Call* call = drawCallAtTime(time, m_rowPrograms[row]);
416
417                     if (call) {
418                         QString text;
419                         text  = QString::fromStdString(call->name);
420                         text += QString("\nCall: %1").arg(call->no);
421                         text += QString("\nGPU Start: %1").arg(call->gpuStart);
422                         text += QString("\nCPU Start: %1").arg(call->cpuStart);
423                         text += QString("\nGPU Duration: %1").arg(call->gpuDuration);
424                         text += QString("\nCPU Duration: %1").arg(call->cpuDuration);
425                         text += QString("\nPixels Drawn: %1").arg(call->pixels);
426
427                         QToolTip::showText(e->globalPos(), text);
428                         tooltip = true;
429                     }
430                 }
431             }
432         }
433     } else if (e->buttons().testFlag(Qt::LeftButton)) {
434         if (m_mousePressMode == DragView) {
435             /* Horizontal scroll */
436             double dt = m_timeWidth;
437             dt /= m_viewWidth;
438             dt *= m_mousePressPosition.x() - e->pos().x();
439             setTimeScroll(m_mousePressTime + dt);
440
441             /* Vertical scroll */
442             int dy = m_mousePressPosition.y() - e->pos().y();
443             setRowScroll(m_mousePressRow + dy);
444         } else if (m_mousePressMode == RulerSelect) {
445             /* Horizontal selection */
446             int64_t down  = positionToTime(m_mousePressPosition.x() - m_axisWidth);
447             int64_t up    = positionToTime(qMax(e->pos().x() - m_axisWidth, 0));
448
449             setSelection(qMin(down, up), qMax(down, up));
450         }
451
452         update();
453     }
454
455     if (!tooltip) {
456         QToolTip::hideText();
457     }
458 }
459
460
461 void TimelineWidget::mousePressEvent(QMouseEvent *e)
462 {
463     if (e->buttons() & Qt::LeftButton) {
464         if (e->pos().y() < m_axisHeight && e->pos().x() >= m_axisWidth) {
465             if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
466                 m_mousePressMode = RulerZoom;
467             } else {
468                 m_mousePressMode = RulerSelect;
469
470                 int64_t time = positionToTime(e->pos().x() - m_axisWidth);
471                 m_timeSelectionStart = time;
472                 m_timeSelectionEnd = time;
473             }
474         } else {
475             m_mousePressMode = DragView;
476         }
477
478         m_mousePressPosition  = e->pos();
479         m_mousePressTime = m_time;
480         m_mousePressRow   = m_scrollY;
481
482         update();
483     }
484 }
485
486
487 void TimelineWidget::mouseReleaseEvent(QMouseEvent *e)
488 {
489     if (!m_profile) {
490         return;
491     }
492
493     /* Calculate new time view based on selected area */
494     int64_t down  = positionToTime(m_mousePressPosition.x() - m_axisWidth);
495     int64_t up    = positionToTime(qMax(e->pos().x() - m_axisWidth, 0));
496
497     int64_t left  = qMin(down, up);
498     int64_t right = qMax(down, up);
499
500     if (m_mousePressMode == RulerZoom) {
501         m_timeWidth = right - left;
502         m_timeWidth = qBound(m_timeWidthMin, m_timeWidth, m_timeWidthMax);
503
504         m_mousePressMode = NoMousePress;
505         setTimeScroll(left);
506     } else if (m_mousePressMode == RulerSelect) {
507         setSelection(m_timeSelectionStart, m_timeSelectionEnd, true);
508     }
509 }
510
511
512 void TimelineWidget::mouseDoubleClickEvent(QMouseEvent *e)
513 {
514     int64_t time = positionToTime(e->pos().x() - m_axisWidth);
515
516     if (e->pos().x() > m_axisWidth) {
517         int row = (e->pos().y() - m_axisHeight) / m_rowHeight;
518
519         if (e->pos().y() < m_axisHeight) {
520             /* Horizontal axis */
521             const Frame* frame = frameAtTime(time);
522
523             if (frame) {
524                 setSelection(frame->cpuStart, frame->cpuStart + frame->cpuDuration, true);
525                 return;
526             }
527         } else if (row == 0) {
528             /* CPU Calls */
529             const Call* call = cpuCallAtTime(time);
530
531             if (call) {
532                 emit jumpToCall(call->no);
533                 return;
534             }
535         } else if (row > 0) {
536             /* Draw Calls */
537             const Call* call = drawCallAtTime(time, 0);
538
539             if (call) {
540                 emit jumpToCall(call->no);
541                 return;
542             }
543         }
544     }
545
546     if (time < m_timeSelectionStart || time > m_timeSelectionEnd) {
547         setSelection(0, 0, true);
548     }
549 }
550
551
552 void TimelineWidget::wheelEvent(QWheelEvent *e)
553 {
554     if (!m_profile) {
555         return;
556     }
557
558     int zoomPercent = 10;
559
560     /* If holding Ctrl key then zoom 2x faster */
561     if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
562         zoomPercent = 20;
563     }
564
565     /* Zoom view by adjusting width */
566     double dt = m_timeWidth;
567     double size = m_timeWidth;
568     size *= -e->delta();
569
570     /* Zoom deltas normally come in increments of 120 */
571     size /= 120 * (100 / zoomPercent);
572
573     m_timeWidth += size;
574     m_timeWidth  = qBound(m_timeWidthMin, m_timeWidth, m_timeWidthMax);
575
576     /* Scroll view to zoom around mouse */
577     dt -= m_timeWidth;
578     dt *= e->x() - m_axisWidth;
579     dt /= m_viewWidth;
580     setTimeScroll(dt + m_time);
581
582     update();
583 }
584
585
586 /**
587  * Paints a single pixel column of the heat map
588  */
589 void TimelineWidget::drawHeat(QPainter& painter, int x, int64_t heat, bool isCpu)
590 {
591     if (heat == 0) {
592         return;
593     }
594
595     double timePerPixel = m_timeWidth;
596     timePerPixel /= m_viewWidth;
597
598     double colour = heat / timePerPixel;
599     colour = qBound(0.0, colour * 255.0, 255.0);
600
601     if (isCpu) {
602         painter.setPen(QColor(255 - colour, 255 - colour, 255));
603     } else {
604         painter.setPen(QColor(255, 255 - colour, 255 - colour));
605     }
606
607     painter.drawLine(x, 0, x, m_rowHeight - 1);
608 }
609
610
611 /**
612  * Render the whole widget
613  */
614 void TimelineWidget::paintEvent(QPaintEvent *e)
615 {
616     if (!m_profile)
617         return;
618
619     QPainter painter(this);
620
621     int rowEnd = qMin(m_row + (m_viewHeight / m_rowHeight) + 1, m_rowCount);
622     int64_t timeEnd = m_time + m_timeWidth;
623     int64_t heat = 0;
624     int lastX = 0;
625     int widgetHeight = height();
626     int widgetWidth = width();
627
628     /* Draw GPU rows */
629     painter.translate(m_axisWidth, m_axisHeight + m_rowHeight - (m_scrollY % m_rowHeight));
630
631     for (int row = m_row; row < rowEnd; ++row) {
632         Program& program = m_profile->programs[m_rowPrograms[row]];
633         lastX = 0;
634         heat = 0;
635
636         for (std::vector<unsigned>::const_iterator itr = program.calls.begin(); itr != program.calls.end(); ++itr) {
637             const Call& call = m_profile->calls[*itr];
638             int64_t gpuEnd = call.gpuStart + call.gpuDuration;
639
640             if (call.gpuStart > timeEnd) {
641                 break;
642             }
643
644             if (gpuEnd < m_time) {
645                 continue;
646             }
647
648             double left  = timeToPosition(call.gpuStart);
649             double right = timeToPosition(gpuEnd);
650
651             int leftX  = left;
652             int rightX = right;
653
654             /* Draw last heat if needed */
655             if (leftX != lastX) {
656                 drawHeat(painter, lastX, heat, false);
657                 lastX = leftX;
658                 heat = 0;
659             }
660
661             if (rightX <= leftX + 1) {
662                 if (rightX == lastX) {
663                     /* Fully contained in this X */
664                     heat += call.gpuDuration;
665                 } else {
666                     /* Split call time between the two pixels it occupies */
667                     int64_t time = positionToTime(rightX);
668
669                     heat += time - call.gpuStart;
670                     drawHeat(painter, lastX, heat, false);
671
672                     heat = gpuEnd - time;
673                     lastX = rightX;
674                 }
675             } else {
676                 QRect rect;
677                 rect.setLeft(left + 0.5);
678                 rect.setWidth(right - left);
679                 rect.setTop(0);
680                 rect.setHeight(m_rowHeight);
681
682                 painter.fillRect(rect, m_itemBackground);
683
684                 if (rect.width() > 6) {
685                     rect.adjust(1, 0, -1, -2);
686                     painter.setPen(m_itemForeground);
687
688                     painter.drawText(rect,
689                                      Qt::AlignLeft | Qt::AlignVCenter,
690                                      painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width()));
691                 }
692             }
693         }
694
695         painter.translate(0, m_rowHeight);
696     }
697
698     /* Draw CPU row */
699     painter.resetTransform();
700     painter.translate(m_axisWidth, m_axisHeight);
701     painter.fillRect(0, 0, m_viewWidth, m_rowHeight, Qt::white);
702
703     for (std::vector<Call>::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) {
704         const Call& call = *itr;
705         int64_t cpuEnd = call.cpuStart + call.cpuDuration;
706
707         if (call.cpuStart > timeEnd) {
708             continue;
709         }
710
711         if (cpuEnd < m_time) {
712             continue;
713         }
714
715         double left = timeToPosition(call.cpuStart);
716         double right = timeToPosition(cpuEnd);
717
718         int leftX = left;
719         int rightX = right;
720
721         /* Draw last heat if needed */
722         if (leftX != lastX) {
723             drawHeat(painter, lastX, heat, true);
724             lastX = leftX;
725             heat = 0;
726         }
727
728         if (rightX <= leftX + 1) {
729             if (rightX == lastX) {
730                 /* Fully contained in this X */
731                 heat += call.cpuDuration;
732             } else {
733                 /* Split call time between the two pixels it occupies */
734                 int64_t time = positionToTime(rightX);
735
736                 heat += time - call.cpuStart;
737                 drawHeat(painter, lastX, heat, true);
738
739                 heat = cpuEnd - time;
740                 lastX = rightX;
741             }
742         } else {
743             QRect rect;
744             rect.setLeft(left + 0.5);
745             rect.setWidth(right - left);
746             rect.setTop(0);
747             rect.setHeight(m_rowHeight);
748
749             painter.fillRect(rect, QColor(0, 0, 255));
750
751             if (rect.width() > 6) {
752                 rect.adjust(1, 0, -1, -2);
753                 painter.setPen(QColor(255, 255, 0));
754
755                 painter.drawText(rect,
756                                  Qt::AlignLeft | Qt::AlignVCenter,
757                                  painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width()));
758             }
759         }
760     }
761
762     /* Draw axis */
763     painter.resetTransform();
764     painter.setPen(m_axisBorder);
765
766     /* Top Rect */
767     painter.fillRect(m_axisWidth - 1, 0, widgetWidth, m_axisHeight - 1, m_axisBackground);
768     painter.drawLine(0, m_axisHeight - 1, widgetWidth, m_axisHeight - 1);
769
770     /* Left Rect */
771     painter.fillRect(0, m_axisHeight - 1, m_axisWidth - 1, widgetHeight, m_axisBackground);
772     painter.drawLine(m_axisWidth - 1, 0, m_axisWidth - 1, widgetHeight);
773
774     /* Draw the program numbers */
775     painter.translate(0, m_axisHeight + m_rowHeight);
776
777     for (int row = m_row; row < rowEnd; ++row) {
778         int y = (row - m_row) * m_rowHeight - (m_scrollY % m_rowHeight);
779
780         painter.setPen(m_axisForeground);
781         painter.drawText(0, y, m_axisWidth, m_rowHeight, Qt::AlignHCenter | Qt::AlignVCenter, QString("%1").arg(m_rowPrograms[row]));
782
783         painter.setPen(m_axisBorder);
784         painter.drawLine(0, y + m_rowHeight - 1, m_axisWidth - 1, y + m_rowHeight - 1);
785
786         painter.setPen(m_axisLine);
787         painter.drawLine(m_axisWidth, y + m_rowHeight - 1, widgetWidth, y + m_rowHeight - 1);
788     }
789
790     /* Draw the "CPU" axis label */
791     painter.resetTransform();
792     painter.translate(0, m_axisHeight);
793
794     painter.setPen(m_axisBorder);
795     painter.setBrush(m_axisBackground);
796     painter.drawRect(-1, -1, m_axisWidth, m_rowHeight);
797
798     painter.setPen(m_axisForeground);
799     painter.drawText(0, 0, m_axisWidth - 1, m_rowHeight - 1, Qt::AlignHCenter | Qt::AlignVCenter, "CPU");
800
801     painter.setPen(m_axisBorder);
802     painter.drawLine(m_axisWidth, m_rowHeight - 1, widgetWidth, m_rowHeight - 1);
803
804
805     /* Draw the frame numbers */
806     painter.resetTransform();
807
808     painter.setPen(m_axisForeground);
809     painter.translate(m_axisWidth, 0);
810
811     int lastLabel = -9999;
812
813     double scroll = m_time;
814     scroll /= m_timeWidth;
815     scroll *= m_viewWidth;
816
817     for (std::vector<Frame>::const_iterator itr = m_profile->frames.begin(); itr != m_profile->frames.end(); ++itr) {
818         static const int padding = 4;
819         const Frame& frame = *itr;
820         bool draw = true;
821         int width;
822
823         if (frame.cpuStart > timeEnd) {
824             break;
825         }
826
827         if (frame.cpuStart + frame.cpuDuration < m_time) {
828             draw = false;
829         }
830
831         double left = frame.cpuStart;
832         left /= m_timeWidth;
833         left *= m_viewWidth;
834
835         double right = frame.cpuStart + frame.cpuDuration;
836         right /= m_timeWidth;
837         right *= m_viewWidth;
838
839         QString text = QString("%1").arg(frame.no);
840
841         width = painter.fontMetrics().width(text) + padding * 2;
842
843         if (left + width > scroll)
844             draw = true;
845
846         /* Draw a frame number if we have space since the last one */
847         if (left - lastLabel > width) {
848             lastLabel = left + width;
849
850             if (draw) {
851                 int textX;
852                 painter.setPen(m_axisForeground);
853
854                 if (left < scroll && right - left > width) {
855                     if (right - scroll > width) {
856                         textX = 0;
857                     } else {
858                         textX = right - scroll - width;
859                     }
860                 } else {
861                     textX = left - scroll;
862                 }
863
864                 /* Draw frame number and major ruler marking */
865                 painter.drawText(textX + padding, 0, width - padding, m_axisHeight - 5, Qt::AlignLeft | Qt::AlignVCenter, text);
866                 painter.drawLine(left - scroll, m_axisHeight / 2, left - scroll, m_axisHeight - 1);
867             }
868         } else if (draw) {
869             /* Draw a minor ruler marking */
870             painter.drawLine(left - scroll, m_axisHeight - (m_axisHeight / 4), left - scroll, m_axisHeight - 1);
871         }
872     }
873
874     /* Draw "Frame" axis label */
875     painter.resetTransform();
876
877     painter.setPen(m_axisBorder);
878     painter.setBrush(m_axisBackground);
879     painter.drawRect(-1, -1, m_axisWidth, m_axisHeight);
880
881     painter.setPen(m_axisForeground);
882     painter.drawText(0, 0, m_axisWidth - 1, m_axisHeight - 1, Qt::AlignHCenter | Qt::AlignVCenter, "Frame");
883
884     /* Draw the active selection border */
885     if (m_timeSelectionStart != m_timeSelectionEnd) {
886         int selectionLeft  = timeToPosition(m_timeSelectionStart) + m_axisWidth;
887         int selectionRight = (timeToPosition(m_timeSelectionEnd) + 0.5) + m_axisWidth;
888
889         painter.setPen(m_selectionBorder);
890
891         if (selectionLeft >= m_axisWidth && selectionLeft < widgetWidth) {
892             painter.drawLine(selectionLeft, 0, selectionLeft, widgetHeight);
893         }
894
895         if (selectionRight >= m_axisWidth && selectionRight < widgetWidth) {
896             painter.drawLine(selectionRight, 0, selectionRight, widgetHeight);
897         }
898
899         selectionLeft = qBound(m_axisWidth, selectionLeft, widgetWidth);
900         selectionRight = qBound(m_axisWidth, selectionRight, widgetWidth);
901
902         painter.drawLine(selectionLeft, m_axisHeight - 1, selectionRight, m_axisHeight - 1);
903         painter.fillRect(selectionLeft, 0, selectionRight - selectionLeft, widgetHeight, m_selectionBackground);
904     }
905
906     /* Draw the ruler zoom */
907     if (m_mousePressMode == RulerZoom) {
908         int x1 = m_mousePressPosition.x();
909         int x2 = qMax(m_mousePosition.x(), m_axisWidth);
910
911         painter.setPen(m_zoomBorder);
912         painter.drawLine(x1, 0, x1, widgetHeight);
913         painter.drawLine(x2, 0, x2, widgetHeight);
914         painter.drawLine(x1, m_axisHeight - 1, x2, m_axisHeight - 1);
915         painter.fillRect(x1, m_axisHeight, x2 - x1, widgetHeight, m_zoomBackground);
916     }
917 }
918
919 #include "timelinewidget.moc"