timelinerenderer.cpp 9.7 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Kai Koehne's avatar
Kai Koehne committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
Kai Koehne's avatar
Kai Koehne committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Kai Koehne's avatar
Kai Koehne committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
Eike Ziller's avatar
Eike Ziller committed
13
14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
Kai Koehne's avatar
Kai Koehne committed
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
25
26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
Kai Koehne's avatar
Kai Koehne committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
Kai Koehne's avatar
Kai Koehne committed
30

31
#include "timelinerenderer_p.h"
32
#include "timelinerenderpass.h"
33
#include "timelinenotesmodel.h"
34
35
36
#include "timelineitemsrenderpass.h"
#include "timelineselectionrenderpass.h"
#include "timelinenotesrenderpass.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
37

38
#include <QElapsedTimer>
39
40
#include <QQmlContext>
#include <QQmlProperty>
41
42
#include <QTimer>
#include <QPixmap>
Christiaan Janssen's avatar
Christiaan Janssen committed
43
#include <QVarLengthArray>
44
45
#include <QSGTransformNode>
#include <QSGSimpleRectNode>
46
47

#include <math.h>
Christiaan Janssen's avatar
Christiaan Janssen committed
48

49
namespace Timeline {
Kai Koehne's avatar
Kai Koehne committed
50

51
TimelineRenderer::TimelineRendererPrivate::TimelineRendererPrivate(TimelineRenderer *q) :
52
    lastState(0), q_ptr(q)
53
54
55
56
{
    resetCurrentSelection();
}

57
58
59
60
61
62
63
64
65
66
67
68
69
TimelineRenderer::TimelineRendererPrivate::~TimelineRendererPrivate()
{
    clear();
}

void TimelineRenderer::TimelineRendererPrivate::clear()
{
    for (auto i = renderStates.begin(); i != renderStates.end(); ++i)
        qDeleteAll(*i);
    renderStates.clear();
    lastState = 0;
}

70
TimelineRenderer::TimelineRenderer(QQuickItem *parent) :
71
    TimelineAbstractRenderer(*(new TimelineRendererPrivate(this)), parent)
Christiaan Janssen's avatar
Christiaan Janssen committed
72
{
73
74
    setAcceptedMouseButtons(Qt::LeftButton);
    setAcceptHoverEvents(true);
Christiaan Janssen's avatar
Christiaan Janssen committed
75
76
}

77
void TimelineRenderer::TimelineRendererPrivate::resetCurrentSelection()
78
{
79
80
81
82
    currentSelection.startTime = -1;
    currentSelection.endTime = -1;
    currentSelection.row = -1;
    currentSelection.eventIndex = -1;
83
84
}

85
TimelineRenderState *TimelineRenderer::TimelineRendererPrivate::findRenderState()
Christiaan Janssen's avatar
Christiaan Janssen committed
86
{
87
    int newLevel = 0;
88
    qint64 newOffset = 0;
89
    int level;
90
    qint64 offset;
91

92
93
    qint64 newStart = zoomer->traceStart();
    qint64 newEnd = zoomer->traceEnd();
94
95
96
97
98
99
100
101
102
    qint64 start;
    qint64 end;
    do {
        level = newLevel;
        offset = newOffset;
        start = newStart;
        end = newEnd;

        newLevel = level + 1;
103
104
105
        qint64 range = zoomer->traceDuration() >> newLevel;
        newOffset = (zoomer->windowStart() - zoomer->traceStart() + range / 2) / range;
        newStart = zoomer->traceStart() + newOffset * range - range / 2;
106
        newEnd = newStart + range;
107
    } while (newStart < zoomer->windowStart() && newEnd > zoomer->windowEnd());
108
109


110
111
112
    if (renderStates.length() <= level)
        renderStates.resize(level + 1);
    TimelineRenderState *state = renderStates[level][offset];
113
114
    if (state == 0) {
        state = new TimelineRenderState(start, end, 1.0 / static_cast<qreal>(SafeFloatMax),
115
116
                                        renderPasses.size());
        renderStates[level][offset] = state;
117
    }
118
    return state;
119
}
Christiaan Janssen's avatar
Christiaan Janssen committed
120

121
QSGNode *TimelineRenderer::updatePaintNode(QSGNode *node, UpdatePaintNodeData *updatePaintNodeData)
122
{
123
    Q_D(TimelineRenderer);
124

125
    if (!d->model || d->model->hidden() || d->model->isEmpty() || !d->zoomer ||
126
            d->zoomer->windowDuration() <= 0) {
127
128
129
        delete node;
        return 0;
    }
130

131
    qreal spacing = width() / d->zoomer->windowDuration();
132

133
134
135
    if (d->modelDirty) {
        if (node)
            node->removeAllChildNodes();
136
        d->clear();
137
    }
138

139
    TimelineRenderState *state = d->findRenderState();
Christiaan Janssen's avatar
Christiaan Janssen committed
140

141
142
    int lastIndex = d->model->lastIndex(d->zoomer->windowEnd());
    int firstIndex = d->model->firstIndex(d->zoomer->windowStart());
143

144
145
    for (int i = 0; i < d->renderPasses.length(); ++i)
        state->setPassState(i, d->renderPasses[i]->update(this, state, state->passState(i),
146
                                                         firstIndex, lastIndex + 1,
147
                                                         state != d->lastState, spacing));
148

149
    if (state->isEmpty()) { // new state
150
151
152
153
154
        state->assembleNodeTree(d->model, TimelineModel::defaultRowHeight(),
                                TimelineModel::defaultRowHeight());
    } else if (d->rowHeightsDirty || state != d->lastState) {
        state->updateExpandedRowHeights(d->model, TimelineModel::defaultRowHeight(),
                                        TimelineModel::defaultRowHeight());
155
    }
156

157
    TimelineAbstractRenderer::updatePaintNode(0, updatePaintNodeData);
158
    d->lastState = state;
159

160
    QMatrix4x4 matrix;
161
    matrix.translate((state->start() - d->zoomer->windowStart()) * spacing, 0, 0);
162
    matrix.scale(spacing / state->scale(), 1, 1);
163

164
    return state->finalize(node, d->model->expanded(), matrix);
165
166
}

167
void TimelineRenderer::mousePressEvent(QMouseEvent *event)
168
{
169
    Q_UNUSED(event);
170
171
}

172
int TimelineRenderer::TimelineRendererPrivate::rowFromPosition(int y) const
173
{
174
    if (!model->expanded())
175
        return y / TimelineModel::defaultRowHeight();
176

177
    int ret = 0;
178
179
    for (int row = 0; row < model->expandedRowCount(); ++row) {
        y -= model->expandedRowHeight(row);
180
        if (y <= 0) return ret;
181
        ++ret;
Christiaan Janssen's avatar
Christiaan Janssen committed
182
    }
183

184
    return ret;
185
186
}

187
void TimelineRenderer::mouseReleaseEvent(QMouseEvent *event)
188
{
189
    Q_D(TimelineRenderer);
190
    Q_UNUSED(event);
191
    if (d->model && !d->model->isEmpty())
192
        d->manageClicked();
193
194
}

195
void TimelineRenderer::mouseMoveEvent(QMouseEvent *event)
196
{
197
198
    event->setAccepted(false);
}
199

200
void TimelineRenderer::hoverMoveEvent(QHoverEvent *event)
201
{
202
203
204
    Q_D(TimelineRenderer);
    d->manageHovered(event->pos().x(), event->pos().y());
    if (d->currentSelection.eventIndex == -1)
205
        event->setAccepted(false);
206
207
}

208
void TimelineRenderer::TimelineRendererPrivate::manageClicked()
209
{
210
    Q_Q(TimelineRenderer);
211
    if (currentSelection.eventIndex != -1)
212
        q->setSelectedItem(currentSelection.eventIndex);
213
    else
214
        q->setSelectedItem(-1);
215
}
216

217
void TimelineRenderer::TimelineRendererPrivate::manageHovered(int mouseX, int mouseY)
218
{
219
    Q_Q(TimelineRenderer);
220
221
222
    if (!zoomer || !model || q->width() < 1)
        return;

223
    qint64 duration = zoomer->windowDuration();
224
    if (duration <= 0)
225
        return;
226

227
    // Make the "selected" area 3 pixels wide by adding/subtracting 1 to catch very narrow events.
228
229
    qint64 startTime = (mouseX - 1) * duration / q->width() + zoomer->windowStart();
    qint64 endTime = (mouseX + 1) * duration / q->width() + zoomer->windowStart();
230
    qint64 exactTime = (startTime + endTime) / 2;
231
    int row = rowFromPosition(mouseY);
232

233
234
235
236
237
238
239
    // already covered? Only recheck selectionLocked and make sure d->selectedItem is correct.
    if (currentSelection.eventIndex != -1 &&
            exactTime >= currentSelection.startTime &&
            exactTime < currentSelection.endTime &&
            row == currentSelection.row) {
        if (!selectionLocked)
            q->setSelectedItem(currentSelection.eventIndex);
240
241
        return;
    }
242

243
    // find if there's items in the time range
244
245
    int eventFrom = model->firstIndex(startTime);
    int eventTo = model->lastIndex(endTime);
246

247
248
    currentSelection.eventIndex = -1;
    if (eventFrom == -1 || eventTo < eventFrom || eventTo >= model->count())
249
250
        return;

251
    // find if we are in the right column
252
    qint64 bestOffset = std::numeric_limits<qint64>::max();
253
    for (int i=eventTo; i>=eventFrom; --i) {
254
        if (model->row(i) == row) {
255
256
            // There can be small events that don't reach the cursor position after large events
            // that do but are in a different row.
257
            qint64 itemEnd = model->endTime(i);
258
259
260
            if (itemEnd < startTime)
                continue;

261
            qint64 itemStart = model->startTime(i);
262
263
264
265

            qint64 offset = qAbs(itemEnd - exactTime) + qAbs(itemStart - exactTime);
            if (offset < bestOffset) {
                // match
266
267
268
269
                currentSelection.eventIndex = i;
                currentSelection.startTime = itemStart;
                currentSelection.endTime = itemEnd;
                currentSelection.row = row;
270
271
                bestOffset = offset;
            }
272
        }
273
    }
274
275
    if (!selectionLocked && currentSelection.eventIndex != -1)
        q->setSelectedItem(currentSelection.eventIndex);
276
277
}

Christiaan Janssen's avatar
Christiaan Janssen committed
278
void TimelineRenderer::clearData()
279
{
280
281
    Q_D(TimelineRenderer);
    d->resetCurrentSelection();
282
283
    setSelectedItem(-1);
    setSelectionLocked(true);
284
285
}

286
void TimelineRenderer::selectNextFromSelectionId(int selectionId)
287
{
288
289
290
    Q_D(TimelineRenderer);
    setSelectedItem(d->model->nextItemBySelectionId(selectionId, d->zoomer->rangeStart(),
                                                   d->selectedItem));
291
292
}

293
void TimelineRenderer::selectPrevFromSelectionId(int selectionId)
294
{
295
296
297
    Q_D(TimelineRenderer);
    setSelectedItem(d->model->prevItemBySelectionId(selectionId, d->zoomer->rangeStart(),
                                                   d->selectedItem));
298
}
299

300
} // namespace Timeline