#include "xshrowviewer.h"
#include "xsheetviewer.h"
#include "tapp.h"
#include "toonz/tscenehandle.h"
#include "toonz/tframehandle.h"
#include "toonz/tonionskinmaskhandle.h"
#include "xsheetdragtool.h"
#include "toonzqt/gutil.h"
#include "onionskinmaskgui.h"
#include "cellselection.h"
#include "menubarcommandids.h"
#include "toonzqt/menubarcommand.h"
#if defined(x64)
#include "../stopmotion/stopmotion.h"
#endif
#include "toonz/toonzscene.h"
#include "tconvert.h"
#include "toonz/txsheet.h"
#include "toonz/sceneproperties.h"
#include "toutputproperties.h"
#include "toonz/preferences.h"
#include "tools/toolhandle.h"
#include "tools/toolcommandids.h"
#include "toonz/tstageobject.h"
#include "toonz/tpinnedrangeset.h"
#include "toonz/navigationtags.h"
#include <QPainter>
#include <QMouseEvent>
#include <QMenu>
#include <QToolTip>
//=============================================================================
namespace XsheetGUI {
//=============================================================================
// RowArea
//-----------------------------------------------------------------------------
RowArea::RowArea(XsheetViewer *parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
, m_viewer(parent)
, m_row(-1)
, m_showOnionToSet(None)
, m_pos(-1, -1)
, m_playRangeActiveInMousePress(false)
, m_mousePressRow(-1)
, m_tooltip(tr(""))
, m_r0(0)
, m_r1(5)
, m_isPanning(false) {
setFocusPolicy(Qt::NoFocus);
setMouseTracking(true);
connect(TApp::instance()->getCurrentOnionSkin(),
SIGNAL(onionSkinMaskChanged()), this, SLOT(update()));
// for displaying the pinned center keys when the skeleton tool is selected
connect(TApp::instance()->getCurrentTool(), SIGNAL(toolSwitched()), this,
SLOT(update()));
}
//-----------------------------------------------------------------------------
RowArea::~RowArea() {}
//-----------------------------------------------------------------------------
DragTool *RowArea::getDragTool() const { return m_viewer->getDragTool(); }
void RowArea::setDragTool(DragTool *dragTool) {
m_viewer->setDragTool(dragTool);
}
//-----------------------------------------------------------------------------
// returns true if the frame area can have extra space
bool RowArea::checkExpandFrameArea() {
return m_viewer->orientation()->isVerticalTimeline() &&
!Preferences::instance()->isOnionSkinEnabled() &&
!CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked();
}
//-----------------------------------------------------------------------------
void RowArea::drawRows(QPainter &p, int r0, int r1) {
const Orientation *o = m_viewer->orientation();
int playR0, playR1, step;
XsheetGUI::getPlayRange(playR0, playR1, step);
if (!XsheetGUI::isPlayRangeEnabled()) {
TXsheet *xsh = m_viewer->getXsheet();
playR1 = xsh->getFrameCount() - 1;
playR0 = 0;
}
QString fontName = Preferences::instance()->getInterfaceFont();
if (fontName == "") {
#ifdef _WIN32
fontName = "Arial";
#else
fontName = "Helvetica";
#endif
}
static QFont font(fontName, -1, QFont::Bold);
// set font size in pixel
font.setPixelSize(XSHEET_FONT_PX_SIZE);
p.setFont(font);
// marker interval
int distance, offset, secDistance;
TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers(
distance, offset, secDistance);
// default value
// if (distance == 0) distance = 6;
QRect visibleRect = visibleRegion().boundingRect();
int x0 = visibleRect.left();
int x1 = visibleRect.right();
int y0 = visibleRect.top();
int y1 = visibleRect.bottom();
NumberRange layerSide = o->layerSide(visibleRect);
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
bool simpleView = m_viewer->getFrameZoomFactor() <=
o->dimension(PredefinedDimension::SCALE_THRESHOLD);
int currentRow = m_viewer->getCurrentRow();
bool hasCurrentFrameTextColor =
m_viewer->getTextColor() != m_viewer->getCurrentFrameTextColor();
for (int r = r0; r <= r1; r++) {
int frameAxis = m_viewer->rowToFrameAxis(r);
//--- draw horizontal line
bool isAfterMarkers =
(distance > 0 && ((r - offset) % distance) == 0 && r != 0);
bool isAfterSecMarkers =
secDistance > 0 && ((r - offset) % secDistance) == 0 && r != 0;
QColor color = (isAfterSecMarkers) ? m_viewer->getSecMarkerLineColor()
: (isAfterMarkers) ? m_viewer->getMarkerLineColor()
: m_viewer->getLightLineColor();
double lineWidth = (isAfterSecMarkers) ? 3.
: (secDistance > 0 && isAfterMarkers) ? 2.
: 1.;
p.setPen(QPen(color, lineWidth, Qt::SolidLine, Qt::FlatCap));
// p.setPen(color);
QLine horizontalLine = o->horizontalLine(frameAxis, layerSide);
if (!o->isVerticalTimeline()) {
int x = horizontalLine.x1();
int y = horizontalLine.y2() - (isAfterMarkers ? 6 : 3);
horizontalLine.setP1(QPoint(x, y));
if (!isAfterMarkers) p.setPen(m_viewer->getFrameRangeMarkerLineColor());
}
p.drawLine(horizontalLine);
}
int extraSpaces = 0;
if (checkExpandFrameArea()) {
extraSpaces =
std::max(0, o->rect(PredefinedRect::FRAME_LABEL).width() /
QFontMetrics(p.font()).boundingRect("0").width() -
6);
}
int z = 0;
for (int r = r0; r <= r1; r++) {
// draw frame text
if (hasCurrentFrameTextColor && r == currentRow)
p.setPen(m_viewer->getCurrentFrameTextColor());
else if (playR0 <= r && r <= playR1) {
p.setPen(((r - m_r0) % step == 0) ? m_viewer->getPreviewFrameTextColor()
: m_viewer->getTextColor());
}
// not in preview range
else
p.setPen(m_viewer->getTextColor());
QPoint basePoint = m_viewer->positionToXY(CellPosition(r, -1));
if (!o->isVerticalTimeline())
basePoint.setY(0);
else
basePoint.setX(0);
QRect labelRect =
o->rect(PredefinedRect::FRAME_LABEL).translated(basePoint);
labelRect.adjust(-frameAdj.x() / 2, -frameAdj.y() / 2, -frameAdj.x() / 2,
-frameAdj.y() / 2);
int align = o->dimension(PredefinedDimension::FRAME_LABEL_ALIGN);
// display time and/or frame number
z++;
switch (m_viewer->getFrameDisplayStyle()) {
case XsheetViewer::SecAndFrame: {
int frameRate = TApp::instance()
->getCurrentScene()
->getScene()
->getProperties()
->getOutputProperties()
->getFrameRate();
int zz = frameRate / 5;
zz = frameRate / zz;
QString str;
int koma = (r + 1) % frameRate;
if (koma == 1) {
int sec = (r + 1) / frameRate;
str = QString("%1' %2\"")
.arg(QString::number(sec).rightJustified(2, '0'))
.arg(QString::number(koma).rightJustified(2, '0'));
z = 0;
} else {
if (simpleView) {
if ((z + 1) % zz) break;
if (r % frameRate == 1 || (r + 2) % frameRate == 1) break;
}
if (koma == 0) koma = frameRate;
str = QString("%1\"").arg(QString::number(koma).rightJustified(2, '0'));
}
p.drawText(labelRect, align, str);
break;
}
case XsheetViewer::Frame: {
if (simpleView && r > 0 && (r + 1) % (distance > 0 ? distance : 5)) break;
QString number = QString::number(r + 1);
p.drawText(labelRect, align, number);
break;
}
// 6 second sheet (144frames per page)
case XsheetViewer::SixSecSheet: {
int frameRate = TApp::instance()
->getCurrentScene()
->getScene()
->getProperties()
->getOutputProperties()
->getFrameRate();
int zz = frameRate / 5;
zz = frameRate / zz;
QString str;
int koma = (r + 1) % (frameRate * 6);
if ((r + 1) % frameRate == 1) {
int page = (r + 1) / (frameRate * 6) + 1;
str = QString("p%1%2%3")
.arg(QString::number(page))
.arg(QString().leftJustified(1 + extraSpaces, ' '))
.arg(QString::number(koma).rightJustified(3, '0'));
z = 0;
} else {
if (simpleView) {
if ((z + 1) % zz) break;
if (r % frameRate == 1 || (r + 2) % frameRate == 1) break;
}
if (koma == 0) koma = frameRate * 6;
str = QString("%1").arg(QString::number(koma).rightJustified(3, '0'));
}
p.drawText(labelRect, align, str);
break;
}
// 3 second sheet (72frames per page)
case XsheetViewer::ThreeSecSheet: {
int frameRate = TApp::instance()
->getCurrentScene()
->getScene()
->getProperties()
->getOutputProperties()
->getFrameRate();
int zz = frameRate / 5;
zz = frameRate / zz;
QString str;
int koma = (r + 1) % (frameRate * 3);
if ((r + 1) % frameRate == 1) {
int page = (r + 1) / (frameRate * 3) + 1;
str = QString("p%1%2%3")
.arg(QString::number(page))
.arg(QString().leftJustified(2 + extraSpaces, ' '))
.arg(QString::number(koma).rightJustified(2, '0'));
z = 0;
} else {
if (simpleView) {
if ((z + 1) % zz) break;
if (r % frameRate == 1 || (r + 2) % frameRate == 1) break;
}
if (koma == 0) koma = frameRate * 3;
str = QString("%1").arg(QString::number(koma).rightJustified(2, '0'));
}
p.drawText(labelRect, align, str);
break;
}
}
}
}
//-----------------------------------------------------------------------------
void RowArea::drawPlayRangeBackground(QPainter &p, int r0, int r1) {
if (!XsheetGUI::isPlayRangeEnabled()) return;
const Orientation *o = m_viewer->orientation();
TXsheet *xsh = m_viewer->getXsheet();
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
int playR0, playR1, step;
XsheetGUI::getPlayRange(playR0, playR1, step);
int hExpansion = 0;
if (checkExpandFrameArea()) {
hExpansion = m_viewer->orientation()->dimension(
PredefinedDimension::FRAME_AREA_EXPANSION);
}
for (int r = r0; r <= r1; r++) {
if (!(playR0 <= r && r <= playR1) && ((r - m_r0) % step == 0)) continue;
QPoint basePoint = m_viewer->positionToXY(CellPosition(r, -1));
if (!o->isVerticalTimeline())
basePoint.setY(0);
else
basePoint.setX(0);
QRect previewBoxRect =
o->rect(PredefinedRect::PREVIEW_FRAME_AREA)
.adjusted(-hExpansion, 0, -frameAdj.x(), -frameAdj.y())
.translated(basePoint);
p.fillRect(previewBoxRect, m_viewer->getPlayRangeColor());
if (!o->isVerticalTimeline()) {
if (r == playR0) {
QLine horizontalLine(previewBoxRect.topLeft(),
previewBoxRect.bottomLeft());
p.setPen(m_viewer->getLightLineColor());
p.drawLine(horizontalLine);
} else if (r == playR1) {
QLine horizontalLine(previewBoxRect.topRight(),
previewBoxRect.bottomRight());
p.setPen(m_viewer->getLightLineColor());
p.drawLine(horizontalLine);
}
}
}
}
void RowArea::drawPlayRange(QPainter &p, int r0, int r1) {
bool playRangeEnabled = XsheetGUI::isPlayRangeEnabled();
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
TXsheet *xsh = m_viewer->getXsheet();
// Update the play range internal fields
if (playRangeEnabled) {
int step;
XsheetGUI::getPlayRange(m_r0, m_r1, step);
}
// if the preview range is not set, then put markers at the first and the last
// frames of the scene
else {
m_r1 = xsh->getFrameCount() - 1;
if (m_r1 == -1) return;
m_r0 = 0;
}
int hOffset = 0;
if (checkExpandFrameArea()) {
hOffset = m_viewer->orientation()->dimension(
PredefinedDimension::FRAME_AREA_EXPANSION);
}
QColor ArrowColor = (playRangeEnabled) ? QColor(255, 255, 255) : grey150;
p.setBrush(QBrush(ArrowColor));
if (m_r0 > r0 - 1 && r1 + 1 > m_r0) {
QPoint topLeft = m_viewer->positionToXY(CellPosition(m_r0, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(-hOffset);
m_viewer->drawPredefinedPath(p, PredefinedPath::BEGIN_PLAY_RANGE, topLeft,
ArrowColor, QColor(Qt::black));
}
if (m_r1 > r0 - 1 && r1 + 1 > m_r1) {
QPoint topLeft = m_viewer->positionToXY(CellPosition(m_r1, -1));
topLeft -= frameAdj;
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(-hOffset);
m_viewer->drawPredefinedPath(p, PredefinedPath::END_PLAY_RANGE, topLeft,
ArrowColor, QColor(Qt::black));
}
}
//-----------------------------------------------------------------------------
void RowArea::drawCurrentRowGadget(QPainter &p, int r0, int r1) {
int currentRow = m_viewer->getCurrentRow();
if (currentRow < r0 || r1 < currentRow) return;
QPoint topLeft = m_viewer->positionToXY(CellPosition(currentRow, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QRect header = m_viewer->orientation()
->rect(PredefinedRect::FRAME_HEADER)
.translated(topLeft);
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
header.adjust(1, 1, -frameAdj.x(), -frameAdj.y());
p.fillRect(header, m_viewer->getCurrentRowBgColor());
}
#if defined(x64)
//-----------------------------------------------------------------------------
void RowArea::drawStopMotionCameraIndicator(QPainter &p) {
int cameraRow = StopMotion::instance()->getXSheetFrameNumber() - 1;
if (cameraRow < 0) return;
QPoint topLeft = m_viewer->positionToXY(CellPosition(cameraRow, 0));
if (!m_viewer->orientation()->isVerticalTimeline()) topLeft.setY(0);
QRect header = m_viewer->orientation()
->rect(PredefinedRect::FRAME_HEADER)
.translated(topLeft);
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
header.adjust(1, 1, -frameAdj.x(), -frameAdj.y());
p.fillRect(header, Qt::GlobalColor::darkGreen);
}
//-----------------------------------------------------------------------------
#endif
void RowArea::drawNavigationTags(QPainter &p, int r0, int r1) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
assert(xsh);
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
NavigationTags *tags = xsh->getNavigationTags();
for (int r = r0; r <= r1; r++) {
if (!xsh->isFrameTagged(r)) continue;
QPoint topLeft = m_viewer->positionToXY(CellPosition(r, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QRect tagRect = m_viewer->orientation()
->rect(PredefinedRect::NAVIGATION_TAG_AREA)
.translated(topLeft)
.translated(-frameAdj / 2);
int frameMid, frameTop;
if (m_viewer->orientation()->isVerticalTimeline()) {
frameMid = tagRect.left() - 3;
frameTop = tagRect.top() + (tagRect.height() / 2);
} else {
frameMid = tagRect.left() + (tagRect.height() / 2) - 3;
frameTop = tagRect.top() + 1;
}
QPainterPath tag = m_viewer->orientation()
->path(PredefinedPath::NAVIGATION_TAG)
.translated(QPoint(frameMid, frameTop));
p.setPen(Qt::black);
p.setBrush(tags->getTagColor(r));
p.drawPath(tag);
p.setBrush(Qt::NoBrush);
}
}
//-----------------------------------------------------------------------------
void RowArea::drawOnionSkinBackground(QPainter &p, int r0, int r1) {
const Orientation *o = m_viewer->orientation();
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
for (int r = r0; r <= r1; r++) {
QPoint basePoint = m_viewer->positionToXY(CellPosition(r, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
basePoint.setY(0);
else
basePoint.setX(0);
QRect oRect = m_viewer->orientation()
->rect(PredefinedRect::ONION_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.translated(basePoint);
p.fillRect(oRect, m_viewer->getOnionSkinAreaBgColor());
}
}
//-----------------------------------------------------------------------------
void RowArea::drawOnionSkinSelection(QPainter &p) {
TApp *app = TApp::instance();
OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask();
TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
assert(xsh);
int currentRow = m_viewer->getCurrentRow();
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
// get onion colors
TPixel frontPixel, backPixel;
bool inksOnly;
Preferences::instance()->getOnionData(frontPixel, backPixel, inksOnly);
// Fill
QColor frontColor((int)frontPixel.r, (int)frontPixel.g, (int)frontPixel.b,
128);
QColor backColor((int)backPixel.r, (int)backPixel.g, (int)backPixel.b, 128);
// Line, outline
QColor frontDotColor((int)frontPixel.r, (int)frontPixel.g, (int)frontPixel.b);
QColor backDotColor((int)backPixel.r, (int)backPixel.g, (int)backPixel.b);
QColor frontDotColorDark(frontDotColor.darker(200));
QColor backDotColorDark(backDotColor.darker(200));
QColor frontDotOutlineColor;
QColor backDotOutlineColor;
if (m_viewer->getOnionSkinAreaBgColor().value() > 128) {
// Set darker outline colors if onion skin area value is above 50% to make
// sure the dots have good visibility across all themes.
frontDotOutlineColor = frontDotColorDark;
backDotOutlineColor = backDotColorDark;
} else {
frontDotOutlineColor = frontDotColor;
backDotOutlineColor = backDotColor;
}
QPen frontPen, backPen;
// If the onion skin is disabled, draw dash line instead.
if (osMask.isEnabled()) {
frontPen.setColor(frontDotOutlineColor);
backPen.setColor(backDotOutlineColor);
} else {
frontPen.setStyle(Qt::DashLine);
frontPen.setColor(QColor(128, 128, 128));
backPen.setStyle(Qt::DashLine);
backPen.setColor(QColor(128, 128, 128));
}
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
// draw line between onion skin range
int minMos = 0;
int maxMos = 0;
int mosCount = osMask.getMosCount();
for (int i = 0; i < mosCount; i++) {
int mos = osMask.getMos(i);
if (minMos > mos) minMos = mos;
if (maxMos < mos) maxMos = mos;
}
p.setBrush(Qt::NoBrush);
int frameAdj_i = (m_viewer->orientation()->isVerticalTimeline())
? frameAdj.y()
: frameAdj.x();
if (minMos < 0) // previous frames
{
int layerAxis = onionCenter_layer;
int fromFrameAxis = m_viewer->rowToFrameAxis(currentRow + minMos) +
onionCenter_frame - (frameAdj_i / 2);
int toFrameAxis = m_viewer->rowToFrameAxis(currentRow) + onionCenter_frame -
(frameAdj_i / 2);
QLine verticalLine = m_viewer->orientation()->verticalLine(
layerAxis, NumberRange(fromFrameAxis, toFrameAxis));
p.setPen(backPen);
if (m_viewer->orientation()->isVerticalTimeline())
p.drawLine(verticalLine.x1(), verticalLine.y1() + 5, verticalLine.x2(),
verticalLine.y2() - 9);
else
p.drawLine(verticalLine.x1() + 5, verticalLine.y1(),
verticalLine.x2() - 10, verticalLine.y2());
}
if (maxMos > 0) // forward frames
{
int layerAxis = onionCenter_layer;
int fromFrameAxis = m_viewer->rowToFrameAxis(currentRow) +
onionCenter_frame - (frameAdj_i / 2);
int toFrameAxis = m_viewer->rowToFrameAxis(currentRow + maxMos) +
onionCenter_frame - (frameAdj_i / 2);
QLine verticalLine = m_viewer->orientation()->verticalLine(
layerAxis, NumberRange(fromFrameAxis, toFrameAxis));
p.setPen(frontPen);
if (m_viewer->orientation()->isVerticalTimeline())
p.drawLine(verticalLine.x1(), verticalLine.y1() + 10, verticalLine.x2(),
verticalLine.y2() - 5);
else
p.drawLine(verticalLine.x1() + 10, verticalLine.y1(),
verticalLine.x2() - 3, verticalLine.y2());
}
// Draw onion skin main handle
QPoint handleTopLeft = m_viewer->positionToXY(CellPosition(currentRow, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
handleTopLeft.setY(0);
else
handleTopLeft.setX(0);
QRect handleRect =
onionRect.translated(handleTopLeft).translated(-frameAdj / 2);
int angle180 = 16 * 180;
int turn =
m_viewer->orientation()->dimension(PredefinedDimension::ONION_TURN) * 16;
p.setPen(backDotOutlineColor);
p.setBrush(QBrush(backColor));
p.drawChord(handleRect, turn, angle180);
p.setPen(frontDotOutlineColor);
p.setBrush(QBrush(frontColor));
p.drawChord(handleRect, turn + angle180, angle180);
// draw onion skin dots
for (int i = 0; i < mosCount; i++) {
// mos : frame offset from the current frame
int mos = osMask.getMos(i);
// skip drawing if the frame is under the mouse cursor
if (m_showOnionToSet == Mos && currentRow + mos == m_row) continue;
p.setPen(mos < 0 ? backDotOutlineColor : frontDotOutlineColor);
if (osMask.isEnabled())
p.setBrush(mos < 0 ? backDotColor : frontDotColor);
else
p.setBrush(Qt::NoBrush);
QPoint topLeft = m_viewer->positionToXY(CellPosition(currentRow + mos, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QRect dotRect = m_viewer->orientation()
->rect(PredefinedRect::ONION_DOT)
.translated(topLeft)
.translated(-frameAdj / 2);
p.drawEllipse(dotRect);
}
//-- draw fixed onions
for (int i = 0; i < osMask.getFosCount(); i++) {
int fos = osMask.getFos(i);
if (fos == currentRow) continue;
// skip drawing if the frame is under the mouse cursor
if (m_showOnionToSet == Fos && fos == m_row) continue;
// Depending on the brightness, make sure dot can be seen on onion area
if (m_viewer->getOnionSkinAreaBgColor().value() > 128)
p.setPen(QColor(25, 118, 170, 255));
else
p.setPen(QColor(0, 255, 255, 128));
if (osMask.isEnabled())
// Depending on the brightness, make sure dot can be seen on onion area
if (m_viewer->getOnionSkinAreaBgColor().value() > 128)
p.setBrush(QBrush(QColor(0, 165, 255, 148)));
else
p.setBrush(QBrush(QColor(0, 255, 255, 148)));
else
p.setBrush(Qt::NoBrush);
QPoint topLeft = m_viewer->positionToXY(CellPosition(fos, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QRect dotRect = m_viewer->orientation()
->rect(PredefinedRect::ONION_DOT_FIXED)
.translated(topLeft)
.translated(-frameAdj / 2);
p.drawEllipse(dotRect);
}
//-- onion placement hint under mouse
if (m_showOnionToSet != None) {
p.setPen(QColor(255, 255, 0));
p.setBrush(QBrush(QColor(255, 255, 0)));
QPoint topLeft = m_viewer->positionToXY(CellPosition(m_row, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QRect dotRect =
m_viewer->orientation()
->rect(m_showOnionToSet == Fos ? PredefinedRect::ONION_DOT_FIXED
: PredefinedRect::ONION_DOT)
.translated(topLeft)
.translated(-frameAdj / 2);
p.drawEllipse(dotRect);
}
}
//-----------------------------------------------------------------------------
void RowArea::drawCurrentTimeIndicator(QPainter &p) {
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
int currentRow = m_viewer->getCurrentRow();
QPoint topLeft = m_viewer->positionToXY(CellPosition(currentRow, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QRect header = m_viewer->orientation()
->rect(PredefinedRect::FRAME_HEADER)
.translated(topLeft)
.translated(-frameAdj / 2);
int frameMid = header.left() + (header.width() / 2) - 1;
int frameTop = header.top() + 22;
QPainterPath markerHead = m_viewer->orientation()
->path(PredefinedPath::TIME_INDICATOR_HEAD)
.translated(QPoint(frameMid, frameTop));
p.setBrush(QColor(0, 162, 232));
p.setPen(Qt::red);
p.drawPath(markerHead);
p.setBrush(Qt::NoBrush);
}
void RowArea::drawCurrentTimeLine(QPainter &p) {
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
int currentRow = m_viewer->getCurrentRow();
QColor indicatorColor = m_viewer->getCurrentTimeIndicatorColor();
QPoint topLeft = m_viewer->positionToXY(CellPosition(currentRow, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QRect header = m_viewer->orientation()
->rect(PredefinedRect::FRAME_HEADER)
.translated(topLeft);
if (m_viewer->orientation()->isVerticalTimeline()) {
header.adjust(1, 1, -frameAdj.x(), -frameAdj.y());
p.setPen(
QPen(indicatorColor, 1, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin));
p.drawRect(header.adjusted(0, 0, -1, -1));
} else {
header.translate(-frameAdj / 2);
int frameMid = header.left() + (header.width() / 2) - 1;
int frameTop = header.top();
int frameBottom = header.bottom();
p.setPen(indicatorColor);
p.drawLine(frameMid, frameTop + 23, frameMid, frameBottom);
}
}
//-----------------------------------------------------------------------------
void RowArea::drawShiftTraceMarker(QPainter &p) {
TApp *app = TApp::instance();
OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask();
TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
assert(xsh);
int currentRow = m_viewer->getCurrentRow();
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
int frameAdj_i = (m_viewer->orientation()->isVerticalTimeline())
? frameAdj.y()
: frameAdj.x();
// get onion colors
TPixel frontPixel, backPixel;
bool inksOnly;
Preferences::instance()->getOnionData(frontPixel, backPixel, inksOnly);
QColor frontColor((int)frontPixel.r, (int)frontPixel.g, (int)frontPixel.b);
QColor backColor((int)backPixel.r, (int)backPixel.g, (int)backPixel.b);
// draw lines to ghost frames
int prevOffset = osMask.getShiftTraceGhostFrameOffset(0);
int forwardOffset = osMask.getShiftTraceGhostFrameOffset(1);
QRect onionRect =
m_viewer->orientation()->rect(PredefinedRect::SHIFTTRACE_DOT);
int onionCenter_frame =
m_viewer->orientation()->frameSide(onionRect).middle();
int onionCenter_layer =
m_viewer->orientation()->layerSide(onionRect).middle();
if (currentRow > 0 && prevOffset < 0) // previous ghost
{
int layerAxis = onionCenter_layer;
int fromFrameAxis = m_viewer->rowToFrameAxis(currentRow + prevOffset) +
onionCenter_frame - (frameAdj_i / 2);
int toFrameAxis = m_viewer->rowToFrameAxis(currentRow) + onionCenter_frame -
(frameAdj_i / 2);
QLine verticalLine = m_viewer->orientation()->verticalLine(
layerAxis, NumberRange(fromFrameAxis, toFrameAxis));
p.setPen(backColor);
p.setBrush(Qt::NoBrush);
p.drawLine(verticalLine);
}
if (forwardOffset > 0) // forward ghost
{
int layerAxis = onionCenter_layer;
int fromFrameAxis = m_viewer->rowToFrameAxis(currentRow) +
onionCenter_frame - (frameAdj_i / 2);
int toFrameAxis = m_viewer->rowToFrameAxis(currentRow + forwardOffset) +
onionCenter_frame - (frameAdj_i / 2);
QLine verticalLine = m_viewer->orientation()->verticalLine(
layerAxis, NumberRange(fromFrameAxis, toFrameAxis));
p.setPen(frontColor);
p.setBrush(Qt::NoBrush);
p.drawLine(verticalLine);
}
if (!m_viewer->orientation()->isVerticalTimeline())
drawCurrentTimeIndicator(p);
// draw dots
std::vector<int> offsVec = {prevOffset, 0, forwardOffset};
std::vector<QColor> colorsVec = {backColor, QColor(0, 162, 232), frontColor};
QFont currentFont = p.font();
QFont tmpFont = p.font();
tmpFont.setPointSize(7);
p.setFont(tmpFont);
for (int i = 0; i < 3; i++) {
if (i != 1 && offsVec[i] == 0) continue;
p.setPen(colorsVec[i]);
p.setBrush(Qt::gray);
QPoint topLeft =
m_viewer->positionToXY(CellPosition(currentRow + offsVec[i], -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QRect dotRect = m_viewer->orientation()
->rect(PredefinedRect::SHIFTTRACE_DOT)
.translated(topLeft)
.translated(-frameAdj / 2);
p.drawRect(dotRect);
// draw shortcut numbers
p.setPen(Qt::black);
p.drawText(dotRect, Qt::AlignCenter, QString::number(i + 1));
}
p.setFont(currentFont);
//-- onion placement hint under mouse
if (m_showOnionToSet == ShiftTraceGhost) {
p.setPen(QColor(255, 255, 0));
p.setBrush(QColor(255, 255, 0, 180));
QPoint topLeft = m_viewer->positionToXY(CellPosition(m_row, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QRect dotRect = m_viewer->orientation()
->rect(PredefinedRect::SHIFTTRACE_DOT)
.translated(topLeft)
.translated(-frameAdj / 2);
p.drawRect(dotRect);
}
}
//-----------------------------------------------------------------------------
namespace {
TStageObjectId getAncestor(TXsheet *xsh, TStageObjectId id) {
assert(id.isColumn());
TStageObjectId parentId;
while (parentId = xsh->getStageObjectParent(id), parentId.isColumn())
id = parentId;
return id;
}
int getPinnedColumnId(int row, TXsheet *xsh, TStageObjectId ancestorId,
int columnCount) {
int tmp_pinnedCol = -1;
for (int c = 0; c < columnCount; c++) {
TStageObjectId columnId(TStageObjectId::ColumnId(c));
if (getAncestor(xsh, columnId) != ancestorId) continue;
TStageObject *obj = xsh->getStageObject(columnId);
if (obj->getPinnedRangeSet()->isPinned(row)) {
tmp_pinnedCol = c;
break;
}
}
return tmp_pinnedCol;
}
} // namespace
void RowArea::drawPinnedCenterKeys(QPainter &p, int r0, int r1) {
// std::cout << "Skeleton Tool activated" << std::endl;
int col = m_viewer->getCurrentColumn();
TXsheet *xsh = m_viewer->getXsheet();
if (col < 0 || !xsh || xsh->isColumnEmpty(col)) return;
TStageObjectId ancestorId = getAncestor(xsh, TStageObjectId::ColumnId(col));
int columnCount = xsh->getColumnCount();
int prev_pinnedCol = -2;
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
QRect keyRect = m_viewer->orientation()
->rect(PredefinedRect::PINNED_CENTER_KEY)
.translated(-frameAdj / 2);
p.setPen(Qt::black);
r1 = (r1 < xsh->getFrameCount() - 1) ? xsh->getFrameCount() - 1 : r1;
for (int r = r0 - 1; r <= r1; r++) {
if (r < 0) continue;
int tmp_pinnedCol = getPinnedColumnId(r, xsh, ancestorId, columnCount);
// Pinned Column is changed at this row
if (tmp_pinnedCol != prev_pinnedCol) {
prev_pinnedCol = tmp_pinnedCol;
if (r != r0 - 1) {
QPoint topLeft = m_viewer->positionToXY(CellPosition(r, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QPoint mouseInCell = m_pos - topLeft;
if (keyRect.contains(mouseInCell))
p.setBrush(QColor(30, 210, 255));
else
p.setBrush(QColor(0, 175, 255));
QRect adjusted = keyRect.translated(topLeft);
p.drawRect(adjusted);
QFont font = p.font();
font.setPixelSize(8);
font.setBold(false);
p.setFont(font);
p.drawText(
adjusted, Qt::AlignCenter,
QString::number((tmp_pinnedCol == -1) ? ancestorId.getIndex() + 1
: tmp_pinnedCol + 1));
}
}
}
}
//-----------------------------------------------------------------------------
void RowArea::paintEvent(QPaintEvent *event) {
QRect toBeUpdated = event->rect();
QPainter p(this);
CellRange cellRange = m_viewer->xyRectToRange(toBeUpdated);
int r0, r1; // range of visible rows
r0 = cellRange.from().frame();
r1 = cellRange.to().frame();
p.setClipRect(toBeUpdated);
// fill background
p.fillRect(toBeUpdated, m_viewer->getBGColor());
drawPlayRangeBackground(p, r0, r1);
if (Preferences::instance()->isOnionSkinEnabled())
drawOnionSkinBackground(p, r0, r1);
if (TApp::instance()->getCurrentFrame()->isEditingScene())
// current frame
drawCurrentRowGadget(p, r0, r1);
#if defined(x64)
StopMotion *stopMotion = StopMotion::instance();
if (stopMotion->getPlaceOnXSheet() && (stopMotion->m_liveViewStatus > 0)) {
drawStopMotionCameraIndicator(p);
}
#endif
// For now, the current frame indicator is not displayed in the xsheet row
// area
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeLine(p);
drawNavigationTags(p, r0, r1);
drawRows(p, r0, r1);
if (TApp::instance()->getCurrentFrame()->isEditingScene()) {
if (CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked())
drawShiftTraceMarker(p);
else if (Preferences::instance()->isOnionSkinEnabled())
drawOnionSkinSelection(p);
// For now, the current frame indicator is not displayed in the xsheet row
// area
else if (Preferences::instance()->isCurrentTimelineIndicatorEnabled() &&
!m_viewer->orientation()->isVerticalTimeline())
drawCurrentTimeIndicator(p);
}
if (TApp::instance()->getCurrentTool()->getTool()->getName() == T_Skeleton)
drawPinnedCenterKeys(p, r0, r1);
drawPlayRange(p, r0, r1);
p.setPen(m_viewer->getVerticalLineColor());
p.setBrush(Qt::NoBrush);
if (m_viewer->orientation()->isVerticalTimeline())
p.drawRect(toBeUpdated.adjusted(-1, -1, -1, 0));
else
p.drawRect(toBeUpdated.adjusted(-1, -1, 0, -1));
}
//-----------------------------------------------------------------------------
void RowArea::mousePressEvent(QMouseEvent *event) {
const Orientation *o = m_viewer->orientation();
m_viewer->setQtModifiers(event->modifiers());
if (event->button() == Qt::LeftButton) {
bool frameAreaIsClicked = false;
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
TPoint pos(event->pos().x(), event->pos().y());
int currentFrame = TApp::instance()->getCurrentFrame()->getFrame();
int row = m_viewer->xyToPosition(pos).frame();
QPoint topLeft = m_viewer->positionToXY(CellPosition(row, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QPoint mouseInCell = event->pos() - topLeft;
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
if (CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked() &&
o->rect(PredefinedRect::SHIFTTRACE_DOT_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell)) {
// Reset ghosts to neighbor frames
if (row == currentFrame)
OnioniSkinMaskGUI::resetShiftTraceFrameOffset();
else {
OnionSkinMask osMask =
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
int prevOffset = osMask.getShiftTraceGhostFrameOffset(0);
int forwardOffset = osMask.getShiftTraceGhostFrameOffset(1);
// Hide previous ghost
if (row == currentFrame + prevOffset)
osMask.setShiftTraceGhostFrameOffset(0, 0);
// Hide forward ghost
else if (row == currentFrame + forwardOffset)
osMask.setShiftTraceGhostFrameOffset(1, 0);
// Move previous ghost
else if (row < currentFrame)
osMask.setShiftTraceGhostFrameOffset(0, row - currentFrame);
// Move forward ghost
else
osMask.setShiftTraceGhostFrameOffset(1, row - currentFrame);
TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osMask);
}
TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
return;
} else if (!CommandManager::instance()
->getAction(MI_ShiftTrace)
->isChecked() &&
Preferences::instance()->isOnionSkinEnabled() &&
o->rect(PredefinedRect::ONION_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell)) {
if (row == currentFrame) {
setDragTool(
XsheetGUI::DragTool::makeCurrentFrameModifierTool(m_viewer));
frameAreaIsClicked = true;
} else if (o->rect(PredefinedRect::ONION_FIXED_DOT_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell))
setDragTool(XsheetGUI::DragTool::makeKeyOnionSkinMaskModifierTool(
m_viewer, true));
else if (o->rect(PredefinedRect::ONION_DOT_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell))
setDragTool(XsheetGUI::DragTool::makeKeyOnionSkinMaskModifierTool(
m_viewer, false));
} else {
int playR0, playR1, step;
XsheetGUI::getPlayRange(playR0, playR1, step);
bool playRangeEnabled = playR0 <= playR1;
if (!playRangeEnabled) {
TXsheet *xsh = m_viewer->getXsheet();
playR1 = xsh->getFrameCount() - 1;
playR0 = 0;
}
int playRangeHOffset = 0;
if (checkExpandFrameArea()) {
playRangeHOffset = m_viewer->orientation()->dimension(
PredefinedDimension::FRAME_AREA_EXPANSION);
}
if (xsh->getNavigationTags()->isTagged(row) &&
o->rect(PredefinedRect::NAVIGATION_TAG_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell)) {
setDragTool(XsheetGUI::DragTool::makeNavigationTagDragTool(m_viewer));
frameAreaIsClicked = true;
} else if (playR1 == -1) { // getFrameCount = 0 i.e. xsheet is empty
setDragTool(
XsheetGUI::DragTool::makeCurrentFrameModifierTool(m_viewer));
frameAreaIsClicked = true;
} else if (o->rect(PredefinedRect::PLAY_RANGE)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.translated(-playRangeHOffset, 0)
.contains(mouseInCell) &&
(row == playR0 || row == playR1)) {
if (!playRangeEnabled) XsheetGUI::setPlayRange(playR0, playR1, step);
setDragTool(XsheetGUI::DragTool::makePlayRangeModifierTool(m_viewer));
} else {
setDragTool(
XsheetGUI::DragTool::makeCurrentFrameModifierTool(m_viewer));
frameAreaIsClicked = true;
}
}
// when shift+click the row area, select a single row region in the cell
// area
if (frameAreaIsClicked && 0 != (event->modifiers() & Qt::ShiftModifier)) {
int filledCol;
for (filledCol = xsh->getColumnCount() - 1; filledCol >= 0; filledCol--) {
TXshColumn *currentColumn = xsh->getColumn(filledCol);
if (!currentColumn) continue;
if (!currentColumn->isEmpty()) break;
}
m_viewer->getCellSelection()->selectNone();
m_viewer->getCellSelection()->selectCells(row, 0, row,
std::max(0, filledCol));
m_viewer->updateCellRowAree();
}
m_viewer->dragToolClick(event);
event->accept();
} // left-click
// pan by middle-drag
else if (event->button() == Qt::MidButton) {
m_pos = event->pos();
m_isPanning = true;
}
}
//-----------------------------------------------------------------------------
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;
if (o->isVerticalTimeline())
delta.setX(0);
else
delta.setY(0);
m_viewer->scroll(delta);
return;
}
m_row = std::max(0, m_viewer->xyToPosition(pos).frame());
int x = pos.x();
if ((event->buttons() & Qt::LeftButton) != 0 &&
!visibleRegion().contains(pos)) {
QRect bounds = visibleRegion().boundingRect();
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();
m_pos = pos;
m_viewer->dragToolDrag(event);
m_showOnionToSet = None;
if (getDragTool()) return;
int currentRow = TApp::instance()->getCurrentFrame()->getFrame();
int row = m_viewer->xyToPosition(m_pos).frame();
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
QPoint topLeft = m_viewer->positionToXY(CellPosition(row, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QPoint mouseInCell = event->pos() - topLeft;
if (row < 0) return;
m_tooltip = tr("");
// whether to show ability to move the shift and trace ghost frame
if (CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked()) {
if (o->rect(PredefinedRect::SHIFTTRACE_DOT_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell)) {
m_showOnionToSet = ShiftTraceGhost;
OnionSkinMask osMask =
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
int prevOffset = osMask.getShiftTraceGhostFrameOffset(0);
int forwardOffset = osMask.getShiftTraceGhostFrameOffset(1);
if (row == currentRow)
m_tooltip =
tr("Click to Reset Shift & Trace Markers to Neighbor Frames\nHold "
"F2 Key on the Viewer to Show This Frame Only");
else if (row == currentRow + prevOffset)
m_tooltip =
tr("Click to Hide This Frame from Shift & Trace\nHold F1 Key on "
"the Viewer to Show This Frame Only");
else if (row == currentRow + forwardOffset)
m_tooltip =
tr("Click to Hide This Frame from Shift & Trace\nHold F3 Key on "
"the Viewer to Show This Frame Only");
else
m_tooltip = tr("Click to Move Shift & Trace Marker");
}
}
// whether to show ability to set onion marks
else if (Preferences::instance()->isOnionSkinEnabled() && row != currentRow) {
if (o->rect(PredefinedRect::ONION_FIXED_DOT_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell))
m_showOnionToSet = Fos;
else if (o->rect(PredefinedRect::ONION_DOT_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell))
m_showOnionToSet = Mos;
}
/*- For showing the pinned center keys -*/
bool isOnPinnedCenterKey = false;
bool isRootBonePinned;
int pinnedCenterColumnId = -1;
if (TApp::instance()->getCurrentTool()->getTool()->getName() == T_Skeleton &&
o->rect(PredefinedRect::PINNED_CENTER_KEY)
.translated(-frameAdj / 2)
.contains(mouseInCell)) {
int col = m_viewer->getCurrentColumn();
TXsheet *xsh = m_viewer->getXsheet();
if (col >= 0 && xsh && !xsh->isColumnEmpty(col)) {
TStageObjectId ancestorId =
getAncestor(xsh, TStageObjectId::ColumnId(col));
int columnCount = xsh->getColumnCount();
/*- Check if the current row is the pinned center key-*/
int prev_pinnedCol =
(row == 0) ? -2
: getPinnedColumnId(row - 1, xsh, ancestorId, columnCount);
int pinnedCol = getPinnedColumnId(row, xsh, ancestorId, columnCount);
if (pinnedCol != prev_pinnedCol) {
isOnPinnedCenterKey = true;
isRootBonePinned = (pinnedCol == -1);
pinnedCenterColumnId =
(isRootBonePinned) ? ancestorId.getIndex() : pinnedCol;
}
}
}
update();
int hOffset = 0;
if (checkExpandFrameArea()) {
hOffset = m_viewer->orientation()->dimension(
PredefinedDimension::FRAME_AREA_EXPANSION);
}
QPoint base0 = m_viewer->positionToXY(CellPosition(m_r0, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
base0.setY(0);
else
base0.setX(-hOffset);
QPoint base1 = m_viewer->positionToXY(CellPosition(m_r1, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
base1.setY(0);
else
base1.setX(-hOffset);
QPainterPath startArrow =
o->path(PredefinedPath::BEGIN_PLAY_RANGE).translated(base0);
QPainterPath endArrow =
o->path(PredefinedPath::END_PLAY_RANGE).translated(base1);
if (!m_tooltip.isEmpty()) return;
if (startArrow.contains(m_pos))
m_tooltip = tr("Playback Start Marker");
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 (o->rect(PredefinedRect::NAVIGATION_TAG_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell)) {
TXsheet *xsh = m_viewer->getXsheet();
QString label = xsh->getNavigationTags()->getTagLabel(m_row);
if (label.isEmpty()) label = "-";
if (xsh->isFrameTagged(m_row)) m_tooltip = tr("Tag: %1").arg(label);
} else if (row == currentRow) {
if (Preferences::instance()->isOnionSkinEnabled() &&
o->rect(PredefinedRect::ONION)
.translated(-frameAdj / 2)
.contains(mouseInCell))
m_tooltip = tr("Double Click to Toggle Onion Skin");
else
m_tooltip = tr("Current Frame");
} else if (m_showOnionToSet == Fos)
m_tooltip = tr("Fixed Onion Skin Toggle");
else if (m_showOnionToSet == Mos)
m_tooltip = tr("Relative Onion Skin Toggle");
}
//-----------------------------------------------------------------------------
void RowArea::mouseReleaseEvent(QMouseEvent *event) {
m_viewer->setQtModifiers(Qt::KeyboardModifiers());
m_viewer->stopAutoPan();
m_isPanning = false;
m_viewer->dragToolRelease(event);
TPoint pos(event->pos().x(), event->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();
}
//-----------------------------------------------------------------------------
void RowArea::contextMenuEvent(QContextMenuEvent *event) {
OnionSkinMask osMask =
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
QMenu *menu = new QMenu(this);
QAction *setStartMarker = menu->addAction(tr("Set Start Marker"));
connect(setStartMarker, SIGNAL(triggered()), SLOT(onSetStartMarker()));
QAction *setStopMarker = menu->addAction(tr("Set Stop Marker"));
connect(setStopMarker, SIGNAL(triggered()), SLOT(onSetStopMarker()));
QAction *setAutoMarkers = menu->addAction(tr("Set Auto Markers"));
connect(setAutoMarkers, SIGNAL(triggered()), SLOT(onSetAutoMarkers()));
setAutoMarkers->setEnabled(canSetAutoMarkers());
QAction *removeMarkers = menu->addAction(tr("Remove Markers"));
connect(removeMarkers, SIGNAL(triggered()), SLOT(onRemoveMarkers()));
// set both the from and to markers at the specified row
QAction *previewThis = menu->addAction(tr("Preview This"));
connect(previewThis, SIGNAL(triggered()), SLOT(onPreviewThis()));
menu->addSeparator();
if (Preferences::instance()->isOnionSkinEnabled()) {
OnioniSkinMaskGUI::addOnionSkinCommand(menu);
menu->addSeparator();
}
CommandManager *cmdManager = CommandManager::instance();
if (!m_viewer->orientation()->isVerticalTimeline()) {
menu->addAction(cmdManager->getAction(MI_ToggleCurrentTimeIndicator));
menu->addSeparator();
}
menu->addAction(cmdManager->getAction(MI_InsertSceneFrame));
menu->addAction(cmdManager->getAction(MI_RemoveSceneFrame));
menu->addAction(cmdManager->getAction(MI_InsertGlobalKeyframe));
menu->addAction(cmdManager->getAction(MI_RemoveGlobalKeyframe));
menu->addAction(cmdManager->getAction(MI_DrawingSubForward));
menu->addAction(cmdManager->getAction(MI_DrawingSubBackward));
menu->addSeparator();
menu->addAction(cmdManager->getAction(MI_ShiftTrace));
menu->addAction(cmdManager->getAction(MI_EditShift));
menu->addAction(cmdManager->getAction(MI_NoShift));
menu->addAction(cmdManager->getAction(MI_ResetShift));
// Tags
menu->addSeparator();
menu->addAction(cmdManager->getAction(MI_ToggleTaggedFrame));
menu->addAction(cmdManager->getAction(MI_EditTaggedFrame));
QMenu *tagMenu = menu->addMenu(tr("Tags"));
NavigationTags *navTags = m_viewer->getXsheet()->getNavigationTags();
QAction *tagAction;
if (!navTags->getCount()) {
tagAction = tagMenu->addAction("Empty");
tagAction->setEnabled(false);
} else {
std::vector<NavigationTags::Tag> tags = navTags->getTags();
for (int i = 0; i < tags.size(); i++) {
int frame = tags[i].m_frame;
QString label = tr("Frame %1").arg(frame + 1);
if (!tags[i].m_label.isEmpty()) label += ": " + tags[i].m_label;
tagAction = tagMenu->addAction(label);
tagAction->setData(frame);
connect(tagAction, SIGNAL(triggered()), this, SLOT(onJumpToTag()));
}
tagMenu->addSeparator();
tagMenu->addAction(cmdManager->getAction(MI_ClearTags));
}
menu->addAction(cmdManager->getAction(MI_NextTaggedFrame));
menu->addAction(cmdManager->getAction(MI_PrevTaggedFrame));
menu->exec(event->globalPos());
}
//-----------------------------------------------------------------------------
// Checks if there is a cell non empty at current row and column to enable the
// auto markers item-menu.
bool RowArea::canSetAutoMarkers() {
TXshCell cell =
m_viewer->getXsheet()->getCell(m_row, m_viewer->getCurrentColumn());
return cell.isEmpty() ? false : true;
}
//-----------------------------------------------------------------------------
int RowArea::getNonEmptyCell(int row, int column, Direction direction) {
int currentPos = row;
bool exit = false;
while (!exit) {
TXshCell cell = m_viewer->getXsheet()->getCell(currentPos, column);
if (cell.isEmpty()) {
(direction == up) ? currentPos++ : currentPos--;
exit = true;
} else
(direction == up) ? currentPos-- : currentPos++;
}
return currentPos;
}
//-----------------------------------------------------------------------------
void RowArea::mouseDoubleClickEvent(QMouseEvent *event) {
int currentFrame = TApp::instance()->getCurrentFrame()->getFrame();
int row = m_viewer->xyToPosition(event->pos()).frame();
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
QPoint topLeft = m_viewer->positionToXY(CellPosition(row, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QPoint mouseInCell = event->pos() - topLeft;
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
event->buttons() & Qt::LeftButton &&
Preferences::instance()->isOnionSkinEnabled() && row == currentFrame &&
m_viewer->orientation()
->rect(PredefinedRect::ONION)
.translated(-frameAdj / 2)
.contains(mouseInCell)) {
TOnionSkinMaskHandle *osmh = TApp::instance()->getCurrentOnionSkin();
OnionSkinMask osm = osmh->getOnionSkinMask();
osm.enable(!osm.isEnabled());
osmh->setOnionSkinMask(osm);
osmh->notifyOnionSkinMaskChanged();
}
}
//-----------------------------------------------------------------------------
bool RowArea::event(QEvent *event) {
if (event->type() == QEvent::ToolTip) {
if (!m_tooltip.isEmpty())
QToolTip::showText(mapToGlobal(m_pos), m_tooltip);
else
QToolTip::hideText();
} else if (event->type() == QEvent::Leave) {
m_showOnionToSet = None;
}
return QWidget::event(event);
}
//-----------------------------------------------------------------------------
void RowArea::setMarker(int index) {
assert(m_row >= 0);
// I use only the step value..
int unused0, unused1, step;
getPlayRange(unused0, unused1, step);
if (m_r0 > m_r1) {
m_r0 = 0;
m_r1 = TApp::instance()->getCurrentScene()->getScene()->getFrameCount() - 1;
if (m_r1 < 1) m_r1 = 1;
}
if (index == 0) {
m_r0 = m_row;
if (m_r1 < m_r0) m_r1 = m_r0;
} else if (index == 1) {
m_r1 = m_row;
if (m_r1 < m_r0) m_r0 = m_r1;
m_r1 -= (step == 0) ? (m_r1 - m_r0) : (m_r1 - m_r0) % step;
}
setPlayRange(m_r0, m_r1, step);
}
//-----------------------------------------------------------------------------
void RowArea::onSetStartMarker() {
setMarker(0);
update();
}
//-----------------------------------------------------------------------------
void RowArea::onSetStopMarker() {
setMarker(1);
update();
}
//-----------------------------------------------------------------------------
// set both the from and to markers at the specified row
void RowArea::onPreviewThis() {
assert(m_row >= 0);
int r0, r1, step;
getPlayRange(r0, r1, step);
setPlayRange(m_row, m_row, step);
update();
}
// Set the playing markers to the continuous block of the cell pointed by
// current row and column
void RowArea::onSetAutoMarkers() {
int currentColumn = m_viewer->getCurrentColumn();
int top = getNonEmptyCell(m_row, currentColumn, Direction::up);
int bottom = getNonEmptyCell(m_row, currentColumn, Direction::down);
int r0, r1, step;
getPlayRange(r0, r1, step);
setPlayRange(top, bottom, step);
update();
}
//-----------------------------------------------------------------------------
void RowArea::onRemoveMarkers() {
int step;
XsheetGUI::getPlayRange(m_r0, m_r1, step);
XsheetGUI::setPlayRange(0, -1, step);
update();
}
//-----------------------------------------------------------------------------
void RowArea::onJumpToTag() {
QAction *senderAction = qobject_cast<QAction *>(sender());
assert(senderAction);
int frame = senderAction->data().toInt();
m_viewer->setCurrentRow(frame);
}
} // namespace XsheetGUI