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