diff options
Diffstat (limited to 'src/gui/general/LinedStaff.h')
-rw-r--r-- | src/gui/general/LinedStaff.h | 759 |
1 files changed, 759 insertions, 0 deletions
diff --git a/src/gui/general/LinedStaff.h b/src/gui/general/LinedStaff.h new file mode 100644 index 0000000..1444bd2 --- /dev/null +++ b/src/gui/general/LinedStaff.h @@ -0,0 +1,759 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rosegarden + A MIDI and audio sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral rights of Guillaume Laurent, Chris Cannam, and Richard + Bown to claim authorship of this work have been asserted. + + Other copyrights also apply to some parts of this work. Please + see the AUTHORS file and individual file headers for details. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _RG_LINEDSTAFF_H_ +#define _RG_LINEDSTAFF_H_ + +#include "base/Event.h" +#include "base/FastVector.h" +#include "base/Staff.h" +#include "base/ViewElement.h" +#include <qrect.h> +#include <utility> +#include <vector> + + +class QCanvasLine; +class QCanvasItem; +class QCanvas; +class isFirstBarInRow; +class barNo; + + +namespace Rosegarden +{ + +class BarLine; +class TimeSignature; +class SnapGrid; +class Segment; +class HorizontalLayoutEngine; +class Event; + + +/** + * LinedStaff is a base class for implementations of Staff that + * display the contents of a Segment on a set of horizontal lines + * with optional vertical bar lines. + * Likely subclasses include the notation and piano-roll staffs. + * + * In general, this class handles x coordinates in floating-point, + * but y-coordinates as integers because of the requirement that + * staff lines be a precise integral distance apart. + */ + +class LinedStaff : public Staff +{ +public: + typedef std::pair<double, int> LinedStaffCoords; + + enum PageMode { + LinearMode = 0, + ContinuousPageMode, + MultiPageMode + }; + + enum BarStyle { + PlainBar = 0, + DoubleBar, + HeavyDoubleBar, + RepeatEndBar, + RepeatStartBar, + RepeatBothBar, + NoVisibleBar + }; + +protected: + /** + * Create a new LinedStaff for the given Segment, with a + * linear layout. + * + * \a id is an arbitrary id for the staff in its view, + * not used within the LinedStaff implementation but + * queryable via getId + * + * \a resolution is the number of blank pixels between + * staff lines + * + * \a lineThickness is the number of pixels thick a + * staff line should be + */ + LinedStaff(QCanvas *, Segment *, SnapGrid *, + int id, int resolution, int lineThickness); + + /** + * Create a new LinedStaff for the given Segment, with a + * page layout. + * + * \a id is an arbitrary id for the staff in its view, + * not used within the LinedStaff implementation but + * queryable via getId + * + * \a resolution is the number of blank pixels between + * staff lines + * + * \a lineThickness is the number of pixels thick a + * staff line should be + * + * \a pageWidth is the width of a page, to determine + * when to break lines for page layout + * + * \a rowsPerPage is the number of rows to a page, or zero + * for a single continuous page + * + * \a rowSpacing is the distance in pixels between + * the tops of consecutive rows on this staff + */ + LinedStaff(QCanvas *, Segment *, SnapGrid *, + int id, int resolution, int lineThickness, + double pageWidth, int rowsPerPage, int rowSpacing); + + /** + * Create a new LinedStaff for the given Segment, with + * either page or linear layout. + */ + LinedStaff(QCanvas *, Segment *, SnapGrid *, + int id, int resolution, int lineThickness, PageMode pageMode, + double pageWidth, int rowsPerPage, int rowSpacing); + +public: + virtual ~LinedStaff(); + +protected: + // Methods required to define the type of staff this is + + /** + * Returns the number of visible staff lines + */ + virtual int getLineCount() const = 0; + + /** + * Returns the number of invisible staff lines + * to leave space for above (and below) the visible staff + */ + virtual int getLegerLineCount() const = 0; + + /** + * Returns the height-on-staff value for + * the bottom visible staff line (a shorthand means for + * referring to staff lines) + */ + virtual int getBottomLineHeight() const = 0; + + /** + * Returns the difference between the height-on- + * staff value of one visible staff line and the next one + * above it + */ + virtual int getHeightPerLine() const = 0; + + /** + * Returns the height-on-staff value for the top visible + * staff line. This is deliberately not virtual. + */ + int getTopLineHeight() const { + return getBottomLineHeight() + + (getLineCount() - 1) * getHeightPerLine(); + } + + /** + * Returns true if elements fill the spaces between lines, + * false if elements can fall on lines. If true, the lines + * will be displaced vertically by half a line spacing. + */ + virtual bool elementsInSpaces() const { + return false; + } + + /** + * Returns true if the staff should draw a faint vertical line at + * each beat, in between the (darker) bar lines. + */ + virtual bool showBeatLines() const { + return false; + } + + /** + * Returns the number of bars between bar-line numbers, or zero if + * bar lines should not be numbered. For example, if this + * function returns 5, every 5th bar (starting at bar 5) will be + * numbered. + */ + virtual int showBarNumbersEvery() const { + return 0; + } + + /** + * Returns the bar line / repeat style for the start of the given bar. + */ + virtual BarStyle getBarStyle(int /* barNo */) const { + return PlainBar; + } + + /** + * Returns the distance the opening (repeat) bar is inset from the + * nominal barline position. This is to accommodate the situation + * where a repeat bar has to appear after the clef and key. + */ + virtual double getBarInset(int /* barNo */, bool /* isFirstBarInRow */) const { + return 0; + } + +protected: + /// Subclass may wish to expose this + virtual void setResolution(int resolution); + + /// Subclass may wish to expose this + virtual void setLineThickness(int lineThickness); + + /// Subclass may wish to expose this + virtual void setPageMode(PageMode pageMode); + + /// Subclass may wish to expose this + virtual void setPageWidth(double pageWidth); + + /// Subclass may wish to expose this + virtual void setRowsPerPage(int rowsPerPage); + + /// Subclass may wish to expose this + virtual void setRowSpacing(int rowSpacing); + + /// Subclass may wish to expose this. Default is zero + virtual void setConnectingLineLength(int length); + +public: + /** + * Return the id of the staff. This is only useful to external + * agents, it isn't used by the LinedStaff itself. + */ + virtual int getId() const; + + /** + * Set the canvas x-coordinate of the left-hand end of the staff. + * This does not move any canvas items that have already been + * created; it should be called before the sizeStaff/positionElements + * procedure begins. + */ + virtual void setX(double x); + + /** + * Get the canvas x-coordinate of the left-hand end of the staff. + */ + virtual double getX() const; + + /** + * Set the canvas y-coordinate of the top of the first staff row. + * This does not move any canvas items that have already been + * created; it should be called before the sizeStaff/positionElements + * procedure begins. + */ + virtual void setY(int y); + + /** + * Get the canvas y-coordinate of the top of the first staff row. + */ + virtual int getY() const; + + /** + * Set the canvas width of the margin to left and right of the + * staff on each page (used only in MultiPageMode). Each staff + * row will still be pageWidth wide (that is, the margin is in + * addition to the pageWidth, not included in it). This does not + * move any canvas items that have already been created; it should + * be called before the sizeStaff/positionElements procedure + * begins. + */ + virtual void setMargin(double m); + + /** + * Get the canvas width of the left and right margins. + */ + virtual double getMargin() const; + + /** + * Set the canvas height of the area at the top of the first page + * reserved for the composition title and composer's name (used + * only in MultiPageMode). + */ + virtual void setTitleHeight(int h); + + /** + * Get the canvas height of the title area. + */ + virtual int getTitleHeight() const; + + /** + * Returns the width of the entire staff after layout. Call + * this only after you've done the full sizeStaff/positionElements + * procedure. + */ + virtual double getTotalWidth() const; + + /** + * Returns the height of the entire staff after layout. Call + * this only after you've done the full sizeStaff/positionElements + * procedure. If there are multiple rows, this will be the + * height of all rows, including any space between rows that + * is used to display other staffs. + */ + virtual int getTotalHeight() const; + + /** + * Returns the total number of pages used by the staff. + */ + int getPageCount() const { + if (m_pageMode != MultiPageMode) return 1; + else return 1 + (getRowForLayoutX(m_endLayoutX) / getRowsPerPage()); + } + + /** + * Returns the difference between the y coordinates of + * neighbouring visible staff lines. Deliberately non-virtual + */ + int getLineSpacing() const { + return m_resolution + m_lineThickness; + } + + /** + * Returns the total height of a single staff row, including ruler + */ + virtual int getHeightOfRow() const; + + /** + * Returns true if the given canvas coordinates fall within + * (any of the rows of) this staff. False if they fall in the + * gap between two rows. + */ + virtual bool containsCanvasCoords(double canvasX, int canvasY) const; + + /** + * Returns the canvas y coordinate of the specified line on the + * staff. baseX/baseY are a canvas coordinates somewhere on the + * correct row, or -1 for the default row. + */ + virtual int getCanvasYForHeight(int height, double baseX = -1, int baseY = -1) const; + + /** + * Returns the y coordinate of the specified line on the + * staff, relative to the top of the row. + */ + virtual int getLayoutYForHeight(int height) const; + + /** + * Returns the height-on-staff value nearest to the given + * canvas coordinates. + */ + virtual int getHeightAtCanvasCoords(double x, int y) const; + + /** + * Return the full width, height and origin of the bar containing + * the given canvas cooordinates. + */ + virtual QRect getBarExtents(double x, int y) const; + + /** + * Set whether this is the current staff or not. A staff that is + * current will differ visually from non-current staffs. + * + * The owner of the staffs should normally ensure that one staff + * is current (the default is non-current, even if there only is + * one staff) and that only one staff is current at once. + */ + virtual void setCurrent(bool current); + + /** + * Move the playback pointer to the layout-X coordinate + * corresponding to the given time, and show it. + */ + virtual void setPointerPosition + (HorizontalLayoutEngine&, timeT); + + /** + * Move the playback pointer to the layout-X coordinate + * corresponding to the given canvas coordinates, and show it. + */ + virtual void setPointerPosition(double x, int y); + + /** + * Move the playback pointer to the given layout-X + * coordinate, and show it. + */ + virtual void setPointerPosition(double x); + + /** + * Returns the layout-X coordinate corresponding to the current + * position of the playback pointer. + */ + virtual double getLayoutXOfPointer() const; + + /** + * Returns the canvas coordinates of the top of the playback + * pointer. + */ + virtual void getPointerPosition(double &x, int &y) const; + + /** + * Hide the playback pointer. + */ + virtual void hidePointer(); + + /** + * Move the insertion cursor to the layout-X coordinate + * corresponding to the given time, and show it. + */ + virtual void setInsertCursorPosition(HorizontalLayoutEngine&, timeT); + + /** + * Move the insertion cursor to the layout-X coordinate + * corresponding to the given canvas coordinates, and show it. + */ + virtual void setInsertCursorPosition(double x, int y); + + /** + * Returns the layout-X coordinate corresponding to the current + * position of the insertion cursor. Returns -1 if this staff + * is not current or there is some other problem. + */ + virtual double getLayoutXOfInsertCursor() const; + + /** + * Return the time of the insert cursor. + */ + virtual timeT getInsertCursorTime(HorizontalLayoutEngine&) const; + + /** + * Return the canvas coordinates of the top of the insert + * cursor. + */ + virtual void getInsertCursorPosition(double &x, int &y) const; + + /** + * Hide the insert cursor. + */ + virtual void hideInsertCursor(); + + /** + * Query the given horizontal layout object (which is assumed to + * have just completed its layout procedure) to determine the + * required extents of the staff and the positions of the bars, + * and create the bars and staff lines accordingly. It may be + * called either before or after renderElements and/or + * positionElements. + * + * No bars or staff lines will appear unless this method has + * been called. + */ + virtual void sizeStaff(HorizontalLayoutEngine& layout); + + /** + * Generate or re-generate sprites for all the elements between + * from and to. See subclasses for specific detailed comments. + * + * A very simplistic staff subclass may choose not to + * implement this (the default implementation is empty) and to + * do all the rendering work in positionElements. If rendering + * elements is slow, however, it makes sense to do it here + * because this method may be called less often. + */ + virtual void renderElements(ViewElementList::iterator from, + ViewElementList::iterator to); + + /** + * Call renderElements(from, to) on the whole staff. + */ + virtual void renderAllElements(); + + /** + * Assign suitable coordinates to the elements on the staff + * between the start and end times, based entirely on the layout + * X and Y coordinates they were given by the horizontal and + * vertical layout processes. + * + * The implementation is free to render any elements it + * chooses in this method as well. + */ + virtual void positionElements(timeT from, + timeT to) = 0; + + /** + * Call positionElements(from, to) on the whole staff. + */ + virtual void positionAllElements(); + + + /* Some optional methods for the subclass. */ + + + /** + * Return an iterator pointing to the nearest view element to the + * given canvas coordinates. + * + * If notesAndRestsOnly is true, do not return any view element + * other than a note or rest. + * + * If the closest view element is further away than + * proximityThreshold pixels in either x or y axis, return end(). + * If proximityThreshold is less than zero, treat it as infinite. + * + * Also return the clef and key in force at these coordinates. + * + * The default implementation should suit for subclasses that only + * show a single element per layout X coordinate. + */ + virtual ViewElementList::iterator getClosestElementToCanvasCoords + (double x, int y, + Event *&clef, Event *&key, + bool notesAndRestsOnly = false, int proximityThreshold = 10) { + LinedStaffCoords layoutCoords = getLayoutCoordsForCanvasCoords(x, y); + return getClosestElementToLayoutX + (layoutCoords.first, clef, key, + notesAndRestsOnly, proximityThreshold); + } + + /** + * Return an iterator pointing to the nearest view element to the + * given layout x-coordinate. + * + * If notesAndRestsOnly is true, do not return any view element + * other than a note or rest. + * + * If the closest view element is further away than + * proximityThreshold pixels in either x or y axis, return end(). + * If proximityThreshold is less than zero, treat it as infinite. + * + * Also return the clef and key in force at these coordinates. + * + * The subclass may decide whether to implement this method or not + * based on the semantics and intended usage of the class. + */ + virtual ViewElementList::iterator getClosestElementToLayoutX + (double x, + Event *&clef, Event *&key, + bool notesAndRestsOnly = false, int proximityThreshold = 10) { + return getViewElementList()->end(); + } + + /** + * Return an iterator pointing to the element "under" the given + * canvas coordinates. + * + * Return end() if there is no such element. + * + * Also return the clef and key in force at these coordinates. + * + * + * The default implementation should suit for subclasses that only + * show a single element per layout X coordinate. + */ + virtual ViewElementList::iterator getElementUnderCanvasCoords + (double x, int y, Event *&clef, Event *&key) { + LinedStaffCoords layoutCoords = getLayoutCoordsForCanvasCoords(x, y); + return getElementUnderLayoutX(layoutCoords.first, clef, key); + } + + /** + * Return an iterator pointing to the element "under" the given + * canvas coordinates. + * + * Return end() if there is no such element. + * + * Also return the clef and key in force at these coordinates. + * + * The subclass may decide whether to implement this method or not + * based on the semantics and intended usage of the class. + */ + virtual ViewElementList::iterator getElementUnderLayoutX + (double x, Event *&clef, Event *&key) { + return getViewElementList()->end(); + } + + // The default implementation of the following is empty. The + // subclass is presumed to know what the staff's name is and + // where to put it; this is simply called at some point during + // the staff-drawing process. + virtual void drawStaffName(); + + +public: + // This should not really be public -- it should be one of the + // protected methods below -- but we have some code that needs + // it and hasn't been supplied with a proper way to do without. + // Please try to avoid calling this method. + //!!! fix NotationView::doDeferredCursorMove + + // This should not really be public -- it should be one of the + // protected methods below -- but we have some code that needs + // it and hasn't been supplied with a proper way to do without. + // Please try to avoid calling this method. + //!!! fix NotationView::getStaffForCanvasCoords + LinedStaffCoords + getLayoutCoordsForCanvasCoords(double x, int y) const; + + // This should not really be public -- it should be one of the + // protected methods below -- but we have some code that needs + // it and hasn't been supplied with a proper way to do without. + // Please try to avoid calling this method. + //!!! fix NotationView::scrollToTime + LinedStaffCoords + getCanvasCoordsForLayoutCoords(double x, int y) const;//!!! + + // This should not really be public -- it should be one of the + // protected methods below -- but we have some code that needs + // it and hasn't been supplied with a proper way to do without. + // Please try to avoid calling this method. + //!!! fix NotationView::print etc + int getRowSpacing() { return m_rowSpacing; } + +protected: + // Methods that the subclass may (indeed, should) use to convert + // between the layout coordinates of elements and their canvas + // coordinates. These are deliberately not virtual. + + // Note that even linear-layout staffs have multiple rows; their + // rows all have the same y coordinate but increasing x + // coordinates, instead of the other way around. (The only reason + // for this is that it seems to be more efficient from the QCanvas + // perspective to create and manipulate many relatively short + // canvas lines rather than a smaller number of very long ones.) + + int getTopLineOffset() const { + return getLineSpacing() * getLegerLineCount(); + } + + int getBarLineHeight() const { + return getLineSpacing() * (getLineCount() - 1) + m_lineThickness; + } + + int getRowForLayoutX(double x) const { + return (int)(x / m_pageWidth); + } + + int getRowForCanvasCoords(double x, int y) const; + + int getCanvasYForTopOfStaff(int row = -1) const; + + int getCanvasYForTopLine(int row = -1) const { + return getCanvasYForTopOfStaff(row) + getTopLineOffset(); + } + + double getCanvasXForLeftOfRow(int row) const; + + double getCanvasXForRightOfRow(int row) const { + return getCanvasXForLeftOfRow(row) + m_pageWidth; + } + + LinedStaffCoords + getCanvasOffsetsForLayoutCoords(double x, int y) const { + LinedStaffCoords cc = getCanvasCoordsForLayoutCoords(x, y); + return LinedStaffCoords(cc.first - x, cc.second - y); + } + + double getCanvasXForLayoutX(double x) const; + + int getRowsPerPage() const { + return m_rowsPerPage; + } + +protected: + // Actual implementation methods. The default implementation + // shows staff lines, connecting lines (where appropriate) and bar + // lines, but does not show time signatures. To see time + // signatures, override the deleteTimeSignatures and + // insertTimeSignature methods. For repeated clefs and keys at + // the start of each row, override deleteRepeatedClefsAndKeys + // and insertRepeatedClefAndKey, but note that your layout class + // will need to allot the space for them separately. + + virtual void resizeStaffLines(); + virtual void clearStaffLineRow(int row); + virtual void resizeStaffLineRow(int row, double offset, double length); + + virtual void deleteBars(); + virtual void insertBar(double layoutX, double width, bool isCorrect, + const TimeSignature &, + int barNo, bool showBarNo); + + // The default implementations of the following two are empty. + virtual void deleteTimeSignatures(); + virtual void insertTimeSignature(double layoutX, + const TimeSignature &); + + // The default implementations of the following two are empty. + virtual void deleteRepeatedClefsAndKeys(); + virtual void insertRepeatedClefAndKey(double layoutX, int barNo); + + void initCursors(); + +protected: + + //--------------- Data members --------------------------------- + + QCanvas *m_canvas; + SnapGrid *m_snapGrid; + + int m_id; + + double m_x; + int m_y; + double m_margin; + int m_titleHeight; + int m_resolution; + int m_lineThickness; + + PageMode m_pageMode; + double m_pageWidth; + int m_rowsPerPage; + int m_rowSpacing; + int m_connectingLineLength; + + double m_startLayoutX; + double m_endLayoutX; + + bool m_current; + + typedef std::vector<QCanvasItem *> ItemList; + typedef std::vector<ItemList> ItemMatrix; + ItemMatrix m_staffLines; + ItemList m_staffConnectingLines; + + typedef std::pair<double, QCanvasItem *> LineRec; // layout-x, line + typedef FastVector<LineRec> LineRecList; + typedef FastVector<BarLine *> BarLineList;//!!! should be multiset I reckon + static bool compareBars(const BarLine *, const BarLine *); + static bool compareBarToLayoutX(const BarLine *, int); + BarLineList m_barLines; + LineRecList m_beatLines; + LineRecList m_barConnectingLines; + ItemList m_barNumbers; + + QCanvasLine *m_pointer; + QCanvasLine *m_insertCursor; + timeT m_insertCursorTime; + bool m_insertCursorTimeValid; +}; + + +} + +#endif |