diff --git a/Include/Timeline/acTimeline.h b/Include/Timeline/acTimeline.h index ee393c9..181631c 100755 --- a/Include/Timeline/acTimeline.h +++ b/Include/Timeline/acTimeline.h @@ -311,10 +311,14 @@ class AC_API acTimeline : public QWidget /// Set should v scroll to end flag void ShouldScrollToEnd(bool scroll) { m_shouldVScrollToEnd = scroll; } - /// returns pixel coord of a time value + /// returns x pixel coord of a time value /// \param time value int TimeToPixel(double timeValue, bool checkBounds = true); + /// returns time value of an x pixel coord + /// \param time value + double PixelToTime(const int pixelValue); + protected: /// Overridden QWidget method used to display tooltip hints. /// \param event the event parameters. @@ -444,6 +448,8 @@ private slots: quint64 m_nTimeStamp; ///< The timestamp of the marker. QColor m_color; ///< The color of the marker. }; + /// Used for sorting markers + static bool markerLessThan(const TimelineMarker* m1, const TimelineMarker* m2) { return m1->m_nTimeStamp < m2->m_nTimeStamp; } /// Resets the row index for all branches. void resetRowIndex(); diff --git a/Include/Timeline/acTimelineGrid.h b/Include/Timeline/acTimelineGrid.h index 551f03e..dc00f75 100755 --- a/Include/Timeline/acTimelineGrid.h +++ b/Include/Timeline/acTimelineGrid.h @@ -186,6 +186,20 @@ class AC_API acTimelineGrid : public QWidget /// \return the timestamp corresponding to the end of a selection quint64 endSelectedTime() const { return m_nEndSelectedTime; } + /// Clear all markers from the grid + void clearMarkers(); + + /// Inserts a marker into the timeline. Used to display times and delta times between markers. Works like the + /// selected time, but the markers are permanent. + /// \param time The timestamp of the marker to add. + void addMarker(const quint64 time); + + /// Removes a marker from the timeline at the specified timestamp. Only one marker is removed if several have + /// the same timestamp. + /// \param time The timestamp of the marker to remove + /// \return Whether a marker was found and removed at the specified timestamp + bool removeMarker(const quint64 time); + /// Sets the format string to used when displaying the duration of the selection in /// the duration hint. /// \param newDurationHintLabel the format string to used when displaying the duration of @@ -242,7 +256,7 @@ class AC_API acTimelineGrid : public QWidget /// Method called by paintEvent to draw the hints along the grid /// \param painter the QPainter instance to use for painting - void drawTimeHints(QPainter& painter); + void drawTimeHints(QPainter& painter, quint64 nStartTime, quint64 nEndTime, bool clearBackground = false); /// Adds the unicode-left-arrow or unicode-right-arrow character to the given string, if /// the given timestamp is not within the visible range of the time grid @@ -266,6 +280,8 @@ class AC_API acTimelineGrid : public QWidget quint64 m_nSelectedTime; ///< The selected timestamp. Used to display a hint in the time grid. quint64 m_nEndSelectedTime; ///< The timestamp corresponding to the end of a selection. Used to display a hint in the time grid. If different than "selectedTime", then three hints are shown (start of selection, end of selection, and duration of selection). + QList m_markers; ///< A list of extra marker times to draw + bool m_bShowTimeHint; ///< A flag indicating whether or not hints should be shown in the time grid for the selected time (either a single timestamp or a range of time). Defaults to true. QString m_strDurationHintLabel; ///< The format string to used when displaying the duration of the selection in the duration hint. Defaults to "%1 units". diff --git a/src/acTimeline.cpp b/src/acTimeline.cpp index a073bb5..27a8f9e 100755 --- a/src/acTimeline.cpp +++ b/src/acTimeline.cpp @@ -734,6 +734,7 @@ void acTimeline::paintEvent(QPaintEvent* /* event */) void acTimeline::mousePressEvent(QMouseEvent* event) { + bool doUpdateGrid = false; if (event->button() == Qt::LeftButton) { int mouseX = event->x(); @@ -743,9 +744,39 @@ void acTimeline::mousePressEvent(QMouseEvent* event) m_bStartDrag = true; m_nStartDragX = mouseX; m_dStartSelectionPivot = (mouseX - m_nTitleWidth) / (double)rowWidth(); - updateGrid(); + doUpdateGrid = true; + } + + if ((event->modifiers() & Qt::AltModifier) || (m_pGrid->rect().contains(event->x(), event->y()))) + { + auto mouseTime = PixelToTime(mouseX); + + // Remove a marker if we are within 5 pixels + bool removedItem = false; + for (auto marker : m_markers) + { + int pixelValue = TimeToPixel(marker->m_nTimeStamp - m_nStartTime); + if (abs(pixelValue - mouseX) < 5) + { + removeMarker(marker->m_nTimeStamp); + removedItem = true; + break; // We only remove one item at a time + } + } + + if (!removedItem) + { + addMarker(mouseTime + m_nStartTime, QColor(255, 0, 0, 255)); + } + + doUpdateGrid = true; } } + + if (doUpdateGrid) + { + updateGrid(); + } } void acTimeline::mouseReleaseEvent(QMouseEvent* event) @@ -1625,6 +1656,13 @@ void acTimeline::updateGrid() time = adjustedStartTime + (quint64)(endPivot * m_nVisibleRange); m_pGrid->setEndSelectedTime(time); + m_pGrid->clearMarkers(); + for (const TimelineMarker* marker : m_markers) + { + const auto time = (TimeToPixel(marker->m_nTimeStamp - m_nStartTime) - m_nTitleWidth) / (double)rowWidth(); + m_pGrid->addMarker(adjustedStartTime + (quint64)(time*m_nVisibleRange)); + } + m_pGrid->update(); } } @@ -1782,6 +1820,19 @@ int acTimeline::TimeToPixel(double timeValue, bool checkBounds) return retPixel; } +double acTimeline::PixelToTime(const int pixelValue) +{ + const int drawingWidth = rowWidth(); + const double drawingPixel = visibleRange() / (drawingWidth * 1.0); + + if (m_pGrid != nullptr) + { + return (pixelValue - titleWidth())*drawingPixel + m_pGrid->visibleStartTime(); + } + + return 0.0; +} + acTimeline::TimelineMarker::TimelineMarker(quint64 timeStamp, QColor color) : m_nTimeStamp(timeStamp), m_color(color) { diff --git a/src/acTimelineGrid.cpp b/src/acTimelineGrid.cpp index bc2a1bb..c285c64 100755 --- a/src/acTimelineGrid.cpp +++ b/src/acTimelineGrid.cpp @@ -134,6 +134,21 @@ void acTimelineGrid::setEndSelectedTime(const quint64 newEndSelectedTime) ensureValidVisibleRange(); } +void acTimelineGrid::clearMarkers() +{ + m_markers.clear(); +} + +void acTimelineGrid::addMarker(const quint64 time) +{ + m_markers.append(time); +} + +bool acTimelineGrid::removeMarker(const quint64 time) +{ + return m_markers.removeOne(time); +} + void acTimelineGrid::setDurationHintLabel(const QString newDurationHintLabel) { m_strDurationHintLabel = newDurationHintLabel; @@ -288,7 +303,33 @@ void acTimelineGrid::paintEvent(QPaintEvent* /* event */) } } - drawTimeHints(painter); + // Draw markers first + QList markersToDraw; + for (const auto marker : m_markers) + { + if ((marker > m_nVisibleStartTime) && (marker < m_nVisibleStartTime + m_nVisibleRange)) + markersToDraw.append(marker); + } + + if (markersToDraw.size() > 0) + { + if (markersToDraw.size() == 1) + { + drawTimeHints(painter, markersToDraw[0], markersToDraw[0]); + } + else + { + quint64 prevMarker = markersToDraw[0]; + for (auto marker = markersToDraw.cbegin() + 1; marker != markersToDraw.cend(); marker++) + { + drawTimeHints(painter, prevMarker, *marker); + prevMarker = *marker; + } + } + } + + // Draw the selected times after markers as we want selected time to take precedence over markers + drawTimeHints(painter, m_nSelectedTime, m_nEndSelectedTime, true); // draw top line last, using the pre-calculated right coordinate if (m_nGridRightXPosition > 0) @@ -316,42 +357,51 @@ void acTimelineGrid::resizeEvent(QResizeEvent* event) QWidget::resizeEvent(event); } -void acTimelineGrid::drawTimeHints(QPainter& painter) +void drawDoubleArrowLine(QPainter& painter, const int x1, const int y1, const int x2, const int y2, const int arrowSize) +{ + painter.drawLine(x1, y1, x2, y2); + painter.drawLine(x1, y1, x1 + arrowSize, y1 - arrowSize); + painter.drawLine(x1, y1, x1 + arrowSize, y1 + arrowSize); + painter.drawLine(x2, y2, x2 - arrowSize, y2 - arrowSize); + painter.drawLine(x2, y2, x2 - arrowSize, y2 + arrowSize); + +} + +void acTimelineGrid::drawTimeHints(QPainter& painter, const quint64 nStartTime, quint64 nEndTime, bool clearBackground) { if (m_bShowTimeHint) { QString startStrNum; - startStrNum.setNum(m_nSelectedTime * m_dInverseScalingFactor, 'f', m_precision); + startStrNum.setNum(nStartTime * m_dInverseScalingFactor, 'f', m_precision); - addOutOfRangeCharacterToHintString(startStrNum, m_nSelectedTime); + addOutOfRangeCharacterToHintString(startStrNum, nStartTime); int startTextWidth = fontMetrics().width(startStrNum); - QRect startHintRect = calcHintRect(startTextWidth, m_nSelectedTime); + QRect startHintRect = calcHintRect(startTextWidth, nStartTime); QColor hintColor = palette().color(QPalette::ToolTipBase); - hintColor.setAlpha(192); - if (m_nSelectedTime != m_nEndSelectedTime) + if (nStartTime != nEndTime) { QString endStrNum; - endStrNum.setNum(m_nEndSelectedTime * m_dInverseScalingFactor, 'f', m_precision); + endStrNum.setNum(nEndTime * m_dInverseScalingFactor, 'f', m_precision); - addOutOfRangeCharacterToHintString(endStrNum, m_nEndSelectedTime); + addOutOfRangeCharacterToHintString(endStrNum, nEndTime); int endTextWidth = fontMetrics().width(endStrNum); - QRect endHintRect = calcHintRect(endTextWidth, m_nEndSelectedTime); + QRect endHintRect = calcHintRect(endTextWidth, nEndTime); quint64 diffTime; quint64 absDiffTime; - if (m_nEndSelectedTime > m_nSelectedTime) + if (nEndTime > nStartTime) { - diffTime = m_nEndSelectedTime - m_nSelectedTime; - absDiffTime = m_nSelectedTime + (diffTime / 2); + diffTime = nEndTime - nStartTime; + absDiffTime = nStartTime + (diffTime / 2); } else { - diffTime = m_nSelectedTime - m_nEndSelectedTime; - absDiffTime = m_nEndSelectedTime + (diffTime / 2); + diffTime = nStartTime - nEndTime; + absDiffTime = nEndTime + (diffTime / 2); } if ((startHintRect.left() == m_nGridLabelSpace && endHintRect.right() == m_nGridLabelSpace + m_nGridSpace - 1) || @@ -370,9 +420,9 @@ void acTimelineGrid::drawTimeHints(QPainter& painter) if (startHintRect.intersects(endHintRect) || startHintRect.intersects(diffHintRect) || endHintRect.intersects(diffHintRect)) { - if (m_nEndSelectedTime > m_nSelectedTime) + if (nEndTime > nStartTime) { - startStrNum = startStrNum + " - " + endStrNum + " (" + diffStrNum + ")" ; + startStrNum = startStrNum + " - " + endStrNum + " (" + diffStrNum + ")"; } else { @@ -380,10 +430,14 @@ void acTimelineGrid::drawTimeHints(QPainter& painter) } startTextWidth = fontMetrics().width(startStrNum); - startHintRect = calcHintRect(startTextWidth, m_nSelectedTime); + startHintRect = calcHintRect(startTextWidth, nStartTime); } else { + const int middle = startHintRect.top() + (startHintRect.bottom() - startHintRect.top()) / 2; + if (clearBackground) + painter.fillRect(QRect(startHintRect.topLeft(), endHintRect.bottomRight()), palette().color(QWidget::backgroundRole())); + drawDoubleArrowLine(painter, startHintRect.right() + 1, middle, endHintRect.left() - 1, middle, 3); painter.fillRect(endHintRect, hintColor); painter.drawText(endHintRect, Qt::AlignCenter, endStrNum); @@ -398,6 +452,7 @@ void acTimelineGrid::drawTimeHints(QPainter& painter) } } + void acTimelineGrid::addOutOfRangeCharacterToHintString(QString& hintString, quint64 timeStamp) { if (timeStamp < m_nVisibleStartTime)