#include "xshcellviewer.h"
// Tnz6 includes
#include "xsheetviewer.h"
#include "tapp.h"
#include "menubarcommandids.h"
#include "cellselection.h"
#include "keyframeselection.h"
#include "filmstripselection.h"
#include "filmstripcommand.h"
#include "xsheetdragtool.h"
#include "iocommand.h"
#include "castselection.h"
#include "cellkeyframeselection.h"
#include "keyframedata.h"
#include "columnselection.h"
// TnzTools includes
#include "tools/cursormanager.h"
#include "tools/toolhandle.h"
// TnzQt includes
#include "toonzqt/gutil.h"
#include "toonzqt/tselectionhandle.h"
#include "historytypes.h"
// TnzLib includes
#include "toonz/tscenehandle.h"
#include "toonz/txsheethandle.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/tobjecthandle.h"
#include "toonz/tframehandle.h"
#include "toonz/txshleveltypes.h"
#include "toonz/txsheet.h"
#include "toonz/toonzscene.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/txshsoundcolumn.h"
#include "toonz/txshcell.h"
#include "toonz/tstageobject.h"
#include "toonz/tstageobjecttree.h"
#include "toonz/sceneproperties.h"
#include "toonz/columnfan.h"
#include "toonz/levelset.h"
#include "toonz/levelproperties.h"
#include "toonz/txshsoundlevel.h"
#include "toonz/txshnoteset.h"
#include "toonz/txshcolumn.h"
#include "toonz/tcolumnfx.h"
#include "toonz/txshchildlevel.h"
#include "toonz/txshzeraryfxcolumn.h"
#include "toonz/txshsoundtextlevel.h"
#include "toonz/txshpalettelevel.h"
#include "toonz/doubleparamcmd.h"
#include "toonz/preferences.h"
// TnzBase includes
#include "tdoublekeyframe.h"
// TnzCore includes
#include "tconvert.h"
#include "tundo.h"
// Qt includes
#include <QPainter>
#include <QMouseEvent>
#include <QMenu>
#include <QUrl>
#include <QToolTip>
#include <QApplication>
#include <QClipboard>
namespace {
bool selectionContainTlvImage(TCellSelection *selection, TXsheet *xsheet,
bool onlyTlv = false) {
int r0, r1, c0, c1;
selection->getSelectedCells(r0, c0, r1, c1);
// Verifico se almeno un livello della selezione e' un tlv
int c, r;
for (c = c0; c <= c1; c++)
for (r = r0; r <= r1; r++) {
TXshCell cell = xsheet->getCell(r, c);
TXshSimpleLevel *level =
cell.isEmpty() ? (TXshSimpleLevel *)0 : cell.getSimpleLevel();
if (!level) continue;
if (level->getType() != TZP_XSHLEVEL && onlyTlv) return false;
if (level->getType() == TZP_XSHLEVEL && !onlyTlv) return true;
}
return false;
}
//-----------------------------------------------------------------------------
bool selectionContainRasterImage(TCellSelection *selection, TXsheet *xsheet,
bool onlyRaster = false) {
int r0, r1, c0, c1;
selection->getSelectedCells(r0, c0, r1, c1);
// Verifico se almeno un livello della selezione e' un tlv
int c, r;
for (c = c0; c <= c1; c++)
for (r = r0; r <= r1; r++) {
TXshCell cell = xsheet->getCell(r, c);
TXshSimpleLevel *level =
cell.isEmpty() ? (TXshSimpleLevel *)0 : cell.getSimpleLevel();
if (!level) continue;
if (level->getType() != OVL_XSHLEVEL && onlyRaster) return false;
if (level->getType() == OVL_XSHLEVEL && !onlyRaster) return true;
}
return false;
}
//-----------------------------------------------------------------------------
bool selectionContainLevelImage(TCellSelection *selection, TXsheet *xsheet) {
int r0, r1, c0, c1;
selection->getSelectedCells(r0, c0, r1, c1);
// Verifico se almeno un livello della selezione e' un tlv, un pli o un
// fullcolor (!= .psd)
int c, r;
for (c = c0; c <= c1; c++)
for (r = r0; r <= r1; r++) {
TXshCell cell = xsheet->getCell(r, c);
TXshSimpleLevel *level =
cell.isEmpty() ? (TXshSimpleLevel *)0 : cell.getSimpleLevel();
if (!level) continue;
std::string ext = level->getPath().getType();
int type = level->getType();
if (type == TZP_XSHLEVEL || type == PLI_XSHLEVEL ||
(type == OVL_XSHLEVEL && ext != "psd"))
return true;
}
return false;
}
//-----------------------------------------------------------------------------
/*! convert the last one digit of the frame number to alphabet
Ex. 12 -> 1B 21 -> 2A 30 -> 3
*/
void parse_with_letter(const QString &text, std::wstring &levelName,
TFrameId &fid) {
QRegExp spaces("\\t|\\s");
QRegExp numbers("\\d+");
QRegExp caracters("[^\\d+]");
QString str = text;
// remove final spaces
int size = str.size();
while (str.lastIndexOf(spaces) == size - 1 && size > 0)
str.remove(str.size() - 1, 1);
if (str.isEmpty()) {
levelName = L"";
fid = TFrameId::NO_FRAME;
return;
}
int lastSpaceIndex = str.lastIndexOf(spaces);
// if input only digits / alphabet
if (lastSpaceIndex == -1) {
// in case of only level name
if (str.contains(caracters) && !str.contains(numbers)) {
levelName = text.toStdWString();
fid = TFrameId::NO_FRAME;
}
// in case of input frame number + alphabet
else if (str.contains(numbers) && str.contains(caracters)) {
levelName = L"";
QString appendix = str.right(1);
int appendNum = 0;
if (appendix == QString('A') || appendix == QString('a'))
appendNum = 1;
else if (appendix == QString('B') || appendix == QString('b'))
appendNum = 2;
else if (appendix == QString('C') || appendix == QString('c'))
appendNum = 3;
else if (appendix == QString('D') || appendix == QString('d'))
appendNum = 4;
else if (appendix == QString('E') || appendix == QString('e'))
appendNum = 5;
else if (appendix == QString('F') || appendix == QString('f'))
appendNum = 6;
else if (appendix == QString('G') || appendix == QString('g'))
appendNum = 7;
else if (appendix == QString('H') || appendix == QString('h'))
appendNum = 8;
else if (appendix == QString('I') || appendix == QString('i'))
appendNum = 9;
str.chop(1);
if (str.contains(caracters) || !str.contains(numbers)) {
levelName = text.toStdWString();
fid = TFrameId::NO_FRAME;
return;
}
fid = TFrameId(str.toInt() * 10 + appendNum);
}
// in case of input only frame number
else if (str.contains(numbers)) {
levelName = L"";
fid = TFrameId(str.toInt() * 10);
}
}
// if input both the level name and frame number
else {
QString lastString = str.right(str.size() - 1 - lastSpaceIndex);
if (lastString.contains(numbers)) {
// level name
QString firstString = str.left(lastSpaceIndex);
levelName = firstString.toStdWString();
// frame number + alphabet
if (lastString.contains(caracters)) {
QString appendix = lastString.right(1);
int appendNum = 0;
if (appendix == QString('A') || appendix == QString('a'))
appendNum = 1;
else if (appendix == QString('B') || appendix == QString('b'))
appendNum = 2;
else if (appendix == QString('C') || appendix == QString('c'))
appendNum = 3;
else if (appendix == QString('D') || appendix == QString('d'))
appendNum = 4;
else if (appendix == QString('E') || appendix == QString('e'))
appendNum = 5;
else if (appendix == QString('F') || appendix == QString('f'))
appendNum = 6;
else if (appendix == QString('G') || appendix == QString('g'))
appendNum = 7;
else if (appendix == QString('H') || appendix == QString('h'))
appendNum = 8;
else if (appendix == QString('I') || appendix == QString('i'))
appendNum = 9;
lastString.chop(1);
if (lastString.contains(caracters) || !lastString.contains(numbers)) {
levelName = text.toStdWString();
fid = TFrameId::NO_FRAME;
return;
}
fid = TFrameId(lastString.toInt() * 10 + appendNum);
}
// only frame number
else if (lastString.contains(numbers)) {
fid = TFrameId(lastString.toInt() * 10);
}
} else {
levelName = text.toStdWString();
fid = TFrameId::NO_FRAME;
}
}
}
//-----------------------------------------------------------------------------
void parse(const QString &text, std::wstring &levelName, TFrameId &fid) {
QRegExp spaces("\\t|\\s");
QRegExp numbers("\\d+");
QRegExp caracters("[^\\d+]");
QString str = text;
// remove final spaces
int size = str.size();
while (str.lastIndexOf(spaces) == size - 1 && size > 0)
str.remove(str.size() - 1, 1);
if (str.isEmpty()) {
levelName = L"";
fid = TFrameId::NO_FRAME;
return;
}
int lastSpaceIndex = str.lastIndexOf(spaces);
if (lastSpaceIndex == -1) {
if (str.contains(numbers) && !str.contains(caracters)) {
levelName = L"";
fid = TFrameId(str.toInt());
} else if (str.contains(caracters)) {
levelName = text.toStdWString();
fid = TFrameId::NO_FRAME;
}
} else {
QString lastString = str.right(str.size() - 1 - lastSpaceIndex);
if (lastString.contains(numbers) && !lastString.contains(caracters)) {
QString firstString = str.left(lastSpaceIndex);
levelName = firstString.toStdWString();
fid = TFrameId(lastString.toInt());
} else if (lastString.contains(caracters)) {
levelName = text.toStdWString();
fid = TFrameId::NO_FRAME;
}
}
}
//-----------------------------------------------------------------------------
bool isGlobalKeyFrameWithSameTypeDiffFromLinear(TStageObject *stageObject,
int frame) {
if (!stageObject->isFullKeyframe(frame)) return false;
TDoubleKeyframe::Type type =
stageObject->getParam(TStageObject::T_Angle)->getKeyframeAt(frame).m_type;
if (type == TDoubleKeyframe::Linear) return false;
if (type !=
stageObject->getParam(TStageObject::T_X)
->getKeyframeAt(frame)
.m_type ||
type !=
stageObject->getParam(TStageObject::T_Y)
->getKeyframeAt(frame)
.m_type ||
type !=
stageObject->getParam(TStageObject::T_Z)
->getKeyframeAt(frame)
.m_type ||
type !=
stageObject->getParam(TStageObject::T_SO)
->getKeyframeAt(frame)
.m_type ||
type !=
stageObject->getParam(TStageObject::T_ScaleX)
->getKeyframeAt(frame)
.m_type ||
type !=
stageObject->getParam(TStageObject::T_ScaleY)
->getKeyframeAt(frame)
.m_type ||
type !=
stageObject->getParam(TStageObject::T_Scale)
->getKeyframeAt(frame)
.m_type ||
type !=
stageObject->getParam(TStageObject::T_Path)
->getKeyframeAt(frame)
.m_type ||
type !=
stageObject->getParam(TStageObject::T_ShearX)
->getKeyframeAt(frame)
.m_type ||
type !=
stageObject->getParam(TStageObject::T_ShearY)
->getKeyframeAt(frame)
.m_type)
return false;
return true;
}
//-----------------------------------------------------------------------------
bool rectContainsPos(QList<QRect> rects, QPoint pos) {
int i;
for (i = 0; i < rects.size(); i++)
if (rects.at(i).contains(pos)) return true;
return false;
}
//-----------------------------------------------------------------------------
bool isGlobalKeyFrameWithSamePrevTypeDiffFromLinear(TStageObject *stageObject,
int frame) {
if (!stageObject->isFullKeyframe(frame)) return false;
TDoubleKeyframe::Type type = stageObject->getParam(TStageObject::T_Angle)
->getKeyframeAt(frame)
.m_prevType;
if (type == TDoubleKeyframe::Linear) return false;
if (type !=
stageObject->getParam(TStageObject::T_X)
->getKeyframeAt(frame)
.m_prevType ||
type !=
stageObject->getParam(TStageObject::T_Y)
->getKeyframeAt(frame)
.m_prevType ||
type !=
stageObject->getParam(TStageObject::T_Z)
->getKeyframeAt(frame)
.m_prevType ||
type !=
stageObject->getParam(TStageObject::T_SO)
->getKeyframeAt(frame)
.m_prevType ||
type !=
stageObject->getParam(TStageObject::T_ScaleX)
->getKeyframeAt(frame)
.m_prevType ||
type !=
stageObject->getParam(TStageObject::T_ScaleY)
->getKeyframeAt(frame)
.m_prevType ||
type !=
stageObject->getParam(TStageObject::T_Scale)
->getKeyframeAt(frame)
.m_prevType ||
type !=
stageObject->getParam(TStageObject::T_Path)
->getKeyframeAt(frame)
.m_prevType ||
type !=
stageObject->getParam(TStageObject::T_ShearX)
->getKeyframeAt(frame)
.m_prevType ||
type !=
stageObject->getParam(TStageObject::T_ShearY)
->getKeyframeAt(frame)
.m_prevType)
return false;
return true;
}
//-----------------------------------------------------------------------------
#ifdef LINETEST
int getParamStep(TStageObject *stageObject, int frame) {
TDoubleKeyframe keyFrame =
stageObject->getParam(TStageObject::T_Angle)->getKeyframeAt(frame);
return keyFrame.m_step;
}
//-----------------------------------------------------------------------------
void setParamStep(int indexKeyframe, int step, TDoubleParam *param) {
KeyframeSetter setter(param, indexKeyframe);
setter.setStep(step);
}
#endif
//=============================================================================
// RenameCellUndo
//-----------------------------------------------------------------------------
class RenameCellUndo : public TUndo {
int m_row;
int m_col;
const TXshCell m_oldCell;
const TXshCell m_newCell;
public:
// indices sono le colonne inserite
RenameCellUndo(int row, int col, TXshCell oldCell, TXshCell newCell)
: m_row(row), m_col(col), m_oldCell(oldCell), m_newCell(newCell) {}
void setcell(const TXshCell cell) const {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
assert(xsh);
if (cell.isEmpty())
xsh->clearCells(m_row, m_col);
else
xsh->setCell(m_row, m_col, cell);
app->getCurrentXsheet()->notifyXsheetChanged();
}
void undo() const { setcell(m_oldCell); }
void redo() const { setcell(m_newCell); }
int getSize() const { return sizeof *this; }
QString getHistoryString() {
return QObject::tr("Rename Cell at Column %1 Frame %2")
.arg(QString::number(m_col + 1))
.arg(QString::number(m_row + 1));
}
int getHistoryType() { return HistoryType::Xsheet; }
};
// display upper-directional smart tab only when pressing ctrl key
bool isCtrlPressed = false;
//-----------------------------------------------------------------------------
} // namespace
//-----------------------------------------------------------------------------
namespace XsheetGUI {
//=============================================================================
// RenameCellField
//-----------------------------------------------------------------------------
RenameCellField::RenameCellField(QWidget *parent, XsheetViewer *viewer)
: QLineEdit(parent), m_viewer(viewer), m_row(-1), m_col(-1) {
setFixedSize(XsheetGUI::ColumnWidth + 3, XsheetGUI::RowHeight + 4);
connect(this, SIGNAL(returnPressed()), SLOT(onReturnPressed()));
setContextMenuPolicy(Qt::PreventContextMenu);
}
//-----------------------------------------------------------------------------
void RenameCellField::showInRowCol(int row, int col) {
m_viewer->scrollTo(row, col);
m_row = row;
m_col = col;
move(QPoint(m_viewer->columnToX(col) - 1, m_viewer->rowToY(row) - 2));
static QFont font("Helvetica", XSHEET_FONT_SIZE, QFont::Normal);
setFont(font);
// Se la cella non e' vuota setto il testo
TXsheet *xsh = m_viewer->getXsheet();
TXshCell cell = xsh->getCell(row, col);
if (!cell.isEmpty()) {
TFrameId fid = cell.getFrameId();
std::wstring levelName = cell.m_level->getName();
// convert the last one digit of the frame number to alphabet
// Ex. 12 -> 1B 21 -> 2A 30 -> 3
if (Preferences::instance()->isShowFrameNumberWithLettersEnabled())
setText((fid.isEmptyFrame() || fid.isNoFrame())
? QString::fromStdWString(levelName)
: QString::fromStdWString(levelName) + QString(" ") +
m_viewer->getFrameNumberWithLetters(fid.getNumber()));
else {
std::string frameNumber("");
if (fid.getNumber() > 0) frameNumber = std::to_string(fid.getNumber());
if (fid.getLetter() != 0) frameNumber.append(1, fid.getLetter());
setText((frameNumber.empty())
? QString::fromStdWString(levelName)
: QString::fromStdWString(levelName) + QString(" ") +
QString::fromStdString(frameNumber));
}
selectAll();
}
// clear the field if the empty cell is clicked
else {
setText("");
}
show();
raise();
setFocus();
}
//-----------------------------------------------------------------------------
void RenameCellField::renameCell() {
QString s = text();
std::wstring newName = s.toStdWString();
setText("");
std::wstring levelName;
TFrameId fid;
// convert the last one digit of the frame number to alphabet
// Ex. 12 -> 1B 21 -> 2A 30 -> 3
if (Preferences::instance()->isShowFrameNumberWithLettersEnabled())
parse_with_letter(QString::fromStdWString(newName), levelName, fid);
else
parse(QString::fromStdWString(newName), levelName, fid);
TXshLevel *xl = 0;
TXsheet *xsheet = m_viewer->getXsheet();
bool animationSheetEnabled =
Preferences::instance()->isAnimationSheetEnabled();
bool levelDefined =
xsheet->getCell(m_row, m_col).getSimpleLevel() != 0 ||
m_row > 0 && xsheet->getCell(m_row - 1, m_col).getSimpleLevel() != 0;
if (animationSheetEnabled && levelDefined) {
TXshCell cell = xsheet->getCell(m_row, m_col);
TXshSimpleLevel *sl = cell.getSimpleLevel();
if (sl) {
QRegExp fidRe("([0-9]+)([a-z]?)");
if (fidRe.exactMatch(s)) {
#if QT_VERSION >= 0x050500
fid = TFrameId(fidRe.cap(1).toInt(),
fidRe.cap(2) == "" ? 0 : fidRe.cap(2).toLatin1()[0]);
#else
fid = TFrameId(fidRe.cap(1).toInt(),
fidRe.cap(2) == "" ? 0 : fidRe.cap(2).toAscii()[0]);
#endif
FilmstripCmd::renumberDrawing(sl, cell.m_frameId, fid);
}
}
return;
}
if (levelName == L"") {
// prendo il livello dalla cella precedente. Se non c'e' dalla corrente
// (forse sto modificando una cella non vuota)
// no: faccio il contrario. cfr #6152. celle A-1,B-1. Edito B-1 e la
// rinomino in 2. Quindi devo prima verificare
// che la cella corrente non sia vuota
TXshCell cell = xsheet->getCell(m_row, m_col);
if (cell.isEmpty() && m_row > 0) {
cell = xsheet->getCell(m_row - 1, m_col);
}
xl = cell.m_level.getPointer();
if (!xl || (xl->getType() == OVL_XSHLEVEL &&
xl->getPath().getFrame() == TFrameId::NO_FRAME))
return;
if (fid == TFrameId::NO_FRAME) fid = cell.m_frameId;
} else {
ToonzScene *scene = m_viewer->getXsheet()->getScene();
TLevelSet *levelSet = scene->getLevelSet();
xl = levelSet->getLevel(levelName);
if (!xl && fid != TFrameId::NO_FRAME) {
if (animationSheetEnabled) {
Preferences *pref = Preferences::instance();
int levelType = pref->getDefLevelType();
xl = scene->createNewLevel(levelType, levelName);
TXshSimpleLevel *sl = xl->getSimpleLevel();
if (levelType == TZP_XSHLEVEL || levelType == OVL_XSHLEVEL)
sl->setFrame(fid, sl->createEmptyFrame());
} else
xl = scene->createNewLevel(TZI_XSHLEVEL, levelName);
}
}
if (!xl) return;
TXshCell cell(xl, fid);
// register undo only if the cell is modified
if (cell == xsheet->getCell(m_row, m_col)) return;
RenameCellUndo *undo =
new RenameCellUndo(m_row, m_col, xsheet->getCell(m_row, m_col), cell);
xsheet->setCell(m_row, m_col, cell);
TUndoManager::manager()->add(undo);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
TApp *app = TApp::instance();
if (m_row == app->getCurrentFrame()->getFrame()) {
app->getCurrentTool()->onImageChanged(
(TImage::Type)app->getCurrentImageType());
xsheet->getStageObjectTree()->invalidateAll();
}
}
//-----------------------------------------------------------------------------
void RenameCellField::onReturnPressed() {
renameCell();
showInRowCol(m_row + 1, m_col);
}
//-----------------------------------------------------------------------------
void RenameCellField::focusOutEvent(QFocusEvent *e) {
hide();
QLineEdit::focusOutEvent(e);
}
//-----------------------------------------------------------------------------
void RenameCellField::keyPressEvent(QKeyEvent *event) {
if (event->key() == Qt::Key_Up && m_row > 0) {
renameCell();
showInRowCol(m_row - 1, m_col);
} else if (event->key() == Qt::Key_Down) {
renameCell();
showInRowCol(m_row + 1, m_col);
}
QLineEdit::keyPressEvent(event);
}
//=============================================================================
// CellArea
//-----------------------------------------------------------------------------
#if QT_VERSION >= 0x050500
CellArea::CellArea(XsheetViewer *parent, Qt::WindowFlags flags)
#else
CellArea::CellArea(XsheetViewer *parent, Qt::WFlags flags)
#endif
: QWidget(parent, flags)
, m_viewer(parent)
, m_levelExtenderRect()
, m_soundLevelModifyRects()
, m_isPanning(false)
, m_isMousePressed(false)
, m_pos(-1, -1)
, m_tooltip(tr(""))
, m_renameCell(new RenameCellField(this, m_viewer)) {
setAcceptDrops(true);
setMouseTracking(true);
m_renameCell->hide();
setFocusPolicy(Qt::NoFocus);
}
//-----------------------------------------------------------------------------
CellArea::~CellArea() {}
//-----------------------------------------------------------------------------
DragTool *CellArea::getDragTool() const { return m_viewer->getDragTool(); }
void CellArea::setDragTool(DragTool *dragTool) {
m_viewer->setDragTool(dragTool);
}
//-----------------------------------------------------------------------------
void CellArea::drawCells(QPainter &p, const QRect toBeUpdated) {
TXsheet *xsh = m_viewer->getXsheet();
ColumnFan *columnFan = xsh->getColumnFan();
TCellSelection *cellSelection = m_viewer->getCellSelection();
int rS0, cS0, rS1, cS1;
if (!cellSelection->isEmpty())
cellSelection->getSelectedCells(rS0, cS0, rS1, cS1);
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());
// Sfondo bianco se row < xsh->getFrameCount()
int xshRowCount = xsh->getFrameCount();
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;
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
int distance, offset;
TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers(
distance, offset);
if (distance == 0) distance = 6;
int currentRow = m_viewer->getCurrentRow();
int col, row;
int x0 = std::max(1, toBeUpdated.left());
int x1 = std::min(width(), toBeUpdated.right());
int y0 = std::max(1, toBeUpdated.top());
int y1 = std::min(height() - 2, toBeUpdated.bottom());
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);
continue;
}
TXshColumn *column = xsh->getColumn(col);
bool isColumn = (column) ? true : false;
bool isSoundColumn = false;
bool isPaletteColumn = false;
bool isSoundTextColumn = false;
if (isColumn) {
isSoundColumn = column->getSoundColumn() != 0;
isPaletteColumn = column->getPaletteColumn() != 0;
isSoundTextColumn = column->getSoundTextColumn() != 0;
}
// check if the column is reference
bool isReference = true;
if (column) { // Verifico se la colonna e' una mask
if (column->isControl() || column->isRendered() ||
column->getMeshColumn())
isReference = false;
}
x0 = x + 1;
x1 = m_viewer->columnToX(col + 1);
// draw vertical lines
p.setPen(Qt::black);
if (x > 0) p.drawLine(x, y0, x, y1);
p.setPen(Qt::white);
if (x > 1) p.drawLine(x - 1, y0, x - 1, y1);
for (row = r0; row <= r1; row++) {
// draw horizontal lines
QColor color = ((row - offset) % distance != 0)
? m_viewer->getLightLineColor()
: m_viewer->getMarkerLineColor();
p.setPen(color);
int y = m_viewer->rowToY(row);
p.drawLine(x0, y, x1, y);
if (!isColumn) continue;
// Disegno le celle a seconda del tipo di colonna
if (isSoundColumn)
drawSoundCell(p, row, col);
else if (isPaletteColumn)
drawPaletteCell(p, row, col, isReference);
else if (isSoundTextColumn)
drawSoundTextCell(p, row, col);
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
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);
p.setPen(Qt::black);
p.setBrush(SmartTabColor);
p.drawRoundRect(m_levelExtenderRect, 30, 75);
QColor color = ((rS1 + 1 - 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);
}
}
//-----------------------------------------------------------------------------
void CellArea::drawSoundCell(QPainter &p, int row, int col) {
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);
int maxNumFrame = soundColumn->getMaxFrame() + 1;
int startFrame = soundColumn->getFirstRow();
TXshCell cell = soundColumn->getCell(row);
if (soundColumn->isCellEmpty(row) || cell.isEmpty() || row > maxNumFrame ||
row < startFrame)
return;
TXshSoundLevelP soundLevel = cell.getSoundLevel();
int r0, r1;
if (!soundColumn->getLevelRange(row, r0, r1)) return;
bool isFirstRow = (row == r0);
bool isLastRow = (row == r1);
TCellSelection *cellSelection = m_viewer->getCellSelection();
TColumnSelection *columnSelection = m_viewer->getColumnSelection();
bool isSelected = cellSelection->isCellSelected(row, col) ||
columnSelection->isColumnSelected(col);
// sfondo celle
QRect backgroundRect = QRect(x + 1, y + 1, ColumnWidth - 1, RowHeight - 1);
p.fillRect(backgroundRect, QBrush((isSelected) ? SelectedSoundColumnColor
: SoundColumnColor));
if (isLastRow) {
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(SoundColumnBorderColor));
} else
p.fillRect(QRect(x, y, 6, RowHeight), QBrush(SoundColumnBorderColor));
int x1 = rect.x() + 5;
int x2 = rect.x() + rect.width();
int x1Bis = x2 - 6;
int offset = row - cell.getFrameId().getNumber();
int y0 = rect.y();
int y1 = rect.bottomLeft().y();
int soundY0 = y0 - m_viewer->rowToY(offset);
int trackWidth = (x1Bis - x1) / 2;
int lastMin, lastMax;
DoublePair minmax;
soundLevel->getValueAtPixel(soundY0, minmax);
double pmin = minmax.first + trackWidth / 2.;
double pmax = minmax.second + trackWidth / 2.;
int delta = x1 + trackWidth / 2;
lastMin = tcrop((int)pmin, 0, trackWidth / 2) + delta;
lastMax = tcrop((int)pmax, trackWidth / 2, trackWidth - 1) + delta;
bool scrub = m_viewer->isScrubHighlighted(row, col);
int i;
for (i = y0; i <= y1; i++) {
DoublePair minmax;
soundLevel->getValueAtPixel(soundY0, minmax);
soundY0++;
int min, max;
double pmin = minmax.first + trackWidth / 2.;
double pmax = minmax.second + trackWidth / 2.;
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((isSelected) ? SelectedSoundColumnColor : SoundColumnColor);
p.drawLine(x1, i, x1Bis, i);
} else {
p.setPen(SoundColumnTrackColor);
p.drawLine(x1Bis + 1, i, x2, i);
}
}
if (scrub && i % 2) {
p.setPen(SoundColumnHlColor);
p.drawLine(x1Bis + 1, i, x2, i);
}
if (i != y0) {
// "traccia audio" al centro della colonna
p.setPen(SoundColumnTrackColor);
p.drawLine(lastMin, i, min, i);
p.drawLine(lastMax, i, max, i);
}
lastMin = min;
lastMax = max;
}
p.setPen(SoundColumnExtenderColor);
int r0WithoutOff, r1WithoutOff;
bool ret =
soundColumn->getLevelRangeWithoutOffset(row, r0WithoutOff, r1WithoutOff);
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);
}
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);
m_soundLevelModifyRects.append(modifierRect);
}
}
//-----------------------------------------------------------------------------
void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference) {
TXsheet *xsh = m_viewer->getXsheet();
TXshCell cell = xsh->getCell(row, col);
TXshCell prevCell;
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);
// nothing to draw
if (cell.isEmpty() && prevCell.isEmpty()) return;
TXshCell nextCell;
nextCell = xsh->getCell(row + 1, col);
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
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;
}
// get cell colors
QColor cellColor, sideColor;
if (isReference) {
cellColor = (isSelected) ? m_viewer->getSelectedReferenceColumnColor()
: m_viewer->getReferenceColumnColor();
sideColor = m_viewer->getReferenceColumnBorderColor();
} else {
int levelType;
m_viewer->getCellTypeAndColors(levelType, cellColor, sideColor, cell,
isSelected);
}
// check if the level is scanned but not cleanupped
bool yetToCleanupCell = false;
if (!cell.isEmpty() && cell.getSimpleLevel()) {
int frameStatus = cell.getSimpleLevel()->getFrameStatus(cell.m_frameId);
const int mask = TXshSimpleLevel::Cleanupped | TXshSimpleLevel::Scanned;
const int yetToCleanupValue = TXshSimpleLevel::Scanned;
yetToCleanupCell = (frameStatus & mask) == yetToCleanupValue;
}
// 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);
// 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));
}
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;
// draw marker interval
if (isAfterMarkers) {
p.setPen(m_viewer->getMarkerLineColor());
p.drawLine(x, y, x + 6, y);
}
QRect nameRect = rect.adjusted(7, 4, -6, 0);
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);
}
// draw text in red if the file does not exist
bool isRed = false;
TXshSimpleLevel *sl = cell.getSimpleLevel();
if (sl && !sl->isFid(cell.m_frameId)) isRed = true;
TXshChildLevel *cl = cell.getChildLevel();
if (cl && cell.getFrameId().getNumber() - 1 >= cl->getFrameCount())
isRed = true;
p.setPen(isRed ? m_viewer->getSelectedColumnTextColor()
: m_viewer->getTextColor());
#ifdef _WIN32
static QFont font("Arial", XSHEET_FONT_SIZE, QFont::Normal);
#else
static QFont font("Helvetica", XSHEET_FONT_SIZE, QFont::Normal);
#endif
p.setFont(font);
// 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());
}
// draw frame number
else {
TFrameId fid = cell.m_frameId;
// 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,
m_viewer->getFrameNumberWithLetters(fid.getNumber()));
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());
p.drawText(nameRect, Qt::AlignRight, QString::fromStdString(frameNumber));
}
}
// draw level name
if (!sameLevel ||
(isAfterMarkers &&
Preferences::instance()->isLevelNameOnEachMarkerEnabled())) {
std::wstring levelName = cell.m_level->getName();
QString text = QString::fromStdWString(levelName);
#if QT_VERSION >= 0x050500
QFontMetrics fm(font);
QString elidaName = elideText(text, fm, nameRect.width(), QString("~"));
#else
QString elidaName = elideText(text, font, nameRect.width());
#endif
p.drawText(nameRect, Qt::AlignLeft, elidaName);
}
}
//-----------------------------------------------------------------------------
void CellArea::drawSoundTextCell(QPainter &p, int row, int col) {
TXsheet *xsh = m_viewer->getXsheet();
TXshCell cell = xsh->getCell(row, col);
TXshCell prevCell;
if (row > 0) prevCell = xsh->getCell(row - 1, col);
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());
p.drawLine(rect.topLeft(), rect.bottomRight());
p.drawLine(rect.topRight(), rect.bottomLeft());
return;
}
int levelType;
QColor cellColor, sideColor;
m_viewer->getCellTypeAndColors(levelType, cellColor, sideColor, 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));
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;
}
QString text = cell.getSoundTextLevel()->getFrameText(fid.getNumber() - 1);
p.setPen(Qt::black);
QRect nameRect = rect.adjusted(8, -H_ADJUST, -10, H_ADJUST);
// il nome va scritto se e' diverso dalla cella precedente oppure se
// siamo su una marker line
static QFont font("Helvetica", XSHEET_FONT_SIZE, QFont::Normal);
p.setFont(font);
#if QT_VERSION >= 0x050500
QFontMetrics metric(font);
QString elidaName = elideText(text, metric, nameRect.width(), "~");
#else
QString elidaName = elideText(text, font, nameRect.width(), "~");
#endif
if (!sameLevel || prevCell.m_frameId != fid)
p.drawText(nameRect, Qt::AlignLeft, elidaName);
}
//-----------------------------------------------------------------------------
void CellArea::drawPaletteCell(QPainter &p, int row, int col,
bool isReference) {
TXsheet *xsh = m_viewer->getXsheet();
TXshCell cell = xsh->getCell(row, col);
TXshCell prevCell;
TCellSelection *cellSelection = m_viewer->getCellSelection();
bool isSelected = cellSelection->isCellSelected(row, col);
if (row > 0) prevCell = xsh->getCell(row - 1, 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
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;
}
QColor cellColor, sideColor;
if (isReference) {
cellColor = (isSelected) ? m_viewer->getSelectedReferenceColumnColor()
: m_viewer->getReferenceColumnColor();
sideColor = m_viewer->getReferenceColumnBorderColor();
} else {
cellColor = (isSelected) ? m_viewer->getSelectedPaletteColumnColor()
: m_viewer->getPaletteColumnColor();
sideColor = m_viewer->getPaletteColumnBorderColor();
}
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));
}
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;
if (isAfterMarkers) {
p.setPen(m_viewer->getMarkerLineColor());
p.drawLine(x, y, x + 6, y);
}
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
QPen oldPen = p.pen();
p.setPen(QPen(m_viewer->getTextColor(), 1));
int x = rect.center().x();
p.drawLine(x, rect.top(), x, rect.bottom());
p.setPen(oldPen);
} else {
TFrameId fid = cell.m_frameId;
std::wstring levelName = cell.m_level->getName();
std::string frameNumber("");
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);
bool isRed = false;
TXshPaletteLevel *pl = cell.getPaletteLevel();
if (pl && !pl->getPalette()) isRed = true;
p.setPen(isRed ? m_viewer->getSelectedColumnTextColor()
: m_viewer->getTextColor());
// il nome va scritto se e' diverso dalla cella precedente oppure se
// siamo su una marker line
#ifndef _WIN32
static QFont font("Arial", XSHEET_FONT_SIZE, QFont::Normal);
#else
static QFont font("Helvetica", XSHEET_FONT_SIZE, QFont::Normal);
#endif
p.setFont(font);
QFontMetrics fm(font);
// il numero va scritto se e' diverso dal precedente oppure se il livello
// e' diverso dal precedente
QString numberStr;
if (!sameLevel || prevCell.m_frameId != cell.m_frameId) {
numberStr = QString::fromStdString(frameNumber);
p.drawText(nameRect, Qt::AlignRight, numberStr);
}
QString text = QString::fromStdWString(levelName);
#if QT_VERSION >= 0x050500
QString elidaName = elideText(
text, fm, nameRect.width() - fm.width(numberStr) - 2, QString("~"));
#else
QString elidaName = elideText(
text, font, nameRect.width() - fm.width(numberStr) - 2, QString("~"));
#endif
if (!sameLevel || isAfterMarkers)
p.drawText(nameRect, Qt::AlignLeft, elidaName);
}
}
//-----------------------------------------------------------------------------
void CellArea::drawKeyframe(QPainter &p, const QRect 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());
static QPixmap selectedKey = QPixmap(":Resources/selected_key.bmp");
static QPixmap key = QPixmap(":Resources/key.bmp");
int keyPixOffset = (RowHeight - key.height()) / 2;
TXsheet *xsh = m_viewer->getXsheet();
ColumnFan *columnFan = xsh->getColumnFan();
int col;
for (col = c0; col <= c1; col++) {
if (!columnFan->isActive(col)) continue;
int x = m_viewer->columnToX(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);
row1 = std::min(row1, r1);
/*- first, draw key segments -*/
p.setPen(m_viewer->getTextColor());
int x_line = x + ColumnWidth - 8;
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());
}
}
// jump to next segment
row = hr1 - 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);
}
}
/*- then draw keyframe pixmaps -*/
int x1 = x + ColumnWidth - 13;
for (row = row0; row <= row1; row++) {
int y = m_viewer->rowToY(row) + 1;
p.setPen(m_viewer->getTextColor());
if (pegbar->isKeyframe(row)) {
if (m_viewer->getKeyframeSelection() &&
m_viewer->getKeyframeSelection()->isSelected(row, col)) {
// keyframe selezionato
p.drawPixmap(x1, y + keyPixOffset, selectedKey);
} else {
// keyframe non selezionato
p.drawPixmap(x1, y + keyPixOffset, key);
}
}
}
int y1 = m_viewer->rowToY(row1 + 1);
if (!emptyKeyframeRange && row0 <= row1 + 1) {
// c'e' piu' di un keyframe
// disegno il bottone per il ciclo
p.setBrush(Qt::white);
p.setPen(Qt::black);
p.drawRect(x1, y1, 10, 10);
p.setBrush(Qt::NoBrush);
// disegno il bordo in basso (arrotondato)
p.drawLine(x1 + 1, y1 + 10, x1 + 9, y1 + 10);
p.setPen(Qt::white);
p.drawLine(x1 + 3, y1 + 10, x1 + 7, y1 + 10);
p.setPen(Qt::black);
p.drawLine(x1 + 3, y1 + 11, x1 + 7, y1 + 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);
}
if (pegbar->isCycleEnabled()) {
// la riga a zigzag sotto il bottone
int ymax = m_viewer->rowToY(r1 + 1);
int qy = y1 + 12;
int zig = 2;
int qx = x1 + 5;
p.setPen(m_viewer->getTextColor());
p.drawLine(qx, qy, qx - zig, qy + 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);
qy += 4 * zig;
}
}
}
}
//-----------------------------------------------------------------------------
void CellArea::drawNotes(QPainter &p, const QRect 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());
TXsheet *xsh = m_viewer->getXsheet();
if (!xsh) return;
TXshNoteSet *notes = xsh->getNotes();
int notesCount = notes->getCount();
int i;
for (i = 0; i < notesCount; i++) {
QList<NoteWidget *> noteWidgets = m_viewer->getNotesWidget();
int widgetCount = noteWidgets.size();
NoteWidget *noteWidget = 0;
if (i < widgetCount)
noteWidget = noteWidgets.at(i);
else {
noteWidget = new NoteWidget(m_viewer, i);
m_viewer->addNoteWidget(noteWidget);
}
if (!noteWidget) continue;
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));
noteWidget->paint(&p, QPoint(pos.x, pos.y),
i == m_viewer->getCurrentNoteIndex());
}
}
//-----------------------------------------------------------------------------
bool CellArea::getEaseHandles(int r0, int r1, double e0, double e1, int &rh0,
int &rh1) {
if (r1 <= r0 + 4) {
rh0 = r0;
rh1 = r1;
return false;
}
if (e0 < 0 || e1 < 0) {
rh0 = r0;
rh1 = r1;
return false;
}
if (e0 <= 0 && e1 <= 0) {
rh0 = r0 + 1;
rh1 = r1 - 1;
} else if (e0 <= 0) {
rh0 = r0 + 1;
int a = rh0 + 1;
int b = r1 - 2;
assert(a <= b);
rh1 = tcrop((int)(r1 - e1 + 0.5), a, b);
} else if (e1 <= 0) {
rh1 = r1 - 1;
int b = rh1 - 1;
int a = r0 + 2;
assert(a <= b);
rh0 = tcrop((int)(r0 + e0 + 0.5), a, b);
} else {
int m = tfloor(0.5 * (r0 + e0 + r1 - e1));
m = tcrop(m, r0 + 2, r1 - 2);
int a = r0 + 2;
int b = std::min(m, r1 - 3);
assert(a <= b);
rh0 = tcrop((int)(r0 + e0 + 0.5), a, b);
a = rh0 + 1;
b = r1 - 2;
assert(a <= b);
rh1 = tcrop((int)(r1 - e1 + 0.5), a, b);
}
return true;
}
//-----------------------------------------------------------------------------
void CellArea::paintEvent(QPaintEvent *event) {
QRect toBeUpdated = event->rect();
QPainter p(this);
p.setClipRect(toBeUpdated);
p.fillRect(toBeUpdated, QBrush(m_viewer->getEmptyCellColor()));
drawCells(p, toBeUpdated);
if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled())
drawKeyframe(p, toBeUpdated);
drawNotes(p, toBeUpdated);
if (getDragTool()) getDragTool()->drawCellsArea(p);
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 - 3, RowHeight - 2);
p.setPen(Qt::black);
p.setBrush(Qt::NoBrush);
p.drawRect(rect);
}
//-----------------------------------------------------------------------------
class CycleUndo : public TUndo {
TStageObject *m_pegbar;
CellArea *m_area;
public:
// indices sono le colonne inserite
CycleUndo(TStageObject *pegbar, CellArea *area)
: m_pegbar(pegbar), m_area(area) {}
void undo() const {
m_pegbar->enableCycle(!m_pegbar->isCycleEnabled());
m_area->update();
}
void redo() const { undo(); }
int getSize() const { return sizeof *this; }
QString getHistoryString() {
return QObject::tr("Toggle cycle of %1")
.arg(QString::fromStdString(m_pegbar->getName()));
}
int getHistoryType() { return HistoryType::Xsheet; }
};
//----------------------------------------------------------
void CellArea::mousePressEvent(QMouseEvent *event) {
m_viewer->setQtModifiers(event->modifiers());
assert(!m_isPanning);
m_isMousePressed = true;
if (event->button() == Qt::LeftButton) {
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;
// Verifico se ho cliccato su una nota
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));
QRect rect(pos.x, pos.y, NoteWidth, NoteHeight);
if (!rect.contains(event->pos())) continue;
setDragTool(XsheetGUI::DragTool::makeNoteMoveTool(m_viewer));
m_viewer->setCurrentNoteIndex(i);
m_viewer->dragToolClick(event);
event->accept();
update();
return;
}
// Se non ho cliccato su una nota e c'e' una nota selezionata la
// deseleziono.
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
bool isSoundColumn = false;
if (column) {
TXshSoundColumn *soundColumn = column->getSoundColumn();
isSoundColumn = (!soundColumn) ? false : true;
}
// TObjectHandle *oh = TApp::instance()->getCurrentObject();
// oh->setObjectId(m_viewer->getObjectId(col));
// gmt, 28/12/2009. Non dovrebbe essere necessario, visto che dopo
// verra cambiata la colonna e quindi l'oggetto corrente
// Inoltre, facendolo qui, c'e' un problema con il calcolo del dpi
// (cfr. SceneViewer::onLevelChanged()): setObjectId() chiama (in cascata)
// onLevelChanged(), ma la colonna corrente risulta ancora quella di prima e
// quindi
// il dpi viene calcolato male. Quando si cambia la colonna l'oggetto
// corrente risulta
// gia' aggiornato e quindi non ci sono altre chiamate a onLevelChanged()
// cfr bug#5235
TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col));
if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) {
int k0, k1;
bool isKeyFrameArea =
(pegbar && pegbar->getKeyframeRange(k0, k1) &&
(k1 > k0 || k0 == row) && k0 <= row && row <= k1 + 1 &&
ColumnWidth - 13 <= x && x <= ColumnWidth)
? true
: false;
if (isKeyFrameArea) { // They are in the keyframe selection
if (pegbar->isKeyframe(row)) // in the keyframe
{
m_viewer->setCurrentRow(
row); // If you click on the key, change the current row as well
setDragTool(XsheetGUI::DragTool::makeKeyframeMoverTool(m_viewer));
} 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
setDragTool(XsheetGUI::DragTool::makeKeyFrameHandleMoverTool(
m_viewer, true, r0));
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));
}
}
m_viewer->dragToolClick(event);
event->accept();
update();
return;
}
}
if ((!xsh->getCell(row, col).isEmpty()) && x < 6) {
TXshColumn *column = xsh->getColumn(col);
if (column && !m_viewer->getCellSelection()->isCellSelected(row, col)) {
int r0, r1;
column->getLevelRange(row, r0, r1);
if (event->modifiers() & Qt::ControlModifier) {
m_viewer->getCellKeyframeSelection()->makeCurrent();
m_viewer->getCellKeyframeSelection()->selectCellsKeyframes(r0, col,
r1, col);
} else {
m_viewer->getKeyframeSelection()->selectNone();
m_viewer->getCellSelection()->makeCurrent();
m_viewer->getCellSelection()->selectCells(r0, col, r1, col);
}
TApp::instance()->getCurrentSelection()->notifySelectionChanged();
}
TSelection *selection =
TApp::instance()->getCurrentSelection()->getSelection();
if (TCellKeyframeSelection *cellKeyframeSelection =
dynamic_cast<TCellKeyframeSelection *>(selection))
setDragTool(XsheetGUI::DragTool::makeCellKeyframeMoverTool(m_viewer));
else
setDragTool(XsheetGUI::DragTool::makeLevelMoverTool(m_viewer));
} else {
m_viewer->getKeyframeSelection()->selectNone();
if (isSoundColumn && x > ColumnWidth - 6 && x < ColumnWidth)
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));
else
setDragTool(XsheetGUI::DragTool::makeSelectionTool(m_viewer));
}
m_viewer->dragToolClick(event);
} else if (event->button() == Qt::MidButton) {
m_pos = event->pos();
m_isPanning = true;
}
event->accept();
update();
}
//-----------------------------------------------------------------------------
void CellArea::mouseMoveEvent(QMouseEvent *event) {
m_viewer->setQtModifiers(event->modifiers());
setCursor(Qt::ArrowCursor);
QPoint pos = event->pos();
QRect visibleRect = visibleRegion().boundingRect();
if (m_isPanning) {
// Pan tasto centrale
m_viewer->scroll(m_pos - pos);
return;
}
if ((event->buttons() & Qt::LeftButton) != 0 &&
!visibleRegion().contains(pos)) {
QRect bounds = visibleRegion().boundingRect();
m_viewer->setAutoPanSpeed(bounds, pos);
} else
m_viewer->stopAutoPan();
m_pos = pos;
if (getDragTool()) {
getDragTool()->onDrag(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;
TXsheet *xsh = m_viewer->getXsheet();
// Verifico se e' una colonna sound
TXshColumn *column = xsh->getColumn(col);
bool isSoundColumn = false;
bool isZeraryColumn = false;
if (column) {
TXshSoundColumn *soundColumn = column->getSoundColumn();
isSoundColumn = (!soundColumn) ? false : true;
TXshZeraryFxColumn *zeraryColumn = column->getZeraryFxColumn();
isZeraryColumn = (!zeraryColumn) ? false : true;
}
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) {
if (pegbar->isKeyframe(row)) // key frame
m_tooltip = tr("Click to select keyframe, drag to move it");
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)) { // triangles in the segment betweeen key frames
if (rh0 == row)
m_tooltip = tr("Click and drag to set the acceleration range");
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)
m_tooltip = tr("Click and drag to move the selection");
else if (isZeraryColumn)
m_tooltip = QString::fromStdWString(column->getZeraryFxColumn()
->getZeraryColumnFx()
->getZeraryFx()
->getName());
else if ((!xsh->getCell(row, col).isEmpty() && !isSoundColumn) && x > 6 &&
x < ColumnWidth) {
TXshCell cell = xsh->getCell(row, col);
TFrameId fid = cell.getFrameId();
std::wstring levelName = cell.m_level->getName();
// convert the last one digit of the frame number to alphabet
// Ex. 12 -> 1B 21 -> 2A 30 -> 3
if (Preferences::instance()->isShowFrameNumberWithLettersEnabled()) {
m_tooltip =
(fid.isEmptyFrame() || fid.isNoFrame())
? QString::fromStdWString(levelName)
: QString::fromStdWString(levelName) + QString(" ") +
m_viewer->getFrameNumberWithLetters(fid.getNumber());
} else {
std::string frameNumber("");
if (fid.getNumber() > 0) frameNumber = std::to_string(fid.getNumber());
if (fid.getLetter() != 0) frameNumber.append(1, fid.getLetter());
m_tooltip =
QString((frameNumber.empty())
? QString::fromStdWString(levelName)
: QString::fromStdWString(levelName) + QString(" ") +
QString::fromStdString(frameNumber));
}
} else if (isSoundColumn && x > ColumnWidth - 6 && x < ColumnWidth)
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("");
} else
m_tooltip = tr("");
}
//-----------------------------------------------------------------------------
void CellArea::mouseReleaseEvent(QMouseEvent *event) {
m_viewer->setQtModifiers(0);
m_isMousePressed = false;
m_viewer->stopAutoPan();
m_isPanning = false;
m_viewer->dragToolRelease(event);
}
//-----------------------------------------------------------------------------
void CellArea::mouseDoubleClickEvent(QMouseEvent *event) {
TPoint pos(event->pos().x(), event->pos().y());
int row = m_viewer->yToRow(pos.y);
int col = m_viewer->xToColumn(pos.x);
// Se la colonna e' sound non devo fare nulla
TXshColumn *column = m_viewer->getXsheet()->getColumn(col);
if (column && (column->getSoundColumn() || column->getSoundTextColumn()))
return;
// Se ho cliccato su una nota devo aprire il popup
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));
QRect rect(pos.x, pos.y, NoteWidth, NoteHeight);
if (!rect.contains(event->pos())) continue;
m_viewer->setCurrentNoteIndex(i);
m_viewer->getNotesWidget().at(i)->openNotePopup();
return;
}
TObjectHandle *oh = TApp::instance()->getCurrentObject();
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;
// If you are in the keyframe area, open a function editor
if (isKeyFrameArea) {
QAction *action =
CommandManager::instance()->getAction(MI_OpenFunctionEditor);
action->trigger();
return;
}
}
if (col == -1) return;
// in modalita' xsheet as animation sheet non deve essere possibile creare
// livelli con doppio click: se la cella e' vuota non bisogna fare nulla
if ((Preferences::instance()->isAnimationSheetEnabled() &&
m_viewer->getXsheet()->getCell(row, col).isEmpty()))
return;
m_renameCell->showInRowCol(row, col);
}
//-----------------------------------------------------------------------------
void CellArea::contextMenuEvent(QContextMenuEvent *event) {
TPoint pos(event->pos().x(), event->pos().y());
int row = m_viewer->yToRow(pos.y);
int col = m_viewer->xToColumn(pos.x);
QMenu menu(this);
// Verifico se ho cliccato su una nota
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));
QRect rect(pos.x, pos.y, NoteWidth, NoteHeight);
if (!rect.contains(event->pos())) continue;
m_viewer->setCurrentNoteIndex(i);
createNoteMenu(menu);
if (!menu.isEmpty()) menu.exec(event->globalPos());
return;
}
TXsheet *xsh = m_viewer->getXsheet();
int x0 = m_viewer->columnToX(col);
int y1 = m_viewer->rowToY(row) - 1;
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()) {
TStageObjectId objectId;
if (col < 0)
objectId = TStageObjectId::CameraId(0);
else { // Set the current column and the current object
objectId = TStageObjectId::ColumnId(col);
m_viewer->setCurrentColumn(col);
}
TApp::instance()->getCurrentObject()->setObjectId(objectId);
m_viewer->setCurrentRow(row);
if (pegbar->isKeyframe(row)) { // clicking on keyframes
TKeyframeSelection *keyframeSelection = m_viewer->getKeyframeSelection();
keyframeSelection->select(row, col);
keyframeSelection->makeCurrent();
createKeyMenu(menu);
} else if (!xsh->getColumn(col) ||
!xsh->getColumn(col)
->isLocked()) // on the line between two keyframes
createKeyLineMenu(menu, row, col);
} else if (col >= 0 && // Non e' la colonna di camera
m_viewer->getCellSelection()->isCellSelected(
row, col) && // La cella e' selezionata
(abs(r1 - r0) > 0 ||
abs(c1 - c0) >
0)) // Il numero di celle selezionate e' maggiore di 1
{ // Sono su una selezione di celle
m_viewer->setCurrentColumn(col);
int e, f;
bool areCellsEmpty = false;
for (e = r0; e <= r1; e++) {
for (f = c0; f <= c1; f++)
if (!xsh->getCell(e, f).isEmpty()) {
areCellsEmpty = true;
break;
}
if (areCellsEmpty) break;
}
createCellMenu(menu, areCellsEmpty);
} else {
if (col >= 0) {
m_viewer->getCellSelection()->makeCurrent();
m_viewer->getCellSelection()->selectCell(row, col);
m_viewer->setCurrentColumn(col);
}
if (!xsh->getCell(row, col).isEmpty())
createCellMenu(menu, true);
else
createCellMenu(menu, false);
}
if (!menu.isEmpty()) menu.exec(event->globalPos());
}
//-----------------------------------------------------------------------------
void CellArea::dragEnterEvent(QDragEnterEvent *e) {
if (acceptResourceOrFolderDrop(e->mimeData()->urls()) ||
e->mimeData()->hasFormat(CastItems::getMimeFormat()) ||
e->mimeData()->hasFormat("application/vnd.toonz.drawings")) {
setDragTool(XsheetGUI::DragTool::makeDragAndDropDataTool(m_viewer));
m_viewer->dragToolClick(e);
e->acceptProposedAction();
}
}
//-----------------------------------------------------------------------------
void CellArea::dragLeaveEvent(QDragLeaveEvent *e) {
if (!getDragTool()) return;
m_viewer->dragToolLeave(e);
}
//-----------------------------------------------------------------------------
void CellArea::dragMoveEvent(QDragMoveEvent *e) {
if (!getDragTool()) return;
m_viewer->dragToolDrag(e);
e->acceptProposedAction();
}
//-----------------------------------------------------------------------------
void CellArea::dropEvent(QDropEvent *e) {
if (!getDragTool()) return;
m_viewer->dragToolRelease(e);
if (e->source() == this) {
e->setDropAction(Qt::MoveAction);
e->accept();
} else
e->acceptProposedAction();
}
//-----------------------------------------------------------------------------
bool CellArea::event(QEvent *event) {
QEvent::Type type = event->type();
if (type == QEvent::ToolTip) {
if (!m_tooltip.isEmpty())
QToolTip::showText(mapToGlobal(m_pos), m_tooltip);
else
QToolTip::hideText();
}
if (type == QEvent::WindowDeactivate && m_isMousePressed) {
QMouseEvent e(QEvent::MouseButtonRelease, m_pos, Qt::LeftButton,
Qt::NoButton, Qt::NoModifier);
mouseReleaseEvent(&e);
}
return QWidget::event(event);
}
//-----------------------------------------------------------------------------
void CellArea::onControlPressed(bool pressed) {
isCtrlPressed = pressed;
update();
}
//-----------------------------------------------------------------------------
void CellArea::createCellMenu(QMenu &menu, bool isCellSelected) {
CommandManager *cmdManager = CommandManager::instance();
bool soundCellsSelected = m_viewer->areSoundCellsSelected();
if (m_viewer->areSoundTextCellsSelected()) return; // Magpies stop here
menu.addSeparator();
if (!soundCellsSelected) {
menu.addAction(cmdManager->getAction(MI_LoadLevel));
menu.addAction(cmdManager->getAction(MI_NewLevel));
menu.addSeparator();
}
if (isCellSelected) {
if (!soundCellsSelected) {
menu.addAction(cmdManager->getAction(MI_LevelSettings));
//- force reframe
QMenu *reframeSubMenu = new QMenu(tr("Reframe"), this);
{
reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe1));
reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe2));
reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe3));
reframeSubMenu->addAction(cmdManager->getAction(MI_Reframe4));
}
menu.addMenu(reframeSubMenu);
QMenu *stepSubMenu = new QMenu(tr("Step"), this);
{
stepSubMenu->addAction(cmdManager->getAction(MI_Step2));
stepSubMenu->addAction(cmdManager->getAction(MI_Step3));
stepSubMenu->addAction(cmdManager->getAction(MI_Step4));
stepSubMenu->addAction(cmdManager->getAction(MI_ResetStep));
stepSubMenu->addAction(cmdManager->getAction(MI_IncreaseStep));
stepSubMenu->addAction(cmdManager->getAction(MI_DecreaseStep));
}
menu.addMenu(stepSubMenu);
QMenu *eachSubMenu = new QMenu(tr("Each"), this);
{
eachSubMenu->addAction(cmdManager->getAction(MI_Each2));
eachSubMenu->addAction(cmdManager->getAction(MI_Each3));
eachSubMenu->addAction(cmdManager->getAction(MI_Each4));
}
menu.addMenu(eachSubMenu);
menu.addSeparator();
menu.addAction(cmdManager->getAction(MI_Reverse));
menu.addAction(cmdManager->getAction(MI_Swing));
menu.addAction(cmdManager->getAction(MI_Random));
menu.addAction(cmdManager->getAction(MI_Dup));
menu.addAction(cmdManager->getAction(MI_Rollup));
menu.addAction(cmdManager->getAction(MI_Rolldown));
menu.addAction(cmdManager->getAction(MI_TimeStretch));
menu.addSeparator();
menu.addAction(cmdManager->getAction(MI_Autorenumber));
}
menu.addAction(cmdManager->getAction(MI_ReplaceLevel));
menu.addAction(cmdManager->getAction(MI_ReplaceParentDirectory));
{
// replace with another level in scene cast
std::vector<TXshLevel *> levels;
TApp::instance()
->getCurrentScene()
->getScene()
->getLevelSet()
->listLevels(levels);
if (!levels.empty()) {
QMenu *replaceMenu = menu.addMenu(tr("Replace"));
connect(replaceMenu, SIGNAL(triggered(QAction *)), this,
SLOT(onReplaceByCastedLevel(QAction *)));
for (int i = 0; i < (int)levels.size(); i++) {
if (!levels[i]->getSimpleLevel() && !levels[i]->getChildLevel())
continue;
if (levels[i]->getChildLevel() &&
!TApp::instance()->getCurrentXsheet()->getXsheet()->isLevelUsed(
levels[i]))
continue;
QString tmpLevelName = QString::fromStdWString(levels[i]->getName());
QAction *tmpAction = new QAction(tmpLevelName, replaceMenu);
tmpAction->setData(tmpLevelName);
replaceMenu->addAction(tmpAction);
}
}
}
if (!soundCellsSelected) {
if (selectionContainTlvImage(m_viewer->getCellSelection(),
m_viewer->getXsheet()))
menu.addAction(cmdManager->getAction(MI_RevertToCleanedUp));
if (selectionContainLevelImage(m_viewer->getCellSelection(),
m_viewer->getXsheet()))
menu.addAction(cmdManager->getAction(MI_RevertToLastSaved));
menu.addAction(cmdManager->getAction(MI_SetKeyframes));
}
menu.addSeparator();
TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
if (sl || soundCellsSelected)
menu.addAction(cmdManager->getAction(MI_FileInfo));
if (sl && (sl->getType() & LEVELCOLUMN_XSHLEVEL))
menu.addAction(cmdManager->getAction(MI_ViewFile));
menu.addSeparator();
if (selectionContainRasterImage(m_viewer->getCellSelection(),
m_viewer->getXsheet())) {
menu.addAction(cmdManager->getAction(MI_AdjustLevels));
menu.addAction(cmdManager->getAction(MI_LinesFade));
menu.addAction(cmdManager->getAction(MI_BrightnessAndContrast));
menu.addAction(cmdManager->getAction(MI_Antialias));
menu.addAction(cmdManager->getAction(MI_CanvasSize));
} else if (selectionContainTlvImage(m_viewer->getCellSelection(),
m_viewer->getXsheet()))
menu.addAction(cmdManager->getAction(MI_CanvasSize));
}
menu.addSeparator();
if (!soundCellsSelected)
menu.addAction(cmdManager->getAction(MI_ImportMagpieFile));
}
//-----------------------------------------------------------------------------
/*! replace level with another level in the cast
*/
void CellArea::onReplaceByCastedLevel(QAction *action) {
std::wstring levelName = action->data().toString().toStdWString();
TXshLevel *level =
TApp::instance()->getCurrentScene()->getScene()->getLevelSet()->getLevel(
levelName);
if (!level) return;
TCellSelection *cellSelection = m_viewer->getCellSelection();
if (cellSelection->isEmpty()) return;
int r0, c0, r1, c1;
cellSelection->getSelectedCells(r0, c0, r1, c1);
bool changed = false;
TUndoManager *um = TUndoManager::manager();
um->beginBlock();
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
for (int c = c0; c <= c1; c++) {
for (int r = r0; r <= r1; r++) {
TXshCell cell = xsh->getCell(r, c);
if (!cell.m_level.getPointer() || cell.m_level.getPointer() == level)
continue;
TXshCell oldCell = cell;
cell.m_level = TXshLevelP(level);
xsh->setCell(r, c, cell);
RenameCellUndo *undo = new RenameCellUndo(r, c, oldCell, cell);
um->add(undo);
changed = true;
}
}
if (changed) TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
um->endBlock();
}
//-----------------------------------------------------------------------------
void CellArea::createKeyMenu(QMenu &menu) {
CommandManager *cmdManager = CommandManager::instance();
menu.addAction(cmdManager->getAction(MI_SelectRowKeyframes));
menu.addAction(cmdManager->getAction(MI_SelectColumnKeyframes));
menu.addAction(cmdManager->getAction(MI_SelectAllKeyframes));
menu.addAction(cmdManager->getAction(MI_SelectAllKeyframesNotBefore));
menu.addAction(cmdManager->getAction(MI_SelectAllKeyframesNotAfter));
menu.addAction(cmdManager->getAction(MI_SelectPreviousKeysInColumn));
menu.addAction(cmdManager->getAction(MI_SelectFollowingKeysInColumn));
menu.addAction(cmdManager->getAction(MI_SelectPreviousKeysInRow));
menu.addAction(cmdManager->getAction(MI_SelectFollowingKeysInRow));
menu.addAction(cmdManager->getAction(MI_InvertKeyframeSelection));
menu.addSeparator();
menu.addAction(cmdManager->getAction(MI_Cut));
menu.addAction(cmdManager->getAction(MI_Copy));
menu.addAction(cmdManager->getAction(MI_Paste));
menu.addAction(cmdManager->getAction(MI_Clear));
menu.addSeparator();
menu.addAction(cmdManager->getAction(MI_OpenFunctionEditor));
}
//-----------------------------------------------------------------------------
void CellArea::createKeyLineMenu(QMenu &menu, int row, int col) {
TStageObject *pegbar =
m_viewer->getXsheet()->getStageObject(m_viewer->getObjectId(col));
CommandManager *cmdManager = CommandManager::instance();
int r0, r1, rh0, rh1;
double e0, e1;
if (pegbar->getKeyframeSpan(row, r0, e0, r1, e1) &&
getEaseHandles(r0, r1, e0, e1, rh0, rh1)) {
menu.addAction(cmdManager->getAction(MI_SetAcceleration));
menu.addAction(cmdManager->getAction(MI_SetDeceleration));
menu.addAction(cmdManager->getAction(MI_SetConstantSpeed));
} else {
// Se le due chiavi non sono linear aggiungo il comando ResetInterpolation
bool isR0FullK = pegbar->isFullKeyframe(r0);
bool isR1FullK = pegbar->isFullKeyframe(r1);
TDoubleKeyframe::Type r0Type =
pegbar->getParam(TStageObject::T_X)->getKeyframeAt(r0).m_type;
TDoubleKeyframe::Type r1Type =
pegbar->getParam(TStageObject::T_X)->getKeyframeAt(r1).m_prevType;
if (isGlobalKeyFrameWithSameTypeDiffFromLinear(pegbar, r0) &&
isGlobalKeyFrameWithSamePrevTypeDiffFromLinear(pegbar, r1))
menu.addAction(cmdManager->getAction(MI_ResetInterpolation));
}
#ifdef LINETEST
menu.addSeparator();
int paramStep = getParamStep(pegbar, r0);
QActionGroup *actionGroup = new QActionGroup(this);
int i;
for (i = 1; i < 4; i++) {
QAction *act = new QAction(QString("Step ") + QString::number(i), this);
if (paramStep == i) act->setEnabled(false);
QList<QVariant> list;
list.append(QVariant(i));
list.append(QVariant(r0));
list.append(QVariant(col));
act->setData(QVariant(list));
actionGroup->addAction(act);
menu.addAction(act);
}
connect(actionGroup, SIGNAL(triggered(QAction *)), this,
SLOT(onStepChanged(QAction *)));
#endif
}
//-----------------------------------------------------------------------------
void CellArea::createNoteMenu(QMenu &menu) {
QAction *openAct = menu.addAction(tr("Open Memo"));
QAction *deleteAct = menu.addAction(tr("Delete Memo"));
bool ret = connect(openAct, SIGNAL(triggered()), this, SLOT(openNote()));
ret =
ret && connect(deleteAct, SIGNAL(triggered()), this, SLOT(deleteNote()));
}
//-----------------------------------------------------------------------------
void CellArea::openNote() {
TXshNoteSet *notes = m_viewer->getXsheet()->getNotes();
int currentIndex = m_viewer->getCurrentNoteIndex();
m_viewer->getNotesWidget().at(currentIndex)->openNotePopup();
}
//-----------------------------------------------------------------------------
void CellArea::deleteNote() {
TXshNoteSet *notes = m_viewer->getXsheet()->getNotes();
int currentIndex = m_viewer->getCurrentNoteIndex();
notes->removeNote(currentIndex);
m_viewer->discardNoteWidget();
}
//-----------------------------------------------------------------------------
void CellArea::onStepChanged(QAction *act) {
#ifdef LINETEST
QList<QVariant> list = act->data().toList();
int step = list.at(0).toInt();
int frame = list.at(1).toInt();
int col = list.at(2).toInt();
// Siamo in LineTest il keyframe è globale quindi basta calcolare l'indice
// del primo parametro!!!!
TUndoManager::manager()->beginBlock();
TStageObject *stageObject =
m_viewer->getXsheet()->getStageObject(m_viewer->getObjectId(col));
TDoubleParam *param = stageObject->getParam(TStageObject::T_Angle);
int keyFrameIndex = param->getClosestKeyframe(frame);
setParamStep(keyFrameIndex, step,
stageObject->getParam(TStageObject::T_Angle));
setParamStep(keyFrameIndex, step, stageObject->getParam(TStageObject::T_X));
setParamStep(keyFrameIndex, step, stageObject->getParam(TStageObject::T_Y));
setParamStep(keyFrameIndex, step, stageObject->getParam(TStageObject::T_Z));
setParamStep(keyFrameIndex, step, stageObject->getParam(TStageObject::T_SO));
setParamStep(keyFrameIndex, step,
stageObject->getParam(TStageObject::T_ScaleX));
setParamStep(keyFrameIndex, step,
stageObject->getParam(TStageObject::T_ScaleY));
setParamStep(keyFrameIndex, step,
stageObject->getParam(TStageObject::T_Scale));
setParamStep(keyFrameIndex, step,
stageObject->getParam(TStageObject::T_Path));
setParamStep(keyFrameIndex, step,
stageObject->getParam(TStageObject::T_ShearX));
setParamStep(keyFrameIndex, step,
stageObject->getParam(TStageObject::T_ShearY));
TUndoManager::manager()->endBlock();
#endif
}
//-----------------------------------------------------------------------------
} // namespace XsheetGUI;