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