Changeset View
Standalone View
src/view/kateviewhelpers.cpp
Show All 26 Lines | |||||
27 | #include "katecmd.h" | 27 | #include "katecmd.h" | ||
28 | #include <ktexteditor/attribute.h> | 28 | #include <ktexteditor/attribute.h> | ||
29 | #include <ktexteditor/annotationinterface.h> | 29 | #include <ktexteditor/annotationinterface.h> | ||
30 | #include <ktexteditor/movingrange.h> | 30 | #include <ktexteditor/movingrange.h> | ||
31 | #include "kateconfig.h" | 31 | #include "kateconfig.h" | ||
32 | #include "katedocument.h" | 32 | #include "katedocument.h" | ||
33 | #include <katebuffer.h> | 33 | #include <katebuffer.h> | ||
34 | #include "katerenderer.h" | 34 | #include "katerenderer.h" | ||
35 | #include "kateannotationitemdelegate.h" | ||||
35 | #include "kateview.h" | 36 | #include "kateview.h" | ||
36 | #include "kateviewinternal.h" | 37 | #include "kateviewinternal.h" | ||
37 | #include "katelayoutcache.h" | 38 | #include "katelayoutcache.h" | ||
38 | #include "katetextlayout.h" | 39 | #include "katetextlayout.h" | ||
39 | #include "kateglobal.h" | 40 | #include "kateglobal.h" | ||
40 | #include "katepartdebug.h" | 41 | #include "katepartdebug.h" | ||
41 | #include "katecommandrangeexpressionparser.h" | 42 | #include "katecommandrangeexpressionparser.h" | ||
42 | #include "kateabstractinputmode.h" | 43 | #include "kateabstractinputmode.h" | ||
▲ Show 20 Lines • Show All 1388 Lines • ▼ Show 20 Line(s) | 1422 | KateIconBorder::KateIconBorder(KateViewInternal *internalView, QWidget *parent) | |||
1431 | , m_dynWrapIndicatorsOn(false) | 1432 | , m_dynWrapIndicatorsOn(false) | ||
1432 | , m_annotationBorderOn(false) | 1433 | , m_annotationBorderOn(false) | ||
1433 | , m_dynWrapIndicators(0) | 1434 | , m_dynWrapIndicators(0) | ||
1434 | , m_lastClickedLine(-1) | 1435 | , m_lastClickedLine(-1) | ||
1435 | , m_cachedLNWidth(0) | 1436 | , m_cachedLNWidth(0) | ||
1436 | , m_maxCharWidth(0.0) | 1437 | , m_maxCharWidth(0.0) | ||
1437 | , iconPaneWidth(16) | 1438 | , iconPaneWidth(16) | ||
1438 | , m_annotationBorderWidth(6) | 1439 | , m_annotationBorderWidth(6) | ||
1440 | , m_annotationItemDelegate(new KateAnnotationItemDelegate(m_viewInternal, this)) | ||||
1441 | , m_hasUniformAnnotationItemSizes(false) | ||||
1442 | , m_isDefaultAnnotationItemDelegate(true) | ||||
1439 | , m_foldingPreview(nullptr) | 1443 | , m_foldingPreview(nullptr) | ||
1440 | , m_foldingRange(nullptr) | 1444 | , m_foldingRange(nullptr) | ||
1441 | , m_nextHighlightBlock(-2) | 1445 | , m_nextHighlightBlock(-2) | ||
1442 | , m_currentBlockLine(-1) | 1446 | , m_currentBlockLine(-1) | ||
1443 | { | 1447 | { | ||
1444 | setAttribute(Qt::WA_StaticContents); | 1448 | setAttribute(Qt::WA_StaticContents); | ||
1445 | setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); | 1449 | setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); | ||
1446 | setMouseTracking(true); | 1450 | setMouseTracking(true); | ||
1447 | m_doc->setMarkDescription(MarkInterface::markType01, i18n("Bookmark")); | 1451 | m_doc->setMarkDescription(MarkInterface::markType01, i18n("Bookmark")); | ||
1448 | m_doc->setMarkPixmap(MarkInterface::markType01, QIcon::fromTheme(QStringLiteral("bookmarks")).pixmap(32, 32)); | 1452 | m_doc->setMarkPixmap(MarkInterface::markType01, QIcon::fromTheme(QStringLiteral("bookmarks")).pixmap(32, 32)); | ||
1449 | 1453 | | |||
1454 | connect(m_annotationItemDelegate, &AbstractAnnotationItemDelegate::sizeHintChanged, | ||||
1455 | this, &KateIconBorder::updateAnnotationBorderWidth); | ||||
1456 | | ||||
1450 | updateFont(); | 1457 | updateFont(); | ||
1451 | 1458 | | |||
1452 | m_delayFoldingHlTimer.setSingleShot(true); | 1459 | m_delayFoldingHlTimer.setSingleShot(true); | ||
1453 | m_delayFoldingHlTimer.setInterval(150); | 1460 | m_delayFoldingHlTimer.setInterval(150); | ||
1454 | connect(&m_delayFoldingHlTimer, SIGNAL(timeout()), this, SLOT(showBlock())); | 1461 | connect(&m_delayFoldingHlTimer, SIGNAL(timeout()), this, SLOT(showBlock())); | ||
1455 | 1462 | | |||
1456 | // user interaction (scrolling) hides e.g. preview | 1463 | // user interaction (scrolling) hides e.g. preview | ||
1457 | connect(m_view, SIGNAL(displayRangeChanged(KTextEditor::ViewPrivate*)), this, SLOT(displayRangeChanged())); | 1464 | connect(m_view, SIGNAL(displayRangeChanged(KTextEditor::ViewPrivate*)), this, SLOT(displayRangeChanged())); | ||
Show All 21 Lines | |||||
1479 | void KateIconBorder::setAnnotationBorderOn(bool enable) | 1486 | void KateIconBorder::setAnnotationBorderOn(bool enable) | ||
1480 | { | 1487 | { | ||
1481 | if (enable == m_annotationBorderOn) { | 1488 | if (enable == m_annotationBorderOn) { | ||
1482 | return; | 1489 | return; | ||
1483 | } | 1490 | } | ||
1484 | 1491 | | |||
1485 | m_annotationBorderOn = enable; | 1492 | m_annotationBorderOn = enable; | ||
1486 | 1493 | | |||
1494 | // make sure the tooltip is hidden | ||||
1495 | if (!m_annotationBorderOn && !m_hoveredAnnotationGroupIdentifier.isEmpty()) { | ||||
1496 | m_hoveredAnnotationGroupIdentifier.clear(); | ||||
1497 | hideAnnotationTooltip(); | ||||
1498 | } | ||||
1499 | | ||||
1487 | emit m_view->annotationBorderVisibilityChanged(m_view, enable); | 1500 | emit m_view->annotationBorderVisibilityChanged(m_view, enable); | ||
1488 | 1501 | | |||
1489 | updateGeometry(); | 1502 | updateGeometry(); | ||
1490 | 1503 | | |||
1491 | QTimer::singleShot(0, this, SLOT(update())); | 1504 | QTimer::singleShot(0, this, SLOT(update())); | ||
1492 | } | 1505 | } | ||
1493 | 1506 | | |||
1494 | void KateIconBorder::removeAnnotationHovering() | 1507 | void KateIconBorder::removeAnnotationHovering() | ||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Line(s) | 1618 | { | |||
1606 | const QFontMetricsF &fm = m_view->renderer()->config()->fontMetrics(); | 1619 | const QFontMetricsF &fm = m_view->renderer()->config()->fontMetrics(); | ||
1607 | m_maxCharWidth = 0.0; | 1620 | m_maxCharWidth = 0.0; | ||
1608 | // Loop to determine the widest numeric character in the current font. | 1621 | // Loop to determine the widest numeric character in the current font. | ||
1609 | // 48 is ascii '0' | 1622 | // 48 is ascii '0' | ||
1610 | for (int i = 48; i < 58; i++) { | 1623 | for (int i = 48; i < 58; i++) { | ||
1611 | const qreal charWidth = ceil(fm.width(QChar(i))); | 1624 | const qreal charWidth = ceil(fm.width(QChar(i))); | ||
1612 | m_maxCharWidth = qMax(m_maxCharWidth, charWidth); | 1625 | m_maxCharWidth = qMax(m_maxCharWidth, charWidth); | ||
1613 | } | 1626 | } | ||
1614 | | ||||
1615 | // the icon pane scales with the font... | 1627 | // the icon pane scales with the font... | ||
1616 | iconPaneWidth = fm.height(); | 1628 | iconPaneWidth = fm.height(); | ||
1617 | 1629 | | |||
1630 | calcAnnotationBorderWidth(); | ||||
1631 | | ||||
1618 | updateGeometry(); | 1632 | updateGeometry(); | ||
1619 | 1633 | | |||
1620 | QTimer::singleShot(0, this, SLOT(update())); | 1634 | QTimer::singleShot(0, this, SLOT(update())); | ||
1621 | } | 1635 | } | ||
1622 | 1636 | | |||
1623 | int KateIconBorder::lineNumberWidth() const | 1637 | int KateIconBorder::lineNumberWidth() const | ||
1624 | { | 1638 | { | ||
1625 | // width = (number of digits + 1) * char width | 1639 | // width = (number of digits + 1) * char width | ||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Line(s) | 1688 | { | |||
1703 | } else { | 1717 | } else { | ||
1704 | QPointF points[3] = { middle + QPointF(-halfSizeP, -halfSize), middle + QPointF(-halfSizeP, halfSize), middle + QPointF(halfSizeP, 0) }; | 1718 | QPointF points[3] = { middle + QPointF(-halfSizeP, -halfSize), middle + QPointF(-halfSizeP, halfSize), middle + QPointF(halfSizeP, 0) }; | ||
1705 | painter.drawConvexPolygon(points, 3); | 1719 | painter.drawConvexPolygon(points, 3); | ||
1706 | } | 1720 | } | ||
1707 | 1721 | | |||
1708 | painter.setRenderHint(QPainter::Antialiasing, false); | 1722 | painter.setRenderHint(QPainter::Antialiasing, false); | ||
1709 | } | 1723 | } | ||
1710 | 1724 | | |||
1725 | /** | ||||
1726 | * Helper class for an identifier which can be an empty or non-empty string or invalid. | ||||
1727 | * Avoids complicated explicit statements in code dealing with the identifier | ||||
1728 | * received as QVariant from a model. | ||||
1729 | */ | ||||
1730 | class KateAnnotationGroupIdentifier | ||||
1731 | { | ||||
1732 | public: | ||||
1733 | KateAnnotationGroupIdentifier(const QVariant &identifier) | ||||
1734 | : m_isValid(identifier.isValid() && identifier.canConvert<QString>()) | ||||
1735 | , m_id(m_isValid ? identifier.toString() : QString()) | ||||
1736 | { | ||||
1737 | } | ||||
1738 | KateAnnotationGroupIdentifier() = default; | ||||
1739 | KateAnnotationGroupIdentifier(const KateAnnotationGroupIdentifier &rhs) = default; | ||||
1740 | | ||||
1741 | KateAnnotationGroupIdentifier& operator=(const KateAnnotationGroupIdentifier &rhs) | ||||
1742 | { | ||||
1743 | m_isValid = rhs.m_isValid; | ||||
1744 | m_id = rhs.m_id; | ||||
1745 | return *this; | ||||
1746 | } | ||||
1747 | KateAnnotationGroupIdentifier& operator=(const QVariant &identifier) | ||||
1748 | { | ||||
1749 | m_isValid = (identifier.isValid() && identifier.canConvert<QString>()); | ||||
1750 | if (m_isValid) { | ||||
1751 | m_id = identifier.toString(); | ||||
1752 | } else { | ||||
1753 | m_id.clear(); | ||||
1754 | } | ||||
1755 | return *this; | ||||
1756 | } | ||||
1757 | | ||||
1758 | bool operator==(const KateAnnotationGroupIdentifier &rhs) const | ||||
1759 | { | ||||
1760 | return (m_isValid == rhs.m_isValid) && (!m_isValid || (m_id == rhs.m_id)); | ||||
1761 | } | ||||
1762 | bool operator!=(const KateAnnotationGroupIdentifier &rhs) const | ||||
1763 | { | ||||
1764 | return (m_isValid != rhs.m_isValid) || (m_isValid && (m_id != rhs.m_id)); | ||||
1765 | } | ||||
1766 | | ||||
1767 | void clear() | ||||
1768 | { | ||||
1769 | m_isValid = false; | ||||
1770 | m_id.clear(); | ||||
1771 | } | ||||
1772 | bool isValid() const { return m_isValid; } | ||||
1773 | const QString& id() const { return m_id; } | ||||
1774 | | ||||
1775 | private: | ||||
1776 | bool m_isValid = false; | ||||
1777 | QString m_id; | ||||
1778 | }; | ||||
1779 | | ||||
1780 | /** | ||||
1781 | * Helper class for iterative calculation of data regarding the position | ||||
1782 | * of a line with regard to annotation item grouping. | ||||
1783 | */ | ||||
1784 | class KateAnnotationGroupPositionState | ||||
1785 | { | ||||
1786 | public: | ||||
1787 | /** | ||||
1788 | * @param startz first rendered displayed line | ||||
1789 | * @param isUsed flag whether the KateAnnotationGroupPositionState object will | ||||
1790 | * be used or is just created due to being on the stack | ||||
1791 | */ | ||||
1792 | KateAnnotationGroupPositionState(KateViewInternal *viewInternal, | ||||
1793 | KTextEditor::AnnotationModel *model, | ||||
1794 | const QString &hoveredAnnotationGroupIdentifier, | ||||
1795 | uint startz, | ||||
1796 | bool isUsed); | ||||
1797 | /** | ||||
1798 | * @param styleOption option to fill with data for the given line | ||||
1799 | * @param z rendered displayed line | ||||
1800 | * @param realLine real line which is rendered here (passed to avoid another look-up) | ||||
1801 | */ | ||||
1802 | void nextLine(KTextEditor::StyleOptionAnnotationItem &styleOption, uint z, int realLine); | ||||
1803 | | ||||
1804 | private: | ||||
1805 | KateViewInternal *m_viewInternal; | ||||
1806 | KTextEditor::AnnotationModel *m_model = nullptr; | ||||
1807 | const QString m_hoveredAnnotationGroupIdentifier; | ||||
1808 | | ||||
1809 | int m_visibleWrappedLineInAnnotationGroup = -1; | ||||
1810 | KateAnnotationGroupIdentifier m_lastAnnotationGroupIdentifier; | ||||
1811 | KateAnnotationGroupIdentifier m_nextAnnotationGroupIdentifier; | ||||
1812 | bool m_isSameAnnotationGroupsSinceLast = false; | ||||
1813 | }; | ||||
1814 | | ||||
1815 | KateAnnotationGroupPositionState::KateAnnotationGroupPositionState(KateViewInternal *viewInternal, | ||||
1816 | KTextEditor::AnnotationModel *model, | ||||
1817 | const QString &hoveredAnnotationGroupIdentifier, | ||||
1818 | uint startz, | ||||
1819 | bool isUsed) | ||||
1820 | : m_viewInternal(viewInternal) | ||||
1821 | , m_model(model) | ||||
1822 | , m_hoveredAnnotationGroupIdentifier(hoveredAnnotationGroupIdentifier) | ||||
1823 | { | ||||
1824 | if (!isUsed) { | ||||
1825 | return; | ||||
1826 | } | ||||
1827 | | ||||
1828 | if (!m_model || (startz >= m_viewInternal->cache()->viewCacheLineCount())) { | ||||
1829 | return; | ||||
1830 | } | ||||
1831 | | ||||
1832 | const auto realLineAtStart = m_viewInternal->cache()->viewLine(startz).line(); | ||||
1833 | m_nextAnnotationGroupIdentifier = m_model->data(realLineAtStart, | ||||
1834 | (Qt::ItemDataRole)KTextEditor::AnnotationModel::GroupIdentifierRole); | ||||
1835 | if (m_nextAnnotationGroupIdentifier.isValid()) { | ||||
1836 | // estimate state of annotation group before first rendered line | ||||
1837 | if (startz == 0) { | ||||
1838 | if (realLineAtStart > 0) { | ||||
1839 | // TODO: here we would want to scan until the next line that would be displayed, | ||||
1840 | // to see if there are any group changes until then | ||||
1841 | // for now simply taking neighbour line into account, not a grave bug on the first displayed line | ||||
1842 | m_lastAnnotationGroupIdentifier = m_model->data(realLineAtStart - 1, | ||||
1843 | (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole); | ||||
1844 | m_isSameAnnotationGroupsSinceLast = (m_lastAnnotationGroupIdentifier == m_nextAnnotationGroupIdentifier); | ||||
1845 | } | ||||
1846 | } else { | ||||
1847 | const auto realLineBeforeStart = m_viewInternal->cache()->viewLine(startz-1).line(); | ||||
1848 | m_lastAnnotationGroupIdentifier = m_model->data(realLineBeforeStart, (Qt::ItemDataRole)KTextEditor::AnnotationModel::GroupIdentifierRole); | ||||
1849 | if (m_lastAnnotationGroupIdentifier.isValid()) { | ||||
1850 | if (m_lastAnnotationGroupIdentifier.id() == m_nextAnnotationGroupIdentifier.id()) { | ||||
1851 | m_isSameAnnotationGroupsSinceLast = true; | ||||
1852 | // estimate m_visibleWrappedLineInAnnotationGroup from lines before startz | ||||
1853 | for (uint z = startz; z > 0; --z) { | ||||
1854 | const auto realLine = m_viewInternal->cache()->viewLine(z-1).line(); | ||||
1855 | const KateAnnotationGroupIdentifier identifier = m_model->data(realLine, (Qt::ItemDataRole)KTextEditor::AnnotationModel::GroupIdentifierRole); | ||||
1856 | if (identifier != m_lastAnnotationGroupIdentifier) { | ||||
1857 | break; | ||||
1858 | } | ||||
1859 | ++m_visibleWrappedLineInAnnotationGroup; | ||||
1860 | } | ||||
1861 | } | ||||
1862 | } | ||||
1863 | } | ||||
1864 | } | ||||
1865 | } | ||||
1866 | | ||||
1867 | void KateAnnotationGroupPositionState::nextLine(KTextEditor::StyleOptionAnnotationItem &styleOption, | ||||
1868 | uint z, int realLine) | ||||
1869 | { | ||||
1870 | styleOption.wrappedLine = m_viewInternal->cache()->viewLine(z).viewLine(); | ||||
1871 | styleOption.wrappedLineCount = m_viewInternal->cache()->viewLineCount(realLine); | ||||
1872 | | ||||
1873 | // Estimate position in group | ||||
1874 | const KateAnnotationGroupIdentifier annotationGroupIdentifier = m_nextAnnotationGroupIdentifier; | ||||
1875 | bool isSameAnnotationGroupsSinceThis = false; | ||||
1876 | // Calculate next line's group identifier | ||||
1877 | // shortcut: assuming wrapped lines are always displayed together, test is simple | ||||
1878 | if (styleOption.wrappedLine+1 < styleOption.wrappedLineCount) { | ||||
1879 | m_nextAnnotationGroupIdentifier = annotationGroupIdentifier; | ||||
1880 | isSameAnnotationGroupsSinceThis = true; | ||||
1881 | } else { | ||||
1882 | if (z+1 < m_viewInternal->cache()->viewCacheLineCount()) { | ||||
1883 | const int realLineAfter = m_viewInternal->cache()->viewLine(z+1).line(); | ||||
1884 | // search for any realLine with a different group id, also the non-displayed | ||||
1885 | int rl = realLine + 1; | ||||
1886 | for (; rl <= realLineAfter; ++rl) { | ||||
1887 | m_nextAnnotationGroupIdentifier = m_model->data(rl, (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole); | ||||
1888 | if (!m_nextAnnotationGroupIdentifier.isValid() || | ||||
1889 | (m_nextAnnotationGroupIdentifier.id() != annotationGroupIdentifier.id())) { | ||||
1890 | break; | ||||
1891 | } | ||||
1892 | } | ||||
1893 | isSameAnnotationGroupsSinceThis = (rl > realLineAfter); | ||||
1894 | if (rl < realLineAfter) { | ||||
1895 | m_nextAnnotationGroupIdentifier = m_model->data(realLineAfter, (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole); | ||||
1896 | } | ||||
1897 | } else { | ||||
1898 | // TODO: check next line after display end | ||||
1899 | m_nextAnnotationGroupIdentifier.clear(); | ||||
1900 | isSameAnnotationGroupsSinceThis = false; | ||||
1901 | } | ||||
1902 | } | ||||
1903 | | ||||
1904 | if (annotationGroupIdentifier.isValid()) { | ||||
1905 | if (m_hoveredAnnotationGroupIdentifier == annotationGroupIdentifier.id()) { | ||||
1906 | styleOption.state |= QStyle::State_MouseOver; | ||||
1907 | } else { | ||||
1908 | styleOption.state &= ~QStyle::State_MouseOver; | ||||
1909 | } | ||||
1910 | | ||||
1911 | if (m_isSameAnnotationGroupsSinceLast) { | ||||
1912 | ++m_visibleWrappedLineInAnnotationGroup; | ||||
1913 | } else { | ||||
1914 | m_visibleWrappedLineInAnnotationGroup = 0; | ||||
1915 | } | ||||
1916 | | ||||
1917 | styleOption.annotationItemGroupingPosition = StyleOptionAnnotationItem::InGroup; | ||||
1918 | if (!m_isSameAnnotationGroupsSinceLast) { | ||||
1919 | styleOption.annotationItemGroupingPosition |= StyleOptionAnnotationItem::GroupBegin; | ||||
1920 | } | ||||
1921 | if (!isSameAnnotationGroupsSinceThis) { | ||||
1922 | styleOption.annotationItemGroupingPosition |= StyleOptionAnnotationItem::GroupEnd; | ||||
1923 | } | ||||
1924 | } else { | ||||
1925 | m_visibleWrappedLineInAnnotationGroup = 0; | ||||
1926 | } | ||||
1927 | styleOption.visibleWrappedLineInGroup = m_visibleWrappedLineInAnnotationGroup; | ||||
1928 | | ||||
1929 | m_lastAnnotationGroupIdentifier = m_nextAnnotationGroupIdentifier; | ||||
1930 | m_isSameAnnotationGroupsSinceLast = isSameAnnotationGroupsSinceThis; | ||||
1931 | } | ||||
1932 | | ||||
1933 | | ||||
1711 | void KateIconBorder::paintBorder(int /*x*/, int y, int /*width*/, int height) | 1934 | void KateIconBorder::paintBorder(int /*x*/, int y, int /*width*/, int height) | ||
1712 | { | 1935 | { | ||
1713 | uint h = m_view->renderer()->lineHeight(); | 1936 | uint h = m_view->renderer()->lineHeight(); | ||
1714 | uint startz = (y / h); | 1937 | uint startz = (y / h); | ||
1715 | uint endz = startz + 1 + (height / h); | 1938 | uint endz = startz + 1 + (height / h); | ||
1716 | uint lineRangesSize = m_viewInternal->cache()->viewCacheLineCount(); | 1939 | uint lineRangesSize = m_viewInternal->cache()->viewCacheLineCount(); | ||
1717 | uint currentLine = m_view->cursorPosition().line(); | 1940 | uint currentLine = m_view->cursorPosition().line(); | ||
1718 | 1941 | | |||
Show All 22 Lines | |||||
1741 | int w(this->width()); // sane value/calc only once | 1964 | int w(this->width()); // sane value/calc only once | ||
1742 | 1965 | | |||
1743 | QPainter p(this); | 1966 | QPainter p(this); | ||
1744 | p.setRenderHints(QPainter::TextAntialiasing); | 1967 | p.setRenderHints(QPainter::TextAntialiasing); | ||
1745 | p.setFont(m_view->renderer()->config()->font()); // for line numbers | 1968 | p.setFont(m_view->renderer()->config()->font()); // for line numbers | ||
1746 | 1969 | | |||
1747 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | 1970 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | ||
1748 | m_view->annotationModel() : m_doc->annotationModel(); | 1971 | m_view->annotationModel() : m_doc->annotationModel(); | ||
1972 | KateAnnotationGroupPositionState annotationGroupPositionState(m_viewInternal, model, | ||||
1973 | m_hoveredAnnotationGroupIdentifier, | ||||
1974 | startz, m_annotationBorderOn); | ||||
1749 | 1975 | | |||
1750 | for (uint z = startz; z <= endz; z++) { | 1976 | for (uint z = startz; z <= endz; z++) { | ||
1751 | int y = h * z; | 1977 | int y = h * z; | ||
1752 | int realLine = -1; | 1978 | int realLine = -1; | ||
1753 | 1979 | | |||
1754 | if (z < lineRangesSize) { | 1980 | if (z < lineRangesSize) { | ||
1755 | realLine = m_viewInternal->cache()->viewLine(z).line(); | 1981 | realLine = m_viewInternal->cache()->viewLine(z).line(); | ||
1756 | } | 1982 | } | ||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Line(s) | |||||
1802 | } | 2028 | } | ||
1803 | 2029 | | |||
1804 | // annotation information | 2030 | // annotation information | ||
1805 | if (m_annotationBorderOn) { | 2031 | if (m_annotationBorderOn) { | ||
1806 | // Draw a border line between annotations and the line numbers | 2032 | // Draw a border line between annotations and the line numbers | ||
1807 | p.setPen(m_view->renderer()->config()->lineNumberColor()); | 2033 | p.setPen(m_view->renderer()->config()->lineNumberColor()); | ||
1808 | p.setBrush(m_view->renderer()->config()->lineNumberColor()); | 2034 | p.setBrush(m_view->renderer()->config()->lineNumberColor()); | ||
1809 | 2035 | | |||
1810 | int borderWidth = m_annotationBorderWidth; | 2036 | const int borderX = lnX + m_annotationBorderWidth; | ||
1811 | p.drawLine(lnX + borderWidth + 1, y, lnX + borderWidth + 1, y + h); | 2037 | p.drawLine(borderX, y, borderX, y + h); | ||
1812 | 2038 | | |||
1813 | if ((realLine > -1) && model) { | 2039 | if ((realLine > -1) && model) { | ||
1814 | // Fetch data from the model | 2040 | KTextEditor::StyleOptionAnnotationItem styleOption; | ||
1815 | QVariant text = model->data(realLine, Qt::DisplayRole); | 2041 | initStyleOption(&styleOption); | ||
1816 | QVariant foreground = model->data(realLine, Qt::ForegroundRole); | 2042 | styleOption.rect.setRect(lnX, y, m_annotationBorderWidth, h); | ||
1817 | QVariant background = model->data(realLine, Qt::BackgroundRole); | | |||
1818 | // Fill the background | | |||
1819 | if (background.isValid()) { | | |||
1820 | p.fillRect(lnX, y, borderWidth + 1, h, background.value<QBrush>()); | | |||
1821 | } | | |||
1822 | // Set the pen for drawing the foreground | | |||
1823 | if (foreground.isValid()) { | | |||
1824 | p.setPen(foreground.value<QPen>()); | | |||
1825 | } | | |||
1826 | 2043 | | |||
1827 | // Draw a border around all adjacent entries that have the same text as the currently hovered one | 2044 | annotationGroupPositionState.nextLine(styleOption, z, realLine); | ||
1828 | const QVariant identifier = model->data( realLine, (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ); | | |||
1829 | if( m_hoveredAnnotationGroupIdentifier == identifier.toString() ) { | | |||
1830 | p.drawLine(lnX, y, lnX, y + h); | | |||
1831 | p.drawLine(lnX + borderWidth, y, lnX + borderWidth, y + h); | | |||
1832 | | ||||
1833 | QVariant beforeText = model->data(realLine - 1, Qt::DisplayRole); | | |||
1834 | QVariant afterText = model->data(realLine + 1, Qt::DisplayRole); | | |||
1835 | if (((beforeText.isValid() && beforeText.canConvert<QString>() | | |||
1836 | && text.isValid() && text.canConvert<QString>() | | |||
1837 | && beforeText.toString() != text.toString()) || realLine == 0) | | |||
1838 | && m_viewInternal->cache()->viewLine(z).viewLine() == 0) { | | |||
1839 | p.drawLine(lnX + 1, y, lnX + borderWidth, y); | | |||
1840 | } | | |||
1841 | | ||||
1842 | if (((afterText.isValid() && afterText.canConvert<QString>() | | |||
1843 | && text.isValid() && text.canConvert<QString>() | | |||
1844 | && afterText.toString() != text.toString()) | | |||
1845 | || realLine == m_view->doc()->lines() - 1) | | |||
1846 | && m_viewInternal->cache()->viewLine(z).viewLine() == m_viewInternal->cache()->viewLineCount(realLine) - 1) { | | |||
1847 | p.drawLine(lnX + 1, y + h - 1, lnX + borderWidth, y + h - 1); | | |||
1848 | } | | |||
1849 | } | | |||
1850 | if (foreground.isValid()) { | | |||
1851 | QPen pen = p.pen(); | | |||
1852 | pen.setWidth(1); | | |||
1853 | p.setPen(pen); | | |||
1854 | } | | |||
1855 | 2045 | | |||
1856 | // Now draw the normal text | 2046 | m_annotationItemDelegate->paint(&p, styleOption, model, realLine); | ||
1857 | if (text.isValid() && text.canConvert<QString>() && (m_viewInternal->cache()->viewLine(z).startCol() == 0)) { | | |||
1858 | p.drawText(lnX + 3, y, borderWidth - 3, h, Qt::AlignLeft | Qt::AlignVCenter, text.toString()); | | |||
1859 | } | | |||
1860 | } | 2047 | } | ||
1861 | 2048 | | |||
1862 | // adjust current X position and reset the pen and brush | 2049 | // adjust current X position | ||
1863 | lnX += borderWidth + 2; | 2050 | lnX += m_annotationBorderWidth + /* separator line width */1; | ||
1864 | } | 2051 | } | ||
1865 | 2052 | | |||
1866 | // line number | 2053 | // line number | ||
1867 | if (m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn)) { | 2054 | if (m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn)) { | ||
1868 | if (realLine > -1) { | 2055 | if (realLine > -1) { | ||
1869 | int distanceToCurrent = abs(realLine - static_cast<int>(currentLine)); | 2056 | int distanceToCurrent = abs(realLine - static_cast<int>(currentLine)); | ||
1870 | QColor color; | 2057 | QColor color; | ||
1871 | 2058 | | |||
▲ Show 20 Lines • Show All 324 Lines • ▼ Show 20 Line(s) | 2382 | } else { | |||
2196 | hideBlock(); | 2383 | hideBlock(); | ||
2197 | } | 2384 | } | ||
2198 | if (positionToArea(e->pos()) == AnnotationBorder) { | 2385 | if (positionToArea(e->pos()) == AnnotationBorder) { | ||
2199 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | 2386 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | ||
2200 | m_view->annotationModel() : m_doc->annotationModel(); | 2387 | m_view->annotationModel() : m_doc->annotationModel(); | ||
2201 | if (model) { | 2388 | if (model) { | ||
2202 | m_hoveredAnnotationGroupIdentifier = model->data( t.line(), | 2389 | m_hoveredAnnotationGroupIdentifier = model->data( t.line(), | ||
2203 | (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ).toString(); | 2390 | (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ).toString(); | ||
2204 | showAnnotationTooltip(t.line(), e->globalPos()); | 2391 | const QPoint viewRelativePos = m_view->mapFromGlobal(e->globalPos()); | ||
2392 | QHelpEvent helpEvent(QEvent::ToolTip, viewRelativePos, e->globalPos()); | ||||
2393 | KTextEditor::StyleOptionAnnotationItem styleOption; | ||||
2394 | initStyleOption(&styleOption); | ||||
2395 | // TODO: add subline info | ||||
2396 | m_annotationItemDelegate->helpEvent(&helpEvent, m_view, styleOption, model, t.line()); | ||||
2397 | | ||||
2205 | QTimer::singleShot(0, this, SLOT(update())); | 2398 | QTimer::singleShot(0, this, SLOT(update())); | ||
2206 | } | 2399 | } | ||
2207 | } else { | 2400 | } else { | ||
2208 | if (positionToArea(e->pos()) == IconBorder) { | 2401 | if (positionToArea(e->pos()) == IconBorder) { | ||
2209 | m_doc->requestMarkTooltip(t.line(), e->globalPos()); | 2402 | m_doc->requestMarkTooltip(t.line(), e->globalPos()); | ||
2210 | } | 2403 | } | ||
2211 | 2404 | | |||
2212 | m_hoveredAnnotationGroupIdentifier.clear(); | 2405 | m_hoveredAnnotationGroupIdentifier.clear(); | ||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Line(s) | 2450 | if (area == FoldingMarkers) { | |||
2282 | delete m_foldingPreview; | 2475 | delete m_foldingPreview; | ||
2283 | } | 2476 | } | ||
2284 | 2477 | | |||
2285 | if (area == AnnotationBorder) { | 2478 | if (area == AnnotationBorder) { | ||
2286 | const bool singleClick = style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this); | 2479 | const bool singleClick = style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this); | ||
2287 | if (e->button() == Qt::LeftButton && singleClick) { | 2480 | if (e->button() == Qt::LeftButton && singleClick) { | ||
2288 | emit m_view->annotationActivated(m_view, cursorOnLine); | 2481 | emit m_view->annotationActivated(m_view, cursorOnLine); | ||
2289 | } else if (e->button() == Qt::RightButton) { | 2482 | } else if (e->button() == Qt::RightButton) { | ||
2290 | showAnnotationMenu(cursorOnLine, e->globalPos()); | 2483 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | ||
2484 | m_view->annotationModel() : m_doc->annotationModel(); | ||||
2485 | const QPoint pos = m_view->mapFromGlobal(e->globalPos()); | ||||
2486 | KTextEditor::StyleOptionAnnotationItem styleOption; | ||||
2487 | initStyleOption(&styleOption); | ||||
2488 | // TODO: add subline info | ||||
2489 | m_annotationItemDelegate->contextMenuRequested(m_view, pos, | ||||
2490 | styleOption, model, cursorOnLine); | ||||
2291 | } | 2491 | } | ||
2292 | } | 2492 | } | ||
2293 | } | 2493 | } | ||
2294 | 2494 | | |||
2295 | QMouseEvent forward(QEvent::MouseButtonRelease, | 2495 | QMouseEvent forward(QEvent::MouseButtonRelease, | ||
2296 | QPoint(0, e->y()), e->button(), e->buttons(), e->modifiers()); | 2496 | QPoint(0, e->y()), e->button(), e->buttons(), e->modifiers()); | ||
2297 | m_viewInternal->mouseReleaseEvent(&forward); | 2497 | m_viewInternal->mouseReleaseEvent(&forward); | ||
2298 | } | 2498 | } | ||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Line(s) | 2586 | } else { | |||
2388 | if (m_doc->mark(line) & markType) { | 2588 | if (m_doc->mark(line) & markType) { | ||
2389 | m_doc->removeMark(line, markType); | 2589 | m_doc->removeMark(line, markType); | ||
2390 | } else { | 2590 | } else { | ||
2391 | m_doc->addMark(line, markType); | 2591 | m_doc->addMark(line, markType); | ||
2392 | } | 2592 | } | ||
2393 | } | 2593 | } | ||
2394 | } | 2594 | } | ||
2395 | 2595 | | |||
2396 | void KateIconBorder::showAnnotationTooltip(int line, const QPoint &pos) | 2596 | KTextEditor::AbstractAnnotationItemDelegate* KateIconBorder::annotationItemDelegate() const | ||
2397 | { | 2597 | { | ||
2398 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | 2598 | return m_annotationItemDelegate; | ||
2399 | m_view->annotationModel() : m_doc->annotationModel(); | 2599 | } | ||
2400 | 2600 | | |||
2401 | if (model) { | 2601 | void KateIconBorder::setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate) | ||
2402 | QVariant data = model->data(line, Qt::ToolTipRole); | 2602 | { | ||
2403 | QString tip = data.toString(); | 2603 | if (delegate == m_annotationItemDelegate) { | ||
2404 | if (!tip.isEmpty()) { | 2604 | return; | ||
2405 | QToolTip::showText(pos, data.toString(), this); | | |||
2406 | } | 2605 | } | ||
2606 | | ||||
2607 | // reset to default, but already on that? | ||||
2608 | if (!delegate && m_isDefaultAnnotationItemDelegate) { | ||||
2609 | // nothing to do | ||||
2610 | return; | ||||
2407 | } | 2611 | } | ||
2612 | | ||||
2613 | // make sure the tooltip is hidden | ||||
2614 | if (m_annotationBorderOn && !m_hoveredAnnotationGroupIdentifier.isEmpty()) { | ||||
2615 | m_hoveredAnnotationGroupIdentifier.clear(); | ||||
2616 | hideAnnotationTooltip(); | ||||
2408 | } | 2617 | } | ||
2409 | 2618 | | |||
2410 | int KateIconBorder::annotationLineWidth(int line) | 2619 | disconnect(m_annotationItemDelegate, &AbstractAnnotationItemDelegate::sizeHintChanged, | ||
2411 | { | 2620 | this, &KateIconBorder::updateAnnotationBorderWidth); | ||
2412 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | | |||
2413 | m_view->annotationModel() : m_doc->annotationModel(); | | |||
2414 | 2621 | | |||
2415 | if (model) { | 2622 | if (!delegate) { | ||
2416 | QVariant data = model->data(line, Qt::DisplayRole); | 2623 | // reset to a default delegate | ||
2417 | return data.toString().length() * m_maxCharWidth + 8; | 2624 | m_annotationItemDelegate = new KateAnnotationItemDelegate(m_viewInternal, this); | ||
2625 | m_isDefaultAnnotationItemDelegate = true; | ||||
2626 | } else { | ||||
2627 | // drop any default delegate | ||||
2628 | if (m_isDefaultAnnotationItemDelegate) { | ||||
2629 | delete m_annotationItemDelegate; | ||||
2630 | m_isDefaultAnnotationItemDelegate = false; | ||||
2418 | } | 2631 | } | ||
2419 | return 8; | 2632 | | ||
2633 | // TODO: catch delegate being destroyed | ||||
dhaumann: You could do that via QPointer, if the delegate is QObject base. It's a poor man's solution… | |||||
2634 | m_annotationItemDelegate = delegate; | ||||
2420 | } | 2635 | } | ||
2421 | 2636 | | |||
2422 | void KateIconBorder::updateAnnotationLine(int line) | 2637 | connect(m_annotationItemDelegate, &AbstractAnnotationItemDelegate::sizeHintChanged, | ||
2423 | { | 2638 | this, &KateIconBorder::updateAnnotationBorderWidth); | ||
2424 | if (annotationLineWidth(line) > m_annotationBorderWidth) { | 2639 | | ||
2425 | m_annotationBorderWidth = annotationLineWidth(line); | 2640 | if (m_annotationBorderOn) { | ||
2426 | updateGeometry(); | 2641 | updateGeometry(); | ||
2427 | 2642 | | |||
2428 | QTimer::singleShot(0, this, SLOT(update())); | 2643 | QTimer::singleShot(0, this, SLOT(update())); | ||
Do you really need the timer here? I thought update() goes through the event queue anyways? dhaumann: Do you really need the timer here? I thought update() goes through the event queue anyways? | |||||
I copied existing code here, for consistency. No really sure when to call it directly and when to go via event loop. kossebau: I copied existing code here, for consistency. No really sure when to call it directly and when… | |||||
2429 | } | 2644 | } | ||
2430 | } | 2645 | } | ||
2431 | 2646 | | |||
2432 | void KateIconBorder::showAnnotationMenu(int line, const QPoint &pos) | 2647 | void KateIconBorder::initStyleOption(KTextEditor::StyleOptionAnnotationItem* styleOption) const | ||
2433 | { | 2648 | { | ||
2434 | QMenu menu; | 2649 | styleOption->initFrom(this); | ||
2435 | QAction a(i18n("Disable Annotation Bar"), &menu); | 2650 | styleOption->view = m_view; | ||
2436 | a.setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); | 2651 | styleOption->decorationSize = QSize(iconPaneWidth, iconPaneWidth); | ||
2437 | menu.addAction(&a); | 2652 | styleOption->contentFontMetrics = m_view->renderer()->config()->fontMetrics(); | ||
2438 | emit m_view->annotationContextMenuAboutToShow(m_view, &menu, line); | 2653 | // TODO have delegate paint all background or not? | ||
2439 | if (menu.exec(pos) == &a) { | 2654 | // styleOption->backgroundBrush = m_view->renderer()->config()->iconBarColor(); | ||
2440 | m_view->setAnnotationBorderVisible(false); | 2655 | } | ||
2656 | | ||||
2657 | void KateIconBorder::updateAnnotationLine(int line) | ||||
2658 | { | ||||
2659 | // TODO: where is that magic number from? | ||||
2660 | int width = 8; | ||||
2661 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | ||||
2662 | m_view->annotationModel() : m_doc->annotationModel(); | ||||
2663 | | ||||
2664 | if (model) { | ||||
2665 | KTextEditor::StyleOptionAnnotationItem styleOption; | ||||
2666 | initStyleOption(&styleOption); | ||||
2nd time this comment pops up. Do you have an answer? In Qt, this would be an extra setAutoFillBackgroundEnabled(bool) , or something similar... In any case - this needs to be decided before the interface goes live :-) dhaumann: 2nd time this comment pops up. Do you have an answer? In Qt, this would be an extra… | |||||
Yes, this is a TODO question to you Kate developers :) p.fillRect(0, y, w - 5, h, m_view->renderer()->config()->iconBarColor()); before starting to draw the annotation stuff and icons, which also cares for the displayed lines with no content. kossebau: Yes, this is a TODO question to you Kate developers :)
Currently KateIconBorder::paintBorder()… | |||||
2667 | width = m_annotationItemDelegate->sizeHint(styleOption, model, line).width(); | ||||
2668 | } | ||||
2669 | | ||||
2670 | if (width > m_annotationBorderWidth) { | ||||
2671 | m_annotationBorderWidth = width; | ||||
dhaumann: Maybe 4+4 margin or so? | |||||
2672 | updateGeometry(); | ||||
2673 | | ||||
2674 | QTimer::singleShot(0, this, SLOT(update())); | ||||
2441 | } | 2675 | } | ||
2442 | } | 2676 | } | ||
2443 | 2677 | | |||
2444 | void KateIconBorder::hideAnnotationTooltip() | 2678 | void KateIconBorder::hideAnnotationTooltip() | ||
2445 | { | 2679 | { | ||
2446 | QToolTip::hideText(); | 2680 | m_annotationItemDelegate->hideTooltip(m_view); | ||
2447 | } | 2681 | } | ||
2448 | 2682 | | |||
2449 | void KateIconBorder::updateAnnotationBorderWidth() | 2683 | void KateIconBorder::updateAnnotationBorderWidth() | ||
2450 | { | 2684 | { | ||
2685 | calcAnnotationBorderWidth(); | ||||
2686 | | ||||
2687 | updateGeometry(); | ||||
2688 | | ||||
2689 | QTimer::singleShot(0, this, SLOT(update())); | ||||
2690 | } | ||||
2691 | | ||||
2692 | void KateIconBorder::calcAnnotationBorderWidth() | ||||
2693 | { | ||||
2694 | // TODO: another magic number, not matching the one in updateAnnotationLine() | ||||
2451 | m_annotationBorderWidth = 6; | 2695 | m_annotationBorderWidth = 6; | ||
2452 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | 2696 | KTextEditor::AnnotationModel *model = m_view->annotationModel() ? | ||
2453 | m_view->annotationModel() : m_doc->annotationModel(); | 2697 | m_view->annotationModel() : m_doc->annotationModel(); | ||
2454 | 2698 | | |||
dhaumann: Again, single shot timer needed? | |||||
2455 | if (model) { | 2699 | if (model) { | ||
2456 | for (int i = 0; i < m_view->doc()->lines(); i++) { | 2700 | KTextEditor::StyleOptionAnnotationItem styleOption; | ||
2457 | int curwidth = annotationLineWidth(i); | 2701 | initStyleOption(&styleOption); | ||
2702 | | ||||
2703 | const int lineCount = m_view->doc()->lines(); | ||||
2704 | if (lineCount > 0) { | ||||
2705 | const int checkedLineCount = m_view->uniformAnnotationItemSizes() ? 1 : lineCount; | ||||
2706 | for (int i = 0; i < checkedLineCount; ++i) { | ||||
2707 | const int curwidth = m_annotationItemDelegate->sizeHint(styleOption, model, i).width(); | ||||
2458 | if (curwidth > m_annotationBorderWidth) { | 2708 | if (curwidth > m_annotationBorderWidth) { | ||
2459 | m_annotationBorderWidth = curwidth; | 2709 | m_annotationBorderWidth = curwidth; | ||
2460 | } | 2710 | } | ||
2461 | } | 2711 | } | ||
2462 | } | 2712 | } | ||
2463 | 2713 | } | |||
2464 | updateGeometry(); | | |||
2465 | | ||||
2466 | QTimer::singleShot(0, this, SLOT(update())); | | |||
2467 | } | 2714 | } | ||
2468 | 2715 | | |||
2469 | void KateIconBorder::annotationModelChanged(KTextEditor::AnnotationModel *oldmodel, KTextEditor::AnnotationModel *newmodel) | 2716 | void KateIconBorder::annotationModelChanged(KTextEditor::AnnotationModel *oldmodel, KTextEditor::AnnotationModel *newmodel) | ||
2470 | { | 2717 | { | ||
2471 | if (oldmodel) { | 2718 | if (oldmodel) { | ||
2472 | oldmodel->disconnect(this); | 2719 | oldmodel->disconnect(this); | ||
2473 | } | 2720 | } | ||
2474 | if (newmodel) { | 2721 | if (newmodel) { | ||
▲ Show 20 Lines • Show All 425 Lines • Show Last 20 Lines |
You could do that via QPointer, if the delegate is QObject base. It's a poor man's solution which has many issues on its own, though. Maybe it's better to simply assume the user uses the interface correctly...