1 | /* |
2 | SPDX-FileCopyrightText: 2002-2007 Hamish Rodda <rodda@kde.org> |
3 | SPDX-FileCopyrightText: 2002 John Firebaugh <jfirebaugh@kde.org> |
4 | SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org> |
5 | SPDX-FileCopyrightText: 2002 Christoph Cullmann <cullmann@kde.org> |
6 | SPDX-FileCopyrightText: 2007 Mirko Stocker <me@misto.ch> |
7 | |
8 | Based on KWriteView: |
9 | SPDX-FileCopyrightText: 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de> |
10 | |
11 | SPDX-License-Identifier: LGPL-2.0-or-later |
12 | */ |
13 | #ifndef _KATE_VIEW_INTERNAL_ |
14 | #define _KATE_VIEW_INTERNAL_ |
15 | |
16 | #include <KSyntaxHighlighting/FoldingRegion> |
17 | |
18 | #include <ktexteditor/attribute.h> |
19 | #include <ktexteditor/range.h> |
20 | #include <ktexteditor/view.h> |
21 | |
22 | #include "inlinenotedata.h" |
23 | #include "katetextcursor.h" |
24 | #include "katetextline.h" |
25 | |
26 | #include <QDrag> |
27 | #include <QElapsedTimer> |
28 | #include <QPoint> |
29 | #include <QPointer> |
30 | #include <QSet> |
31 | #include <QTime> |
32 | #include <QTimer> |
33 | #include <QWidget> |
34 | |
35 | #include <array> |
36 | #include <memory> |
37 | |
38 | namespace KTextEditor |
39 | { |
40 | class MovingRange; |
41 | class TextHintProvider; |
42 | class DocumentPrivate; |
43 | class ViewPrivate; |
44 | } |
45 | |
46 | class KateIconBorder; |
47 | class KateScrollBar; |
48 | class KateAnnotationItemDelegate; |
49 | class KateAnnotationGroupPositionState; |
50 | class KateTextLayout; |
51 | class KateTextAnimation; |
52 | class KateAbstractInputMode; |
53 | class ZoomEventFilter; |
54 | class KateRenderer; |
55 | class KateTextPreview; |
56 | class KateViewTest; |
57 | |
58 | class QScrollBar; |
59 | class QScroller; |
60 | class QScrollEvent; |
61 | class QScrollPrepareEvent; |
62 | |
63 | class KateViewInternal final : public QWidget |
64 | { |
65 | Q_OBJECT |
66 | |
67 | friend class KTextEditor::ViewPrivate; |
68 | friend class KateIconBorder; |
69 | friend class KateScrollBar; |
70 | friend class KateAnnotationGroupPositionState; |
71 | friend class CalculatingCursor; |
72 | friend class BoundedCursor; |
73 | friend class WrappingCursor; |
74 | friend class CamelCursor; |
75 | friend class KateAbstractInputMode; |
76 | friend class ::KateTextPreview; |
77 | friend class KateViewTest; |
78 | |
79 | public: |
80 | enum Bias { left = -1, none = 0, right = 1 }; |
81 | |
82 | public: |
83 | explicit KateViewInternal(KTextEditor::ViewPrivate *view); |
84 | ~KateViewInternal() override; |
85 | KTextEditor::ViewPrivate *view() const |
86 | { |
87 | return m_view; |
88 | } |
89 | |
90 | // BEGIN EDIT STUFF |
91 | public: |
92 | void editStart(); |
93 | void editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom); |
94 | |
95 | void editSetCursor(const KTextEditor::Cursor cursor); |
96 | |
97 | private: |
98 | uint editSessionNumber; |
99 | bool editIsRunning; |
100 | KTextEditor::Cursor editOldCursor; |
101 | KTextEditor::Range editOldSelection; |
102 | // END |
103 | |
104 | // BEGIN TAG & CLEAR & UPDATE STUFF |
105 | public: |
106 | bool tagLine(const KTextEditor::Cursor virtualCursor); |
107 | |
108 | bool tagLines(int start, int end, bool realLines = false); |
109 | // cursors not const references as they are manipulated within |
110 | bool tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors = false); |
111 | |
112 | bool tagRange(KTextEditor::Range range, bool realCursors); |
113 | |
114 | void tagAll(); |
115 | |
116 | void updateDirty(); |
117 | |
118 | void clear(); |
119 | // END |
120 | |
121 | private Q_SLOTS: |
122 | // Updates the view and requests a redraw. |
123 | void updateView(bool changed = false, int viewLinesScrolled = 0); |
124 | |
125 | private: |
126 | void makeVisible(const KTextEditor::Cursor c, int endCol, bool force = false, bool center = false, bool calledExternally = false); |
127 | |
128 | public: |
129 | // Start Position is a virtual cursor |
130 | KTextEditor::Cursor startPos() const |
131 | { |
132 | return m_startPos; |
133 | } |
134 | int startLine() const |
135 | { |
136 | return m_startPos.line(); |
137 | } |
138 | int startX() const |
139 | { |
140 | return m_startX; |
141 | } |
142 | |
143 | KTextEditor::Cursor endPos() const; |
144 | int endLine() const; |
145 | |
146 | KateTextLayout yToKateTextLayout(int y) const; |
147 | |
148 | void dynWrapChanged(); |
149 | |
150 | public Q_SLOTS: |
151 | void slotIncFontSizes(qreal step = 1.0); |
152 | void slotDecFontSizes(qreal step = 1.0); |
153 | void slotResetFontSizes(); |
154 | |
155 | void paintCursor(); |
156 | |
157 | private Q_SLOTS: |
158 | void scrollLines(int line); // connected to the sliderMoved of the m_lineScroll |
159 | void scrollViewLines(int offset); |
160 | void scrollAction(int action); |
161 | void scrollNextPage(); |
162 | void scrollPrevPage(); |
163 | void scrollPrevLine(); |
164 | void scrollNextLine(); |
165 | void scrollColumns(int x); // connected to the valueChanged of the m_columnScroll |
166 | void viewSelectionChanged(); |
167 | |
168 | public: |
169 | void cursorPrevChar(bool sel = false); |
170 | void cursorNextChar(bool sel = false); |
171 | void wordPrev(bool sel = false); |
172 | void wordNext(bool sel = false); |
173 | void home(bool sel = false); |
174 | void end(bool sel = false); |
175 | void cursorUp(bool sel = false); |
176 | void cursorDown(bool sel = false); |
177 | void cursorToMatchingBracket(bool sel = false); |
178 | void scrollUp(); |
179 | void scrollDown(); |
180 | void topOfView(bool sel = false); |
181 | void bottomOfView(bool sel = false); |
182 | void pageUp(bool sel = false, bool half = false); |
183 | void pageDown(bool sel = false, bool half = false); |
184 | void top(bool sel = false); |
185 | void bottom(bool sel = false); |
186 | void top_home(bool sel = false); |
187 | void bottom_end(bool sel = false); |
188 | |
189 | private: |
190 | // Takes as input @p c and applies the home command on it |
191 | KTextEditor::Cursor moveCursorToLineStart(KTextEditor::Cursor c); |
192 | // Takes as input @p c and applies the end command on it |
193 | KTextEditor::Cursor moveCursorToLineEnd(KTextEditor::Cursor c); |
194 | |
195 | public: |
196 | /** |
197 | * Accessor to the current caret position |
198 | * @return position of the caret as @c KTextEditor::Cursor |
199 | * @see KTextEditor::Cursor |
200 | */ |
201 | KTextEditor::Cursor cursorPosition() const |
202 | { |
203 | return m_cursor; |
204 | } |
205 | |
206 | /** |
207 | * Accessor to the current mouse position |
208 | * @return position of the mouse as @c KTextEditor::Cursor |
209 | * @see KTextEditor::Cursor |
210 | */ |
211 | KTextEditor::Cursor mousePosition() const |
212 | { |
213 | return m_mouse; |
214 | } |
215 | |
216 | QPoint cursorToCoordinate(const KTextEditor::Cursor cursor, bool realCursor = true, bool includeBorder = true) const; |
217 | // by default, works on coordinates of the whole widget, eg. offsetted by the border |
218 | KTextEditor::Cursor coordinatesToCursor(const QPoint &coord, bool includeBorder = true) const; |
219 | QPoint cursorCoordinates(bool includeBorder = true) const; |
220 | KTextEditor::Cursor findMatchingBracket(); |
221 | |
222 | // exported for unit tests |
223 | KTEXTEDITOR_EXPORT KTextEditor::Range |
224 | findMatchingFoldingMarker(const KTextEditor::Cursor current_cursor_pos, const KSyntaxHighlighting::FoldingRegion foldingRegion, const int maxLines); |
225 | KTEXTEDITOR_EXPORT void updateFoldingMarkersHighlighting(); |
226 | |
227 | inline int getStartOffset(int direction, int offset, int length) const |
228 | { |
229 | return direction == 1 ? offset - length : offset; |
230 | } |
231 | |
232 | inline int getEndOffset(int direction, int offset, int length) const |
233 | { |
234 | return direction == 1 ? offset : offset + length; |
235 | } |
236 | |
237 | KateIconBorder *iconBorder() const |
238 | { |
239 | return m_leftBorder; |
240 | } |
241 | |
242 | // EVENT HANDLING STUFF - IMPORTANT |
243 | private: |
244 | void fixDropEvent(QDropEvent *event); |
245 | |
246 | static bool isAcceptableInput(const QKeyEvent *e); |
247 | |
248 | protected: |
249 | void hideEvent(QHideEvent *e) override; |
250 | void paintEvent(QPaintEvent *e) override; |
251 | bool eventFilter(QObject *obj, QEvent *e) override; |
252 | void keyPressEvent(QKeyEvent *) override; |
253 | void keyReleaseEvent(QKeyEvent *) override; |
254 | void resizeEvent(QResizeEvent *) override; |
255 | void moveEvent(QMoveEvent *) override; |
256 | void mousePressEvent(QMouseEvent *) override; |
257 | void mouseDoubleClickEvent(QMouseEvent *) override; |
258 | void mouseReleaseEvent(QMouseEvent *) override; |
259 | void mouseMoveEvent(QMouseEvent *) override; |
260 | void leaveEvent(QEvent *) override; |
261 | void dragEnterEvent(QDragEnterEvent *) override; |
262 | void dragMoveEvent(QDragMoveEvent *) override; |
263 | void dropEvent(QDropEvent *) override; |
264 | void showEvent(QShowEvent *) override; |
265 | void wheelEvent(QWheelEvent *e) override; |
266 | void scrollPrepareEvent(QScrollPrepareEvent *); |
267 | void scrollEvent(QScrollEvent *); |
268 | void focusInEvent(QFocusEvent *) override; |
269 | void focusOutEvent(QFocusEvent *) override; |
270 | void inputMethodEvent(QInputMethodEvent *e) override; |
271 | |
272 | void (QContextMenuEvent *e) override; |
273 | |
274 | private Q_SLOTS: |
275 | void tripleClickTimeout(); |
276 | |
277 | Q_SIGNALS: |
278 | // emitted when KateViewInternal is not handling its own URI drops |
279 | void dropEventPass(QDropEvent *); |
280 | |
281 | private Q_SLOTS: |
282 | void slotRegionVisibilityChanged(); |
283 | void slotRegionBeginEndAddedRemoved(unsigned int); |
284 | |
285 | private: |
286 | void moveChar(Bias bias, bool sel); |
287 | void moveEdge(Bias bias, bool sel); |
288 | KTextEditor::Cursor maxStartPos(bool changed = false); |
289 | void scrollPos(KTextEditor::Cursor &c, bool force = false, bool calledExternally = false, bool emitSignals = true); |
290 | void scrollLines(int lines, bool sel); |
291 | |
292 | KTextEditor::Attribute::Ptr attributeAt(const KTextEditor::Cursor position) const; |
293 | int linesDisplayed() const; |
294 | |
295 | int lineToY(int viewLine) const; |
296 | |
297 | void updateSecondarySelection(int cursorIdx, KTextEditor::Cursor old, KTextEditor::Cursor newPos) const; |
298 | void updateSelection(const KTextEditor::Cursor, bool keepSel); |
299 | void setSelection(KTextEditor::Range); |
300 | void moveCursorToSelectionEdge(bool scroll = true); |
301 | void updateCursor(const KTextEditor::Cursor newCursor, bool force = false, bool center = false, bool calledExternally = false, bool scroll = true); |
302 | void updateBracketMarks(); |
303 | void beginSelectLine(const QPoint &pos); |
304 | |
305 | struct CursorPair { |
306 | KTextEditor::Cursor oldPos; |
307 | KTextEditor::Cursor newPos; |
308 | }; |
309 | // @brief updates the secondary cursor, schedules repaint |
310 | // MUST setPosition of the corresponding moving cursors before calling this |
311 | void updateSecondaryCursors(const QVarLengthArray<CursorPair, 16> &cursors, bool sel); |
312 | void mergeSelections(); |
313 | |
314 | KTextEditor::Cursor cursorForPoint(QPoint p); |
315 | void placeCursor(const QPoint &p, bool keepSelection = false, bool updateSelection = true); |
316 | bool isTargetSelected(const QPoint &p); |
317 | // Returns whether the given range affects the area currently visible in the view |
318 | bool rangeAffectsView(KTextEditor::Range range, bool realCursors) const; |
319 | |
320 | void doDrag(); |
321 | |
322 | KateRenderer *renderer() const; |
323 | |
324 | bool sendMouseEventToInputContext(QMouseEvent *e); |
325 | void commitPreedit(); |
326 | |
327 | KTextEditor::ViewPrivate *m_view; |
328 | class KateIconBorder *m_leftBorder; |
329 | |
330 | int m_mouseX; |
331 | int m_mouseY; |
332 | int m_scrollX; |
333 | int m_scrollY; |
334 | |
335 | std::unique_ptr<ZoomEventFilter> m_zoomEventFilter; |
336 | |
337 | Qt::CursorShape m_mouseCursor; |
338 | |
339 | Kate::TextCursor m_cursor; |
340 | KTextEditor::Cursor m_mouse; |
341 | KTextEditor::Cursor m_displayCursor; |
342 | |
343 | bool m_possibleTripleClick; |
344 | |
345 | // Bracket mark and corresponding decorative ranges |
346 | std::unique_ptr<KTextEditor::MovingRange> m_bm, m_bmStart, m_bmEnd; |
347 | std::unique_ptr<KTextEditor::MovingCursor> m_bmLastFlashPos; |
348 | std::unique_ptr<KateTextPreview> m_bmPreview; |
349 | void updateBracketMarkAttributes(); |
350 | |
351 | // Folding mark |
352 | std::unique_ptr<KTextEditor::MovingRange> m_fmStart, m_fmEnd; |
353 | |
354 | enum DragState { diNone, diPending, diDragging }; |
355 | |
356 | struct _dragInfo { |
357 | DragState state; |
358 | QPoint start; |
359 | QDrag *dragObject; |
360 | } m_dragInfo; |
361 | |
362 | // |
363 | // line scrollbar + first visible (virtual) line in the current view |
364 | // |
365 | KateScrollBar *m_lineScroll; |
366 | qreal m_accumulatedScroll = 0.0; |
367 | QWidget *m_dummy; |
368 | |
369 | // These are now cursors to account for word-wrap. |
370 | // Start Position is a virtual cursor |
371 | Kate::TextCursor m_startPos; |
372 | // Count of lines that are visible behind m_startPos. |
373 | // This does not respect dynamic word wrap, so take it as an approximation. |
374 | uint m_visibleLineCount; |
375 | |
376 | // This is set to false on resize or scroll (other than that called by makeVisible), |
377 | // so that makeVisible is again called when a key is pressed and the cursor is in the same spot |
378 | bool m_madeVisible; |
379 | bool m_shiftKeyPressed; |
380 | |
381 | // How many lines to should be kept visible above/below the cursor when possible |
382 | void setAutoCenterLines(int viewLines, bool updateView = true); |
383 | int m_autoCenterLines; |
384 | int m_minLinesVisible; |
385 | |
386 | // |
387 | // column scrollbar + x position |
388 | // |
389 | QScrollBar *m_columnScroll; |
390 | QScroller *m_scroller; |
391 | int m_startX; |
392 | |
393 | // has selection changed while your mouse or shift key is pressed |
394 | bool m_selChangedByUser; |
395 | KTextEditor::Cursor m_selectAnchor; |
396 | |
397 | enum SelectionMode { Default = 0, Mouse, Word, Line }; ///< for drag selection. |
398 | uint m_selectionMode; |
399 | // when drag selecting after double/triple click, keep the initial selected |
400 | // word/line independent of direction. |
401 | // They get set in the event of a double click, and is used with mouse move + leftbutton |
402 | KTextEditor::Range m_selectionCached; |
403 | |
404 | // maximal length of textlines visible from given startLine |
405 | int maxLen(int startLine); |
406 | |
407 | // are we allowed to scroll columns? |
408 | bool columnScrollingPossible(); |
409 | |
410 | // the same for lines |
411 | bool lineScrollingPossible(); |
412 | |
413 | // returns the maximum X value / col value a cursor can take for a specific line range |
414 | int lineMaxCursorX(const KateTextLayout &line); |
415 | static int lineMaxCol(const KateTextLayout &line); |
416 | |
417 | class KateLayoutCache *cache() const; |
418 | KateLayoutCache *m_layoutCache; |
419 | |
420 | // convenience methods |
421 | |
422 | /// returns layout for the line c.line() |
423 | KateTextLayout currentLayout(KTextEditor::Cursor c) const; |
424 | // returns layout for the line previous to @p c |
425 | KateTextLayout previousLayout(KTextEditor::Cursor c) const; |
426 | // returns layout for the line next to @p c |
427 | KateTextLayout nextLayout(KTextEditor::Cursor c) const; |
428 | |
429 | // find the cursor offset by (offset) view lines from a cursor. |
430 | // when keepX is true, the column position will be calculated based on the x |
431 | // position of the specified cursor. |
432 | KTextEditor::Cursor viewLineOffset(const KTextEditor::Cursor virtualCursor, int offset, bool keepX = false); |
433 | |
434 | KTextEditor::Cursor toRealCursor(const KTextEditor::Cursor virtualCursor) const; |
435 | KTextEditor::Cursor toVirtualCursor(const KTextEditor::Cursor realCursor) const; |
436 | |
437 | // These variable holds the most recent maximum real & visible column number |
438 | bool m_preserveX; |
439 | int m_preservedX; |
440 | |
441 | KTextEditor::Cursor m_cachedMaxStartPos; |
442 | |
443 | // |
444 | // implementation details for KTextEditor::FlashTextInterface |
445 | // |
446 | public: |
447 | void flashChar(const KTextEditor::Cursor pos, KTextEditor::Attribute::Ptr attribute); |
448 | void showBracketMatchPreview(); |
449 | void hideBracketMatchPreview(); |
450 | |
451 | private: |
452 | QPointer<KateTextAnimation> m_textAnimation; |
453 | |
454 | private Q_SLOTS: |
455 | void doDragScroll(); |
456 | void startDragScroll(); |
457 | void stopDragScroll(); |
458 | |
459 | private: |
460 | // Timers |
461 | QTimer m_dragScrollTimer; |
462 | QTimer m_scrollTimer; |
463 | QTimer m_cursorTimer; |
464 | QTimer m_textHintTimer; |
465 | |
466 | static const int s_scrollTime = 30; |
467 | static const int s_scrollMargin = 16; |
468 | |
469 | private Q_SLOTS: |
470 | void scrollTimeout(); |
471 | void cursorTimeout(); |
472 | void textHintTimeout(); |
473 | |
474 | void documentTextInserted(KTextEditor::Document *document, KTextEditor::Range range); |
475 | void documentTextRemoved(KTextEditor::Document *document, KTextEditor::Range range, const QString &oldText); |
476 | |
477 | // |
478 | // KTE::TextHintInterface |
479 | // |
480 | public: |
481 | void registerTextHintProvider(KTextEditor::TextHintProvider *provider); |
482 | void unregisterTextHintProvider(KTextEditor::TextHintProvider *provider); |
483 | void setTextHintDelay(int delay); |
484 | int textHintDelay() const; |
485 | bool textHintsEnabled(); // not part of the interface |
486 | |
487 | private: |
488 | std::vector<KTextEditor::TextHintProvider *> m_textHintProviders; |
489 | int m_textHintDelay; |
490 | QPoint m_textHintPos; |
491 | |
492 | // |
493 | // IM input stuff |
494 | // |
495 | public: |
496 | QVariant inputMethodQuery(Qt::InputMethodQuery query) const override; |
497 | |
498 | private: |
499 | std::unique_ptr<KTextEditor::MovingRange> m_imPreeditRange; |
500 | std::vector<std::unique_ptr<KTextEditor::MovingRange>> m_imPreeditRangeChildren; |
501 | |
502 | private: |
503 | void mouseMoved(); |
504 | void cursorMoved(); |
505 | |
506 | private: |
507 | KTextEditor::DocumentPrivate *doc(); |
508 | KTextEditor::DocumentPrivate *doc() const; |
509 | |
510 | // input modes |
511 | private: |
512 | std::array<std::unique_ptr<KateAbstractInputMode>, KTextEditor::View::ViInputMode + 1> m_inputModes; |
513 | KateAbstractInputMode *m_currentInputMode; |
514 | |
515 | KateInlineNoteData m_activeInlineNote; |
516 | KateInlineNoteData inlineNoteAt(const QPoint &globalPos) const; |
517 | QRect inlineNoteRect(const KateInlineNoteData ¬e) const; |
518 | }; |
519 | |
520 | #endif |
521 | |