/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** 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 ** conditions see http://www.qt.io/licensing. For further information ** use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** 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. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ import QtQuick 2.1 import QtQuick.Controls 1.0 Rectangle { id: root // ***** properties property bool lockItemSelection : false signal updateCursorPosition property string fileName: "" property int lineNumber: -1 property int columnNumber: 0 property int selectedModel: -1 property int selectedItem: -1 property bool selectionRangeMode: false property bool selectionRangeReady: selectionRange.ready property real selectionRangeStart: selectionRange.startTime property real selectionRangeEnd: selectionRange.startTime + selectionRange.duration property int typeId: content.typeId onTypeIdChanged: updateCursorPosition() color: "#dcdcdc" // ***** connections with external objects Connections { target: zoomControl onRangeChanged: { zoomSliderToolBar.updateZoomLevel(); content.scroll(); // If you select something in the main view and then resize the current range by some // other means, the selection should stay where it was. var oldTimePerPixel = selectionRange.viewTimePerPixel; selectionRange.viewTimePerPixel = zoomControl.rangeDuration / content.width; if (selectionRange.creationState === selectionRange.creationFinished && oldTimePerPixel != selectionRange.viewTimePerPixel) { var newWidth = selectionRange.rangeWidth * oldTimePerPixel / selectionRange.viewTimePerPixel; selectionRange.rangeLeft = selectionRange.rangeLeft * oldTimePerPixel / selectionRange.viewTimePerPixel; selectionRange.rangeRight = selectionRange.rangeLeft + newWidth; } } onWindowChanged: { content.scroll(); } } Connections { target: timelineModelAggregator onDataAvailable: { content.clearChildren(); zoomControl.setRange(zoomControl.traceStart, zoomControl.traceStart + zoomControl.traceDuration / 10); } } onSelectionRangeModeChanged: { selectionRange.reset(); buttonsBar.updateRangeButton(selectionRangeMode); } // ***** functions function clear() { content.clearChildren(); rangeDetails.hide(); selectionRangeMode = false; zoomSlider.externalUpdate = true; zoomSlider.value = zoomSlider.minimumValue; overview.clear(); } function enableButtonsBar(enable) { buttonsBar.enabled = enable; } function selectByTypeId(typeId) { if (lockItemSelection || typeId === -1) return; var itemIndex = -1; var modelIndex = -1; var notes = timelineModelAggregator.notesByTypeId(typeId); if (notes.length !== 0) { modelIndex = timelineModelAggregator.noteTimelineModel(notes[0]); itemIndex = timelineModelAggregator.noteTimelineIndex(notes[0]); } else { for (modelIndex = 0; modelIndex < timelineModelAggregator.models.length; ++modelIndex) { if (modelIndex === selectedModel && selectedItem !== -1 && typeId === timelineModelAggregator.models[modelIndex].typeId(selectedItem)) break; if (!timelineModelAggregator.models[modelIndex].handlesTypeId(typeId)) continue; itemIndex = timelineModelAggregator.models[modelIndex].nextItemByTypeId(typeId, zoomControl.rangeStart, selectedItem); if (itemIndex !== -1) break; } } if (itemIndex !== -1) { // select an item, lock to it, and recenter if necessary content.select(modelIndex, itemIndex); content.selectionLocked = true; } } focus: true property bool shiftPressed: false; Keys.onPressed: shiftPressed = (event.key === Qt.Key_Shift); Keys.onReleased: shiftPressed = false; TimelineLabels { id: categories anchors.top: buttonsBar.bottom anchors.bottom: overview.top anchors.left: parent.left anchors.right: parent.right contentY: content.contentY selectedModel: root.selectedModel selectedItem: root.selectedItem color: root.color modelProxy: timelineModelAggregator zoomer: zoomControl reverseSelect: shiftPressed onMoveCategories: content.moveCategories(sourceIndex, targetIndex) onSelectItem: content.select(modelIndex, eventIndex) } TimeDisplay { id: timeDisplay anchors.top: parent.top anchors.left: buttonsBar.right anchors.right: parent.right anchors.bottom: overview.top zoomer: zoomControl contentX: content.contentX clip: true } ButtonsBar { id: buttonsBar enabled: false anchors.top: parent.top anchors.left: parent.left width: 150 height: 24 onZoomControlChanged: zoomSliderToolBar.visible = !zoomSliderToolBar.visible onFilterMenuChanged: filterMenu.visible = !filterMenu.visible onJumpToNext: { var next = timelineModelAggregator.nextItem(root.selectedModel, root.selectedItem, zoomControl.rangeStart); content.select(next.model, next.item); } onJumpToPrev: { var prev = timelineModelAggregator.prevItem(root.selectedModel, root.selectedItem, zoomControl.rangeEnd); content.select(prev.model, prev.item); } onRangeSelectChanged: selectionRangeMode = rangeButtonChecked(); onLockChanged: content.selectionLocked = !lockButtonChecked(); } TimelineContent { id: content anchors.left: buttonsBar.right anchors.top: buttonsBar.bottom anchors.bottom: overview.top anchors.right: parent.right selectionLocked: true zoomer: zoomControl modelProxy: timelineModelAggregator onSelectionLockedChanged: { buttonsBar.updateLockButton(selectionLocked); } onPropagateSelection: { if (lockItemSelection || (newModel === selectedModel && newItem === selectedItem)) return; lockItemSelection = true; if (selectedModel !== -1 && selectedModel !== newModel) select(selectedModel, -1); selectedItem = newItem selectedModel = newModel if (selectedItem !== -1) { // display details rangeDetails.showInfo(selectedModel, selectedItem); // update in other views var model = timelineModelAggregator.models[selectedModel]; var eventLocation = model.location(selectedItem); gotoSourceLocation(eventLocation.file, eventLocation.line, eventLocation.column); typeId = model.typeId(selectedItem); } else { rangeDetails.hide(); } lockItemSelection = false; } onGotoSourceLocation: { if (file !== undefined) { root.fileName = file; root.lineNumber = line; root.columnNumber = column; } } } MouseArea { id: selectionRangeControl enabled: selectionRangeMode && selectionRange.creationState !== selectionRange.creationFinished anchors.right: content.right anchors.left: buttonsBar.right anchors.top: content.top anchors.bottom: content.bottom hoverEnabled: enabled z: 2 onReleased: { if (selectionRange.creationState === selectionRange.creationSecondLimit) { content.stayInteractive = true; selectionRange.creationState = selectionRange.creationFinished; } } onPressed: { if (selectionRange.creationState === selectionRange.creationFirstLimit) { content.stayInteractive = false; selectionRange.setPos(selectionRangeControl.mouseX + content.contentX); selectionRange.creationState = selectionRange.creationSecondLimit; } } onPositionChanged: { if (selectionRange.creationState === selectionRange.creationInactive) selectionRange.creationState = selectionRange.creationFirstLimit; if (selectionRangeControl.pressed || selectionRange.creationState !== selectionRange.creationFinished) selectionRange.setPos(selectionRangeControl.mouseX + content.contentX); } onCanceled: pressed() } Flickable { flickableDirection: Flickable.HorizontalFlick clip: true visible: selectionRangeMode && selectionRange.creationState !== selectionRange.creationInactive interactive: false x: content.x + content.flickableItem.x y: content.y + content.flickableItem.y height: content.flickableItem.height width: content.flickableItem.width contentX: content.contentX contentWidth: content.contentWidth SelectionRange { id: selectionRange zoomer: zoomControl visible: parent.visible onRangeDoubleClicked: { zoomControl.setRange(startTime, endTime); root.selectionRangeMode = false; } } } SelectionRangeDetails { z: 1 x: 200 y: 125 id: selectionRangeDetails visible: selectionRange.visible startTime: selectionRange.startTime duration: selectionRange.duration endTime: selectionRange.endTime showDuration: selectionRange.rangeWidth > 1 onRecenter: { if ((selectionRange.startTime < zoomControl.rangeStart) ^ (selectionRange.endTime > zoomControl.rangeEnd)) { var center = selectionRange.startTime + selectionRange.duration / 2; var halfDuration = Math.max(selectionRange.duration, zoomControl.rangeDuration / 2); zoomControl.setRange(center - halfDuration, center + halfDuration); } } onClose: selectionRangeMode = false; } RangeDetails { id: rangeDetails z: 1 visible: false x: 200 y: 25 property alias locked: content.selectionLocked models: timelineModelAggregator.models notes: timelineModelAggregator.notes onRecenterOnItem: { content.gotoSourceLocation(file, line, column); content.select(selectedModel, selectedItem) } onToggleSelectionLocked: { content.selectionLocked = !content.selectionLocked; } onClearSelection: { content.propagateSelection(-1, -1); } } Rectangle { anchors.left: buttonsBar.right anchors.bottom: overview.top anchors.top: parent.top width: 1 color: "#B0B0B0" } Rectangle { id: filterMenu color: "#9b9b9b" enabled: buttonsBar.enabled visible: false width: buttonsBar.width anchors.left: parent.left anchors.top: buttonsBar.bottom height: timelineModelAggregator.models.length * buttonsBar.height Repeater { id: filterMenuInner model: timelineModelAggregator.models CheckBox { anchors.left: filterMenu.left anchors.right: filterMenu.right height: buttonsBar.height y: index * height text: modelData.displayName enabled: !modelData.empty checked: enabled && !modelData.hidden onCheckedChanged: modelData.hidden = !checked } } } Rectangle { id: zoomSliderToolBar objectName: "zoomSliderToolBar" color: "#9b9b9b" enabled: buttonsBar.enabled visible: false width: buttonsBar.width height: buttonsBar.height anchors.left: parent.left anchors.top: buttonsBar.bottom function updateZoomLevel() { zoomSlider.externalUpdate = true; zoomSlider.value = Math.pow(zoomControl.rangeDuration / Math.max(1, zoomControl.windowDuration), 1 / zoomSlider.exponent) * zoomSlider.maximumValue; } Slider { id: zoomSlider anchors.fill: parent minimumValue: 1 maximumValue: 10000 stepSize: 100 property int exponent: 3 property bool externalUpdate: false property int minWindowLength: 1e5 // 0.1 ms onValueChanged: { if (externalUpdate || zoomControl.windowEnd <= zoomControl.windowStart) { // Zoom range is independently updated. We shouldn't mess // with it here as otherwise we might introduce rounding // or arithmetic errors. externalUpdate = false; return; } var windowLength = Math.max( Math.pow(value / maximumValue, exponent) * zoomControl.windowDuration, minWindowLength); var fixedPoint = (zoomControl.rangeStart + zoomControl.rangeEnd) / 2; if (root.selectedItem !== -1) { // center on selected item if it's inside the current screen var model = timelineModelAggregator.models[root.selectedModel] var newFixedPoint = (model.startTime(root.selectedItem) + model.endTime(root.selectedItem)) / 2; if (newFixedPoint >= zoomControl.rangeStart && newFixedPoint < zoomControl.rangeEnd) fixedPoint = newFixedPoint; } var startTime = Math.max(zoomControl.windowStart, fixedPoint - windowLength / 2) zoomControl.setRange(startTime, startTime + windowLength); } } } Overview { id: overview height: 50 anchors.bottom: parent.bottom anchors.right: parent.right anchors.left: parent.left modelProxy: timelineModelAggregator zoomer: zoomControl } }