1 | /* |
2 | SPDX-FileCopyrightText: 2002-2005 Hamish Rodda <rodda@kde.org> |
3 | SPDX-FileCopyrightText: 2003 Anakim Border <aborder@sources.sourceforge.net> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #include "katelinelayout.h" |
9 | #include "katetextfolding.h" |
10 | #include "katetextlayout.h" |
11 | |
12 | #include <QTextLine> |
13 | |
14 | #include "katepartdebug.h" |
15 | |
16 | #include "katedocument.h" |
17 | #include "katerenderer.h" |
18 | |
19 | KateLineLayout::KateLineLayout(KateRenderer &renderer) |
20 | : m_renderer(renderer) |
21 | , m_line(-1) |
22 | , m_virtualLine(-1) |
23 | , m_layout(nullptr) |
24 | { |
25 | } |
26 | |
27 | void KateLineLayout::clear() |
28 | { |
29 | m_textLine.reset(); |
30 | m_line = -1; |
31 | m_virtualLine = -1; |
32 | shiftX = 0; |
33 | // not touching dirty |
34 | m_layout.reset(); |
35 | // not touching layout dirty |
36 | } |
37 | |
38 | bool KateLineLayout::includesCursor(const KTextEditor::Cursor realCursor) const |
39 | { |
40 | return realCursor.line() == line(); |
41 | } |
42 | |
43 | const Kate::TextLine &KateLineLayout::textLine(bool reloadForce) const |
44 | { |
45 | if (reloadForce || !m_textLine) { |
46 | m_textLine.reset(); |
47 | if (m_line >= 0 && m_line < m_renderer.doc()->lines()) { |
48 | m_textLine = usePlainTextLine ? m_renderer.doc()->plainKateTextLine(i: m_line) : m_renderer.doc()->kateTextLine(i: m_line); |
49 | } |
50 | } |
51 | |
52 | Q_ASSERT(m_textLine); |
53 | |
54 | return *m_textLine; |
55 | } |
56 | |
57 | int KateLineLayout::line() const |
58 | { |
59 | return m_line; |
60 | } |
61 | |
62 | void KateLineLayout::setLine(int line, int virtualLine) |
63 | { |
64 | m_line = line; |
65 | m_virtualLine = (virtualLine == -1) ? m_renderer.folding().lineToVisibleLine(line) : virtualLine; |
66 | m_textLine.reset(); |
67 | } |
68 | |
69 | int KateLineLayout::virtualLine() const |
70 | { |
71 | return m_virtualLine; |
72 | } |
73 | |
74 | void KateLineLayout::setVirtualLine(int virtualLine) |
75 | { |
76 | m_virtualLine = virtualLine; |
77 | } |
78 | |
79 | bool KateLineLayout::startsInvisibleBlock() const |
80 | { |
81 | if (!isValid()) { |
82 | return false; |
83 | } |
84 | |
85 | return (virtualLine() + 1) != m_renderer.folding().lineToVisibleLine(line: line() + 1); |
86 | } |
87 | |
88 | bool KateLineLayout::isValid() const |
89 | { |
90 | return line() != -1 && layout() && (textLine(), m_textLine); |
91 | } |
92 | |
93 | QTextLayout *KateLineLayout::layout() const |
94 | { |
95 | return m_layout.get(); |
96 | } |
97 | |
98 | void KateLineLayout::setLayout(QTextLayout *layout) |
99 | { |
100 | if (m_layout.get() != layout) { |
101 | m_layout.reset(p: layout); |
102 | } |
103 | |
104 | layoutDirty = !m_layout; |
105 | m_dirtyList.clear(); |
106 | if (m_layout) { |
107 | for (int i = 0; i < qMax(a: 1, b: m_layout->lineCount()); ++i) { |
108 | m_dirtyList.append(t: true); |
109 | } |
110 | } |
111 | } |
112 | |
113 | void KateLineLayout::invalidateLayout() |
114 | { |
115 | setLayout(nullptr); |
116 | } |
117 | |
118 | bool KateLineLayout::isDirty(int viewLine) const |
119 | { |
120 | Q_ASSERT(isValid() && viewLine >= 0 && viewLine < viewLineCount()); |
121 | return m_dirtyList[viewLine]; |
122 | } |
123 | |
124 | bool KateLineLayout::setDirty(int viewLine, bool dirty) |
125 | { |
126 | Q_ASSERT(isValid() && viewLine >= 0 && viewLine < viewLineCount()); |
127 | m_dirtyList[viewLine] = dirty; |
128 | return dirty; |
129 | } |
130 | |
131 | KTextEditor::Cursor KateLineLayout::start() const |
132 | { |
133 | return KTextEditor::Cursor(line(), 0); |
134 | } |
135 | |
136 | int KateLineLayout::length() const |
137 | { |
138 | return textLine().length(); |
139 | } |
140 | |
141 | int KateLineLayout::viewLineCount() const |
142 | { |
143 | return m_layout->lineCount(); |
144 | } |
145 | |
146 | KateTextLayout KateLineLayout::viewLine(int viewLine) |
147 | { |
148 | if (viewLine < 0) { |
149 | viewLine += viewLineCount(); |
150 | } |
151 | Q_ASSERT(isValid()); |
152 | Q_ASSERT(viewLine >= 0 && viewLine < viewLineCount()); |
153 | return KateTextLayout(this, viewLine); |
154 | } |
155 | |
156 | int KateLineLayout::width() const |
157 | { |
158 | int width = 0; |
159 | |
160 | for (int i = 0; i < m_layout->lineCount(); ++i) { |
161 | width = qMax(a: (int)m_layout->lineAt(i).naturalTextWidth(), b: width); |
162 | } |
163 | |
164 | return width; |
165 | } |
166 | |
167 | int KateLineLayout::widthOfLastLine() |
168 | { |
169 | const KateTextLayout &lastLine = viewLine(viewLine: viewLineCount() - 1); |
170 | return lastLine.width() + lastLine.xOffset(); |
171 | } |
172 | |
173 | bool KateLineLayout::isOutsideDocument() const |
174 | { |
175 | return line() < 0 || line() >= m_renderer.doc()->lines(); |
176 | } |
177 | |
178 | void KateLineLayout::debugOutput() const |
179 | { |
180 | qCDebug(LOG_KTE) << "KateLineLayout: " << this << " valid " << isValid() << " line " << line() << " length " << length() << " width " << width() |
181 | << " viewLineCount " << viewLineCount(); |
182 | } |
183 | |
184 | int KateLineLayout::viewLineForColumn(int column) const |
185 | { |
186 | int len = 0; |
187 | int i = 0; |
188 | for (; i < m_layout->lineCount() - 1; ++i) { |
189 | len += m_layout->lineAt(i).textLength(); |
190 | if (column < len) { |
191 | return i; |
192 | } |
193 | } |
194 | return i; |
195 | } |
196 | |
197 | bool KateLineLayout::isRightToLeft() const |
198 | { |
199 | if (!m_layout) { |
200 | return false; |
201 | } |
202 | |
203 | return m_layout->textOption().textDirection() == Qt::RightToLeft; |
204 | } |
205 | |