diff --git a/toonz/sources/common/tcore/tstring.cpp b/toonz/sources/common/tcore/tstring.cpp index b8754c2a..a993f8d8 100644 --- a/toonz/sources/common/tcore/tstring.cpp +++ b/toonz/sources/common/tcore/tstring.cpp @@ -125,9 +125,12 @@ bool isDouble(std::wstring s) { return isDouble(::to_string(s)); } std::string toUpper(std::string a) { #ifdef _WIN32 - return _strupr(const_cast(a.c_str())); + size_t size = a.size(); + const char* cstr = a.c_str(); + std::vector buf(cstr, cstr + size + 1); + return _strupr(&buf[0]); #else - std::string ret = a; + std::string ret(a); for (int i = 0; i < (int)ret.length(); i++) ret[i] = toupper(ret[i]); return ret; #endif @@ -135,9 +138,12 @@ std::string toUpper(std::string a) { std::string toLower(std::string a) { #ifdef _WIN32 - return _strlwr(const_cast(a.c_str())); + size_t size = a.size(); + const char* cstr = a.c_str(); + std::vector buf(cstr, cstr + size + 1); + return _strlwr(&buf[0]); #else - std::string ret = a; + std::string ret(a); for (int i = 0; i < (int)ret.length(); i++) ret[i] = tolower(ret[i]); return ret; #endif @@ -145,27 +151,26 @@ std::string toLower(std::string a) { std::wstring toUpper(std::wstring a) { #ifdef _WIN32 - return _wcsupr(const_cast(a.c_str())); + size_t size = a.size(); + const wchar_t* cstr = a.c_str(); + std::vector buf(cstr, cstr + size + 1); + return _wcsupr(&buf[0]); #else - std::wstring ret; - for (int i = 0; i < (int)a.length(); i++) { - wchar_t c = towupper(a[i]); - ret += c; - } + std::wstring ret(a); + for (int i = 0; i < (int)ret.length(); i++) ret[i] = towupper(ret[i]); return ret; #endif } std::wstring toLower(std::wstring a) { #ifdef _WIN32 - return _wcslwr(const_cast(a.c_str())); + size_t size = a.size(); + const wchar_t* cstr = a.c_str(); + std::vector buf(cstr, cstr + size + 1); + return _wcslwr(&buf[0]); #else - const int length = (int)a.length(); - std::wstring ret; - ret.resize(length); - for (int i = 0; i < length; i++) { - ret[i] = towlower(a[i]); - } + std::wstring ret(a); + for (int i = 0; i < (int)ret.length(); i++) ret[i] = towlower(ret[i]); return ret; #endif } diff --git a/toonz/sources/include/cellposition.h b/toonz/sources/include/cellposition.h new file mode 100644 index 00000000..e510d10f --- /dev/null +++ b/toonz/sources/include/cellposition.h @@ -0,0 +1,58 @@ +#pragma once + +#ifndef CELL_POSITION_INCLUDED +#define CELL_POSITION_INCLUDED + +#include + +using std::min; +using std::max; + +// Identifies cells by frame and layer rather than row and column +class CellPosition { + int _frame; // a frame number. corresponds to row in vertical xsheet + int _layer; // a layer number. corresponds to col in vertical xsheet + +public: + CellPosition() : _frame(0), _layer(0) {} + CellPosition(int frame, int layer) : _frame(frame), _layer(layer) {} + + int frame() const { return _frame; } + int layer() const { return _layer; } + + void setFrame(int frame) { _frame = frame; } + void setLayer(int layer) { _layer = layer; } + + CellPosition &operator=(const CellPosition &that) = default; + + operator bool() const { return _frame || _layer; } + + CellPosition operator+(const CellPosition &add) { + return CellPosition(_frame + add._frame, _layer + add._layer); + } + CellPosition operator*(const CellPosition &mult) { + return CellPosition(_frame * mult._frame, _layer * mult._layer); + } + void ensureValid() { + if (_frame < 0) _frame = 0; + if (_layer < 0) _layer = 0; + } +}; + +// A square range identified by two corners +class CellRange { + CellPosition _from, _to; // from.frame <= to.frame && from.layer <= to.layer + +public: + CellRange() {} + CellRange(const CellPosition &from, const CellPosition &to) + : _from(min(from.frame(), to.frame()), min(from.layer(), to.layer())) + , _to(max(from.frame(), to.frame()), max(from.layer(), to.layer())) {} + + const CellPosition &from() const { return _from; } + const CellPosition &to() const { return _to; } + + CellRange &operator=(const CellRange &that) = default; +}; + +#endif diff --git a/toonz/sources/include/orientation.h b/toonz/sources/include/orientation.h new file mode 100644 index 00000000..56f6f04d --- /dev/null +++ b/toonz/sources/include/orientation.h @@ -0,0 +1,243 @@ +#pragma once + +#ifndef ORIENTATION_INCLUDED +#define ORIENTATION_INCLUDED + +#undef DVAPI +#undef DVVAR +#ifdef TOONZLIB_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +#include "tcommon.h" +#include "cellposition.h" +#include "toonz/cellpositionratio.h" +#include +#include +#include +#include +#include +#include + +using std::vector; +using std::map; + +// Defines timeline direction: top to bottom; left to right. +// old (vertical timeline) = new (universal) = old (kept) +// x = layer axis = column +// y = frame axis = row + +// A pair of numbers +class DVAPI NumberRange { + int _from, _to; // _from <= _to + + NumberRange() : _from(0), _to(0) {} + +public: + NumberRange(int from, int to) : _from(min(from, to)), _to(max(from, to)) {} + + int from() const { return _from; } + int to() const { return _to; } + + int length() const { return _to - _from; } + int middle() const { return (_to + _from) / 2; } + int weight(double toWeight) const; + double ratio(int at) const; + + NumberRange adjusted(int addFrom, int addTo) const; +}; + +class ColumnFan; +class QPixmap; +class QPainterPath; + +//! lists predefined rectangle sizes and positions (relative to top left corner +//! of a cell) +enum class PredefinedRect { + CELL, //! size of a cell + DRAG_HANDLE_CORNER, //! area for dragging a cell + KEY_ICON, //! position of key icon + CELL_NAME, //! cell name box + CELL_NAME_WITH_KEYFRAME, //! cell name box when keyframe is displayed + END_EXTENDER, //! bottom / right extender + BEGIN_EXTENDER, //! top / left extender + KEYFRAME_AREA, //! part of cell dedicated to key frames + DRAG_AREA, //! draggable side bar + SOUND_TRACK, //! area dedicated to waveform display + PREVIEW_TRACK, //! sound preview area + BEGIN_SOUND_EDIT, //! top sound resize + END_SOUND_EDIT, //! bottom sound resize + NOTE_AREA, //! size of top left note controls + NOTE_ICON, //! size of note icons that appear in cells area + FRAME_LABEL, //! area for writing frame number + FRAME_HEADER, + LAYER_HEADER, + FOLDED_LAYER_HEADER, //! size of layer header when it is folded + PLAY_RANGE, //! area for play range marker within frame header + ONION, //! onion handle placement + ONION_DOT, //! moveable dot placement + ONION_DOT_FIXED, //! fixed dot placement + ONION_AREA, //! area where mouse events will alter onion + ONION_FIXED_DOT_AREA, + ONION_DOT_AREA, + PINNED_CENTER_KEY, //! displays a small blue number + RENAME_COLUMN, //! where column rename control appears after clicking + EYE_AREA, //! clickable area larger than the eye, containing it + EYE, //! the eye itself + PREVIEW_LAYER_AREA, //! clickable area larger than preview icon, containing + //! it + PREVIEW_LAYER, + LOCK_AREA, //! clickable area larger than lock icon, containing it + LOCK, //! the lock icon itself + DRAG_LAYER, //! draggable area in layer header + LAYER_NAME, //! where to display column name. clicking will rename + LAYER_NUMBER, //! where to display column number. + SOUND_ICON, + VOLUME_TRACK, //! area where track is displayed + VOLUME_AREA, //! active area for volume control + LOOP_ICON, //! area for repeat animation icon + LAYER_HEADER_PANEL, //! panel displaying headers for the layer rows in + //! timeline mode + THUMBNAIL_AREA, //! area for header thumbnails and other icons + THUMBNAIL, //! the actual thumbnail, if there is one + PEGBAR_NAME, //! where to display pegbar name + PARENT_HANDLE_NAME, //! where to display parent handle number + FILTER_COLOR //! where to show layer's filter color +}; +enum class PredefinedLine { + LOCKED, //! dotted vertical line when cell is locked + SEE_MARKER_THROUGH, //! horizontal marker visible through drag handle + CONTINUE_LEVEL, //! level with the same name represented by vertical line + CONTINUE_LEVEL_WITH_NAME, //! adjusted when level name is on each marker + EXTENDER_LINE //! see grid through extender handle +}; +enum class PredefinedDimension { + LAYER, //! width of a layer column / height of layer row + FRAME, //! height of frame row / width of frame column + INDEX, //! index of this orientation in the array of all + SOUND_AMPLITUDE, //! amplitude of sound track, in pixels + FRAME_LABEL_ALIGN, //! alignment flag for frame number + ONION_TURN, //! onion handle turn in degrees + QBOXLAYOUT_DIRECTION, //! direction of QBoxLayout + CENTER_ALIGN, //! horizontal / vertical align +}; +enum class PredefinedPath { + DRAG_HANDLE_CORNER, //! triangle corner at drag sidebar + BEGIN_EASE_TRIANGLE, //! triangle marking beginning of ease range + END_EASE_TRIANGLE, //! triangle marking end of ease range + BEGIN_PLAY_RANGE, //! play range markers + END_PLAY_RANGE, + VOLUME_SLIDER_TRACK, //! slider track + VOLUME_SLIDER_HEAD //! slider head +}; +enum class PredefinedPoint { + KEY_HIDDEN, //! move extender handle that much if key icons are disabled + EXTENDER_XY_RADIUS, //! x and y radius for rounded rectangle + VOLUME_DIVISIONS_TOP_LEFT //! where to draw volume slider +}; +enum class PredefinedRange { + HEADER_FRAME, //! size of of column header height(v) / row header width(h) + HEADER_LAYER, //! size of row header width(v) / column header height(h) +}; + +// Knows everything about geometry of a particular orientation. +class DVAPI Orientation { +protected: + map _rects; + map _lines; + map _dimensions; + map _paths; + map _points; + map _ranges; + +public: + virtual CellPosition xyToPosition(const QPoint &xy, + const ColumnFan *fan) const = 0; + virtual QPoint positionToXY(const CellPosition &position, + const ColumnFan *fan) const = 0; + virtual CellPositionRatio xyToPositionRatio(const QPoint &xy) const = 0; + virtual QPoint positionRatioToXY(const CellPositionRatio &ratio) const = 0; + + virtual int colToLayerAxis(int layer, const ColumnFan *fan) const = 0; + virtual int rowToFrameAxis(int frame) const = 0; + + virtual QPoint frameLayerToXY(int frameAxis, int layerAxis) const = 0; + QRect frameLayerRect(const NumberRange &frameAxis, + const NumberRange &layerAxis) const; + + virtual NumberRange layerSide(const QRect &area) const = 0; + virtual NumberRange frameSide(const QRect &area) const = 0; + virtual int layerAxis(const QPoint &xy) const = 0; + virtual int frameAxis(const QPoint &xy) const = 0; + //! top right corner in vertical layout. bottom left in horizontal + virtual QPoint topRightCorner(const QRect &area) const = 0; + QRect foldedRectangle(int layerAxis, const NumberRange &frameAxis, + int i) const; + QLine foldedRectangleLine(int layerAxis, const NumberRange &frameAxis, + int i) const; + + virtual CellPosition arrowShift(int direction) const = 0; + + //! line was vertical in vertical timeline. adjust accordingly + QLine verticalLine(int layerAxis, const NumberRange &frameAxis) const; + QLine horizontalLine(int frameAxis, const NumberRange &layerAxis) const; + + virtual bool isVerticalTimeline() const = 0; + virtual bool flipVolume() const = 0; + + virtual QString name() const = 0; + virtual QString caption() const = 0; + virtual const Orientation *next() const = 0; + + const QRect &rect(PredefinedRect which) const { return _rects.at(which); } + const QLine &line(PredefinedLine which) const { return _lines.at(which); } + int dimension(PredefinedDimension which) const { + return _dimensions.at(which); + } + const QPainterPath &path(PredefinedPath which) const { + return _paths.at(which); + } + const QPoint &point(PredefinedPoint which) const { return _points.at(which); } + const NumberRange &range(PredefinedRange which) const { + return _ranges.at(which); + } + + virtual int cellWidth() const = 0; + virtual int cellHeight() const = 0; + +protected: + void addRect(PredefinedRect which, const QRect &rect); + void addLine(PredefinedLine which, const QLine &line); + void addDimension(PredefinedDimension which, int dimension); + void addPath(PredefinedPath which, const QPainterPath &path); + void addPoint(PredefinedPoint which, const QPoint &point); + void addRange(PredefinedRange which, const NumberRange &range); +}; + +// Enumerates all orientations available in the system as global const objects. +class DVAPI Orientations { + const Orientation *_topToBottom, *_leftToRight; + vector _all; + + Orientations(); + +public: + ~Orientations(); + + static const Orientations &instance(); + + static const int COUNT = 2; + + static const Orientation *topToBottom(); + static const Orientation *leftToRight(); + + static const vector &all(); + + static const Orientation *byName(const QString &name); +}; + +#endif diff --git a/toonz/sources/include/saveloadqsettings.h b/toonz/sources/include/saveloadqsettings.h new file mode 100644 index 00000000..b7ecb09e --- /dev/null +++ b/toonz/sources/include/saveloadqsettings.h @@ -0,0 +1,18 @@ +#pragma once + +#ifndef SAVE_LOAD_QSETTINGS_INCLUDED +#define SAVE_LOAD_QSETTINGS_INCLUDED + +#include + +class QSettings; + +//! An interface that claims: this object wants to save / load something +//! into / from provided qsettings +class SaveLoadQSettings { +public: + virtual void save(QSettings &settings) const = 0; + virtual void load(QSettings &settings) = 0; +}; + +#endif diff --git a/toonz/sources/include/toonz/cellpositionratio.h b/toonz/sources/include/toonz/cellpositionratio.h new file mode 100644 index 00000000..a46d708c --- /dev/null +++ b/toonz/sources/include/toonz/cellpositionratio.h @@ -0,0 +1,48 @@ +#pragma once + +#ifndef CELL_POSITION_RATIO_INCLUDED +#define CELL_POSITION_RATIO_INCLUDED + +#undef DVAPI +#undef DVVAR +#ifdef TOONZLIB_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + +#include "tcommon.h" + +class DVAPI Ratio { + int m_num, m_denom; + + void normalize(); + Ratio normalized() const; + +public: + Ratio(int num, int denom); + + friend Ratio operator+(const Ratio &a, const Ratio &b); + friend Ratio operator-(const Ratio &a, const Ratio &b); + friend Ratio operator*(const Ratio &a, const Ratio &b); + friend Ratio operator/(const Ratio &a, const Ratio &b); + + friend int operator*(const Ratio &a, int b); + + bool operator!() const { return m_num == 0; } +}; + +class DVAPI CellPositionRatio { + Ratio m_frame, m_layer; + +public: + CellPositionRatio(const Ratio &frame, const Ratio &layer) + : m_frame(frame), m_layer(layer) {} + + Ratio frame() const { return m_frame; } + Ratio layer() const { return m_layer; } +}; + +#endif diff --git a/toonz/sources/include/toonz/columnfan.h b/toonz/sources/include/toonz/columnfan.h index 7c035cae..062510ec 100644 --- a/toonz/sources/include/toonz/columnfan.h +++ b/toonz/sources/include/toonz/columnfan.h @@ -24,9 +24,10 @@ class TIStream; open folded columns, activate() and to know if column is folded or not, isActive(). - Class provides column x-axis coordinate too. - It's possible to know column index by column x-axis coordinate, colToX() - and vice versa, xToCol(). + Class provides column layer-axis coordinate too. + It's possible to know column index by column layer-axis coordinate, + colToLayerAxis() + and vice versa, layerAxisToCol(). */ //============================================================================= @@ -40,6 +41,7 @@ class DVAPI ColumnFan { std::vector m_columns; std::map m_table; int m_firstFreePos; + int m_unfolded, m_folded; /*! Called by activate() and deactivate() to update columns coordinates. @@ -52,6 +54,9 @@ Constructs a ColumnFan with default value. */ ColumnFan(); + //! Adjust column sizes when switching orientation + void setDimension(int unfolded); + /*! Set column \b col not folded. \sa deactivate() and isActive() @@ -69,15 +74,19 @@ Return true if column \b col is active, column is not folded, else return false. bool isActive(int col) const; /*! -Return column index of column in x-axis coordinate \b x. -\sa colToX() +Return column index of column in layer axis (x for vertical timeline, y for +horizontal). +\sa colToLayerAxis() */ - int xToCol(int x) const; + int layerAxisToCol(int layerAxis) const; /*! -Return column x-axis coordinate of column identify with \b col. -\sa xToCol() +Return layer coordinate (x for vertical timeline, y for horizontal) +of column identified by \b col. +\sa layerAxisToCol() */ - int colToX(int col) const; + int colToLayerAxis(int col) const; + + void copyFoldedStateFrom(const ColumnFan &from); bool isEmpty() const; diff --git a/toonz/sources/include/toonz/preferences.h b/toonz/sources/include/toonz/preferences.h index e269580d..bdfc116f 100644 --- a/toonz/sources/include/toonz/preferences.h +++ b/toonz/sources/include/toonz/preferences.h @@ -362,6 +362,9 @@ public: void enableExpandFunctionHeader(bool on); bool isExpandFunctionHeaderEnabled() const { return m_expandFunctionHeader; } + void enableShowColumnNumbers(bool on); + bool isShowColumnNumbersEnabled() const { return m_showColumnNumbers; } + // Animation tab void setKeyframeType(int s); @@ -503,7 +506,7 @@ private: m_rewindAfterPlaybackEnabled, m_fitToFlipbookEnabled, m_autosaveEnabled, m_autosaveSceneEnabled, m_autosaveOtherFilesEnabled, m_defaultViewerEnabled, m_pixelsOnly, m_showXSheetToolbar, - m_expandFunctionHeader; + m_expandFunctionHeader, m_showColumnNumbers; bool m_rasterOptimizedMemory, m_saveUnpaintedInCleanup, m_askForOverrideRender, m_automaticSVNFolderRefreshEnabled, m_SVNEnabled, m_levelsBackupEnabled, m_minimizeSaveboxAfterEditing, diff --git a/toonz/sources/include/toonz/txsheet.h b/toonz/sources/include/toonz/txsheet.h index f75b9266..0886596d 100644 --- a/toonz/sources/include/toonz/txsheet.h +++ b/toonz/sources/include/toonz/txsheet.h @@ -13,6 +13,8 @@ // TnzLib includes #include "toonz/txshcolumn.h" +#include "cellposition.h" + // STD includes #include @@ -46,6 +48,7 @@ class ToonzScene; class TXshSoundColumn; class TXshNoteSet; class TFrameId; +class Orientation; //============================================================================= @@ -58,62 +61,68 @@ class TFrameId; /*! Inherits \b TSmartObject and \b TPersist. - The class provides a collection of functions that returns xsheet + The class provides a collection of functions that returns xsheet elements, defined in - struct \b TXsheetImp, and enables manipulation of these. Most -important xsheet elements - are: a \b column \b set \b TColumnSetT, a \b pegbar \b tree \b + struct \b TXsheetImp, and enables manipulation of these. Most important +xsheet elements + are: a \b column \b set \b TColumnSetT, a \b pegbar \b tree \b TStageObjectTree, a \b fx - \b dag \b FxDag, a \b sound \b track \b TSoundTrack and a \b -scene \b ToonzScene. + \b dag \b FxDag, a \b sound \b track \b TSoundTrack and a \b scene \b +ToonzScene. - A \b column \b set contains all xsheet column. A collection of -function, concern column - set, allows to manage xsheet column. It's possible to know -xsheet column number, - getColumnCount(), know first not empty column index, + For purposes of this class, a Column is a graphics layer, and a +row is a frame number. + (Whether horizontal or vertial is a matter of displaying). + + A \b column \b set contains all xsheet columns. A collection of +functions, concerning column + set, allows to manage xsheet column. It's possible to know xsheet column +count, + getColumnCount(), know first non empty column index, getFirstFreeColumnIndex(), - or know if column in datum index is empty, isColumnEmpty(). You -can work on single xsheet - column, getColumn(), using its indexes: insert and remove a -column with insertColumn() - and removeColumn() or move column from an index to another using + or know if column in datum index is empty, isColumnEmpty(). You can work +on single xsheet + column, getColumn(), using its indexes: insert and remove a column with +insertColumn() + and removeColumn() or move column from an index to another using moveColumn(). - You can manage also column visualization in xsheet, using the -xsheet object \b ColumnFan - getColumnFan(), and find column icon getColumnIcon(). + You can manage also column visualization in xsheet, using the xsheet +object \b ColumnFan + getColumnFan(), and find column icon getColumnIcon(). - It's possible work on xsheet cells directly, getCell() and -setCell() or getCells() and - setCells(); cells are identified in xsheet by two index one for -row and one for column. - You can insert, remove or clear cells using insertCells(), -removeCells() or clearCells(); - the difference between the remove and the clear function is the -shift of remains cells. - Also there are functions to manipulate cells reverseCells(), + cell positions will be identified by a pair of row+column index, +which is a separate class. + + It's possible work on xsheet cells directly, getCell() and setCell() or +getCells() and + setCells(); cells are identified in xsheet by two index one for row and +one for column. + You can insert, remove or clear cells using insertCells(), removeCells() +or clearCells(); + the difference between the remove and the clear function is the shift of +remains cells. + Also there are functions to manipulate cells reverseCells(), swingCells(), - incrementCells(), duplicateCells(), int upTo, stepCells(), -eachCells(). + incrementCells(), duplicateCells(), int upTo, stepCells(), eachCells(). - About \b pegbar \b tree \b TStageObjectTree, it's possible to -manage it through the stage object's - related functions. + About \b pegbar \b tree \b TStageObjectTree, it's possible to manage it +through the stage object's + related functions. - The \b fx \b dag \b FxDag getFxDag() is not managed with direct + The \b fx \b dag \b FxDag getFxDag() is not managed with direct functions but is always - up to date; in fact same functions update it. For example + up to date; in fact same functions update it. For example insertColumn(), if necessary, - insert column index in fx dag, the same happen in setCells(). + insert column index in fx dag, the same happen in setCells(). - The \b sound \b track \b TSoundTrack contain a mixed sound, -computed using makeSound(), - of all \b TXshSoundColumn present in xsheet. + The \b sound \b track \b TSoundTrack contain a mixed sound, computed +using makeSound(), + of all \b TXshSoundColumn present in xsheet. - It's possible take and know the \b scene \b ToonzScene to which -the xsheet refers using - getScene() and setScene(). + It's possible take and know the \b scene \b ToonzScene to which the +xsheet refers using + getScene() and setScene(). */ class DVAPI TXsheet final : public TSmartObject, public TPersist { @@ -159,64 +168,66 @@ public: \sa getMaxFrame() */ int getFrameCount() const; - /*! Returns true if cells contained in rect delimited by first row \b \e r0, - last row \b \e r1 - and first column \b \e c0, last column \b \e c1 are empty; otherwise, - returns false. + + /*! Returns true if all cells in rect delimited by first frame \b \e + pos0.frame, + last frame \b \e pos1.frame and first layer \b \e pos0.layer, last layer \b + \e pos1.layer + are empty; otherwise, false. */ - bool isRectEmpty(int r0, int c0, int r1, int c1) const; + bool isRectEmpty(const CellPosition &pos0, const CellPosition &pos1) const; + /*! Returns the \b TXshCell cell in row identified by index \b \e row and - column identified by - index \b \e col. If column \b TXshColumnP in \b \e col is empty return + column identified by index \b \e col. If column \b TXshColumnP in \b \e col + is empty return an empty cell. \sa setCell(), getCells(), setCells() */ const TXshCell &getCell(int row, int col) const; + const TXshCell &getCell(const CellPosition &pos) const; + bool setCell(int row, int col, const TXshCell &cell); /*! Set \b \e cells[] to \b \e rowCount cells of column identified by index \b - \e col starting from - row identified by index \b \e row. If column is empty or is not a \b - TXshCellColumn - set \b \e cells[] to \b \e rowCount empty cells. + \e col starting from row identified by index \b \e row. If column is empty + or is not a \b + TXshCellColumn set \b \e cells[] to \b \e rowCount empty cells. \sa getCells(), setCells(), getCell() */ void getCells(int row, int col, int rowCount, TXshCell cells[]) const; /*! If column identified by index \b \e col is a \b TXshCellColumn or is empty -and is not - locked, this method sets to \b \e cells[] the given \b \e rowCount -cells of column \b \e col starting from - row \b \e row. If column in \b \e col is empty it creates a new column -recalling - \b TColumnSetT::touchColumn() and sets the new cells to \b \e cells[], -and on creating a new - column it adds it to fx dag \b FxDag. - If cells in \b \e row and \b \e col are not empty recalls \b -TXshCellColumn::setCells(), - insert the new cells \b \e cells[] in \b \e row \b \e col and shift -old cells. - If xsheet change it updates xsheet's frame count. -Return false if cannot set cells. - \sa getCells(), setCell(), getCell() + and is not + locked, this method sets to \b \e cells[] the given \b \e rowCount cells of + column \b \e col starting from + row \b \e row. If column in \b \e col is empty it creates a new column + recalling + \b TColumnSetT::touchColumn() and sets the new cells to \b \e cells[], and + on creating a new + column it adds it to fx dag \b FxDag. + If cells in \b \e row and \b \e col are not empty recalls \b + TXshCellColumn::setCells(), + insert the new cells \b \e cells[] in \b \e row \b \e col and shift old + cells. + If xsheet change it updates xsheet's frame count. Return false if cannot set + cells. + \sa getCells(), setCell(), getCell() */ bool setCells(int row, int col, int rowCount, const TXshCell cells[]); /*! If column identified by index \b \e col is not empty, is a \b \e - TXshCellColumn and is not - locked this method inserts in row identified by index \b \e row \b \e - rowCount empty cells, it calls - TXshCellColumn::insertEmptyCells(). An update of xsheet's frame count + TXshCellColumn and is not locked this method inserts in row identified by + index \b \e row \b \e + rowCount empty cells, it calls TXshCellColumn::insertEmptyCells(). An + update of xsheet's frame count is performed. \sa setCells(), removeCells() */ void insertCells(int row, int col, int rowCount = 1); /*! If column identified by index \b \e col is not empty, is a \b - TXshCellColumn and is not - locked, this method removes \b \e rowCount cells starting from \b \e - row, it calls - TXshCellColumn::removeCells(). It removes cells without shift - remaining cells. - An update of xsheet's frame count is performed. + TXshCellColumn and is not locked, this method removes \b \e rowCount cells + starting from \b \e + row, it calls TXshCellColumn::removeCells(). It removes cells without shift + remaining cells. An update of xsheet's frame count is performed. \sa clearCells(), insertCells() */ void removeCells(int row, int col, int rowCount = 1); @@ -232,10 +243,10 @@ Return false if cannot set cells. */ void clearAll(); /*! Returns cell range of column identified by index \b \e col and set \b \e - r0 and \b \e r1 - respectively to first and last not empty cell, it then recalls \b - TXshCellColumn::getRange(). - If column is empty or is not a \b TXshCellColumn this method returns + r0 and \b \e r1 respectively to first and last not empty cell, it then + recalls \b + TXshCellColumn::getRange(). If column is empty or is not a \b + TXshCellColumn this method returns zero and sets \b \e r0 to 0 and \b \e r1 to -1. */ @@ -504,8 +515,9 @@ in TXsheetImp. FxDag *getFxDag() const; /*! Returns a pointer to object \b ColumnFan contained in \b TXsheetImp, this object allows the user to manage columns visualization in xsheet. + TXsheet maintains one column fan per each orientation. */ - ColumnFan *getColumnFan() const; + ColumnFan *getColumnFan(const Orientation *o) const; /*! Returns a pointer to \b ToonzScene contained in \b TXsheetImp, that is the scene to which the xsheet refers. diff --git a/toonz/sources/include/toonz/txshsoundlevel.h b/toonz/sources/include/toonz/txshsoundlevel.h index 3d693e3d..c536ae94 100644 --- a/toonz/sources/include/toonz/txshsoundlevel.h +++ b/toonz/sources/include/toonz/txshsoundlevel.h @@ -9,6 +9,7 @@ #include #include "tpersist.h" +#include "orientation.h" #undef DVAPI #undef DVVAR @@ -30,10 +31,11 @@ class DVAPI TXshSoundLevel final : public TXshLevel { int m_frameSoundCount; double m_fps; //! Values is a map of \b Integer and \b DoublePair. - /*!Integer is horizontal value of row pixel. + /*! Two maps, one for vertical layout and one for horizontal. +Integer is pixel number since start of sound. DoublePair is computed according to frameRate, frameCount -and soundtrack pressure.*/ - std::map m_values; +and soundtrack pressure. Means sound min and max.*/ + std::map m_values[Orientations::COUNT]; TFilePath m_path; @@ -60,9 +62,11 @@ public: void save() override; void save(const TFilePath &path); - void computeValues(int frameHeight = 20); + void computeValuesFor(const Orientation *o); + void computeValues(); - void getValueAtPixel(int pixel, DoublePair &values) const; + void getValueAtPixel(const Orientation *o, int pixel, + DoublePair &values) const; /*! Set frame rate to \b fps. \sa getSamplePerFrame() */ void setFrameRate(double fps); diff --git a/toonz/sources/include/toonzqt/spreadsheetviewer.h b/toonz/sources/include/toonzqt/spreadsheetviewer.h index 9e95be49..e49170b6 100644 --- a/toonz/sources/include/toonzqt/spreadsheetviewer.h +++ b/toonz/sources/include/toonzqt/spreadsheetviewer.h @@ -4,6 +4,9 @@ #define SPREADSHEETVIEWER_H #include "tcommon.h" +#include "cellposition.h" +#include "toonz/cellpositionratio.h" +// #include "orientation.h" #include #include @@ -20,6 +23,7 @@ // forward declaration class TFrameHandle; class SpreadsheetViewer; +class Orientation; //------------------------------------------------------------------- @@ -30,23 +34,51 @@ class GenericPanel; //------------------------------------------------------------------- -class DVAPI FrameScroller { - QList m_connectedScrollers; +// Use composition rather than inheritance. +// How this works: +// * scroll area scrollbars sends event to this; +// * it notifies every other FrameScroller with difference; +// * they handle it by adjusting their scrollbars +class DVAPI FrameScroller final : public QObject { + Q_OBJECT + + const Orientation *m_orientation; + QScrollArea *m_scrollArea; + int m_lastX, m_lastY; + bool m_syncing; public: FrameScroller(); virtual ~FrameScroller(); - void connectScroller(FrameScroller *scroller); - void disconnectScroller(FrameScroller *scroller); - bool isScrollerConnected(FrameScroller *scroller); - virtual QScrollArea *getFrameScrollArea() const = 0; + void setFrameScrollArea(QScrollArea *scrollArea); + QScrollArea *getFrameScrollArea() const { return m_scrollArea; } + + void setOrientation(const Orientation *o) { m_orientation = o; } + const Orientation *orientation() const { return m_orientation; } void registerFrameScroller(); void unregisterFrameScroller(); - void prepareToScroll(int dy); - virtual void onPrepareToScroll(int dy) {} + void prepareToScrollOthers(const QPoint &offset); + + void setSyncing(bool s) { m_syncing = s; } + bool isSyncing() { return m_syncing; } + +private: + void connectScrollbars(); + void disconnectScrollbars(); + + void handleScroll(const QPoint &offset) const; + void onScroll(const CellPositionRatio &offset); + + void prepareToScrollRatio(const CellPositionRatio &offset); + +private slots: + void onVScroll(int value); + void onHScroll(int value); +signals: + void prepareToScrollOffset(const QPoint &offset); }; //------------------------------------------------------------------- @@ -190,8 +222,7 @@ protected: //------------------------------------------------------------------- -class DVAPI SpreadsheetViewer : public QFrame, - public Spreadsheet::FrameScroller { +class DVAPI SpreadsheetViewer : public QFrame { Q_OBJECT QColor m_lightLightBgColor; // RowPanel background (124,124,124) @@ -269,6 +300,10 @@ class DVAPI SpreadsheetViewer : public QFrame, int m_markRowDistance, m_markRowOffset; // QRect m_selectedCells; // x=col, y=row bool m_isComputingSize; + // const Orientation *m_orientation; + +protected: + Spreadsheet::FrameScroller m_frameScroller; public: SpreadsheetViewer(QWidget *parent); @@ -286,9 +321,6 @@ public: int getRowCount() const { return m_rowCount; } - // provvisorio - QScrollArea *getFrameScrollArea() const override { return m_cellScrollArea; } - // QProperty void setLightLightBGColor(const QColor &color) { m_lightLightBgColor = color; @@ -354,7 +386,6 @@ public: } void scroll(QPoint delta); - void onPrepareToScroll(int dy) override { refreshContentSize(0, dy); } void setAutoPanSpeed(const QPoint &speed); void setAutoPanSpeed(const QRect &widgetBounds, const QPoint &mousePos); @@ -368,6 +399,13 @@ public: int columnToX(int col) const; int rowToY(int row) const; + CellPosition xyToPosition(const QPoint &point) const; + QPoint positionToXY(const CellPosition &pos) const; + + CellRange xyRectToRange(const QRect &rect) const; + + // const Orientation *orientation () const { return m_orientation; } + bool refreshContentSize(int scrollDx, int scrollDy); int getCurrentRow() const { return m_currentRow; } @@ -413,6 +451,8 @@ public slots: void updateAreas(); void onVSliderChanged(int); void onHSliderChanged(int); + + void onPrepareToScrollOffset(const QPoint &offset); /* void updateAllAree(); void updateCellColumnAree(); diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt index 52c41b2a..067d52e8 100644 --- a/toonz/sources/toonz/CMakeLists.txt +++ b/toonz/sources/toonz/CMakeLists.txt @@ -19,6 +19,7 @@ set(MOC_HEADERS cellkeyframedata.h cellkeyframeselection.h cellselection.h + ../include/cellposition.h cleanuppaletteviewer.h cleanuppopup.h cleanuppreview.h @@ -61,6 +62,7 @@ set(MOC_HEADERS keyframedata.h keyframeselection.h keyframemover.h + layerheaderpanel.h levelcreatepopup.h levelsettingspopup.h linesfadepopup.h @@ -77,6 +79,7 @@ set(MOC_HEADERS meshifypopup.h messagepanel.h moviegenerator.h + ../include/orientation.h onionskinmaskgui.h outputsettingspopup.h overwritepopup.h @@ -90,6 +93,7 @@ set(MOC_HEADERS renumberpopup.h reslist.h ruler.h + ../include/saveloadqsettings.h savepresetpopup.h scanlist.h scanpopup.h @@ -217,6 +221,7 @@ set(SOURCES fxparameditorpopup.cpp histogrampopup.cpp insertfxpopup.cpp + layerheaderpanel.cpp levelcreatepopup.cpp levelsettingspopup.cpp linetestcapturepane.cpp diff --git a/toonz/sources/toonz/cellselection.cpp b/toonz/sources/toonz/cellselection.cpp index 0f3e74aa..f5aa3729 100644 --- a/toonz/sources/toonz/cellselection.cpp +++ b/toonz/sources/toonz/cellselection.cpp @@ -1586,7 +1586,7 @@ void TCellSelection::deleteCells() { getSelectedCells(r0, c0, r1, c1); TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); // if all the selected cells are already empty, then do nothing - if (xsh->isRectEmpty(r0, c0, r1, c1)) return; + if (xsh->isRectEmpty(CellPosition(r0, c0), CellPosition(r1, c1))) return; TCellData *data = new TCellData(); data->setCells(xsh, r0, c0, r1, c1); DeleteCellsUndo *undo = diff --git a/toonz/sources/toonz/columnselection.cpp b/toonz/sources/toonz/columnselection.cpp index 530bd7f1..ad905e05 100644 --- a/toonz/sources/toonz/columnselection.cpp +++ b/toonz/sources/toonz/columnselection.cpp @@ -16,6 +16,7 @@ #include "toonz/txshleveltypes.h" #include "toonz/txshsimplelevel.h" #include "toonz/txshcell.h" +#include "orientation.h" // TnzCore includes #include "tvectorimage.h" @@ -201,10 +202,13 @@ void TColumnSelection::cloneChild() { //----------------------------------------------------------------------------- void TColumnSelection::hideColumns() { - TApp *app = TApp::instance(); - ColumnFan *columnFan = app->getCurrentXsheet()->getXsheet()->getColumnFan(); - std::set::iterator it = m_indices.begin(); - for (; it != m_indices.end(); ++it) columnFan->deactivate(*it); + TApp *app = TApp::instance(); + for (auto o : Orientations::all()) { + ColumnFan *columnFan = + app->getCurrentXsheet()->getXsheet()->getColumnFan(o); + std::set::iterator it = m_indices.begin(); + for (; it != m_indices.end(); ++it) columnFan->deactivate(*it); + } m_indices.clear(); app->getCurrentXsheet()->notifyXsheetChanged(); // DA FARE (non c'e una notica per il solo cambiamento della testa delle diff --git a/toonz/sources/toonz/keyframemover.cpp b/toonz/sources/toonz/keyframemover.cpp index 4eb135c7..114aec44 100644 --- a/toonz/sources/toonz/keyframemover.cpp +++ b/toonz/sources/toonz/keyframemover.cpp @@ -334,7 +334,7 @@ bool KeyframeMoverTool::canMove(const TPoint &pos) { if (pos.x < 0) return false; int col = pos.x; - int startCol = getViewer()->xToColumn(m_startPos.x); + int startCol = getViewer()->xyToPosition(m_startPos).layer(); if (col != startCol) return false; return true; @@ -358,14 +358,15 @@ void KeyframeMoverTool::onCellChange(int row, int col) { //----------------------------------------------------------------------------- void KeyframeMoverTool::onClick(const QMouseEvent *event) { - m_firstKeyframeMovement = true; - m_selecting = false; - TXsheet *xsheet = getViewer()->getXsheet(); - int row = getViewer()->yToRow(event->pos().y()); - int col = getViewer()->xToColumn(event->pos().x()); - m_firstRow = row; - m_firstCol = col; - bool isSelected = getSelection()->isSelected(row, col); + m_firstKeyframeMovement = true; + m_selecting = false; + TXsheet *xsheet = getViewer()->getXsheet(); + CellPosition cellPosition = getViewer()->xyToPosition(event->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); + m_firstRow = row; + m_firstCol = col; + bool isSelected = getSelection()->isSelected(row, col); if (!m_justMovement) { if (event->modifiers() & Qt::ControlModifier) ctrlSelect(row, col); @@ -389,11 +390,12 @@ void KeyframeMoverTool::onClick(const QMouseEvent *event) { //----------------------------------------------------------------------------- void KeyframeMoverTool::onDrag(const QMouseEvent *e) { - int x = e->pos().x(); - int y = e->pos().y(); - m_curPos = TPointD(x, y); - int row = getViewer()->yToRow(y); - int col = getViewer()->xToColumn(x); + int x = e->pos().x(); + int y = e->pos().y(); + m_curPos = TPointD(x, y); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); if (m_selecting) rectSelect(row, col); else { @@ -417,7 +419,7 @@ void KeyframeMoverTool::onDrag(const QMouseEvent *e) { //----------------------------------------------------------------------------- -void KeyframeMoverTool::onRelease(int row, int col) { +void KeyframeMoverTool::onRelease(const CellPosition &pos) { if (m_selecting) { m_selecting = false; getViewer()->updateCells(); diff --git a/toonz/sources/toonz/keyframemover.h b/toonz/sources/toonz/keyframemover.h index 7660eba5..69c88699 100644 --- a/toonz/sources/toonz/keyframemover.h +++ b/toonz/sources/toonz/keyframemover.h @@ -92,7 +92,7 @@ public: void onClick(const QMouseEvent *event) override; void onDrag(const QMouseEvent *event) override; - void onRelease(int row, int col) override; + void onRelease(const CellPosition &pos) override; void drawCellsArea(QPainter &p) override; }; diff --git a/toonz/sources/toonz/layerheaderpanel.cpp b/toonz/sources/toonz/layerheaderpanel.cpp new file mode 100644 index 00000000..f1f6f54f --- /dev/null +++ b/toonz/sources/toonz/layerheaderpanel.cpp @@ -0,0 +1,240 @@ +#include "layerheaderpanel.h" + +#include +#include + +#include "xsheetviewer.h" +#include "xshcolumnviewer.h" + +#include "tapp.h" +#include "toonz/tscenehandle.h" +#include "toonz/txsheethandle.h" +#include "toonz/tobjecthandle.h" + +#include "toonz/preferences.h" + +using XsheetGUI::ColumnArea; + +#if QT_VERSION >= 0x050500 +LayerHeaderPanel::LayerHeaderPanel(XsheetViewer *viewer, QWidget *parent, + Qt::WindowFlags flags) +#else +LayerHeaderPanel::LayerHeaderPanel(XsheetViewer *viewer, QWidget *parent, + Qt::WFlags flags) +#endif + : QWidget(parent, flags), m_viewer(viewer) { + const Orientation *o = Orientations::leftToRight(); + QRect rect = o->rect(PredefinedRect::LAYER_HEADER_PANEL); + + setObjectName("layerHeaderPanel"); + + setFixedSize(rect.size()); + + setMouseTracking(true); +} + +LayerHeaderPanel::~LayerHeaderPanel() {} + +namespace { + +QColor mix(const QColor &a, const QColor &b, double w) { + return QColor(a.red() * w + b.red() * (1 - w), + a.green() * w + b.green() * (1 - w), + a.blue() * w + b.blue() * (1 - w)); +} + +QColor withAlpha(const QColor &color, double alpha) { + QColor result(color); + result.setAlpha(alpha * 255); + return result; +} + +QRect shorter(const QRect original) { return original.adjusted(0, 2, 0, -2); } + +QLine leftSide(const QRect &r) { return QLine(r.topLeft(), r.bottomLeft()); } + +QLine rightSide(const QRect &r) { return QLine(r.topRight(), r.bottomRight()); } +} + +void LayerHeaderPanel::paintEvent(QPaintEvent *event) { + QPainter p(this); + p.setRenderHint(QPainter::SmoothPixmapTransform, true); + + const Orientation *o = Orientations::leftToRight(); + + QColor background = m_viewer->getBGColor(); + QColor slightlyLighter = {mix(background, Qt::white, 0.95)}; + QRect rect = QRect(QPoint(0, 0), size()); + p.fillRect(rect.adjusted(0, 0, -3, 0), slightlyLighter); + + drawIcon(p, PredefinedRect::EYE, XsheetGUI::PreviewVisibleColor, + ColumnArea::Pixmaps::eye()); + drawIcon(p, PredefinedRect::PREVIEW_LAYER, boost::none, + ColumnArea::Pixmaps::cameraStand()); + drawIcon(p, PredefinedRect::LOCK, QColor(255, 255, 255, 128), + ColumnArea::Pixmaps::lock()); + + QRect numberRect = o->rect(PredefinedRect::LAYER_NUMBER); + + int leftadj = 2; + if (Preferences::instance()->isShowColumnNumbersEnabled()) + { + p.drawText(numberRect, Qt::AlignCenter | Qt::TextSingleLine, "#"); + + leftadj += 20; + } + + QRect nameRect = o->rect(PredefinedRect::LAYER_NAME).adjusted(leftadj, 0, -1, 0); + p.drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, + QObject::tr("Layer name")); + + drawLines(p, numberRect, nameRect); +} + +void LayerHeaderPanel::drawIcon(QPainter &p, PredefinedRect rect, + optional fill, + const QPixmap &pixmap) const { + QRect iconRect = Orientations::leftToRight()->rect(rect).adjusted(-2, 0,-2, 0); + + if (rect == PredefinedRect::LOCK) + { + p.setPen(Qt::gray); + p.setBrush(QColor(255, 255, 255, 128)); + p.drawRect(iconRect); + iconRect.adjust(1, 1, -1, -1); + } + else + if (fill) p.fillRect(iconRect, *fill); + p.drawPixmap(iconRect, pixmap); +} + +void LayerHeaderPanel::drawLines(QPainter &p, const QRect &numberRect, + const QRect &nameRect) const { + p.setPen(withAlpha(m_viewer->getTextColor(), 0.5)); + + QLine line = {leftSide(shorter(numberRect)).translated(-2, 0)}; + p.drawLine(line); + + if (Preferences::instance()->isShowColumnNumbersEnabled()) + { + line = rightSide(shorter(numberRect)).translated(-2, 0); + p.drawLine(line); + } + + line = rightSide(shorter(nameRect)); + p.drawLine(line); +} + +void LayerHeaderPanel::showOrHide(const Orientation *o) { + QRect rect = o->rect(PredefinedRect::LAYER_HEADER_PANEL); + if (rect.isEmpty()) + hide(); + else + show(); +} + +//----------------------------------------------------------------------------- + +void LayerHeaderPanel::mousePressEvent(QMouseEvent *event) { + const Orientation *o = Orientations::leftToRight(); + + m_doOnRelease = 0; + + if (event->button() == Qt::LeftButton) { + // get mouse position + QPoint pos = event->pos(); + + // preview button + if (o->rect(PredefinedRect::EYE_AREA).contains(pos)) { + m_doOnRelease = ToggleAllPreviewVisible; + } + // camstand button + else if (o->rect(PredefinedRect::PREVIEW_LAYER_AREA).contains(pos)) { + m_doOnRelease = ToggleAllTransparency; + } + // lock button + else if (o->rect(PredefinedRect::LOCK_AREA).contains(pos)) { + m_doOnRelease = ToggleAllLock; + } + } + + update(); +} + +void LayerHeaderPanel::mouseMoveEvent(QMouseEvent *event) { + const Orientation *o = Orientations::leftToRight(); + + QPoint pos = event->pos(); + + // preview button + if (o->rect(PredefinedRect::EYE_AREA).contains(pos)) { + m_tooltip = tr("Preview Visbility Toggle All"); + } + // camstand button + else if (o->rect(PredefinedRect::PREVIEW_LAYER_AREA).contains(pos)) { + m_tooltip = tr("Camera Stand Visibility Toggle All"); + } + // lock button + else if (o->rect(PredefinedRect::LOCK).contains(pos)) { + m_tooltip = tr("Lock Toggle All"); + } + else { + m_tooltip = tr(""); + } + + m_pos = pos; + + update(); +} + +//----------------------------------------------------------------------------- + +bool LayerHeaderPanel::event(QEvent *event) { + if (event->type() == QEvent::ToolTip) { + if (!m_tooltip.isEmpty()) + QToolTip::showText(mapToGlobal(m_pos), m_tooltip); + else + QToolTip::hideText(); + } + return QWidget::event(event); +} + +//----------------------------------------------------------------------------- + +void LayerHeaderPanel::mouseReleaseEvent(QMouseEvent *event) { + TApp *app = TApp::instance(); + TXsheet *xsh = m_viewer->getXsheet(); + int col, totcols = xsh->getColumnCount(); + bool sound_changed = false; + + if (m_doOnRelease != 0 && totcols > 0) + { + for (col = 0; col < totcols; col++) + { + if (!xsh->isColumnEmpty(col)) { + TXshColumn *column = xsh->getColumn(col); + + if (m_doOnRelease == ToggleAllPreviewVisible) { + column->setPreviewVisible(!column->isPreviewVisible()); + } + else if (m_doOnRelease == ToggleAllTransparency) { + column->setCamstandVisible(!column->isCamstandVisible()); + if (column->getSoundColumn()) sound_changed = true; + } + else if (m_doOnRelease == ToggleAllLock) { + column->lock(!column->isLocked()); + } + } + } + + if (sound_changed) { + app->getCurrentXsheet()->notifyXsheetSoundChanged(); + } + + app->getCurrentScene()->notifySceneChanged(); + app->getCurrentXsheet()->notifyXsheetChanged(); + } + m_viewer->updateColumnArea(); + update(); + m_doOnRelease = 0; +} diff --git a/toonz/sources/toonz/layerheaderpanel.h b/toonz/sources/toonz/layerheaderpanel.h new file mode 100644 index 00000000..7a716105 --- /dev/null +++ b/toonz/sources/toonz/layerheaderpanel.h @@ -0,0 +1,55 @@ +#pragma once + +#ifndef LAYER_HEADER_PANEL_INCLUDED +#define LAYER_HEADER_PANEL_INCLUDED + +#include +#include + +#include "orientation.h" + +using boost::optional; + +class XsheetViewer; + +// Panel showing column headers for layers in timeline mode +class LayerHeaderPanel final : public QWidget { + Q_OBJECT + + enum { ToggleAllTransparency = 1, ToggleAllPreviewVisible, ToggleAllLock }; + + int m_doOnRelease; + QString m_tooltip; + QPoint m_pos; + +private: + XsheetViewer *m_viewer; + +public: +#if QT_VERSION >= 0x050500 + LayerHeaderPanel(XsheetViewer *viewer, QWidget *parent = 0, + Qt::WindowFlags flags = 0); +#else + LayerHeaderPanel(XsheetViewer *viewer, QWidget *parent = 0, + Qt::WFlags flags = 0); +#endif + ~LayerHeaderPanel(); + + void showOrHide(const Orientation *o); + +protected: + void paintEvent(QPaintEvent *event) override; + + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + bool event(QEvent *event) override; + +private: + void drawIcon(QPainter &p, PredefinedRect rect, optional fill, + const QPixmap &pixmap) const; + void drawLines(QPainter &p, const QRect &numberRect, + const QRect &nameRect) const; +}; + +#endif diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index 0d2c90e7..37b086c5 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -89,7 +89,8 @@ TEnv::IntVar NoShiftToggleAction("NoShiftToggleAction", 0); namespace { //============================================================================= -const std::string layoutsFileName = "layouts.txt"; +// layout file name may be overwritten by the argument +std::string layoutsFileName = "layouts.txt"; const std::string currentRoomFileName = "currentRoom.txt"; bool scrambledRooms = false; @@ -108,8 +109,10 @@ bool readRoomList(std::vector &roomPaths, " not found!"); fp = ToonzFolder::getRoomsFile(layoutsFileName); if (!TFileStatus(fp).doesExist()) return false; - } else + } else { argumentLayoutFileLoaded = true; + layoutsFileName = argumentLayoutFileName.toStdString(); + } } else { fp = ToonzFolder::getRoomsFile(layoutsFileName); if (!TFileStatus(fp).doesExist()) return false; @@ -255,6 +258,9 @@ void Room::save() { TPanel *pane = static_cast(layout->itemAt(i)->widget()); settings.setValue("name", pane->objectName()); settings.setValue("geometry", geometries[i]); // Use passed geometry + if (SaveLoadQSettings *persistent = + dynamic_cast(pane->widget())) + persistent->save(settings); if (pane->getViewType() != -1) // If panel has different viewtypes, store current one settings.setValue("viewtype", pane->getViewType()); @@ -303,6 +309,9 @@ void Room::load(const TFilePath &fp) { // Allocate panel paneObjectName = name.toString(); pane = TPanelFactory::createPanel(this, paneObjectName); + if (SaveLoadQSettings *persistent = + dynamic_cast(pane->widget())) + persistent->load(settings); } if (!pane) { diff --git a/toonz/sources/toonz/preferencespopup.cpp b/toonz/sources/toonz/preferencespopup.cpp index a12379fd..39ec78f4 100644 --- a/toonz/sources/toonz/preferencespopup.cpp +++ b/toonz/sources/toonz/preferencespopup.cpp @@ -671,7 +671,7 @@ void PreferencesPopup::setChessboardColor2(const TPixel32 &color, //----------------------------------------------------------------------------- void PreferencesPopup::onColumnIconChange(const QString &value) { - m_pref->setColumnIconLoadingPolicy(value == QString("At Once") + m_pref->setColumnIconLoadingPolicy(value == tr("At Once") ? Preferences::LoadAtOnce : Preferences::LoadOnDemand); } @@ -1002,6 +1002,10 @@ void PreferencesPopup::onExpandFunctionHeaderClicked(bool checked) { m_pref->enableExpandFunctionHeader(checked); } +void PreferencesPopup::onShowColumnNumbersChanged(int index) { + m_pref->enableShowColumnNumbers(index == Qt::Checked); +} + //----------------------------------------------------------------------------- void PreferencesPopup::onUseArrowKeyToShiftCellSelectionClicked(int on) { @@ -1224,6 +1228,7 @@ PreferencesPopup::PreferencesPopup() tr("Expand Function Editor Header to Match XSheet Toolbar Height " "(Requires Restart)"), this); + CheckBox *showColumnNumbersCB = new CheckBox(tr("Show Column Numbers in Column Headers"), this); //--- Animation ------------------------------ categoryList->addItem(tr("Animation")); @@ -1492,6 +1497,7 @@ PreferencesPopup::PreferencesPopup() m_pref->isInputCellsWithoutDoubleClickingEnabled()); m_showXSheetToolbar->setChecked(m_pref->isShowXSheetToolbarEnabled()); m_expandFunctionHeader->setChecked(m_pref->isExpandFunctionHeaderEnabled()); + showColumnNumbersCB->setChecked(m_pref->isShowColumnNumbersEnabled()); //--- Animation ------------------------------ QStringList list; @@ -1929,7 +1935,7 @@ PreferencesPopup::PreferencesPopup() QGridLayout *xsheetFrameLay = new QGridLayout(); xsheetFrameLay->setMargin(15); xsheetFrameLay->setHorizontalSpacing(15); - xsheetFrameLay->setVerticalSpacing(10); + xsheetFrameLay->setVerticalSpacing(11); { xsheetFrameLay->addWidget(new QLabel(tr("Next/Previous Step Frames:")), 0, 0, Qt::AlignRight | Qt::AlignVCenter); @@ -1955,7 +1961,8 @@ PreferencesPopup::PreferencesPopup() m_showXSheetToolbar->setLayout(xSheetToolbarLay); xsheetFrameLay->addWidget(m_showXSheetToolbar, 7, 0, 3, 3); - } + xsheetFrameLay->addWidget(showColumnNumbersCB, 10, 0, 1, 2); + } xsheetFrameLay->setColumnStretch(0, 0); xsheetFrameLay->setColumnStretch(1, 0); xsheetFrameLay->setColumnStretch(2, 1); @@ -2305,6 +2312,9 @@ PreferencesPopup::PreferencesPopup() ret = ret && connect(m_expandFunctionHeader, SIGNAL(clicked(bool)), SLOT(onExpandFunctionHeaderClicked(bool))); + ret = ret && connect(showColumnNumbersCB, SIGNAL(stateChanged(int)), + this, SLOT(onShowColumnNumbersChanged(int))); + //--- Animation ---------------------- ret = ret && connect(m_keyframeType, SIGNAL(currentIndexChanged(int)), SLOT(onKeyframeTypeChanged(int))); diff --git a/toonz/sources/toonz/preferencespopup.h b/toonz/sources/toonz/preferencespopup.h index fe99d17a..b2e06ff5 100644 --- a/toonz/sources/toonz/preferencespopup.h +++ b/toonz/sources/toonz/preferencespopup.h @@ -174,6 +174,7 @@ private slots: void onUseNumpadForSwitchingStylesClicked(bool); void onShowXSheetToolbarClicked(bool); void onExpandFunctionHeaderClicked(bool); + void onShowColumnNumbersChanged(int); void onUseArrowKeyToShiftCellSelectionClicked(int); void onInputCellsWithoutDoubleClickingClicked(int); void onWatchFileSystemClicked(int); diff --git a/toonz/sources/toonz/sceneviewerevents.cpp b/toonz/sources/toonz/sceneviewerevents.cpp index 0498cd72..2a3abae9 100644 --- a/toonz/sources/toonz/sceneviewerevents.cpp +++ b/toonz/sources/toonz/sceneviewerevents.cpp @@ -935,9 +935,9 @@ void SceneViewer::keyPressEvent(QKeyEvent *event) { if (!ret) { TFrameHandle *fh = TApp::instance()->getCurrentFrame(); - if (key == TwConsts::TK_UpArrow) + if (key == TwConsts::TK_UpArrow || key == TwConsts::TK_LeftArrow) fh->prevFrame(); - else if (key == TwConsts::TK_DownArrow) + else if (key == TwConsts::TK_DownArrow || key == TwConsts::TK_RightArrow) fh->nextFrame(); else if (key == TwConsts::TK_Home) fh->firstFrame(); diff --git a/toonz/sources/toonz/xshcellmover.cpp b/toonz/sources/toonz/xshcellmover.cpp index 89052c65..60359e76 100644 --- a/toonz/sources/toonz/xshcellmover.cpp +++ b/toonz/sources/toonz/xshcellmover.cpp @@ -371,9 +371,10 @@ qDebug() << "check: " << srcIndex << ":" << } void LevelMoverTool::onClick(const QMouseEvent *e) { - QPoint pos = e->pos(); - int row = getViewer()->yToRow(pos.y()); - int col = getViewer()->xToColumn(pos.x()); + QPoint pos = e->pos(); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); m_qualifiers = 0; if (Preferences::instance()->getDragCellsBehaviour() == 1) @@ -509,13 +510,13 @@ void LevelMoverTool::onCellChange(int row, int col) { } void LevelMoverTool::onDrag(const QMouseEvent *e) { - QPoint pos = e->pos(); - onCellChange(getViewer()->yToRow(pos.y()), getViewer()->xToColumn(pos.x())); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + onCellChange(cellPosition.frame(), cellPosition.layer()); refreshCellsArea(); refreshColumnsArea(); } -void LevelMoverTool::onRelease(int row, int col) { +void LevelMoverTool::onRelease(const CellPosition &pos) { m_validPos = false; m_range.lx = 0; m_range.ly = 0; @@ -534,15 +535,13 @@ void LevelMoverTool::drawCellsArea(QPainter &p) { if (rect.x1 < 0 || rect.y1 < 0) return; if (rect.x0 < 0) rect.x0 = 0; if (rect.y0 < 0) rect.y0 = 0; - int x0, y0, x1, y1; - x0 = getViewer()->columnToX(rect.x0); - x1 = getViewer()->columnToX(rect.x1 + 1); - y0 = getViewer()->rowToY(rect.y0); - y1 = getViewer()->rowToY(rect.y1 + 1); - int x = x1 - x0; - int y = y1 - y0; + + QRect screen = getViewer()->rangeToXYRect(CellRange( + CellPosition(rect.y0, rect.x0), CellPosition(rect.y1 + 1, rect.x1 + 1))); p.setPen((m_aimedPos == m_lastPos && m_validPos) ? QColor(190, 220, 255) : QColor(255, 0, 0)); int i; - for (i = 0; i < 3; i++) p.drawRect(x0 + i, y0 + i, x - 2 * i, y - 2 * i); + for (i = 0; i < 3; i++) // thick border inside cell + p.drawRect(QRect(screen.topLeft() + QPoint(i, i), + screen.size() - QSize(2 * i, 2 * i))); } diff --git a/toonz/sources/toonz/xshcellmover.h b/toonz/sources/toonz/xshcellmover.h index ec63ceeb..ef54568f 100644 --- a/toonz/sources/toonz/xshcellmover.h +++ b/toonz/sources/toonz/xshcellmover.h @@ -89,7 +89,7 @@ private: class LevelMoverTool : public XsheetGUI::DragTool { protected: TPoint m_grabOffset; - TPoint m_startPos, m_lastPos, m_aimedPos; + TPoint m_startPos, m_lastPos, m_aimedPos; // x=col, y=row coordinates TDimension m_range; int m_qualifiers; @@ -109,7 +109,7 @@ public: void onClick(const QMouseEvent *e) override; void onCellChange(int row, int col); void onDrag(const QMouseEvent *e) override; - void onRelease(int row, int col) override; + void onRelease(const CellPosition &pos) override; void drawCellsArea(QPainter &p) override; }; diff --git a/toonz/sources/toonz/xshcellviewer.cpp b/toonz/sources/toonz/xshcellviewer.cpp index eaaf0890..3ce32de1 100644 --- a/toonz/sources/toonz/xshcellviewer.cpp +++ b/toonz/sources/toonz/xshcellviewer.cpp @@ -16,6 +16,7 @@ #include "cellkeyframeselection.h" #include "keyframedata.h" #include "columnselection.h" +#include "orientation.h" // TnzTools includes #include "tools/cursormanager.h" @@ -506,6 +507,8 @@ RenameCellField::RenameCellField(QWidget *parent, XsheetViewer *viewer) //----------------------------------------------------------------------------- void RenameCellField::showInRowCol(int row, int col, bool multiColumnSelected) { + const Orientation *o = m_viewer->orientation(); + m_viewer->scrollTo(row, col); m_row = row; @@ -544,9 +547,10 @@ void RenameCellField::showInRowCol(int row, int col, bool multiColumnSelected) { setStyleSheet(styleSheetStr); TXshCell cell = xsh->getCell(row, col); + QPoint xy = m_viewer->positionToXY(CellPosition(row, col)) - QPoint(1, 2); if (!cell.isEmpty()) { - setFixedSize(XsheetGUI::ColumnWidth - 5, XsheetGUI::RowHeight + 4); - move(QPoint(m_viewer->columnToX(col) + 7, m_viewer->rowToY(row) - 2)); + setFixedSize(o->cellWidth(), o->cellHeight() + 2); + move(xy + QPoint(1, 1)); TFrameId fid = cell.getFrameId(); std::wstring levelName = cell.m_level->getName(); @@ -576,8 +580,8 @@ void RenameCellField::showInRowCol(int row, int col, bool multiColumnSelected) { } // clear the field if the empty cell is clicked else { - setFixedSize(XsheetGUI::ColumnWidth + 3, XsheetGUI::RowHeight + 4); - move(QPoint(m_viewer->columnToX(col) - 1, m_viewer->rowToY(row) - 2)); + setFixedSize(o->cellWidth(), o->cellHeight() + 2); + move(xy + QPoint(1, 1)); setText(""); } @@ -756,43 +760,41 @@ void RenameCellField::keyPressEvent(QKeyEvent *event) { } int r0, c0, r1, c1; + CellPosition stride(1, 1); // stride in row and column axes cellSelection->getSelectedCells(r0, c0, r1, c1); - int rowStride = r1 - r0 + 1; + stride.setFrame(cellSelection->getSelectedCells().getRowCount()); - QPoint offset(0, 0); + CellPosition offset; switch (int key = event->key()) { case Qt::Key_Up: - offset.setY(-1); - break; case Qt::Key_Down: - offset.setY(1); - break; case Qt::Key_Left: - offset.setX(-1); - break; case Qt::Key_Right: - offset.setX(1); - break; + offset = m_viewer->orientation()->arrowShift(key); + break; default: - QLineEdit::keyPressEvent(event); - return; - break; + QLineEdit::keyPressEvent(event); + return; + break; } + if (isCtrlPressed && Preferences::instance()->isUseArrowKeyToShiftCellSelectionEnabled()) { - if (r0 == r1 && offset.y() == -1) return; - if (c0 == c1 && offset.x() == -1) return; - cellSelection->selectCells(r0, c0, r1 + offset.y(), c1 + offset.x()); + if (r0 == r1 && offset.frame() == -1) return; + if (c0 == c1 && offset.layer() == -1) return; + cellSelection->selectCells(r0, c0, r1 + offset.frame(), c1 + offset.layer()); } else { - offset.setY(offset.y() * rowStride); - if (r0 + offset.y() < 0) offset.setY(-r0); - if (c0 + offset.x() < 0) return; + CellPosition offset(offset * stride); + int movedR0 = std::max(0, r0 + offset.frame()); + int movedC0 = std::max(0, c0 + offset.layer()); + int diffFrame = movedR0 - r0; + int diffLayer = movedC0 - c0; // It needs to be discussed - I made not to rename cell with arrow key. // 19/Jan/2017 shun-iwasawa // renameCell(); - cellSelection->selectCells(r0 + offset.y(), c0 + offset.x(), - r1 + offset.y(), c1 + offset.x()); - showInRowCol(m_row + offset.y(), m_col + offset.x(), c1 - c0 > 0); + cellSelection->selectCells(r0 + diffFrame, c0 + diffLayer, r1 + diffFrame, + c1 + diffLayer); + showInRowCol(m_row + offset.frame(), m_col + offset.layer(), c1 - c0 > 0); } m_viewer->updateCells(); TApp::instance()->getCurrentSelection()->notifySelectionChanged(); @@ -865,47 +867,27 @@ void CellArea::setDragTool(DragTool *dragTool) { void CellArea::drawCells(QPainter &p, const QRect toBeUpdated) { TXsheet *xsh = m_viewer->getXsheet(); - ColumnFan *columnFan = xsh->getColumnFan(); + ColumnFan *columnFan = xsh->getColumnFan(m_viewer->orientation()); + // selected cells range TCellSelection *cellSelection = m_viewer->getCellSelection(); int rS0, cS0, rS1, cS1; if (!cellSelection->isEmpty()) cellSelection->getSelectedCells(rS0, cS0, rS1, cS1); + // visible cells range + CellRange visible = m_viewer->xyRectToRange(toBeUpdated); int r0, r1, c0, c1; // range of visible rows and columns - r0 = m_viewer->yToRow(toBeUpdated.top()); - r1 = m_viewer->yToRow(toBeUpdated.bottom()); - c0 = m_viewer->xToColumn(toBeUpdated.left()); - c1 = m_viewer->xToColumn(toBeUpdated.right()); + r0 = visible.from().frame(); + r1 = visible.to().frame(); + c0 = visible.from().layer(); + c1 = visible.to().layer(); - // Sfondo bianco se row < xsh->getFrameCount() - int xshRowCount = xsh->getFrameCount(); + drawNonEmptyBackground(p); - if (xshRowCount > 0) { - int filledCol; - for (filledCol = xsh->getColumnCount() - 1; filledCol >= 0; filledCol--) { - TXshColumn *currentColumn = xsh->getColumn(filledCol); - if (!currentColumn) continue; - if (currentColumn->isEmpty() == false) { - p.fillRect(1, 0, m_viewer->columnToX(filledCol + 1), - m_viewer->rowToY(xshRowCount), - QBrush(m_viewer->getNotEmptyColumnColor())); - break; - } - } - } - int xS0, xS1, yS0, yS1; + drawSelectionBackground(p); - if (!cellSelection->isEmpty()) { - xS0 = m_viewer->columnToX(cS0); - xS1 = m_viewer->columnToX(cS1 + 1) - 1; - yS0 = m_viewer->rowToY(rS0); - yS1 = m_viewer->rowToY(rS1 + 1) - 1; - QRect rect = QRect(xS0, yS0, xS1 - xS0 + 1, yS1 - yS0 + 1); - p.fillRect(rect, QBrush(m_viewer->getSelectedEmptyCellColor())); - } - - // marker interval + // marker interval every 6 frames int distance, offset; TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers( distance, offset); @@ -914,23 +896,22 @@ void CellArea::drawCells(QPainter &p, const QRect toBeUpdated) { int currentRow = m_viewer->getCurrentRow(); int col, row; - int x0 = std::max(1, toBeUpdated.left()); - int x1 = std::min(width(), toBeUpdated.right()); + int drawLeft = std::max(1, toBeUpdated.left()); + int drawRight = std::min(width(), toBeUpdated.right()); - int y0 = std::max(1, toBeUpdated.top()); - int y1 = std::min(height() - 2, toBeUpdated.bottom()); + int drawTop = std::max(1, toBeUpdated.top()); + int drawBottom = std::min(height() - 2, toBeUpdated.bottom()); + QRect drawingArea(QPoint(drawLeft, drawTop), QPoint(drawRight, drawBottom)); + NumberRange frameSide = m_viewer->orientation()->frameSide(drawingArea); + + // for each layer m_soundLevelModifyRects.clear(); for (col = c0; col <= c1; col++) { - int x = m_viewer->columnToX(col); - // Disegno la colonna - if (!columnFan->isActive(col)) { - p.fillRect(x + 1, y0, 2, y1 - y0 + 1, QBrush(Qt::white)); - p.fillRect(x + 4, y0, 2, y1 - y0 + 1, QBrush(Qt::white)); - p.fillRect(x + 7, y0, 2, y1 - y0 + 1, QBrush(Qt::white)); - p.setPen(m_viewer->getLightLineColor()); - p.drawLine(x, y0, x, y1); - p.drawLine(x + 3, y0, x + 3, y1); - p.drawLine(x + 6, y0, x + 6, y1); + // x in vertical timeline / y in horizontal + int layerAxis = m_viewer->columnToLayerAxis(col); + + if (!columnFan->isActive(col)) { // folded column + drawFoldedColumns(p, layerAxis, frameSide); continue; } @@ -952,24 +933,33 @@ void CellArea::drawCells(QPainter &p, const QRect toBeUpdated) { isReference = false; } - x0 = x + 1; - x1 = m_viewer->columnToX(col + 1); + NumberRange layerAxisRange(layerAxis + 1, + m_viewer->columnToLayerAxis(col + 1)); - // draw vertical lines - p.setPen(m_viewer->getVerticalLineColor()); - if (x > 0) p.drawLine(x, y0, x, y1); + // draw vertical line + if (layerAxis > 0) { + p.setPen(m_viewer->getVerticalLineColor()); + QLine verticalLine = + m_viewer->orientation()->verticalLine(layerAxis, frameSide); + p.drawLine(verticalLine); + } + // for each frame for (row = r0; row <= r1; row++) { // draw horizontal lines - QColor color = ((row - offset) % distance != 0) - ? m_viewer->getLightLineColor() - : m_viewer->getMarkerLineColor(); + // hide top-most marker line + QColor color = ((row - offset) % distance == 0 && row != 0) + ? m_viewer->getMarkerLineColor() + : m_viewer->getLightLineColor(); p.setPen(color); - int y = m_viewer->rowToY(row); - p.drawLine(x0, y, x1, y); + int frameAxis = m_viewer->rowToFrameAxis(row); + QLine horizontalLine = + m_viewer->orientation()->horizontalLine(frameAxis, layerAxisRange); + p.drawLine(horizontalLine); + if (!isColumn) continue; - // Disegno le celle a seconda del tipo di colonna + // Cells appearance depending on the type of column if (isSoundColumn) drawSoundCell(p, row, col); else if (isPaletteColumn) @@ -979,55 +969,145 @@ void CellArea::drawCells(QPainter &p, const QRect toBeUpdated) { else drawLevelCell(p, row, col, isReference); } - - // hide top-most interval line - p.setPen(m_viewer->getLightLineColor()); - p.drawLine(x - 1, 0, x1 - 1, 0); } - // smart tab - // initialize the rectangles first + drawExtenderHandles(p); +} + +// slightly bright background for non-empty rectangular area +void CellArea::drawNonEmptyBackground(QPainter &p) const { + TXsheet *xsh = m_viewer->getXsheet(); + + int totalFrames = xsh->getFrameCount(); + if (!totalFrames) return; + + int lastNonEmptyCol; + for (lastNonEmptyCol = xsh->getColumnCount() - 1; lastNonEmptyCol >= 0; + lastNonEmptyCol--) { + TXshColumn *currentColumn = xsh->getColumn(lastNonEmptyCol); + if (!currentColumn) continue; + if (!currentColumn->isEmpty()) break; + } + QPoint xy = + m_viewer->positionToXY(CellPosition(totalFrames, lastNonEmptyCol + 1)); + p.fillRect(1, 0, xy.x(), xy.y(), QBrush(m_viewer->getNotEmptyColumnColor())); +} + +void CellArea::drawFoldedColumns(QPainter &p, int layerAxis, + const NumberRange &frameAxis) const { + // 3 white bars + for (int i = 0; i < 3; i++) { + QRect whiteRect = + m_viewer->orientation()->foldedRectangle(layerAxis, frameAxis, i); + p.fillRect(whiteRect, QBrush(Qt::white)); + } + + // 3 dark lines + p.setPen(m_viewer->getLightLineColor()); + for (int i = 0; i < 3; i++) { + QLine darkLine = + m_viewer->orientation()->foldedRectangleLine(layerAxis, frameAxis, i); + p.drawLine(darkLine); + } +} + +void CellArea::drawSelectionBackground(QPainter &p) const { + // selected cells range + TCellSelection *cellSelection = m_viewer->getCellSelection(); + if (cellSelection->isEmpty()) return; + + int selRow0, selCol0, selRow1, selCol1; + cellSelection->getSelectedCells(selRow0, selCol0, selRow1, selCol1); + QRect selectionRect = m_viewer->rangeToXYRect(CellRange( + CellPosition(selRow0, selCol0), CellPosition(selRow1 + 1, selCol1 + 1))); + p.fillRect(selectionRect, QBrush(m_viewer->getSelectedEmptyCellColor())); +} + +void CellArea::drawExtenderHandles(QPainter &p) { + const Orientation *o = m_viewer->orientation(); + m_levelExtenderRect = QRect(); m_upperLevelExtenderRect = QRect(); - int smartTabPosOffset = - (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) ? 31 - : 20; - if (!cellSelection->isEmpty() && !m_viewer->areSoundCellsSelected()) { - m_levelExtenderRect = QRect(xS1 - smartTabPosOffset, yS1 + 1, 19, 8); + + // selected cells range + TCellSelection *cellSelection = m_viewer->getCellSelection(); + if (cellSelection->isEmpty() || m_viewer->areSoundCellsSelected()) return; + + int selRow0, selCol0, selRow1, selCol1; + cellSelection->getSelectedCells(selRow0, selCol0, selRow1, selCol1); + QRect selected = m_viewer->rangeToXYRect(CellRange( + CellPosition(selRow0, selCol0), CellPosition(selRow1 + 1, selCol1 + 1))); + + int x0, y0, x1, y1; + x0 = selected.left(); + x1 = selected.right(); + y0 = selected.top(); + y1 = selected.bottom(); + + // smart tab + QPoint smartTabPosOffset = + Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled() + ? QPoint(0, 0) + : o->point(PredefinedPoint::KEY_HIDDEN); + + int distance, offset; + TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers( + distance, offset); + if (distance == 0) distance = 6; + + QPoint xyRadius = o->point(PredefinedPoint::EXTENDER_XY_RADIUS); + + // bottom / right extender handle + m_levelExtenderRect = + o->rect(PredefinedRect::END_EXTENDER) + .translated(selected.bottomRight() + smartTabPosOffset); + p.setPen(Qt::black); + p.setBrush(SmartTabColor); + p.drawRoundRect(m_levelExtenderRect, xyRadius.x(), xyRadius.y()); + QColor color = ((selRow1 + 1 - offset) % distance != 0) + ? m_viewer->getLightLineColor() + : m_viewer->getMarkerLineColor(); + p.setPen(color); + QLine extenderLine = o->line(PredefinedLine::EXTENDER_LINE); + extenderLine.setP1(extenderLine.p1() + smartTabPosOffset); + p.drawLine(extenderLine.translated(selected.bottomRight())); + + // up / left extender handle + if (isCtrlPressed && selRow0 > 0 && !m_viewer->areCellsSelectedEmpty()) { + QPoint properPoint = o->topRightCorner(selected); + m_upperLevelExtenderRect = o->rect(PredefinedRect::BEGIN_EXTENDER) + .translated(properPoint + smartTabPosOffset); p.setPen(Qt::black); p.setBrush(SmartTabColor); - p.drawRoundRect(m_levelExtenderRect, 30, 75); - QColor color = ((rS1 + 1 - offset) % distance != 0) + p.drawRoundRect(m_upperLevelExtenderRect, xyRadius.x(), xyRadius.y()); + QColor color = ((selRow0 - offset) % distance != 0) ? m_viewer->getLightLineColor() : m_viewer->getMarkerLineColor(); p.setPen(color); - p.drawLine(xS1 - smartTabPosOffset, yS1 + 1, xS1 - 1, yS1 + 1); - - // upper-directional smart tab - if (isCtrlPressed && rS0 > 0 && !m_viewer->areCellsSelectedEmpty()) { - m_upperLevelExtenderRect = QRect(xS1 - smartTabPosOffset, yS0 - 8, 19, 8); - p.setPen(Qt::black); - p.setBrush(SmartTabColor); - p.drawRoundRect(m_upperLevelExtenderRect, 30, 75); - QColor color = ((rS0 - offset) % distance != 0) - ? m_viewer->getLightLineColor() - : m_viewer->getMarkerLineColor(); - p.setPen(color); - p.drawLine(xS1 - smartTabPosOffset, yS0, xS1 - 1, yS0); - } - - p.setBrush(Qt::NoBrush); + p.drawLine(extenderLine.translated(properPoint)); } + + p.setBrush(Qt::NoBrush); } //----------------------------------------------------------------------------- void CellArea::drawSoundCell(QPainter &p, int row, int col) { + const Orientation *o = m_viewer->orientation(); TXshSoundColumn *soundColumn = m_viewer->getXsheet()->getColumn(col)->getSoundColumn(); - int x = m_viewer->columnToX(col); - int y = m_viewer->rowToY(row); - QRect rect = QRect(x + 1, y, ColumnWidth - 1, RowHeight); + QPoint xy = m_viewer->positionToXY(CellPosition(row, col)); + int x = xy.x(); + int y = xy.y(); + if (row == 0) + { + if (o->isVerticalTimeline()) + xy.setY(xy.y() + 1); + else + xy.setX(xy.x() + 1); + } + QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y)); + QRect rect = cellRect.adjusted(1, 1, 0, 0); int maxNumFrame = soundColumn->getMaxFrame() + 1; int startFrame = soundColumn->getFirstRow(); TXshCell cell = soundColumn->getCell(row); @@ -1053,88 +1133,81 @@ void CellArea::drawSoundCell(QPainter &p, int row, int col) { m_viewer->getCellTypeAndColors(levelType, cellColor, sideColor, cell, isSelected); - // sfondo celle - QRect backgroundRect = QRect(x + 1, y + 1, ColumnWidth - 1, RowHeight - 1); - p.fillRect(backgroundRect, cellColor); - p.fillRect(QRect(x, y, 7, RowHeight), QBrush(sideColor)); + // cells background + p.fillRect(rect, QBrush(cellColor)); - // draw dot line if the column is locked - if (soundColumn->isLocked()) { - p.setPen(QPen(cellColor, 2, Qt::DotLine)); - p.drawLine(x + 3, y, x + 3, y + RowHeight); - } - // draw "end of the level" - if (isLastRow) { - QPainterPath path(QPointF(x, y + RowHeight)); - path.lineTo(QPointF(x + 7, y + RowHeight)); - path.lineTo(QPointF(x + 7, y + RowHeight - 7)); - path.lineTo(QPointF(x, y + RowHeight)); - p.fillPath(path, QBrush(cellColor)); - } + drawDragHandle(p, xy, sideColor); + drawEndOfDragHandle(p, isLastRow, xy, cellColor); + drawLockedDottedLine(p, soundColumn->isLocked(), xy, cellColor); - int x1 = rect.x() + 6; - int x2 = rect.x() + rect.width(); - int x1Bis = x2 - 7; + QRect trackRect = o->rect(PredefinedRect::SOUND_TRACK).translated(xy); + QRect previewRect = o->rect(PredefinedRect::PREVIEW_TRACK).translated(xy); + NumberRange trackBounds = o->layerSide(trackRect); + NumberRange previewBounds = o->layerSide(previewRect); + NumberRange trackAndPreview(trackBounds.from(), previewBounds.to()); - int offset = row - cell.getFrameId().getNumber(); - int y0 = rect.y(); - int y1 = rect.bottomLeft().y(); - int soundY0 = y0 - m_viewer->rowToY(offset); + NumberRange timeBounds = o->frameSide(trackRect); + int offset = + row - cell.getFrameId().getNumber(); // rows since start of the clip + int begin = timeBounds.from(); // time axis + int end = timeBounds.to(); + int soundPixel = + begin - m_viewer->rowToFrameAxis(offset); // pixels since start of clip - int trackWidth = (x1Bis - x1) / 2; + int trackWidth = trackBounds.length(); int lastMin, lastMax; DoublePair minmax; - soundLevel->getValueAtPixel(soundY0, minmax); + soundLevel->getValueAtPixel(o, soundPixel, minmax); - double pmin = minmax.first + trackWidth / 2.; - double pmax = minmax.second + trackWidth / 2.; + double pmin = minmax.first; + double pmax = minmax.second; - int delta = x1 + trackWidth / 2; - lastMin = tcrop((int)pmin, 0, trackWidth / 2) + delta; - lastMax = tcrop((int)pmax, trackWidth / 2, trackWidth - 1) + delta; + int center = trackBounds.middle(); + lastMin = tcrop((int)pmin, -trackWidth / 2, 0) + center; + lastMax = tcrop((int)pmax, 0, trackWidth / 2 - 1) + center; bool scrub = m_viewer->isScrubHighlighted(row, col); int i; - for (i = y0; i <= y1; i++) { - DoublePair minmax; - soundLevel->getValueAtPixel(soundY0, minmax); - soundY0++; + for (i = begin; i <= end; i++) { + soundLevel->getValueAtPixel(o, soundPixel, minmax); + soundPixel++; int min, max; - double pmin = minmax.first + trackWidth / 2.; - double pmax = minmax.second + trackWidth / 2.; + pmin = minmax.first; + pmax = minmax.second; - int delta = x1 + trackWidth / 2; - min = tcrop((int)pmin, 0, trackWidth / 2) + delta; - max = tcrop((int)pmax, trackWidth / 2, trackWidth - 1) + delta; - - if (i != y0 || !isFirstRow) { - // trattini a destra della colonna - if (i % 2) { - p.setPen(cellColor); - p.drawLine(x1, i, x1Bis, i); - } else { - p.setPen(m_viewer->getSoundColumnTrackColor()); - p.drawLine(x1Bis + 1, i, x2, i); - } - } + center = trackBounds.middle(); + min = tcrop((int)pmin, -trackWidth / 2, 0) + center; + max = tcrop((int)pmax, 0, trackWidth / 2 - 1) + center; if (scrub && i % 2) { p.setPen(m_viewer->getSoundColumnHlColor()); - p.drawLine(x1Bis + 1, i, x2, i); + QLine stroke = o->horizontalLine(i, previewBounds.adjusted(-1,-1)); + p.drawLine(stroke); + } else if (i != begin || !isFirstRow) { + // preview tool on the right side + if (i % 2) + p.setPen(cellColor); + else + p.setPen(m_viewer->getSoundColumnTrackColor()); + QLine stroke = o->horizontalLine(i, previewBounds.adjusted(-1, -1)); + p.drawLine(stroke); } - if (i != y0) { - // "traccia audio" al centro della colonna + if (i != begin) { + // "audio track" in the middle of the column p.setPen(m_viewer->getSoundColumnTrackColor()); - p.drawLine(lastMin, i, min, i); - p.drawLine(lastMax, i, max, i); + QLine minLine = o->horizontalLine(i, NumberRange(lastMin, min)); + p.drawLine(minLine); + QLine maxLine = o->horizontalLine(i, NumberRange(lastMax, max)); + p.drawLine(maxLine); } lastMin = min; lastMax = max; } + // yellow clipped border p.setPen(SoundColumnExtenderColor); int r0WithoutOff, r1WithoutOff; bool ret = @@ -1142,26 +1215,58 @@ void CellArea::drawSoundCell(QPainter &p, int row, int col) { assert(ret); if (isFirstRow) { - if (r0 != r0WithoutOff) { - p.drawLine(x1, y0 + 1, x2, y0 + 1); - p.drawLine(x1, y0 + 2, x2, y0 + 2); - } - QRect modifierRect(x1, y0 + 1, XsheetGUI::ColumnWidth, 2); - m_soundLevelModifyRects.append(modifierRect); + QRect modifierRect = m_viewer->orientation() + ->rect(PredefinedRect::BEGIN_SOUND_EDIT) + .translated(xy); + if (r0 != r0WithoutOff) p.fillRect(modifierRect, SoundColumnExtenderColor); + m_soundLevelModifyRects.append(modifierRect); // list of clipping rects } if (isLastRow) { - if (r1 != r1WithoutOff) { - p.drawLine(x, y1, x2, y1); - p.drawLine(x, y1 - 1, x2, y1 - 1); - } - QRect modifierRect(x1, y1 - 1, XsheetGUI::ColumnWidth, 2); + QRect modifierRect = m_viewer->orientation() + ->rect(PredefinedRect::END_SOUND_EDIT) + .translated(xy); + if (r1 != r1WithoutOff) p.fillRect(modifierRect, SoundColumnExtenderColor); m_soundLevelModifyRects.append(modifierRect); } } //----------------------------------------------------------------------------- +// paint side bar +void CellArea::drawDragHandle(QPainter &p, const QPoint &xy, + const QColor &sideColor) const { + QRect dragHandleRect = m_viewer->orientation() + ->rect(PredefinedRect::DRAG_HANDLE_CORNER) + .translated(xy); + p.fillRect(dragHandleRect, QBrush(sideColor)); +} + +// cut off triangle at the end of drag handle +void CellArea::drawEndOfDragHandle(QPainter &p, bool isEnd, const QPoint &xy, + const QColor &cellColor) const { + if (!isEnd) return; + QPainterPath corner = m_viewer->orientation() + ->path(PredefinedPath::DRAG_HANDLE_CORNER) + .translated(xy); + p.fillPath(corner, QBrush(cellColor)); +} + +// draw dot line if the column is locked +void CellArea::drawLockedDottedLine(QPainter &p, bool isLocked, + const QPoint &xy, + const QColor &cellColor) const { + if (!isLocked) return; + p.setPen(QPen(cellColor, 2, Qt::DotLine)); + QLine dottedLine = + m_viewer->orientation()->line(PredefinedLine::LOCKED).translated(xy); + p.drawLine(dottedLine); +} + +//----------------------------------------------------------------------------- + void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference) { + const Orientation *o = m_viewer->orientation(); + TXsheet *xsh = m_viewer->getXsheet(); TXshCell cell = xsh->getCell(row, col); TXshCell prevCell; @@ -1171,16 +1276,26 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference) { bool isSelected = cellSelection->isCellSelected(row, col) || columnSelection->isColumnSelected(col); - if (row > 0) prevCell = xsh->getCell(row - 1, col); + if (row > 0) prevCell = xsh->getCell(row - 1, col); // cell in previous frame // nothing to draw if (cell.isEmpty() && prevCell.isEmpty()) return; TXshCell nextCell; - nextCell = xsh->getCell(row + 1, col); + nextCell = xsh->getCell(row + 1, col); // cell in next frame - int x = m_viewer->columnToX(col); - int y = m_viewer->rowToY(row); - QRect rect = QRect(x + 1, y + 1, ColumnWidth - 1, RowHeight - 1); - if (cell.isEmpty()) { // vuol dire che la precedente non e' vuota + QPoint xy = m_viewer->positionToXY(CellPosition(row, col)); + int x = xy.x(); + int y = xy.y(); + if (row == 0) + { + if (o->isVerticalTimeline()) + xy.setY(xy.y() + 1); + else + xy.setX(xy.x() + 1); + } + QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y)); + QRect rect = cellRect.adjusted(1, 1, 0, 0); + if (cell.isEmpty()) { // it means previous is not empty + // diagonal cross meaning end of level QColor levelEndColor = m_viewer->getTextColor(); levelEndColor.setAlphaF(0.3); p.setPen(levelEndColor); @@ -1212,51 +1327,48 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference) { // paint cell p.fillRect(rect, QBrush(cellColor)); - // paint side bar - p.fillRect(QRect(x, y, 7, RowHeight), QBrush(sideColor)); - if (yetToCleanupCell) - p.fillRect( - rect.adjusted(rect.width() / 2, 0, 0, 0), - (isSelected) ? SelectedFullcolorColumnColor : FullcolorColumnColor); + drawDragHandle(p, xy, sideColor); - // draw dot line if the column is locked - TXshColumn *column = xsh->getColumn(col); - if (column->isLocked()) { - p.setPen(QPen(cellColor, 2, Qt::DotLine)); - p.drawLine(x + 3, y, x + 3, y + RowHeight); - } - // draw "end of the level" - if (nextCell.isEmpty() || - cell.m_level.getPointer() != nextCell.m_level.getPointer()) { - QPainterPath path(QPointF(x, y + RowHeight)); - path.lineTo(QPointF(x + 7, y + RowHeight)); - path.lineTo(QPointF(x + 7, y + RowHeight - 7)); - path.lineTo(QPointF(x, y + RowHeight)); - p.fillPath(path, QBrush(cellColor)); + if (yetToCleanupCell) // ORIENTATION: what's this? + { + if (o->isVerticalTimeline()) + p.fillRect( + rect.adjusted(rect.width() / 2, 0, 0, 0), + (isSelected) ? SelectedFullcolorColumnColor : FullcolorColumnColor); + else + p.fillRect( + rect.adjusted(0, rect.height() / 2, 0, 0), + (isSelected) ? SelectedFullcolorColumnColor : FullcolorColumnColor); } + bool isLastRow = nextCell.isEmpty() || + cell.m_level.getPointer() != nextCell.m_level.getPointer(); + drawEndOfDragHandle(p, isLastRow, xy, cellColor); + + drawLockedDottedLine(p, xsh->getColumn(col)->isLocked(), xy, cellColor); + bool sameLevel = prevCell.m_level.getPointer() == cell.m_level.getPointer(); int distance, offset; TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers( distance, offset); - if (distance == 0) distance = 6; - bool isAfterMarkers = (row - offset) % distance == 0; + bool isAfterMarkers = (row - offset) % distance == 0 && distance != 0 && row != 0; // draw marker interval if (isAfterMarkers) { p.setPen(m_viewer->getMarkerLineColor()); - p.drawLine(x, y, x + 6, y); + p.drawLine(o->line(PredefinedLine::SEE_MARKER_THROUGH).translated(xy)); } - QRect nameRect = rect.adjusted(7, 4, -6, 0); + QRect nameRect = o->rect(PredefinedRect::CELL_NAME).translated(QPoint(x, y)); if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) { TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col)); int r0, r1; if (pegbar && pegbar->getKeyframeRange(r0, r1)) - nameRect.adjust(0, 0, -9, 0); + nameRect = + o->rect(PredefinedRect::CELL_NAME_WITH_KEYFRAME).translated(QPoint(x, y)); } // draw text in red if the file does not exist @@ -1280,16 +1392,17 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference) { // do not draw frame number under RenameCellField if (m_renameCell->isVisible() && m_renameCell->isLocatedAt(row, col)) return; - // if the same level & same fId with the previous cell, then draw vertical - // line - if (sameLevel && prevCell.m_frameId == cell.m_frameId) { // cella uguale a - // quella precedente - // (non sulla marker - // line): - int x = (Preferences::instance()->isLevelNameOnEachMarkerEnabled()) - ? rect.right() - 11 - : rect.center().x(); - p.drawLine(x, rect.top(), x, rect.bottom()); + // if the same level & same fId with the previous cell, + // draw continue line + if (sameLevel && prevCell.m_frameId == cell.m_frameId) { + // not on line marker + PredefinedLine which = + Preferences::instance()->isLevelNameOnEachMarkerEnabled() + ? PredefinedLine::CONTINUE_LEVEL_WITH_NAME + : PredefinedLine::CONTINUE_LEVEL; + + QLine continueLine = o->line(which).translated(xy); + p.drawLine(continueLine); } // draw frame number else { @@ -1298,7 +1411,7 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference) { // convert the last one digit of the frame number to alphabet // Ex. 12 -> 1B 21 -> 2A 30 -> 3 if (Preferences::instance()->isShowFrameNumberWithLettersEnabled()) - p.drawText(nameRect, Qt::AlignRight, + p.drawText(nameRect, Qt::AlignRight | Qt::AlignBottom, m_viewer->getFrameNumberWithLetters(fid.getNumber())); else { std::string frameNumber(""); @@ -1306,7 +1419,7 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference) { if (fid.getNumber() > 0) frameNumber = std::to_string(fid.getNumber()); // add letter if (fid.getLetter() != 0) frameNumber.append(1, fid.getLetter()); - p.drawText(nameRect, Qt::AlignRight, QString::fromStdString(frameNumber)); + p.drawText(nameRect, Qt::AlignRight | Qt::AlignBottom, QString::fromStdString(frameNumber)); } } @@ -1322,26 +1435,45 @@ void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference) { #else QString elidaName = elideText(text, font, nameRect.width()); #endif - p.drawText(nameRect, Qt::AlignLeft, elidaName); + p.drawText(nameRect, Qt::AlignLeft | Qt::AlignBottom, elidaName); } } //----------------------------------------------------------------------------- void CellArea::drawSoundTextCell(QPainter &p, int row, int col) { + const Orientation *o = m_viewer->orientation(); TXsheet *xsh = m_viewer->getXsheet(); TXshCell cell = xsh->getCell(row, col); TXshCell prevCell; - if (row > 0) prevCell = xsh->getCell(row - 1, col); + + TCellSelection *cellSelection = m_viewer->getCellSelection(); + TColumnSelection *columnSelection = m_viewer->getColumnSelection(); + bool isSelected = cellSelection->isCellSelected(row, col) || + columnSelection->isColumnSelected(col); + + if (row > 0) prevCell = xsh->getCell(row - 1, col); // cell in previous frame + // nothing to draw + if (cell.isEmpty() && prevCell.isEmpty()) return; TXshCell nextCell; nextCell = xsh->getCell(row + 1, col); - if (cell.isEmpty() && prevCell.isEmpty()) return; - int x = m_viewer->columnToX(col); - int y = m_viewer->rowToY(row); - QRect rect = QRect(x + 1, y + 1, ColumnWidth - 1, RowHeight - 1); - if (cell.isEmpty()) { // vuol dire che la precedente non e' vuota - p.setPen(m_viewer->getLightLineColor()); + QPoint xy = m_viewer->positionToXY(CellPosition(row, col)); + int x = xy.x(); + int y = xy.y(); + if (row == 0) + { + if (o->isVerticalTimeline()) + xy.setY(xy.y() + 1); + else + xy.setX(xy.x() + 1); + } + QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y)); + QRect rect = cellRect.adjusted(1, 1, 0, 0); + if (cell.isEmpty()) { // diagonal cross meaning end of level + QColor levelEndColor = m_viewer->getTextColor(); + levelEndColor.setAlphaF(0.3); + p.setPen(levelEndColor); p.drawLine(rect.topLeft(), rect.bottomRight()); p.drawLine(rect.topRight(), rect.bottomLeft()); return; @@ -1349,35 +1481,35 @@ void CellArea::drawSoundTextCell(QPainter &p, int row, int col) { int levelType; QColor cellColor, sideColor; - m_viewer->getCellTypeAndColors(levelType, cellColor, sideColor, cell); + m_viewer->getCellTypeAndColors(levelType, cellColor, sideColor, cell, + isSelected); + + // paint cell p.fillRect(rect, QBrush(cellColor)); - if (nextCell.isEmpty() || - cell.m_level.getPointer() != nextCell.m_level.getPointer()) { - QPainterPath path(QPointF(x, y)); - path.lineTo(QPointF(x + 6, y)); - path.lineTo(QPointF(x + 6, y + 2)); - path.lineTo(QPointF(x, y + RowHeight - 2)); - p.fillPath(path, QBrush(sideColor)); - } else - p.fillRect(QRect(x, y, 6, RowHeight), QBrush(sideColor)); + drawDragHandle(p, xy, sideColor); + bool isLastRow = nextCell.isEmpty() || + cell.m_level.getPointer() != nextCell.m_level.getPointer(); + drawEndOfDragHandle(p, isLastRow, xy, cellColor); + + drawLockedDottedLine(p, xsh->getColumn(col)->isLocked(), xy, cellColor); bool sameLevel = prevCell.m_level.getPointer() == cell.m_level.getPointer(); - TFrameId fid = cell.m_frameId; - if (sameLevel && - prevCell.m_frameId == fid) { // cella uguale a quella precedente - // non scrivo nulla e disegno una linea verticale - QPen oldPen = p.pen(); - p.setPen(QPen(Qt::black, 1, Qt::DotLine)); - int x = rect.center().x(); - p.drawLine(x, rect.top(), x, rect.bottom()); - p.setPen(oldPen); - return; + int distance, offset; + TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers( + distance, offset); + bool isAfterMarkers = (row - offset) % distance == 0 && distance != 0 && row != 0; + + // draw marker interval + if (isAfterMarkers) { + p.setPen(m_viewer->getMarkerLineColor()); + p.drawLine(o->line(PredefinedLine::SEE_MARKER_THROUGH).translated(xy)); } - QString text = cell.getSoundTextLevel()->getFrameText(fid.getNumber() - 1); + p.setPen(Qt::black); - QRect nameRect = rect.adjusted(8, -H_ADJUST, -10, H_ADJUST); + QRect nameRect = o->rect(PredefinedRect::CELL_NAME).translated(QPoint(x, y)); + // il nome va scritto se e' diverso dalla cella precedente oppure se // siamo su una marker line #ifdef _WIN32 @@ -1388,6 +1520,20 @@ void CellArea::drawSoundTextCell(QPainter &p, int row, int col) { font.setPixelSize(XSHEET_FONT_PX_SIZE); p.setFont(font); + // if the same level & same fId with the previous cell, + // draw continue line + if (sameLevel && prevCell.m_frameId == cell.m_frameId) { + // not on line marker + PredefinedLine which = + Preferences::instance()->isLevelNameOnEachMarkerEnabled() + ? PredefinedLine::CONTINUE_LEVEL_WITH_NAME + : PredefinedLine::CONTINUE_LEVEL; + QLine continueLine = o->line(which).translated(xy); + p.drawLine(continueLine); + } + + QString text = cell.getSoundTextLevel()->getFrameText(cell.m_frameId.getNumber() - 1); + #if QT_VERSION >= 0x050500 QFontMetrics metric(font); QString elidaName = elideText(text, metric, nameRect.width(), "~"); @@ -1395,14 +1541,16 @@ void CellArea::drawSoundTextCell(QPainter &p, int row, int col) { QString elidaName = elideText(text, font, nameRect.width(), "~"); #endif - if (!sameLevel || prevCell.m_frameId != fid) - p.drawText(nameRect, Qt::AlignLeft, elidaName); + if (!sameLevel || prevCell.m_frameId != cell.m_frameId) + p.drawText(nameRect, Qt::AlignLeft | Qt::AlignBottom, elidaName); } //----------------------------------------------------------------------------- void CellArea::drawPaletteCell(QPainter &p, int row, int col, bool isReference) { + const Orientation *o = m_viewer->orientation(); + TXsheet *xsh = m_viewer->getXsheet(); TXshCell cell = xsh->getCell(row, col); TXshCell prevCell; @@ -1414,10 +1562,19 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, TXshCell nextCell = xsh->getCell(row + 1, col); if (cell.isEmpty() && prevCell.isEmpty()) return; - int x = m_viewer->columnToX(col); - int y = m_viewer->rowToY(row); - QRect rect = QRect(x + 1, y + 1, ColumnWidth - 1, RowHeight - 1); - if (cell.isEmpty()) { // vuol dire che la precedente non e' vuota + QPoint xy = m_viewer->positionToXY(CellPosition(row, col)); + int x = xy.x(); + int y = xy.y(); + if (row == 0) + { + if (o->isVerticalTimeline()) + xy.setY(xy.y() + 1); + else + xy.setX(xy.x() + 1); + } + QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y)); + QRect rect = cellRect.adjusted(1, 1, 0, 0); + if (cell.isEmpty()) { // this means the former is not empty QColor levelEndColor = m_viewer->getTextColor(); levelEndColor.setAlphaF(0.3); p.setPen(levelEndColor); @@ -1438,22 +1595,12 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, } p.fillRect(rect, QBrush(cellColor)); - p.fillRect(QRect(x, y, 7, RowHeight), QBrush(sideColor)); - TXshColumn *column = xsh->getColumn(col); - if (column->isLocked()) { - p.setPen(QPen(cellColor, 2, Qt::DotLine)); - p.drawLine(x + 3, y, x + 3, y + RowHeight); - } - - if (nextCell.isEmpty() || - cell.m_level.getPointer() != nextCell.m_level.getPointer()) { - QPainterPath path(QPointF(x, y + RowHeight)); - path.lineTo(QPointF(x + 7, y + RowHeight)); - path.lineTo(QPointF(x + 7, y + RowHeight - 7)); - path.lineTo(QPointF(x, y + RowHeight)); - p.fillPath(path, QBrush(cellColor)); - } + drawDragHandle(p, xy, sideColor); + bool isLastRow = nextCell.isEmpty() || + cell.m_level.getPointer() != nextCell.m_level.getPointer(); + drawEndOfDragHandle(p, isLastRow, xy, cellColor); + drawLockedDottedLine(p, xsh->getColumn(col)->isLocked(), xy, cellColor); bool sameLevel = prevCell.m_level.getPointer() == cell.m_level.getPointer(); @@ -1461,21 +1608,20 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers( distance, offset); if (distance == 0) distance = 6; - bool isAfterMarkers = (row - offset) % distance == 0; + bool isAfterMarkers = (row - offset) % distance == 0 && row != 0; if (isAfterMarkers) { p.setPen(m_viewer->getMarkerLineColor()); - p.drawLine(x, y, x + 6, y); + p.drawLine(o->line(PredefinedLine::SEE_MARKER_THROUGH).translated(xy)); } if (sameLevel && prevCell.m_frameId == cell.m_frameId && - !isAfterMarkers) { // cella uguale a quella precedente (non sulla marker - // line): - // non scrivo nulla e disegno una linea verticale + !isAfterMarkers) { // cell equal to previous one (not on marker line): + // do not write anything and draw a vertical line QPen oldPen = p.pen(); p.setPen(QPen(m_viewer->getTextColor(), 1)); - int x = rect.center().x(); - p.drawLine(x, rect.top(), x, rect.bottom()); + QLine continueLine = o->line(PredefinedLine::CONTINUE_LEVEL).translated(xy); + p.drawLine(continueLine); p.setPen(oldPen); } else { TFrameId fid = cell.m_frameId; @@ -1485,7 +1631,15 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, if (fid.getNumber() > 0) frameNumber = std::to_string(fid.getNumber()); if (fid.getLetter() != 0) frameNumber.append(1, fid.getLetter()); - QRect nameRect = rect.adjusted(7, 4, -12, 0); + QRect nameRect = o->rect(PredefinedRect::CELL_NAME).translated(QPoint(x, y)); + + if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) { + TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col)); + int r0, r1; + if (pegbar && pegbar->getKeyframeRange(r0, r1)) + nameRect = + o->rect(PredefinedRect::CELL_NAME_WITH_KEYFRAME).translated(QPoint(x, y)); + } bool isRed = false; TXshPaletteLevel *pl = cell.getPaletteLevel(); @@ -1507,8 +1661,21 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, // e' diverso dal precedente QString numberStr; if (!sameLevel || prevCell.m_frameId != cell.m_frameId) { - numberStr = QString::fromStdString(frameNumber); - p.drawText(nameRect, Qt::AlignRight, numberStr); + // convert the last one digit of the frame number to alphabet + // Ex. 12 -> 1B 21 -> 2A 30 -> 3 + if (Preferences::instance()->isShowFrameNumberWithLettersEnabled()) { + numberStr = m_viewer->getFrameNumberWithLetters(fid.getNumber()); + p.drawText(nameRect, Qt::AlignRight | Qt::AlignBottom, numberStr); + } + else { + std::string frameNumber(""); + // set number + if (fid.getNumber() > 0) frameNumber = std::to_string(fid.getNumber()); + // add letter + if (fid.getLetter() != 0) frameNumber.append(1, fid.getLetter()); + numberStr = QString::fromStdString(frameNumber); + p.drawText(nameRect, Qt::AlignRight | Qt::AlignBottom, numberStr); + } } QString text = QString::fromStdWString(levelName); @@ -1521,34 +1688,39 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, #endif if (!sameLevel || isAfterMarkers) - p.drawText(nameRect, Qt::AlignLeft, elidaName); + p.drawText(nameRect, Qt::AlignLeft | Qt::AlignBottom, elidaName); } } //----------------------------------------------------------------------------- void CellArea::drawKeyframe(QPainter &p, const QRect toBeUpdated) { + const Orientation *o = m_viewer->orientation(); int r0, r1, c0, c1; // range of visible rows and columns - r0 = m_viewer->yToRow(toBeUpdated.top()); - r1 = m_viewer->yToRow(toBeUpdated.bottom()); - c0 = m_viewer->xToColumn(toBeUpdated.left()); - c1 = m_viewer->xToColumn(toBeUpdated.right()); + CellRange visible = m_viewer->xyRectToRange(toBeUpdated); + r0 = visible.from().frame(); + r1 = visible.to().frame(); + c0 = visible.from().layer(); + c1 = visible.to().layer(); static QPixmap selectedKey = QPixmap(":Resources/selected_key.bmp"); static QPixmap key = QPixmap(":Resources/key.bmp"); - int keyPixOffset = (RowHeight - key.height()) / 2; + const QRect &keyRect = o->rect(PredefinedRect::KEY_ICON); TXsheet *xsh = m_viewer->getXsheet(); - ColumnFan *columnFan = xsh->getColumnFan(); + ColumnFan *columnFan = xsh->getColumnFan(o); int col; for (col = c0; col <= c1; col++) { if (!columnFan->isActive(col)) continue; - int x = m_viewer->columnToX(col); + int layerAxis = m_viewer->columnToLayerAxis(col); + TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col)); if (!pegbar) return; + int row0, row1; bool emptyKeyframe = !pegbar->getKeyframeRange(row0, row1); if (emptyKeyframe) continue; + bool emptyKeyframeRange = row0 >= row1; int row; row0 = std::max(row0, r0); @@ -1556,98 +1728,120 @@ void CellArea::drawKeyframe(QPainter &p, const QRect toBeUpdated) { /*- first, draw key segments -*/ p.setPen(m_viewer->getTextColor()); - int x_line = x + ColumnWidth - 8; + int line_layerAxis = layerAxis + o->layerSide(keyRect).middle(); for (row = row0; row <= row1; row++) { - int hr0, hr1; - double e0, e1; - if (pegbar->getKeyframeSpan(row, hr0, e0, hr1, e1)) { - int y0 = m_viewer->rowToY(hr0 + 1) - keyPixOffset; - int y1 = m_viewer->rowToY(hr1) + keyPixOffset; - p.drawLine(x_line, y0, x_line, y1); - if (hr1 - hr0 > 4) { - int rh0, rh1; - if (getEaseHandles(hr0, hr1, e0, e1, rh0, rh1)) { - int e0Y = m_viewer->rowToY(rh0) + keyPixOffset; - drawArrow(p, QPointF(x_line - 4, e0Y + 2), - QPointF(x_line + 4, e0Y + 2), QPointF(x_line, e0Y + 6), - true, m_viewer->getLightLineColor(), - m_viewer->getTextColor()); - int e1Y = m_viewer->rowToY(rh1 + 1) - keyPixOffset; - drawArrow(p, QPointF(x_line - 4, e1Y - 2), - QPointF(x_line + 4, e1Y - 2), QPointF(x_line, e1Y - 6), - true, m_viewer->getLightLineColor(), - m_viewer->getTextColor()); + int segmentRow0, segmentRow1; + double ease0, ease1; + if (pegbar->getKeyframeSpan(row, segmentRow0, ease0, segmentRow1, + ease1)) { + drawKeyframeLine(p, col, NumberRange(segmentRow0, segmentRow1)); + + if (segmentRow1 - segmentRow0 > + 4) { // only show if distance more than 4 frames + int handleRow0, handleRow1; + if (getEaseHandles(segmentRow0, segmentRow1, ease0, ease1, handleRow0, + handleRow1)) { + m_viewer->drawPredefinedPath(p, PredefinedPath::BEGIN_EASE_TRIANGLE, + CellPosition(handleRow0, col), + m_viewer->getLightLineColor(), + m_viewer->getTextColor()); + + m_viewer->drawPredefinedPath(p, PredefinedPath::END_EASE_TRIANGLE, + CellPosition(handleRow1, col), + m_viewer->getLightLineColor(), + m_viewer->getTextColor()); } } - // jump to next segment - row = hr1 - 1; + // skip to next segment + row = segmentRow1 - 1; } else if (pegbar->isKeyframe(row) && pegbar->isKeyframe(row + 1)) { - int y0 = m_viewer->rowToY(row + 1); - p.drawLine(x_line, y0 - keyPixOffset, x_line, y0 + keyPixOffset); + // 2 keyframes in a row - connect with a short line + drawKeyframeLine(p, col, NumberRange(row, row + 1)); } } /*- then draw keyframe pixmaps -*/ - int x1 = x + ColumnWidth - 13; + int icon_layerAxis = line_layerAxis - 5; for (row = row0; row <= row1; row++) { - int y = m_viewer->rowToY(row) + 1; p.setPen(m_viewer->getTextColor()); if (pegbar->isKeyframe(row)) { + QPoint target = + keyRect.translated(m_viewer->positionToXY(CellPosition(row, col))) + .topLeft(); if (m_viewer->getKeyframeSelection() && m_viewer->getKeyframeSelection()->isSelected(row, col)) { - // keyframe selezionato - p.drawPixmap(x1, y + keyPixOffset, selectedKey); + // keyframe selected + p.drawPixmap(target, selectedKey); } else { - // keyframe non selezionato - p.drawPixmap(x1, y + keyPixOffset, key); + // keyframe not selected + p.drawPixmap(target, key); } } } - int y1 = m_viewer->rowToY(row1 + 1); + int icon_frameAxis = m_viewer->rowToFrameAxis(row1 + 1); if (!emptyKeyframeRange && row0 <= row1 + 1) { - // c'e' piu' di un keyframe - // disegno il bottone per il ciclo + // there's just a keyframe + // drawing loop button p.setBrush(Qt::white); p.setPen(Qt::black); - p.drawRect(x1, y1, 10, 10); + QPoint target = o->frameLayerToXY(icon_frameAxis, icon_layerAxis); + p.drawRect(QRect(target, QSize(10, 10))); p.setBrush(Qt::NoBrush); - // disegno il bordo in basso (arrotondato) - p.drawLine(x1 + 1, y1 + 10, x1 + 9, y1 + 10); + // drawing the bottom edge (rounded) + p.drawLine(target + QPoint(1, 10), target + QPoint(9, 10)); p.setPen(Qt::white); - p.drawLine(x1 + 3, y1 + 10, x1 + 7, y1 + 10); + p.drawLine(target + QPoint(3, 10), target + QPoint(7, 10)); p.setPen(Qt::black); - p.drawLine(x1 + 3, y1 + 11, x1 + 7, y1 + 11); + p.drawLine(target + QPoint(3, 11), target + QPoint(7, 11)); - // disegno la freccia - p.drawArc(QRect(x1 + 2, y1 + 3, 6, 6), 180 * 16, 270 * 16); - p.drawLine(x1 + 5, y1 + 2, x1 + 5, y1 + 5); - p.drawLine(x1 + 5, y1 + 2, x1 + 8, y1 + 2); + // drawing the arrow + p.drawArc(QRect(target + QPoint(2, 3), QSize(6, 6)), 180 * 16, 270 * 16); + p.drawLine(target + QPoint(5, 2), target + QPoint(5, 5)); + p.drawLine(target + QPoint(5, 2), target + QPoint(8, 2)); } if (pegbar->isCycleEnabled()) { - // la riga a zigzag sotto il bottone - int ymax = m_viewer->rowToY(r1 + 1); - int qy = y1 + 12; + // the row zigzag bellow the button + int ymax = m_viewer->rowToFrameAxis(r1 + 1); + int qy = icon_frameAxis + 12; int zig = 2; - int qx = x1 + 5; + int qx = icon_layerAxis + 5; p.setPen(m_viewer->getTextColor()); - p.drawLine(qx, qy, qx - zig, qy + zig); + p.drawLine(o->frameLayerToXY(qy, qx), + o->frameLayerToXY(qy + zig, qx - zig)); while (qy < ymax) { - p.drawLine(qx - zig, qy + zig, qx + zig, qy + 3 * zig); - p.drawLine(qx + zig, qy + 3 * zig, qx - zig, qy + 5 * zig); + p.drawLine(o->frameLayerToXY(qy + zig, qx - zig), + o->frameLayerToXY(qy + 3 * zig, qx + zig)); + p.drawLine(o->frameLayerToXY(qy + 3 * zig, qx + zig), + o->frameLayerToXY(qy + 5 * zig, qx - zig)); qy += 4 * zig; } } } } + +void CellArea::drawKeyframeLine(QPainter &p, int col, + const NumberRange &rows) const { + const QRect &keyRect = + m_viewer->orientation()->rect(PredefinedRect::KEY_ICON); + QPoint begin = + keyRect.center() + m_viewer->positionToXY(CellPosition(rows.from(), col)); + QPoint end = + keyRect.center() + m_viewer->positionToXY(CellPosition(rows.to(), col)); + + p.setPen(m_viewer->getTextColor()); + p.drawLine(QLine(begin, end)); +} + //----------------------------------------------------------------------------- void CellArea::drawNotes(QPainter &p, const QRect toBeUpdated) { + CellRange visible = m_viewer->xyRectToRange(toBeUpdated); int r0, r1, c0, c1; // range of visible rows and columns - r0 = m_viewer->yToRow(toBeUpdated.top()); - r1 = m_viewer->yToRow(toBeUpdated.bottom()); - c0 = m_viewer->xToColumn(toBeUpdated.left()); - c1 = m_viewer->xToColumn(toBeUpdated.right()); + r0 = visible.from().frame(); + r1 = visible.to().frame(); + c0 = visible.from().layer(); + c1 = visible.to().layer(); TXsheet *xsh = m_viewer->getXsheet(); if (!xsh) return; @@ -1668,8 +1862,8 @@ void CellArea::drawNotes(QPainter &p, const QRect toBeUpdated) { int r = notes->getNoteRow(i); int c = notes->getNoteCol(i); if (r < r0 || r > r1 || c < c0 || c > c1) continue; - TPointD pos = notes->getNotePos(i) + - TPointD(m_viewer->columnToX(c), m_viewer->rowToY(r)); + QPoint xy = m_viewer->positionToXY(CellPosition(r, c)); + TPointD pos = notes->getNotePos(i) + TPointD(xy.x(), xy.y()); noteWidget->paint(&p, QPoint(pos.x, pos.y), i == m_viewer->getCurrentNoteIndex()); } @@ -1679,7 +1873,7 @@ void CellArea::drawNotes(QPainter &p, const QRect toBeUpdated) { bool CellArea::getEaseHandles(int r0, int r1, double e0, double e1, int &rh0, int &rh1) { - if (r1 <= r0 + 4) { + if (r1 <= r0 + 4) { // ... what? rh0 = r0; rh1 = r1; return false; @@ -1726,6 +1920,7 @@ void CellArea::paintEvent(QPaintEvent *event) { QPainter p(this); p.setClipRect(toBeUpdated); + p.fillRect(toBeUpdated, QBrush(m_viewer->getEmptyCellColor())); drawCells(p, toBeUpdated); @@ -1735,11 +1930,14 @@ void CellArea::paintEvent(QPaintEvent *event) { if (getDragTool()) getDragTool()->drawCellsArea(p); + // focus cell border int row = m_viewer->getCurrentRow(); int col = m_viewer->getCurrentColumn(); - int x = m_viewer->columnToX(col); - int y = m_viewer->rowToY(row); - QRect rect = QRect(x + 1, y + 1, ColumnWidth - 2, RowHeight - 2); + QPoint xy = m_viewer->positionToXY(CellPosition(row, col)); + QRect rect = m_viewer->orientation() + ->rect(PredefinedRect::CELL) + .translated(xy) + .adjusted(1, 1, -1, -1); p.setPen(Qt::black); p.setBrush(Qt::NoBrush); p.drawRect(rect); @@ -1771,6 +1969,8 @@ public: //---------------------------------------------------------- void CellArea::mousePressEvent(QMouseEvent *event) { + const Orientation *o = m_viewer->orientation(); + m_viewer->setQtModifiers(event->modifiers()); assert(!m_isPanning); m_isMousePressed = true; @@ -1778,20 +1978,21 @@ void CellArea::mousePressEvent(QMouseEvent *event) { assert(getDragTool() == 0); TPoint pos(event->pos().x(), event->pos().y()); - int row = m_viewer->yToRow(pos.y); - int col = m_viewer->xToColumn(pos.x); - int x0 = m_viewer->columnToX(col); - int y1 = m_viewer->rowToY(row) - 1; - int x = pos.x - x0; + CellPosition cellPosition = m_viewer->xyToPosition(event->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); + QPoint cellTopLeft = m_viewer->positionToXY(CellPosition(row, col)); + QPoint mouseInCell = event->pos() - cellTopLeft; + int x = mouseInCell.x(); // where in the cell click is - // Verifico se ho cliccato su una nota + // Check if a note is clicked TXshNoteSet *notes = m_viewer->getXsheet()->getNotes(); int i; for (i = notes->getCount() - 1; i >= 0; i--) { int r = notes->getNoteRow(i); int c = notes->getNoteCol(i); - TPointD pos = notes->getNotePos(i) + - TPointD(m_viewer->columnToX(c), m_viewer->rowToY(r)); + QPoint xy = m_viewer->positionToXY(CellPosition(r, c)); + TPointD pos = notes->getNotePos(i) + TPointD(xy.x(), xy.y()); QRect rect(pos.x, pos.y, NoteWidth, NoteHeight); if (!rect.contains(event->pos())) continue; setDragTool(XsheetGUI::DragTool::makeNoteMoveTool(m_viewer)); @@ -1801,14 +2002,13 @@ void CellArea::mousePressEvent(QMouseEvent *event) { update(); return; } - // Se non ho cliccato su una nota e c'e' una nota selezionata la - // deseleziono. + // If I have not clicked on a note, and there's a note selected, deselect it if (m_viewer->getCurrentNoteIndex() >= 0) m_viewer->setCurrentNoteIndex(-1); TXsheet *xsh = m_viewer->getXsheet(); TXshColumn *column = xsh->getColumn(col); - // Verifico se e' una colonna sound + // Check if it's the sound column bool isSoundColumn = false; if (column) { TXshSoundColumn *soundColumn = column->getSoundColumn(); @@ -1832,13 +2032,16 @@ void CellArea::mousePressEvent(QMouseEvent *event) { TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col)); if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) { + // only if key frame area is active int k0, k1; + bool isKeyframeFrame = pegbar && pegbar->getKeyframeRange(k0, k1) && + (k1 > k0 || k0 == row) && k0 <= row && + row <= k1 + 1; bool isKeyFrameArea = - (pegbar && pegbar->getKeyframeRange(k0, k1) && - (k1 > k0 || k0 == row) && k0 <= row && row <= k1 + 1 && - ColumnWidth - 13 <= x && x <= ColumnWidth) - ? true - : false; + isKeyframeFrame && + o->rect(PredefinedRect::KEYFRAME_AREA).contains(mouseInCell) + && row < k1 + 1; + bool accept = false; if (isKeyFrameArea) { // They are in the keyframe selection if (pegbar->isKeyframe(row)) // in the keyframe @@ -1846,33 +2049,49 @@ void CellArea::mousePressEvent(QMouseEvent *event) { m_viewer->setCurrentRow( row); // If you click on the key, change the current row as well setDragTool(XsheetGUI::DragTool::makeKeyframeMoverTool(m_viewer)); + accept = true; } else { int r0, r1; double e0, e1; int rh0, rh1; if (pegbar->getKeyframeSpan(row, r0, e0, r1, e1) && getEaseHandles(r0, r1, e0, e1, rh0, rh1)) { - if (rh0 == row) // in a keyframe handle + if (rh0 == row) { // in a keyframe handle setDragTool(XsheetGUI::DragTool::makeKeyFrameHandleMoverTool( m_viewer, true, r0)); - else if (rh1 == row) // in a keyframe handle + accept = true; + } else if (rh1 == row) { // in a keyframe handle setDragTool(XsheetGUI::DragTool::makeKeyFrameHandleMoverTool( m_viewer, false, r1)); - } - if (row == k1 + 1) // in the cycle toggle - { - pegbar->enableCycle(!pegbar->isCycleEnabled()); - TUndoManager::manager()->add(new CycleUndo(pegbar, this)); + accept = true; + } } } + } else if (isKeyframeFrame && row == k1 + 1 && + o->rect(PredefinedRect::LOOP_ICON) + .contains(mouseInCell)) { // cycle toggle + pegbar->enableCycle(!pegbar->isCycleEnabled()); + TUndoManager::manager()->add(new CycleUndo(pegbar, this)); + accept = true; + } + if (accept) { m_viewer->dragToolClick(event); event->accept(); update(); return; } + // keep searching } - if ((!xsh->getCell(row, col).isEmpty()) && x < 6) { + if (m_levelExtenderRect.contains(pos.x, pos.y)) { + m_viewer->getKeyframeSelection()->selectNone(); + setDragTool(XsheetGUI::DragTool::makeLevelExtenderTool(m_viewer)); + } else if (event->modifiers() & Qt::ControlModifier && + m_upperLevelExtenderRect.contains(pos.x, pos.y)) { + m_viewer->getKeyframeSelection()->selectNone(); + setDragTool(XsheetGUI::DragTool::makeLevelExtenderTool(m_viewer, true)); + } else if ((!xsh->getCell(row, col).isEmpty()) && + o->rect(PredefinedRect::DRAG_AREA).contains(mouseInCell)) { TXshColumn *column = xsh->getColumn(col); if (column && !m_viewer->getCellSelection()->isCellSelected(row, col)) { int r0, r1; @@ -1897,14 +2116,10 @@ void CellArea::mousePressEvent(QMouseEvent *event) { setDragTool(XsheetGUI::DragTool::makeLevelMoverTool(m_viewer)); } else { m_viewer->getKeyframeSelection()->selectNone(); - if (isSoundColumn && x > ColumnWidth - 6 && x < ColumnWidth) + if (isSoundColumn && + o->rect(PredefinedRect::PREVIEW_TRACK).contains(mouseInCell)) setDragTool(XsheetGUI::DragTool::makeSoundScrubTool( m_viewer, column->getSoundColumn())); - else if (m_levelExtenderRect.contains(pos.x, pos.y)) - setDragTool(XsheetGUI::DragTool::makeLevelExtenderTool(m_viewer)); - else if (event->modifiers() & Qt::ControlModifier && - m_upperLevelExtenderRect.contains(pos.x, pos.y)) - setDragTool(XsheetGUI::DragTool::makeLevelExtenderTool(m_viewer, true)); else if (isSoundColumn && rectContainsPos(m_soundLevelModifyRects, event->pos())) setDragTool(XsheetGUI::DragTool::makeSoundLevelModifierTool(m_viewer)); @@ -1923,6 +2138,8 @@ void CellArea::mousePressEvent(QMouseEvent *event) { //----------------------------------------------------------------------------- void CellArea::mouseMoveEvent(QMouseEvent *event) { + const Orientation *o = m_viewer->orientation(); + m_viewer->setQtModifiers(event->modifiers()); setCursor(Qt::ArrowCursor); QPoint pos = event->pos(); @@ -1945,10 +2162,13 @@ void CellArea::mouseMoveEvent(QMouseEvent *event) { return; } - int row = m_viewer->yToRow(pos.y()); - int col = m_viewer->xToColumn(pos.x()); - int x0 = m_viewer->columnToX(col); - int x = m_pos.x() - x0; + CellPosition cellPosition = m_viewer->xyToPosition(pos); + int row = cellPosition.frame(); + int col = cellPosition.layer(); + QPoint cellTopLeft = m_viewer->positionToXY(CellPosition(row, col)); + int x = m_pos.x() - cellTopLeft.x(); + int y = m_pos.y() - cellTopLeft.y(); + QPoint mouseInCell = m_pos - cellTopLeft; TXsheet *xsh = m_viewer->getXsheet(); @@ -1965,9 +2185,15 @@ void CellArea::mouseMoveEvent(QMouseEvent *event) { TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col)); int k0, k1; - if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled() && - pegbar && pegbar->getKeyframeRange(k0, k1) && k0 <= row && - row <= k1 + 1 && ColumnWidth - 13 <= x && x <= ColumnWidth) { + bool isKeyframeFrame = + Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled() && + pegbar && pegbar->getKeyframeRange(k0, k1) && k0 <= row && row <= k1 + 1; + bool isKeyFrameArea = + isKeyframeFrame && + o->rect(PredefinedRect::KEYFRAME_AREA).contains(mouseInCell) + && row < k1 + 1; + + if (isKeyFrameArea) { if (pegbar->isKeyframe(row)) // key frame m_tooltip = tr("Click to select keyframe, drag to move it"); else { @@ -1983,10 +2209,12 @@ void CellArea::mouseMoveEvent(QMouseEvent *event) { else if (rh1 == row) m_tooltip = tr("Click and drag to set the deceleration range"); } - if (row == k1 + 1) // cycle toggle of key frames - m_tooltip = tr("Set the cycle of previous keyframes"); } - } else if ((!xsh->getCell(row, col).isEmpty() || isSoundColumn) && x < 6) + } else if (isKeyframeFrame && row == k1 + 1 && + o->rect(PredefinedRect::LOOP_ICON) + .contains(mouseInCell)) // cycle toggle of key frames + m_tooltip = tr("Set the cycle of previous keyframes"); + else if ((!xsh->getCell(row, col).isEmpty()) && o->rect(PredefinedRect::DRAG_AREA).contains(mouseInCell)) m_tooltip = tr("Click and drag to move the selection"); else if (isZeraryColumn) m_tooltip = QString::fromStdWString(column->getZeraryFxColumn() @@ -1994,7 +2222,7 @@ void CellArea::mouseMoveEvent(QMouseEvent *event) { ->getZeraryFx() ->getName()); else if ((!xsh->getCell(row, col).isEmpty() && !isSoundColumn) && x > 6 && - x < ColumnWidth) { + x < o->cellWidth()) { TXshCell cell = xsh->getCell(row, col); TFrameId fid = cell.getFrameId(); std::wstring levelName = cell.m_level->getName(); @@ -2017,13 +2245,16 @@ void CellArea::mouseMoveEvent(QMouseEvent *event) { : QString::fromStdWString(levelName) + QString(" ") + QString::fromStdString(frameNumber)); } - } else if (isSoundColumn && x > ColumnWidth - 6 && x < ColumnWidth) + } else if (isSoundColumn && o->rect(PredefinedRect::PREVIEW_TRACK).contains(mouseInCell)) m_tooltip = tr("Click and drag to play"); else if (m_levelExtenderRect.contains(pos)) m_tooltip = tr("Click and drag to repeat selected cells"); else if (isSoundColumn && rectContainsPos(m_soundLevelModifyRects, pos)) { - setCursor(Qt::SplitVCursor); - m_tooltip = tr(""); + if (o->isVerticalTimeline()) + setCursor(Qt::SplitVCursor); + else + setCursor(Qt::SplitHCursor); + m_tooltip = tr(""); } else m_tooltip = tr(""); } @@ -2041,9 +2272,11 @@ void CellArea::mouseReleaseEvent(QMouseEvent *event) { //----------------------------------------------------------------------------- void CellArea::mouseDoubleClickEvent(QMouseEvent *event) { + const Orientation *o = m_viewer->orientation(); TPoint pos(event->pos().x(), event->pos().y()); - int row = m_viewer->yToRow(pos.y); - int col = m_viewer->xToColumn(pos.x); + CellPosition cellPosition = m_viewer->xyToPosition(event->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); // Se la colonna e' sound non devo fare nulla TXshColumn *column = m_viewer->getXsheet()->getColumn(col); if (column && (column->getSoundColumn() || column->getSoundTextColumn())) @@ -2055,8 +2288,8 @@ void CellArea::mouseDoubleClickEvent(QMouseEvent *event) { for (i = notes->getCount() - 1; i >= 0; i--) { int r = notes->getNoteRow(i); int c = notes->getNoteCol(i); - TPointD pos = notes->getNotePos(i) + - TPointD(m_viewer->columnToX(c), m_viewer->rowToY(r)); + QPoint xy = m_viewer->positionToXY(CellPosition(r, c)); + TPointD pos = notes->getNotePos(i) + TPointD(xy.x(), xy.y()); QRect rect(pos.x, pos.y, NoteWidth, NoteHeight); if (!rect.contains(event->pos())) continue; m_viewer->setCurrentNoteIndex(i); @@ -2068,13 +2301,18 @@ void CellArea::mouseDoubleClickEvent(QMouseEvent *event) { oh->setObjectId(m_viewer->getObjectId(col)); if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) { - int x = pos.x - m_viewer->columnToX(col); - TStageObject *pegbar = - m_viewer->getXsheet()->getStageObject(m_viewer->getObjectId(col)); - bool isKeyFrameArea = (pegbar && pegbar->isKeyframe(row) && - ColumnWidth - 13 <= x && x <= ColumnWidth) - ? true - : false; + QPoint cellTopLeft = m_viewer->positionToXY(CellPosition(row, col)); + QPoint mouseInCell = event->pos() - cellTopLeft; + TXsheet *xsh = m_viewer->getXsheet(); + TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col)); + int k0, k1; + bool isKeyframeFrame = + pegbar && pegbar->getKeyframeRange(k0, k1) && k0 <= row && row <= k1 + 1; + bool isKeyFrameArea = + isKeyframeFrame && + o->rect(PredefinedRect::KEYFRAME_AREA).contains(mouseInCell) + && row < k1 + 1; + // If you are in the keyframe area, open a function editor if (isKeyFrameArea) { QAction *action = @@ -2100,9 +2338,11 @@ void CellArea::mouseDoubleClickEvent(QMouseEvent *event) { //----------------------------------------------------------------------------- void CellArea::contextMenuEvent(QContextMenuEvent *event) { + const Orientation *o = m_viewer->orientation(); TPoint pos(event->pos().x(), event->pos().y()); - int row = m_viewer->yToRow(pos.y); - int col = m_viewer->xToColumn(pos.x); + CellPosition cellPosition = m_viewer->xyToPosition(event->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); QMenu menu(this); @@ -2112,8 +2352,8 @@ void CellArea::contextMenuEvent(QContextMenuEvent *event) { for (i = notes->getCount() - 1; i >= 0; i--) { int r = notes->getNoteRow(i); int c = notes->getNoteCol(i); - TPointD pos = notes->getNotePos(i) + - TPointD(m_viewer->columnToX(c), m_viewer->rowToY(r)); + QPoint xy = m_viewer->positionToXY(CellPosition(r, c)); + TPointD pos = notes->getNotePos(i) + TPointD(xy.x(), xy.y()); QRect rect(pos.x, pos.y, NoteWidth, NoteHeight); if (!rect.contains(event->pos())) continue; m_viewer->setCurrentNoteIndex(i); @@ -2123,17 +2363,25 @@ void CellArea::contextMenuEvent(QContextMenuEvent *event) { } TXsheet *xsh = m_viewer->getXsheet(); - int x0 = m_viewer->columnToX(col); - int y1 = m_viewer->rowToY(row) - 1; + int x0 = m_viewer->positionToXY(cellPosition).x(); int x = pos.x - x0; TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col)); int k0, k1; int r0, r1, c0, c1; if (col >= 0) m_viewer->getCellSelection()->getSelectedCells(r0, c0, r1, c1); - if (pegbar && pegbar->getKeyframeRange(k0, k1) && k0 <= row && row <= k1 && - ColumnWidth - 13 <= x && x <= ColumnWidth && - Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) { + QPoint cellTopLeft = m_viewer->positionToXY(CellPosition(row, col)); + QPoint mouseInCell = event->pos() - cellTopLeft; + bool isKeyframeFrame = + Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled() && + pegbar && pegbar->getKeyframeRange(k0, k1) && + k0 <= row && row <= k1 + 1; + bool isKeyFrameArea = + isKeyframeFrame && + o->rect(PredefinedRect::KEYFRAME_AREA).contains(mouseInCell) + && row < k1 + 1; + + if (isKeyFrameArea) { TStageObjectId objectId; if (col < 0) objectId = TStageObjectId::CameraId(0); diff --git a/toonz/sources/toonz/xshcellviewer.h b/toonz/sources/toonz/xshcellviewer.h index 2bd47629..677c6e46 100644 --- a/toonz/sources/toonz/xshcellviewer.h +++ b/toonz/sources/toonz/xshcellviewer.h @@ -5,6 +5,7 @@ #include #include +#include "orientation.h" // forward declaration class XsheetViewer; @@ -70,11 +71,27 @@ class CellArea final : public QWidget { RenameCellField *m_renameCell; void drawCells(QPainter &p, const QRect toBeUpdated); + void drawNonEmptyBackground(QPainter &p) const; + void drawFoldedColumns(QPainter &p, int layerAxis, + const NumberRange &frameAxis) const; + void drawSelectionBackground(QPainter &p) const; + void drawExtenderHandles(QPainter &p); + + void drawDragHandle(QPainter &p, const QPoint &xy, + const QColor &sideColor) const; + void drawEndOfDragHandle(QPainter &p, bool isEnd, const QPoint &xy, + const QColor &cellColor) const; + void drawLockedDottedLine(QPainter &p, bool isLocked, const QPoint &xy, + const QColor &cellColor) const; + void drawLevelCell(QPainter &p, int row, int col, bool isReference = false); void drawSoundTextCell(QPainter &p, int row, int col); void drawSoundCell(QPainter &p, int row, int col); void drawPaletteCell(QPainter &p, int row, int col, bool isReference = false); + void drawKeyframe(QPainter &p, const QRect toBeUpdated); + void drawKeyframeLine(QPainter &p, int col, const NumberRange &rows) const; + void drawNotes(QPainter &p, const QRect toBeUpdated); // Restistusce true diff --git a/toonz/sources/toonz/xshcolumnviewer.cpp b/toonz/sources/toonz/xshcolumnviewer.cpp index 452ba192..dc30cf43 100644 --- a/toonz/sources/toonz/xshcolumnviewer.cpp +++ b/toonz/sources/toonz/xshcolumnviewer.cpp @@ -1,5 +1,4 @@ - #include "xshcolumnviewer.h" // Tnz6 includes @@ -111,6 +110,8 @@ const QIcon getColorChipIcon(const int id) { pixmap.fill(colors.at(id - 1)); return QIcon(pixmap); } + +bool isCtrlPressed = false; } //----------------------------------------------------------------------------- @@ -451,14 +452,15 @@ void ChangeObjectHandle::onTextChanged(const QString &text) { RenameColumnField::RenameColumnField(QWidget *parent, XsheetViewer *viewer) : QLineEdit(parent), m_col(-1) { - setFixedSize(XsheetGUI::ColumnWidth + 3, XsheetGUI::RowHeight + 4); + setFixedSize(20, 20); connect(this, SIGNAL(returnPressed()), SLOT(renameColumn())); } //----------------------------------------------------------------------------- -void RenameColumnField::show(QPoint pos, int col) { - move(pos); +void RenameColumnField::show(const QRect &rect, int col) { + move(rect.topLeft()); + setFixedSize(rect.size()); #ifdef _WIN32 static QFont font("Arial", -1, QFont::Normal); #else @@ -514,6 +516,435 @@ void RenameColumnField::focusOutEvent(QFocusEvent *e) { QLineEdit::focusOutEvent(e); } +//============================================================================= +// ColumnArea +//----------------------------------------------------------------------------- + +void ColumnArea::onControlPressed(bool pressed) { + isCtrlPressed = pressed; + update(); +} + +const bool ColumnArea::isControlPressed() { return isCtrlPressed; } + +//----------------------------------------------------------------------------- + +ColumnArea::DrawHeader::DrawHeader(ColumnArea *nArea, QPainter &nP, int nCol) + : area(nArea), p(nP), col(nCol) { + m_viewer = area->m_viewer; + o = m_viewer->orientation(); + app = TApp::instance(); + xsh = m_viewer->getXsheet(); + column = col >= 0 ? xsh->getColumn(col) : 0; + isEmpty = col >= 0 && xsh->isColumnEmpty(col); + + TStageObjectId currentColumnId = app->getCurrentObject()->getObjectId(); + + // check if the column is current + isCurrent = false; + if (currentColumnId == TStageObjectId::CameraId(0)) // CAMERA + isCurrent = col == -1; + else + isCurrent = m_viewer->getCurrentColumn() == col; + + orig = m_viewer->positionToXY(CellPosition(0, max(col, 0))); +} + +void ColumnArea::DrawHeader::prepare() const { +// Preparing painter +#ifdef _WIN32 + QFont font("Arial", -1, QFont::Normal); +#else + QFont font("Helvetica", -1, QFont::Normal); +#endif + font.setPixelSize(XSHEET_FONT_PX_SIZE); + + p.setFont(font); + p.setRenderHint(QPainter::SmoothPixmapTransform, true); +} + +//----------------------------------------------------------------------------- +const QPixmap &ColumnArea::Pixmaps::eye() { + static QPixmap eye = QPixmap(":Resources/x_prev_eye.png"); + return eye; +} +const QPixmap &ColumnArea::Pixmaps::cameraStand() { + static QPixmap cameraStand = QPixmap(":Resources/x_table_view.png"); + return cameraStand; +} +const QPixmap &ColumnArea::Pixmaps::cameraStandTransparent() { + static QPixmap cameraStandTransparent = + QPixmap(":Resources/x_table_view_transp.png"); + return cameraStandTransparent; +} +const QPixmap &ColumnArea::Pixmaps::lock() { + static QPixmap lock = QPixmap(":Resources/x_lock.png"); + return lock; +} +const QPixmap &ColumnArea::Pixmaps::sound() { + static QPixmap sound = QPixmap(":Resources/sound_header_off.png"); + return sound; +} +const QPixmap &ColumnArea::Pixmaps::soundPlaying() { + static QPixmap soundPlaying = QPixmap(":Resources/sound_header_on.png"); + return soundPlaying; +} +//----------------------------------------------------------------------------- + +void ColumnArea::DrawHeader::levelColors(QColor &columnColor, + QColor &dragColor) const { + enum { Normal, Reference, Control } usage = Reference; + if (column) { + if (column->isControl()) usage = Control; + if (column->isRendered() || column->getMeshColumn()) usage = Normal; + } + + if (usage == Reference) { + columnColor = m_viewer->getReferenceColumnColor(); + dragColor = m_viewer->getReferenceColumnBorderColor(); + } else + m_viewer->getColumnColor(columnColor, dragColor, col, xsh); +} +void ColumnArea::DrawHeader::soundColors(QColor &columnColor, + QColor &dragColor) const { + m_viewer->getColumnColor(columnColor, dragColor, col, xsh); +} +void ColumnArea::DrawHeader::paletteColors(QColor &columnColor, + QColor &dragColor) const { + enum { Normal, Reference, Control } usage = Reference; + if (column) { // Check if column is a mask + if (column->isControl()) usage = Control; + if (column->isRendered()) usage = Normal; + } + + if (usage == Reference) { + columnColor = m_viewer->getReferenceColumnColor(); + dragColor = m_viewer->getReferenceColumnBorderColor(); + } else { + columnColor = m_viewer->getPaletteColumnColor(); + dragColor = m_viewer->getPaletteColumnBorderColor(); + } +} + +void ColumnArea::DrawHeader::drawBaseFill(const QColor &columnColor, + const QColor &dragColor) const { + // check if the column is reference + bool isEditingSpline = app->getCurrentObject()->isSpline(); + + QRect rect = o->rect(PredefinedRect::LAYER_HEADER).translated(orig); + + int x0 = rect.left(); + int x1 = rect.right(); + int y0 = rect.top(); + int y1 = rect.bottom(); + + // fill base color + if (isEmpty || col < 0) { + p.fillRect(rect, m_viewer->getEmptyColumnHeadColor()); + + p.setPen(m_viewer->getVerticalLineHeadColor()); + QLine vertical = + o->verticalLine(m_viewer->columnToLayerAxis(col), o->frameSide(rect)); + p.drawLine(vertical); + } else { + p.fillRect(rect, columnColor); + + // column handle + QRect sideBar = o->rect(PredefinedRect::DRAG_LAYER).translated(x0, y0); + + p.fillRect(sideBar, sideBar.contains(area->m_pos) ? Qt::yellow : dragColor); + } + + // highlight selection + bool isSelected = + m_viewer->getColumnSelection()->isColumnSelected(col) && !isEditingSpline; + bool isCameraSelected = col == -1 && isCurrent && !isEditingSpline; + + QColor pastelizer(m_viewer->getColumnHeadPastelizer()); + pastelizer.setAlpha(50); + + QColor colorSelection(m_viewer->getSelectedColumnHead()); + colorSelection.setAlpha(170); + p.fillRect(rect, + (isSelected || isCameraSelected) ? colorSelection : pastelizer); +} + +void ColumnArea::DrawHeader::drawEye() const { + if (col < 0 || isEmpty) return; + if (!column->isPreviewVisible() || column->getPaletteColumn() || column->getSoundTextColumn()) return; + + QRect prevViewRect = o->rect(PredefinedRect::EYE_AREA).translated(orig); + QRect eyeRect = o->rect(PredefinedRect::EYE).translated(orig); + // preview visible toggle + p.fillRect(prevViewRect, PreviewVisibleColor); + p.drawPixmap(eyeRect, Pixmaps::eye()); +} + +void ColumnArea::DrawHeader::drawPreviewToggle(int opacity) const { + if (col < 0 || isEmpty) return; + // camstand visible toggle + if (!column->isCamstandVisible() || column->getPaletteColumn() || column->getSoundTextColumn()) return; + + QRect tableViewRect = + o->rect(PredefinedRect::PREVIEW_LAYER_AREA).translated(orig); + QRect tableViewImgRect = + o->rect(PredefinedRect::PREVIEW_LAYER).translated(orig); + + p.fillRect(tableViewRect, CamStandVisibleColor); + p.drawPixmap(tableViewImgRect, opacity < 255 + ? Pixmaps::cameraStandTransparent() + : Pixmaps::cameraStand()); +} + +void ColumnArea::DrawHeader::drawLock() const { + if (col < 0 || isEmpty) return; + + QRect lockModeRect = o->rect(PredefinedRect::LOCK_AREA).translated(orig); + QRect lockModeImgRect = o->rect(PredefinedRect::LOCK).translated(orig); + + // lock button + p.setPen(Qt::gray); + p.setBrush(QColor(255, 255, 255, 128)); + p.drawRect(lockModeRect); + p.setBrush(Qt::NoBrush); + bool isLocked = column && column->isLocked(); + if (isLocked) p.drawPixmap(lockModeImgRect, Pixmaps::lock()); +} + +void ColumnArea::DrawHeader::drawColumnNumber() const { + if (o->isVerticalTimeline()) return; + if (col < 0 || isEmpty || !Preferences::instance()->isShowColumnNumbersEnabled()) return; + + if (!isEmpty) + p.setPen((isCurrent) ? Qt::red : Qt::black); + else + p.setPen((isCurrent) ? m_viewer->getSelectedColumnTextColor() + : m_viewer->getTextColor()); + + QRect pos = o->rect(PredefinedRect::LAYER_NUMBER).translated(orig); + if (pos.isEmpty()) return; + p.drawText(pos, Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, + QString::number(col + 1)); +} + +void ColumnArea::DrawHeader::drawColumnName() const { + TStageObjectId columnId = m_viewer->getObjectId(col); + TStageObject *columnObject = xsh->getStageObject(columnId); + + // Build column name + std::string name(columnObject->getName()); + if (col < 0) name = std::string("Camera"); + + //ZeraryFx columns store name elsewhere + TXshZeraryFxColumn *zColumn = dynamic_cast(column); + if (zColumn) + name = ::to_string(zColumn->getZeraryColumnFx()->getZeraryFx()->getName()); + + QRect columnName = o->rect(PredefinedRect::LAYER_NAME).translated(orig); + + int rightadj = -2; + int leftadj = 3; + + if (!isEmpty) + { + if (Preferences::instance()->isShowColumnNumbersEnabled()) + { + if (o->isVerticalTimeline()) + rightadj = -20; + else + leftadj = 24; + } + + if (!o->isVerticalTimeline()) + { + if (column->getSoundColumn()) + rightadj -= 97; + else if (column->getFilterColorId()) + rightadj -= 15; + } + + p.setPen((isCurrent) ? Qt::red : Qt::black); + } + else + p.setPen((isCurrent) ? m_viewer->getSelectedColumnTextColor() + : m_viewer->getTextColor()); + + if(o->isVerticalTimeline()) + p.drawText(columnName.adjusted(leftadj, 0, rightadj, 0), Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, + QString(name.c_str())); + else + p.drawText(columnName.adjusted(leftadj, 0, rightadj, 0), Qt::AlignLeft | Qt::AlignBottom | Qt::TextSingleLine, + QString(name.c_str())); + +} + +void ColumnArea::DrawHeader::drawThumbnail(QPixmap &iconPixmap) const { + if (col < 0 || isEmpty) return; + + QRect thumbnailRect = o->rect(PredefinedRect::THUMBNAIL_AREA).translated(orig); + + // sound thumbnail + if (column->getSoundColumn()) + { + TXshSoundColumn *sc = xsh->getColumn(col) ? xsh->getColumn(col)->getSoundColumn() : 0; + + drawSoundIcon(sc->isPlaying()); + drawVolumeControl(sc->getVolume()); + return; + } + + if (!o->isVerticalTimeline()) return; + + QRect thumbnailImageRect = o->rect(PredefinedRect::THUMBNAIL).translated(orig); + + // pallete thumbnail + if (column->getPaletteColumn()) + { + p.drawPixmap(thumbnailImageRect, iconPixmap); + return; + } + + // soundtext thumbnail + if (column->getSoundTextColumn()) + { + p.drawPixmap(thumbnailImageRect, iconPixmap); + return; + } + + // All other thumbnails + p.setPen(m_viewer->getTextColor()); + + // for zerary fx, display fxId here instead of thumbnail + TXshZeraryFxColumn *zColumn = dynamic_cast(column); + if (zColumn) { + QFont lastfont = p.font(); + QFont font("Verdana", 8); + p.setFont(font); + + TFx *fx = zColumn->getZeraryColumnFx()->getZeraryFx(); + QString fxName = QString::fromStdWString(fx->getFxId()); + p.drawText(thumbnailImageRect, Qt::TextWrapAnywhere | Qt::TextWordWrap, fxName); + p.setFont(lastfont); + } + else { + TXshLevelColumn *levelColumn = column->getLevelColumn(); + + if (levelColumn && + Preferences::instance()->getColumnIconLoadingPolicy() == + Preferences::LoadOnDemand && + !levelColumn->isIconVisible()) { + // display nothing + } + else { + if (!iconPixmap.isNull()) { + p.drawPixmap(thumbnailImageRect, iconPixmap); + } + // notify that the column icon is already shown + if (levelColumn) levelColumn->setIconVisible(true); + } + } +} + +void ColumnArea::DrawHeader::drawPegbarName() const { + if (col < 0 || isEmpty || !o->isVerticalTimeline()) return; + + TStageObjectId columnId = m_viewer->getObjectId(col); + TStageObjectId parentId = xsh->getStageObjectParent(columnId); + + // pegbar name + QRect pegbarnamerect = o->rect(PredefinedRect::PEGBAR_NAME).translated(orig); + + if (column->getSoundColumn() || column->getSoundTextColumn() || column->getPaletteColumn()) return; + + p.setPen(m_viewer->getTextColor()); + + p.drawText(pegbarnamerect.adjusted(3, 0, 0, 0), Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, + QString(parentId.toString().c_str())); +} + +void ColumnArea::DrawHeader::drawParentHandleName() const { + if (col < 0 || isEmpty || !o->isVerticalTimeline() + || column->getSoundColumn() || column->getSoundTextColumn() || column->getPaletteColumn()) return; + + QRect parenthandleRect = o->rect(PredefinedRect::PARENT_HANDLE_NAME).translated(orig); + + TStageObjectId columnId = m_viewer->getObjectId(col); + TStageObjectId parentId = xsh->getStageObjectParent(columnId); + + p.setPen(m_viewer->getTextColor()); + + std::string handle = xsh->getStageObject(columnId)->getParentHandle(); + if (handle[0] == 'H' && handle.length() > 1) handle = handle.substr(1); + if (parentId != TStageObjectId::TableId) + p.drawText(parenthandleRect, Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, QString::fromStdString(handle)); +} + +void ColumnArea::DrawHeader::drawFilterColor() const { + if (col < 0 || isEmpty || !column->getFilterColorId() + || column->getSoundColumn() || column->getSoundTextColumn() || column->getPaletteColumn()) return; + + QRect filterColorRect = o->rect(PredefinedRect::FILTER_COLOR).translated(orig); + p.drawPixmap(filterColorRect, getColorChipIcon(column->getFilterColorId()).pixmap(12, 12)); +} + +void ColumnArea::DrawHeader::drawSoundIcon(bool isPlaying) const { + QRect rect = m_viewer->orientation() + ->rect(PredefinedRect::SOUND_ICON) + .translated(orig); + p.drawPixmap(rect, isPlaying ? Pixmaps::soundPlaying() : Pixmaps::sound()); +} + +void ColumnArea::DrawHeader::drawVolumeControl(double volume) const { + // slider subdivisions + p.setPen(m_viewer->getTextColor()); + QPoint divisionsTopLeft = + o->point(PredefinedPoint::VOLUME_DIVISIONS_TOP_LEFT) + orig; + int layerAxis = o->layerAxis(divisionsTopLeft); + int frameAxis = o->frameAxis(divisionsTopLeft); + if (o->isVerticalTimeline()) { + for (int i = 0; i <= 20; i++, frameAxis += 3) + if ((i % 10) == 0) + p.drawLine( + o->horizontalLine(frameAxis, NumberRange(layerAxis - 3, layerAxis))); + else if (i & 1) + p.drawLine( + o->horizontalLine(frameAxis, NumberRange(layerAxis, layerAxis))); + else + p.drawLine( + o->horizontalLine(frameAxis, NumberRange(layerAxis - 2, layerAxis))); + } + else { + for (int i = 0; i <= 20; i++, frameAxis += 3) + if ((i % 10) == 0) + p.drawLine( + o->horizontalLine(frameAxis, NumberRange(layerAxis, layerAxis + 3))); + else if (i & 1) + p.drawLine( + o->horizontalLine(frameAxis, NumberRange(layerAxis, layerAxis))); + else + p.drawLine( + o->horizontalLine(frameAxis, NumberRange(layerAxis, layerAxis + 2))); + } + + // slider track + QPainterPath track = + o->path(PredefinedPath::VOLUME_SLIDER_TRACK).translated(orig); + p.drawPath(track); + + // cursor + QRect trackRect = o->rect(PredefinedRect::VOLUME_TRACK).translated(orig); + if (o->flipVolume()) volume = 1 - volume; + + layerAxis = o->layerSide(trackRect).middle(); + frameAxis = o->frameSide(trackRect).weight(volume); + QPoint cursor = o->frameLayerToXY(frameAxis, layerAxis); + QPainterPath head = + o->path(PredefinedPath::VOLUME_SLIDER_HEAD).translated(cursor); + p.fillPath(head, QBrush(Qt::white)); + p.setPen(m_viewer->getLightLineColor()); + p.drawPath(head); +} + //============================================================================= // ColumnArea //----------------------------------------------------------------------------- @@ -524,18 +955,11 @@ ColumnArea::ColumnArea(XsheetViewer *parent, Qt::WFlags flags) #endif : QWidget(parent, flags) , m_viewer(parent) - , m_indexBox(0, 3, ColumnWidth - RowHeight * 3 - 1, RowHeight) - , m_tabBox(ColumnWidth - RowHeight * 3 - 1, 3, RowHeight * 3, RowHeight + 1) - , m_nameBox(0, RowHeight + 3, ColumnWidth, RowHeight) - , m_linkBox(0, RowHeight * 7 + 3, 12, RowHeight) , m_pos(-1, -1) , m_tooltip(tr("")) , m_col(-1) , m_columnTransparencyPopup(0) , m_transparencyPopupTimer(0) - , m_prevViewBox(10, 6, ColumnWidth - 12, RowHeight - 3) - , m_tableViewBox(10, RowHeight + 6, ColumnWidth - 12, RowHeight - 3) - , m_lockBox(9, RowHeight + 6, RowHeight - 4, RowHeight - 4) , m_isPanning(false) { TXsheetHandle *xsheetHandle = TApp::instance()->getCurrentXsheet(); #ifndef LINETEST @@ -585,9 +1009,91 @@ void ColumnArea::setDragTool(DragTool *dragTool) { } //----------------------------------------------------------------------------- +void ColumnArea::drawFoldedColumnHead(QPainter &p, int col) { + const Orientation *o = m_viewer->orientation(); + + QPoint orig = m_viewer->positionToXY(CellPosition(0, col)); + QRect rect = o->rect(PredefinedRect::FOLDED_LAYER_HEADER).translated(orig); + + int x0, y0, x, y; + + if (o->isVerticalTimeline()) + { + x0 = rect.topLeft().x() + 1; + y0 = 0; + + p.setPen(m_viewer->getDarkLineColor()); + p.fillRect(x0 , y0 + 1, rect.width(), 18, QBrush(m_viewer->getDarkBGColor())); + p.fillRect(x0 , y0 + 17, 2, rect.height() - 34, QBrush(m_viewer->getLightLightBGColor())); + p.fillRect(x0 + 3, y0 + 20, 2, rect.height() - 36, QBrush(m_viewer->getLightLightBGColor())); + p.fillRect(x0 + 6, y0 + 17, 2, rect.height() - 34, QBrush(m_viewer->getLightLightBGColor())); + + p.setPen(Qt::black); + p.drawLine(x0 - 1, y0 + 17, x0 - 1, rect.height()); + p.setPen(m_viewer->getDarkLineColor()); + p.drawLine(x0 + 2, y0 + 17, x0 + 2, rect.height()); + p.drawLine(x0 + 5, y0 + 17, x0 + 5, rect.height()); + p.drawLine(x0 , y0 + 17, x0 + 1, 17); + p.drawLine(x0 + 3, y0 + 20, x0 + 4, 20); + p.drawLine(x0 + 6, y0 + 17, x0 + 7, 17); + + // triangolini + p.setPen(Qt::black); + x = x0; + y = 12; + p.drawPoint(QPointF(x, y)); + x++; + p.drawLine(x, y - 1, x, y + 1); + x++; + p.drawLine(x, y - 2, x, y + 2); + x += 3; + p.drawLine(x, y - 2, x, y + 2); + x++; + p.drawLine(x, y - 1, x, y + 1); + x++; + p.drawPoint(x, y); + } + else + { + x0 = 0; + y0 = rect.topLeft().y() + 1; + + p.setPen(m_viewer->getDarkLineColor()); + p.fillRect( x0 + 1, y0, 18, rect.height(), QBrush(m_viewer->getDarkBGColor())); + p.fillRect(x0 + 17, y0, rect.width() - 34, 2, QBrush(m_viewer->getLightLightBGColor())); + p.fillRect(x0 + 20, y0 + 3, rect.width() - 36, 2, QBrush(m_viewer->getLightLightBGColor())); + p.fillRect(x0 + 17, y0 + 6, rect.width() - 34, 2, QBrush(m_viewer->getLightLightBGColor())); + + p.setPen(Qt::black); + p.drawLine(x0 + 17, y0 - 1, rect.width(), y0 - 1); + p.setPen(m_viewer->getDarkLineColor()); + p.drawLine(x0 + 17, y0 + 2, rect.width(), y0 + 2); + p.drawLine(x0 + 17, y0 + 5, rect.width(), y0 + 5); + p.drawLine(x0 + 17, y0, 17, y0 + 1); + p.drawLine(x0 + 20, y0 + 3, 20, y0 + 4); + p.drawLine(x0 + 17, y0 + 6, 17, y0 + 7); + + // triangolini + p.setPen(Qt::black); + x = 12; + y = y0; + p.drawPoint(QPointF(x, y)); + y++; + p.drawLine(x - 1, y, x + 1, y); + y++; + p.drawLine(x - 2, y, x + 2, y); + y += 3; + p.drawLine(x - 2, y, x + 2, y); + y++; + p.drawLine(x - 1, y, x + 1, y); + y++; + p.drawPoint(x, y); + } +} void ColumnArea::drawLevelColumnHead(QPainter &p, int col) { TColumnSelection *selection = m_viewer->getColumnSelection(); + const Orientation *o = m_viewer->orientation(); // Preparing painter #ifdef _WIN32 @@ -602,9 +1108,10 @@ void ColumnArea::drawLevelColumnHead(QPainter &p, int col) { // Retrieve reference coordinates int currentColumnIndex = m_viewer->getCurrentColumn(); - int x = m_viewer->columnToX(col); + int layerAxis = m_viewer->columnToLayerAxis(col); - QRect rect(x, 0, ColumnWidth, height()); + QPoint orig = m_viewer->positionToXY(CellPosition(0, col)); + QRect rect = o->rect(PredefinedRect::LAYER_HEADER).translated(orig); TApp *app = TApp::instance(); TXsheet *xsh = m_viewer->getXsheet(); @@ -612,30 +1119,14 @@ void ColumnArea::drawLevelColumnHead(QPainter &p, int col) { TStageObjectId columnId = m_viewer->getObjectId(col); TStageObjectId currentColumnId = app->getCurrentObject()->getObjectId(); TStageObjectId parentId = xsh->getStageObjectParent(columnId); - TStageObject *columnObject = xsh->getStageObject(columnId); - - // Build column name - std::string name(columnObject->getName()); - - if (col < 0) name = std::string("Camera"); // Retrieve column properties - bool isEmpty = false; - if (col >= 0) // Verifico se la colonna e' vuota - isEmpty = xsh->isColumnEmpty(col); + // Check if the column is empty + bool isEmpty = col >= 0 && xsh->isColumnEmpty(col); + TXshColumn *column = col >= 0 ? xsh->getColumn(col) : 0; bool isEditingSpline = app->getCurrentObject()->isSpline(); - // check if the column is reference - TXshColumn *column = col >= 0 ? xsh->getColumn(col) : 0; - enum { Normal, Reference, Control } usage = Reference; - if (column) { - if (column->isControl()) usage = Control; - if (column->isRendered() || column->getMeshColumn()) usage = Normal; - } - - bool isLocked = column != 0 && column->isLocked(); - // check if the column is current bool isCurrent = false; if (currentColumnId == TStageObjectId::CameraId(0)) // CAMERA @@ -648,159 +1139,30 @@ void ColumnArea::drawLevelColumnHead(QPainter &p, int col) { bool isCameraSelected = col == -1 && isCurrent && !isEditingSpline; // Draw column - QPoint orig = rect.topLeft(); - - QPoint columnNamePos = orig + QPoint(12, RowHeight); - QPoint pegbarNamePos = orig + QPoint(12, RowHeight * 3 + 48); - QPoint handleNamePos = - orig + - QPoint(ColumnWidth - 10 - p.fontMetrics().width('B'), RowHeight * 3 + 48); - - int x0 = rect.x(); - int x1 = x0 + rect.width() - 1; - int y0 = rect.y(); - int y1 = y0 + rect.height() - 1; - - // fill base color - if (isEmpty || col < 0) { - p.fillRect(rect, m_viewer->getEmptyColumnHeadColor()); - - p.setPen(m_viewer->getVerticalLineHeadColor()); - p.drawLine(x0, y0, x0, y1); - } else { - QColor columnColor, sideColor; - if (usage == Reference) { - columnColor = m_viewer->getReferenceColumnColor(); - sideColor = m_viewer->getReferenceColumnBorderColor(); - } else - m_viewer->getColumnColor(columnColor, sideColor, col, xsh); - p.fillRect(rect, sideColor); - p.fillRect(rect.adjusted(7, 3, 0, 0), columnColor); - - // column handle - QRect sideBar(x0, y0, 7, rect.height() - 5); - if (sideBar.contains(m_pos)) { - p.fillRect(sideBar, Qt::yellow); - } - } - - QColor pastelizer(m_viewer->getColumnHeadPastelizer()); - pastelizer.setAlpha(50); - - QColor colorSelection(m_viewer->getSelectedColumnHead()); - colorSelection.setAlpha(170); - p.fillRect(rect, - (isSelected || isCameraSelected) ? colorSelection : pastelizer); - - int prevViewImgHeight = RowHeight - 5; - int prevViewImgWidth = prevViewImgHeight * 5 / 4; - - QRect prevViewRect = m_prevViewBox.translated(orig); - QRect prevViewImgRect(prevViewRect.right() - prevViewImgWidth - 1, 7, - prevViewImgWidth, prevViewImgHeight); - static QPixmap prevViewPix = QPixmap(":Resources/x_prev_eye.png"); - - QRect tableViewRect = m_tableViewBox.translated(orig); - QRect tableViewImgRect = prevViewImgRect.translated(0, RowHeight); - static QPixmap tableViewPix = QPixmap(":Resources/x_table_view.png"); - - static QPixmap tableTranspViewPix = - QPixmap(":Resources/x_table_view_transp.png"); - - QRect lockModeRect = m_lockBox.translated(orig); - static QPixmap lockModePix = QPixmap(":Resources/x_lock.png"); - - if (col >= 0 && !isEmpty) { - // preview visible toggle - if (column->isPreviewVisible()) { - p.fillRect(prevViewRect, PreviewVisibleColor); - p.drawPixmap(prevViewImgRect, prevViewPix); - } - // camstand visible toggle - if (column->isCamstandVisible()) { - p.fillRect(tableViewRect, CamStandVisibleColor); - p.drawPixmap(tableViewImgRect, column->getOpacity() < 255 - ? tableTranspViewPix - : tableViewPix); - } - - // lock button - p.setPen(Qt::gray); - p.setBrush(QColor(255, 255, 255, 128)); - p.drawRect(lockModeRect); - lockModeRect.adjust(1, 1, -1, -1); - if (isLocked) { - p.drawPixmap(lockModeRect, lockModePix); - } - } - - // column number - if (!isEmpty) - p.setPen((isCurrent) ? Qt::red : Qt::black); - else - p.setPen((isCurrent) ? m_viewer->getSelectedColumnTextColor() - : m_viewer->getTextColor()); - - p.drawText(columnNamePos, QString(name.c_str())); - - p.setPen(m_viewer->getTextColor()); - - if (col >= 0 && !isEmpty) { - // pegbar name - p.drawText(pegbarNamePos, QString(parentId.toString().c_str())); - - std::string handle = xsh->getStageObject(columnId)->getParentHandle(); - if (handle[0] == 'H' && handle.length() > 1) handle = handle.substr(1); - if (parentId != TStageObjectId::TableId) - p.drawText(handleNamePos, QString::fromStdString(handle)); - - // thumbnail - QRect thumbnailRect(orig.x() + 9, orig.y() + RowHeight * 2 + 7, - ColumnWidth - 11, 42); - - // for zerary fx, display fxId here instead of thumbnail - TXshZeraryFxColumn *zColumn = dynamic_cast(column); - if (zColumn) { - QFont font("Verdana", 8); - p.setFont(font); - - TFx *fx = zColumn->getZeraryColumnFx()->getZeraryFx(); - QString fxName = QString::fromStdWString(fx->getFxId()); - p.drawText(thumbnailRect, Qt::TextWrapAnywhere | Qt::TextWordWrap, - fxName); - } else { - TXshLevelColumn *levelColumn = column->getLevelColumn(); - - if (levelColumn && - Preferences::instance()->getColumnIconLoadingPolicy() == - Preferences::LoadOnDemand && - !levelColumn->isIconVisible()) { - // display nothing - } else { - QPixmap iconPixmap = getColumnIcon(col); - if (!iconPixmap.isNull()) { - p.drawPixmap(thumbnailRect, iconPixmap); - } - // notify that the column icon is already shown - if (levelColumn) levelColumn->setIconVisible(true); - } - - // filter color - if (column->getFilterColorId() != 0) { - QRect filterColorRect(thumbnailRect.topRight().x() - 14, - thumbnailRect.topRight().y(), 14, 14); - p.fillRect(filterColorRect, Qt::white); - p.drawPixmap( - filterColorRect.adjusted(2, 2, -2, -2), - getColorChipIcon(column->getFilterColorId()).pixmap(12, 12)); - } - } - } + DrawHeader drawHeader(this, p, col); + drawHeader.prepare(); + QColor columnColor, dragColor; + drawHeader.levelColors(columnColor, dragColor); + drawHeader.drawBaseFill(columnColor, dragColor); + drawHeader.drawEye(); + drawHeader.drawPreviewToggle(column ? column->getOpacity() : 0); + drawHeader.drawLock(); + drawHeader.drawColumnName(); + drawHeader.drawColumnNumber(); + QPixmap iconPixmap = getColumnIcon(col); + drawHeader.drawThumbnail(iconPixmap); + drawHeader.drawPegbarName(); + drawHeader.drawParentHandleName(); + drawHeader.drawFilterColor(); } + //----------------------------------------------------------------------------- -void ColumnArea::drawSoundColumnHead(QPainter &p, int col) { +void ColumnArea::drawSoundColumnHead(QPainter &p, int col) { // AREA TColumnSelection *selection = m_viewer->getColumnSelection(); + const Orientation *o = m_viewer->orientation(); + + int x = m_viewer->columnToLayerAxis(col); p.setRenderHint(QPainter::SmoothPixmapTransform, true); #ifdef _WIN32 @@ -811,151 +1173,45 @@ void ColumnArea::drawSoundColumnHead(QPainter &p, int col) { font.setPixelSize(XSHEET_FONT_PX_SIZE); p.setFont(font); - int x = m_viewer->columnToX(col); - TXsheet *xsh = m_viewer->getXsheet(); TXshSoundColumn *sc = xsh->getColumn(col) ? xsh->getColumn(col)->getSoundColumn() : 0; - QRect rect(x, 0, ColumnWidth, height()); + QPoint orig = m_viewer->positionToXY(CellPosition(0, col)); + QRect rect = m_viewer->orientation() + ->rect(PredefinedRect::LAYER_HEADER) + .translated(orig); - QPoint orig = rect.topLeft(); - QPoint columnNamePos = orig + QPoint(12, RowHeight); + QPoint columnNamePos = orig + QPoint(12, o->cellHeight()); - bool isEmpty = xsh->isColumnEmpty(col); - bool isCurrent = m_viewer->getCurrentColumn() == col; - bool isSelected = m_viewer->getColumnSelection()->isColumnSelected(col); - bool isPrecSelected = - col > 0 ? m_viewer->getColumnSelection()->isColumnSelected(col - 1) - : false; - bool isActive = sc && sc->isPreviewVisible(); - bool isLocked = sc && sc->isLocked(); - bool isLeftBorderHighlighted = isSelected || isPrecSelected; + bool isCurrent = m_viewer->getCurrentColumn() == col; - int x0 = rect.x(); - int x1 = x0 + rect.width() - 1; - int y0 = rect.y(); - int y1 = y0 + rect.height() - 1; - // base color - if (isEmpty || col < 0) { - p.fillRect(rect, EmptyColumnHeadColor); - p.setPen(m_viewer->getVerticalLineHeadColor()); - p.drawLine(x0, y0, x0, y1); - } else { - QColor columnColor, sideColor; - - m_viewer->getColumnColor(columnColor, sideColor, col, xsh); - - p.fillRect(rect, sideColor); - p.fillRect(rect.adjusted(7, 3, 0, 0), columnColor); - - // column handle - QRect sideBar(x0, y0, 7, rect.height() - 5); - if (sideBar.contains(m_pos)) { - p.fillRect(sideBar, Qt::yellow); - } - } - - QColor pastelizer(m_viewer->getColumnHeadPastelizer()); - pastelizer.setAlpha(50); - - QColor colorSelection(m_viewer->getSelectedColumnHead()); - colorSelection.setAlpha(170); - p.fillRect(rect, (isSelected) ? colorSelection : pastelizer); - - int prevViewImgHeight = RowHeight - 5; - int prevViewImgWidth = prevViewImgHeight * 5 / 4; - - QRect prevViewRect = m_prevViewBox.translated(orig); - QRect prevViewImgRect(prevViewRect.right() - prevViewImgWidth - 1, 7, - prevViewImgWidth, prevViewImgHeight); - static QPixmap prevViewPix = QPixmap(":Resources/x_prev_eye.png"); - - QRect tableViewRect = m_tableViewBox.translated(orig); - QRect tableViewImgRect = prevViewImgRect.translated(0, RowHeight); - static QPixmap tableViewPix = QPixmap(":Resources/x_table_view.png"); - static QPixmap tableTranspViewPix = - QPixmap(":Resources/x_table_view_transp.png"); - - QRect lockModeRect = m_lockBox.translated(orig); - static QPixmap lockModePix = QPixmap(":Resources/x_lock.png"); - - if (col >= 0 && !isEmpty) { - // preview visible toggle - if (isActive) { - p.fillRect(prevViewRect, PreviewVisibleColor); - p.drawPixmap(prevViewImgRect, prevViewPix); - } - // camstand visible toggle - if (sc->isCamstandVisible()) { - p.fillRect(tableViewRect, CamStandVisibleColor); - p.drawPixmap(tableViewImgRect, tableViewPix); - } - - // lock button - p.setPen(Qt::gray); - p.setBrush(QColor(255, 255, 255, 128)); - p.drawRect(lockModeRect); - lockModeRect.adjust(1, 1, -1, -1); - if (isLocked) { - p.drawPixmap(lockModeRect, lockModePix); - } - } - - // column number - p.setPen((isCurrent) ? Qt::red : Qt::black); - p.drawText(columnNamePos, QString(std::to_string(col + 1).c_str())); - - // Icona sound - if (sc->isPlaying()) { - static QPixmap soundActiveIcon = QPixmap(":Resources/sound_header_on.png"); - p.drawPixmap(x + 29, 3 * RowHeight + 4, 40, 30, soundActiveIcon); - } else { - static QPixmap soundIcon = QPixmap(":Resources/sound_header_off.png"); - p.drawPixmap(x + 29, 3 * RowHeight + 4, 40, 30, soundIcon); - } - - QRect rr(rect.x() + 8, RowHeight * 2 + 3, rect.width() - 7, m_tabBox.y() - 3); - - // suddivisioni slider - p.setPen(m_viewer->getTextColor()); - int xa = rr.x() + 7, ya = rr.y() + 4; - int y = ya; - for (int i = 0; i <= 20; i++, y += 3) - if ((i % 10) == 0) - p.drawLine(xa - 3, y, xa, y); - else if (i & 1) - p.drawLine(xa, y, xa, y); - else - p.drawLine(xa - 2, y, xa, y); - - // slider - int ly = 60; - xa += 5; - p.drawPoint(xa, ya); - p.drawPoint(xa, ya + ly); - p.drawLine(xa - 1, ya + 1, xa - 1, ya + ly - 1); - p.drawLine(xa + 1, ya + 1, xa + 1, ya + ly - 1); - - // cursore - QRect cursorRect; - getVolumeCursorRect(cursorRect, sc->getVolume(), rr.topLeft()); - - std::vector pts; - x = cursorRect.x(); - y = cursorRect.y() + 4; - pts.push_back(QPointF(x, y)); - pts.push_back(QPointF(x + 4.0, y + 4.0)); - pts.push_back(QPointF(x + 8.0, y + 4.0)); - pts.push_back(QPointF(x + 8.0, y - 4.0)); - pts.push_back(QPointF(x + 4.0, y - 4.0)); - drawPolygon(p, pts, true, m_viewer->getLightLineColor()); + DrawHeader drawHeader(this, p, col); + drawHeader.prepare(); + QColor columnColor, dragColor; + drawHeader.soundColors(columnColor, dragColor); + drawHeader.drawBaseFill(columnColor, dragColor); + drawHeader.drawEye(); + drawHeader.drawPreviewToggle(255); + drawHeader.drawLock(); + drawHeader.drawColumnName(); + drawHeader.drawColumnNumber(); + // Sound columns don't have an image. Passing in an image + // for arguement, but it will be ignored. + static QPixmap iconignored; + drawHeader.drawThumbnail(iconignored); + drawHeader.drawPegbarName(); + drawHeader.drawParentHandleName(); + drawHeader.drawFilterColor(); } //----------------------------------------------------------------------------- -void ColumnArea::drawPaletteColumnHead(QPainter &p, int col) { +void ColumnArea::drawPaletteColumnHead(QPainter &p, int col) { // AREA TColumnSelection *selection = m_viewer->getColumnSelection(); + const Orientation *o = m_viewer->orientation(); + + QPoint orig = m_viewer->positionToXY(CellPosition(0, max(col, 0))); #ifdef _WIN32 static QFont font("Arial", -1, QFont::Normal); @@ -968,130 +1224,40 @@ void ColumnArea::drawPaletteColumnHead(QPainter &p, int col) { p.setRenderHint(QPainter::SmoothPixmapTransform, true); int currentColumnIndex = m_viewer->getCurrentColumn(); - int x = m_viewer->columnToX(col); + int x = m_viewer->columnToLayerAxis(col); - QRect rect(x, 0, ColumnWidth, height()); + QRect rect(x, 0, o->cellWidth(), height()); - TApp *app = TApp::instance(); TXsheet *xsh = m_viewer->getXsheet(); - TStageObjectId columnId = m_viewer->getObjectId(col); - TStageObjectId currentColumnId = app->getCurrentObject()->getObjectId(); - TStageObjectId parentId = xsh->getStageObjectParent(columnId); - - std::string name = xsh->getStageObject(columnId)->getName(); - bool isEmpty = false; if (col >= 0) // Verifico se la colonna e' vuota - isEmpty = xsh->isColumnEmpty(col); - bool isEditingSpline = app->getCurrentObject()->isSpline(); + isEmpty = xsh->isColumnEmpty(col); - TXshColumn *column = col >= 0 ? xsh->getColumn(col) : 0; - enum { Normal, Reference, Control } usage = Reference; - if (column) { // Verifico se la colonna e' una mask - if (column->isControl()) usage = Control; - if (column->isRendered()) usage = Normal; - } - - bool isLocked = column != 0 && column->isLocked(); - - bool isCurrent = false; - if (currentColumnId == TStageObjectId::CameraId(0)) // CAMERA - isCurrent = col == -1; - else - isCurrent = m_viewer->getCurrentColumn() == col; - - bool isSelected = - m_viewer->getColumnSelection()->isColumnSelected(col) && !isEditingSpline; - bool isCameraSelected = col == -1 && isCurrent && !isEditingSpline; - - QPoint orig = rect.topLeft(); - QPoint columnNamePos = orig + QPoint(12, RowHeight); - QPoint pegbarNamePos = orig + QPoint(12, RowHeight * 3 + 48); - QPoint handleNamePos = - orig + - QPoint(ColumnWidth - 10 - p.fontMetrics().width('B'), RowHeight * 3 + 48); - - int x0 = rect.x(); - int x1 = x0 + rect.width() - 1; - int y0 = rect.y(); - int y1 = y0 + rect.height() - 1; - - // fill base color - if (isEmpty || col < 0) { - p.fillRect(rect, EmptyColumnHeadColor); - p.setPen(m_viewer->getVerticalLineHeadColor()); - p.drawLine(x0, y0, x0, y1); - } else { - QColor columnColor, sideColor; - if (usage == Reference) { - columnColor = m_viewer->getReferenceColumnColor(); - sideColor = m_viewer->getReferenceColumnBorderColor(); - } else { - columnColor = m_viewer->getPaletteColumnColor(); - sideColor = m_viewer->getPaletteColumnBorderColor(); - } - p.fillRect(rect, sideColor); - p.fillRect(rect.adjusted(7, 3, 0, 0), columnColor); - - // column handle - QRect sideBar(x0, y0, 7, rect.height() - 5); - if (sideBar.contains(m_pos)) { - p.fillRect(sideBar, Qt::yellow); - } - } - - QColor pastelizer(m_viewer->getColumnHeadPastelizer()); - pastelizer.setAlpha(50); - - p.fillRect(rect, - (isSelected || isCameraSelected) ? ColorSelection : pastelizer); - - int prevViewImgHeight = RowHeight - 5; - int prevViewImgWidth = prevViewImgHeight * 5 / 4; - - QRect prevViewRect = m_prevViewBox.translated(orig); - QRect prevViewImgRect(prevViewRect.right() - prevViewImgWidth - 1, 7, - prevViewImgWidth, prevViewImgHeight); - static QPixmap prevViewPix = QPixmap(":Resources/x_prev_eye.png"); - - QRect lockModeRect = m_lockBox.translated(orig); - static QPixmap lockModePix = QPixmap(":Resources/x_lock.png"); - - if (col >= 0 && !isEmpty) { - // preiew visible toggle - if (column->isPreviewVisible()) { - p.fillRect(prevViewRect, PreviewVisibleColor); - p.drawPixmap(prevViewImgRect, prevViewPix); - } - - // lock button - p.setPen(Qt::gray); - p.setBrush(QColor(255, 255, 255, 128)); - p.drawRect(lockModeRect); - lockModeRect.adjust(1, 1, -1, -1); - if (isLocked) { - p.drawPixmap(lockModeRect, lockModePix); - } - } - - // column number - p.setPen((isCurrent) ? Qt::red : Qt::black); - p.drawText(columnNamePos, QString(name.c_str())); - - p.setPen(Qt::black); - if (col >= 0 && !isEmpty) { - static QPixmap paletteHeader(":Resources/palette_header.png"); - QRect thumbnailRect(orig.x() + 9, orig.y() + RowHeight * 2 + 7, - ColumnWidth - 11, 42); - p.drawPixmap(thumbnailRect, paletteHeader); - } + DrawHeader drawHeader(this, p, col); + drawHeader.prepare(); + QColor columnColor, dragColor; + drawHeader.paletteColors(columnColor, dragColor); + drawHeader.drawBaseFill(columnColor, dragColor); + drawHeader.drawEye(); + drawHeader.drawPreviewToggle(0); + drawHeader.drawLock(); + drawHeader.drawColumnName(); + drawHeader.drawColumnNumber(); + static QPixmap iconPixmap(":Resources/palette_header.png"); + drawHeader.drawThumbnail(iconPixmap); + drawHeader.drawPegbarName(); + drawHeader.drawParentHandleName(); + drawHeader.drawFilterColor(); } //----------------------------------------------------------------------------- -void ColumnArea::drawSoundTextColumnHead(QPainter &p, int col) { +void ColumnArea::drawSoundTextColumnHead(QPainter &p, int col) { // AREA TColumnSelection *selection = m_viewer->getColumnSelection(); + const Orientation *o = m_viewer->orientation(); + + int x = m_viewer->columnToLayerAxis(col); p.setRenderHint(QPainter::SmoothPixmapTransform, true); #ifdef _WIN32 @@ -1102,10 +1268,7 @@ void ColumnArea::drawSoundTextColumnHead(QPainter &p, int col) { font.setPixelSize(XSHEET_FONT_PX_SIZE); p.setFont(font); - int x = m_viewer->columnToX(col); - QRect rect(x, 0, ColumnWidth, height()); - - int x0, x1, y, y0, y1; + QRect rect(x, 0, o->cellWidth(), height()); TApp *app = TApp::instance(); TXsheet *xsh = m_viewer->getXsheet(); @@ -1115,108 +1278,35 @@ void ColumnArea::drawSoundTextColumnHead(QPainter &p, int col) { bool isEditingSpline = app->getCurrentObject()->isSpline(); - // Verifico se la colonna e' lockata se e' quella corrente e se e' selezionata + // Check if column is locked and selected TXshColumn *column = col >= 0 ? xsh->getColumn(col) : 0; bool isLocked = column != 0 && column->isLocked(); bool isCurrent = m_viewer->getCurrentColumn() == col; bool isSelected = m_viewer->getColumnSelection()->isColumnSelected(col) && !isEditingSpline; - QPoint orig = rect.topLeft(); - x0 = rect.x() + 1; - x1 = orig.x() + m_tabBox.x() + m_tabBox.width(); - y = orig.y() + m_tabBox.height(); - - static QPixmap header(":Resources/magpie.png"); - int iconW = header.width(); - int iconH = header.height(); - QRect iconBox(orig.x() + m_nameBox.x(), - orig.y() + m_nameBox.y() + m_nameBox.height() + 2, iconW + 1, - iconH + 1); - p.drawPixmap(iconBox, header); - - bool isPrecedentColSelected = - selection->isColumnSelected(col - 1) && !isEditingSpline; - // bordo sinistro - if (isSelected || col > 0 && isPrecedentColSelected) { - p.setPen(ColorSelection); - p.drawLine(rect.x(), orig.y(), rect.x(), height()); - p.setPen(m_viewer->getDarkLineColor()); - p.drawLine(rect.x(), orig.y(), rect.x(), y + 2); - } else { - p.setPen(m_viewer->getDarkLineColor()); - p.drawLine(rect.x(), orig.y(), rect.x(), height()); - } - - if (col >= 0) { - // sfondo della parte indice - QRect indexBox(orig.x() + 1, orig.y(), m_indexBox.width(), - m_indexBox.height() + 3); - p.fillRect(indexBox, m_viewer->getDarkBGColor()); - // indice colonna in alto a sinistra - p.setPen(isCurrent ? Qt::red : Qt::black); - p.drawText(indexBox.adjusted(0, 2, -2, 0), Qt::AlignRight, - QString(std::to_string(col + 1).c_str())); - - x0 = orig.x() + m_tabBox.x() + 1; - int x1 = x0 + RowHeight; - int x2 = x0 + 2 * RowHeight; - int x3 = x0 + 3 * RowHeight + 2; - y0 = orig.y() + m_tabBox.y(); - y1 = orig.y() + m_tabBox.height() + 1; - // Sfondo dei due bottoni che non vengono mostrati - p.fillRect(QRect(x0, y0, 2 * RowHeight, RowHeight), - m_viewer->getDarkBGColor()); - p.setPen(m_viewer->getDarkBGColor()); - p.drawLine(x0, y0 - 1, x3, y0 - 1); - p.drawLine(x0, y0 - 2, x3, y0 - 2); - - // Linea di separazione tra indice e nome - p.setPen(m_viewer->getDarkLineColor()); - p.drawLine(orig.x(), y1 + 1, orig.x() + ColumnWidth, y1 + 1); - // contorno del bottone in alto a dx - p.drawLine(x2, y0, x2, y1); - p.drawLine(x2, y0, x3, y0); - - // lucchetto - QRect lockBox(x2 + 1, y0 + 1, 11, 11); - p.fillRect(lockBox, QBrush(m_viewer->getLightLightBGColor())); - if (isLocked) { - static QPixmap lockMode = QPixmap(":Resources/lock_toggle.png"); - p.drawPixmap(lockBox, lockMode); - } - } - - // nome colonna - QColor cellColor = m_viewer->getLightLightBGColor(); - QColor dummyColor; - m_viewer->getColumnColor(cellColor, dummyColor, col, xsh); - QRect nameBox(orig.x() + m_nameBox.x() + 1, orig.y() + m_nameBox.y() + 1, - m_nameBox.width() - 1, m_nameBox.height()); - QColor columnColor = (isSelected) ? ColorSelection : cellColor; - p.fillRect(nameBox, QBrush(columnColor)); - p.setPen(isCurrent ? Qt::red : Qt::black); - p.drawText(nameBox.adjusted(3, -1, -3, 0), Qt::AlignLeft, - QString(name.c_str())); // Adjusted to match with lineEdit - - // separazione fra nome e icona - p.setPen(isSelected ? ColorSelection : m_viewer->getLightLineColor()); - x0 = nameBox.x(); - x1 = x0 + nameBox.width(); - y0 = nameBox.y() + nameBox.height(); - p.drawLine(x0, y0, x1, y0); - - if (isSelected) { - QRect box(x0, y0 + 1, ColumnWidth, height() - 3 * RowHeight - 6); - QRect adjustBox = box.adjusted(0, 0, -2, -1); - p.setPen(ColorSelection); - p.drawRect(adjustBox); - } + DrawHeader drawHeader(this, p, col); + drawHeader.prepare(); + QColor columnColor, dragColor; + drawHeader.paletteColors(columnColor, dragColor); + drawHeader.drawBaseFill(columnColor, dragColor); + drawHeader.drawEye(); + drawHeader.drawPreviewToggle(255); + drawHeader.drawLock(); + drawHeader.drawColumnName(); + drawHeader.drawColumnNumber(); + static QPixmap iconPixmap(":Resources/magpie.png"); + drawHeader.drawThumbnail(iconPixmap); + drawHeader.drawPegbarName(); + drawHeader.drawParentHandleName(); + drawHeader.drawFilterColor(); } //----------------------------------------------------------------------------- QPixmap ColumnArea::getColumnIcon(int columnIndex) { + const Orientation *o = m_viewer->orientation(); + if (columnIndex == -1) { // Indice colonna = -1 -> CAMERA TApp *app = TApp::instance(); static QPixmap camera = QPixmap(":Resources/camera.png"); @@ -1241,67 +1331,34 @@ QPixmap ColumnArea::getColumnIcon(int columnIndex) { IconGenerator::instance()->getIcon(xl, cell.m_frameId, false, onDemand); #ifndef LINETEST return scalePixmapKeepingAspectRatio( - icon, QSize(ColumnWidth, height() - 3 * RowHeight - 8)); + icon, QSize(o->cellWidth(), height() - 3 * o->cellHeight() - 8)); #else return scalePixmapKeepingAspectRatio( - icon, QSize(ColumnWidth, height() - 4 * RowHeight - 8)); + icon, QSize(o->cellWidth(), height() - 4 * o->cellHeight() - 8)); #endif } } //----------------------------------------------------------------------------- -void ColumnArea::paintEvent(QPaintEvent *event) { +void ColumnArea::paintEvent(QPaintEvent *event) { // AREA QRect toBeUpdated = event->rect(); QPainter p(this); p.setClipRect(toBeUpdated); - int c0, c1; // range di righe visibili - c0 = m_viewer->xToColumn(toBeUpdated.left()); - c1 = m_viewer->xToColumn(toBeUpdated.right()); + CellRange cellRange = m_viewer->xyRectToRange(toBeUpdated); + int c0, c1; // range of visible columns + c0 = cellRange.from().layer(); + c1 = cellRange.to().layer(); TXsheet *xsh = m_viewer->getXsheet(); - ColumnFan *columnFan = xsh->getColumnFan(); + ColumnFan *columnFan = xsh->getColumnFan(m_viewer->orientation()); int col; for (col = c0; col <= c1; col++) { // draw column fan (collapsed columns) if (!columnFan->isActive(col)) { - int x = m_viewer->columnToX(col); - QRect rect(x, 0, 8, height()); - int x0 = rect.topLeft().x() + 1; - int y = 16; - - p.setPen(m_viewer->getDarkLineColor()); - p.fillRect(x0, 0, 8, 18, QBrush(m_viewer->getDarkBGColor())); - p.fillRect(x0, y, 2, 84, QBrush(m_viewer->getLightLightBGColor())); - p.fillRect(x0 + 3, y + 3, 2, 82, - QBrush(m_viewer->getLightLightBGColor())); - p.fillRect(x0 + 6, y, 2, 84, QBrush(m_viewer->getLightLightBGColor())); - - p.setPen(m_viewer->getDarkLineColor()); - p.drawLine(x0 - 1, y, x0 - 1, rect.height()); - p.drawLine(x0 + 2, y, x0 + 2, rect.height()); - p.drawLine(x0 + 5, y, x0 + 5, rect.height()); - p.drawLine(x0, y, x0 + 1, y); - p.drawLine(x0 + 3, y + 3, x0 + 4, y + 3); - p.drawLine(x0 + 6, y, x0 + 7, y); - - // triangolini - p.setPen(Qt::black); - x = x0; - y = y - 4; - p.drawPoint(QPointF(x, y)); - x++; - p.drawLine(x, y - 1, x, y + 1); - x++; - p.drawLine(x, y - 2, x, y + 2); - x += 3; - p.drawLine(x, y - 2, x, y + 2); - x++; - p.drawLine(x, y - 1, x, y + 1); - x++; - p.drawPoint(x, y); + drawFoldedColumnHead(p, col); } else if (col >= 0) { TXshColumn *column = m_viewer->getXsheet()->getColumn(col); @@ -1327,9 +1384,13 @@ void ColumnArea::paintEvent(QPaintEvent *event) { p.setPen(grey150); p.setBrush(Qt::NoBrush); - p.drawRect(toBeUpdated.adjusted(0, 0, -1, -1)); + if (m_viewer->orientation()->isVerticalTimeline()) + p.drawRect(toBeUpdated.adjusted(0, 0, -1, -3)); + else + p.drawRect(toBeUpdated.adjusted(0, 0, -3, -1)); if (getDragTool()) getDragTool()->drawColumnsArea(p); + } //----------------------------------------------------------------------------- @@ -1498,15 +1559,12 @@ void ColumnArea::openTransparencyPopup() { //---------------------------------------------------------------- -void ColumnArea::startTransparencyPopupTimer(QMouseEvent *e) { +void ColumnArea::startTransparencyPopupTimer(QMouseEvent *e) { // AREA if (!m_columnTransparencyPopup) m_columnTransparencyPopup = new ColumnTransparencyPopup( this); // Qt::ToolTip|Qt::MSWindowsFixedSizeDialogHint);//Qt::MSWindowsFixedSizeDialogHint|Qt::Tool); - int x = e->pos().x() - m_tabBox.left() - m_viewer->columnToX(m_col); - int y = e->pos().y() - m_tabBox.bottom(); - m_columnTransparencyPopup->move(e->globalPos().x() - x + 2, - e->globalPos().y() - y + 1); + m_columnTransparencyPopup->move(e->globalPos().x(), e->globalPos().y()); if (!m_transparencyPopupTimer) { m_transparencyPopupTimer = new QTimer(this); @@ -1522,6 +1580,8 @@ void ColumnArea::startTransparencyPopupTimer(QMouseEvent *e) { //---------------------------------------------------------------- void ColumnArea::mousePressEvent(QMouseEvent *event) { + const Orientation *o = m_viewer->orientation(); + m_doOnRelease = 0; m_viewer->setQtModifiers(event->modifiers()); assert(getDragTool() == 0); @@ -1530,8 +1590,9 @@ void ColumnArea::mousePressEvent(QMouseEvent *event) { // both left and right click can change the selection if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) { - TXsheet *xsh = m_viewer->getXsheet(); - m_col = m_viewer->xToColumn(event->pos().x()); + TXsheet *xsh = m_viewer->getXsheet(); + ColumnFan *fan = xsh->getColumnFan(o); + m_col = m_viewer->xyToPosition(event->pos()).layer(); // do nothing for the camera column if (m_col < 0) // CAMERA { @@ -1539,14 +1600,14 @@ void ColumnArea::mousePressEvent(QMouseEvent *event) { m_viewer->getColumnSelection()->selectNone(); } // when clicking the column fan - else if (m_col >= 0 && !xsh->getColumnFan()->isActive(m_col)) // column Fan + else if (m_col >= 0 && !fan->isActive(m_col)) // column Fan { - for (;;) { - xsh->getColumnFan()->activate(m_col); - TApp::instance()->getCurrentScene()->setDirtyFlag(true); - m_col--; - if (m_col < 0 || xsh->getColumnFan()->isActive(m_col)) break; + for (auto o : Orientations::all()) { + fan = xsh->getColumnFan(o); + for (int i = m_col; i >= 0 && !fan->isActive(i); i--) fan->activate(i); } + + TApp::instance()->getCurrentScene()->setDirtyFlag(true); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); return; } @@ -1559,40 +1620,54 @@ void ColumnArea::mousePressEvent(QMouseEvent *event) { TApp::instance()->getCurrentObject()->setIsSpline(false); // get mouse position - int x = event->pos().x() - m_viewer->columnToX(m_col); - int y = event->pos().y(); - QPoint mousePos(x, y); + QPoint mouseInCell = + event->pos() - m_viewer->positionToXY(CellPosition(0, m_col)); + // int x = event->pos().x() - m_viewer->columnToX(m_col); + // int y = event->pos().y(); + // QPoint mouseInCell(x, y); + int x = mouseInCell.x(), y = mouseInCell.y(); if (!isEmpty && m_col >= 0) { // grabbing the left side of the column enables column move - if (x <= 7) { + if (o->rect(PredefinedRect::DRAG_LAYER).contains(mouseInCell)) { setDragTool(XsheetGUI::DragTool::makeColumnMoveTool(m_viewer)); } // lock button - else if (m_lockBox.contains(mousePos) && + else if (o->rect(PredefinedRect::LOCK_AREA).contains(mouseInCell) && event->button() == Qt::LeftButton) { - m_doOnRelease = ToggleLock; + m_doOnRelease = isCtrlPressed ? ToggleAllLock : ToggleLock; } // preview button - else if (m_prevViewBox.contains(mousePos) && + else if (o->rect(PredefinedRect::EYE_AREA).contains(mouseInCell) && event->button() == Qt::LeftButton) { - m_doOnRelease = TogglePreviewVisible; - if (column->getSoundColumn()) - TApp::instance()->getCurrentXsheet()->notifyXsheetSoundChanged(); - } + if (column->getPaletteColumn() || column->getSoundTextColumn()) { + // do nothing + } + else { + m_doOnRelease = isCtrlPressed ? ToggleAllPreviewVisible : TogglePreviewVisible; + if (column->getSoundColumn()) + TApp::instance()->getCurrentXsheet()->notifyXsheetSoundChanged(); + } + } // camstand button - else if (m_tableViewBox.contains(mousePos) && + else if (o->rect(PredefinedRect::PREVIEW_LAYER_AREA) + .contains(mouseInCell) && event->button() == Qt::LeftButton) { - m_doOnRelease = ToggleTransparency; - if (column->getSoundColumn()) { - // do nothing - } else - startTransparencyPopupTimer(event); + if (column->getPaletteColumn() || column->getSoundTextColumn()) { + // do nothing + } + else { + m_doOnRelease = isCtrlPressed ? ToggleAllTransparency : ToggleTransparency; + if (column->getSoundColumn()) { + // do nothing + } + else + startTransparencyPopupTimer(event); + } } - // sound column else if (column && column->getSoundColumn()) { - if (x > 29 && 3 * RowHeight + 5 <= y && y < 3 * RowHeight + 33) { + if (o->rect(PredefinedRect::SOUND_ICON).contains(mouseInCell)) { TXshSoundColumn *s = column->getSoundColumn(); if (s) { if (s->isPlaying()) @@ -1611,19 +1686,11 @@ void ColumnArea::mousePressEvent(QMouseEvent *event) { if (s->isPlaying() && interval > 0) QTimer::singleShot(interval, this, SLOT(update())); } - int x0 = m_viewer->columnToX(m_col); - int x1 = m_viewer->columnToX(m_col + 1); update(); - } else if (x >= 15 && x <= 25 && RowHeight * 2 + 4 < y && - y < 8 * RowHeight + 4) + } else if (o->rect(PredefinedRect::VOLUME_AREA).contains(mouseInCell)) setDragTool(XsheetGUI::DragTool::makeVolumeDragTool(m_viewer)); else setDragTool(XsheetGUI::DragTool::makeColumnSelectionTool(m_viewer)); - } else if (column && column->getSoundTextColumn()) { - if (y < m_tabBox.bottom() || m_nameBox.contains(x, y)) - setDragTool(XsheetGUI::DragTool::makeColumnMoveTool(m_viewer)); - else - setDragTool(XsheetGUI::DragTool::makeColumnSelectionTool(m_viewer)); } // clicking another area means column selection else { @@ -1661,25 +1728,30 @@ void ColumnArea::mousePressEvent(QMouseEvent *event) { //----------------------------------------------------------------------------- void ColumnArea::mouseMoveEvent(QMouseEvent *event) { + const Orientation *o = m_viewer->orientation(); + m_viewer->setQtModifiers(event->modifiers()); QPoint pos = event->pos(); if (m_isPanning) { // Pan tasto centrale QPoint delta = m_pos - pos; - delta.setY(0); + if(o->isVerticalTimeline()) + delta.setY(0); + else + delta.setX(0); m_viewer->scroll(delta); return; } - int col = m_viewer->xToColumn(pos.x()); + int col = m_viewer->xyToPosition(pos).layer(); if (col < -1) col = 0; TXsheet *xsh = m_viewer->getXsheet(); TXshColumn *column = xsh->getColumn(col); - int x = pos.x() - m_viewer->columnToX(col); - int y = pos.y(); + QPoint mouseInCell = pos - m_viewer->positionToXY(CellPosition(0, col)); + int x = mouseInCell.x(), y = mouseInCell.y(); #ifdef LINETEST - // Controllo che il menu del motion Path sia chiuso. + // Ensure that the menu of the motion path is hidden if ((x - m_mtypeBox.left() > 20 || y < m_mtypeBox.y() || y > m_mtypeBox.bottom()) && !m_motionPathMenu->isHidden()) @@ -1688,7 +1760,10 @@ void ColumnArea::mouseMoveEvent(QMouseEvent *event) { if ((event->buttons() & Qt::LeftButton) != 0 && !visibleRegion().contains(pos)) { QRect bounds = visibleRegion().boundingRect(); - m_viewer->setAutoPanSpeed(bounds, QPoint(pos.x(), bounds.top())); + if (o->isVerticalTimeline()) + m_viewer->setAutoPanSpeed(bounds, QPoint(pos.x(), bounds.top())); + else + m_viewer->setAutoPanSpeed(bounds, QPoint(bounds.left(), pos.y())); } else m_viewer->stopAutoPan(); @@ -1704,29 +1779,24 @@ void ColumnArea::mouseMoveEvent(QMouseEvent *event) { TStageObjectId columnId = m_viewer->getObjectId(col); TStageObjectId parentId = xsh->getStageObjectParent(columnId); - QRect sideBar(0, 0, 7, height()); - - if (col < 0) m_tooltip = tr("Click to select camera"); - if (column && column->getSoundTextColumn()) - m_tooltip = tr(""); - else if (sideBar.contains(x, y)) { + if (col < 0) + m_tooltip = tr("Click to select camera"); + else if (o->rect(PredefinedRect::DRAG_LAYER).contains(mouseInCell)) { m_tooltip = tr("Click to select column, drag to move it"); - } else if (m_lockBox.contains(x, y)) { + } else if (o->rect(PredefinedRect::LOCK_AREA).contains(mouseInCell)) { m_tooltip = tr("Lock Toggle"); - } else if (m_prevViewBox.contains(x, y)) { + } else if (o->rect(PredefinedRect::EYE_AREA).contains(mouseInCell)) { m_tooltip = tr("Preview Visibility Toggle"); - } else if (m_tableViewBox.contains(x, y)) { + } else if (o->rect(PredefinedRect::PREVIEW_LAYER_AREA).contains(mouseInCell)) { m_tooltip = tr("Camera Stand Visibility Toggle"); } else { if (column && column->getSoundColumn()) { // sound column - if (x > 20 && 3 * RowHeight + 5 <= y && y < 3 * RowHeight + 33) + if (o->rect(PredefinedRect::SOUND_ICON).contains(mouseInCell)) m_tooltip = tr("Click to play the soundtrack back"); - else if (x >= 10 && x <= 20 && RowHeight + RowHeight / 2 < y && - y < 8 * RowHeight - RowHeight / 2) + else if (o->rect(PredefinedRect::VOLUME_AREA).contains(mouseInCell)) m_tooltip = tr("Set the volume of the soundtrack"); } - else if (Preferences::instance()->getColumnIconLoadingPolicy() == Preferences::LoadOnDemand) m_tooltip = tr("Alt + Click to Toggle Thumbnail"); @@ -1752,17 +1822,51 @@ bool ColumnArea::event(QEvent *event) { void ColumnArea::mouseReleaseEvent(QMouseEvent *event) { TApp *app = TApp::instance(); + TXsheet *xsh = m_viewer->getXsheet(); + int col, totcols = xsh->getColumnCount(); if (m_doOnRelease != 0 && m_col != -1) { - TXshColumn *column = m_viewer->getXsheet()->getColumn(m_col); + TXshColumn *column = xsh->getColumn(m_col); if (m_doOnRelease == ToggleTransparency && (!m_columnTransparencyPopup || m_columnTransparencyPopup->isHidden())) { column->setCamstandVisible(!column->isCamstandVisible()); app->getCurrentXsheet()->notifyXsheetSoundChanged(); - } else if (m_doOnRelease == TogglePreviewVisible) + } + else if (m_doOnRelease == TogglePreviewVisible) column->setPreviewVisible(!column->isPreviewVisible()); else if (m_doOnRelease == ToggleLock) column->lock(!column->isLocked()); - else + else if (m_doOnRelease == ToggleAllPreviewVisible) { + for (col = 0; col < totcols; col++) { + TXshColumn *column = xsh->getColumn(col); + if (!xsh->isColumnEmpty(col) && !column->getPaletteColumn() && !column->getSoundTextColumn()) { + column->setPreviewVisible(!column->isPreviewVisible()); + } + } + } + else if (m_doOnRelease == ToggleAllTransparency) { + bool sound_changed = false; + for (col = 0; col < totcols; col++) { + TXshColumn *column = xsh->getColumn(col); + if (!xsh->isColumnEmpty(col) && !column->getPaletteColumn() && !column->getSoundTextColumn()) { + column->setCamstandVisible(!column->isCamstandVisible()); + if (column->getSoundColumn()) sound_changed = true; + } + } + + if (sound_changed) { + app->getCurrentXsheet()->notifyXsheetSoundChanged(); + } + } + else if (m_doOnRelease == ToggleAllLock) { + for (col = 0; col < totcols; col++) + { + TXshColumn *column = xsh->getColumn(col); + if (!xsh->isColumnEmpty(col)) { + column->lock(!column->isLocked()); + } + } + } + else assert(false); app->getCurrentScene()->notifySceneChanged(); @@ -1782,27 +1886,26 @@ void ColumnArea::mouseReleaseEvent(QMouseEvent *event) { //----------------------------------------------------------------------------- void ColumnArea::mouseDoubleClickEvent(QMouseEvent *event) { + const Orientation *o = m_viewer->orientation(); + QPoint pos = event->pos(); - int col = m_viewer->xToColumn(pos.x()); - int x0 = m_viewer->columnToX(col); + int col = m_viewer->xyToPosition(pos).layer(); + CellPosition cellPosition(0, col); + QPoint topLeft = m_viewer->positionToXY(cellPosition); + QPoint mouseInCell = pos - topLeft; #ifdef LINETEST // Camera column if (col == -1) return; #endif - if (!m_prevViewBox.translated(x0, 0) - .adjusted(0, 0, -ColumnWidth / 2, 0) - .contains(pos)) { - return; - } + if (!o->rect(PredefinedRect::LAYER_NAME).contains(mouseInCell)) return; TXsheet *xsh = m_viewer->getXsheet(); if (col >= 0 && xsh->isColumnEmpty(col)) return; - pos = QPoint(x0 + m_prevViewBox.x() - 10, m_prevViewBox.y() - 2); - - m_renameColumnField->show(pos, col); + QRect renameRect = o->rect(PredefinedRect::RENAME_COLUMN).translated(topLeft); + m_renameColumnField->show(renameRect, col); } //----------------------------------------------------------------------------- @@ -1817,22 +1920,22 @@ void ColumnArea::contextMenuEvent(QContextMenuEvent *event) { QApplication::instance()->sendEvent(this, &fakeRelease); #endif - QPoint qPos = event->pos(); - TPoint pos(qPos.x(), qPos.y()); - int row = m_viewer->yToRow(pos.y); - int col = m_viewer->xToColumn(pos.x); + const Orientation *o = m_viewer->orientation(); + + int col = m_viewer->xyToPosition(event->pos()).layer(); if (col < 0) // CAMERA return; m_viewer->setCurrentColumn(col); - TXsheet *xsh = m_viewer->getXsheet(); - int x0 = m_viewer->columnToX(col); + TXsheet *xsh = m_viewer->getXsheet(); + QPoint topLeft = m_viewer->positionToXY(CellPosition(0, col)); + QPoint mouseInCell = event->pos() - topLeft; QMenu menu(this); CommandManager *cmdManager = CommandManager::instance(); //---- Preview if (!xsh->isColumnEmpty(col) && - m_prevViewBox.translated(x0, 0).contains(qPos)) { + o->rect(PredefinedRect::EYE_AREA).contains(mouseInCell)) { menu.setObjectName("xsheetColumnAreaMenu_Preview"); menu.addAction(cmdManager->getAction("MI_EnableThisColumnOnly")); @@ -1844,7 +1947,7 @@ void ColumnArea::contextMenuEvent(QContextMenuEvent *event) { } //---- Lock else if (!xsh->isColumnEmpty(col) && - m_lockBox.translated(x0, 0).contains(qPos)) { + o->rect(PredefinedRect::LOCK_AREA).contains(mouseInCell)) { menu.setObjectName("xsheetColumnAreaMenu_Lock"); menu.addAction(cmdManager->getAction("MI_LockThisColumnOnly")); @@ -1856,7 +1959,7 @@ void ColumnArea::contextMenuEvent(QContextMenuEvent *event) { } //---- Camstand else if (!xsh->isColumnEmpty(col) && - m_tableViewBox.translated(x0, 0).contains(qPos)) { + o->rect(PredefinedRect::PREVIEW_LAYER_AREA).contains(mouseInCell)) { menu.setObjectName("xsheetColumnAreaMenu_Camstand"); menu.addAction(cmdManager->getAction("MI_ActivateThisColumnOnly")); diff --git a/toonz/sources/toonz/xshcolumnviewer.h b/toonz/sources/toonz/xshcolumnviewer.h index e4dd0760..10b22237 100644 --- a/toonz/sources/toonz/xshcolumnviewer.h +++ b/toonz/sources/toonz/xshcolumnviewer.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include // forward declaration class XsheetViewer; @@ -14,6 +16,9 @@ class TXsheetHandle; class TStageObjectId; class TXshColumn; class QComboBox; +class Orientation; +class TApp; +class TXsheet; //============================================================================= namespace XsheetGUI { @@ -134,7 +139,7 @@ public: m_xsheetHandle = xsheetHandle; } - void show(QPoint pos, int col); + void show(const QRect &rect, int col); protected: void focusOutEvent(QFocusEvent *) override; @@ -173,11 +178,11 @@ protected slots: void onFilterColorChanged(int id); }; -//! La classe si occupa della visualizzazione dell'area che gestisce le colonne. +//! The class in charge of the region showing layer headers class ColumnArea final : public QWidget { Q_OBJECT - enum { ToggleTransparency = 1, TogglePreviewVisible, ToggleLock }; + enum { ToggleTransparency = 1, ToggleAllTransparency, TogglePreviewVisible, ToggleAllPreviewVisible, ToggleLock, ToggleAllLock }; ColumnTransparencyPopup *m_columnTransparencyPopup; QTimer *m_transparencyPopupTimer; @@ -188,9 +193,6 @@ class ColumnArea final : public QWidget { QRect m_tabBox; QRect m_nameBox; QRect m_linkBox; - QRect m_prevViewBox; - QRect m_tableViewBox; - QRect m_lockBox; bool m_isPanning; @@ -214,6 +216,43 @@ class ColumnArea final : public QWidget { void setDragTool(DragTool *dragTool); void startTransparencyPopupTimer(QMouseEvent *e); + // extracted all variables of drawSomething methods + class DrawHeader { + ColumnArea *area; + QPainter &p; + int col; + XsheetViewer *m_viewer; + const Orientation *o; + TApp *app; + TXsheet *xsh; + bool isEmpty, isCurrent; + TXshColumn *column; + QPoint orig; + + public: + DrawHeader(ColumnArea *area, QPainter &p, int col); + + void prepare() const; + + void levelColors(QColor &columnColor, QColor &dragColor) const; + void soundColors(QColor &columnColor, QColor &dragColor) const; + void paletteColors(QColor &columnColor, QColor &dragColor) const; + + void drawBaseFill(const QColor &columnColor, const QColor &dragColor) const; + void drawEye() const; + void drawPreviewToggle(int opacity) const; + void drawLock() const; + void drawColumnNumber() const; + void drawColumnName() const; + void drawThumbnail(QPixmap &iconPixmap) const; + void drawPegbarName() const; + void drawParentHandleName() const; + void drawFilterColor() const; + + void drawSoundIcon(bool isPlaying) const; + void drawVolumeControl(double volume) const; + }; + public: #if QT_VERSION >= 0x050500 ColumnArea(XsheetViewer *parent, Qt::WindowFlags flags = 0); @@ -222,6 +261,10 @@ public: #endif ~ColumnArea(); + void onControlPressed(bool pressed); + const bool isControlPressed(); + + void drawFoldedColumnHead(QPainter &p, int col); void drawLevelColumnHead(QPainter &p, int col); void drawSoundColumnHead(QPainter &p, int col); void drawPaletteColumnHead(QPainter &p, int col); @@ -229,6 +272,16 @@ public: QPixmap getColumnIcon(int columnIndex); + class Pixmaps { + public: + static const QPixmap &eye(); + static const QPixmap &cameraStand(); + static const QPixmap &cameraStandTransparent(); + static const QPixmap &lock(); + static const QPixmap &sound(); + static const QPixmap &soundPlaying(); + }; + protected: void select(int columnIndex, QMouseEvent *event); diff --git a/toonz/sources/toonz/xsheetdragtool.cpp b/toonz/sources/toonz/xsheetdragtool.cpp index 3ea0ed48..3e8b1fde 100644 --- a/toonz/sources/toonz/xsheetdragtool.cpp +++ b/toonz/sources/toonz/xsheetdragtool.cpp @@ -86,22 +86,25 @@ void XsheetGUI::DragTool::refreshRowsArea() { getViewer()->updateRows(); } //----------------------------------------------------------------------------- void XsheetGUI::DragTool::onClick(const QMouseEvent *event) { - QPoint pos = event->pos(); - onClick(getViewer()->yToRow(pos.y()), getViewer()->xToColumn(pos.x())); + QPoint xy = event->pos(); + CellPosition pos = getViewer()->xyToPosition(xy); + onClick(pos); } //----------------------------------------------------------------------------- void XsheetGUI::DragTool::onDrag(const QMouseEvent *event) { - QPoint pos = event->pos(); - onDrag(getViewer()->yToRow(pos.y()), getViewer()->xToColumn(pos.x())); + QPoint xy = event->pos(); + CellPosition pos = getViewer()->xyToPosition(xy); + onDrag(pos); } //----------------------------------------------------------------------------- void XsheetGUI::DragTool::onRelease(const QMouseEvent *event) { - QPoint pos = event->pos(); - onRelease(getViewer()->yToRow(pos.y()), getViewer()->xToColumn(pos.x())); + QPoint xy = event->pos(); + CellPosition pos = getViewer()->xyToPosition(xy); + onRelease(pos); } //============================================================================= @@ -117,11 +120,12 @@ public: : DragTool(viewer), m_firstRow(0), m_firstCol(0), m_modifier() {} // activate when clicked the cell void onClick(const QMouseEvent *event) override { - m_modifier = event->modifiers(); - int row = getViewer()->yToRow(event->pos().y()); - int col = getViewer()->xToColumn(event->pos().x()); - m_firstCol = col; - m_firstRow = row; + m_modifier = event->modifiers(); + CellPosition pos = getViewer()->xyToPosition(event->pos()); + int row = pos.frame(); + int col = pos.layer(); + m_firstCol = col; + m_firstRow = row; if (m_modifier & Qt::ShiftModifier) { int r0, c0, r1, c1; getViewer()->getCellSelection()->getSelectedCells(r0, c0, r1, c1); @@ -171,7 +175,8 @@ public: refreshCellsArea(); refreshRowsArea(); } - void onDrag(int row, int col) override { + void onDrag(const CellPosition &pos) override { + int row = pos.frame(), col = pos.layer(); if (col < 0) return; if (row < 0) row = 0; if (m_modifier & Qt::ControlModifier) @@ -626,7 +631,8 @@ public: , m_invert(invert) {} // called when the smart tab is clicked - void onClick(int row, int col) override { + void onClick(const CellPosition &pos) override { + int row = pos.frame(), col = pos.layer(); int r0, c0, r1, c1; getViewer()->getCellSelection()->getSelectedCells(r0, c0, r1, c1); if (m_invert) @@ -647,7 +653,8 @@ public: m_undo->setCells(xsh, r0, c0, m_rowCount, m_colCount); } - void onDrag(int row, int col) override { + void onDrag(const CellPosition &pos) override { + int row = pos.frame(), col = pos.layer(); if (!m_invert) onCellChange(row, col); else @@ -704,7 +711,7 @@ public: bool found = false; for (int c = 0; c < m_colCount; c++) { TXshColumn *column = xsh->getColumn(m_c0 + c); - if (column && column->getSoundColumn()) continue; + if (!column || column->getSoundColumn()) continue; if (!column->isCellEmpty(emptyRow)) { emptyRow += 1; found = true; @@ -725,7 +732,7 @@ public: // clear cells for (int c = 0; c < m_colCount; c++) { TXshColumn *column = xsh->getColumn(m_c0 + c); - if (column && column->getSoundColumn()) continue; + if (!column || column->getSoundColumn()) continue; xsh->clearCells(m_r0, m_c0 + c, dr); } } @@ -733,7 +740,7 @@ public: else { for (int c = 0; c < m_colCount; c++) { TXshColumn *column = xsh->getColumn(m_c0 + c); - if (column && column->getSoundColumn()) continue; + if (!column || column->getSoundColumn()) continue; for (int r = r0; r <= m_r0 - 1; r++) { xsh->setCell(r, m_c0 + c, m_columns[c].generate(r)); } @@ -744,7 +751,8 @@ public: m_c0 + m_colCount - 1); } - void onRelease(int row, int col) override { + void onRelease(const CellPosition &pos) override { + int row = pos.frame(), col = pos.layer(); int delta = m_r1 - (m_r0 + m_rowCount - 1); if (delta == 0) delete m_undo; @@ -883,9 +891,9 @@ public: return soundColumn; } - void onClick(int row, int col) override { - m_firstRow = row; - m_col = col; + void onClick(const CellPosition &pos) override { + m_firstRow = pos.frame(); + m_col = pos.layer(); TXshSoundColumn *soundColumn = getColumn(); if (!soundColumn) return; m_oldColumn = dynamic_cast(soundColumn->clone()); @@ -893,8 +901,8 @@ public: getViewer()->update(); } - void onDrag(int row, int col) override { - onChange(row); + void onDrag(const CellPosition &pos) override { + onChange(pos.frame()); refreshCellsArea(); } @@ -922,7 +930,8 @@ public: TApp::instance()->getCurrentXsheet()->notifyXsheetSoundChanged(); } - void onRelease(int row, int col) override { + void onRelease(const CellPosition &pos) override { + int row = pos.frame(); if (row - m_firstRow == 0) { m_undo = 0; return; @@ -985,10 +994,11 @@ public: LevelMoverTool::onDrag(e); if (m_validPos) m_keyframeMoverTool->onDrag(e); } - void onRelease(int row, int col) override { + void onRelease(const CellPosition &pos) override { + int row = pos.frame(), col = pos.layer(); TUndoManager::manager()->beginBlock(); - LevelMoverTool::onRelease(row, col); - m_keyframeMoverTool->onRelease(row, col); + LevelMoverTool::onRelease(pos); + m_keyframeMoverTool->onRelease(pos); TUndoManager::manager()->endBlock(); } @@ -1089,7 +1099,8 @@ public: , m_r1(0) , m_enable(true) {} - void onClick(int row, int col) override { + void onClick(const CellPosition &pos) override { + int row = pos.frame(), col = pos.layer(); m_r0 = m_r1 = row; TXsheet *xsh = getViewer()->getXsheet(); TStageObjectId cameraId = xsh->getStageObjectTree()->getCurrentCameraId(); @@ -1105,7 +1116,8 @@ public: m_undo = new KeyFrameHandleUndo(objId, m_startRow); } - void onDrag(int row, int col) override { + void onDrag(const CellPosition &pos) override { + int row = pos.frame(), col = pos.layer(); if (!m_enable) return; m_r1 = row; onCellChange(row, col); @@ -1127,7 +1139,8 @@ public: m_stageObject->setKeyframeWithoutUndo(m_startRow, m_k); } - void onRelease(int row, int col) override { + void onRelease(const CellPosition &pos) override { + int row = pos.frame(), col = pos.layer(); if (!m_enable) return; if (m_r0 == m_r1) delete m_undo; @@ -1175,21 +1188,22 @@ public: m_startCol = notes->getNoteCol(currentIndex); QPoint p = e->pos(); TPointD mousePos(p.x(), p.y()); - TPointD cellTopLeft(getViewer()->columnToX(m_startCol), - getViewer()->rowToY(m_startRow)); + QPoint xy = getViewer()->positionToXY(CellPosition(m_startRow, m_startCol)); + TPointD cellTopLeft(xy.x(), xy.y()); m_delta = mousePos - (cellTopLeft + m_startPos); } void onChange(TPointD pos) { - pos = pos - m_delta; - int row = getViewer()->yToRow(pos.y); - int col = getViewer()->xToColumn(pos.x); + pos = pos - m_delta; + CellPosition cellPosition = getViewer()->xyToPosition(pos); + int row = cellPosition.frame(); + int col = cellPosition.layer(); if (row < 0) row = 0; if (col < 0) col = 0; - TPointD newPos = - pos - TPointD(getViewer()->columnToX(col), getViewer()->rowToY(row)); + QPoint xy = getViewer()->positionToXY(CellPosition(row, col)); + TPointD newPos = pos - TPointD(xy.x(), xy.y()); if (newPos.x < 0) newPos.x = 0; if (newPos.y < 0) newPos.y = 0; @@ -1252,14 +1266,16 @@ public: TApp::instance()->getCurrentFrame()->getFrame()) , m_isFos(isFos) {} - void onClick(int row, int col) override { + void onClick(const CellPosition &pos) override { + int row = pos.frame(); OnionSkinMask mask = m_modifier.getMask(); TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(mask); m_modifier.click(row, m_isFos); TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged(); } - void onDrag(int row, int col) override { + void onDrag(const CellPosition &pos) override { + int row = pos.frame(); if (row < 0) row = 0; onRowChange(row); TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged(); @@ -1271,7 +1287,8 @@ public: TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(mask); } - void onRelease(int row, int col) override { + void onRelease(const CellPosition &pos) override { + int row = pos.frame(); m_modifier.release(row); OnionSkinMask mask = m_modifier.getMask(); TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(mask); @@ -1302,12 +1319,14 @@ class CurrentFrameModifier final : public XsheetGUI::DragTool { public: CurrentFrameModifier(XsheetViewer *viewer) : DragTool(viewer) {} - void onClick(int row, int col) override { + void onClick(const CellPosition &pos) override { + int row = pos.frame(); TApp::instance()->getCurrentFrame()->setFrame(row); refreshRowsArea(); } - void onDrag(int row, int col) override { + void onDrag(const CellPosition &pos) override { + int row = pos.frame(); if (row < 0) row = 0; int lastRow = TApp::instance()->getCurrentFrame()->getFrameIndex(); if (lastRow == row) return; @@ -1322,7 +1341,7 @@ public: app->getCurrentFrame()->scrubXsheet(row, row, getViewer()->getXsheet()); } - void onRelease(int row, int col) override { + void onRelease(const CellPosition &pos) override { getViewer()->getXsheet()->stopScrub(); } }; @@ -1354,14 +1373,16 @@ public: PlayRangeModifier(XsheetViewer *viewer) : DragTool(viewer), m_movingFirst(false) {} - void onClick(int row, int col) override { + void onClick(const CellPosition &pos) override { + int row = pos.frame(); XsheetGUI::getPlayRange(m_oldR0, m_oldR1, m_oldStep); assert(m_oldR0 == row || m_oldR1 == row); m_movingFirst = m_oldR0 == row; refreshRowsArea(); } - void onDrag(int row, int col) override { + void onDrag(const CellPosition &pos) override { + int row = pos.frame(); if (row < 0) row = 0; onRowChange(row); refreshRowsArea(); @@ -1391,7 +1412,8 @@ public: XsheetGUI::setPlayRange(r0, r1, step, false); } - void onRelease(int row, int col) override { + void onRelease(const CellPosition &pos) override { + int row = pos.frame(); int newR0, newR1, newStep; XsheetGUI::getPlayRange(newR0, newR1, newStep); if (m_oldR0 != newR0 || m_oldR1 != newR1) { @@ -1428,7 +1450,8 @@ public: void onClick(const QMouseEvent *event) override { TColumnSelection *selection = getViewer()->getColumnSelection(); - int col = getViewer()->xToColumn(event->pos().x()); + CellPosition cellPosition = getViewer()->xyToPosition(event->pos()); + int col = cellPosition.layer(); m_firstColumn = col; bool isSelected = selection->isColumnSelected(col); if (event->modifiers() & Qt::ControlModifier) { @@ -1453,7 +1476,8 @@ public: getViewer()->update(); } - void onDrag(int row, int col) override { + void onDrag(const CellPosition &pos) override { + int col = pos.layer(); if (!m_enabled) return; if (col < 0) return; TColumnSelection *selection = getViewer()->getColumnSelection(); @@ -1465,7 +1489,7 @@ public: refreshCellsArea(); return; } - void onRelease(int row, int col) override { + void onRelease(const CellPosition &pos) override { TSelectionHandle::getCurrent()->notifySelectionChanged(); } }; @@ -1559,7 +1583,8 @@ public: , m_lastCol(-1) , m_offset(0) {} - void onClick(int row, int col) override { + void onClick(const CellPosition &pos) override { + int col = pos.layer(); TColumnSelection *selection = getViewer()->getColumnSelection(); if (!selection->isColumnSelected(col)) { selection->selectNone(); @@ -1574,7 +1599,8 @@ public: assert(m_lastCol == *indices.begin()); getViewer()->update(); } - void onDrag(int row, int col) override { + void onDrag(const CellPosition &pos) override { + int col = pos.layer(); TColumnSelection *selection = getViewer()->getColumnSelection(); std::set indices = selection->getIndices(); if (indices.empty()) return; @@ -1595,7 +1621,7 @@ public: ++it) selection->selectColumn(*it + dCol, true); } - void onRelease(int row, int col) override { + void onRelease(const CellPosition &pos) override { int delta = m_lastCol - m_firstCol; if (delta == 0) return; TColumnSelection *selection = getViewer()->getColumnSelection(); @@ -1657,9 +1683,11 @@ public: ChangePegbarParentDragTool(XsheetViewer *viewer) : XsheetGUI::DragTool(viewer), m_firstCol(-1), m_lastCol(-1) {} - void onClick(int row, int col) override { m_firstCol = m_lastCol = col; } - void onDrag(int row, int col) override { m_lastCol = col; } - void onRelease(int row, int col) override { + void onClick(const CellPosition &pos) override { + m_firstCol = m_lastCol = pos.layer(); + } + void onDrag(const CellPosition &pos) override { m_lastCol = pos.layer(); } + void onRelease(const CellPosition &pos) override { // TUndoManager::manager()->add(new ColumnMoveUndo(indices, delta)); TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); TStageObjectId columnId(getViewer()->getObjectId(m_firstCol)); @@ -1732,9 +1760,14 @@ public: void onDrag(const QMouseEvent *event) override { if (!m_enabled) return; - double v = - (double)(60 - (event->pos().y() - (XsheetGUI::RowHeight * 2 + 4))) / - 60.0; + + const Orientation *o = getViewer()->orientation(); + QRect track = o->rect(PredefinedRect::VOLUME_TRACK); + NumberRange range = o->frameSide(track); + int frameAxis = o->frameAxis(event->pos()); + double v = range.ratio(frameAxis); + if (o->flipVolume()) v = 1 - v; + TXsheet *xsh = getViewer()->getXsheet(); TXshColumn *column = xsh->getColumn(m_index); if (!column) return; @@ -1782,7 +1815,8 @@ public: , m_oldRow(0) , m_timerId(0) {} - void onClick(int row, int col) override { + void onClick(const CellPosition &pos) override { + int row = pos.frame(), col = pos.layer(); TColumnSelection *selection = getViewer()->getColumnSelection(); selection->selectNone(); m_startRow = row; @@ -1790,7 +1824,10 @@ public: getViewer()->updateCells(); } - void onDrag(int row, int col) override { onCellChange(row, col); } + void onDrag(const CellPosition &pos) override { + int row = pos.frame(), col = pos.layer(); + onCellChange(row, col); + } void onCellChange(int row, int col) { assert(m_startRow >= 0); @@ -1798,9 +1835,9 @@ public: getViewer()->updateCells(); } - void onRelease(int row, int col) override { - int r0 = std::min(row, m_startRow); - int r1 = std::max(row, m_startRow); + void onRelease(const CellPosition &pos) override { + int r0 = std::min(pos.frame(), m_startRow); + int r1 = std::max(pos.frame(), m_startRow); assert(m_soundColumn); TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); @@ -1870,7 +1907,7 @@ enum CellMovementType { NO_MOVEMENT, INSERT_CELLS, OVERWRITE_CELLS }; class DataDragTool final : public XsheetGUI::DragTool { DragAndDropData *m_data; bool m_valid; - TPoint m_curPos; + TPoint m_curPos; // screen xy of drag begin CellMovementType m_type; protected: @@ -1928,8 +1965,9 @@ public: } void onDrag(const QDropEvent *e) override { TPoint pos(e->pos().x(), e->pos().y()); - int row = getViewer()->yToRow(pos.y); - int col = getViewer()->xToColumn(pos.x); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); m_valid = true; if (e->keyboardModifiers() & Qt::ShiftModifier) @@ -1943,8 +1981,9 @@ public: } void onRelease(const QDropEvent *e) override { TPoint pos(e->pos().x(), e->pos().y()); - int row = getViewer()->yToRow(pos.y); - int col = getViewer()->xToColumn(pos.x); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); if (m_type != NO_MOVEMENT && !m_valid) return; bool insert = m_type == INSERT_CELLS; @@ -1973,24 +2012,23 @@ public: refreshCellsArea(); } void drawCellsArea(QPainter &p) override { - TPoint pos(getViewer()->xToColumn(m_curPos.x), - getViewer()->yToRow(m_curPos.y)); - TRect rect = m_data->getLevelFrameRect(); + CellPosition beginDragPosition = getViewer()->xyToPosition(m_curPos); + TPoint pos(beginDragPosition.layer(), + beginDragPosition.frame()); // row and cell coordinates + TRect rect = m_data->getLevelFrameRect(); // row and cell coordinates if (rect.isEmpty()) return; rect += pos; if (rect.x1 < 0 || rect.y1 < 0) return; if (rect.x0 < 0) rect.x0 = 0; if (rect.y0 < 0) rect.y0 = 0; - int x0, y0, x1, y1; - x0 = getViewer()->columnToX(rect.x0); - x1 = getViewer()->columnToX(rect.x1 + 1); - y0 = getViewer()->rowToY(rect.y0); - y1 = getViewer()->rowToY(rect.y1 + 1); - int x = x1 - x0; - int y = y1 - y0; + QRect screenCell = getViewer()->rangeToXYRect( + CellRange(CellPosition(rect.y0, rect.x0), + CellPosition(rect.y1 + 1, rect.x1 + 1))); p.setPen(m_valid ? QColor(190, 220, 255) : QColor(255, 0, 0)); int i; - for (i = 0; i < 3; i++) p.drawRect(x0 + i, y0 + i, x - 2 * i, y - 2 * i); + for (i = 0; i < 3; i++) // thick border within cell + p.drawRect(QRect(screenCell.topLeft() + QPoint(i, i), + screenCell.size() - QSize(2 * i, 2 * i))); } }; diff --git a/toonz/sources/toonz/xsheetdragtool.h b/toonz/sources/toonz/xsheetdragtool.h index c2cef5c4..e01c8923 100644 --- a/toonz/sources/toonz/xsheetdragtool.h +++ b/toonz/sources/toonz/xsheetdragtool.h @@ -4,6 +4,7 @@ #define XSHEET_DRAG_TOOL_H #include "tgeometry.h" +#include "cellposition.h" // forward declaration class QPainter; @@ -28,9 +29,9 @@ public: void refreshRowsArea(); void refreshColumnsArea(); - virtual void onClick(int row, int col) {} - virtual void onDrag(int row, int col) {} - virtual void onRelease(int row, int col) {} + virtual void onClick(const CellPosition &pos) {} + virtual void onDrag(const CellPosition &pos) {} + virtual void onRelease(const CellPosition &pos) {} virtual void onClick(const QMouseEvent *event); virtual void onDrag(const QMouseEvent *event); diff --git a/toonz/sources/toonz/xsheetviewer.cpp b/toonz/sources/toonz/xsheetviewer.cpp index a8debfc4..12faedca 100644 --- a/toonz/sources/toonz/xsheetviewer.cpp +++ b/toonz/sources/toonz/xsheetviewer.cpp @@ -48,8 +48,10 @@ TEnv::IntVar FrameDisplayStyleInXsheetRowArea( namespace XsheetGUI { //----------------------------------------------------------------------------- -const int ColumnWidth = 74; -const int RowHeight = 20; +const int ColumnWidth = 74; +const int RowHeight = 20; +const int SCROLLBAR_WIDTH = 16; +const int TOOLBAR_HEIGHT = 30; } // namespace XsheetGUI @@ -139,14 +141,6 @@ XsheetViewer::XsheetViewer(QWidget *parent, Qt::WindowFlags flags) XsheetViewer::XsheetViewer(QWidget *parent, Qt::WFlags flags) #endif : QFrame(parent) - , m_x0(XsheetGUI::ColumnWidth + 1) -#ifndef LINETEST - , m_y0(XsheetGUI::RowHeight * 3 + - 60) // Per tab il numero delle righe era 8 perche c'e' il linkBox -#else - , m_y0(XsheetGUI::RowHeight * 8 + - 5) // Per tab il numero delle righe era 8 perche c'e' il linkBox -#endif , m_timerId(0) , m_autoPanSpeed(0, 0) , m_dragTool(0) @@ -161,28 +155,31 @@ XsheetViewer::XsheetViewer(QWidget *parent, Qt::WFlags flags) , m_isComputingSize(false) , m_currentNoteIndex(0) , m_qtModifiers(0) - , m_toolbarHeight(30) - , m_frameDisplayStyle(to_enum(FrameDisplayStyleInXsheetRowArea)) { + , m_frameDisplayStyle(to_enum(FrameDisplayStyleInXsheetRowArea)) + , m_orientation(nullptr) { + setFocusPolicy(Qt::StrongFocus); setFrameStyle(QFrame::StyledPanel); setObjectName("XsheetViewer"); + m_orientation = Orientations::topToBottom(); + m_cellKeyframeSelection->setXsheetHandle( TApp::instance()->getCurrentXsheet()); - + m_toolbarScrollArea = new XsheetScrollArea(this); - m_toolbarScrollArea->setFixedSize(m_x0 * 12, m_toolbarHeight); + m_toolbarScrollArea->setFixedSize(m_orientation->cellWidth() * 12, XsheetGUI::TOOLBAR_HEIGHT); m_toolbarScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_toolbarScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_toolbar = new XsheetGUI::Toolbar(this); - m_toolbar->setFixedSize(m_x0 * 12, m_toolbarHeight); + m_toolbar->setFixedSize(m_orientation->cellWidth() * 12, XsheetGUI::TOOLBAR_HEIGHT); m_toolbarScrollArea->setWidget(m_toolbar); - m_noteArea = new XsheetGUI::NoteArea(this); - m_noteArea->setFixedSize(m_x0 + 1, m_y0 - 3); + QRect noteArea(0, 0, 75, 120); + m_noteArea = new XsheetGUI::NoteArea(this); m_noteScrollArea = new XsheetScrollArea(this); - m_noteScrollArea->setFixedSize(m_x0 + 1, m_y0 - 3); + m_noteScrollArea->setObjectName("xsheetArea"); m_noteScrollArea->setWidget(m_noteArea); m_noteScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_noteScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -193,8 +190,6 @@ XsheetViewer::XsheetViewer(QWidget *parent, Qt::WFlags flags) m_cellScrollArea->setWidget(m_cellArea); m_cellScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); m_cellScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - // m_cellScrollArea->horizontalScrollBar()->setObjectName("XsheetScrollBar"); - // m_cellScrollArea->verticalScrollBar()->setObjectName("XsheetScrollBar"); m_columnArea = new XsheetGUI::ColumnArea(this); m_columnScrollArea = new XsheetScrollArea(this); @@ -210,19 +205,16 @@ XsheetViewer::XsheetViewer(QWidget *parent, Qt::WFlags flags) m_rowScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_rowScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - connect(m_rowScrollArea->verticalScrollBar(), SIGNAL(valueChanged(int)), - m_cellScrollArea->verticalScrollBar(), SLOT(setValue(int))); - connect(m_cellScrollArea->verticalScrollBar(), SIGNAL(valueChanged(int)), - m_rowScrollArea->verticalScrollBar(), SLOT(setValue(int))); - connect(m_columnScrollArea->horizontalScrollBar(), SIGNAL(valueChanged(int)), - m_cellScrollArea->horizontalScrollBar(), SLOT(setValue(int))); - connect(m_cellScrollArea->horizontalScrollBar(), SIGNAL(valueChanged(int)), - m_columnScrollArea->horizontalScrollBar(), SLOT(setValue(int))); + m_frameScroller.setFrameScrollArea(m_cellScrollArea); + connect(&m_frameScroller, &Spreadsheet::FrameScroller::prepareToScrollOffset, + this, &XsheetViewer::onPrepareToScrollOffset); - connect(m_cellScrollArea->verticalScrollBar(), SIGNAL(valueChanged(int)), - SLOT(updateCellRowAree())); - connect(m_cellScrollArea->horizontalScrollBar(), SIGNAL(valueChanged(int)), - SLOT(updateCellColumnAree())); + connectScrollBars(); + + connect(this, &XsheetViewer::orientationChanged, this, + &XsheetViewer::onOrientationChanged); + + emit orientationChanged(orientation()); } //----------------------------------------------------------------------------- @@ -284,6 +276,115 @@ void XsheetViewer::dragToolLeave(QEvent *e) { //----------------------------------------------------------------------------- +const Orientation *XsheetViewer::orientation() const { + if (!m_orientation) throw std::runtime_error("!m_orientation"); + return m_orientation; +} + +void XsheetViewer::flipOrientation() { + m_orientation = orientation()->next(); + emit orientationChanged(orientation()); +} + +void XsheetViewer::onOrientationChanged(const Orientation *newOrientation) { + disconnectScrollBars(); + + positionSections(); + m_frameScroller.setOrientation(newOrientation); + refreshContentSize(0, 0); + + connectScrollBars(); + + update(); +} + +void XsheetViewer::positionSections() { + const Orientation *o = orientation(); + QRect size = QRect(QPoint(0, 0), geometry().size()); + NumberRange allLayer = o->layerSide(size); + NumberRange allFrame = o->frameSide(size); + + NumberRange headerLayer = o->range(PredefinedRange::HEADER_LAYER); + NumberRange headerFrame = o->range(PredefinedRange::HEADER_FRAME); + NumberRange bodyLayer(headerLayer.to(), allLayer.to()); + NumberRange bodyFrame(headerFrame.to(), allFrame.to()); + + if (Preferences::instance()->isShowXSheetToolbarEnabled()) { + m_toolbar->showToolbar(true); + + int w = geometry().size().width(); + m_toolbarScrollArea->setGeometry(0, 0, w, XsheetGUI::TOOLBAR_HEIGHT); + + if (o->isVerticalTimeline()) { + headerFrame = headerFrame.adjusted(XsheetGUI::TOOLBAR_HEIGHT, XsheetGUI::TOOLBAR_HEIGHT); + bodyFrame = bodyFrame.adjusted(XsheetGUI::TOOLBAR_HEIGHT, 0); + } + else { + headerLayer = headerLayer.adjusted(XsheetGUI::TOOLBAR_HEIGHT, XsheetGUI::TOOLBAR_HEIGHT); + bodyLayer = bodyLayer.adjusted(XsheetGUI::TOOLBAR_HEIGHT, 0); + } + } + else { + m_toolbar->showToolbar(false); + } + + m_noteScrollArea->setGeometry(o->frameLayerRect(headerFrame, headerLayer)); + m_cellScrollArea->setGeometry(o->frameLayerRect(bodyFrame, bodyLayer)); + m_columnScrollArea->setGeometry(o->frameLayerRect( + headerFrame.adjusted(-1,-1), bodyLayer.adjusted(0, -XsheetGUI::SCROLLBAR_WIDTH))); + m_rowScrollArea->setGeometry(o->frameLayerRect( + bodyFrame.adjusted(0, -XsheetGUI::SCROLLBAR_WIDTH), headerLayer)); +} + +void XsheetViewer::disconnectScrollBars() { + connectOrDisconnectScrollBars(false); +} +void XsheetViewer::connectScrollBars() { connectOrDisconnectScrollBars(true); } + +void XsheetViewer::connectOrDisconnectScrollBars(bool toConnect) { + const Orientation *o = orientation(); + bool isVertical = o->isVerticalTimeline(); + QWidget *scrolledVertically = + (isVertical ? m_rowScrollArea : m_columnScrollArea)->verticalScrollBar(); + QWidget *scrolledHorizontally = + (isVertical ? m_columnScrollArea : m_rowScrollArea) + ->horizontalScrollBar(); + + connectOrDisconnect(toConnect, scrolledVertically, SIGNAL(valueChanged(int)), + m_cellScrollArea->verticalScrollBar(), + SLOT(setValue(int))); + connectOrDisconnect(toConnect, m_cellScrollArea->verticalScrollBar(), + SIGNAL(valueChanged(int)), scrolledVertically, + SLOT(setValue(int))); + + connectOrDisconnect( + toConnect, scrolledHorizontally, SIGNAL(valueChanged(int)), + m_cellScrollArea->horizontalScrollBar(), SLOT(setValue(int))); + connectOrDisconnect(toConnect, m_cellScrollArea->horizontalScrollBar(), + SIGNAL(valueChanged(int)), scrolledHorizontally, + SLOT(setValue(int))); + + connectOrDisconnect( + toConnect, m_cellScrollArea->verticalScrollBar(), + SIGNAL(valueChanged(int)), this, + isVertical ? SLOT(updateCellRowAree()) : SLOT(updateCellColumnAree())); + connectOrDisconnect( + toConnect, m_cellScrollArea->horizontalScrollBar(), + SIGNAL(valueChanged(int)), this, + isVertical ? SLOT(updateCellColumnAree()) : SLOT(updateCellRowAree())); +} + +void XsheetViewer::connectOrDisconnect(bool toConnect, QWidget *sender, + const char *signal, QWidget *receiver, + const char *slot) { + if (toConnect) + connect(sender, signal, receiver, slot); + else + disconnect(sender, signal, receiver, slot); +} + +//----------------------------------------------------------------------------- + TXsheet *XsheetViewer::getXsheet() const { return TApp::instance()->getCurrentXsheet()->getXsheet(); } @@ -361,8 +462,7 @@ frameHandle->setFrame(row);*/ void XsheetViewer::scroll(QPoint delta) { int x = delta.x(); int y = delta.y(); - prepareToScroll(y); - + int valueH = m_cellScrollArea->horizontalScrollBar()->value() + x; int valueV = m_cellScrollArea->verticalScrollBar()->value() + y; int maxValueH = m_cellScrollArea->horizontalScrollBar()->maximum(); @@ -391,7 +491,9 @@ void XsheetViewer::scroll(QPoint delta) { //----------------------------------------------------------------------------- -void XsheetViewer::onPrepareToScroll(int dy) { refreshContentSize(0, dy); } +void XsheetViewer::onPrepareToScrollOffset(const QPoint &offset) { + refreshContentSize(offset.x(), offset.y()); +} //----------------------------------------------------------------------------- @@ -445,18 +547,20 @@ void XsheetViewer::timerEvent(QTimerEvent *) { //----------------------------------------------------------------------------- +// adjust sizes after scrolling event bool XsheetViewer::refreshContentSize(int dx, int dy) { QSize viewportSize = m_cellScrollArea->viewport()->size(); QPoint offset = m_cellArea->pos(); - offset = QPoint(qMin(0, offset.x() - dx), qMin(0, offset.y() - dy)); + offset = QPoint(qMin(0, offset.x() - dx), qMin(0, offset.y() - dy)); // what? TXsheet *xsh = getXsheet(); int frameCount = xsh ? xsh->getFrameCount() : 0; int columnCount = xsh ? xsh->getColumnCount() : 0; - QSize contentSize(columnToX(columnCount + 1), rowToY(frameCount + 1)); + QPoint contentSize = + positionToXY(CellPosition(frameCount + 1, columnCount + 1)); - QSize actualSize(contentSize); - int x = viewportSize.width() - offset.x(); + QSize actualSize(contentSize.x(), contentSize.y()); + int x = viewportSize.width() - offset.x(); // wtf is going on int y = viewportSize.height() - offset.y(); if (x > actualSize.width()) actualSize.setWidth(x); if (y > actualSize.height()) actualSize.setHeight(y); @@ -464,10 +568,17 @@ bool XsheetViewer::refreshContentSize(int dx, int dy) { if (actualSize == m_cellArea->size()) return false; else { + const Orientation *o = orientation(); + NumberRange allLayer = o->layerSide(QRect(QPoint(0, 0), actualSize)); + NumberRange allFrame = o->frameSide(QRect(QPoint(0, 0), actualSize)); + NumberRange headerLayer = o->range(PredefinedRange::HEADER_LAYER); + NumberRange headerFrame = o->range(PredefinedRange::HEADER_FRAME); + m_isComputingSize = true; + m_noteArea->setFixedSize(o->rect(PredefinedRect::NOTE_AREA).size()); m_cellArea->setFixedSize(actualSize); - m_rowArea->setFixedSize(m_x0, actualSize.height()); - m_columnArea->setFixedSize(actualSize.width(), m_y0); + m_rowArea->setFixedSize(o->frameLayerRect(allFrame, headerLayer).size()); + m_columnArea->setFixedSize(o->frameLayerRect(headerFrame, allLayer).size()); m_isComputingSize = false; return true; } @@ -475,43 +586,92 @@ bool XsheetViewer::refreshContentSize(int dx, int dy) { //----------------------------------------------------------------------------- +// call when in doubt void XsheetViewer::updateAreeSize() { - int w = m_cellScrollArea->width() - 15; - int h = m_cellScrollArea->height() - 15; + const Orientation *o = orientation(); + QRect viewArea(QPoint(0, 0), m_cellScrollArea->geometry() + .adjusted(0, 0, -XsheetGUI::SCROLLBAR_WIDTH, + -XsheetGUI::SCROLLBAR_WIDTH) + .size()); - TXsheet *xsh = getXsheet(); - int frameCount = xsh ? xsh->getFrameCount() : 0; - int hCounted = (XsheetGUI::RowHeight) * (frameCount + 1); - if (h < hCounted) h = hCounted; + QPoint areaFilled(0, 0); + TXsheet *xsh = getXsheet(); + if (xsh) + areaFilled = positionToXY( + CellPosition(xsh->getFrameCount() + 1, xsh->getColumnCount() + 1)); + if (viewArea.right() < areaFilled.x()) viewArea.setRight(areaFilled.x()); + if (viewArea.bottom() < areaFilled.y()) viewArea.setBottom(areaFilled.y()); - int columnCount = xsh ? xsh->getColumnCount() : 0; - int wCounted = (XsheetGUI::ColumnWidth) * (columnCount + 1); - if (w < wCounted) w = wCounted; + NumberRange allLayer = o->layerSide(viewArea); + NumberRange allFrame = o->frameSide(viewArea); + NumberRange headerLayer = o->range(PredefinedRange::HEADER_LAYER); + NumberRange headerFrame = o->range(PredefinedRange::HEADER_FRAME); - m_cellArea->setFixedSize(w, h); - m_rowArea->setFixedSize(m_x0, h); - m_columnArea->setFixedSize(w, m_y0); + m_cellArea->setFixedSize(viewArea.size()); + m_rowArea->setFixedSize(o->frameLayerRect(allFrame, headerLayer).size()); + m_columnArea->setFixedSize(o->frameLayerRect(headerFrame, allLayer).size()); } //----------------------------------------------------------------------------- -int XsheetViewer::xToColumn(int x) const { - return getXsheet()->getColumnFan()->xToCol(x); +CellPosition XsheetViewer::xyToPosition(const QPoint &point) const { + const Orientation *o = orientation(); + return o->xyToPosition(point, getXsheet()->getColumnFan(o)); +} +CellPosition XsheetViewer::xyToPosition(const TPoint &point) const { + return xyToPosition(QPoint(point.x, point.y)); +} +CellPosition XsheetViewer::xyToPosition(const TPointD &point) const { + return xyToPosition(QPoint((int)point.x, (int)point.y)); } //----------------------------------------------------------------------------- -int XsheetViewer::yToRow(int y) const { return y / XsheetGUI::RowHeight; } +QPoint XsheetViewer::positionToXY(const CellPosition &pos) const { + const Orientation *o = orientation(); + return o->positionToXY(pos, getXsheet()->getColumnFan(o)); +} -//----------------------------------------------------------------------------- - -int XsheetViewer::columnToX(int col) const { - return getXsheet()->getColumnFan()->colToX(col); +int XsheetViewer::columnToLayerAxis(int layer) const { + const Orientation *o = orientation(); + return o->colToLayerAxis(layer, getXsheet()->getColumnFan(o)); +} +int XsheetViewer::rowToFrameAxis(int frame) const { + return orientation()->rowToFrameAxis(frame); } //----------------------------------------------------------------------------- -int XsheetViewer::rowToY(int row) const { return row * XsheetGUI::RowHeight; } +CellRange XsheetViewer::xyRectToRange(const QRect &rect) const { + CellPosition topLeft = xyToPosition(rect.topLeft()); + CellPosition bottomRight = xyToPosition(rect.bottomRight()); + return CellRange(topLeft, bottomRight); +} + +//----------------------------------------------------------------------------- + +QRect XsheetViewer::rangeToXYRect(const CellRange &range) const { + QPoint from = positionToXY(range.from()); + QPoint to = positionToXY(range.to()); + QPoint topLeft = QPoint(min(from.x(), to.x()), min(from.y(), to.y())); + QPoint bottomRight = QPoint(max(from.x(), to.x()), max(from.y(), to.y())); + return QRect(topLeft, bottomRight); +} + +//----------------------------------------------------------------------------- + +void XsheetViewer::drawPredefinedPath(QPainter &p, PredefinedPath which, + const CellPosition &pos, + optional fill, + optional outline) const { + QPoint xy = positionToXY(pos); + QPainterPath path = orientation()->path(which).translated(xy); + if (fill) p.fillPath(path, QBrush(*fill)); + if (outline) { + p.setPen(*outline); + p.drawPath(path); + } +} //----------------------------------------------------------------------------- @@ -590,7 +750,7 @@ bool XsheetViewer::isScrubHighlighted(int row, int col) { //----------------------------------------------------------------------------- void XsheetViewer::showEvent(QShowEvent *) { - registerFrameScroller(); + m_frameScroller.registerFrameScroller(); if (m_isCurrentFrameSwitched) onCurrentFrameSwitched(); if (m_isCurrentColumnSwitched) onCurrentColumnSwitched(); m_isCurrentFrameSwitched = false; @@ -651,7 +811,7 @@ void XsheetViewer::showEvent(QShowEvent *) { //----------------------------------------------------------------------------- void XsheetViewer::hideEvent(QHideEvent *) { - unregisterFrameScroller(); + m_frameScroller.unregisterFrameScroller(); TApp *app = TApp::instance(); @@ -704,39 +864,13 @@ void XsheetViewer::paintEvent(QPaintEvent*) //----------------------------------------------------------------------------- -void XsheetViewer::updatePanelsSizes() { - int w = width(); - int h = height(); - int scrollBarWidth = 16; - if (Preferences::instance()->isShowXSheetToolbarEnabled()) { - m_toolbar->showToolbar(true); - m_toolbarScrollArea->setGeometry(1, 1, m_x0 * 12, m_y0 - 3); - m_noteScrollArea->setGeometry(3, m_toolbarHeight + 1, m_x0 - 4, m_y0 - 3); - m_cellScrollArea->setGeometry(m_x0, m_y0 + m_toolbarHeight, w - m_x0, - h - m_y0 - m_toolbarHeight); - m_columnScrollArea->setGeometry(m_x0, m_toolbarHeight + 1, - w - m_x0 - scrollBarWidth, m_y0 - 3); - m_rowScrollArea->setGeometry(1, m_y0 + m_toolbarHeight, m_x0 - 1, - h - m_y0 - scrollBarWidth - m_toolbarHeight); - } else { - m_toolbar->showToolbar(false); - m_toolbarScrollArea->setGeometry(3, 1, m_x0 - 4, m_y0 - 3); - m_noteScrollArea->setGeometry(3, 1, m_x0 - 4, m_y0 - 3); - m_cellScrollArea->setGeometry(m_x0, m_y0, w - m_x0, h - m_y0); - m_columnScrollArea->setGeometry(m_x0, 1, w - m_x0 - scrollBarWidth, - m_y0 - 3); - m_rowScrollArea->setGeometry(1, m_y0, m_x0 - 1, h - m_y0 - scrollBarWidth); - } -} - -//----------------------------------------------------------------------------- - void XsheetViewer::resizeEvent(QResizeEvent *event) { - updatePanelsSizes(); - //(Nuovo Layout Manager) Reintrodotto per il refresh automatico + positionSections(); + + //(New Layout Manager) introduced automatic refresh refreshContentSize( 0, - 0); // Non updateAreeSize() perche' si deve tener conto degli scrollbar. + 0); // Don't updateAreeSize because you have to account scrollbars updateAllAree(); } @@ -751,13 +885,19 @@ void XsheetViewer::wheelEvent(QWheelEvent *event) { ->getScene() ->getProperties() ->getMarkers(markerDistance, markerOffset); + if (event->angleDelta().x() == 0) { // vertical scroll + if (!orientation()->isVerticalTimeline()) + markerDistance = 1; int scrollPixels = (event->angleDelta().y() > 0 ? 1 : -1) * - markerDistance * XsheetGUI::RowHeight; + markerDistance * orientation()->cellHeight(); scroll(QPoint(0, -scrollPixels)); } else { // horizontal scroll + if (orientation()->isVerticalTimeline()) + markerDistance = 1; int scrollPixels = - (event->angleDelta().x() > 0 ? 1 : -1) * XsheetGUI::ColumnWidth; + (event->angleDelta().x() > 0 ? 1 : -1) * + markerDistance * orientation()->cellWidth(); scroll(QPoint(-scrollPixels, 0)); } break; @@ -806,9 +946,10 @@ void XsheetViewer::keyPressEvent(QKeyEvent *event) { if (changeFrameSkippingHolds(event)) return; int frameCount = getXsheet()->getFrameCount(); - int row = getCurrentRow(), col = getCurrentColumn(); + CellPosition now(getCurrentRow(), getCurrentColumn()); + CellPosition shift = orientation()->arrowShift(event->key()); + CellPosition stride(1, 1); // stride in row and column axes - int rowStride = 1; TCellSelection *cellSel = dynamic_cast(TSelection::getCurrent()); // Use arrow keys to shift the cell selection. Ctrl + arrow keys to resize the @@ -817,77 +958,62 @@ void XsheetViewer::keyPressEvent(QKeyEvent *event) { cellSel && !cellSel->isEmpty()) { int r0, c0, r1, c1; cellSel->getSelectedCells(r0, c0, r1, c1); - rowStride = cellSel->getSelectedCells().getRowCount(); - QPoint offset(0, 0); - switch (int key = event->key()) { - case Qt::Key_Up: - offset.setY(-1); - break; - case Qt::Key_Down: - offset.setY(1); - break; - case Qt::Key_Left: - offset.setX(-1); - break; - case Qt::Key_Right: - offset.setX(1); - break; - } - if (m_cellArea->isControlPressed()) { - if (r0 == r1 && offset.y() == -1) return; - if (c0 == c1 && offset.x() == -1) return; - cellSel->selectCells(r0, c0, r1 + offset.y(), c1 + offset.x()); + stride.setFrame(cellSel->getSelectedCells().getRowCount()); + + if (m_cellArea->isControlPressed()) { // resize + if (r0 == r1 && shift.frame() < 0) return; + if (c0 == c1 && shift.layer() < 0) return; + cellSel->selectCells(r0, c0, r1 + shift.frame(), c1 + shift.layer()); updateCells(); TApp::instance()->getCurrentSelection()->notifySelectionChanged(); return; - } else { - offset.setY(offset.y() * rowStride); - if (r0 + offset.y() < 0) offset.setY(-r0); - if (c0 + offset.x() < 0) return; - cellSel->selectCells(r0 + offset.y(), c0 + offset.x(), r1 + offset.y(), - c1 + offset.x()); + } else { // shift + CellPosition offset(shift * stride); + int movedR0 = std::max(0, r0 + offset.frame()); + int movedC0 = std::max(0, c0 + offset.layer()); + int diffFrame = movedR0 - r0; + int diffLayer = movedC0 - c0; + cellSel->selectCells(r0 + diffFrame, c0 + diffLayer, r1 + diffFrame, + c1 + diffLayer); TApp::instance()->getCurrentSelection()->notifySelectionChanged(); } } + if (shift) { + now = now + shift * stride; + now.ensureValid(); + setCurrentRow(now.frame()); + setCurrentColumn(now.layer()); + return; + } + switch (int key = event->key()) { - case Qt::Key_Up: - setCurrentRow(std::max(row - rowStride, 0)); - break; - case Qt::Key_Down: - setCurrentRow(row + rowStride); - break; - case Qt::Key_Left: - setCurrentColumn(std::max(col - 1, 0)); - break; - case Qt::Key_Right: - setCurrentColumn(col + 1); - break; case Qt::Key_Control: // display the upper-directional smart tab only when the ctrl key is pressed m_cellArea->onControlPressed(true); - break; + m_columnArea->onControlPressed(true); + break; default: { QRect visibleRect = m_cellArea->visibleRegion().boundingRect(); - int visibleRowCount = visibleRect.height() / XsheetGUI::RowHeight; + int visibleRowCount = visibleRect.height() / orientation()->cellHeight(); switch (key) { case Qt::Key_PageUp: locals.scrollTo( - visibleRect.top() - visibleRowCount * XsheetGUI::RowHeight, + visibleRect.top() - visibleRowCount * orientation()->cellHeight(), visibleRect); break; case Qt::Key_PageDown: locals.scrollTo( - visibleRect.bottom() + visibleRowCount * XsheetGUI::RowHeight, + visibleRect.bottom() + visibleRowCount * orientation()->cellHeight(), visibleRect); break; case Qt::Key_Home: locals.scrollTo(0, visibleRect); break; case Qt::Key_End: - locals.scrollTo((frameCount + 1) * XsheetGUI::RowHeight, visibleRect); + locals.scrollTo((frameCount + 1) * orientation()->cellHeight(), visibleRect); break; } break; @@ -898,18 +1024,24 @@ void XsheetViewer::keyPressEvent(QKeyEvent *event) { //----------------------------------------------------------------------------- // display the upper-directional smart tab only when the ctrl key is pressed void XsheetViewer::keyReleaseEvent(QKeyEvent *event) { - if (event->key() == Qt::Key_Control) m_cellArea->onControlPressed(false); + if (event->key() == Qt::Key_Control) { + m_cellArea->onControlPressed(false); + m_columnArea->onControlPressed(false); + } } -void XsheetViewer::enterEvent(QEvent *) { m_cellArea->onControlPressed(false); } +void XsheetViewer::enterEvent(QEvent *) { + m_cellArea->onControlPressed(false); + m_columnArea->onControlPressed(false); +} //----------------------------------------------------------------------------- /*! scroll the cell area to make a cell at (row,col) visible */ void XsheetViewer::scrollTo(int row, int col) { QRect visibleRect = m_cellArea->visibleRegion().boundingRect(); - QRect cellRect(columnToX(col), rowToY(row), XsheetGUI::ColumnWidth, - XsheetGUI::RowHeight); + QPoint topLeft = positionToXY(CellPosition(row, col)); + QRect cellRect(topLeft, QSize(orientation()->cellWidth(), orientation()->cellHeight())); int deltaX = 0; int deltaY = 0; @@ -949,7 +1081,10 @@ void XsheetViewer::onXsheetChanged() { //----------------------------------------------------------------------------- void XsheetViewer::onPreferenceChanged(const QString &prefName) { - if (prefName == "XSheetToolbar") updatePanelsSizes(); + if (prefName == "XSheetToolbar") { + positionSections(); + refreshContentSize(0, 0); + } } //----------------------------------------------------------------------------- @@ -988,50 +1123,63 @@ void XsheetViewer::onCurrentColumnSwitched() { //----------------------------------------------------------------------------- void XsheetViewer::scrollToColumn(int col) { - int x0 = columnToX(col); - int x1 = columnToX(col + 1); + int x0 = columnToLayerAxis(col); + int x1 = columnToLayerAxis(col + 1); - scrollToHorizontalRange(x0, x1); + if (orientation()->isVerticalTimeline()) + scrollToHorizontalRange(x0, x1); + else + scrollToVerticalRange(x0, x1); } //----------------------------------------------------------------------------- void XsheetViewer::scrollToHorizontalRange(int x0, int x1) { QRect visibleRect = m_cellArea->visibleRegion().boundingRect(); - int visibleLeft = visibleRect.left(); - int visibleRight = visibleRect.right(); + if (visibleRect.isEmpty()) return; + int visibleLeft = visibleRect.left(); + int visibleRight = visibleRect.right(); - if (visibleLeft > x1) { // Se sono fuori dalla regione visibile in alto + if (visibleLeft > x0) { // If they are out of left visible region int deltaX = x0 - visibleLeft; scroll(QPoint(deltaX, 0)); return; } - if (visibleRight < x1) { // Se sono fuori dalla regione visibile in basso + if (visibleRight < x1) { // If they are out of right visible region int deltaX = x1 + 2 - visibleRight; scroll(QPoint(deltaX, 0)); return; } - updateCellColumnAree(); + if (orientation()->isVerticalTimeline()) + updateCellColumnAree(); + else + updateCellRowAree(); } //----------------------------------------------------------------------------- void XsheetViewer::scrollToRow(int row) { - int y0 = rowToY(row); - int y1 = rowToY(row + 1); + int y0 = rowToFrameAxis(row); + int y1 = rowToFrameAxis(row + 1); - scrollToVerticalRange(y0, y1); + if (orientation()->isVerticalTimeline()) + scrollToVerticalRange(y0, y1); + else + scrollToHorizontalRange(y0, y1); } //----------------------------------------------------------------------------- void XsheetViewer::scrollToVerticalRange(int y0, int y1) { + int yMin = min(y0, y1); + int yMax = max(y0, y1); QRect visibleRect = m_cellArea->visibleRegion().boundingRect(); + if (visibleRect.isEmpty()) return; int visibleTop = visibleRect.top(); int visibleBottom = visibleRect.bottom(); - if (visibleTop > y0) { // Se sono fuori dalla regione visibile in alto - int deltaY = y0 - visibleTop; + if (visibleTop > yMin) { // If they are out of top visible region + int deltaY = yMin - visibleTop; if (!TApp::instance()->getCurrentFrame()->isPlaying() || Preferences::instance()->isXsheetAutopanEnabled()) { scroll(QPoint(0, deltaY)); @@ -1039,16 +1187,18 @@ void XsheetViewer::scrollToVerticalRange(int y0, int y1) { } } - if (visibleBottom < y1) { // Se sono fuori dalla regione visibile in basso - int deltaY = y1 + 2 - visibleBottom; + if (visibleBottom < yMax) { // If they are out of bottom visible region + int deltaY = yMax + 2 - visibleBottom; if (!TApp::instance()->getCurrentFrame()->isPlaying() || Preferences::instance()->isXsheetAutopanEnabled()) { scroll(QPoint(0, deltaY)); return; } } - m_rowArea->update(m_rowArea->visibleRegion()); - m_cellArea->update(m_cellArea->visibleRegion()); + if (orientation()->isVerticalTimeline()) + updateCellRowAree(); + else + updateCellColumnAree(); } //----------------------------------------------------------------------------- @@ -1089,6 +1239,7 @@ void XsheetViewer::updateAllAree(bool isDragging) { m_rowArea->update(m_rowArea->visibleRegion()); m_columnArea->update(m_columnArea->visibleRegion()); } + m_toolbar->update(m_toolbar->visibleRegion()); } //----------------------------------------------------------------------------- @@ -1166,12 +1317,12 @@ void XsheetViewer::setCurrentNoteIndex(int currentNoteIndex) { int col = notes->getNoteCol(currentNoteIndex); TPointD pos = notes->getNotePos(currentNoteIndex); - int x0 = columnToX(col) + pos.x; - int x1 = x0 + XsheetGUI::NoteWidth; - scrollToHorizontalRange(x0, x1); - int y0 = rowToY(row) + pos.y; - int y1 = y0 + XsheetGUI::NoteHeight; - scrollToVerticalRange(y0, y1); + QPoint topLeft = positionToXY(CellPosition(row, col)) + + QPoint(pos.x, pos.y); // actually xy + QSize size(XsheetGUI::NoteWidth, XsheetGUI::NoteHeight); + QRect noteRect(topLeft, size); + scrollToHorizontalRange(noteRect.left(), noteRect.right()); + scrollToVerticalRange(noteRect.top(), noteRect.bottom()); } //----------------------------------------------------------------------------- @@ -1295,6 +1446,19 @@ void XsheetViewer::setFrameDisplayStyle(FrameDisplayStyle style) { FrameDisplayStyleInXsheetRowArea = (int)style; } +//----------------------------------------------------------------------------- + +void XsheetViewer::save(QSettings &settings) const { + settings.setValue("orientation", orientation()->name()); +} +void XsheetViewer::load(QSettings &settings) { + QVariant name = settings.value("orientation"); + if (!name.canConvert(QVariant::String)) return; + + m_orientation = Orientations::byName(name.toString()); + emit orientationChanged(orientation()); +} + //----------------------------------------------------------------------------- /* TPanel *createXsheetViewer(QWidget *parent) diff --git a/toonz/sources/toonz/xsheetviewer.h b/toonz/sources/toonz/xsheetviewer.h index d1eb9e4b..7a8238c5 100644 --- a/toonz/sources/toonz/xsheetviewer.h +++ b/toonz/sources/toonz/xsheetviewer.h @@ -12,7 +12,12 @@ #include "xshnoteviewer.h" #include "xshtoolbar.h" #include "cellkeyframeselection.h" +#include "saveloadqsettings.h" #include "toonzqt/spreadsheetviewer.h" +#include "orientation.h" +#include + +using boost::optional; #define XSHEET_FONT_PX_SIZE 12 #define H_ADJUST 2 @@ -150,7 +155,7 @@ protected: //! Note: some refactoring is needed. XsheetViewer is going to derive from //! SpreadsheetViewer. -class XsheetViewer final : public QFrame, public Spreadsheet::FrameScroller { +class XsheetViewer final : public QFrame, public SaveLoadQSettings { Q_OBJECT QColor m_lightLightBgColor; @@ -322,7 +327,8 @@ class XsheetViewer final : public QFrame, public Spreadsheet::FrameScroller { XsheetGUI::NoteArea *m_noteArea; XsheetGUI::Toolbar *m_toolbar; - int m_x0, m_y0, m_toolbarHeight; + Spreadsheet::FrameScroller m_frameScroller; + int m_timerId; QPoint m_autoPanSpeed; QPoint m_lastAutoPanPos; @@ -344,6 +350,8 @@ class XsheetViewer final : public QFrame, public Spreadsheet::FrameScroller { Qt::KeyboardModifiers m_qtModifiers; + const Orientation *m_orientation; + public: enum FrameDisplayStyle { Frame = 0, SecAndFrame, SixSecSheet, ThreeSecSheet }; @@ -417,7 +425,6 @@ public: void setCurrentRow(int row); void scroll(QPoint delta); - void onPrepareToScroll(int dy) override; void setAutoPanSpeed(const QPoint &speed); void setAutoPanSpeed(const QRect &widgetBounds, const QPoint &mousePos); @@ -426,22 +433,33 @@ public: return m_autoPanSpeed.x() != 0 || m_autoPanSpeed.y() != 0; } - int xToColumn(int x) const; - int yToRow(int y) const; - int columnToX(int col) const; - int rowToY(int row) const; + //------- + const Orientation *orientation() const; + void flipOrientation(); + + CellPosition xyToPosition(const QPoint &point) const; + CellPosition xyToPosition(const TPoint &point) const; + CellPosition xyToPosition(const TPointD &point) const; + QPoint positionToXY(const CellPosition &pos) const; + + int columnToLayerAxis(int layer) const; + int rowToFrameAxis(int frame) const; + + CellRange xyRectToRange(const QRect &rect) const; + QRect rangeToXYRect(const CellRange &range) const; + + void drawPredefinedPath(QPainter &p, PredefinedPath which, + const CellPosition &pos, optional fill, + optional outline) const; + //--------- void updateCells() { m_cellArea->update(m_cellArea->visibleRegion()); } void updateRows() { m_rowArea->update(m_rowArea->visibleRegion()); } void updateColumns() { m_columnArea->update(m_columnArea->visibleRegion()); } - void updatePanelsSizes(); bool refreshContentSize(int scrollDx, int scrollDy); void updateAreeSize(); - // provvisorio - QScrollArea *getFrameScrollArea() const override { return m_cellScrollArea; } - QList getNotesWidget() const; void addNoteWidget(XsheetGUI::NoteWidget *w); int getCurrentNoteIndex() const; @@ -660,6 +678,10 @@ public: void setFrameDisplayStyle(FrameDisplayStyle style); FrameDisplayStyle getFrameDisplayStyle() { return m_frameDisplayStyle; } + // SaveLoadQSettings + virtual void save(QSettings &settings) const override; + virtual void load(QSettings &settings) override; + protected: void scrollToColumn(int col); void scrollToHorizontalRange(int x0, int x1); @@ -676,6 +698,15 @@ protected: void wheelEvent(QWheelEvent *event) override; void timerEvent(QTimerEvent *) override; + void positionSections(); + void disconnectScrollBars(); + void connectScrollBars(); + void connectOrDisconnectScrollBars(bool toConnect); + void connectOrDisconnect(bool toConnect, QWidget *sender, const char *signal, + QWidget *receiver, const char *slot); +signals: + void orientationChanged(const Orientation *newOrientation); + public slots: void onSceneSwitched(); void onXsheetChanged(); @@ -697,6 +728,9 @@ public slots: void changeWindowTitle(); void resetXsheetNotes(); + + void onOrientationChanged(const Orientation *newOrientation); + void onPrepareToScrollOffset(const QPoint &offset); }; #endif // XSHEETVIEWER_H diff --git a/toonz/sources/toonz/xshnoteviewer.cpp b/toonz/sources/toonz/xshnoteviewer.cpp index 55b00152..e9dcf3f1 100644 --- a/toonz/sources/toonz/xshnoteviewer.cpp +++ b/toonz/sources/toonz/xshnoteviewer.cpp @@ -14,6 +14,7 @@ #include "toonz/txshnoteset.h" #include "toonz/sceneproperties.h" #include "toonz/txsheethandle.h" +#include "orientation.h" // Qt includes #include @@ -372,7 +373,7 @@ NoteWidget::NoteWidget(XsheetViewer *parent, int noteIndex) void NoteWidget::paint(QPainter *painter, QPoint pos, bool isCurrent) { painter->translate(pos); - QRect rect(0, 0, width(), height()); + QRect rect = m_viewer->orientation()->rect(PredefinedRect::NOTE_ICON); TXshNoteSet *notes = m_viewer->getXsheet()->getNotes(); TSceneProperties *sp = m_viewer->getXsheet()->getScene()->getProperties(); @@ -450,23 +451,38 @@ NoteArea::NoteArea(XsheetViewer *parent, Qt::WindowFlags flags) #else NoteArea::NoteArea(XsheetViewer *parent, Qt::WFlags flags) #endif - : QFrame(parent), m_viewer(parent) { + : QFrame(parent) + , m_viewer(parent) + , m_flipOrientationButton(nullptr) + , m_noteButton(nullptr) + , m_precNoteButton(nullptr) + , m_nextNoteButton(nullptr) + , m_frameDisplayStyleCombo(nullptr) + , m_layerHeaderPanel(nullptr) { + setFrameStyle(QFrame::StyledPanel); setObjectName("cornerWidget"); - QToolButton *toolButton = new QToolButton(this); + m_flipOrientationButton = + new QPushButton(m_viewer->orientation()->name(), this); + m_noteButton = new QToolButton(this); m_precNoteButton = new QToolButton(this); m_nextNoteButton = new QToolButton(this); m_frameDisplayStyleCombo = new QComboBox(this); + m_layerHeaderPanel = new LayerHeaderPanel(m_viewer, this); + //----- - toolButton->setObjectName("ToolbarToolButton"); - toolButton->setFixedSize(44, 26); - toolButton->setIconSize(QSize(38, 20)); - QIcon addNoteIcon = createQIcon("newmemo"); + m_flipOrientationButton->setObjectName("flipOrientationButton"); + m_flipOrientationButton->setFocusPolicy(Qt::FocusPolicy::NoFocus); + + m_noteButton->setObjectName("ToolbarToolButton"); + m_noteButton->setFixedSize(44, 26); + m_noteButton->setIconSize(QSize(38, 20)); + QIcon addNoteIcon = createQIconPNG("newmemo"); addNoteIcon.addFile(QString(":Resources/newmemo_disabled.svg"), QSize(), QIcon::Disabled); - toolButton->setIcon(addNoteIcon); + m_noteButton->setIcon(addNoteIcon); m_precNoteButton->setObjectName("ToolbarToolButton"); m_precNoteButton->setFixedSize(22, 22); @@ -492,35 +508,14 @@ NoteArea::NoteArea(XsheetViewer *parent, Qt::WFlags flags) (int)m_viewer->getFrameDisplayStyle()); // layout - QVBoxLayout *mainLay = new QVBoxLayout(); - mainLay->setMargin(0); - mainLay->setSpacing(5); - { - mainLay->addStretch(1); - - mainLay->addWidget(toolButton, 0, Qt::AlignHCenter); - - QHBoxLayout *noteLay = new QHBoxLayout(); - noteLay->setMargin(0); - noteLay->setSpacing(0); - { - noteLay->addStretch(1); - noteLay->addWidget(m_precNoteButton, 0); - noteLay->addWidget(m_nextNoteButton, 0); - noteLay->addStretch(1); - } - mainLay->addLayout(noteLay, 0); - - mainLay->addStretch(1); - - mainLay->addWidget(m_frameDisplayStyleCombo, 0); - } - setLayout(mainLay); + createLayout(); // signal-slot connections bool ret = true; + ret = ret && connect(m_flipOrientationButton, SIGNAL(clicked()), + SLOT(flipOrientation())); - ret = ret && connect(toolButton, SIGNAL(clicked()), SLOT(toggleNewNote())); + ret = ret && connect(m_noteButton, SIGNAL(clicked()), SLOT(toggleNewNote())); ret = ret && connect(m_precNoteButton, SIGNAL(clicked()), this, SLOT(precNote())); ret = ret && @@ -530,6 +525,9 @@ NoteArea::NoteArea(XsheetViewer *parent, Qt::WFlags flags) ret && connect(m_frameDisplayStyleCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onFrameDisplayStyleChanged(int))); + ret = ret && connect(m_viewer, &XsheetViewer::orientationChanged, this, + &NoteArea::onXsheetOrientationChanged); + updateButtons(); assert(ret); @@ -537,6 +535,69 @@ NoteArea::NoteArea(XsheetViewer *parent, Qt::WFlags flags) //----------------------------------------------------------------------------- +void NoteArea::removeLayout() { + QLayout *currentLayout = layout(); + if (!currentLayout) return; + + currentLayout->removeWidget(m_flipOrientationButton); + currentLayout->removeWidget(m_noteButton); + currentLayout->removeWidget(m_precNoteButton); + currentLayout->removeWidget(m_nextNoteButton); + currentLayout->removeWidget(m_frameDisplayStyleCombo); + currentLayout->removeWidget(m_layerHeaderPanel); + delete currentLayout; +} + +void NoteArea::createLayout() { + const Orientation *o = m_viewer->orientation(); + QRect rect = o->rect(PredefinedRect::NOTE_AREA); + + setFixedSize(rect.size()); + + // has two elements: main layout and header panel + QVBoxLayout *panelLayout = new QVBoxLayout(); + panelLayout->setMargin(1); + panelLayout->setSpacing(0); + { + QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::Direction( + o->dimension(PredefinedDimension::QBOXLAYOUT_DIRECTION))); + Qt::AlignmentFlag centerAlign = + Qt::AlignmentFlag(o->dimension(PredefinedDimension::CENTER_ALIGN)); + mainLayout->setMargin(1); + mainLayout->setSpacing(0); + { + mainLayout->addWidget(m_flipOrientationButton, 0, centerAlign); + + mainLayout->addStretch(1); + + mainLayout->addWidget(m_noteButton, 0, centerAlign); + + QHBoxLayout *buttonsLayout = new QHBoxLayout(); + buttonsLayout->setMargin(0); + buttonsLayout->setSpacing(0); + { + buttonsLayout->addStretch(1); + buttonsLayout->addWidget(m_precNoteButton, 0); + buttonsLayout->addWidget(m_nextNoteButton, 0); + buttonsLayout->addStretch(1); + } + mainLayout->addLayout(buttonsLayout, 0); + + mainLayout->addStretch(1); + + mainLayout->addWidget(m_frameDisplayStyleCombo, 0); + } + panelLayout->addLayout(mainLayout); + + panelLayout->addWidget(m_layerHeaderPanel); + } + setLayout(panelLayout); + + m_layerHeaderPanel->showOrHide(o); +} + +//----------------------------------------------------------------------------- + void NoteArea::updateButtons() { TXshNoteSet *notes = m_viewer->getXsheet()->getNotes(); @@ -555,6 +616,16 @@ void NoteArea::updateButtons() { //----------------------------------------------------------------------------- +void NoteArea::flipOrientation() { m_viewer->flipOrientation(); } + +void NoteArea::onXsheetOrientationChanged(const Orientation *newOrientation) { + m_flipOrientationButton->setText(newOrientation->caption()); + removeLayout(); + createLayout(); +} + +//----------------------------------------------------------------------------- + void NoteArea::toggleNewNote() { if (!m_newNotePopup) m_newNotePopup.reset(new XsheetGUI::NotePopup(m_viewer, -1)); diff --git a/toonz/sources/toonz/xshnoteviewer.h b/toonz/sources/toonz/xshnoteviewer.h index d2d4cc4e..7de096a0 100644 --- a/toonz/sources/toonz/xshnoteviewer.h +++ b/toonz/sources/toonz/xshnoteviewer.h @@ -13,6 +13,8 @@ #include #include +#include "layerheaderpanel.h" + //----------------------------------------------------------------------------- // forward declaration @@ -22,6 +24,7 @@ class TColorStyle; class QToolButton; class QPushButton; class QComboBox; +class Orientation; //----------------------------------------------------------------------------- @@ -117,11 +120,16 @@ class NoteArea final : public QFrame { std::unique_ptr m_newNotePopup; // Popup used to create new note XsheetViewer *m_viewer; + QPushButton *m_flipOrientationButton; + + QToolButton *m_noteButton; QToolButton *m_nextNoteButton; QToolButton *m_precNoteButton; QComboBox *m_frameDisplayStyleCombo; + LayerHeaderPanel *m_layerHeaderPanel; + public: #if QT_VERSION >= 0x050500 NoteArea(XsheetViewer *parent = 0, Qt::WindowFlags flags = 0); @@ -133,11 +141,17 @@ public: void updateButtons(); protected slots: + void flipOrientation(); void toggleNewNote(); void nextNote(); void precNote(); void onFrameDisplayStyleChanged(int id); + void onXsheetOrientationChanged(const Orientation *orientation); + +protected: + void removeLayout(); + void createLayout(); }; } // namespace XsheetGUI; diff --git a/toonz/sources/toonz/xshrowviewer.cpp b/toonz/sources/toonz/xshrowviewer.cpp index 53e8913c..f4194e04 100644 --- a/toonz/sources/toonz/xshrowviewer.cpp +++ b/toonz/sources/toonz/xshrowviewer.cpp @@ -46,7 +46,6 @@ RowArea::RowArea(XsheetViewer *parent, Qt::WFlags flags) #endif : QWidget(parent, flags) , m_viewer(parent) - , m_xa(ColumnWidth / 2 - 12) , m_row(-1) , m_showOnionToSet(None) , m_pos(-1, -1) @@ -108,20 +107,23 @@ void RowArea::drawRows(QPainter &p, int r0, int r1) { QRect visibleRect = visibleRegion().boundingRect(); - int x0 = visibleRect.left(); - int x1 = visibleRect.right(); - int y0 = visibleRect.top(); - int y1 = visibleRect.bottom(); + int x0 = visibleRect.left(); + int x1 = visibleRect.right(); + int y0 = visibleRect.top(); + int y1 = visibleRect.bottom(); + NumberRange layerSide = m_viewer->orientation()->layerSide(visibleRect); for (int r = r0; r <= r1; r++) { - int y = m_viewer->rowToY(r); + int frameAxis = m_viewer->rowToFrameAxis(r); //--- draw horizontal line - QColor color = ((r - offset) % distance != 0) - ? m_viewer->getLightLineColor() - : m_viewer->getMarkerLineColor(); + QColor color = ((r - offset) % distance == 0 && r != 0) + ? m_viewer->getMarkerLineColor() + : m_viewer->getLightLineColor(); p.setPen(color); - p.drawLine(x0, y, x1, y); + QLine horizontalLine = + m_viewer->orientation()->horizontalLine(frameAxis, layerSide); + p.drawLine(horizontalLine); // draw frame text if (playR0 <= r && r <= playR1) { @@ -132,6 +134,13 @@ void RowArea::drawRows(QPainter &p, int r0, int r1) { else p.setPen(m_viewer->getTextColor()); + QPoint basePoint = m_viewer->positionToXY(CellPosition(r, 0)); + QRect labelRect = m_viewer->orientation() + ->rect(PredefinedRect::FRAME_LABEL) + .translated(basePoint); + int align = m_viewer->orientation()->dimension( + PredefinedDimension::FRAME_LABEL_ALIGN); + // display time and/or frame number switch (m_viewer->getFrameDisplayStyle()) { case XsheetViewer::SecAndFrame: { int frameRate = TApp::instance() @@ -152,16 +161,14 @@ void RowArea::drawRows(QPainter &p, int r0, int r1) { str = QString("%1\"").arg(QString::number(koma).rightJustified(2, '0')); } - p.drawText(QRect(width() / 2 - 15, y + 1, width() / 2 + 7, RowHeight - 2), - Qt::AlignRight | Qt::AlignBottom, str); + p.drawText(labelRect, align, str); break; } case XsheetViewer::Frame: { QString number = QString::number(r + 1); - p.drawText(QRect(width() / 2 - 2, y + 1, width() / 2, RowHeight - 2), - Qt::AlignHCenter | Qt::AlignBottom, number); + p.drawText(labelRect, align, number); break; } @@ -177,15 +184,14 @@ void RowArea::drawRows(QPainter &p, int r0, int r1) { int koma = (r + 1) % (frameRate * 6); if ((r + 1) % frameRate == 1) { int page = (r + 1) / (frameRate * 6) + 1; - str = QString("p%1 %2") + str = QString("p%1 %2") .arg(QString::number(page)) .arg(QString::number(koma).rightJustified(3, '0')); } else { if (koma == 0) koma = frameRate * 6; str = QString("%1").arg(QString::number(koma).rightJustified(3, '0')); } - p.drawText(QRect(width() / 2 - 21, y + 1, width() / 2 + 7, RowHeight - 2), - Qt::AlignRight | Qt::AlignBottom, str); + p.drawText(labelRect, align, str); break; } // 3 second sheet (72frames per page) @@ -200,25 +206,18 @@ void RowArea::drawRows(QPainter &p, int r0, int r1) { int koma = (r + 1) % (frameRate * 3); if ((r + 1) % frameRate == 1) { int page = (r + 1) / (frameRate * 3) + 1; - str = QString("p%1 %2") + str = QString("p%1 %2") .arg(QString::number(page)) .arg(QString::number(koma).rightJustified(2, '0')); } else { if (koma == 0) koma = frameRate * 3; str = QString("%1").arg(QString::number(koma).rightJustified(2, '0')); } - p.drawText(QRect(width() / 2 - 21, y + 1, width() / 2 + 7, RowHeight - 2), - Qt::AlignRight | Qt::AlignBottom, str); + p.drawText(labelRect, align, str); break; } } } - - // hide the top-most horizontal line - if (r0 == 0) { - p.setPen(m_viewer->getLightLineColor()); - p.drawLine(x0, m_viewer->rowToY(0), x1, m_viewer->rowToY(0)); - } } //----------------------------------------------------------------------------- @@ -243,28 +242,29 @@ void RowArea::drawPlayRange(QPainter &p, int r0, int r1) { QColor ArrowColor = (playRangeEnabled) ? QColor(255, 255, 255) : grey150; p.setBrush(QBrush(ArrowColor)); - if (m_r0 > r0 - 1 && r1 + 1 > m_r0) { - int y0 = m_viewer->rowToY(m_r0); - drawArrow(p, QPointF(m_xa, y0 + 1), QPointF(m_xa + 10, y0 + 1), - QPointF(m_xa, y0 + 11), true, ArrowColor); - } + if (m_r0 > r0 - 1 && r1 + 1 > m_r0) + m_viewer->drawPredefinedPath(p, PredefinedPath::BEGIN_PLAY_RANGE, + CellPosition(m_r0, 0), ArrowColor, + QColor(Qt::black)); - if (m_r1 > r0 - 1 && r1 + 1 > m_r1) { - int y1 = m_viewer->rowToY(m_r1 + 1) - 12; - drawArrow(p, QPointF(m_xa, y1 + 1), QPointF(m_xa + 10, y1 + 11), - QPointF(m_xa, y1 + 11), true, ArrowColor); - } + if (m_r1 > r0 - 1 && r1 + 1 > m_r1) + m_viewer->drawPredefinedPath(p, PredefinedPath::END_PLAY_RANGE, + CellPosition(m_r1, 0), ArrowColor, + QColor(Qt::black)); } //----------------------------------------------------------------------------- void RowArea::drawCurrentRowGadget(QPainter &p, int r0, int r1) { int currentRow = m_viewer->getCurrentRow(); - int y = m_viewer->rowToY(currentRow); if (currentRow < r0 || r1 < currentRow) return; - p.fillRect(1, y + 1, width() - 4, RowHeight - 1, - m_viewer->getCurrentRowBgColor()); + QPoint topLeft = m_viewer->positionToXY(CellPosition(currentRow, 0)); + QRect header = m_viewer->orientation() + ->rect(PredefinedRect::FRAME_HEADER) + .translated(topLeft) + .adjusted(1, 1, 0, 0); + p.fillRect(header, m_viewer->getCurrentRowBgColor()); } //----------------------------------------------------------------------------- @@ -282,12 +282,9 @@ void RowArea::drawOnionSkinSelection(QPainter &p) { bool inksOnly; Preferences::instance()->getOnionData(frontPixel, backPixel, inksOnly); QColor frontColor((int)frontPixel.r, (int)frontPixel.g, (int)frontPixel.b, - 128); - QColor backColor((int)backPixel.r, (int)backPixel.g, (int)backPixel.b, 128); - - int onionDotDiam = 8; - int onionHandleDiam = RowHeight - 1; - int onionDotYPos = (RowHeight - onionDotDiam) / 2; + 128); + QColor backColor((int)backPixel.r, (int)backPixel.g, (int)backPixel.b, + 128); // If the onion skin is disabled, draw dash line instead. if (osMask.isEnabled()) @@ -299,14 +296,11 @@ void RowArea::drawOnionSkinSelection(QPainter &p) { p.setPen(currentPen); } - // Draw onion skin extender handles. - QRectF handleRect(3, m_viewer->rowToY(currentRow) + 1, onionHandleDiam, - onionHandleDiam); - int angle180 = 16 * 180; - p.setBrush(QBrush(backColor)); - p.drawChord(handleRect, 0, angle180); - p.setBrush(QBrush(frontColor)); - p.drawChord(handleRect, angle180, angle180); + QRect onionRect = m_viewer->orientation()->rect(PredefinedRect::ONION); + int onionCenter_frame = + m_viewer->orientation()->frameSide(onionRect).middle(); + int onionCenter_layer = + m_viewer->orientation()->layerSide(onionRect).middle(); //-- draw movable onions @@ -322,18 +316,43 @@ void RowArea::drawOnionSkinSelection(QPainter &p) { p.setBrush(Qt::NoBrush); if (minMos < 0) // previous frames { - int y0 = - m_viewer->rowToY(currentRow + minMos) + onionDotYPos + onionDotDiam; - int y1 = m_viewer->rowToY(currentRow); - p.drawLine(onionDotDiam * 1.5, y0, onionDotDiam * 1.5, y1); + int layerAxis = onionCenter_layer; + int fromFrameAxis = + m_viewer->rowToFrameAxis(currentRow + minMos) + onionCenter_frame; + int toFrameAxis = m_viewer->rowToFrameAxis(currentRow) + onionCenter_frame; + QLine verticalLine = m_viewer->orientation()->verticalLine( + layerAxis, NumberRange(fromFrameAxis, toFrameAxis)); + if (m_viewer->orientation()->isVerticalTimeline()) + p.drawLine(verticalLine.x1() + 1, verticalLine.y1() + 4, verticalLine.x2() + 1, verticalLine.y2() - 10); + else + p.drawLine(verticalLine.x1() + 4, verticalLine.y1() + 1, verticalLine.x2() - 10, verticalLine.y2() + 1); } - if (maxMos > 0) // foward frames + if (maxMos > 0) // forward frames { - int y0 = m_viewer->rowToY(currentRow + 1); - int y1 = m_viewer->rowToY(currentRow + maxMos) + onionDotYPos; - p.drawLine(onionDotDiam * 1.5, y0, onionDotDiam * 1.5, y1); + int layerAxis = onionCenter_layer; + int fromFrameAxis = + m_viewer->rowToFrameAxis(currentRow) + onionCenter_frame; + int toFrameAxis = + m_viewer->rowToFrameAxis(currentRow + maxMos) + onionCenter_frame; + QLine verticalLine = m_viewer->orientation()->verticalLine( + layerAxis, NumberRange(fromFrameAxis, toFrameAxis)); + if (m_viewer->orientation()->isVerticalTimeline()) + p.drawLine(verticalLine.x1() + 1, verticalLine.y1() + 10, verticalLine.x2() + 1, verticalLine.y2() - 4); + else + p.drawLine(verticalLine.x1() + 10, verticalLine.y1() + 1, verticalLine.x2() - 4, verticalLine.y2() + 1); } + // Draw onion skin main handle + QPoint handleTopLeft = m_viewer->positionToXY(CellPosition(currentRow, 0)); + QRect handleRect = onionRect.translated(handleTopLeft); + int angle180 = 16 * 180; + int turn = + m_viewer->orientation()->dimension(PredefinedDimension::ONION_TURN) * 16; + p.setBrush(QBrush(backColor)); + p.drawChord(handleRect, turn, angle180); + p.setBrush(QBrush(frontColor)); + p.drawChord(handleRect, turn + angle180, angle180); + // draw onion skin dots p.setPen(Qt::red); for (int i = 0; i < mosCount; i++) { @@ -341,13 +360,16 @@ void RowArea::drawOnionSkinSelection(QPainter &p) { int mos = osMask.getMos(i); // skip drawing if the frame is under the mouse cursor if (m_showOnionToSet == Mos && currentRow + mos == m_row) continue; - int y = m_viewer->rowToY(currentRow + mos) + onionDotYPos; if (osMask.isEnabled()) p.setBrush(mos < 0 ? backColor : frontColor); else p.setBrush(Qt::NoBrush); - p.drawEllipse(onionDotDiam, y, onionDotDiam, onionDotDiam); + QPoint topLeft = m_viewer->positionToXY(CellPosition(currentRow + mos, 0)); + QRect dotRect = m_viewer->orientation() + ->rect(PredefinedRect::ONION_DOT) + .translated(topLeft); + p.drawEllipse(dotRect); } //-- draw fixed onions @@ -356,22 +378,29 @@ void RowArea::drawOnionSkinSelection(QPainter &p) { if (fos == currentRow) continue; // skip drawing if the frame is under the mouse cursor if (m_showOnionToSet == Fos && fos == m_row) continue; - int y = m_viewer->rowToY(fos) + onionDotYPos; if (osMask.isEnabled()) p.setBrush(QBrush(QColor(0, 255, 255, 128))); else p.setBrush(Qt::NoBrush); - p.drawEllipse(0, y, onionDotDiam, onionDotDiam); + QPoint topLeft = m_viewer->positionToXY(CellPosition(fos, 0)); + QRect dotRect = m_viewer->orientation() + ->rect(PredefinedRect::ONION_DOT_FIXED) + .translated(topLeft); + p.drawEllipse(dotRect); } - //-- draw highlighted onion + //-- onion placement hint under mouse if (m_showOnionToSet != None) { - int y = m_viewer->rowToY(m_row) + onionDotYPos; - int xPos = (m_showOnionToSet == Fos) ? 0 : onionDotDiam; p.setPen(QColor(255, 128, 0)); - p.setBrush(QBrush(QColor(255, 255, 0, 200))); - p.drawEllipse(xPos, y, onionDotDiam, onionDotDiam); + p.setBrush(QBrush(QColor(255, 255, 0))); + QPoint topLeft = m_viewer->positionToXY(CellPosition(m_row, 0)); + QRect dotRect = + m_viewer->orientation() + ->rect(m_showOnionToSet == Fos ? PredefinedRect::ONION_DOT_FIXED + : PredefinedRect::ONION_DOT) + .translated(topLeft); + p.drawEllipse(dotRect); } } @@ -416,7 +445,8 @@ void RowArea::drawPinnedCenterKeys(QPainter &p, int r0, int r1) { int columnCount = xsh->getColumnCount(); int prev_pinnedCol = -2; - QRect keyRect(30, 5, 10, 10); + QRect keyRect = + m_viewer->orientation()->rect(PredefinedRect::PINNED_CENTER_KEY); p.setPen(Qt::black); r1 = (r1 < xsh->getFrameCount() - 1) ? xsh->getFrameCount() - 1 : r1; @@ -430,21 +460,22 @@ void RowArea::drawPinnedCenterKeys(QPainter &p, int r0, int r1) { if (tmp_pinnedCol != prev_pinnedCol) { prev_pinnedCol = tmp_pinnedCol; if (r != r0 - 1) { - if (m_pos.x() >= 30 && m_pos.x() <= 40 && m_row == r) + QPoint mouseInCell = m_pos - m_viewer->positionToXY(CellPosition(r, 0)); + if (keyRect.contains(mouseInCell)) p.setBrush(QColor(30, 210, 255)); else p.setBrush(QColor(0, 175, 255)); - int y = m_viewer->rowToY(r); - QRect tmpKeyRect = keyRect.translated(0, y); - p.drawRect(tmpKeyRect); + QPoint topLeft = m_viewer->positionToXY(CellPosition(r, 0)); + QRect adjusted = keyRect.translated(topLeft); + p.drawRect(adjusted); QFont font = p.font(); font.setPixelSize(8); font.setBold(false); p.setFont(font); p.drawText( - tmpKeyRect, Qt::AlignCenter, + adjusted, Qt::AlignCenter, QString::number((tmp_pinnedCol == -1) ? ancestorId.getIndex() + 1 : tmp_pinnedCol + 1)); } @@ -459,9 +490,10 @@ void RowArea::paintEvent(QPaintEvent *event) { QPainter p(this); - int r0, r1; // range di righe visibili - r0 = m_viewer->yToRow(toBeUpdated.top()); - r1 = m_viewer->yToRow(toBeUpdated.bottom()); + CellRange cellRange = m_viewer->xyRectToRange(toBeUpdated); + int r0, r1; // range of visible rows + r0 = cellRange.from().frame(); + r1 = cellRange.to().frame(); p.setClipRect(toBeUpdated); @@ -487,6 +519,8 @@ void RowArea::paintEvent(QPaintEvent *event) { //----------------------------------------------------------------------------- void RowArea::mousePressEvent(QMouseEvent *event) { + const Orientation *o = m_viewer->orientation(); + m_viewer->setQtModifiers(event->modifiers()); if (event->button() == Qt::LeftButton) { bool frameAreaIsClicked = false; @@ -496,22 +530,28 @@ void RowArea::mousePressEvent(QMouseEvent *event) { TPoint pos(event->pos().x(), event->pos().y()); int currentFrame = TApp::instance()->getCurrentFrame()->getFrame(); - int row = m_viewer->yToRow(pos.y); + int row = m_viewer->xyToPosition(pos).frame(); + QPoint topLeft = m_viewer->positionToXY(CellPosition(row, 0)); + QPoint mouseInCell = event->pos() - topLeft; - int onionDotDiam = 8; if (Preferences::instance()->isOnionSkinEnabled() && - ((row == currentFrame && pos.x < RowHeight + 2) || - (pos.x < onionDotDiam * 2))) { + o->rect(PredefinedRect::ONION_AREA).contains(mouseInCell)) { if (row == currentFrame) { setDragTool( XsheetGUI::DragTool::makeCurrentFrameModifierTool(m_viewer)); frameAreaIsClicked = true; - } else if (pos.x <= onionDotDiam) + } else if (o->rect(PredefinedRect::ONION_FIXED_DOT_AREA) + .contains(mouseInCell)) setDragTool(XsheetGUI::DragTool::makeKeyOnionSkinMaskModifierTool( m_viewer, true)); - else + else if (o->rect(PredefinedRect::ONION_DOT_AREA).contains(mouseInCell)) setDragTool(XsheetGUI::DragTool::makeKeyOnionSkinMaskModifierTool( m_viewer, false)); + else { + setDragTool( + XsheetGUI::DragTool::makeCurrentFrameModifierTool(m_viewer)); + frameAreaIsClicked = true; + } } else { int playR0, playR1, step; XsheetGUI::getPlayRange(playR0, playR1, step); @@ -527,7 +567,7 @@ void RowArea::mousePressEvent(QMouseEvent *event) { setDragTool( XsheetGUI::DragTool::makeCurrentFrameModifierTool(m_viewer)); frameAreaIsClicked = true; - } else if (m_xa <= pos.x && pos.x <= m_xa + 10 && + } else if (o->rect(PredefinedRect::PLAY_RANGE).contains(mouseInCell) && (row == playR0 || row == playR1)) { if (!playRangeEnabled) XsheetGUI::setPlayRange(playR0, playR1, step); setDragTool(XsheetGUI::DragTool::makePlayRangeModifierTool(m_viewer)); @@ -566,24 +606,31 @@ void RowArea::mousePressEvent(QMouseEvent *event) { //----------------------------------------------------------------------------- void RowArea::mouseMoveEvent(QMouseEvent *event) { + const Orientation *o = m_viewer->orientation(); m_viewer->setQtModifiers(event->modifiers()); QPoint pos = event->pos(); // pan by middle-drag if (m_isPanning) { QPoint delta = m_pos - pos; - delta.setX(0); - m_viewer->scroll(delta); + if (o->isVerticalTimeline()) + delta.setX(0); + else + delta.setY(0); + m_viewer->scroll(delta); return; } - m_row = m_viewer->yToRow(pos.y()); + m_row = m_viewer->xyToPosition(pos).frame(); int x = pos.x(); if ((event->buttons() & Qt::LeftButton) != 0 && !visibleRegion().contains(pos)) { QRect bounds = visibleRegion().boundingRect(); - m_viewer->setAutoPanSpeed(bounds, QPoint(bounds.left(), pos.y())); + if(o->isVerticalTimeline()) + m_viewer->setAutoPanSpeed(bounds, QPoint(bounds.left(), pos.y())); + else + m_viewer->setAutoPanSpeed(bounds, QPoint(pos.x(), bounds.top())); } else m_viewer->stopAutoPan(); @@ -596,14 +643,15 @@ void RowArea::mouseMoveEvent(QMouseEvent *event) { if (getDragTool()) return; int currentRow = TApp::instance()->getCurrentFrame()->getFrame(); - int row = m_viewer->yToRow(m_pos.y()); + int row = m_viewer->xyToPosition(m_pos).frame(); + QPoint mouseInCell = + event->pos() - m_viewer->positionToXY(CellPosition(row, 0)); if (row < 0) return; - // "decide" se mostrare la possibilita' di settare l'onion skin - if (Preferences::instance()->isOnionSkinEnabled()) { - int onionDotDiam = 8; - if (x <= onionDotDiam && row != currentRow) + // whether to show ability to set onion marks + if (Preferences::instance()->isOnionSkinEnabled() && row != currentRow) { + if (o->rect(PredefinedRect::ONION_FIXED_DOT_AREA).contains(mouseInCell)) m_showOnionToSet = Fos; - else if (x <= onionDotDiam * 2 && row != currentRow) + else if (o->rect(PredefinedRect::ONION_DOT_AREA).contains(mouseInCell)) m_showOnionToSet = Mos; } @@ -612,7 +660,7 @@ void RowArea::mouseMoveEvent(QMouseEvent *event) { bool isRootBonePinned; int pinnedCenterColumnId = -1; if (TApp::instance()->getCurrentTool()->getTool()->getName() == T_Skeleton && - x >= 30 && x <= 40) { + o->rect(PredefinedRect::PINNED_CENTER_KEY).contains(mouseInCell)) { int col = m_viewer->getCurrentColumn(); TXsheet *xsh = m_viewer->getXsheet(); if (col >= 0 && xsh && !xsh->isColumnEmpty(col)) { @@ -635,27 +683,27 @@ void RowArea::mouseMoveEvent(QMouseEvent *event) { update(); - int y0 = m_viewer->rowToY(m_r0); - int y1 = m_viewer->rowToY(m_r1 + 1) - 12; - QPolygon startArrow, endArrow; - startArrow << QPoint(m_xa, y0 + 1) << QPoint(m_xa + 10, y0 + 1) - << QPoint(m_xa, y0 + 11); - endArrow << QPoint(m_xa, y1 + 1) << QPoint(m_xa + 10, y1 + 11) - << QPoint(m_xa, y1 + 11); + QPoint base0 = m_viewer->positionToXY(CellPosition(m_r0, 0)); + QPoint base1 = m_viewer->positionToXY(CellPosition(m_r1, 0)); + QPainterPath startArrow = + o->path(PredefinedPath::BEGIN_PLAY_RANGE).translated(base0); + QPainterPath endArrow = + o->path(PredefinedPath::END_PLAY_RANGE).translated(base1); - if (startArrow.containsPoint(m_pos, Qt::OddEvenFill)) + if (startArrow.contains(m_pos)) m_tooltip = tr("Playback Start Marker"); - else if (endArrow.containsPoint(m_pos, Qt::OddEvenFill)) + else if (endArrow.contains(m_pos)) m_tooltip = tr("Playback End Marker"); else if (isOnPinnedCenterKey) m_tooltip = tr("Pinned Center : Col%1%2") .arg(pinnedCenterColumnId + 1) .arg((isRootBonePinned) ? " (Root)" : ""); else if (row == currentRow) { - if (Preferences::instance()->isOnionSkinEnabled() && x < RowHeight + 2) + if (Preferences::instance()->isOnionSkinEnabled() && + o->rect(PredefinedRect::ONION).contains(mouseInCell)) m_tooltip = tr("Double Click to Toggle Onion Skin"); else - m_tooltip = tr("Curren Frame"); + m_tooltip = tr("Current Frame"); } else if (m_showOnionToSet == Fos) m_tooltip = tr("Fixed Onion Skin Toggle"); else if (m_showOnionToSet == Mos) @@ -674,7 +722,7 @@ void RowArea::mouseReleaseEvent(QMouseEvent *event) { TPoint pos(event->pos().x(), event->pos().y()); - int row = m_viewer->yToRow(pos.y); + int row = m_viewer->xyToPosition(pos).frame(); if (m_playRangeActiveInMousePress && row == m_mousePressRow && (13 <= pos.x && pos.x <= 26 && (row == m_r0 || row == m_r1))) onRemoveMarkers(); @@ -756,11 +804,15 @@ int RowArea::getNonEmptyCell(int row, int column, Direction direction) { void RowArea::mouseDoubleClickEvent(QMouseEvent *event) { int currentFrame = TApp::instance()->getCurrentFrame()->getFrame(); - int row = m_viewer->yToRow(event->pos().y()); + int row = m_viewer->xyToPosition(event->pos()).frame(); + QPoint mouseInCell = + event->pos() - m_viewer->positionToXY(CellPosition(row, 0)); if (TApp::instance()->getCurrentFrame()->isEditingScene() && event->buttons() & Qt::LeftButton && Preferences::instance()->isOnionSkinEnabled() && row == currentFrame && - event->pos().x() < RowHeight + 2) { + m_viewer->orientation() + ->rect(PredefinedRect::ONION) + .contains(mouseInCell)) { TOnionSkinMaskHandle *osmh = TApp::instance()->getCurrentOnionSkin(); OnionSkinMask osm = osmh->getOnionSkinMask(); osm.enable(!osm.isEnabled()); diff --git a/toonz/sources/toonz/xshrowviewer.h b/toonz/sources/toonz/xshrowviewer.h index 53e1bdfa..404c64d1 100644 --- a/toonz/sources/toonz/xshrowviewer.h +++ b/toonz/sources/toonz/xshrowviewer.h @@ -21,7 +21,6 @@ class DragTool; class RowArea final : public QWidget { Q_OBJECT XsheetViewer *m_viewer; - int m_xa; int m_row; enum ShowOnionToSetFlag { diff --git a/toonz/sources/toonzlib/CMakeLists.txt b/toonz/sources/toonzlib/CMakeLists.txt index ae3f654a..6ddad092 100644 --- a/toonz/sources/toonzlib/CMakeLists.txt +++ b/toonz/sources/toonzlib/CMakeLists.txt @@ -152,6 +152,7 @@ set(MOC_HEADERS sandor_fxs/YOMBParam.h texturemanager.h imagebuilders.h + ../include/orientation.h ) set(HEADERS ${MOC_HEADERS}) @@ -161,6 +162,7 @@ set(SOURCES autoclose.cpp autopos.cpp captureparameters.cpp + cellpositionratio.cpp childstack.cpp cleanupcolorstyles.cpp cleanuppalette.cpp @@ -193,6 +195,7 @@ set(SOURCES Naa2TlvConverter.cpp observer.cpp onionskinmask.cpp + orientation.cpp outputproperties.cpp preferences.cpp rasterbrush.cpp diff --git a/toonz/sources/toonzlib/cellpositionratio.cpp b/toonz/sources/toonzlib/cellpositionratio.cpp new file mode 100644 index 00000000..1b95b03f --- /dev/null +++ b/toonz/sources/toonzlib/cellpositionratio.cpp @@ -0,0 +1,66 @@ +#include "toonz/cellpositionratio.h" + +#include +#include + +// Euclid's algorithm +int greatestCommonDivisor(int a, int b) { + a = std::abs(a); + b = std::abs(b); + int c = std::max(a, b); + int d = std::min(a, b); + while (d) { + int q = c / d; + int r = c % d; + c = d; + d = r; + } + return c; +} + +int leastCommonMultiple(int a, int b) { + return a * b / greatestCommonDivisor(a, b); +} + +void Ratio::normalize() { + int gcd = greatestCommonDivisor(m_num, m_denom); + if (m_denom < 0) gcd = -gcd; + m_num /= gcd; + m_denom /= gcd; +} + +Ratio Ratio::normalized() const { + Ratio copy(*this); + copy.normalize(); + return copy; +} + +Ratio::Ratio(int num, int denom) : m_num(num), m_denom(denom) { + if (!denom) throw std::runtime_error("ratio with denominator == 0"); + normalize(); +} + +Ratio operator*(const Ratio &a, const Ratio &b) { + return Ratio(a.m_num * b.m_num, a.m_denom * b.m_denom); +} +Ratio operator/(const Ratio &a, const Ratio &b) { + return Ratio(a.m_num * b.m_denom, a.m_denom * b.m_num); +} + +Ratio operator+(const Ratio &a, const Ratio &b) { + int gcd = greatestCommonDivisor(a.m_denom, b.m_denom); + int denom = a.m_denom * b.m_denom / gcd; + int aMult = b.m_denom * gcd; + int bMult = a.m_denom * gcd; + return Ratio(a.m_num * aMult + b.m_num * bMult, denom); +} + +Ratio operator-(const Ratio &a, const Ratio &b) { + int gcd = greatestCommonDivisor(a.m_denom, b.m_denom); + int denom = a.m_denom * b.m_denom / gcd; + int aMult = b.m_denom * gcd; + int bMult = a.m_denom * gcd; + return Ratio(a.m_num * aMult - b.m_num * bMult, denom); +} + +int operator*(const Ratio &a, int b) { return a.m_num * b / a.m_denom; } diff --git a/toonz/sources/toonzlib/columnfan.cpp b/toonz/sources/toonzlib/columnfan.cpp index 84a37b5e..4b24cf3a 100644 --- a/toonz/sources/toonzlib/columnfan.cpp +++ b/toonz/sources/toonzlib/columnfan.cpp @@ -8,37 +8,40 @@ // STD includss #include -//============================================================================= - -const int colWidth = 74; -const int colSkip = 9; - //============================================================================= // ColumnFan -ColumnFan::ColumnFan() : m_firstFreePos(0) {} +ColumnFan::ColumnFan() : m_firstFreePos(0), m_unfolded(74), m_folded(9) {} + +//----------------------------------------------------------------------------- + +void ColumnFan::setDimension(int unfolded) { + m_unfolded = unfolded; + // folded always 9 + update(); +} //----------------------------------------------------------------------------- void ColumnFan::update() { - int lastPos = -colWidth; + int lastPos = -m_unfolded; bool lastActive = true; int m = m_columns.size(); int i; for (i = 0; i < m; i++) { bool active = m_columns[i].m_active; if (lastActive) - lastPos += colWidth; + lastPos += m_unfolded; else if (active) - lastPos += colSkip; + lastPos += m_folded; m_columns[i].m_pos = lastPos; lastActive = active; } - m_firstFreePos = lastPos + (lastActive ? colWidth : colSkip); + m_firstFreePos = lastPos + (lastActive ? m_unfolded : m_folded); m_table.clear(); for (i = 0; i < m; i++) if (m_columns[i].m_active) - m_table[m_columns[i].m_pos + colWidth - 1] = i; + m_table[m_columns[i].m_pos + m_unfolded - 1] = i; else if (i + 1 < m && m_columns[i + 1].m_active) m_table[m_columns[i + 1].m_pos - 1] = i; else if (i + 1 == m) @@ -47,24 +50,24 @@ void ColumnFan::update() { //----------------------------------------------------------------------------- -int ColumnFan::xToCol(int x) const { - if (x < m_firstFreePos) { - std::map::const_iterator it = m_table.lower_bound(x); +int ColumnFan::layerAxisToCol(int coord) const { + if (coord < m_firstFreePos) { + std::map::const_iterator it = m_table.lower_bound(coord); if (it == m_table.end()) return -3; assert(it != m_table.end()); return it->second; } else - return m_columns.size() + (x - m_firstFreePos) / colWidth; + return m_columns.size() + (coord - m_firstFreePos) / m_unfolded; } //----------------------------------------------------------------------------- -int ColumnFan::colToX(int col) const { +int ColumnFan::colToLayerAxis(int col) const { int m = m_columns.size(); if (col >= 0 && col < m) return m_columns[col].m_pos; else - return m_firstFreePos + (col - m) * colWidth; + return m_firstFreePos + (col - m) * m_unfolded; } //----------------------------------------------------------------------------- @@ -106,7 +109,15 @@ bool ColumnFan::isEmpty() const { return m_columns.empty(); } //----------------------------------------------------------------------------- -void ColumnFan::saveData(TOStream &os) { +void ColumnFan::copyFoldedStateFrom(const ColumnFan &from) { + for (int i = 0, n = (int)from.m_columns.size(); i < n; i++) + if (!from.isActive(i)) deactivate(i); +} + +//----------------------------------------------------------------------------- + +void ColumnFan::saveData( + TOStream &os) { // only saves indices of folded columns int index, n = (int)m_columns.size(); for (index = 0; index < n;) { while (index < n && m_columns[index].m_active) index++; diff --git a/toonz/sources/toonzlib/orientation.cpp b/toonz/sources/toonzlib/orientation.cpp new file mode 100644 index 00000000..6fcbb649 --- /dev/null +++ b/toonz/sources/toonzlib/orientation.cpp @@ -0,0 +1,706 @@ +#include "orientation.h" +#include "toonz/columnfan.h" + +#include +#include +#include + +using std::pair; + +namespace { +const int KEY_ICON_WIDTH = 11; +const int KEY_ICON_HEIGHT = 13; +const int EASE_TRIANGLE_SIZE = 4; +const int PLAY_MARKER_SIZE = 10; +const int ONION_SIZE = 19; +const int ONION_DOT_SIZE = 8; +const int PINNED_SIZE = 10; +} + +class TopToBottomOrientation : public Orientation { + const int CELL_WIDTH = 74; + const int CELL_HEIGHT = 20; + const int CELL_DRAG_WIDTH = 7; + const int EXTENDER_WIDTH = 18; + const int EXTENDER_HEIGHT = 8; + const int SOUND_PREVIEW_WIDTH = 7; + const int LAYER_HEADER_HEIGHT = CELL_HEIGHT * 3 + 60; + const int FOLDED_LAYER_HEADER_HEIGHT = LAYER_HEADER_HEIGHT; + const int FOLDED_LAYER_HEADER_WIDTH = 8; + const int FRAME_HEADER_WIDTH = CELL_WIDTH; + const int PLAY_RANGE_X = FRAME_HEADER_WIDTH / 2 - PLAY_MARKER_SIZE; + const int ONION_X = 0, ONION_Y = 0; + const int ICON_WIDTH = CELL_HEIGHT; + +public: + TopToBottomOrientation(); + + virtual CellPosition xyToPosition(const QPoint &xy, + const ColumnFan *fan) const override; + virtual QPoint positionToXY(const CellPosition &position, + const ColumnFan *fan) const override; + virtual CellPositionRatio xyToPositionRatio(const QPoint &xy) const override; + virtual QPoint positionRatioToXY( + const CellPositionRatio &ratio) const override; + + virtual int colToLayerAxis(int layer, const ColumnFan *fan) const override; + virtual int rowToFrameAxis(int frame) const override; + + virtual QPoint frameLayerToXY(int frameAxis, int layerAxis) const override; + virtual int layerAxis(const QPoint &xy) const override; + virtual int frameAxis(const QPoint &xy) const override; + + virtual NumberRange layerSide(const QRect &area) const override; + virtual NumberRange frameSide(const QRect &area) const override; + virtual QPoint topRightCorner(const QRect &area) const override; + + virtual CellPosition arrowShift(int direction) const override; + + virtual QString name() const override { return "TopToBottom"; } + virtual QString caption() const override { return "Xsheet"; } + virtual const Orientation *next() const override { + return Orientations::leftToRight(); + } + + virtual bool isVerticalTimeline() const override { return true; } + virtual bool flipVolume() const override { return true; } + + virtual int cellWidth() const override { return CELL_WIDTH; } + virtual int cellHeight() const override { return CELL_HEIGHT; } +}; + +class LeftToRightOrientation : public Orientation { + const int CELL_WIDTH = 50; + const int CELL_HEIGHT = 20; + const int CELL_DRAG_HEIGHT = 5; + const int EXTENDER_WIDTH = 8; + const int EXTENDER_HEIGHT = 12; + const int SOUND_PREVIEW_HEIGHT = 6; + const int FRAME_HEADER_HEIGHT = 50; + const int ONION_X = (CELL_WIDTH - ONION_SIZE) / 2, ONION_Y = 0; + const int PLAY_RANGE_Y = ONION_SIZE; + const int ICON_WIDTH = CELL_HEIGHT; + const int ICON_OFFSET = ICON_WIDTH; + const int ICONS_WIDTH = ICON_OFFSET * 3; // 88 + const int LAYER_NUMBER_WIDTH = 20; + const int LAYER_NAME_WIDTH = 170; + const int LAYER_HEADER_WIDTH = + ICONS_WIDTH + LAYER_NUMBER_WIDTH + LAYER_NAME_WIDTH; + const int FOLDED_LAYER_HEADER_HEIGHT = 8; + const int FOLDED_LAYER_HEADER_WIDTH = LAYER_HEADER_WIDTH; + +public: + LeftToRightOrientation(); + + virtual CellPosition xyToPosition(const QPoint &xy, + const ColumnFan *fan) const override; + virtual QPoint positionToXY(const CellPosition &position, + const ColumnFan *fan) const override; + virtual CellPositionRatio xyToPositionRatio(const QPoint &xy) const override; + virtual QPoint positionRatioToXY( + const CellPositionRatio &ratio) const override; + + virtual int colToLayerAxis(int layer, const ColumnFan *fan) const override; + virtual int rowToFrameAxis(int frame) const override; + + virtual QPoint frameLayerToXY(int frameAxis, int layerAxis) const override; + virtual int layerAxis(const QPoint &xy) const override; + virtual int frameAxis(const QPoint &xy) const override; + + virtual NumberRange layerSide(const QRect &area) const override; + virtual NumberRange frameSide(const QRect &area) const override; + virtual QPoint topRightCorner(const QRect &area) const override; + + virtual CellPosition arrowShift(int direction) const override; + + virtual QString name() const override { return "LeftToRight"; } + virtual QString caption() const override { return "Timeline"; } + virtual const Orientation *next() const override { + return Orientations::topToBottom(); + } + + virtual bool isVerticalTimeline() const override { return false; } + virtual bool flipVolume() const override { return false; } + + virtual int cellWidth() const override { return CELL_WIDTH; } + virtual int cellHeight() const override { return CELL_HEIGHT; } +}; + +/// ------------------------------------------------------------------------------- + +int NumberRange::weight(double toWeight) const { // weight ranging 0..1 + return _from + (_to - _from) * toWeight; +} + +NumberRange NumberRange::adjusted(int addFrom, int addTo) const { + return NumberRange(_from + addFrom, _to + addTo); +} + +double NumberRange::ratio(int at) const { + double result = ((double)at - _from) / (_to - _from); + if (result < 0) result = 0; + if (result > 1) result = 1; + return result; +} + +/// ------------------------------------------------------------------------------- + +// const int Orientations::COUNT = 2; + +Orientations::Orientations() : _topToBottom(nullptr), _leftToRight(nullptr) { + _topToBottom = new TopToBottomOrientation(); + _leftToRight = new LeftToRightOrientation(); + + _all.push_back(_topToBottom); + _all.push_back(_leftToRight); +} +Orientations::~Orientations() { + delete _topToBottom; + _topToBottom = nullptr; + delete _leftToRight; + _leftToRight = nullptr; +} + +const Orientations &Orientations::instance() { + static Orientations singleton; + return singleton; +} + +const Orientation *Orientations::topToBottom() { + return instance()._topToBottom; +} +const Orientation *Orientations::leftToRight() { + return instance()._leftToRight; +} +const vector &Orientations::all() { + return instance()._all; +} +const Orientation *Orientations::byName(const QString &name) { + vector m_all = all(); + for (auto it = m_all.begin(); it != m_all.end(); it++) + if ((*it)->name() == name) return *it; + throw std::runtime_error( + (QString("no such orientation: ") + name).toStdString().c_str()); +} + +/// ------------------------------------------------------------------------------- + +QLine Orientation::verticalLine(int layerAxis, + const NumberRange &frameAxis) const { + QPoint first = frameLayerToXY(frameAxis.from(), layerAxis); + QPoint second = frameLayerToXY(frameAxis.to(), layerAxis); + return QLine(first, second); +} +QLine Orientation::horizontalLine(int frameAxis, + const NumberRange &layerAxis) const { + QPoint first = frameLayerToXY(frameAxis, layerAxis.from()); + QPoint second = frameLayerToXY(frameAxis, layerAxis.to()); + return QLine(first, second); +} +QRect Orientation::frameLayerRect(const NumberRange &frameAxis, + const NumberRange &layerAxis) const { + QPoint topLeft = frameLayerToXY(frameAxis.from(), layerAxis.from()); + QPoint bottomRight = frameLayerToXY(frameAxis.to(), layerAxis.to()); + return QRect(topLeft, bottomRight); +} + +QRect Orientation::foldedRectangle(int layerAxis, const NumberRange &frameAxis, + int i) const { + QPoint topLeft = frameLayerToXY(frameAxis.from(), layerAxis + 1 + i * 3); + QPoint size = frameLayerToXY(frameAxis.length(), 2); + return QRect(topLeft, QSize(size.x(), size.y())); +} +QLine Orientation::foldedRectangleLine(int layerAxis, + const NumberRange &frameAxis, + int i) const { + return verticalLine(layerAxis + i * 3, frameAxis); +} + +void Orientation::addRect(PredefinedRect which, const QRect &rect) { + _rects.insert(pair(which, rect)); +} +void Orientation::addLine(PredefinedLine which, const QLine &line) { + _lines.insert(pair(which, line)); +} +void Orientation::addDimension(PredefinedDimension which, int dimension) { + _dimensions.insert(pair(which, dimension)); +} +void Orientation::addPath(PredefinedPath which, const QPainterPath &path) { + _paths.insert(pair(which, path)); +} +void Orientation::addPoint(PredefinedPoint which, const QPoint &point) { + _points.insert(pair(which, point)); +} +void Orientation::addRange(PredefinedRange which, const NumberRange &range) { + _ranges.insert(pair(which, range)); +} + +/// ------------------------------------------------------------------------------- + +TopToBottomOrientation::TopToBottomOrientation() { + // + // Area rectangles + // + + // Cell viewer + QRect cellRect(0, 0, CELL_WIDTH, CELL_HEIGHT); + addRect(PredefinedRect::CELL, cellRect); + addRect(PredefinedRect::DRAG_HANDLE_CORNER, QRect(0, 0, CELL_DRAG_WIDTH, CELL_HEIGHT)); + QRect keyRect(CELL_WIDTH - KEY_ICON_WIDTH, (CELL_HEIGHT - KEY_ICON_HEIGHT) / 2, KEY_ICON_WIDTH, KEY_ICON_HEIGHT); + addRect(PredefinedRect::KEY_ICON, keyRect); + QRect nameRect = cellRect.adjusted(8, 0, -6, 0); + addRect(PredefinedRect::CELL_NAME, nameRect); + addRect(PredefinedRect::CELL_NAME_WITH_KEYFRAME, nameRect.adjusted(0, 0, -KEY_ICON_WIDTH, 0)); + addRect(PredefinedRect::END_EXTENDER, QRect(-EXTENDER_WIDTH - KEY_ICON_WIDTH, 0, EXTENDER_WIDTH, EXTENDER_HEIGHT)); + addRect(PredefinedRect::BEGIN_EXTENDER, QRect(-EXTENDER_WIDTH - KEY_ICON_WIDTH, -EXTENDER_HEIGHT, EXTENDER_WIDTH, EXTENDER_HEIGHT)); + addRect(PredefinedRect::KEYFRAME_AREA, QRect(CELL_WIDTH - KEY_ICON_WIDTH, 0, KEY_ICON_WIDTH, CELL_HEIGHT)); + addRect(PredefinedRect::DRAG_AREA, QRect(0, 0, CELL_DRAG_WIDTH, CELL_HEIGHT)); + QRect soundRect(CELL_DRAG_WIDTH, 0, CELL_WIDTH - CELL_DRAG_WIDTH - SOUND_PREVIEW_WIDTH, CELL_HEIGHT); + addRect(PredefinedRect::SOUND_TRACK, soundRect); + addRect(PredefinedRect::PREVIEW_TRACK, QRect(CELL_WIDTH - SOUND_PREVIEW_WIDTH + 1, 0, SOUND_PREVIEW_WIDTH, CELL_HEIGHT)); + addRect(PredefinedRect::BEGIN_SOUND_EDIT, QRect(CELL_DRAG_WIDTH, 0, CELL_WIDTH - CELL_DRAG_WIDTH, 2)); + addRect(PredefinedRect::END_SOUND_EDIT, QRect(CELL_DRAG_WIDTH, CELL_HEIGHT - 2, CELL_WIDTH - CELL_DRAG_WIDTH, 2)); + addRect(PredefinedRect::LOOP_ICON, QRect(keyRect.left(), 0, 10, 11)); + + // Note viewer + addRect(PredefinedRect::NOTE_AREA, QRect(QPoint(0, 0), QSize(FRAME_HEADER_WIDTH, LAYER_HEADER_HEIGHT - 1))); + addRect(PredefinedRect::NOTE_ICON, QRect(QPoint(0, 0), QSize(CELL_WIDTH - 2, CELL_HEIGHT - 2))); + addRect(PredefinedRect::LAYER_HEADER_PANEL, QRect(0, 0, -1, -1)); // hide + + // Row viewer + addRect(PredefinedRect::FRAME_LABEL, QRect(CELL_WIDTH / 2, 1, CELL_WIDTH / 2, CELL_HEIGHT - 2)); + addRect(PredefinedRect::FRAME_HEADER, QRect(0, 0, FRAME_HEADER_WIDTH - 1, CELL_HEIGHT)); + addRect(PredefinedRect::PLAY_RANGE, QRect(PLAY_RANGE_X, 0, PLAY_MARKER_SIZE, CELL_HEIGHT)); + addRect(PredefinedRect::ONION, QRect(ONION_X + (3 * ONION_DOT_SIZE - ONION_SIZE) / 2, ONION_Y, ONION_SIZE, ONION_SIZE)); + int adjustOnion = (ONION_SIZE - ONION_DOT_SIZE) / 2; + addRect(PredefinedRect::ONION_DOT, QRect(ONION_X + ONION_DOT_SIZE, ONION_Y + adjustOnion, ONION_DOT_SIZE, ONION_DOT_SIZE)); + addRect(PredefinedRect::ONION_DOT_FIXED, QRect(ONION_X, ONION_Y + adjustOnion, ONION_DOT_SIZE, ONION_DOT_SIZE)); + addRect(PredefinedRect::ONION_AREA, QRect(ONION_X, ONION_Y, PLAY_RANGE_X, CELL_HEIGHT)); + addRect(PredefinedRect::ONION_FIXED_DOT_AREA, QRect(ONION_X, ONION_Y, ONION_DOT_SIZE, CELL_HEIGHT)); + addRect(PredefinedRect::ONION_DOT_AREA, QRect(ONION_X + ONION_DOT_SIZE, ONION_Y, ONION_DOT_SIZE, CELL_HEIGHT)); + addRect(PredefinedRect::PINNED_CENTER_KEY, QRect((FRAME_HEADER_WIDTH - PINNED_SIZE) / 2, (CELL_HEIGHT - PINNED_SIZE) / 2, PINNED_SIZE, PINNED_SIZE)); + + // Column viewer + static int INDENT = 9; + static int HDRROW1 = 6; // Name/eye + static int HDRROW2 = HDRROW1 + CELL_HEIGHT; // lock, preview + static int HDRROW3 = HDRROW2 + CELL_HEIGHT + 1; // thumbnail + static int HDRROW4 = HDRROW3 + 48; // pegbar, parenthandle + + addRect(PredefinedRect::LAYER_HEADER, QRect(0, 1, CELL_WIDTH, LAYER_HEADER_HEIGHT - 3)); + addRect(PredefinedRect::DRAG_LAYER, QRect(0, 0, CELL_DRAG_WIDTH, LAYER_HEADER_HEIGHT - 3)); + addRect(PredefinedRect::FOLDED_LAYER_HEADER, QRect(0, 1, FOLDED_LAYER_HEADER_WIDTH, FOLDED_LAYER_HEADER_HEIGHT - 3)); + + addRect(PredefinedRect::RENAME_COLUMN, QRect(0, 6, CELL_WIDTH, CELL_HEIGHT - 3)); + + QRect layername(INDENT, HDRROW1, CELL_WIDTH - 11, CELL_HEIGHT - 3); + addRect(PredefinedRect::LAYER_NAME, layername); + addRect(PredefinedRect::LAYER_NUMBER, QRect(0, 0, -1, -1)); // hide + + QRect eyeArea(INDENT, HDRROW1, CELL_WIDTH - 11, CELL_HEIGHT - 3); + addRect(PredefinedRect::EYE_AREA, eyeArea); + QRect eye(eyeArea.right() - 18, HDRROW1, 18, 15); + addRect(PredefinedRect::EYE, eye); + + QRect previewArea(INDENT, HDRROW2, CELL_WIDTH - 11, CELL_HEIGHT - 3); + addRect(PredefinedRect::PREVIEW_LAYER_AREA, previewArea); + QRect preview(previewArea.right() - 18, HDRROW2, 18, 15); + addRect(PredefinedRect::PREVIEW_LAYER, preview); + + QRect lockArea(INDENT, HDRROW2, 16, 16); + addRect(PredefinedRect::LOCK_AREA, lockArea); + QRect lock(INDENT, HDRROW2, 16, 16); + addRect(PredefinedRect::LOCK, lock); + + QRect thumbnailArea(INDENT, HDRROW3, CELL_WIDTH - 11, 42); + addRect(PredefinedRect::THUMBNAIL_AREA, thumbnailArea); + QRect thumbnail(INDENT, HDRROW3, CELL_WIDTH - 11, 42); + addRect(PredefinedRect::THUMBNAIL, thumbnail); + addRect(PredefinedRect::FILTER_COLOR, QRect(thumbnailArea.right() - 14, thumbnailArea.top() + 3, 12, 12)); + addRect(PredefinedRect::SOUND_ICON, QRect(thumbnailArea.right() - 40, 3 * CELL_HEIGHT + 4, 40, 30)); + int trackLen = 60; + QRect volumeArea(thumbnailArea.left(), thumbnailArea.top() + 1, 29 - CELL_DRAG_WIDTH, trackLen + 7); + addRect(PredefinedRect::VOLUME_AREA, volumeArea); + QPoint soundTopLeft(volumeArea.left() + 12, volumeArea.top() + 4); + addRect(PredefinedRect::VOLUME_TRACK, QRect(soundTopLeft, QSize(3, trackLen))); + + QRect pegbarname(INDENT, HDRROW4, CELL_WIDTH - 11, CELL_HEIGHT - 3); + addRect(PredefinedRect::PEGBAR_NAME, pegbarname); + addRect(PredefinedRect::PARENT_HANDLE_NAME, QRect(INDENT + pegbarname.width() - 20, HDRROW4, 20, CELL_HEIGHT - 3)); + + // + // Lines + // + addLine(PredefinedLine::LOCKED, verticalLine((CELL_DRAG_WIDTH + 1) / 2, NumberRange(0, CELL_HEIGHT))); + addLine(PredefinedLine::SEE_MARKER_THROUGH, horizontalLine(0, NumberRange(0, CELL_DRAG_WIDTH))); + addLine(PredefinedLine::CONTINUE_LEVEL, verticalLine(CELL_WIDTH / 2, NumberRange(0, CELL_HEIGHT))); + addLine(PredefinedLine::CONTINUE_LEVEL_WITH_NAME, verticalLine(CELL_WIDTH - 11, NumberRange(0, CELL_HEIGHT))); + addLine(PredefinedLine::EXTENDER_LINE, horizontalLine(0, NumberRange(-EXTENDER_WIDTH - KEY_ICON_WIDTH, 0))); + + // + // Dimensions + // + addDimension(PredefinedDimension::LAYER, CELL_WIDTH); + addDimension(PredefinedDimension::FRAME, CELL_HEIGHT); + addDimension(PredefinedDimension::INDEX, 0); + addDimension(PredefinedDimension::SOUND_AMPLITUDE, int(sqrt(CELL_HEIGHT * soundRect.width()) / 2)); + addDimension(PredefinedDimension::FRAME_LABEL_ALIGN, Qt::AlignCenter); + addDimension(PredefinedDimension::ONION_TURN, 0); + addDimension(PredefinedDimension::QBOXLAYOUT_DIRECTION, QBoxLayout::Direction::TopToBottom); + addDimension(PredefinedDimension::CENTER_ALIGN, Qt::AlignHCenter); + + // + // Paths + // + QPainterPath corner(QPointF(0, CELL_HEIGHT)); + corner.lineTo(QPointF(CELL_DRAG_WIDTH, CELL_HEIGHT)); + corner.lineTo(QPointF(CELL_DRAG_WIDTH, CELL_HEIGHT - CELL_DRAG_WIDTH)); + corner.lineTo(QPointF(0, CELL_HEIGHT)); + addPath(PredefinedPath::DRAG_HANDLE_CORNER, corner); + + QPainterPath fromTriangle(QPointF(0, EASE_TRIANGLE_SIZE / 2)); + fromTriangle.lineTo(QPointF(EASE_TRIANGLE_SIZE, -EASE_TRIANGLE_SIZE / 2)); + fromTriangle.lineTo(QPointF(-EASE_TRIANGLE_SIZE, -EASE_TRIANGLE_SIZE / 2)); + fromTriangle.lineTo(QPointF(0, EASE_TRIANGLE_SIZE / 2)); + fromTriangle.translate(keyRect.center()); + addPath(PredefinedPath::BEGIN_EASE_TRIANGLE, fromTriangle); + + QPainterPath toTriangle(QPointF(0, -EASE_TRIANGLE_SIZE / 2)); + toTriangle.lineTo(QPointF(EASE_TRIANGLE_SIZE, EASE_TRIANGLE_SIZE / 2)); + toTriangle.lineTo(QPointF(-EASE_TRIANGLE_SIZE, EASE_TRIANGLE_SIZE / 2)); + toTriangle.lineTo(QPointF(0, -EASE_TRIANGLE_SIZE / 2)); + toTriangle.translate(keyRect.center()); + addPath(PredefinedPath::END_EASE_TRIANGLE, toTriangle); + + QPainterPath playFrom(QPointF(0, 0)); + playFrom.lineTo(QPointF(PLAY_MARKER_SIZE, 0)); + playFrom.lineTo(QPointF(0, PLAY_MARKER_SIZE)); + playFrom.lineTo(QPointF(0, 0)); + playFrom.translate(PLAY_RANGE_X, 1); + addPath(PredefinedPath::BEGIN_PLAY_RANGE, playFrom); + + QPainterPath playTo(QPointF(0, 0)); + playTo.lineTo(QPointF(PLAY_MARKER_SIZE, 0)); + playTo.lineTo(QPointF(0, -PLAY_MARKER_SIZE)); + playTo.lineTo(QPointF(0, 0)); + playTo.translate(PLAY_RANGE_X, CELL_HEIGHT - 1); + addPath(PredefinedPath::END_PLAY_RANGE, playTo); + + QPainterPath track(QPointF(0, 0)); + track.lineTo(QPointF(1, 1)); + track.lineTo(QPointF(1, trackLen - 1)); + track.lineTo(QPointF(0, trackLen)); + track.lineTo(QPointF(-1, trackLen - 1)); + track.lineTo(QPointF(-1, 1)); + track.lineTo(QPointF(0, 0)); + track.translate(soundTopLeft); + addPath(PredefinedPath::VOLUME_SLIDER_TRACK, track); + + QPainterPath head(QPointF(0, 0)); + head.lineTo(QPointF(4, 4)); + head.lineTo(QPointF(8, 4)); + head.lineTo(QPointF(8, -4)); + head.lineTo(QPointF(4, -4)); + head.lineTo(QPointF(0, 0)); + addPath(PredefinedPath::VOLUME_SLIDER_HEAD, head); + + // + // Points + // + addPoint(PredefinedPoint::KEY_HIDDEN, QPoint(KEY_ICON_WIDTH, 0)); + addPoint(PredefinedPoint::EXTENDER_XY_RADIUS, QPoint(30, 75)); + addPoint(PredefinedPoint::VOLUME_DIVISIONS_TOP_LEFT, soundTopLeft - QPoint(5, 0)); + + // + // Ranges + // + addRange(PredefinedRange::HEADER_LAYER, NumberRange(0, FRAME_HEADER_WIDTH)); + addRange(PredefinedRange::HEADER_FRAME, NumberRange(0, LAYER_HEADER_HEIGHT)); +} + +CellPosition TopToBottomOrientation::xyToPosition(const QPoint &xy, + const ColumnFan *fan) const { + int layer = fan->layerAxisToCol(xy.x()); + int frame = xy.y() / CELL_HEIGHT; + return CellPosition(frame, layer); +} +QPoint TopToBottomOrientation::positionToXY(const CellPosition &position, + const ColumnFan *fan) const { + int x = colToLayerAxis(position.layer(), fan); + int y = rowToFrameAxis(position.frame()); + return QPoint(x, y); +} +CellPositionRatio TopToBottomOrientation::xyToPositionRatio( + const QPoint &xy) const { + Ratio frame{xy.y(), CELL_HEIGHT}; + Ratio layer{xy.x(), CELL_WIDTH}; + return CellPositionRatio{frame, layer}; +} +QPoint TopToBottomOrientation::positionRatioToXY( + const CellPositionRatio &ratio) const { + int x = ratio.layer() * CELL_WIDTH; + int y = ratio.frame() * CELL_HEIGHT; + return QPoint(x, y); +} + +int TopToBottomOrientation::colToLayerAxis(int layer, + const ColumnFan *fan) const { + return fan->colToLayerAxis(layer); +} +int TopToBottomOrientation::rowToFrameAxis(int frame) const { + return frame * CELL_HEIGHT; +} +QPoint TopToBottomOrientation::frameLayerToXY(int frameAxis, + int layerAxis) const { + return QPoint(layerAxis, frameAxis); +} +int TopToBottomOrientation::layerAxis(const QPoint &xy) const { return xy.x(); } +int TopToBottomOrientation::frameAxis(const QPoint &xy) const { return xy.y(); } +NumberRange TopToBottomOrientation::layerSide(const QRect &area) const { + return NumberRange(area.left(), area.right()); +} +NumberRange TopToBottomOrientation::frameSide(const QRect &area) const { + return NumberRange(area.top(), area.bottom()); +} +QPoint TopToBottomOrientation::topRightCorner(const QRect &area) const { + return area.topRight(); +} + +CellPosition TopToBottomOrientation::arrowShift(int direction) const { + switch (direction) { + case Qt::Key_Up: + return CellPosition(-1, 0); + case Qt::Key_Down: + return CellPosition(1, 0); + case Qt::Key_Left: + return CellPosition(0, -1); + case Qt::Key_Right: + return CellPosition(0, 1); + default: + return CellPosition(0, 0); + } +} + +/// -------------------------------------------------------------------------------- + +LeftToRightOrientation::LeftToRightOrientation() { + // + // Ranges + // + + // Cell viewer + QRect cellRect(0, 0, CELL_WIDTH, CELL_HEIGHT); + addRect(PredefinedRect::CELL, cellRect); + addRect(PredefinedRect::DRAG_HANDLE_CORNER, QRect(0, 0, CELL_WIDTH, CELL_DRAG_HEIGHT)); + QRect keyRect((CELL_WIDTH - KEY_ICON_WIDTH) / 2, CELL_HEIGHT - KEY_ICON_HEIGHT, KEY_ICON_WIDTH, KEY_ICON_HEIGHT); + addRect(PredefinedRect::KEY_ICON, keyRect); + QRect nameRect = cellRect.adjusted(4, 4, -6, 0); + addRect(PredefinedRect::CELL_NAME, nameRect); + addRect(PredefinedRect::CELL_NAME_WITH_KEYFRAME, nameRect); + addRect(PredefinedRect::END_EXTENDER, QRect(0, -EXTENDER_HEIGHT - 10, EXTENDER_WIDTH, EXTENDER_HEIGHT)); + addRect(PredefinedRect::BEGIN_EXTENDER, QRect(-EXTENDER_WIDTH, -EXTENDER_HEIGHT - 10, EXTENDER_WIDTH, EXTENDER_HEIGHT)); + addRect(PredefinedRect::KEYFRAME_AREA, keyRect); + addRect(PredefinedRect::DRAG_AREA, QRect(0, 0, CELL_WIDTH, CELL_DRAG_HEIGHT)); + QRect soundRect(0, CELL_DRAG_HEIGHT, CELL_WIDTH, CELL_HEIGHT - CELL_DRAG_HEIGHT - SOUND_PREVIEW_HEIGHT); + addRect(PredefinedRect::SOUND_TRACK, soundRect); + addRect(PredefinedRect::PREVIEW_TRACK, QRect(0, CELL_HEIGHT - SOUND_PREVIEW_HEIGHT + 1, CELL_WIDTH, SOUND_PREVIEW_HEIGHT)); + addRect(PredefinedRect::BEGIN_SOUND_EDIT, QRect(0, CELL_DRAG_HEIGHT, 2, CELL_HEIGHT - CELL_DRAG_HEIGHT)); + addRect(PredefinedRect::END_SOUND_EDIT, QRect(CELL_WIDTH - 2, CELL_DRAG_HEIGHT, 2, CELL_HEIGHT - CELL_DRAG_HEIGHT)); + addRect(PredefinedRect::LOOP_ICON, QRect(0, keyRect.top(), 10, 11)); + + // Notes viewer + addRect(PredefinedRect::NOTE_AREA, QRect(QPoint(0, 0), QSize(LAYER_HEADER_WIDTH - 1, FRAME_HEADER_HEIGHT))); + addRect(PredefinedRect::NOTE_ICON, QRect(QPoint(0, 0), QSize(CELL_WIDTH - 2, CELL_HEIGHT - 2))); + addRect(PredefinedRect::LAYER_HEADER_PANEL, QRect(0, FRAME_HEADER_HEIGHT - CELL_HEIGHT, LAYER_HEADER_WIDTH, CELL_HEIGHT)); + + // Row viewer + addRect(PredefinedRect::FRAME_LABEL, QRect(CELL_WIDTH / 4, 1, CELL_WIDTH / 2, FRAME_HEADER_HEIGHT - 2)); + addRect(PredefinedRect::FRAME_HEADER, QRect(0, 0, CELL_WIDTH, FRAME_HEADER_HEIGHT - 1)); + addRect(PredefinedRect::PLAY_RANGE, QRect(0, PLAY_RANGE_Y, CELL_WIDTH, PLAY_MARKER_SIZE)); + addRect(PredefinedRect::ONION, QRect(ONION_X, ONION_Y + (3 * ONION_DOT_SIZE - ONION_SIZE) / 2, ONION_SIZE, ONION_SIZE)); + int adjustOnion = (ONION_SIZE - ONION_DOT_SIZE) / 2; + addRect(PredefinedRect::ONION_DOT, QRect(ONION_X + adjustOnion, ONION_Y + ONION_DOT_SIZE, ONION_DOT_SIZE, ONION_DOT_SIZE)); + addRect(PredefinedRect::ONION_DOT_FIXED, QRect(ONION_X + adjustOnion, ONION_Y, ONION_DOT_SIZE, ONION_DOT_SIZE)); + addRect(PredefinedRect::ONION_AREA, QRect(ONION_X, ONION_Y, CELL_WIDTH, ONION_SIZE)); + addRect(PredefinedRect::ONION_FIXED_DOT_AREA, QRect(ONION_X, ONION_Y, CELL_WIDTH, ONION_DOT_SIZE)); + addRect(PredefinedRect::ONION_DOT_AREA, QRect(ONION_X, ONION_Y + ONION_DOT_SIZE, CELL_WIDTH, ONION_DOT_SIZE)); + addRect(PredefinedRect::PINNED_CENTER_KEY, QRect((CELL_WIDTH - PINNED_SIZE) / 2, (FRAME_HEADER_HEIGHT - PINNED_SIZE) / 2, PINNED_SIZE, PINNED_SIZE)); + + // Column viewer + addRect(PredefinedRect::LAYER_HEADER, QRect(1, 0, LAYER_HEADER_WIDTH - 3, CELL_HEIGHT)); + addRect(PredefinedRect::FOLDED_LAYER_HEADER, QRect(1, 0, FOLDED_LAYER_HEADER_WIDTH - 3, FOLDED_LAYER_HEADER_HEIGHT)); + QRect columnName(ICONS_WIDTH + 1, 0, LAYER_NAME_WIDTH + LAYER_NUMBER_WIDTH - 3, CELL_HEIGHT); + addRect(PredefinedRect::RENAME_COLUMN, columnName); + QRect eye(1, 0, ICON_WIDTH, CELL_HEIGHT); + addRect(PredefinedRect::EYE_AREA, eye); + addRect(PredefinedRect::EYE, eye.adjusted(1, 1, 0, 0)); + addRect(PredefinedRect::PREVIEW_LAYER_AREA, eye.translated(ICON_OFFSET, 0)); + addRect(PredefinedRect::PREVIEW_LAYER, eye.translated(ICON_OFFSET + 1, 0).adjusted(-1, 1, -1, 0)); + addRect(PredefinedRect::LOCK_AREA, eye.translated(2 * ICON_OFFSET, 0)); + addRect(PredefinedRect::LOCK, eye.translated(2 * ICON_OFFSET + 1, 0).adjusted(-1, 1, -1, 0)); + addRect(PredefinedRect::DRAG_LAYER, QRect(ICONS_WIDTH + 1, 0, LAYER_HEADER_WIDTH - ICONS_WIDTH - 3, CELL_DRAG_HEIGHT)); + addRect(PredefinedRect::LAYER_NAME, columnName); + addRect(PredefinedRect::LAYER_NUMBER, QRect(ICONS_WIDTH + 1, 0, LAYER_NUMBER_WIDTH, CELL_HEIGHT)); + addRect(PredefinedRect::THUMBNAIL_AREA, QRect(0, 0, -1, -1)); // hide + addRect(PredefinedRect::FILTER_COLOR, QRect(LAYER_HEADER_WIDTH - 17, 6, 12, 12)); + addRect(PredefinedRect::PEGBAR_NAME, QRect(0, 0, -1, -1)); // hide + addRect(PredefinedRect::PARENT_HANDLE_NAME, QRect(0, 0, -1, -1)); // hide + + int trackLen = 60; + QRect volumeArea(QRect(columnName.topRight(), QSize(trackLen + 8, columnName.height())).adjusted(-97, 0, -97, 0)); + addRect(PredefinedRect::VOLUME_AREA, volumeArea); + QPoint soundTopLeft(volumeArea.left() + 4, volumeArea.top() + 12); + addRect(PredefinedRect::VOLUME_TRACK, QRect(soundTopLeft, QSize(trackLen, 3))); + addRect(PredefinedRect::SOUND_ICON, QRect(columnName.topRight(), QSize(27, columnName.height())).adjusted(-28, 0, -28, 0)); + + // + // Lines + // + addLine(PredefinedLine::LOCKED, verticalLine(CELL_DRAG_HEIGHT / 2, NumberRange(0, CELL_WIDTH))); + addLine(PredefinedLine::SEE_MARKER_THROUGH, horizontalLine(0, NumberRange(0, CELL_DRAG_HEIGHT))); + addLine(PredefinedLine::CONTINUE_LEVEL, verticalLine(CELL_HEIGHT / 2, NumberRange(0, CELL_WIDTH))); + addLine(PredefinedLine::CONTINUE_LEVEL_WITH_NAME, verticalLine(CELL_HEIGHT / 2, NumberRange(0, CELL_WIDTH))); + addLine(PredefinedLine::EXTENDER_LINE, horizontalLine(0, NumberRange(-EXTENDER_WIDTH - KEY_ICON_WIDTH, 0))); + + // + // Dimensions + // + addDimension(PredefinedDimension::LAYER, CELL_HEIGHT); + addDimension(PredefinedDimension::FRAME, CELL_WIDTH); + addDimension(PredefinedDimension::INDEX, 1); + addDimension(PredefinedDimension::SOUND_AMPLITUDE, soundRect.height() / 2); + addDimension(PredefinedDimension::FRAME_LABEL_ALIGN, Qt::AlignHCenter | Qt::AlignBottom | Qt::TextWordWrap); + addDimension(PredefinedDimension::ONION_TURN, 90); + addDimension(PredefinedDimension::QBOXLAYOUT_DIRECTION, QBoxLayout::Direction::LeftToRight); + addDimension(PredefinedDimension::CENTER_ALIGN, Qt::AlignVCenter); + + // + // Paths + // + QPainterPath corner(QPointF(CELL_WIDTH, 0)); + corner.lineTo(QPointF(CELL_WIDTH, CELL_DRAG_HEIGHT)); + corner.lineTo(QPointF(CELL_WIDTH - CELL_DRAG_HEIGHT, CELL_DRAG_HEIGHT)); + corner.lineTo(QPointF(CELL_WIDTH, 0)); + addPath(PredefinedPath::DRAG_HANDLE_CORNER, corner); + + QPainterPath fromTriangle(QPointF(EASE_TRIANGLE_SIZE / 2, 0)); + fromTriangle.lineTo(QPointF(-EASE_TRIANGLE_SIZE / 2, EASE_TRIANGLE_SIZE)); + fromTriangle.lineTo(QPointF(-EASE_TRIANGLE_SIZE / 2, -EASE_TRIANGLE_SIZE)); + fromTriangle.lineTo(QPointF(EASE_TRIANGLE_SIZE / 2, 0)); + fromTriangle.translate(keyRect.center()); + addPath(PredefinedPath::BEGIN_EASE_TRIANGLE, fromTriangle); + + QPainterPath toTriangle(QPointF(-EASE_TRIANGLE_SIZE / 2, 0)); + toTriangle.lineTo(QPointF(EASE_TRIANGLE_SIZE / 2, EASE_TRIANGLE_SIZE)); + toTriangle.lineTo(QPointF(EASE_TRIANGLE_SIZE / 2, -EASE_TRIANGLE_SIZE)); + toTriangle.lineTo(QPointF(-EASE_TRIANGLE_SIZE / 2, 0)); + toTriangle.translate(keyRect.center()); + addPath(PredefinedPath::END_EASE_TRIANGLE, toTriangle); + + QPainterPath playFrom(QPointF(0, 0)); + playFrom.lineTo(QPointF(PLAY_MARKER_SIZE, 0)); + playFrom.lineTo(QPointF(0, PLAY_MARKER_SIZE)); + playFrom.lineTo(QPointF(0, 0)); + playFrom.translate(1, PLAY_RANGE_Y); + addPath(PredefinedPath::BEGIN_PLAY_RANGE, playFrom); + + QPainterPath playTo(QPointF(0, 0)); + playTo.lineTo(QPointF(-PLAY_MARKER_SIZE, 0)); + playTo.lineTo(QPointF(0, PLAY_MARKER_SIZE)); + playTo.lineTo(QPointF(0, 0)); + playTo.translate(CELL_WIDTH - 1, PLAY_RANGE_Y); + addPath(PredefinedPath::END_PLAY_RANGE, playTo); + + QPainterPath track(QPointF(0, 0)); + track.lineTo(QPointF(1, 1)); + track.lineTo(QPointF(trackLen - 1, 1)); + track.lineTo(QPointF(trackLen, 0)); + track.lineTo(QPointF(trackLen - 1, -1)); + track.lineTo(QPointF(1, -1)); + track.lineTo(QPointF(0, 0)); + track.translate(soundTopLeft); + addPath(PredefinedPath::VOLUME_SLIDER_TRACK, track); + + QPainterPath head(QPointF(0, 0)); + head.lineTo(QPointF(4, -4)); + head.lineTo(QPointF(4, -8)); + head.lineTo(QPointF(-4, -8)); + head.lineTo(QPointF(-4, -4)); + head.lineTo(QPointF(0, 0)); + addPath(PredefinedPath::VOLUME_SLIDER_HEAD, head); + + // + // Points + // + addPoint(PredefinedPoint::KEY_HIDDEN, QPoint(0, 10)); + addPoint(PredefinedPoint::EXTENDER_XY_RADIUS, QPoint(75, 30)); + addPoint(PredefinedPoint::VOLUME_DIVISIONS_TOP_LEFT, soundTopLeft + QPoint(0, 3)); + + // + // Ranges + // + addRange(PredefinedRange::HEADER_LAYER, NumberRange(0, FRAME_HEADER_HEIGHT)); + addRange(PredefinedRange::HEADER_FRAME, NumberRange(0, LAYER_HEADER_WIDTH)); +} + +CellPosition LeftToRightOrientation::xyToPosition(const QPoint &xy, + const ColumnFan *fan) const { + int layer = fan->layerAxisToCol(xy.y()); + int frame = xy.x() / CELL_WIDTH; + return CellPosition(frame, layer); +} +QPoint LeftToRightOrientation::positionToXY(const CellPosition &position, + const ColumnFan *fan) const { + int y = colToLayerAxis(position.layer(), fan); + int x = rowToFrameAxis(position.frame()); + return QPoint(x, y); +} +CellPositionRatio LeftToRightOrientation::xyToPositionRatio( + const QPoint &xy) const { + Ratio frame{xy.x(), CELL_WIDTH}; + Ratio layer{xy.y(), CELL_HEIGHT}; + return CellPositionRatio{frame, layer}; +} +QPoint LeftToRightOrientation::positionRatioToXY( + const CellPositionRatio &ratio) const { + int x = ratio.frame() * CELL_WIDTH; + int y = ratio.layer() * CELL_HEIGHT; + return QPoint(x, y); +} + +int LeftToRightOrientation::colToLayerAxis(int layer, + const ColumnFan *fan) const { + return fan->colToLayerAxis(layer); +} +int LeftToRightOrientation::rowToFrameAxis(int frame) const { + return frame * CELL_WIDTH; +} +QPoint LeftToRightOrientation::frameLayerToXY(int frameAxis, + int layerAxis) const { + return QPoint(frameAxis, layerAxis); +} +int LeftToRightOrientation::layerAxis(const QPoint &xy) const { return xy.y(); } +int LeftToRightOrientation::frameAxis(const QPoint &xy) const { return xy.x(); } +NumberRange LeftToRightOrientation::layerSide(const QRect &area) const { + return NumberRange(area.top(), area.bottom()); +} +NumberRange LeftToRightOrientation::frameSide(const QRect &area) const { + return NumberRange(area.left(), area.right()); +} +QPoint LeftToRightOrientation::topRightCorner(const QRect &area) const { + return area.bottomLeft(); +} +CellPosition LeftToRightOrientation::arrowShift(int direction) const { + switch (direction) { + case Qt::Key_Up: + return CellPosition(0, -1); + case Qt::Key_Down: + return CellPosition(0, 1); + case Qt::Key_Left: + return CellPosition(-1, 0); + case Qt::Key_Right: + return CellPosition(1, 0); + default: + return CellPosition(0, 0); + } +} diff --git a/toonz/sources/toonzlib/preferences.cpp b/toonz/sources/toonzlib/preferences.cpp index 9df1d7b6..8f9f9e81 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -310,7 +310,8 @@ Preferences::Preferences() , m_useNumpadForSwitchingStyles(true) , m_showXSheetToolbar(false) , m_expandFunctionHeader(false) - , m_useArrowKeyToShiftCellSelection(false) + , m_showColumnNumbers(false) + , m_useArrowKeyToShiftCellSelection(false) , m_inputCellsWithoutDoubleClickingEnabled(false) , m_importPolicy(0) , m_watchFileSystem(true) { @@ -589,6 +590,7 @@ Preferences::Preferences() m_useNumpadForSwitchingStyles); getValue(*m_settings, "showXSheetToolbar", m_showXSheetToolbar); getValue(*m_settings, "expandFunctionHeader", m_expandFunctionHeader); + getValue(*m_settings, "showColumnNumbers", m_showColumnNumbers); getValue(*m_settings, "useArrowKeyToShiftCellSelection", m_useArrowKeyToShiftCellSelection); getValue(*m_settings, "inputCellsWithoutDoubleClickingEnabled", @@ -1389,6 +1391,11 @@ void Preferences::enableExpandFunctionHeader(bool on) { m_settings->setValue("expandFunctionHeader", on ? "1" : "0"); } +void Preferences::enableShowColumnNumbers(bool on) { + m_showColumnNumbers = on; + m_settings->setValue("showColumnNumbers", on ? "1" : "0"); +} + //----------------------------------------------------------------- void Preferences::enableUseArrowKeyToShiftCellSelection(bool on) { diff --git a/toonz/sources/toonzlib/txsheet.cpp b/toonz/sources/toonzlib/txsheet.cpp index 607b77a1..3377cb70 100644 --- a/toonz/sources/toonzlib/txsheet.cpp +++ b/toonz/sources/toonzlib/txsheet.cpp @@ -31,6 +31,7 @@ #include "toonz/stage.h" #include "toonz/textureutils.h" #include "xshhandlemanager.h" +#include "orientation.h" #include "toonz/txsheet.h" @@ -92,7 +93,7 @@ struct TXsheet::TXsheetImp { int m_viewColumn; TSoundTrackP m_mixedSound; - ColumnFan m_columnFan; + ColumnFan m_columnFans[Orientations::COUNT]; XshHandleManager *m_handleManager; ToonzScene *m_scene; @@ -105,7 +106,11 @@ public: return ++currentId; } + void copyFoldedState(); + private: + void initColumnFans(); + // not implemented TXsheetImp(const TXsheetImp &); TXsheetImp &operator=(const TXsheetImp &); @@ -144,7 +149,9 @@ TXsheet::TXsheetImp::TXsheetImp() , m_soloColumn(-1) , m_viewColumn(-1) , m_mixedSound(0) - , m_scene(0) {} + , m_scene(0) { + initColumnFans(); +} //----------------------------------------------------------------------------- @@ -157,6 +164,20 @@ TXsheet::TXsheetImp::~TXsheetImp() { delete m_handleManager; } +//----------------------------------------------------------------------------- + +void TXsheet::TXsheetImp::initColumnFans() { + for (auto o : Orientations::all()) { + int index = o->dimension(PredefinedDimension::INDEX); + m_columnFans[index].setDimension(o->dimension(PredefinedDimension::LAYER)); + } +} + +void TXsheet::TXsheetImp::copyFoldedState() { + for (int i = 1; i < Orientations::COUNT; i++) + m_columnFans[i].copyFoldedStateFrom(m_columnFans[0]); +} + //============================================================================= // TXsheet @@ -193,12 +214,17 @@ int TXsheet::getFrameCount() const { return m_imp->m_frameCount; } //----------------------------------------------------------------------------- const TXshCell &TXsheet::getCell(int row, int col) const { + return getCell(CellPosition(row, col)); +} + +const TXshCell &TXsheet::getCell(const CellPosition &pos) const { static const TXshCell emptyCell; - TXshColumnP column = m_imp->m_columnSet.getColumn(col); + + TXshColumnP column = m_imp->m_columnSet.getColumn(pos.layer()); if (!column) return emptyCell; TXshCellColumn *xshColumn = column->getCellColumn(); if (!xshColumn) return emptyCell; - return xshColumn->getCell(row); + return xshColumn->getCell(pos.frame()); } //----------------------------------------------------------------------------- @@ -590,8 +616,8 @@ void TXsheet::reverseCells(int r0, int c0, int r1, int c1) { for (int j = c0; j <= c1; j++) { int i1, i2; for (i1 = r0, i2 = r1; i1 < i2; i1++, i2--) { - TXshCell app1 = getCell(i1, j); - TXshCell app2 = getCell(i2, j); + TXshCell app1 = getCell(CellPosition(i1, j)); + TXshCell app2 = getCell(CellPosition(i2, j)); setCell(i1, j, app2); setCell(i2, j, app1); } @@ -608,7 +634,7 @@ void TXsheet::swingCells(int r0, int c0, int r1, int c1) { for (int j = c0; j <= c1; j++) { for (int i1 = r0Mod, i2 = r1 - 1; i2 >= r0; i1++, i2--) { - TXshCell cell = getCell(i2, j); + TXshCell cell = getCell(CellPosition(i2, j)); setCell(i1, j, cell); } } @@ -620,49 +646,52 @@ bool TXsheet::incrementCells(int r0, int c0, int r1, int c1, vector> &forUndo) { for (int j = c0; j <= c1; j++) { int i = r0; - while (getCell(i, j).isEmpty() && i <= r1 - 1) i++; + while (getCell(CellPosition(i, j)).isEmpty() && i <= r1 - 1) i++; for (; i <= r1 - 1; i++) { - if (getCell(i + 1, j).isEmpty()) break; - const TXshCell &ce1 = getCell(i, j), &ce2 = getCell(i + 1, j); + if (getCell(CellPosition(i + 1, j)).isEmpty()) break; + const TXshCell &ce1 = getCell(CellPosition(i, j)), + &ce2 = getCell(CellPosition(i + 1, j)); if (ce2.getSimpleLevel() != ce1.getSimpleLevel() || ce2.getFrameId().getNumber() < ce1.getFrameId().getNumber()) return false; } i = r0; - while (getCell(i, j).isEmpty() && i <= r1 - 1) i++; + while (getCell(CellPosition(i, j)).isEmpty() && i <= r1 - 1) i++; int count; for (; i <= r1 - 1; i++) { count = 1; - if (getCell(i + 1, j).isEmpty()) continue; + if (getCell(CellPosition(i + 1, j)).isEmpty()) continue; - int frame1 = getCell(i, j).getFrameId().getNumber(); + int frame1 = getCell(CellPosition(i, j)).getFrameId().getNumber(); if (frame1 == -1) break; - while (!getCell(i + 1, j).isEmpty() && - getCell(i + 1, j).getFrameId().getNumber() == - getCell(i, j).getFrameId().getNumber()) + while (!getCell(CellPosition(i + 1, j)).isEmpty() && + getCell(CellPosition(i + 1, j)).getFrameId().getNumber() == + getCell(CellPosition(i, j)).getFrameId().getNumber()) i++, count++; - int frame2 = getCell(i + 1, j).getFrameId().getNumber(); + int frame2 = getCell(CellPosition(i + 1, j)).getFrameId().getNumber(); if (frame2 == -1) break; if (frame1 + count == frame2) continue; - else if (frame1 + count < frame2) // aggiungo + else if (frame1 + count < frame2) // add { int numCells = frame2 - frame1 - count; insertCells(i + 1, j, numCells); forUndo.push_back(std::pair( TRect(i + 1, j, i + 1 + numCells - 1, j), TXshCell())); - for (int k = 1; k <= numCells; k++) setCell(i + k, j, getCell(i, j)); + for (int k = 1; k <= numCells; k++) + setCell(i + k, j, getCell(CellPosition(i, j))); i += numCells; r1 += numCells; - } else // tolgo + } else // remove { int numCells = count - frame2 + frame1; i = i - numCells; - forUndo.push_back(std::pair( - TRect(i + 1, j, i + 1 + numCells - 1, j), getCell(i + 1, j))); + forUndo.push_back( + std::pair(TRect(i + 1, j, i + 1 + numCells - 1, j), + getCell(CellPosition(i + 1, j)))); removeCells(i + 1, j, numCells); r1 -= numCells; } @@ -680,7 +709,7 @@ void TXsheet::duplicateCells(int r0, int c0, int r1, int c1, int upTo) { for (int j = c0; j <= c1; j++) { insertCells(r1 + 1, j, upTo - (r1 + 1) + 1); for (int i = r1 + 1; i <= upTo; i++) - setCell(i, j, getCell(r0 + ((i - (r1 + 1)) % chunk), j)); + setCell(i, j, getCell(CellPosition(r0 + ((i - (r1 + 1)) % chunk), j))); } } @@ -697,7 +726,7 @@ void TXsheet::stepCells(int r0, int c0, int r1, int c1, int type) { int k = 0; for (int r = r0; r <= r1; r++) for (int c = c0; c <= c1; c++) { - cells[k++] = getCell(r, c); + cells[k++] = getCell(CellPosition(r, c)); } int nrows = nr * (type - 1); @@ -726,13 +755,13 @@ void TXsheet::increaseStepCells(int r0, int c0, int &r1, int c1) { for (c = c0; c <= c1; c++) { int r = r0, i = 0, rEnd = r1; while (r <= rEnd) { - TXshCell cell = getCell(r, c); + TXshCell cell = getCell(CellPosition(r, c)); if (!cell.isEmpty()) { insertCells(r, c); setCell(r, c, cell); rEnd++; r++; - while (cell == getCell(r, c) && r <= rEnd) r++; + while (cell == getCell(CellPosition(r, c)) && r <= rEnd) r++; } else r++; i++; @@ -755,11 +784,11 @@ void TXsheet::decreaseStepCells(int r0, int c0, int &r1, int c1) { for (c = c0; c <= c1; c++) { int r = r0, i = 0, rEnd = r1; while (r <= rEnd) { - TXshCell cell = getCell(r, c); + TXshCell cell = getCell(CellPosition(r, c)); if (!cell.isEmpty()) { r++; bool removed = false; - while (cell == getCell(r, c) && r <= rEnd) { + while (cell == getCell(CellPosition(r, c)) && r <= rEnd) { if (!removed) { removed = true; removeCells(r, c); @@ -799,7 +828,7 @@ void TXsheet::eachCells(int r0, int c0, int r1, int c1, int type) { for (j = r0, i = 0; i < size; j += type) // in cells copio il contenuto delle celle che mi interessano { - for (k = c0; k <= c1; k++, i++) cells[i] = getCell(j, k); + for (k = c0; k <= c1; k++, i++) cells[i] = getCell(CellPosition(j, k)); } int c; @@ -831,8 +860,8 @@ int TXsheet::reframeCells(int r0, int r1, int col, int type) { cells.clear(); for (int r = r0; r <= r1; r++) { - if (cells.size() == 0 || cells.last() != getCell(r, col)) - cells.push_back(getCell(r, col)); + if (cells.size() == 0 || cells.last() != getCell(CellPosition(r, col))) + cells.push_back(getCell(CellPosition(r, col))); } if (cells.empty()) return 0; @@ -871,9 +900,9 @@ void TXsheet::resetStepCells(int r0, int c0, int r1, int c1) { TXshCell *cells = new TXshCell[size]; while (r <= r1) { // mi prendo le celle che mi servono - cells[i] = getCell(r, c); + cells[i] = getCell(CellPosition(r, c)); r++; - while (cells[i] == getCell(r, c) && r <= r1) r++; + while (cells[i] == getCell(CellPosition(r, c)) && r <= r1) r++; i++; } @@ -899,7 +928,7 @@ void TXsheet::rollupCells(int r0, int c0, int r1, int c1) { // in cells copio il contenuto delle celle che mi interessano int k; - for (k = c0; k <= c1; k++) cells[k - c0] = getCell(r0, k); + for (k = c0; k <= c1; k++) cells[k - c0] = getCell(CellPosition(r0, k)); for (k = c0; k <= c1; k++) removeCells(r0, k, 1); @@ -922,7 +951,7 @@ void TXsheet::rolldownCells(int r0, int c0, int r1, int c1) { // in cells copio il contenuto delle celle che mi interessano int k; - for (k = c0; k <= c1; k++) cells[k - c0] = getCell(r1, k); + for (k = c0; k <= c1; k++) cells[k - c0] = getCell(CellPosition(r1, k)); for (k = c0; k <= c1; k++) removeCells(r1, k, 1); @@ -1188,7 +1217,8 @@ void TXsheet::loadData(TIStream &is) { TFxSet fxSet; fxSet.loadData(is); } else if (tagName == "columnFan") { - m_imp->m_columnFan.loadData(is); + m_imp->m_columnFans[0].loadData(is); + m_imp->copyFoldedState(); } else if (tagName == "noteSet") { m_notes->loadData(is); } else { @@ -1218,7 +1248,8 @@ void TXsheet::saveData(TOStream &os) { fxDag->saveData(os, getFirstFreeColumnIndex()); os.closeChild(); - ColumnFan *columnFan = getColumnFan(); + // does not matter which Orientation to take, as all fans share folded data + ColumnFan *columnFan = getColumnFan(Orientations::topToBottom()); if (!columnFan->isEmpty()) { os.openChild("columnFan"); columnFan->saveData(os); @@ -1439,7 +1470,10 @@ FxDag *TXsheet::getFxDag() const { return m_imp->m_fxDag; } //----------------------------------------------------------------------------- -ColumnFan *TXsheet::getColumnFan() const { return &m_imp->m_columnFan; } +ColumnFan *TXsheet::getColumnFan(const Orientation *o) const { + int index = o->dimension(PredefinedDimension::INDEX); + return &m_imp->m_columnFans[index]; +} //----------------------------------------------------------------------------- @@ -1504,7 +1538,7 @@ TRectD TXsheet::getBBox(int r) const { struct locals { static TRectD getBBox(const TXsheet *xsh, int r, int c) { // Discriminate cell content - const TXshCell &cell = xsh->getCell(r, c); + const TXshCell &cell = xsh->getCell(CellPosition(r, c)); if (cell.isEmpty()) return voidRect; if (TXshChildLevel *cl = cell.getChildLevel()) @@ -1570,11 +1604,10 @@ TRectD TXsheet::getBBox(int r) const { //----------------------------------------------------------------------- -bool TXsheet::isRectEmpty(int r0, int c0, int r1, int c1) const { - for (int r = r0; r <= r1; r++) { - for (int c = c0; c <= c1; c++) { - if (!getCell(r, c).isEmpty()) return false; - } - } +bool TXsheet::isRectEmpty(const CellPosition &pos0, + const CellPosition &pos1) const { + for (int frame = pos0.frame(); frame <= pos1.frame(); frame++) + for (int layer = pos0.layer(); layer <= pos1.layer(); layer++) + if (!getCell(CellPosition(frame, layer)).isEmpty()) return false; return true; } \ No newline at end of file diff --git a/toonz/sources/toonzlib/txshsoundlevel.cpp b/toonz/sources/toonzlib/txshsoundlevel.cpp index 0f1220db..c282db6f 100644 --- a/toonz/sources/toonzlib/txshsoundlevel.cpp +++ b/toonz/sources/toonzlib/txshsoundlevel.cpp @@ -145,9 +145,13 @@ void TXshSoundLevel::saveData(TOStream &os) { //----------------------------------------------------------------------------- -void TXshSoundLevel::computeValues(int frameHeight) { +void TXshSoundLevel::computeValuesFor(const Orientation *o) { + int frameHeight = o->dimension(PredefinedDimension::FRAME); // time axis + int index = o->dimension(PredefinedDimension::INDEX); + map &values = m_values[index]; + if (frameHeight == 0) frameHeight = 1; - m_values.clear(); + values.clear(); if (!m_soundTrack) { m_frameSoundCount = 0; m_samplePerFrame = 0; @@ -174,7 +178,9 @@ void TXshSoundLevel::computeValues(int frameHeight) { if (absMaxPressure <= 0) return; // Adjusting using a fixed scaleFactor - double weightA = 20.0 / absMaxPressure; + int desiredAmplitude = o->dimension(PredefinedDimension::SOUND_AMPLITUDE); + // results will be in range -desiredAmplitude .. +desiredAmplitude + double weightA = desiredAmplitude / absMaxPressure; long i = 0, j; long p = 0; // se p parte da zero notazione per pixel, @@ -187,7 +193,7 @@ void TXshSoundLevel::computeValues(int frameHeight) { (TINT32)(i * m_samplePerFrame + j * samplePerPixel), (TINT32)(i * m_samplePerFrame + (j + 1) * samplePerPixel - 1), TSound::MONO, min, max); - m_values.insert(std::pair>( + values.insert(std::pair>( p + j, std::pair(min * weightA, max * weightA))); } @@ -196,7 +202,7 @@ void TXshSoundLevel::computeValues(int frameHeight) { m_soundTrack->getMinMaxPressure( (TINT32)(i * m_samplePerFrame + j * samplePerPixel), (TINT32)((i + 1) * m_samplePerFrame - 1), TSound::MONO, min, max); - m_values.insert(std::pair>( + values.insert(std::pair>( p + j, std::pair(min * weightA, max * weightA))); ++i; @@ -206,9 +212,17 @@ void TXshSoundLevel::computeValues(int frameHeight) { //----------------------------------------------------------------------------- -void TXshSoundLevel::getValueAtPixel(int pixel, DoublePair &values) const { - std::map::const_iterator it = m_values.find(pixel); - if (it != m_values.end()) values = it->second; +void TXshSoundLevel::computeValues() { + for (auto o : Orientations::all()) computeValuesFor(o); +} + +//----------------------------------------------------------------------------- + +void TXshSoundLevel::getValueAtPixel(const Orientation *o, int pixel, + DoublePair &values) const { + int index = o->dimension(PredefinedDimension::INDEX); + std::map::const_iterator it = m_values[index].find(pixel); + if (it != m_values[index].end()) values = it->second; } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonzqt/functionsheet.cpp b/toonz/sources/toonzqt/functionsheet.cpp index 9d86d2df..2f304dd2 100644 --- a/toonz/sources/toonzqt/functionsheet.cpp +++ b/toonz/sources/toonzqt/functionsheet.cpp @@ -251,12 +251,10 @@ void FunctionSheetColumnHeadViewer::paintEvent(QPaintEvent *e) { QRect toBeUpdated = e->rect(); painter.setClipRect(toBeUpdated); - int x0 = toBeUpdated.left(); - int x1 = toBeUpdated.right(); - // visible columns range - int c0 = getViewer()->xToColumn(x0); - int c1 = getViewer()->xToColumn(x1); + CellRange visible = getViewer()->xyRectToRange(toBeUpdated); + int c0 = visible.from().layer(); + int c1 = visible.to().layer(); if (c0 > c1) return; @@ -264,7 +262,8 @@ void FunctionSheetColumnHeadViewer::paintEvent(QPaintEvent *e) { FunctionTreeModel::ChannelGroup *currentGroup = 0; - /*--- 表示範囲+左右1カラムの範囲で、ChannelGroupを格納。無ければ0を入れる + /*--- Display range + right and left 1 column range ChannelGroup. If there is + * none, put 0 * ---*/ std::vector channelGroups(n); for (int i = 0; i < n; ++i) { @@ -281,11 +280,11 @@ void FunctionSheetColumnHeadViewer::paintEvent(QPaintEvent *e) { } int y0 = 0; - int y1 = 17; + int y1 = 17; // needs work int y2 = 34; int y3 = 53; - /*--- Channelの有る範囲内を明るめのグレーで塗る ---*/ + /*--- Fill header with background color ---*/ for (int c = c0; c <= c1; c++) { FunctionTreeModel::Channel *channel = m_sheet->getChannel(c); if (!channel) continue; @@ -301,16 +300,18 @@ void FunctionSheetColumnHeadViewer::paintEvent(QPaintEvent *e) { FunctionTreeModel::Channel *channel = m_sheet->getChannel(c); if (!channel) continue; int i = c - c0 + 1; - /*---- 現在のColumnと前後のColumnのChannelGroup ---*/ + /*---- Channel Column of the current Column and the preceding and following + * Columns ---*/ FunctionTreeModel::ChannelGroup *prevGroup = channelGroups[c - c0], *group = channelGroups[c - c0 + 1], *nextGroup = channelGroups[c - c0 + 2]; - /*---- 前後とグループが違ってた場合、それぞれフラグを立てる ---*/ + /*---- If the group is different from the before and after, flags are set + * respectively ---*/ bool firstGroupColumn = prevGroup != group; bool lastGroupColumn = nextGroup != group; - /*--- 現在のカラムの左右座標 ---*/ + /*--- The left and right coordinates of the current column ---*/ int x0 = getViewer()->columnToX(c); int x1 = getViewer()->columnToX(c + 1) - 1; // Column width @@ -380,7 +381,7 @@ void FunctionSheetColumnHeadViewer::mouseMoveEvent(QMouseEvent *e) { } // get the column under the cursor - int col = getViewer()->xToColumn(e->pos().x()); + int col = getViewer()->xyToPosition(e->pos()).layer(); FunctionTreeModel::Channel *channel = m_sheet->getChannel(col); if (!channel) { setToolTip(QString("")); @@ -392,7 +393,7 @@ void FunctionSheetColumnHeadViewer::mouseMoveEvent(QMouseEvent *e) { void FunctionSheetColumnHeadViewer::mousePressEvent(QMouseEvent *e) { QPoint pos = e->pos(); - int currentC = getViewer()->xToColumn(pos.x()); + int currentC = getViewer()->xyToPosition(pos).layer(); FunctionTreeModel::Channel *channel; for (int c = 0; c <= m_sheet->getChannelCount(); c++) { channel = m_sheet->getChannel(c); @@ -428,7 +429,7 @@ void FunctionSheetColumnHeadViewer::mousePressEvent(QMouseEvent *e) { void FunctionSheetColumnHeadViewer::contextMenuEvent(QContextMenuEvent *ce) { // First, select column under cursor const QPoint &pos = ce->pos(); - int cursorCol = getViewer()->xToColumn(pos.x()); + int cursorCol = getViewer()->xyToPosition(pos).layer(); if (cursorCol < 0 || cursorCol >= m_sheet->getChannelCount()) return; @@ -510,10 +511,11 @@ FunctionSheetCellViewer::FunctionSheetCellViewer(FunctionSheet *parent) /*! Called when the cell panel is left/right-clicked */ Spreadsheet::DragTool *FunctionSheetCellViewer::createDragTool(QMouseEvent *e) { - int row = getViewer()->yToRow(e->pos().y()); - int col = getViewer()->xToColumn(e->pos().x()); - bool isEmpty = true; - TDoubleParam *curve = m_sheet->getCurve(col); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); + bool isEmpty = true; + TDoubleParam *curve = m_sheet->getCurve(col); if (curve) { int kCount = curve->getKeyframeCount(); if (kCount > 0) { @@ -665,8 +667,9 @@ void FunctionSheetCellViewer::drawCells(QPainter &painter, int r0, int c0, //----------------------------------------------------------------------------- void FunctionSheetCellViewer::mouseDoubleClickEvent(QMouseEvent *e) { - int row = getViewer()->yToRow(e->pos().y()); - int col = getViewer()->xToColumn(e->pos().x()); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); int x0, y0, x1, y1; x0 = getViewer()->columnToX(col); x1 = getViewer()->columnToX(col + 1) - 1; @@ -737,8 +740,6 @@ void FunctionSheetCellViewer::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton || e->button() == Qt::MidButton) Spreadsheet::CellPanel::mousePressEvent(e); else if (e->button() == Qt::RightButton) { - int row = getViewer()->yToRow(e->pos().y()); - int col = getViewer()->xToColumn(e->pos().x()); update(); openContextMenu(e); } @@ -749,8 +750,9 @@ void FunctionSheetCellViewer::mousePressEvent(QMouseEvent *e) { void FunctionSheetCellViewer::mouseReleaseEvent(QMouseEvent *e) { Spreadsheet::CellPanel::mouseReleaseEvent(e); /* -int row = getViewer()->yToRow(e->pos().y()); -int col = getViewer()->xToColumn(e->pos().x()); + CellPosition cellPosition = getViewer ()->xyToPosition (e->pos ()); +int row = cellPosition.frame (); +int col = cellPosition.layer (); FunctionSheet::DragTool *dragTool = m_sheet->getDragTool(); if(dragTool) dragTool->release(row,col); m_sheet->setDragTool(0); @@ -792,9 +794,10 @@ void FunctionSheetCellViewer::openContextMenu(QMouseEvent *e) { QAction setStep3Action(tr("Step 3"), 0); QAction setStep4Action(tr("Step 4"), 0); - int row = getViewer()->yToRow(e->pos().y()); - int col = getViewer()->xToColumn(e->pos().x()); - TDoubleParam *curve = m_sheet->getCurve(col); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); + TDoubleParam *curve = m_sheet->getCurve(col); if (!curve) return; bool isEmpty = true; @@ -947,14 +950,14 @@ void FunctionSheet::setSelection(FunctionSelection *selection) { //----------------------------------------------------------------------------- void FunctionSheet::showEvent(QShowEvent *e) { - registerFrameScroller(); + m_frameScroller.registerFrameScroller(); SpreadsheetViewer::showEvent(e); } //----------------------------------------------------------------------------- void FunctionSheet::hideEvent(QHideEvent *e) { - unregisterFrameScroller(); + m_frameScroller.unregisterFrameScroller(); SpreadsheetViewer::hideEvent(e); } diff --git a/toonz/sources/toonzqt/spreadsheetviewer.cpp b/toonz/sources/toonzqt/spreadsheetviewer.cpp index 3858bf2a..8d6136df 100644 --- a/toonz/sources/toonzqt/spreadsheetviewer.cpp +++ b/toonz/sources/toonzqt/spreadsheetviewer.cpp @@ -4,6 +4,7 @@ #include "toonzqt/gutil.h" #include "toonz/tframehandle.h" +#include "orientation.h" #include #include @@ -21,60 +22,109 @@ namespace Spreadsheet { //============================================================================= -FrameScroller::FrameScroller() {} +FrameScroller::FrameScroller() + : m_orientation(Orientations::topToBottom()) + , m_scrollArea(nullptr) + , m_lastX(0) + , m_lastY(0) + ,m_syncing(false){} FrameScroller::~FrameScroller() {} -void FrameScroller::connectScroller(FrameScroller *scroller) { - if (scroller != this && !m_connectedScrollers.contains(scroller)) { - m_connectedScrollers.append(scroller); - scroller->connectScroller(this); - QScrollBar *sb0 = getFrameScrollArea()->verticalScrollBar(); - QScrollBar *sb1 = scroller->getFrameScrollArea()->verticalScrollBar(); - QObject::connect(sb0, SIGNAL(valueChanged(int)), sb1, SLOT(setValue(int))); - QObject::connect(sb1, SIGNAL(valueChanged(int)), sb0, SLOT(setValue(int))); - } +void FrameScroller::setFrameScrollArea(QScrollArea *scrollArea) { + disconnectScrollbars(); + m_scrollArea = scrollArea; + connectScrollbars(); } -void FrameScroller::disconnectScroller(FrameScroller *scroller) { - if (m_connectedScrollers.contains(scroller)) { - m_connectedScrollers.removeAll(scroller); - scroller->disconnectScroller(this); - QScrollBar *sb0 = getFrameScrollArea()->verticalScrollBar(); - QScrollBar *sb1 = scroller->getFrameScrollArea()->verticalScrollBar(); - QObject::disconnect(sb0, SIGNAL(valueChanged(int)), sb1, - SLOT(setValue(int))); - QObject::disconnect(sb1, SIGNAL(valueChanged(int)), sb0, - SLOT(setValue(int))); - } +void FrameScroller::disconnectScrollbars() { + if (!m_scrollArea) return; + disconnect(m_scrollArea->verticalScrollBar(), &QScrollBar::valueChanged, this, + &FrameScroller::onVScroll); + disconnect(m_scrollArea->horizontalScrollBar(), &QScrollBar::valueChanged, + this, &FrameScroller::onHScroll); } -bool FrameScroller::isScrollerConnected(FrameScroller *scroller) { - return m_connectedScrollers.contains(scroller); +void FrameScroller::connectScrollbars() { + if (!m_scrollArea) return; + m_lastX = m_scrollArea->horizontalScrollBar()->value(); + m_lastY = m_scrollArea->verticalScrollBar()->value(); + connect(m_scrollArea->verticalScrollBar(), &QScrollBar::valueChanged, this, + &FrameScroller::onVScroll); + connect(m_scrollArea->horizontalScrollBar(), &QScrollBar::valueChanged, this, + &FrameScroller::onHScroll); +} + +void FrameScroller::onVScroll(int y) { + QPoint offset(0, y - m_lastY); + if (isSyncing()) return; + m_lastY = y; + setSyncing(true); + handleScroll(offset); + setSyncing(false); +} +void FrameScroller::onHScroll(int x) { + QPoint offset(x - m_lastX, 0); + if (isSyncing()) return; + m_lastX = x; + setSyncing(true); + handleScroll(offset); + setSyncing(false); } static QList frameScrollers; +void FrameScroller::handleScroll(const QPoint &offset) const { + CellPositionRatio ratio = orientation()->xyToPositionRatio(offset); + if( (m_orientation->isVerticalTimeline() && offset.x()) + || (!m_orientation->isVerticalTimeline() && offset.y())) // only synchronize changes by frames axis + return; + + for (int i = 0; i < frameScrollers.size(); i++) + if (frameScrollers[i] != this) + { + if (!frameScrollers[i]->isSyncing()) + { + frameScrollers[i]->onScroll(ratio); + break; + } + } +} + +void adjustScrollbar(QScrollBar *scrollBar, int add); + +void FrameScroller::onScroll(const CellPositionRatio &ratio) { + QPoint offset = orientation()->positionRatioToXY(ratio); + if (offset.x()) + adjustScrollbar(m_scrollArea->horizontalScrollBar(), + offset.x()); + if (offset.y()) + adjustScrollbar(m_scrollArea->verticalScrollBar(), + offset.y()); + + emit prepareToScrollOffset(offset); +} +void adjustScrollbar(QScrollBar *scrollBar, int add) { + scrollBar->setValue(scrollBar->value() + add); +} + void FrameScroller::registerFrameScroller() { - if (!frameScrollers.contains(this)) { - for (int i = 0; i < frameScrollers.size(); i++) - connectScroller(frameScrollers[i]); - frameScrollers.append(this); - } + if (!frameScrollers.contains(this)) frameScrollers.append(this); } void FrameScroller::unregisterFrameScroller() { - if (frameScrollers.contains(this)) { - frameScrollers.removeAll(this); - for (int i = 0; i < frameScrollers.size(); i++) - disconnectScroller(frameScrollers[i]); - } + if (frameScrollers.contains(this)) frameScrollers.removeAll(this); } -void FrameScroller::prepareToScroll(int dy) { - if (dy == 0) return; +void FrameScroller::prepareToScrollOthers(const QPoint &offset) { + CellPositionRatio ratio = orientation()->xyToPositionRatio(offset); for (int i = 0; i < frameScrollers.size(); i++) - if (frameScrollers[i] != this) frameScrollers[i]->onPrepareToScroll(dy); + if (frameScrollers[i] != this) + frameScrollers[i]->prepareToScrollRatio(ratio); +} +void FrameScroller::prepareToScrollRatio(const CellPositionRatio &ratio) { + QPoint offset = orientation()->positionRatioToXY(ratio); + emit prepareToScrollOffset(offset); } //============================================================================= @@ -188,14 +238,16 @@ void GenericPanel::mousePressEvent(QMouseEvent *e) { else m_dragTool = createDragTool(e); - int row = getViewer()->yToRow(e->pos().y()); - int col = getViewer()->xToColumn(e->pos().x()); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); if (m_dragTool) m_dragTool->click(row, col, e); } void GenericPanel::mouseReleaseEvent(QMouseEvent *e) { - int row = getViewer()->yToRow(e->pos().y()); - int col = getViewer()->xToColumn(e->pos().x()); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); m_viewer->stopAutoPan(); if (m_dragTool) { m_dragTool->release(row, col, e); @@ -205,8 +257,9 @@ void GenericPanel::mouseReleaseEvent(QMouseEvent *e) { } void GenericPanel::mouseMoveEvent(QMouseEvent *e) { - int row = getViewer()->yToRow(e->pos().y()); - int col = getViewer()->xToColumn(e->pos().x()); + CellPosition cellPosition = getViewer()->xyToPosition(e->pos()); + int row = cellPosition.frame(); + int col = cellPosition.layer(); if (e->buttons() != 0 && m_dragTool != 0) { if ((e->buttons() & Qt::LeftButton) != 0 && !visibleRegion().contains(e->pos())) { @@ -289,9 +342,10 @@ void RowPanel::paintEvent(QPaintEvent *e) { QRect toBeUpdated = e->rect(); QPainter p(this); - // range di righe visibili - int r0 = getViewer()->yToRow(toBeUpdated.top()); - int r1 = getViewer()->yToRow(toBeUpdated.bottom()); + CellRange visible = getViewer()->xyRectToRange(toBeUpdated); + // range of visible rows + int r0 = visible.from().frame(); + int r1 = visible.to().frame(); p.setClipRect(toBeUpdated); p.fillRect(toBeUpdated, QBrush(getViewer()->getLightLightBGColor())); @@ -324,13 +378,15 @@ void CellPanel::paintEvent(QPaintEvent *e) { int y0 = toBeUpdated.top(); int x1 = toBeUpdated.right(), y1 = toBeUpdated.bottom(); + QRect alteredRect(QPoint(x0, y0), QPoint(x1, y1)); + CellRange cellRange = getViewer()->xyRectToRange(alteredRect); // visible rows range - int r0 = getViewer()->yToRow(y0); - int r1 = getViewer()->yToRow(y1); + int r0 = cellRange.from().frame(); + int r1 = cellRange.to().frame(); // visible columns range - int c0 = getViewer()->xToColumn(x0); - int c1 = getViewer()->xToColumn(x1); + int c0 = cellRange.from().layer(); + int c1 = cellRange.to().layer(); // cambia colore alle celle prima di rowCount() int rowCount = getViewer()->getRowCount(); @@ -389,7 +445,10 @@ SpreadsheetViewer::SpreadsheetViewer(QWidget *parent) , m_currentRow(0) , m_markRowDistance(6) , m_markRowOffset(0) - , m_isComputingSize(false) { + , m_isComputingSize(false) + , m_frameScroller() { + // m_orientation = Orientations::topToBottom (); + setFocusPolicy(Qt::NoFocus); setFrameStyle(QFrame::StyledPanel); @@ -427,6 +486,10 @@ SpreadsheetViewer::SpreadsheetViewer(QWidget *parent) m_columnScrollArea->setFixedHeight(m_rowHeight * 3 - 3); // m_columnScrollArea->setFixedHeight(m_rowHeight * 3 + 60 - 63); + m_frameScroller.setFrameScrollArea(m_cellScrollArea); + connect(&m_frameScroller, &Spreadsheet::FrameScroller::prepareToScrollOffset, + this, &SpreadsheetViewer::onPrepareToScrollOffset); + //---- layout QGridLayout *layout = new QGridLayout(); layout->setMargin(0); @@ -520,7 +583,6 @@ void SpreadsheetViewer::setColumnCount(int columnCount) { void SpreadsheetViewer::scroll(QPoint delta) { int x = delta.x(); int y = delta.y(); - prepareToScroll(y); QScrollBar *hSc = m_cellScrollArea->horizontalScrollBar(); QScrollBar *vSc = m_cellScrollArea->verticalScrollBar(); @@ -547,6 +609,10 @@ void SpreadsheetViewer::scroll(QPoint delta) { vSc->setValue(valueV); } +void SpreadsheetViewer::onPrepareToScrollOffset(const QPoint &offset) { + refreshContentSize(offset.x(), offset.y()); +} + void SpreadsheetViewer::setAutoPanSpeed(const QPoint &speed) { bool wasAutoPanning = isAutoPanning(); m_autoPanSpeed = speed; @@ -583,21 +649,42 @@ void SpreadsheetViewer::setAutoPanSpeed(const QRect &widgetBounds, m_lastAutoPanPos = mousePos; } -/*!Shift is a consequence of style sheet border.*/ int SpreadsheetViewer::xToColumn(int x) const { - return (x + 1) / m_columnWidth; + CellPosition pos = xyToPosition(QPoint(x, 0)); + return pos.layer(); +} +int SpreadsheetViewer::yToRow(int y) const { + CellPosition pos = xyToPosition(QPoint(0, y)); + return pos.frame(); } - -/*!Shift is a consequence of style sheet border.*/ int SpreadsheetViewer::columnToX(int col) const { - return (col * m_columnWidth) - 1; + QPoint xy = positionToXY(CellPosition(0, col)); + return xy.x(); +} +int SpreadsheetViewer::rowToY(int row) const { + QPoint xy = positionToXY(CellPosition(row, 0)); + return xy.y(); } /*!Shift is a consequence of style sheet border.*/ -int SpreadsheetViewer::yToRow(int y) const { return (y + 1) / m_rowHeight; } +CellPosition SpreadsheetViewer::xyToPosition(const QPoint &point) const { + int row = (point.y() + 1) / m_rowHeight; + int col = (point.x() + 1) / m_columnWidth; + return CellPosition(row, col); +} /*!Shift is a consequence of style sheet border.*/ -int SpreadsheetViewer::rowToY(int row) const { return (row * m_rowHeight) - 1; } +QPoint SpreadsheetViewer::positionToXY(const CellPosition &pos) const { + int x = (pos.layer() * m_columnWidth) - 1; + int y = (pos.frame() * m_rowHeight) - 1; + return QPoint(x, y); +} + +CellRange SpreadsheetViewer::xyRectToRange(const QRect &rect) const { + CellPosition topLeft = xyToPosition(rect.topLeft()); + CellPosition bottomRight = xyToPosition(rect.bottomRight()); + return CellRange(topLeft, bottomRight); +} bool SpreadsheetViewer::refreshContentSize(int scrollDx, int scrollDy) { QSize viewportSize = m_cellScrollArea->viewport()->size();