1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 2 3 /* 4 Rosegarden 5 A MIDI and audio sequencer and musical notation editor. 6 Copyright 2000-2021 the Rosegarden development team. 7 8 Other copyrights also apply to some parts of this work. Please 9 see the AUTHORS file and individual file headers for details. 10 11 This program is free software; you can redistribute it and/or 12 modify it under the terms of the GNU General Public License as 13 published by the Free Software Foundation; either version 2 of the 14 License, or (at your option) any later version. See the file 15 COPYING included with this distribution for more information. 16 */ 17 18 #ifndef RG_STAFFLAYOUT_H 19 #define RG_STAFFLAYOUT_H 20 21 #include "base/Event.h" 22 #include "base/ViewElement.h" 23 #include <QRect> 24 #include <utility> 25 #include <vector> 26 27 class QGraphicsScene; 28 class QGraphicsLineItem; 29 class QGraphicsItem; 30 31 namespace Rosegarden 32 { 33 34 class BarLineItem; 35 class TimeSignature; 36 class SnapGrid; 37 class ViewSegment; 38 class HorizontalLayoutEngine; 39 class Event; 40 41 42 /** 43 * StaffLayout is a base for classes that display the contents of a 44 * Segment on a set of horizontal lines with optional vertical bar 45 * lines. Possible subclasses include the notation and piano-roll 46 * staffs. 47 * 48 * In general, this class handles x coordinates in floating-point, 49 * but y-coordinates as integers because of the requirement that 50 * staff lines be a precise integral distance apart. 51 */ 52 53 class StaffLayout 54 { 55 public: 56 typedef std::pair<double, int> StaffLayoutCoords; 57 58 enum PageMode { 59 LinearMode = 0, 60 ContinuousPageMode, 61 MultiPageMode 62 }; 63 64 enum BarStyle { 65 PlainBar = 0, 66 DoubleBar, 67 HeavyDoubleBar, 68 RepeatEndBar, 69 RepeatStartBar, 70 RepeatBothBar, 71 NoVisibleBar 72 }; 73 74 protected: 75 /** 76 * Create a new StaffLayout for the given ViewSegment, with a 77 * linear layout. 78 * 79 * \a id is an arbitrary id for the staff in its view, 80 * not used within the StaffLayout implementation but 81 * queryable via getId 82 * 83 * \a resolution is the number of blank pixels between 84 * staff lines 85 * 86 * \a lineThickness is the number of pixels thick a 87 * staff line should be 88 */ 89 StaffLayout(QGraphicsScene *, ViewSegment *, SnapGrid *, 90 int id, int resolution, int lineThickness); 91 92 /** 93 * Create a new StaffLayout for the given ViewSegment, with a 94 * page layout. 95 * 96 * \a id is an arbitrary id for the staff in its view, 97 * not used within the StaffLayout implementation but 98 * queryable via getId 99 * 100 * \a resolution is the number of blank pixels between 101 * staff lines 102 * 103 * \a lineThickness is the number of pixels thick a 104 * staff line should be 105 * 106 * \a pageWidth is the width of a page, to determine 107 * when to break lines for page layout 108 * 109 * \a rowsPerPage is the number of rows to a page, or zero 110 * for a single continuous page 111 * 112 * \a rowSpacing is the distance in pixels between 113 * the tops of consecutive rows on this staff 114 */ 115 StaffLayout(QGraphicsScene *, ViewSegment *, SnapGrid *, 116 int id, int resolution, int lineThickness, 117 double pageWidth, int rowsPerPage, int rowSpacing); 118 119 /** 120 * Create a new StaffLayout for the given Segment, with 121 * either page or linear layout. 122 */ 123 StaffLayout(QGraphicsScene *, ViewSegment *, SnapGrid *, 124 int id, int resolution, int lineThickness, PageMode pageMode, 125 double pageWidth, int rowsPerPage, int rowSpacing); 126 127 public: 128 virtual ~StaffLayout(); 129 130 protected: 131 // Methods required to define the type of staff this is 132 133 /** 134 * Returns the number of visible staff lines 135 */ 136 virtual int getLineCount() const = 0; 137 138 /** 139 * Returns the number of invisible staff lines 140 * to leave space for above (and below) the visible staff 141 */ 142 virtual int getLegerLineCount() const = 0; 143 144 /** 145 * Returns the height-on-staff value for 146 * the bottom visible staff line (a shorthand means for 147 * referring to staff lines) 148 */ 149 virtual int getBottomLineHeight() const = 0; 150 151 /** 152 * Returns the difference between the height-on- 153 * staff value of one visible staff line and the next one 154 * above it 155 */ 156 virtual int getHeightPerLine() const = 0; 157 158 /** 159 * Returns the height-on-staff value for the top visible 160 * staff line. This is deliberately not virtual. 161 */ getTopLineHeight()162 int getTopLineHeight() const { 163 return getBottomLineHeight() + 164 (getLineCount() - 1) * getHeightPerLine(); 165 } 166 167 /** 168 * Returns true if elements fill the spaces between lines, 169 * false if elements can fall on lines. If true, the lines 170 * will be displaced vertically by half a line spacing. 171 */ elementsInSpaces()172 virtual bool elementsInSpaces() const { 173 return false; 174 } 175 176 /** 177 * Returns true if the staff should draw a faint vertical line at 178 * each beat, in between the (darker) bar lines. 179 */ showBeatLines()180 virtual bool showBeatLines() const { 181 return false; 182 } 183 184 /** 185 * Returns the number of bars between bar-line numbers, or zero if 186 * bar lines should not be numbered. For example, if this 187 * function returns 5, every 5th bar (starting at bar 5) will be 188 * numbered. 189 */ showBarNumbersEvery()190 virtual int showBarNumbersEvery() const { 191 return 0; 192 } 193 194 /** 195 * Returns the bar line / repeat style for the start of the given bar. 196 */ getBarStyle(int)197 virtual BarStyle getBarStyle(int /* barNo */) const { 198 return PlainBar; 199 } 200 201 /** 202 * Returns the distance the opening (repeat) bar is inset from the 203 * nominal barline position. This is to accommodate the situation 204 * where a repeat bar has to appear after the clef and key. 205 */ getBarInset(int,bool)206 virtual double getBarInset(int /* barNo */, bool /* isFirstBarInRow */) const { 207 return 0; 208 } 209 210 protected: 211 /// Subclass may wish to expose this 212 virtual void setResolution(int resolution); 213 214 /// Subclass may wish to expose this 215 virtual void setLineThickness(int lineThickness); 216 217 /// Subclass may wish to expose this 218 virtual void setPageMode(PageMode pageMode); 219 220 /// Subclass may wish to expose this 221 virtual void setPageWidth(double pageWidth); 222 223 /// Subclass may wish to expose this 224 virtual void setRowsPerPage(int rowsPerPage); 225 226 /// Subclass may wish to expose this 227 virtual void setRowSpacing(int rowSpacing); 228 229 /// Subclass may wish to expose this. Default is zero 230 virtual void setConnectingLineLength(int length); 231 232 public: 233 /** 234 * Return the id of the staff. This is only useful to external 235 * agents, it isn't used by the StaffLayout itself. 236 */ 237 virtual int getId() const; 238 239 /** 240 * Set the scene x-coordinate of the left-hand end of the staff. 241 * This does not move any scene items that have already been 242 * created; it should be called before the sizeStaff/positionElements 243 * procedure begins. 244 */ 245 virtual void setX(double x); 246 247 /** 248 * Get the scene x-coordinate of the left-hand end of the staff. 249 */ 250 virtual double getX() const; 251 252 /** 253 * Set the scene y-coordinate of the top of the first staff row. 254 * This does not move any scene items that have already been 255 * created; it should be called before the sizeStaff/positionElements 256 * procedure begins. 257 */ 258 virtual void setY(int y); 259 260 /** 261 * Get the scene y-coordinate of the top of the first staff row. 262 */ 263 virtual int getY() const; 264 265 /** 266 * Set the scene width of the margin to left and right of the 267 * staff on each page (used only in MultiPageMode). Each staff 268 * row will still be pageWidth wide (that is, the margin is in 269 * addition to the pageWidth, not included in it). This does not 270 * move any scene items that have already been created; it should 271 * be called before the sizeStaff/positionElements procedure 272 * begins. 273 */ 274 virtual void setMargin(double m); 275 276 /** 277 * Get the scene width of the left and right margins. 278 */ 279 virtual double getMargin() const; 280 281 /** 282 * Set the scene height of the area at the top of the first page 283 * reserved for the composition title and composer's name (used 284 * only in MultiPageMode). 285 */ 286 virtual void setTitleHeight(int h); 287 288 /** 289 * Get the scene height of the title area. 290 */ 291 virtual int getTitleHeight() const; 292 293 /** 294 * Returns the width of the entire staff after layout. Call 295 * this only after you've done the full sizeStaff/positionElements 296 * procedure. 297 */ 298 virtual double getTotalWidth() const; 299 300 /** 301 * Returns the height of the entire staff after layout. Call 302 * this only after you've done the full sizeStaff/positionElements 303 * procedure. If there are multiple rows, this will be the 304 * height of all rows, including any space between rows that 305 * is used to display other staffs. 306 */ 307 virtual int getTotalHeight() const; 308 309 /** 310 * Returns the total number of pages used by the staff. 311 */ getPageCount()312 int getPageCount() const { 313 if (m_pageMode != MultiPageMode) return 1; 314 else return 1 + (getRowForLayoutX(m_endLayoutX) / getRowsPerPage()); 315 } 316 317 /** 318 * Returns the difference between the y coordinates of 319 * neighbouring visible staff lines. Deliberately non-virtual 320 */ getLineSpacing()321 int getLineSpacing() const { 322 return m_resolution + m_lineThickness; 323 } 324 325 /** 326 * Returns the total height of a single staff row, including ruler 327 */ 328 virtual int getHeightOfRow() const; 329 330 /** 331 * Returns true if the given scene coordinates fall within 332 * (any of the rows of) this staff. False if they fall in the 333 * gap between two rows. 334 */ 335 virtual bool containsSceneCoords(double sceneX, int sceneY) const; 336 337 /** 338 * Returns the scene y coordinate of the specified line on the 339 * staff. baseX/baseY are a scene coordinates somewhere on the 340 * correct row, or -1 for the default row. 341 */ 342 virtual int getSceneYForHeight(int height, double baseX = -1, int baseY = -1) const; 343 344 /** 345 * Returns the y coordinate of the specified line on the 346 * staff, relative to the top of the row. 347 */ 348 virtual int getLayoutYForHeight(int height) const; 349 350 351 /** 352 * Returns the height-on-staff value nearest to the given scene coordinates, 353 * weighted toward the height specified in originalHeight. This is used 354 * when we are comparing a new height calculation to an existing one, and 355 * want the new calculation to err on the side of matching the original 356 * value unless the difference is strong enough. 357 */ 358 virtual int getWeightedHeightAtSceneCoords(int originalHeight, double x, int y); 359 360 /** 361 * Returns the height-on-staff value nearest to the given 362 * scene coordinates. 363 */ 364 virtual int getHeightAtSceneCoords(double x, int y) const; 365 366 /** 367 * Return the full width, height and origin of the bar containing 368 * the given scene cooordinates. 369 */ 370 virtual QRect getBarExtents(double x, int y) const; 371 372 /** 373 * Set whether this is the current staff or not. A staff that is 374 * current will differ visually from non-current staffs. 375 * 376 * The owner of the staffs should normally ensure that one staff 377 * is current (the default is non-current, even if there only is 378 * one staff) and that only one staff is current at once. 379 */ 380 virtual void setCurrent(bool current); 381 382 /** 383 * Query the given horizontal layout object (which is assumed to 384 * have just completed its layout procedure) to determine the 385 * required extents of the staff and the positions of the bars, 386 * and create the bars and staff lines accordingly. It may be 387 * called either before or after renderElements and/or 388 * positionElements. 389 * 390 * No bars or staff lines will appear unless this method has 391 * been called. 392 */ 393 virtual void sizeStaff(HorizontalLayoutEngine& layout); 394 395 /** 396 * Generate or re-generate sprites for all the elements between 397 * from and to. See subclasses for specific detailed comments. 398 * 399 * A very simplistic staff subclass may choose not to 400 * implement this (the default implementation is empty) and to 401 * do all the rendering work in positionElements. If rendering 402 * elements is slow, however, it makes sense to do it here 403 * because this method may be called less often. 404 */ 405 virtual void renderElements(ViewElementList::iterator from, 406 ViewElementList::iterator to); 407 408 /** 409 * Assign suitable coordinates to the elements on the staff 410 * between the start and end times, based entirely on the layout 411 * X and Y coordinates they were given by the horizontal and 412 * vertical layout processes. 413 * 414 * The implementation is free to render any elements it 415 * chooses in this method as well. 416 */ 417 virtual void positionElements(timeT from, 418 timeT to) = 0; 419 420 /* Some optional methods for the subclass. */ 421 422 /** 423 * Return an iterator pointing to the nearest view element to the 424 * given scene coordinates. 425 * 426 * If notesAndRestsOnly is true, do not return any view element 427 * other than a note or rest. 428 * 429 * If the closest view element is further away than 430 * proximityThreshold pixels in either x or y axis, return end(). 431 * If proximityThreshold is less than zero, treat it as infinite. 432 * 433 * Also return the clef and key in force at these coordinates. 434 * 435 * The default implementation should suit for subclasses that only 436 * show a single element per layout X coordinate. 437 */ 438 /* 439 virtual ViewElementList::iterator getClosestElementToSceneCoords 440 (double x, int y, 441 Event *&clef, Event *&key, 442 bool notesAndRestsOnly = false, int proximityThreshold = 10) { 443 StaffLayoutCoords layoutCoords = getLayoutCoordsForSceneCoords(x, y); 444 return getClosestElementToLayoutX 445 (layoutCoords.first, clef, key, 446 notesAndRestsOnly, proximityThreshold); 447 } 448 */ 449 /** 450 * Return an iterator pointing to the nearest view element to the 451 * given layout x-coordinate. 452 * 453 * If notesAndRestsOnly is true, do not return any view element 454 * other than a note or rest. 455 * 456 * If the closest view element is further away than 457 * proximityThreshold pixels in either x or y axis, return end(). 458 * If proximityThreshold is less than zero, treat it as infinite. 459 * 460 * Also return the clef and key in force at these coordinates. 461 */ 462 /* 463 virtual ViewElementList::iterator getClosestElementToLayoutX 464 (double x, 465 Event *&clef, Event *&key, 466 bool notesAndRestsOnly = false, int proximityThreshold = 10) = 0; 467 */ 468 /** 469 * Return an iterator pointing to the element "under" the given 470 * scene coordinates. 471 * 472 * Return end() if there is no such element. 473 * 474 * Also return the clef and key in force at these coordinates. 475 * 476 * 477 * The default implementation should suit for subclasses that only 478 * show a single element per layout X coordinate. 479 */ getElementUnderSceneCoords(double x,int y,Event * & clef,Event * & key)480 virtual ViewElementList::iterator getElementUnderSceneCoords 481 (double x, int y, Event *&clef, Event *&key) { 482 StaffLayoutCoords layoutCoords = getLayoutCoordsForSceneCoords(x, y); 483 return getElementUnderLayoutX(layoutCoords.first, clef, key); 484 } 485 486 /** 487 * Return an iterator pointing to the element "under" the given 488 * scene coordinates. 489 * 490 * Return end() if there is no such element. 491 * 492 * Also return the clef and key in force at these coordinates. 493 */ 494 virtual ViewElementList::iterator getElementUnderLayoutX 495 (double x, Event *&clef, Event *&key) = 0; 496 497 // The default implementation of the following is empty. The 498 // subclass is presumed to know what the staff's name is and 499 // where to put it; this is simply called at some point during 500 // the staff-drawing process. 501 virtual void drawStaffName(); 502 503 /** 504 * Return the smaller rectangle (in scene coords) enclosing the 505 * whole segment area. 506 */ 507 virtual QRectF getSceneArea(); 508 509 public: 510 // This should not really be public -- it should be one of the 511 // protected methods below -- but we have some code that needs 512 // it and hasn't been supplied with a proper way to do without. 513 // Please try to avoid calling this method. 514 //!!! fix NotationView::doDeferredCursorMove 515 516 // This should not really be public -- it should be one of the 517 // protected methods below -- but we have some code that needs 518 // it and hasn't been supplied with a proper way to do without. 519 // Please try to avoid calling this method. 520 //!!! fix NotationView::getStaffForSceneCoords 521 StaffLayoutCoords 522 getLayoutCoordsForSceneCoords(double x, int y) const; 523 524 // This should not really be public -- it should be one of the 525 // protected methods below -- but we have some code that needs 526 // it and hasn't been supplied with a proper way to do without. 527 // Please try to avoid calling this method. 528 //!!! fix NotationView::scrollToTime 529 StaffLayoutCoords 530 getSceneCoordsForLayoutCoords(double x, int y) const;//!!! 531 532 // This should not really be public -- it should be one of the 533 // protected methods below -- but we have some code that needs 534 // it and hasn't been supplied with a proper way to do without. 535 // Please try to avoid calling this method. 536 //!!! fix NotationView::print etc getRowSpacing()537 int getRowSpacing() { return m_rowSpacing; } 538 539 protected: 540 // Methods that the subclass may (indeed, should) use to convert 541 // between the layout coordinates of elements and their scene 542 // coordinates. These are deliberately not virtual. 543 544 // Note that even linear-layout staffs have multiple rows; their 545 // rows all have the same y coordinate but increasing x 546 // coordinates, instead of the other way around. (The only reason 547 // for this is that it seems to be more efficient from the canvas 548 // perspective to create and manipulate many relatively short 549 // lines rather than a smaller number of very long ones.) 550 //!!! review that for qgraphicsview 551 getTopLineOffset()552 int getTopLineOffset() const { 553 return getLineSpacing() * getLegerLineCount(); 554 } 555 getBarLineHeight()556 int getBarLineHeight() const { 557 return getLineSpacing() * (getLineCount() - 1) + m_lineThickness; 558 } 559 getRowForLayoutX(double x)560 int getRowForLayoutX(double x) const { 561 return (int)(x / m_pageWidth); 562 } 563 564 int getRowForSceneCoords(double x, int y) const; 565 566 int getSceneYForTopOfStaff(int row = -1) const; 567 568 int getSceneYForTopLine(int row = -1) const { 569 return getSceneYForTopOfStaff(row) + getTopLineOffset(); 570 } 571 572 double getSceneXForLeftOfRow(int row) const; 573 getSceneXForRightOfRow(int row)574 double getSceneXForRightOfRow(int row) const { 575 return getSceneXForLeftOfRow(row) + m_pageWidth; 576 } 577 578 StaffLayoutCoords getSceneOffsetsForLayoutCoords(double x,int y)579 getSceneOffsetsForLayoutCoords(double x, int y) const { 580 StaffLayoutCoords cc = getSceneCoordsForLayoutCoords(x, y); 581 return StaffLayoutCoords(cc.first - x, cc.second - y); 582 } 583 584 double getSceneXForLayoutX(double x) const; 585 getRowsPerPage()586 int getRowsPerPage() const { 587 return m_rowsPerPage; 588 } 589 590 protected: 591 // Actual implementation methods. The default implementation 592 // shows staff lines, connecting lines (where appropriate) and bar 593 // lines, but does not show time signatures. To see time 594 // signatures, override the deleteTimeSignatures and 595 // insertTimeSignature methods. For repeated clefs and keys at 596 // the start of each row, override deleteRepeatedClefsAndKeys 597 // and insertRepeatedClefAndKey, but note that your layout class 598 // will need to allot the space for them separately. 599 600 virtual void resizeStaffLines(); 601 virtual void clearStaffLineRow(int row); 602 virtual void resizeStaffLineRow(int row, double offset, double length); 603 604 virtual void deleteBars(); 605 virtual void insertBar(double layoutX, double width, bool isCorrect, 606 const TimeSignature &, 607 int barNo, bool showBarNo); 608 609 // The default implementations of the following two are empty. 610 virtual void deleteTimeSignatures(); 611 virtual void insertTimeSignature(double layoutX, 612 const TimeSignature &, bool grayed); 613 614 // The default implementations of the following two are empty. 615 virtual void deleteRepeatedClefsAndKeys(); 616 virtual void insertRepeatedClefAndKey(double layoutX, int barNo); 617 getScene()618 QGraphicsScene *getScene() { return m_scene; } 619 620 void initCursors(); 621 622 protected: 623 624 //--------------- Data members --------------------------------- 625 626 QGraphicsScene *m_scene; 627 ViewSegment *m_viewSegment; 628 SnapGrid *m_snapGrid; 629 630 int m_id; 631 632 double m_x; 633 int m_y; 634 double m_margin; 635 int m_titleHeight; 636 int m_resolution; 637 int m_lineThickness; 638 639 PageMode m_pageMode; 640 double m_pageWidth; 641 int m_rowsPerPage; 642 int m_rowSpacing; 643 int m_connectingLineLength; 644 645 double m_startLayoutX; 646 double m_endLayoutX; 647 648 bool m_current; 649 650 typedef std::vector<QGraphicsItem *> ItemList; 651 typedef std::vector<ItemList> ItemMatrix; 652 ItemMatrix m_staffLines; 653 ItemList m_staffConnectingLines; 654 655 struct BarLineComparator { operatorBarLineComparator656 bool operator()(const BarLineItem *a, const BarLineItem *b) const { 657 return compareBars(a, b); 658 } 659 }; 660 661 typedef std::pair<double, QGraphicsItem *> LineRec; // layout-x, line 662 typedef std::vector<LineRec> LineRecList; 663 typedef std::multiset<BarLineItem *, BarLineComparator> BarLineList; 664 static bool compareBars(const BarLineItem *, const BarLineItem *); 665 static bool compareBarToLayoutX(const BarLineItem *, int); 666 BarLineList m_barLines; 667 LineRecList m_beatLines; 668 LineRecList m_barConnectingLines; 669 ItemList m_barNumbers; 670 }; 671 672 673 } 674 675 #endif 676