1 | /* |
2 | This file is part of the KDE libraries |
3 | SPDX-FileCopyrightText: 2002 John Firebaugh <jfirebaugh@kde.org> |
4 | SPDX-FileCopyrightText: 2001 Anders Lund <anders@alweb.dk> |
5 | SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org> |
6 | SPDX-FileCopyrightText: 2017-2018 Friedrich W. H. Kossebau <kossebau@kde.org> |
7 | |
8 | SPDX-License-Identifier: LGPL-2.0-only |
9 | */ |
10 | |
11 | #ifndef KATE_VIEW_HELPERS_H |
12 | #define KATE_VIEW_HELPERS_H |
13 | |
14 | #include <KActionMenu> |
15 | #include <KLineEdit> |
16 | #include <KSelectAction> |
17 | |
18 | #include <QColor> |
19 | #include <QHash> |
20 | #include <QLabel> |
21 | #include <QLayout> |
22 | #include <QPixmap> |
23 | #include <QPointer> |
24 | #include <QScrollBar> |
25 | #include <QTimer> |
26 | |
27 | #include "katetextline.h" |
28 | #include <ktexteditor/cursor.h> |
29 | #include <ktexteditor/message.h> |
30 | |
31 | namespace KTextEditor |
32 | { |
33 | class ViewPrivate; |
34 | class DocumentPrivate; |
35 | class Command; |
36 | class AnnotationModel; |
37 | class MovingRange; |
38 | class AbstractAnnotationItemDelegate; |
39 | class StyleOptionAnnotationItem; |
40 | } |
41 | |
42 | class KateViewInternal; |
43 | class KateTextLayout; |
44 | |
45 | #define MAXFOLDINGCOLORS 16 |
46 | |
47 | class KateLineInfo; |
48 | class KateTextPreview; |
49 | |
50 | namespace Kate |
51 | { |
52 | class ; |
53 | } |
54 | |
55 | class QTimer; |
56 | class QVBoxLayout; |
57 | class QStackedWidget; |
58 | class QLabel; |
59 | |
60 | /** |
61 | * Class to layout KTextEditor::Message%s in KateView. Only the floating |
62 | * positions TopInView, CenterInView, and BottomInView are supported. |
63 | * AboveView and BelowView are not supported and ASSERT. |
64 | */ |
65 | class KateMessageLayout : public QLayout |
66 | { |
67 | public: |
68 | explicit KateMessageLayout(QWidget *parent); |
69 | ~KateMessageLayout() override; |
70 | |
71 | void addWidget(QWidget *widget, KTextEditor::Message::MessagePosition pos); |
72 | int count() const override; |
73 | QLayoutItem *itemAt(int index) const override; |
74 | void setGeometry(const QRect &rect) override; |
75 | QSize sizeHint() const override; |
76 | QLayoutItem *takeAt(int index) override; |
77 | |
78 | void add(QLayoutItem *item, KTextEditor::Message::MessagePosition pos); |
79 | |
80 | private: |
81 | void addItem(QLayoutItem *item) override; // never called publically |
82 | |
83 | struct ItemWrapper { |
84 | ItemWrapper() = default; |
85 | ItemWrapper(QLayoutItem *i, KTextEditor::Message::MessagePosition pos) |
86 | : item(i) |
87 | , position(pos) |
88 | { |
89 | } |
90 | |
91 | QLayoutItem *item = nullptr; |
92 | KTextEditor::Message::MessagePosition position = KTextEditor::Message::AboveView; |
93 | }; |
94 | |
95 | QList<ItemWrapper> m_items; |
96 | }; |
97 | |
98 | /** |
99 | * This class is required because QScrollBar's sliderMoved() signal is |
100 | * really supposed to be a sliderDragged() signal... so this way we can capture |
101 | * MMB slider moves as well |
102 | * |
103 | * Also, it adds some useful indicators on the scrollbar. |
104 | */ |
105 | class KateScrollBar : public QScrollBar |
106 | { |
107 | Q_OBJECT |
108 | |
109 | public: |
110 | KateScrollBar(Qt::Orientation orientation, class KateViewInternal *parent); |
111 | ~KateScrollBar() override; |
112 | QSize sizeHint() const override; |
113 | |
114 | void showEvent(QShowEvent *event) override; |
115 | |
116 | inline bool showMarks() const |
117 | { |
118 | return m_showMarks; |
119 | } |
120 | inline void setShowMarks(bool b) |
121 | { |
122 | m_showMarks = b; |
123 | update(); |
124 | } |
125 | |
126 | inline bool showMiniMap() const |
127 | { |
128 | return m_showMiniMap; |
129 | } |
130 | void setShowMiniMap(bool b); |
131 | |
132 | inline bool miniMapAll() const |
133 | { |
134 | return m_miniMapAll; |
135 | } |
136 | inline void setMiniMapAll(bool b) |
137 | { |
138 | m_miniMapAll = b; |
139 | updateGeometry(); |
140 | update(); |
141 | } |
142 | |
143 | inline bool miniMapWidth() const |
144 | { |
145 | return m_miniMapWidth; |
146 | } |
147 | inline void setMiniMapWidth(int width) |
148 | { |
149 | m_miniMapWidth = width; |
150 | updateGeometry(); |
151 | update(); |
152 | } |
153 | |
154 | inline void queuePixmapUpdate() |
155 | { |
156 | m_updateTimer.start(); |
157 | } |
158 | |
159 | Q_SIGNALS: |
160 | void sliderMMBMoved(int value); |
161 | |
162 | protected: |
163 | void mousePressEvent(QMouseEvent *e) override; |
164 | void mouseReleaseEvent(QMouseEvent *e) override; |
165 | void mouseMoveEvent(QMouseEvent *e) override; |
166 | void leaveEvent(QEvent *event) override; |
167 | bool eventFilter(QObject *object, QEvent *event) override; |
168 | void paintEvent(QPaintEvent *e) override; |
169 | void resizeEvent(QResizeEvent *) override; |
170 | void sliderChange(SliderChange change) override; |
171 | #if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) |
172 | void contextMenuEvent(QContextMenuEvent *e) override; |
173 | #endif |
174 | |
175 | protected Q_SLOTS: |
176 | void sliderMaybeMoved(int value); |
177 | void marksChanged(); |
178 | |
179 | public Q_SLOTS: |
180 | void updatePixmap(); |
181 | |
182 | private Q_SLOTS: |
183 | void showTextPreview(); |
184 | |
185 | private: |
186 | void showTextPreviewDelayed(); |
187 | void hideTextPreview(); |
188 | |
189 | void redrawMarks(); |
190 | void recomputeMarksPositions(); |
191 | |
192 | void miniMapPaintEvent(QPaintEvent *e); |
193 | void normalPaintEvent(QPaintEvent *e); |
194 | |
195 | int minimapYToStdY(int y); |
196 | |
197 | struct ColumnRangeWithColor { |
198 | int penIndex = -1; |
199 | int startColumn; |
200 | int endColumn; |
201 | }; |
202 | void getCharColorRanges(const QList<Kate::TextLine::Attribute> &attributes, |
203 | const QList<Kate::TextRange *> &decorations, |
204 | const QString &text, |
205 | QList<KateScrollBar::ColumnRangeWithColor> &ranges, |
206 | QVarLengthArray<std::pair<QRgb, QPen>, 20> &penCache); |
207 | |
208 | bool m_middleMouseDown; |
209 | bool m_leftMouseDown; |
210 | |
211 | KTextEditor::ViewPrivate *m_view; |
212 | KTextEditor::DocumentPrivate *m_doc; |
213 | class KateViewInternal *m_viewInternal; |
214 | QPointer<KateTextPreview> m_textPreview; |
215 | QTimer m_delayTextPreviewTimer; |
216 | |
217 | QHash<int, QColor> m_lines; |
218 | |
219 | bool m_showMarks; |
220 | bool m_showMiniMap; |
221 | bool m_miniMapAll; |
222 | bool m_needsUpdateOnShow; |
223 | int m_miniMapWidth; |
224 | |
225 | QPixmap m_pixmap; |
226 | int m_grooveHeight; |
227 | QRect m_stdGroveRect; |
228 | QRect m_mapGroveRect; |
229 | QRect m_sliderRect; |
230 | QTimer m_updateTimer; |
231 | QPoint m_toolTipPos; |
232 | QLabel m_tooltipLineNoInfo; |
233 | |
234 | // lists of lines added/removed recently to avoid scrollbar flickering |
235 | QHash<int, int> m_linesAdded; |
236 | |
237 | static const unsigned char characterOpacity[256]; |
238 | }; |
239 | |
240 | class KateIconBorder : public QWidget |
241 | { |
242 | Q_OBJECT |
243 | |
244 | public: |
245 | KateIconBorder(KateViewInternal *internalView, QWidget *parent); |
246 | ~KateIconBorder() override; |
247 | // VERY IMPORTANT ;) |
248 | QSize sizeHint() const override; |
249 | |
250 | void updateFont(); |
251 | int lineNumberWidth() const; |
252 | |
253 | void setIconBorderOn(bool enable); |
254 | void setLineNumbersOn(bool enable); |
255 | void setRelLineNumbersOn(bool enable); |
256 | void setAnnotationBorderOn(bool enable); |
257 | void setDynWrapIndicators(int state); |
258 | int dynWrapIndicators() const |
259 | { |
260 | return m_dynWrapIndicators; |
261 | } |
262 | bool dynWrapIndicatorsOn() const |
263 | { |
264 | return m_dynWrapIndicatorsOn; |
265 | } |
266 | void setFoldingMarkersOn(bool enable); |
267 | void toggleIconBorder() |
268 | { |
269 | setIconBorderOn(!iconBorderOn()); |
270 | } |
271 | void toggleLineNumbers() |
272 | { |
273 | setLineNumbersOn(!lineNumbersOn()); |
274 | } |
275 | void toggleFoldingMarkers() |
276 | { |
277 | setFoldingMarkersOn(!foldingMarkersOn()); |
278 | } |
279 | inline bool iconBorderOn() const |
280 | { |
281 | return m_iconBorderOn; |
282 | } |
283 | inline bool lineNumbersOn() const |
284 | { |
285 | return m_lineNumbersOn; |
286 | } |
287 | inline bool viRelNumbersOn() const |
288 | { |
289 | return m_relLineNumbersOn; |
290 | } |
291 | inline bool foldingMarkersOn() const |
292 | { |
293 | return m_foldingMarkersOn; |
294 | } |
295 | inline bool annotationBorderOn() const |
296 | { |
297 | return m_annotationBorderOn; |
298 | } |
299 | |
300 | void updateForCursorLineChange(); |
301 | |
302 | enum BorderArea { |
303 | None, |
304 | LineNumbers, |
305 | IconBorder, |
306 | FoldingMarkers, |
307 | AnnotationBorder, |
308 | ModificationBorder |
309 | }; |
310 | BorderArea positionToArea(const QPoint &) const; |
311 | |
312 | KTextEditor::AbstractAnnotationItemDelegate *annotationItemDelegate() const; |
313 | void setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate); |
314 | inline bool uniformAnnotationItemSizes() const |
315 | { |
316 | return m_hasUniformAnnotationItemSizes; |
317 | } |
318 | inline void setAnnotationUniformItemSizes(bool enable) |
319 | { |
320 | m_hasUniformAnnotationItemSizes = enable; |
321 | } |
322 | |
323 | public Q_SLOTS: |
324 | void updateAnnotationBorderWidth(); |
325 | void updateAnnotationLine(int line); |
326 | void annotationModelChanged(KTextEditor::AnnotationModel *oldmodel, KTextEditor::AnnotationModel *newmodel); |
327 | void displayRangeChanged(); |
328 | |
329 | private: |
330 | void dragEnterEvent(QDragEnterEvent *) override; |
331 | void dragMoveEvent(QDragMoveEvent *event) override; |
332 | void dropEvent(QDropEvent *event) override; |
333 | void paintEvent(QPaintEvent *) override; |
334 | void paintBorder(int x, int y, int width, int height); |
335 | |
336 | void mousePressEvent(QMouseEvent *) override; |
337 | void mouseMoveEvent(QMouseEvent *) override; |
338 | void mouseReleaseEvent(QMouseEvent *) override; |
339 | void mouseDoubleClickEvent(QMouseEvent *) override; |
340 | void (QContextMenuEvent *e) override; |
341 | void leaveEvent(QEvent *event) override; |
342 | void wheelEvent(QWheelEvent *e) override; |
343 | |
344 | void enterEvent(QEnterEvent *e) override; |
345 | |
346 | void (uint line, const QPoint &pos); |
347 | |
348 | void hideAnnotationTooltip(); |
349 | void removeAnnotationHovering(); |
350 | void (int line, const QPoint &pos); |
351 | void calcAnnotationBorderWidth(); |
352 | |
353 | void initStyleOption(KTextEditor::StyleOptionAnnotationItem *styleOption) const; |
354 | void setStyleOptionLineData(KTextEditor::StyleOptionAnnotationItem *styleOption, |
355 | int y, |
356 | int realLine, |
357 | const KTextEditor::AnnotationModel *model, |
358 | const QString &annotationGroupIdentifier) const; |
359 | QRect annotationLineRectInView(int line) const; |
360 | |
361 | private: |
362 | KTextEditor::ViewPrivate *m_view; |
363 | KTextEditor::DocumentPrivate *m_doc; |
364 | KateViewInternal *m_viewInternal; |
365 | |
366 | bool m_iconBorderOn : 1; |
367 | bool m_lineNumbersOn : 1; |
368 | bool m_relLineNumbersOn : 1; |
369 | bool m_updateRelLineNumbers : 1; |
370 | bool m_foldingMarkersOn : 1; |
371 | bool m_dynWrapIndicatorsOn : 1; |
372 | bool m_annotationBorderOn : 1; |
373 | bool m_updatePositionToArea : 1; |
374 | bool m_mouseOver = false; |
375 | |
376 | typedef QPair<int, KateIconBorder::BorderArea> AreaPosition; |
377 | std::vector<AreaPosition> m_positionToArea; |
378 | |
379 | const int m_separatorWidth = 2; |
380 | const int m_modAreaWidth = 3; |
381 | qreal m_maxCharWidth = 0.0; |
382 | int m_lineNumberAreaWidth = 0; |
383 | int m_iconAreaWidth = 0; |
384 | int m_foldingAreaWidth = 0; |
385 | int m_annotationAreaWidth = 0; |
386 | const QChar m_dynWrapIndicatorChar = QChar(0x21AA); |
387 | int m_dynWrapIndicators = 0; |
388 | int m_lastClickedLine = -1; |
389 | |
390 | KTextEditor::AbstractAnnotationItemDelegate *m_annotationItemDelegate; |
391 | bool m_hasUniformAnnotationItemSizes = false; |
392 | bool m_isDefaultAnnotationItemDelegate = true; |
393 | |
394 | QPointer<KateTextPreview> m_foldingPreview; |
395 | KTextEditor::MovingRange *m_foldingRange = nullptr; |
396 | int m_currentLine = -1; |
397 | QTimer m_antiFlickerTimer; |
398 | void highlightFoldingDelayed(int line); |
399 | void hideFolding(); |
400 | |
401 | private Q_SLOTS: |
402 | void highlightFolding(); |
403 | void handleDestroyedAnnotationItemDelegate(); |
404 | void delayedUpdateOfSizeWithRepaint(); |
405 | |
406 | private: |
407 | QString m_hoveredAnnotationGroupIdentifier; |
408 | }; |
409 | |
410 | class KateViewEncodingAction : public KSelectAction |
411 | { |
412 | public: |
413 | KateViewEncodingAction(KTextEditor::DocumentPrivate *_doc, KTextEditor::ViewPrivate *_view, const QString &text, QObject *parent, bool saveAsMode = false); |
414 | |
415 | bool setCurrentCodec(const QString &codec); |
416 | |
417 | private: |
418 | void init(); |
419 | void subActionTriggered(QAction *); |
420 | |
421 | KTextEditor::DocumentPrivate *doc; |
422 | KTextEditor::ViewPrivate *view; |
423 | QAction *currentSubAction; |
424 | const bool m_saveAsMode; |
425 | |
426 | private: |
427 | void setEncoding(const QString &e); |
428 | void slotAboutToShow(); |
429 | }; |
430 | |
431 | class KateViewBar; |
432 | |
433 | class KateViewBarWidget : public QWidget |
434 | { |
435 | Q_OBJECT |
436 | friend class KateViewBar; |
437 | |
438 | public: |
439 | explicit KateViewBarWidget(bool addCloseButton, QWidget *parent = nullptr); |
440 | |
441 | virtual void closed() |
442 | { |
443 | } |
444 | |
445 | /// returns the currently associated KateViewBar and 0, if it is not associated |
446 | KateViewBar *viewBar() |
447 | { |
448 | return m_viewBar; |
449 | } |
450 | |
451 | protected: |
452 | /** |
453 | * @return widget that should be used to add controls to bar widget |
454 | */ |
455 | QWidget *centralWidget() |
456 | { |
457 | return m_centralWidget; |
458 | } |
459 | |
460 | /** |
461 | * @return close button, if there |
462 | */ |
463 | QToolButton *closeButton() |
464 | { |
465 | return m_closeButton; |
466 | } |
467 | |
468 | Q_SIGNALS: |
469 | void hideMe(); |
470 | |
471 | // for friend class KateViewBar |
472 | private: |
473 | void setAssociatedViewBar(KateViewBar *bar) |
474 | { |
475 | m_viewBar = bar; |
476 | } |
477 | |
478 | private: |
479 | QWidget *m_centralWidget = nullptr; |
480 | KateViewBar *m_viewBar = nullptr; // 0-pointer, if not added to a view bar |
481 | QToolButton *m_closeButton = nullptr; |
482 | }; |
483 | |
484 | class KateViewBar : public QWidget |
485 | { |
486 | Q_OBJECT |
487 | |
488 | public: |
489 | KateViewBar(bool external, QWidget *parent, KTextEditor::ViewPrivate *view); |
490 | |
491 | /** |
492 | * Adds a widget to this viewbar. |
493 | * Widget is initially invisible, you should call showBarWidget, to show it. |
494 | * Several widgets can be added to the bar, but only one can be visible |
495 | */ |
496 | void addBarWidget(KateViewBarWidget *newBarWidget); |
497 | |
498 | /** |
499 | * Removes a widget from this viewbar. |
500 | * Removing a widget makes sense if it takes a lot of space vertically, |
501 | * because we use a QStackedWidget to maintain the same height for all |
502 | * widgets in the viewbar. |
503 | */ |
504 | void removeBarWidget(KateViewBarWidget *barWidget); |
505 | |
506 | /** |
507 | * @return if viewbar has widget @p barWidget |
508 | */ |
509 | bool hasBarWidget(KateViewBarWidget *barWidget) const; |
510 | |
511 | /** |
512 | * Shows barWidget that was previously added with addBarWidget. |
513 | * @see hideCurrentBarWidget |
514 | */ |
515 | void showBarWidget(KateViewBarWidget *barWidget); |
516 | |
517 | /** |
518 | * Adds widget that will be always shown in the viewbar. |
519 | * After adding permanent widget viewbar is immediately shown. |
520 | * ViewBar with permanent widget won't hide itself |
521 | * until permanent widget is removed. |
522 | * OTOH showing/hiding regular barWidgets will work as usual |
523 | * (they will be shown above permanent widget) |
524 | * |
525 | * If permanent widget already exists, asserts! |
526 | */ |
527 | void addPermanentBarWidget(KateViewBarWidget *barWidget); |
528 | |
529 | /** |
530 | * Removes permanent bar widget from viewbar. |
531 | * If no other viewbar widgets are shown, viewbar gets hidden. |
532 | * |
533 | * barWidget is not deleted, caller must do it if it wishes |
534 | */ |
535 | void removePermanentBarWidget(KateViewBarWidget *barWidget); |
536 | |
537 | /** |
538 | * @return true if the a KateViewBar is visible*/ |
539 | bool barWidgetVisible() const; |
540 | |
541 | public Q_SLOTS: |
542 | /** |
543 | * Hides currently shown bar widget |
544 | */ |
545 | void hideCurrentBarWidget(); |
546 | |
547 | protected: |
548 | void keyPressEvent(QKeyEvent *event) override; |
549 | void hideEvent(QHideEvent *event) override; |
550 | |
551 | private: |
552 | /** |
553 | * Shows or hides whole viewbar |
554 | */ |
555 | void setViewBarVisible(bool visible); |
556 | |
557 | bool m_external; |
558 | |
559 | private: |
560 | KTextEditor::ViewPrivate *m_view; |
561 | QStackedWidget *m_stack; |
562 | KateViewBarWidget *m_permanentBarWidget; |
563 | QVBoxLayout *m_layout; |
564 | }; |
565 | |
566 | class KateCommandLineBar : public KateViewBarWidget |
567 | { |
568 | public: |
569 | explicit KateCommandLineBar(KTextEditor::ViewPrivate *view, QWidget *parent = nullptr); |
570 | ~KateCommandLineBar() override; |
571 | |
572 | void setText(const QString &text, bool selected = true); |
573 | void execute(const QString &text); |
574 | |
575 | public: |
576 | static void showHelpPage(); |
577 | |
578 | private: |
579 | class KateCmdLineEdit *m_lineEdit; |
580 | }; |
581 | |
582 | class KateCmdLineEdit : public KLineEdit |
583 | { |
584 | Q_OBJECT |
585 | |
586 | public: |
587 | KateCmdLineEdit(KateCommandLineBar *bar, KTextEditor::ViewPrivate *view); |
588 | bool event(QEvent *e) override; |
589 | |
590 | void hideEvent(QHideEvent *e) override; |
591 | |
592 | Q_SIGNALS: |
593 | void hideRequested(); |
594 | |
595 | public Q_SLOTS: |
596 | void slotReturnPressed(const QString &cmd); |
597 | |
598 | private Q_SLOTS: |
599 | void hideLineEdit(); |
600 | |
601 | protected: |
602 | void focusInEvent(QFocusEvent *ev) override; |
603 | void keyPressEvent(QKeyEvent *ev) override; |
604 | |
605 | private: |
606 | /** |
607 | * Parse an expression denoting a position in the document. |
608 | * Return the position as an integer. |
609 | * Examples of expressions are "10" (the 10th line), |
610 | * "$" (the last line), "." (the current line), |
611 | * "'a" (the mark 'a), "/foo/" (a forwards search for "foo"), |
612 | * and "?bar?" (a backwards search for "bar"). |
613 | * @param string the expression to parse |
614 | * @return the position, an integer |
615 | */ |
616 | void fromHistory(bool up); |
617 | QString helptext(const QPoint &) const; |
618 | |
619 | KTextEditor::ViewPrivate *m_view; |
620 | KateCommandLineBar *m_bar; |
621 | bool m_msgMode; |
622 | QString m_oldText; |
623 | uint m_histpos; ///< position in the history |
624 | uint m_cmdend; ///< the point where a command ends in the text, if we have a valid one. |
625 | KTextEditor::Command *m_command; ///< For completing flags/args and interactiveness |
626 | class KateCmdLnWhatsThis *m_help; |
627 | |
628 | QTimer *m_hideTimer; |
629 | }; |
630 | |
631 | class KateViewSchemaAction : public KActionMenu |
632 | { |
633 | public: |
634 | KateViewSchemaAction(const QString &text, QObject *parent) |
635 | : KActionMenu(text, parent) |
636 | { |
637 | init(); |
638 | setPopupMode(QToolButton::InstantPopup); |
639 | } |
640 | |
641 | void (KTextEditor::ViewPrivate *view); |
642 | |
643 | private: |
644 | void init(); |
645 | |
646 | QPointer<KTextEditor::ViewPrivate> m_view; |
647 | QStringList names; |
648 | QActionGroup *m_group; |
649 | int last; |
650 | |
651 | public: |
652 | void slotAboutToShow(); |
653 | |
654 | private: |
655 | void setSchema(); |
656 | }; |
657 | |
658 | #endif |
659 | |