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