1 /* 2 This file is part of KCachegrind. 3 4 SPDX-FileCopyrightText: 2002-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 5 6 SPDX-License-Identifier: GPL-2.0-only 7 */ 8 9 /** 10 * A Widget for visualizing hierarchical metrics as areas. 11 * The API is similar to QListView. 12 * 13 * This file defines the following classes: 14 * DrawParams, RectDrawing, TreeMapItem, TreeMapWidget 15 * 16 * DrawParams/RectDrawing allows reusing of TreeMap drawing 17 * functions in other widgets. 18 */ 19 20 #ifndef TREEMAP_H 21 #define TREEMAP_H 22 23 #include <QString> 24 #include <QWidget> 25 #include <QPixmap> 26 #include <QColor> 27 #include <QStringList> 28 #include <QPaintEvent> 29 #include <QKeyEvent> 30 #include <QContextMenuEvent> 31 #include <QMouseEvent> 32 33 class QMenu; 34 class TreeMapWidget; 35 class TreeMapItem; 36 class TreeMapItemList; 37 38 39 /** 40 * Drawing parameters for an object. 41 * A Helper Interface for RectDrawing. 42 */ 43 class DrawParams 44 { 45 public: 46 /** 47 * Positions for drawing into a rectangle. 48 * 49 * The specified position assumes no rotation. 50 * If there is more than one text for one position, it is put 51 * nearer to the center of the item. 52 * 53 * Drawing at top positions cuts free space from top, 54 * drawing at bottom positions cuts from bottom. 55 * Default usually gives positions clockwise according to field number. 56 */ 57 enum Position { TopLeft, TopCenter, TopRight, 58 BottomLeft, BottomCenter, BottomRight, 59 Default, Unknown}; 60 61 // no constructor as this is an abstract class ~DrawParams()62 virtual ~DrawParams() {} 63 64 virtual QString text(int) const = 0; 65 virtual QPixmap pixmap(int) const = 0; 66 virtual Position position(int) const = 0; 67 // 0: no limit, negative: leave at least -maxLines() free maxLines(int)68 virtual int maxLines(int) const { return 0; } 69 // allow breaking up content into multiple lines? allowBreak(int)70 virtual bool allowBreak(int) const { return true; } 71 // truncate or show nothing if space not enough? allowTruncation(int)72 virtual bool allowTruncation(int) const { return true; } fieldCount()73 virtual int fieldCount() const { return 0; } 74 backColor()75 virtual QColor backColor() const { return Qt::white; } 76 virtual const QFont& font() const = 0; 77 selected()78 virtual bool selected() const { return false; } current()79 virtual bool current() const { return false; } shaded()80 virtual bool shaded() const { return true; } rotated()81 virtual bool rotated() const { return false; } drawFrame()82 virtual bool drawFrame() const { return true; } 83 }; 84 85 86 /* 87 * DrawParam with attributes stored 88 */ 89 class StoredDrawParams: public DrawParams 90 { 91 public: 92 StoredDrawParams(); 93 explicit StoredDrawParams(const QColor& c, 94 bool selected = false, bool current = false); 95 96 // getters 97 QString text(int) const override; 98 QPixmap pixmap(int) const override; 99 Position position(int) const override; 100 int maxLines(int) const override; fieldCount()101 int fieldCount() const override { return _field.size(); } 102 backColor()103 QColor backColor() const override { return _backColor; } selected()104 bool selected() const override { return _selected; } current()105 bool current() const override { return _current; } shaded()106 bool shaded() const override { return _shaded; } rotated()107 bool rotated() const override { return _rotated; } drawFrame()108 bool drawFrame() const override { return _drawFrame; } 109 110 const QFont& font() const override; 111 112 // attribute setters 113 void setField(int f, const QString& t, const QPixmap& pm = QPixmap(), 114 Position p = Default, int maxLines = 0); 115 void setText(int f, const QString&); 116 void setPixmap(int f, const QPixmap&); 117 void setPosition(int f, Position); 118 void setMaxLines(int f, int); setBackColor(const QColor & c)119 void setBackColor(const QColor& c) { _backColor = c; } setSelected(bool b)120 void setSelected(bool b) { _selected = b; } setCurrent(bool b)121 void setCurrent(bool b) { _current = b; } setShaded(bool b)122 void setShaded(bool b) { _shaded = b; } setRotated(bool b)123 void setRotated(bool b) { _rotated = b; } drawFrame(bool b)124 void drawFrame(bool b) { _drawFrame = b; } 125 126 protected: 127 QColor _backColor; 128 bool _selected :1; 129 bool _current :1; 130 bool _shaded :1; 131 bool _rotated :1; 132 bool _drawFrame :1; 133 134 private: 135 // resize field array if needed to allow to access field <f> 136 void ensureField(int f); 137 138 struct Field { 139 QString text; 140 QPixmap pix; 141 Position pos; 142 int maxLines; 143 }; 144 145 QVector<Field> _field; 146 }; 147 148 149 /* State for drawing on a rectangle. 150 * 151 * Following drawing functions are provided: 152 * - background drawing with shading and 3D frame 153 * - successive pixmap/text drawing at various positions with wrap-around 154 * optimized for minimal space usage (e.g. if a text is drawn at top right 155 * after text on top left, the same line is used if space allows) 156 * 157 */ 158 class RectDrawing 159 { 160 public: 161 explicit RectDrawing(const QRect&); 162 ~RectDrawing(); 163 164 // The default DrawParams object used. 165 DrawParams* drawParams(); 166 // we take control over the given object (i.e. delete at destruction) 167 void setDrawParams(DrawParams*); 168 169 // draw on a given QPainter, use this class as info provider per default 170 void drawBack(QPainter*, DrawParams* dp = nullptr); 171 /* Draw field at position() from pixmap()/text() with maxLines(). 172 * Returns true if something was drawn 173 */ 174 bool drawField(QPainter*, int f, DrawParams* dp = nullptr); 175 176 // resets rectangle for free space 177 void setRect(const QRect&); 178 179 // Returns the rectangle area still free of text/pixmaps after 180 // a number of drawText() calls. 181 QRect remainingRect(DrawParams* dp = nullptr); 182 183 private: 184 int _usedTopLeft, _usedTopCenter, _usedTopRight; 185 int _usedBottomLeft, _usedBottomCenter, _usedBottomRight; 186 QRect _rect; 187 188 // temporary 189 int _fontHeight; 190 QFontMetrics* _fm; 191 DrawParams* _dp; 192 }; 193 194 195 class TreeMapItemList: public QList<TreeMapItem*> 196 { 197 public: 198 TreeMapItem* commonParent(); 199 }; 200 201 202 /** 203 * Base class of items in TreeMap. 204 * 205 * This class supports an arbitrary number of text() strings 206 * positioned counterclock-wise starting at TopLeft. Each item 207 * has its own static value(), sum() and sorting(). The 208 * splitMode() and borderWidth() is taken from a TreeMapWidget. 209 * 210 * If you want more flexibility, reimplement TreeMapItem and 211 * override the corresponding methods. For dynamic creation of child 212 * items on demand, reimplement children(). 213 */ 214 class TreeMapItem: public StoredDrawParams 215 { 216 public: 217 218 /** 219 * Split direction for nested areas: 220 * AlwaysBest: Choose split direction for every subitem according to 221 * longest side of rectangle left for drawing 222 * Best: Choose split direction for all subitems of an area 223 * depending on longest side 224 * HAlternate: Horizontal at top; alternate direction on depth step 225 * VAlternate: Vertical at top; alternate direction on depth step 226 * Horizontal: Always horizontal split direction 227 * Vertical: Always vertical split direction 228 */ 229 enum SplitMode { Bisection, Columns, Rows, 230 AlwaysBest, Best, 231 HAlternate, VAlternate, 232 Horizontal, Vertical }; 233 234 explicit TreeMapItem(TreeMapItem* parent = nullptr, double value = 1.0 ); 235 TreeMapItem(TreeMapItem* parent, double value, 236 const QString& text1, const QString& text2 = QString(), 237 const QString& text3 = QString(), const QString& text4 = QString()); 238 ~TreeMapItem() override; 239 240 bool isChildOf(TreeMapItem*); 241 242 TreeMapItem* commonParent(TreeMapItem* item); 243 244 // force a redraw of this item 245 void redraw(); 246 247 // delete all children 248 void clear(); 249 250 // force new child generation & refresh 251 void refresh(); 252 253 // call in a reimplemented items() method to check if already called 254 // after a clear(), this will return false 255 bool initialized(); 256 257 /** 258 * Adds an item to a parent. 259 * When no sorting is used, the item is appended (drawn at bottom). 260 * This is only needed if the parent was not already specified in the 261 * construction of the item. 262 */ 263 void addItem(TreeMapItem*); 264 265 /** 266 * Returns a list of text strings of specified text number, 267 * from root up to this item. 268 */ 269 QStringList path(int) const; 270 271 /** 272 * Depth of this item. This is the distance to root. 273 */ 274 int depth() const; 275 276 /** 277 * Parent Item 278 */ parent()279 TreeMapItem* parent() const { return _parent; } 280 281 /** 282 * Temporary rectangle used for drawing this item the last time. 283 * This is internally used to map from a point to an item. 284 */ setItemRect(const QRect & r)285 void setItemRect(const QRect& r) { _rect = r; } 286 void clearItemRect(); itemRect()287 const QRect& itemRect() const { return _rect; } width()288 int width() const { return _rect.width(); } height()289 int height() const { return _rect.height(); } 290 291 /** 292 * Temporary rectangle list of free space of this item. 293 * Used internally to enable tooltip. 294 */ 295 void clearFreeRects(); freeRects()296 const QList<QRect>& freeRects() const { return _freeRects; } 297 void addFreeRect(const QRect& r); 298 299 /** 300 * Temporary child item index of the child that was current() recently. 301 */ index()302 int index() const { return _index; } setIndex(int i)303 void setIndex(int i) { _index = i; } 304 305 306 /** 307 * TreeMap widget this item is put in. 308 */ widget()309 TreeMapWidget* widget() const { return _widget; } 310 311 void setParent(TreeMapItem* p); setWidget(TreeMapWidget * w)312 void setWidget(TreeMapWidget* w) { _widget = w; } setSum(double s)313 void setSum(double s) { _sum = s; } setValue(double s)314 void setValue(double s) { _value = s; } 315 316 virtual double sum() const; 317 virtual double value() const; 318 // replace "Default" position with setting from TreeMapWidget 319 Position position(int) const override; 320 const QFont& font() const override; 321 virtual bool isMarked(int) const; 322 323 virtual int borderWidth() const; 324 325 /** 326 * Returns the text number after that sorting is done or 327 * -1 for no sorting, -2 for value() sorting (default). 328 * If ascending != 0, a bool value is written at that location 329 * to indicate if sorting should be ascending. 330 */ 331 virtual int sorting(bool* ascending) const; 332 333 /** 334 * Set the sorting for child drawing. 335 * 336 * Default is no sorting: @p textNo = -1 337 * For value() sorting, use @p textNo = -2 338 * 339 * For fast sorting, set this to -1 before child insertions and call 340 * again after inserting all children. 341 */ 342 void setSorting(int textNo, bool ascending = true); 343 344 /** 345 * Resort according to the already set sorting. 346 * 347 * This has to be done if the sorting base changes (e.g. text or values 348 * change). If this is only true for the children of this item, you can 349 * set the recursive parameter to false. 350 */ 351 void resort(bool recursive = true); 352 353 virtual SplitMode splitMode() const; 354 virtual int rtti() const; 355 // not const as this can create children on demand 356 virtual TreeMapItemList* children(); 357 358 protected: 359 TreeMapItemList* _children; 360 double _sum, _value; 361 362 private: 363 TreeMapWidget* _widget; 364 TreeMapItem* _parent; 365 366 int _sortTextNo; 367 bool _sortAscending; 368 369 // temporary layout 370 QRect _rect; 371 QList<QRect> _freeRects; 372 int _depth; 373 374 // temporary self value (when using level skipping) 375 double _unused_self; 376 377 // index of last active subitem 378 int _index; 379 }; 380 381 382 /** 383 * Class for visualization of a metric of hierarchically 384 * nested items as 2D areas. 385 */ 386 class TreeMapWidget: public QWidget 387 { 388 Q_OBJECT 389 390 public: 391 392 /** 393 * Same as in QListBox/QListView 394 */ 395 enum SelectionMode { Single, Multi, Extended, NoSelection }; 396 397 /* The widget gets owner of the base item */ 398 explicit TreeMapWidget(TreeMapItem* base, QWidget* parent=nullptr); 399 ~TreeMapWidget() override; 400 401 /** 402 * Returns the TreeMapItem filling out the widget space 403 */ base()404 TreeMapItem* base() const { return _base; } 405 406 /** 407 * Returns a reference to the current widget font. 408 */ 409 const QFont& currentFont() const; 410 411 /** 412 * Returns the area item at position x/y, independent from any 413 * maxSelectDepth setting. 414 */ 415 TreeMapItem* item(int x, int y) const; 416 417 /** 418 * Returns the nearest item with a visible area; this 419 * can be the given item itself. 420 */ 421 TreeMapItem* visibleItem(TreeMapItem*) const; 422 423 /** 424 * Returns the item possible for selection. this returns the 425 * given item itself or a parent thereof, 426 * depending on setting of maxSelectDepth(). 427 */ 428 TreeMapItem* possibleSelection(TreeMapItem*) const; 429 430 /** 431 * Selects or unselects an item. 432 * In multiselection mode, the constrain that a selected item 433 * has no selected children or parents stays true. 434 */ 435 void setSelected(TreeMapItem*, bool selected = true); 436 437 /** 438 * Switches on the marking @p markNo. Marking 0 switches off marking. 439 * This is mutually exclusive to selection, and is automatically 440 * switched off when selection is changed (also by the user). 441 * Marking is visually the same as selection, and is based on 442 * TreeMapItem::isMarked(@c markNo). 443 * This enables to programmatically show multiple selected items 444 * at once even in single selection mode. 445 */ 446 void setMarked(int markNo = 1, bool redraw = true); 447 448 /** 449 * Clear selection of all selected items which are children of 450 * parent. When parent == 0, clears whole selection 451 * Returns true if selection changed. 452 */ 453 bool clearSelection(TreeMapItem* parent = nullptr); 454 455 /** 456 * Selects or unselects items in a range. 457 * This is needed internally for Shift-Click in Extended mode. 458 * Range means for a hierarchical widget: 459 * - select/unselect i1 and i2 according selected 460 * - search common parent of i1 and i2, and select/unselect the 461 * range of direct children between but excluding the child 462 * leading to i1 and the child leading to i2. 463 */ 464 void setRangeSelection(TreeMapItem* i1, 465 TreeMapItem* i2, bool selected); 466 467 /** 468 * Sets the current item. 469 * The current item is mainly used for keyboard navigation. 470 */ 471 void setCurrent(TreeMapItem*, bool kbd=false); 472 473 /** 474 * Set the maximal depth a selected item can have. 475 * If you try to select a item with higher depth, the ancestor holding 476 * this condition is used. 477 * 478 * See also possibleSelection(). 479 */ setMaxSelectDepth(int d)480 void setMaxSelectDepth(int d) { _maxSelectDepth = d; } 481 482 setSelectionMode(SelectionMode m)483 void setSelectionMode(SelectionMode m) { _selectionMode = m; } 484 485 /** 486 * for setting/getting global split direction 487 */ 488 void setSplitMode(TreeMapItem::SplitMode m); 489 TreeMapItem::SplitMode splitMode() const; 490 // returns true if string was recognized 491 bool setSplitMode(const QString&); 492 QString splitModeString() const; 493 494 495 /* 496 * Shading of rectangles enabled ? 497 */ 498 void setShadingEnabled(bool s); isShadingEnabled()499 bool isShadingEnabled() const { return _shading; } 500 501 /* Setting for a whole depth level: draw 3D frame (default) or solid */ 502 void drawFrame(int d, bool b); drawFrame(int d)503 bool drawFrame(int d) const { return (d<4)?_drawFrame[d]:true; } 504 505 /* Setting for a whole depth level: draw items (default) or transparent */ 506 void setTransparent(int d, bool b); isTransparent(int d)507 bool isTransparent(int d) const { return (d<4)?_transparent[d]:false; } 508 509 /** 510 * Items usually have a size proportional to their value(). 511 * With @p width, you can give the minimum width 512 * of the resulting rectangle to still be drawn. 513 * For space not used because of to small items, you can specify 514 * with @p reuseSpace if the background should shine through or 515 * the space will be used to enlarge the next item to be drawn 516 * at this level. 517 */ 518 void setVisibleWidth(int width, bool reuseSpace = false); 519 520 /** 521 * If a children value() is almost the parents sum(), 522 * it can happen that the border to be drawn for visibility of 523 * nesting relations takes to much space, and the 524 * parent/child size relation can not be mapped to a correct 525 * area size relation. 526 * 527 * Either 528 * (1) Ignore the incorrect drawing, or 529 * (2) Skip drawing of the parent level altogether. 530 */ 531 void setSkipIncorrectBorder(bool enable = true); skipIncorrectBorder()532 bool skipIncorrectBorder() const { return _skipIncorrectBorder; } 533 534 /** 535 * Maximal nesting depth 536 */ 537 void setMaxDrawingDepth(int d); maxDrawingDepth()538 int maxDrawingDepth() const { return _maxDrawingDepth; } 539 540 /** 541 * Minimal area for rectangles to draw 542 */ 543 void setMinimalArea(int area); minimalArea()544 int minimalArea() const { return _minimalArea; } 545 546 /* defaults for text attributes */ 547 QString defaultFieldType(int) const; 548 QString defaultFieldStop(int) const; 549 bool defaultFieldVisible(int) const; 550 bool defaultFieldForced(int) const; 551 DrawParams::Position defaultFieldPosition(int) const; 552 553 /** 554 * Set the type name of a field. 555 * This is important for the visualization menu generated 556 * with visualizationMenu() 557 */ 558 void setFieldType(int, const QString&); 559 QString fieldType(int) const; 560 561 /** 562 * Stop drawing at item with name 563 */ 564 void setFieldStop(int, const QString&); 565 QString fieldStop(int) const; 566 567 /** 568 * Should the text with number textNo be visible? 569 * This is only done if remaining space is enough to allow for 570 * proportional size constrains. 571 */ 572 void setFieldVisible(int, bool); 573 bool fieldVisible(int) const; 574 575 /** 576 * Should the drawing of the name into the rectangle be forced? 577 * This enables drawing of the name before drawing subitems, and 578 * thus destroys proportional constrains. 579 */ 580 void setFieldForced(int, bool); 581 bool fieldForced(int) const; 582 583 /** 584 * Set the field position in the area. See TreeMapItem::Position 585 */ 586 void setFieldPosition(int, DrawParams::Position); 587 DrawParams::Position fieldPosition(int) const; 588 void setFieldPosition(int, const QString&); 589 QString fieldPositionString(int) const; 590 591 /** 592 * Do we allow the texts to be rotated by 90 degrees for better fitting? 593 */ 594 void setAllowRotation(bool); allowRotation()595 bool allowRotation() const { return _allowRotation; } 596 597 void setBorderWidth(int w); borderWidth()598 int borderWidth() const { return _borderWidth; } 599 600 /** 601 * Populate given menu with option items. 602 * The added items are automatically connected to handlers. 603 */ 604 void addSplitDirectionItems(QMenu*); 605 widget()606 TreeMapWidget* widget() { return this; } current()607 TreeMapItem* current() const { return _current; } selection()608 TreeMapItemList selection() const { return _selection; } 609 bool isSelected(TreeMapItem* i) const; maxSelectDepth()610 int maxSelectDepth() const { return _maxSelectDepth; } selectionMode()611 SelectionMode selectionMode() const { return _selectionMode; } 612 613 /** 614 * Return tooltip string to show for a item (can be rich text) 615 * Default implementation gives lines with "text0 (text1)" going to root. 616 */ 617 virtual QString tipString(TreeMapItem* i) const; 618 619 /** 620 * Redraws an item with all children. 621 * This takes changed values(), sums(), colors() and text() into account. 622 */ 623 void redraw(TreeMapItem*); redraw()624 void redraw() { redraw(_base); } 625 626 /** 627 * Resort all TreeMapItems. See TreeMapItem::resort(). 628 */ resort()629 void resort() { _base->resort(true); } 630 631 // internal 632 void drawTreeMap(); 633 634 // used internally when items are destroyed 635 void deletingItem(TreeMapItem*); 636 637 protected Q_SLOTS: 638 void splitActivated(QAction*); 639 640 Q_SIGNALS: 641 void selectionChanged(); 642 void selectionChanged(TreeMapItem*); 643 644 /** 645 * This signal is emitted if the current item changes. 646 * If the change is done because of keyboard navigation, 647 * the @p keyboard is set to true 648 */ 649 void currentChanged(TreeMapItem*, bool keyboard); 650 void clicked(TreeMapItem*); 651 void returnPressed(TreeMapItem*); 652 void doubleClicked(TreeMapItem*); 653 void rightButtonPressed(TreeMapItem*, const QPoint &); 654 void contextMenuRequested(TreeMapItem*, const QPoint &); 655 656 protected: 657 void mousePressEvent( QMouseEvent * ) override; 658 void contextMenuEvent( QContextMenuEvent * ) override; 659 void mouseReleaseEvent( QMouseEvent * ) override; 660 void mouseMoveEvent( QMouseEvent * ) override; 661 void mouseDoubleClickEvent( QMouseEvent * ) override; 662 void keyPressEvent( QKeyEvent* ) override; 663 void paintEvent( QPaintEvent * ) override; 664 void fontChange( const QFont& ); 665 bool event(QEvent *event) override; 666 667 private: 668 TreeMapItemList diff(TreeMapItemList&, TreeMapItemList&); 669 // returns true if selection changed 670 TreeMapItem* setTmpSelected(TreeMapItem*, bool selected = true); 671 TreeMapItem* setTmpRangeSelection(TreeMapItem* i1, 672 TreeMapItem* i2, bool selected); 673 bool isTmpSelected(TreeMapItem* i); 674 675 void drawItem(QPainter* p, TreeMapItem*); 676 void drawItems(QPainter* p, TreeMapItem*); 677 bool horizontal(TreeMapItem* i, const QRect& r); 678 void drawFill(TreeMapItem*,QPainter* p, const QRect& r); 679 void drawFill(TreeMapItem*,QPainter* p, const QRect& r, 680 TreeMapItemList* list, int idx, int len, bool goBack); 681 bool drawItemArray(QPainter* p, TreeMapItem*, const QRect& r, double, 682 TreeMapItemList* list, int idx, int len, bool); 683 bool resizeAttr(int); 684 685 void addSplitAction(QMenu*, const QString&, int); 686 687 TreeMapItem* _base; 688 TreeMapItem *_current, *_pressed, *_lastOver, *_oldCurrent; 689 int _maxSelectDepth, _maxDrawingDepth; 690 691 // attributes for field, per textNo 692 struct FieldAttr { 693 QString type, stop; 694 bool visible, forced; 695 DrawParams::Position pos; 696 }; 697 QVector<FieldAttr> _attr; 698 699 SelectionMode _selectionMode; 700 TreeMapItem::SplitMode _splitMode; 701 int _visibleWidth, _stopArea, _minimalArea, _borderWidth; 702 bool _reuseSpace, _skipIncorrectBorder, _drawSeparators, _shading; 703 bool _allowRotation; 704 bool _transparent[4], _drawFrame[4]; 705 TreeMapItem * _needsRefresh; 706 TreeMapItemList _selection; 707 int _markNo; 708 709 // temporary selection while dragging, used for drawing 710 // most of the time, _selection == _tmpSelection 711 TreeMapItemList _tmpSelection; 712 bool _inShiftDrag, _inControlDrag; 713 714 // temporary widget font metrics while drawing 715 QFont _font; 716 int _fontHeight; 717 718 // back buffer pixmap 719 QPixmap _pixmap; 720 }; 721 722 #endif 723