]> git.cworth.org Git - vogl/blob - src/vogleditor/vogleditor_qtimelineview.cpp
UI: Fix issue #21: Resizing of vogleditor window is slow depending on trace size
[vogl] / src / vogleditor / vogleditor_qtimelineview.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 #include <QtGui>
27 #include "vogleditor_qtimelineview.h"
28 #include "vogleditor_frameitem.h"
29
30 vogleditor_QTimelineView::vogleditor_QTimelineView(QWidget *parent) :
31    QWidget(parent),
32    m_curFrame(0),
33    m_curApiCallNumber(0),
34    m_pModel(NULL),
35    m_pPixmap(NULL)
36 {
37    QLinearGradient gradient(QPointF(50, -20), QPointF(80, 20));
38    gradient.setColorAt(0.0, Qt::white);
39    gradient.setColorAt(1.0, QColor(0xa6, 0xce, 0x39));
40
41    m_background = QBrush(QColor(200,200,200));//QBrush(parent->palette().brush(parent->backgroundRole()));
42    m_triangleBrush = QBrush(gradient);
43    m_trianglePen = QPen(Qt::black);
44    m_trianglePen.setWidth(1);
45    m_textPen = QPen(Qt::white);
46    m_textFont.setPixelSize(50);
47
48    m_horizontalScale = 1;
49    m_lineLength = 1;
50 }
51
52 vogleditor_QTimelineView::~vogleditor_QTimelineView()
53 {
54     deletePixmap();
55 }
56
57 void vogleditor_QTimelineView::paintEvent(QPaintEvent *event)
58 {
59     // Don't bother drawing if the rect is too small.
60     // For some reason this is happening at unexpected times.
61     int rectHeight = event->rect().height();
62     int rectWidth = event->rect().width();
63     if (rectHeight < 100 || rectWidth < 100)
64     {
65         return;
66     }
67
68     QPainter painter;
69     painter.begin(this);
70     paint(&painter, event);
71     painter.end();
72 }
73
74 void vogleditor_QTimelineView::drawBaseTimeline(QPainter* painter, const QRect& rect, int gap)
75 {
76     painter->save();
77
78     // fill entire background with background color
79     painter->fillRect(rect, m_background);
80
81     // translate drawing to vertical center of rect
82     painter->translate(0, rect.height()/2);
83
84     painter->setBrush(m_triangleBrush);
85     painter->setPen(m_trianglePen);
86
87     // everything will have a small gap on the left and right sides
88     painter->translate(gap, 0);
89
90     // draw the actual timeline
91     int lineLength = rect.width()-2*gap;
92     painter->drawLine(0,0, lineLength, 0);
93
94     painter->restore();
95 }
96
97 void vogleditor_QTimelineView::paint(QPainter *painter, QPaintEvent *event)
98 {
99     int gap = 10;
100     int arrowHeight = 10;
101     int arrowTop = event->rect().height()/2-gap-arrowHeight;
102     int arrowHalfWidth = 3;
103      m_lineLength = event->rect().width()-2*gap;
104
105     QPolygon triangle(3);
106     triangle.setPoint(0, 0, arrowTop);
107     triangle.setPoint(1, -arrowHalfWidth, arrowTop+arrowHeight);
108     triangle.setPoint(2, arrowHalfWidth, arrowTop+arrowHeight);
109
110     drawBaseTimeline(painter, event->rect(), gap);
111
112     if (m_pModel == NULL)
113     {
114         return;
115     }
116
117     if (m_pModel->get_root_item() == NULL)
118     {
119         return;
120     }
121
122     if (m_pPixmap != NULL)
123     {
124         int rectHeight = event->rect().height();
125         int rectWidth = event->rect().width();
126         int pmHeight = m_pPixmap->height();
127         int pmWidth = m_pPixmap->width();
128
129         float widthPctDelta = (float)(rectWidth - pmWidth) / (float)pmWidth;
130         float heightPctDelta = (float)(rectHeight - pmHeight) / (float)pmHeight;
131
132         // If the resize is of a 'signficant' amount, then delete the pixmap so that it will be regenerated at the new size.
133         if (fabs(widthPctDelta) > 0.2 ||
134             fabs(heightPctDelta) > 0.2)
135         {
136             deletePixmap();
137         }
138     }
139
140     if (m_pPixmap == NULL)
141     {
142         m_pPixmap = new QPixmap(event->rect().width(), event->rect().height());
143         QPainter pixmapPainter(m_pPixmap);
144         drawBaseTimeline(&pixmapPainter, event->rect(), gap);
145
146         // translate drawing to vertical center of rect
147         // everything will have a small gap on the left and right sides
148         pixmapPainter.translate(gap, event->rect().height()/2);
149
150         if (m_pModel->get_root_item()->getBrush() == NULL)
151         {
152             m_pModel->get_root_item()->setBrush(&m_triangleBrush);
153         }
154
155         m_horizontalScale = (float)m_lineLength / (float)m_pModel->get_root_item()->getDuration();
156
157         // we don't want to draw the root item, but all of its children
158         int numChildren = m_pModel->get_root_item()->childCount();
159         int height = event->rect().height()/2-2*gap;
160
161         pixmapPainter.setBrush(m_triangleBrush);
162         pixmapPainter.setPen(m_trianglePen);
163
164         float minimumOffset = 0;
165         for (int c = 0; c < numChildren; c++)
166         {
167             vogleditor_timelineItem* pChild = m_pModel->get_root_item()->child(c);
168             drawTimelineItem(&pixmapPainter, pChild, height, minimumOffset);
169         }
170     }
171
172     painter->drawPixmap(event->rect(), *m_pPixmap, m_pPixmap->rect());
173
174     // translate drawing to vertical center of rect
175     // everything will have a small gap on the left and right sides
176     painter->translate(gap, event->rect().height()/2);
177
178     painter->setBrush(m_triangleBrush);
179     painter->setPen(m_trianglePen);
180
181     int numChildren = m_pModel->get_root_item()->childCount();
182     for (int c = 0; c < numChildren; c++)
183     {
184         vogleditor_timelineItem* pChild = m_pModel->get_root_item()->child(c);
185
186         // draw current frame marker
187         if (pChild->getFrameItem() != NULL && pChild->getFrameItem()->frameNumber() == m_curFrame)
188         {
189             painter->save();
190             painter->translate(scalePositionHorizontally(pChild->getBeginTime()), 0);
191             painter->drawPolygon(triangle);
192             painter->restore();
193         }
194
195         // draw current api call marker
196         if (pChild->getApiCallItem() != NULL && pChild->getApiCallItem()->globalCallIndex() == m_curApiCallNumber)
197         {
198             painter->save();
199             painter->translate(scalePositionHorizontally(pChild->getBeginTime()), 0);
200             painter->drawPolygon(triangle);
201             painter->restore();
202         }
203     }
204 }
205
206 float vogleditor_QTimelineView::scaleDurationHorizontally(float value)
207 {
208    float scaled = value * m_horizontalScale;
209    if (scaled <= m_horizontalScale)
210    {
211       scaled = m_horizontalScale;
212    }
213
214    return scaled;
215 }
216
217 float vogleditor_QTimelineView::scalePositionHorizontally(float value)
218 {
219    float horizontalShift = m_pModel->get_root_item()->getBeginTime();
220    float horizontalLength = m_pModel->get_root_item()->getDuration();
221    float offset = ((value - horizontalShift) / horizontalLength) * m_lineLength;
222
223    return offset;
224 }
225
226 void vogleditor_QTimelineView::drawTimelineItem(QPainter* painter, vogleditor_timelineItem *pItem, int height, float& minimumOffset)
227 {
228    float duration = pItem->getDuration();
229    if (duration < 0)
230    {
231       return;
232    }
233
234    painter->save();
235    if (pItem->isMarker())
236    {
237       painter->setBrush(m_triangleBrush);
238       painter->setPen(m_trianglePen);
239
240       float offset = scalePositionHorizontally(pItem->getBeginTime());
241       painter->drawLine(QLineF(offset, -height, offset, height));
242    }
243    else
244    {
245        // only draw if the item will extend beyond the minimum offset
246        float leftOffset = scalePositionHorizontally(pItem->getBeginTime());
247        float scaledWidth = scaleDurationHorizontally(duration);
248        if (minimumOffset < leftOffset + scaledWidth)
249        {
250            float durationRatio = duration / m_maxItemDuration;
251            int intensity = std::min(255, (int)(durationRatio * 255.0f));
252            //   painter->setBrush(*(pItem->getBrush()));
253            QColor color(intensity, 255-intensity, 0);
254            painter->setBrush(QBrush(color));
255            painter->setPen(color);
256
257            // Clamp the item so that it is 1 pixel wide.
258            // This is intentionally being done before updating the minimum offset
259            // so that small items after the current item will not be drawn
260            if (scaledWidth < 1)
261            {
262                scaledWidth = 1;
263            }
264
265            // update minimum offset
266            minimumOffset = leftOffset + scaledWidth;
267
268            // draw the colored box that represents this item
269            QRectF rect;
270            rect.setLeft(leftOffset);
271            rect.setTop(-height/2);
272            rect.setWidth(scaledWidth);
273            rect.setHeight(height);
274            painter->drawRect(rect);
275
276            // now draw all children
277            int numChildren = pItem->childCount();
278            for (int c = 0; c < numChildren; c++)
279            {
280                drawTimelineItem(painter, pItem->child(c), height-1, minimumOffset);
281            }
282        }
283    }
284
285    painter->restore();
286 }