#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"
#include "orientation.h"
// TnzTools includes
#include "tools/cursormanager.h"
#include "tools/toolhandle.h"
// TnzQt includes
#include "toonzqt/gutil.h"
#include "toonzqt/tselectionhandle.h"
#include "historytypes.h"
#include "toonzqt/menubarcommand.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 final : 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 override { setcell(m_oldCell); }
void redo() const override { setcell(m_newCell); }
int getSize() const override { return sizeof *this; }
QString getHistoryString() override {
return QObject::tr("Rename Cell at Column %1 Frame %2")
.arg(QString::number(m_col + 1))
.arg(QString::number(m_row + 1));
}
int getHistoryType() override { 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) {
connect(this, SIGNAL(returnPressed()), SLOT(onReturnPressed()));
setContextMenuPolicy(Qt::PreventContextMenu);
setObjectName("RenameCellField");
setAttribute(Qt::WA_TranslucentBackground, true);
installEventFilter(this);
}
//-----------------------------------------------------------------------------
void RenameCellField::showInRowCol(int row, int col, bool multiColumnSelected) {
const Orientation *o = m_viewer->orientation();
m_viewer->scrollTo(row, col);
m_row = row;
m_col = col;
QString fontName = Preferences::instance()->getInterfaceFont();
if (fontName == "") {
#ifdef _WIN32
fontName = "Arial";
#else
fontName = "Helvetica";
#endif
}
static QFont font(fontName, -1, QFont::Normal);
font.setPixelSize(XSHEET_FONT_PX_SIZE);
setFont(font);
setAlignment(Qt::AlignRight | Qt::AlignBottom);
// Se la cella non e' vuota setto il testo
TXsheet *xsh = m_viewer->getXsheet();
// adjust text position
int padding = 3;
if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) {
TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col));
int r0, r1;
if (pegbar && pegbar->getKeyframeRange(r0, r1)) padding += 9;
}
// make the field semi-transparent
QColor bgColor = m_viewer->getColumnHeadPastelizer();
QString styleSheetStr = QString(
"#RenameCellField { padding-right:%1px; "
"background-color:rgba(%2,%3,%4,75); color:%5;}")
.arg(padding)
.arg(bgColor.red())
.arg(bgColor.green())
.arg(bgColor.blue())
.arg(m_viewer->getTextColor().name());
setStyleSheet(styleSheetStr);
TXshCell cell = xsh->getCell(row, col);
QPoint xy = m_viewer->positionToXY(CellPosition(row, col)) - QPoint(1, 2);
if (!cell.isEmpty()) {
// Do not cover left side of the cell in order to enable grabbing the drag
// handle
if (o->isVerticalTimeline()) {
int dragHandleWidth = o->rect(PredefinedRect::DRAG_HANDLE_CORNER).width();
setFixedSize(o->cellWidth() - dragHandleWidth, o->cellHeight() + 2);
move(xy + QPoint(1 + dragHandleWidth, 1));
} else {
setFixedSize(o->cellWidth(), o->cellHeight() + 2);
move(xy + QPoint(1, 1));
}
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)
: (multiColumnSelected)
? m_viewer->getFrameNumberWithLetters(fid.getNumber())
: 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)
: (multiColumnSelected)
? QString::fromStdString(frameNumber)
: QString::fromStdWString(levelName) + QString(" ") +
QString::fromStdString(frameNumber));
}
selectAll();
}
// clear the field if the empty cell is clicked
else {
setFixedSize(o->cellWidth(), o->cellHeight() + 2);
move(xy + QPoint(1, 1));
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);
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;
}
TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
TApp::instance()->getCurrentSelection()->getSelection());
if (!cellSelection) return;
QList<TXshCell> cells;
if (levelName == L"") {
int r0, c0, r1, c1;
cellSelection->getSelectedCells(r0, c0, r1, c1);
bool changed = false;
// rename cells for each column in the selection
for (int c = c0; c <= c1; c++) {
// if there is no level at the current cell, take the level from the
// previous frames
// (when editing not empty column)
TXshCell cell;
int tmpRow = m_row;
while (1) {
cell = xsheet->getCell(tmpRow, c);
if (!cell.isEmpty() || tmpRow == 0) break;
tmpRow--;
}
TXshLevel *xl = cell.m_level.getPointer();
if (!xl || (xl->getType() == OVL_XSHLEVEL &&
xl->getPath().getFrame() == TFrameId::NO_FRAME)) {
cells.append(TXshCell());
continue;
}
// if the next upper cell is empty, then make this cell empty too
if (fid == TFrameId::NO_FRAME)
fid = (m_row - tmpRow <= 1) ? cell.m_frameId : TFrameId(0);
cells.append(TXshCell(xl, fid));
changed = true;
}
if (!changed) return;
} else {
ToonzScene *scene = m_viewer->getXsheet()->getScene();
TLevelSet *levelSet = scene->getLevelSet();
TXshLevel *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;
cells.append(TXshCell(xl, fid));
}
if (fid.getNumber() == 0) {
TCellSelection::Range range = cellSelection->getSelectedCells();
cellSelection->deleteCells();
// revert cell selection
cellSelection->selectCells(range.m_r0, range.m_c0, range.m_r1, range.m_c1);
} else if (cells.size() == 1)
cellSelection->renameCells(cells[0]);
else
cellSelection->renameMultiCells(cells);
}
//-----------------------------------------------------------------------------
void RenameCellField::onReturnPressed() {
renameCell();
// move the cell selection
TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
TApp::instance()->getCurrentSelection()->getSelection());
if (!cellSelection) return;
TCellSelection::Range range = cellSelection->getSelectedCells();
int offset = range.m_r1 - range.m_r0 + 1;
cellSelection->selectCells(range.m_r0 + offset, range.m_c0,
range.m_r1 + offset, range.m_c1);
showInRowCol(m_row + offset, m_col, range.getColCount() > 1);
m_viewer->updateCells();
TApp::instance()->getCurrentSelection()->notifySelectionChanged();
}
//-----------------------------------------------------------------------------
void RenameCellField::focusOutEvent(QFocusEvent *e) {
hide();
QLineEdit::focusOutEvent(e);
}
//-----------------------------------------------------------------------------
// Override shortcut keys for cell selection commands
bool RenameCellField::eventFilter(QObject *obj, QEvent *e) {
if (e->type() != QEvent::ShortcutOverride)
return QLineEdit::eventFilter(obj, e); // return false;
TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
TApp::instance()->getCurrentSelection()->getSelection());
if (!cellSelection) return QLineEdit::eventFilter(obj, e);
QKeyEvent *ke = (QKeyEvent *)e;
std::string keyStr =
QKeySequence(ke->key() + ke->modifiers()).toString().toStdString();
QAction *action = CommandManager::instance()->getActionFromShortcut(keyStr);
if (!action) return QLineEdit::eventFilter(obj, e);
std::string actionId = CommandManager::instance()->getIdFromAction(action);
// These are usally standard ctrl/command strokes for text editing.
// Default to standard behavior and don't execute OT's action while renaming
// cell if users prefer to do so.
// Or, always invoke OT's commands when renaming cell even the standard
// command strokes for text editing.
// The latter option is demanded by Japanese animation industry in order to
// gain efficiency for inputting xsheet.
if (!Preferences::instance()->isShortcutCommandsWhileRenamingCellEnabled() &&
(actionId == "MI_Undo" || actionId == "MI_Redo" ||
actionId == "MI_Clear" || actionId == "MI_Copy" ||
actionId == "MI_Paste" || actionId == "MI_Cut"))
return QLineEdit::eventFilter(obj, e);
return TCellSelection::isEnabledCommand(actionId);
}
//-----------------------------------------------------------------------------
void RenameCellField::keyPressEvent(QKeyEvent *event) {
if (event->key() == Qt::Key_Escape) {
clearFocus();
return;
}
// move the cell selection
TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
TApp::instance()->getCurrentSelection()->getSelection());
if (!cellSelection) {
QLineEdit::keyPressEvent(event);
return;
}
int r0, c0, r1, c1;
CellPosition stride(1, 1); // stride in row and column axes
cellSelection->getSelectedCells(r0, c0, r1, c1);
stride.setFrame(cellSelection->getSelectedCells().getRowCount());
CellPosition offset;
switch (int key = event->key()) {
case Qt::Key_Up:
case Qt::Key_Down:
offset = m_viewer->orientation()->arrowShift(key);
break;
case Qt::Key_Left:
case Qt::Key_Right:
// ctrl+left/right arrow for moving cursor to the end in the field
if (isCtrlPressed &&
!Preferences::instance()->isUseArrowKeyToShiftCellSelectionEnabled()) {
QLineEdit::keyPressEvent(event);
return;
}
offset = m_viewer->orientation()->arrowShift(key);
break;
default:
QLineEdit::keyPressEvent(event);
return;
break;
}
if (isCtrlPressed &&
Preferences::instance()->isUseArrowKeyToShiftCellSelectionEnabled()) {
if (r0 == r1 && offset.frame() == -1) return;
if (c0 == c1 && offset.layer() == -1) return;
cellSelection->selectCells(r0, c0, r1 + offset.frame(),
c1 + offset.layer());
} else {
CellPosition offset(offset * stride);
int movedR0 = std::max(0, r0 + offset.frame());
int movedC0 = std::max(0, c0 + offset.layer());
int diffFrame = movedR0 - r0;
int diffLayer = movedC0 - c0;
// It needs to be discussed - I made not to rename cell with arrow key.
// 19/Jan/2017 shun-iwasawa
// renameCell();
cellSelection->selectCells(r0 + diffFrame, c0 + diffLayer, r1 + diffFrame,
c1 + diffLayer);
showInRowCol(m_row + offset.frame(), m_col + offset.layer(), c1 - c0 > 0);
}
m_viewer->updateCells();
TApp::instance()->getCurrentSelection()->notifySelectionChanged();
}
//-----------------------------------------------------------------------------
void RenameCellField::showEvent(QShowEvent *) {
bool ret = connect(TApp::instance()->getCurrentXsheet(),
SIGNAL(xsheetChanged()), this, SLOT(onXsheetChanged()));
assert(ret);
}
//-----------------------------------------------------------------------------
void RenameCellField::hideEvent(QHideEvent *) {
disconnect(TApp::instance()->getCurrentXsheet(), SIGNAL(xsheetChanged()),
this, SLOT(onXsheetChanged()));
}
//-----------------------------------------------------------------------------
void RenameCellField::onXsheetChanged() {
TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(
TApp::instance()->getCurrentSelection()->getSelection());
if (!cellSelection) {
hide();
return;
}
TCellSelection::Range range = cellSelection->getSelectedCells();
showInRowCol(m_row, m_col, range.getColCount() > 1);
}
//=============================================================================
// 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(m_viewer->orientation());
// selected cells range
TCellSelection *cellSelection = m_viewer->getCellSelection();
int rS0, cS0, rS1, cS1;
if (!cellSelection->isEmpty())
cellSelection->getSelectedCells(rS0, cS0, rS1, cS1);
// visible cells range
CellRange visible = m_viewer->xyRectToRange(toBeUpdated);
int r0, r1, c0, c1; // range of visible rows and columns
r0 = visible.from().frame();
r1 = visible.to().frame();
c0 = visible.from().layer();
c1 = visible.to().layer();
drawNonEmptyBackground(p);
drawSelectionBackground(p);
// marker interval every 6 frames
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 drawLeft = std::max(1, toBeUpdated.left());
int drawRight = std::min(width(), toBeUpdated.right());
int drawTop = std::max(1, toBeUpdated.top());
int drawBottom = std::min(height() - 2, toBeUpdated.bottom());
QRect drawingArea(QPoint(drawLeft, drawTop), QPoint(drawRight, drawBottom));
NumberRange frameSide = m_viewer->orientation()->frameSide(drawingArea);
// for each layer
m_soundLevelModifyRects.clear();
for (col = c0; col <= c1; col++) {
// x in vertical timeline / y in horizontal
int layerAxis = m_viewer->columnToLayerAxis(col);
if (!columnFan->isActive(col)) { // folded column
drawFoldedColumns(p, layerAxis, frameSide);
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled()) {
QPoint xy = m_viewer->positionToXY(CellPosition(row, col));
int x = xy.x();
int y = xy.y();
if (row == 0) {
if (m_viewer->orientation()->isVerticalTimeline())
xy.setY(xy.y() + 1);
else
xy.setX(xy.x() + 1);
}
drawCurrentTimeIndicator(p, xy);
}
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;
}
NumberRange layerAxisRange(layerAxis + 1,
m_viewer->columnToLayerAxis(col + 1));
// draw vertical line
if (layerAxis > 0) {
p.setPen(m_viewer->getVerticalLineColor());
QLine verticalLine =
m_viewer->orientation()->verticalLine(layerAxis, frameSide);
p.drawLine(verticalLine);
}
// for each frame
for (row = r0; row <= r1; row++) {
// draw horizontal lines
// hide top-most marker line
QColor color = ((row - offset) % distance == 0 && row != 0)
? m_viewer->getMarkerLineColor()
: m_viewer->getLightLineColor();
p.setPen(color);
int frameAxis = m_viewer->rowToFrameAxis(row);
QLine horizontalLine =
m_viewer->orientation()->horizontalLine(frameAxis, layerAxisRange);
p.drawLine(horizontalLine);
if (!isColumn) {
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled()) {
QPoint xy = m_viewer->positionToXY(CellPosition(row, col));
int x = xy.x();
int y = xy.y();
if (row == 0) {
if (m_viewer->orientation()->isVerticalTimeline())
xy.setY(xy.y() + 1);
else
xy.setX(xy.x() + 1);
}
drawCurrentTimeIndicator(p, xy);
}
continue;
}
// Cells appearance depending on the type of column
if (isSoundColumn)
drawSoundCell(p, row, col, isReference);
else if (isPaletteColumn)
drawPaletteCell(p, row, col, isReference);
else if (isSoundTextColumn)
drawSoundTextCell(p, row, col);
else
drawLevelCell(p, row, col, isReference);
}
}
drawExtenderHandles(p);
}
// slightly bright background for non-empty rectangular area
void CellArea::drawNonEmptyBackground(QPainter &p) const {
TXsheet *xsh = m_viewer->getXsheet();
int totalFrames = xsh->getFrameCount();
if (!totalFrames) return;
int lastNonEmptyCol;
for (lastNonEmptyCol = xsh->getColumnCount() - 1; lastNonEmptyCol >= 0;
lastNonEmptyCol--) {
TXshColumn *currentColumn = xsh->getColumn(lastNonEmptyCol);
if (!currentColumn) continue;
if (!currentColumn->isEmpty()) break;
}
QPoint xy =
m_viewer->positionToXY(CellPosition(totalFrames, lastNonEmptyCol + 1));
p.fillRect(1, 0, xy.x(), xy.y(), QBrush(m_viewer->getNotEmptyColumnColor()));
}
void CellArea::drawFoldedColumns(QPainter &p, int layerAxis,
const NumberRange &frameAxis) const {
// 3 white bars
for (int i = 0; i < 3; i++) {
QRect whiteRect =
m_viewer->orientation()->foldedRectangle(layerAxis, frameAxis, i);
p.fillRect(whiteRect, QBrush(Qt::white));
}
// 3 dark lines
p.setPen(m_viewer->getLightLineColor());
for (int i = 0; i < 3; i++) {
QLine darkLine =
m_viewer->orientation()->foldedRectangleLine(layerAxis, frameAxis, i);
p.drawLine(darkLine);
}
}
void CellArea::drawSelectionBackground(QPainter &p) const {
// selected cells range
TCellSelection *cellSelection = m_viewer->getCellSelection();
if (cellSelection->isEmpty()) return;
int selRow0, selCol0, selRow1, selCol1;
cellSelection->getSelectedCells(selRow0, selCol0, selRow1, selCol1);
QRect selectionRect = m_viewer->rangeToXYRect(CellRange(
CellPosition(selRow0, selCol0), CellPosition(selRow1 + 1, selCol1 + 1)));
p.fillRect(selectionRect, QBrush(m_viewer->getSelectedEmptyCellColor()));
}
void CellArea::drawExtenderHandles(QPainter &p) {
const Orientation *o = m_viewer->orientation();
m_levelExtenderRect = QRect();
m_upperLevelExtenderRect = QRect();
// selected cells range
TCellSelection *cellSelection = m_viewer->getCellSelection();
if (cellSelection->isEmpty() || m_viewer->areSoundCellsSelected()) return;
int selRow0, selCol0, selRow1, selCol1;
cellSelection->getSelectedCells(selRow0, selCol0, selRow1, selCol1);
QRect selected = m_viewer->rangeToXYRect(CellRange(
CellPosition(selRow0, selCol0), CellPosition(selRow1 + 1, selCol1 + 1)));
int x0, y0, x1, y1;
x0 = selected.left();
x1 = selected.right();
y0 = selected.top();
y1 = selected.bottom();
// smart tab
QPoint smartTabPosOffset =
Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()
? QPoint(0, 0)
: o->point(PredefinedPoint::KEY_HIDDEN);
int distance, offset;
TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers(
distance, offset);
if (distance == 0) distance = 6;
QPoint xyRadius = o->point(PredefinedPoint::EXTENDER_XY_RADIUS);
// bottom / right extender handle
m_levelExtenderRect =
o->rect(PredefinedRect::END_EXTENDER)
.translated(selected.bottomRight() + smartTabPosOffset);
p.setPen(Qt::black);
p.setBrush(SmartTabColor);
p.drawRoundRect(m_levelExtenderRect, xyRadius.x(), xyRadius.y());
QColor color = ((selRow1 + 1 - offset) % distance != 0)
? m_viewer->getLightLineColor()
: m_viewer->getMarkerLineColor();
p.setPen(color);
QLine extenderLine = o->line(PredefinedLine::EXTENDER_LINE);
extenderLine.setP1(extenderLine.p1() + smartTabPosOffset);
p.drawLine(extenderLine.translated(selected.bottomRight()));
// up / left extender handle
if (isCtrlPressed && selRow0 > 0 && !m_viewer->areCellsSelectedEmpty()) {
QPoint properPoint = o->topRightCorner(selected);
m_upperLevelExtenderRect = o->rect(PredefinedRect::BEGIN_EXTENDER)
.translated(properPoint + smartTabPosOffset);
p.setPen(Qt::black);
p.setBrush(SmartTabColor);
p.drawRoundRect(m_upperLevelExtenderRect, xyRadius.x(), xyRadius.y());
QColor color = ((selRow0 - offset) % distance != 0)
? m_viewer->getLightLineColor()
: m_viewer->getMarkerLineColor();
p.setPen(color);
p.drawLine(extenderLine.translated(properPoint));
}
p.setBrush(Qt::NoBrush);
}
//-----------------------------------------------------------------------------
void CellArea::drawSoundCell(QPainter &p, int row, int col, bool isReference) {
const Orientation *o = m_viewer->orientation();
TXshSoundColumn *soundColumn =
m_viewer->getXsheet()->getColumn(col)->getSoundColumn();
QPoint xy = m_viewer->positionToXY(CellPosition(row, col));
int x = xy.x();
int y = xy.y();
if (row == 0) {
if (o->isVerticalTimeline())
xy.setY(xy.y() + 1);
else
xy.setX(xy.x() + 1);
}
QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y));
QRect rect = cellRect.adjusted(1, 1, 0, 0);
int maxNumFrame = soundColumn->getMaxFrame() + 1;
int startFrame = soundColumn->getFirstRow();
TXshCell cell = soundColumn->getCell(row);
if (soundColumn->isCellEmpty(row) || cell.isEmpty() || row > maxNumFrame ||
row < startFrame) {
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
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);
// get cell colors
QColor cellColor, sideColor;
int levelType;
if (isReference) {
cellColor = (isSelected) ? m_viewer->getSelectedReferenceColumnColor()
: m_viewer->getReferenceColumnColor();
sideColor = m_viewer->getReferenceColumnBorderColor();
} else
m_viewer->getCellTypeAndColors(levelType, cellColor, sideColor, cell,
isSelected);
// cells background
p.fillRect(rect, QBrush(cellColor));
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
drawDragHandle(p, xy, sideColor);
drawEndOfDragHandle(p, isLastRow, xy, cellColor);
drawLockedDottedLine(p, soundColumn->isLocked(), xy, cellColor);
QRect trackRect = o->rect(PredefinedRect::SOUND_TRACK).translated(xy);
QRect previewRect = o->rect(PredefinedRect::PREVIEW_TRACK).translated(xy);
NumberRange trackBounds = o->layerSide(trackRect);
NumberRange previewBounds = o->layerSide(previewRect);
NumberRange trackAndPreview(trackBounds.from(), previewBounds.to());
NumberRange timeBounds = o->frameSide(trackRect);
int offset =
row - cell.getFrameId().getNumber(); // rows since start of the clip
int begin = timeBounds.from(); // time axis
int end = timeBounds.to();
int soundPixel =
begin - m_viewer->rowToFrameAxis(offset); // pixels since start of clip
int trackWidth = trackBounds.length();
int lastMin, lastMax;
DoublePair minmax;
soundLevel->getValueAtPixel(o, soundPixel, minmax);
double pmin = minmax.first;
double pmax = minmax.second;
int center = trackBounds.middle();
lastMin = tcrop((int)pmin, -trackWidth / 2, 0) + center;
lastMax = tcrop((int)pmax, 0, trackWidth / 2 - 1) + center;
bool scrub = m_viewer->isScrubHighlighted(row, col);
int i;
for (i = begin; i <= end; i++) {
soundLevel->getValueAtPixel(o, soundPixel, minmax);
soundPixel++;
int min, max;
pmin = minmax.first;
pmax = minmax.second;
center = trackBounds.middle();
min = tcrop((int)pmin, -trackWidth / 2, 0) + center;
max = tcrop((int)pmax, 0, trackWidth / 2 - 1) + center;
if (scrub && i % 2) {
p.setPen(m_viewer->getSoundColumnHlColor());
QLine stroke = o->horizontalLine(i, previewBounds.adjusted(-1, -1));
p.drawLine(stroke);
} else if (i != begin || !isFirstRow) {
// preview tool on the right side
if (i % 2)
p.setPen(cellColor);
else
p.setPen(m_viewer->getSoundColumnTrackColor());
QLine stroke = o->horizontalLine(i, previewBounds.adjusted(-1, -1));
p.drawLine(stroke);
}
if (i != begin) {
// "audio track" in the middle of the column
p.setPen(m_viewer->getSoundColumnTrackColor());
QLine minLine = o->horizontalLine(i, NumberRange(lastMin, min));
p.drawLine(minLine);
QLine maxLine = o->horizontalLine(i, NumberRange(lastMax, max));
p.drawLine(maxLine);
}
lastMin = min;
lastMax = max;
}
// yellow clipped border
p.setPen(SoundColumnExtenderColor);
int r0WithoutOff, r1WithoutOff;
bool ret =
soundColumn->getLevelRangeWithoutOffset(row, r0WithoutOff, r1WithoutOff);
assert(ret);
if (isFirstRow) {
QRect modifierRect = m_viewer->orientation()
->rect(PredefinedRect::BEGIN_SOUND_EDIT)
.translated(xy);
if (r0 != r0WithoutOff) p.fillRect(modifierRect, SoundColumnExtenderColor);
m_soundLevelModifyRects.append(modifierRect); // list of clipping rects
}
if (isLastRow) {
QRect modifierRect = m_viewer->orientation()
->rect(PredefinedRect::END_SOUND_EDIT)
.translated(xy);
if (r1 != r1WithoutOff) p.fillRect(modifierRect, SoundColumnExtenderColor);
m_soundLevelModifyRects.append(modifierRect);
}
}
//-----------------------------------------------------------------------------
// paint side bar
void CellArea::drawDragHandle(QPainter &p, const QPoint &xy,
const QColor &sideColor) const {
QRect dragHandleRect = m_viewer->orientation()
->rect(PredefinedRect::DRAG_HANDLE_CORNER)
.translated(xy);
p.fillRect(dragHandleRect, QBrush(sideColor));
}
// cut off triangle at the end of drag handle
void CellArea::drawEndOfDragHandle(QPainter &p, bool isEnd, const QPoint &xy,
const QColor &cellColor) const {
if (!isEnd) return;
QPainterPath corner = m_viewer->orientation()
->path(PredefinedPath::DRAG_HANDLE_CORNER)
.translated(xy);
p.fillPath(corner, QBrush(cellColor));
}
// draw dot line if the column is locked
void CellArea::drawLockedDottedLine(QPainter &p, bool isLocked,
const QPoint &xy,
const QColor &cellColor) const {
if (!isLocked) return;
p.setPen(QPen(cellColor, 2, Qt::DotLine));
QLine dottedLine =
m_viewer->orientation()->line(PredefinedLine::LOCKED).translated(xy);
p.drawLine(dottedLine);
}
void CellArea::drawCurrentTimeIndicator(QPainter &p, const QPoint &xy) {
QRect cell =
m_viewer->orientation()->rect(PredefinedRect::CELL).translated(xy);
int cellMid = cell.left() + (cell.width() / 2);
int cellTop = cell.top();
int cellBottom = cell.bottom();
p.setPen(Qt::red);
p.drawLine(cellMid, cellTop, cellMid, cellBottom);
}
//-----------------------------------------------------------------------------
void CellArea::drawLevelCell(QPainter &p, int row, int col, bool isReference) {
const Orientation *o = m_viewer->orientation();
TXsheet *xsh = m_viewer->getXsheet();
TXshCell cell = xsh->getCell(row, col);
TXshCell prevCell;
TCellSelection *cellSelection = m_viewer->getCellSelection();
TColumnSelection *columnSelection = m_viewer->getColumnSelection();
bool isSelected = cellSelection->isCellSelected(row, col) ||
columnSelection->isColumnSelected(col);
if (row > 0) prevCell = xsh->getCell(row - 1, col); // cell in previous frame
QPoint xy = m_viewer->positionToXY(CellPosition(row, col));
int x = xy.x();
int y = xy.y();
if (row == 0) {
if (o->isVerticalTimeline())
xy.setY(xy.y() + 1);
else
xy.setX(xy.x() + 1);
}
// nothing to draw
if (cell.isEmpty() && prevCell.isEmpty()) {
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
return;
}
TXshCell nextCell;
nextCell = xsh->getCell(row + 1, col); // cell in next frame
QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y));
QRect rect = cellRect.adjusted(1, 1, 0, 0);
if (cell.isEmpty()) { // it means previous is not empty
// diagonal cross meaning end of level
QColor levelEndColor = m_viewer->getTextColor();
levelEndColor.setAlphaF(0.3);
p.setPen(levelEndColor);
p.drawLine(rect.topLeft(), rect.bottomRight());
p.drawLine(rect.topRight(), rect.bottomLeft());
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
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));
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
drawDragHandle(p, xy, sideColor);
if (yetToCleanupCell) // ORIENTATION: what's this?
{
if (o->isVerticalTimeline())
p.fillRect(rect.adjusted(rect.width() / 2, 0, 0, 0),
(isSelected) ? m_viewer->getSelectedFullcolorColumnColor()
: m_viewer->getFullcolorColumnColor());
else
p.fillRect(rect.adjusted(0, rect.height() / 2, 0, 0),
(isSelected) ? m_viewer->getSelectedFullcolorColumnColor()
: m_viewer->getFullcolorColumnColor());
}
bool isLastRow = nextCell.isEmpty() ||
cell.m_level.getPointer() != nextCell.m_level.getPointer();
drawEndOfDragHandle(p, isLastRow, xy, cellColor);
drawLockedDottedLine(p, xsh->getColumn(col)->isLocked(), xy, cellColor);
bool sameLevel = prevCell.m_level.getPointer() == cell.m_level.getPointer();
int distance, offset;
TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers(
distance, offset);
bool isAfterMarkers =
(row - offset) % distance == 0 && distance != 0 && row != 0;
// draw marker interval
if (isAfterMarkers) {
p.setPen(m_viewer->getMarkerLineColor());
p.drawLine(o->line(PredefinedLine::SEE_MARKER_THROUGH).translated(xy));
}
QRect nameRect = o->rect(PredefinedRect::CELL_NAME).translated(QPoint(x, y));
if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) {
TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col));
int r0, r1;
if (pegbar && pegbar->getKeyframeRange(r0, r1))
nameRect = o->rect(PredefinedRect::CELL_NAME_WITH_KEYFRAME)
.translated(QPoint(x, y));
}
// 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 ? QColor(230, 100, 100) // m_viewer->getSelectedColumnTextColor()
: m_viewer->getTextColor());
QString fontName = Preferences::instance()->getInterfaceFont();
if (fontName == "") {
#ifdef _WIN32
fontName = "Arial";
#else
fontName = "Helvetica";
#endif
}
static QFont font(fontName, -1, QFont::Normal);
font.setPixelSize(XSHEET_FONT_PX_SIZE);
p.setFont(font);
// do not draw frame number under RenameCellField
if (m_renameCell->isVisible() && m_renameCell->isLocatedAt(row, col)) return;
// if the same level & same fId with the previous cell,
// draw continue line
QString fnum;
if (sameLevel && prevCell.m_frameId == cell.m_frameId) {
// not on line marker
PredefinedLine which =
Preferences::instance()->isLevelNameOnEachMarkerEnabled()
? PredefinedLine::CONTINUE_LEVEL_WITH_NAME
: PredefinedLine::CONTINUE_LEVEL;
QLine continueLine = o->line(which).translated(xy);
p.drawLine(continueLine);
}
// draw frame number
else {
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())
fnum = 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());
fnum = QString::fromStdString(frameNumber);
}
p.drawText(nameRect, Qt::AlignRight | Qt::AlignBottom, fnum);
}
// draw level name
if (!sameLevel ||
(isAfterMarkers &&
Preferences::instance()->isLevelNameOnEachMarkerEnabled())) {
std::wstring levelName = cell.m_level->getName();
QString text = QString::fromStdWString(levelName);
QFontMetrics fm(font);
#if QT_VERSION >= 0x050500
// QFontMetrics fm(font);
QString elidaName =
elideText(text, fm, nameRect.width() - fm.width(fnum), QString("~"));
#else
QString elidaName =
elideText(text, font, nameRect.width() - fm.width(fnum));
#endif
p.drawText(nameRect, Qt::AlignLeft | Qt::AlignBottom, elidaName);
}
}
//-----------------------------------------------------------------------------
void CellArea::drawSoundTextCell(QPainter &p, int row, int col) {
const Orientation *o = m_viewer->orientation();
TXsheet *xsh = m_viewer->getXsheet();
TXshCell cell = xsh->getCell(row, col);
TXshCell prevCell;
TCellSelection *cellSelection = m_viewer->getCellSelection();
TColumnSelection *columnSelection = m_viewer->getColumnSelection();
bool isSelected = cellSelection->isCellSelected(row, col) ||
columnSelection->isColumnSelected(col);
if (row > 0) prevCell = xsh->getCell(row - 1, col); // cell in previous frame
// nothing to draw
QPoint xy = m_viewer->positionToXY(CellPosition(row, col));
int x = xy.x();
int y = xy.y();
if (row == 0) {
if (o->isVerticalTimeline())
xy.setY(xy.y() + 1);
else
xy.setX(xy.x() + 1);
}
if (cell.isEmpty() && prevCell.isEmpty()) {
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
return;
}
TXshCell nextCell;
nextCell = xsh->getCell(row + 1, col);
QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y));
QRect rect = cellRect.adjusted(1, 1, 0, 0);
if (cell.isEmpty()) { // diagonal cross meaning end of level
QColor levelEndColor = m_viewer->getTextColor();
levelEndColor.setAlphaF(0.3);
p.setPen(levelEndColor);
p.drawLine(rect.topLeft(), rect.bottomRight());
p.drawLine(rect.topRight(), rect.bottomLeft());
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
return;
}
int levelType;
QColor cellColor, sideColor;
m_viewer->getCellTypeAndColors(levelType, cellColor, sideColor, cell,
isSelected);
// paint cell
p.fillRect(rect, QBrush(cellColor));
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
drawDragHandle(p, xy, sideColor);
bool isLastRow = nextCell.isEmpty() ||
cell.m_level.getPointer() != nextCell.m_level.getPointer();
drawEndOfDragHandle(p, isLastRow, xy, cellColor);
drawLockedDottedLine(p, xsh->getColumn(col)->isLocked(), xy, cellColor);
bool sameLevel = prevCell.m_level.getPointer() == cell.m_level.getPointer();
int distance, offset;
TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers(
distance, offset);
bool isAfterMarkers =
(row - offset) % distance == 0 && distance != 0 && row != 0;
// draw marker interval
if (isAfterMarkers) {
p.setPen(m_viewer->getMarkerLineColor());
p.drawLine(o->line(PredefinedLine::SEE_MARKER_THROUGH).translated(xy));
}
p.setPen(Qt::black);
QRect nameRect = o->rect(PredefinedRect::CELL_NAME).translated(QPoint(x, y));
// il nome va scritto se e' diverso dalla cella precedente oppure se
// siamo su una marker line
QString fontName = Preferences::instance()->getInterfaceFont();
if (fontName == "") {
#ifdef _WIN32
fontName = "Arial";
#else
fontName = "Helvetica";
#endif
}
static QFont font(fontName, -1, QFont::Normal);
font.setPixelSize(XSHEET_FONT_PX_SIZE);
p.setFont(font);
// if the same level & same fId with the previous cell,
// draw continue line
if (sameLevel && prevCell.m_frameId == cell.m_frameId) {
// not on line marker
PredefinedLine which =
Preferences::instance()->isLevelNameOnEachMarkerEnabled()
? PredefinedLine::CONTINUE_LEVEL_WITH_NAME
: PredefinedLine::CONTINUE_LEVEL;
QLine continueLine = o->line(which).translated(xy);
p.drawLine(continueLine);
}
QString text =
cell.getSoundTextLevel()->getFrameText(cell.m_frameId.getNumber() - 1);
#if QT_VERSION >= 0x050500
QFontMetrics metric(font);
QString elidaName = elideText(text, metric, nameRect.width(), "~");
#else
QString elidaName = elideText(text, font, nameRect.width(), "~");
#endif
if (!sameLevel || prevCell.m_frameId != cell.m_frameId)
p.drawText(nameRect, Qt::AlignLeft | Qt::AlignBottom, elidaName);
}
//-----------------------------------------------------------------------------
void CellArea::drawPaletteCell(QPainter &p, int row, int col,
bool isReference) {
const Orientation *o = m_viewer->orientation();
TXsheet *xsh = m_viewer->getXsheet();
TXshCell cell = xsh->getCell(row, col);
TXshCell prevCell;
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);
QPoint xy = m_viewer->positionToXY(CellPosition(row, col));
int x = xy.x();
int y = xy.y();
if (row == 0) {
if (o->isVerticalTimeline())
xy.setY(xy.y() + 1);
else
xy.setX(xy.x() + 1);
}
if (cell.isEmpty() && prevCell.isEmpty()) {
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
return;
}
QRect cellRect = o->rect(PredefinedRect::CELL).translated(QPoint(x, y));
QRect rect = cellRect.adjusted(1, 1, 0, 0);
if (cell.isEmpty()) { // this means the former is not empty
QColor levelEndColor = m_viewer->getTextColor();
levelEndColor.setAlphaF(0.3);
p.setPen(levelEndColor);
p.drawLine(rect.topLeft(), rect.bottomRight());
p.drawLine(rect.topRight(), rect.bottomLeft());
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
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));
if (TApp::instance()->getCurrentFrame()->isEditingScene() &&
!m_viewer->orientation()->isVerticalTimeline() &&
row == m_viewer->getCurrentRow() &&
Preferences::instance()->isCurrentTimelineIndicatorEnabled())
drawCurrentTimeIndicator(p, xy);
drawDragHandle(p, xy, sideColor);
bool isLastRow = nextCell.isEmpty() ||
cell.m_level.getPointer() != nextCell.m_level.getPointer();
drawEndOfDragHandle(p, isLastRow, xy, cellColor);
drawLockedDottedLine(p, xsh->getColumn(col)->isLocked(), xy, cellColor);
bool sameLevel = prevCell.m_level.getPointer() == cell.m_level.getPointer();
int distance, offset;
TApp::instance()->getCurrentScene()->getScene()->getProperties()->getMarkers(
distance, offset);
if (distance == 0) distance = 6;
bool isAfterMarkers = (row - offset) % distance == 0 && row != 0;
if (isAfterMarkers) {
p.setPen(m_viewer->getMarkerLineColor());
p.drawLine(o->line(PredefinedLine::SEE_MARKER_THROUGH).translated(xy));
}
if (sameLevel && prevCell.m_frameId == cell.m_frameId &&
!isAfterMarkers) { // cell equal to previous one (not on marker line):
// do not write anything and draw a vertical line
QPen oldPen = p.pen();
p.setPen(QPen(m_viewer->getTextColor(), 1));
QLine continueLine = o->line(PredefinedLine::CONTINUE_LEVEL).translated(xy);
p.drawLine(continueLine);
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 =
o->rect(PredefinedRect::CELL_NAME).translated(QPoint(x, y));
if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) {
TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col));
int r0, r1;
if (pegbar && pegbar->getKeyframeRange(r0, r1))
nameRect = o->rect(PredefinedRect::CELL_NAME_WITH_KEYFRAME)
.translated(QPoint(x, y));
}
bool isRed = false;
TXshPaletteLevel *pl = cell.getPaletteLevel();
if (pl && !pl->getPalette()) isRed = true;
p.setPen(
isRed ? QColor(230, 100, 100) // m_viewer->getSelectedColumnTextColor()
: m_viewer->getTextColor());
// il nome va scritto se e' diverso dalla cella precedente oppure se
// siamo su una marker line
QString fontName = Preferences::instance()->getInterfaceFont();
if (fontName == "") {
#ifdef _WIN32
fontName = "Arial";
#else
fontName = "Helvetica";
#endif
}
static QFont font(fontName, -1, QFont::Normal);
font.setPixelSize(XSHEET_FONT_PX_SIZE);
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) {
// convert the last one digit of the frame number to alphabet
// Ex. 12 -> 1B 21 -> 2A 30 -> 3
if (Preferences::instance()->isShowFrameNumberWithLettersEnabled()) {
numberStr = m_viewer->getFrameNumberWithLetters(fid.getNumber());
p.drawText(nameRect, Qt::AlignRight | Qt::AlignBottom, numberStr);
} else {
std::string frameNumber("");
// set number
if (fid.getNumber() > 0) frameNumber = std::to_string(fid.getNumber());
// add letter
if (fid.getLetter() != 0) frameNumber.append(1, fid.getLetter());
numberStr = QString::fromStdString(frameNumber);
p.drawText(nameRect, Qt::AlignRight | Qt::AlignBottom, numberStr);
}
}
QString text = QString::fromStdWString(levelName);
#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 | Qt::AlignBottom, elidaName);
}
}
//-----------------------------------------------------------------------------
void CellArea::drawKeyframe(QPainter &p, const QRect toBeUpdated) {
const Orientation *o = m_viewer->orientation();
int r0, r1, c0, c1; // range of visible rows and columns
CellRange visible = m_viewer->xyRectToRange(toBeUpdated);
r0 = visible.from().frame();
r1 = visible.to().frame();
c0 = visible.from().layer();
c1 = visible.to().layer();
static QPixmap selectedKey = svgToPixmap(":Resources/selected_key.svg");
static QPixmap key = svgToPixmap(":Resources/key.svg");
const QRect &keyRect = o->rect(PredefinedRect::KEY_ICON);
TXsheet *xsh = m_viewer->getXsheet();
ColumnFan *columnFan = xsh->getColumnFan(o);
int col;
for (col = c0; col <= c1; col++) {
if (!columnFan->isActive(col)) continue;
int layerAxis = m_viewer->columnToLayerAxis(col);
TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col));
if (!pegbar) return;
int row0, row1;
bool emptyKeyframe = !pegbar->getKeyframeRange(row0, row1);
if (emptyKeyframe) continue;
bool emptyKeyframeRange = row0 >= row1;
int row;
row0 = std::max(row0, r0);
row1 = std::min(row1, r1);
/*- first, draw key segments -*/
p.setPen(m_viewer->getTextColor());
int line_layerAxis = layerAxis + o->layerSide(keyRect).middle();
for (row = row0; row <= row1; row++) {
int segmentRow0, segmentRow1;
double ease0, ease1;
if (pegbar->getKeyframeSpan(row, segmentRow0, ease0, segmentRow1,
ease1)) {
drawKeyframeLine(p, col, NumberRange(segmentRow0, segmentRow1));
if (segmentRow1 - segmentRow0 >
4) { // only show if distance more than 4 frames
int handleRow0, handleRow1;
if (getEaseHandles(segmentRow0, segmentRow1, ease0, ease1, handleRow0,
handleRow1)) {
m_viewer->drawPredefinedPath(p, PredefinedPath::BEGIN_EASE_TRIANGLE,
CellPosition(handleRow0, col),
m_viewer->getLightLineColor(),
m_viewer->getTextColor());
m_viewer->drawPredefinedPath(p, PredefinedPath::END_EASE_TRIANGLE,
CellPosition(handleRow1, col),
m_viewer->getLightLineColor(),
m_viewer->getTextColor());
}
}
// skip to next segment
row = segmentRow1 - 1;
} else if (pegbar->isKeyframe(row) && pegbar->isKeyframe(row + 1)) {
// 2 keyframes in a row - connect with a short line
drawKeyframeLine(p, col, NumberRange(row, row + 1));
}
}
/*- then draw keyframe pixmaps -*/
int icon_layerAxis = line_layerAxis - 5;
for (row = row0; row <= row1; row++) {
p.setPen(m_viewer->getTextColor());
if (pegbar->isKeyframe(row)) {
QPoint target =
keyRect.translated(m_viewer->positionToXY(CellPosition(row, col)))
.topLeft();
if (m_viewer->getKeyframeSelection() &&
m_viewer->getKeyframeSelection()->isSelected(row, col)) {
// keyframe selected
p.drawPixmap(target, selectedKey);
} else {
// keyframe not selected
p.drawPixmap(target, key);
}
}
}
int icon_frameAxis = m_viewer->rowToFrameAxis(row1 + 1);
if (!emptyKeyframeRange && row0 <= row1 + 1) {
// there's just a keyframe
// drawing loop button
p.setBrush(Qt::white);
p.setPen(Qt::black);
QPoint target = o->frameLayerToXY(icon_frameAxis, icon_layerAxis);
p.drawRect(QRect(target, QSize(10, 10)));
p.setBrush(Qt::NoBrush);
// drawing the bottom edge (rounded)
p.drawLine(target + QPoint(1, 10), target + QPoint(9, 10));
p.setPen(Qt::white);
p.drawLine(target + QPoint(3, 10), target + QPoint(7, 10));
p.setPen(Qt::black);
p.drawLine(target + QPoint(3, 11), target + QPoint(7, 11));
// drawing the arrow
p.drawArc(QRect(target + QPoint(2, 3), QSize(6, 6)), 180 * 16, 270 * 16);
p.drawLine(target + QPoint(5, 2), target + QPoint(5, 5));
p.drawLine(target + QPoint(5, 2), target + QPoint(8, 2));
}
if (pegbar->isCycleEnabled()) {
// the row zigzag bellow the button
int ymax = m_viewer->rowToFrameAxis(r1 + 1);
int qy = icon_frameAxis + 12;
int zig = 2;
int qx = icon_layerAxis + 5;
p.setPen(m_viewer->getTextColor());
p.drawLine(o->frameLayerToXY(qy, qx),
o->frameLayerToXY(qy + zig, qx - zig));
while (qy < ymax) {
p.drawLine(o->frameLayerToXY(qy + zig, qx - zig),
o->frameLayerToXY(qy + 3 * zig, qx + zig));
p.drawLine(o->frameLayerToXY(qy + 3 * zig, qx + zig),
o->frameLayerToXY(qy + 5 * zig, qx - zig));
qy += 4 * zig;
}
}
}
}
void CellArea::drawKeyframeLine(QPainter &p, int col,
const NumberRange &rows) const {
const QRect &keyRect =
m_viewer->orientation()->rect(PredefinedRect::KEY_ICON);
QPoint begin =
keyRect.center() + m_viewer->positionToXY(CellPosition(rows.from(), col));
QPoint end =
keyRect.center() + m_viewer->positionToXY(CellPosition(rows.to(), col));
p.setPen(m_viewer->getTextColor());
p.drawLine(QLine(begin, end));
}
//-----------------------------------------------------------------------------
void CellArea::drawNotes(QPainter &p, const QRect toBeUpdated) {
CellRange visible = m_viewer->xyRectToRange(toBeUpdated);
int r0, r1, c0, c1; // range of visible rows and columns
r0 = visible.from().frame();
r1 = visible.to().frame();
c0 = visible.from().layer();
c1 = visible.to().layer();
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;
QPoint xy = m_viewer->positionToXY(CellPosition(r, c));
TPointD pos = notes->getNotePos(i) + TPointD(xy.x(), xy.y());
noteWidget->paint(&p, QPoint(pos.x, pos.y),
i == m_viewer->getCurrentNoteIndex());
}
}
//-----------------------------------------------------------------------------
bool CellArea::getEaseHandles(int r0, int r1, double e0, double e1, int &rh0,
int &rh1) {
if (r1 <= r0 + 4) { // ... what?
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);
// focus cell border
int row = m_viewer->getCurrentRow();
int col = m_viewer->getCurrentColumn();
QPoint xy = m_viewer->positionToXY(CellPosition(row, col));
QRect rect = m_viewer->orientation()
->rect(PredefinedRect::CELL)
.translated(xy)
.adjusted(1, 1, -1, -1);
p.setPen(Qt::black);
p.setBrush(Qt::NoBrush);
p.drawRect(rect);
}
//-----------------------------------------------------------------------------
class CycleUndo final : 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 override {
m_pegbar->enableCycle(!m_pegbar->isCycleEnabled());
m_area->update();
}
void redo() const override { undo(); }
int getSize() const override { return sizeof *this; }
QString getHistoryString() override {
return QObject::tr("Toggle cycle of %1")
.arg(QString::fromStdString(m_pegbar->getName()));
}
int getHistoryType() override { return HistoryType::Xsheet; }
};
//----------------------------------------------------------
void CellArea::mousePressEvent(QMouseEvent *event) {
const Orientation *o = m_viewer->orientation();
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());
CellPosition cellPosition = m_viewer->xyToPosition(event->pos());
int row = cellPosition.frame();
int col = cellPosition.layer();
QPoint cellTopLeft = m_viewer->positionToXY(CellPosition(row, col));
QPoint mouseInCell = event->pos() - cellTopLeft;
int x = mouseInCell.x(); // where in the cell click is
// Check if a note is clicked
TXshNoteSet *notes = m_viewer->getXsheet()->getNotes();
int i;
for (i = notes->getCount() - 1; i >= 0; i--) {
int r = notes->getNoteRow(i);
int c = notes->getNoteCol(i);
QPoint xy = m_viewer->positionToXY(CellPosition(r, c));
TPointD pos = notes->getNotePos(i) + TPointD(xy.x(), xy.y());
QRect rect(pos.x, pos.y, NoteWidth, NoteHeight);
if (!rect.contains(event->pos())) continue;
setDragTool(XsheetGUI::DragTool::makeNoteMoveTool(m_viewer));
m_viewer->setCurrentNoteIndex(i);
m_viewer->dragToolClick(event);
event->accept();
update();
return;
}
// If I have not clicked on a note, and there's a note selected, deselect it
if (m_viewer->getCurrentNoteIndex() >= 0) m_viewer->setCurrentNoteIndex(-1);
TXsheet *xsh = m_viewer->getXsheet();
TXshColumn *column = xsh->getColumn(col);
// Check if it's the sound column
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()) {
// only if key frame area is active
int k0, k1;
bool isKeyframeFrame = pegbar && pegbar->getKeyframeRange(k0, k1) &&
(k1 > k0 || k0 == row) && k0 <= row &&
row <= k1 + 1;
bool isKeyFrameArea =
isKeyframeFrame &&
o->rect(PredefinedRect::KEYFRAME_AREA).contains(mouseInCell) &&
row < k1 + 1;
bool accept = false;
if (isKeyFrameArea) { // They are in the keyframe selection
if (pegbar->isKeyframe(row)) // in the keyframe
{
m_viewer->setCurrentRow(
row); // If you click on the key, change the current row as well
setDragTool(XsheetGUI::DragTool::makeKeyframeMoverTool(m_viewer));
accept = true;
} else {
int r0, r1;
double e0, e1;
int rh0, rh1;
if (pegbar->getKeyframeSpan(row, r0, e0, r1, e1) &&
getEaseHandles(r0, r1, e0, e1, rh0, rh1)) {
if (rh0 == row) { // in a keyframe handle
setDragTool(XsheetGUI::DragTool::makeKeyFrameHandleMoverTool(
m_viewer, true, r0));
accept = true;
} else if (rh1 == row) { // in a keyframe handle
setDragTool(XsheetGUI::DragTool::makeKeyFrameHandleMoverTool(
m_viewer, false, r1));
accept = true;
}
}
}
} else if (isKeyframeFrame && row == k1 + 1 &&
o->rect(PredefinedRect::LOOP_ICON)
.contains(mouseInCell)) { // cycle toggle
pegbar->enableCycle(!pegbar->isCycleEnabled());
TUndoManager::manager()->add(new CycleUndo(pegbar, this));
accept = true;
}
if (accept) {
m_viewer->dragToolClick(event);
event->accept();
update();
return;
}
// keep searching
}
if (m_levelExtenderRect.contains(pos.x, pos.y)) {
m_viewer->getKeyframeSelection()->selectNone();
setDragTool(XsheetGUI::DragTool::makeLevelExtenderTool(m_viewer));
} else if (event->modifiers() & Qt::ControlModifier &&
m_upperLevelExtenderRect.contains(pos.x, pos.y)) {
m_viewer->getKeyframeSelection()->selectNone();
setDragTool(XsheetGUI::DragTool::makeLevelExtenderTool(m_viewer, true));
} else if ((!xsh->getCell(row, col).isEmpty()) &&
o->rect(PredefinedRect::DRAG_AREA).contains(mouseInCell)) {
TXshColumn *column = xsh->getColumn(col);
if (column && !m_viewer->getCellSelection()->isCellSelected(row, col)) {
int r0, r1;
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 &&
o->rect(PredefinedRect::PREVIEW_TRACK).contains(mouseInCell))
setDragTool(XsheetGUI::DragTool::makeSoundScrubTool(
m_viewer, column->getSoundColumn()));
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) {
const Orientation *o = m_viewer->orientation();
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;
}
CellPosition cellPosition = m_viewer->xyToPosition(pos);
int row = cellPosition.frame();
int col = cellPosition.layer();
QPoint cellTopLeft = m_viewer->positionToXY(CellPosition(row, col));
int x = m_pos.x() - cellTopLeft.x();
int y = m_pos.y() - cellTopLeft.y();
QPoint mouseInCell = m_pos - cellTopLeft;
TXsheet *xsh = m_viewer->getXsheet();
// 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;
bool isKeyframeFrame =
Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled() &&
pegbar && pegbar->getKeyframeRange(k0, k1) && k0 <= row && row <= k1 + 1;
bool isKeyFrameArea =
isKeyframeFrame &&
o->rect(PredefinedRect::KEYFRAME_AREA).contains(mouseInCell) &&
row < k1 + 1;
if (isKeyFrameArea) {
if (pegbar->isKeyframe(row)) // key frame
m_tooltip = tr("Click to select keyframe, drag to move it");
else {
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");
}
}
} else if (isKeyframeFrame && row == k1 + 1 &&
o->rect(PredefinedRect::LOOP_ICON)
.contains(mouseInCell)) // cycle toggle of key frames
m_tooltip = tr("Set the cycle of previous keyframes");
else if ((!xsh->getCell(row, col).isEmpty()) &&
o->rect(PredefinedRect::DRAG_AREA).contains(mouseInCell))
m_tooltip = tr("Click and drag to move the selection");
else if (isZeraryColumn)
m_tooltip = QString::fromStdWString(column->getZeraryFxColumn()
->getZeraryColumnFx()
->getZeraryFx()
->getName());
else if ((!xsh->getCell(row, col).isEmpty() && !isSoundColumn) && x > 6 &&
x < o->cellWidth()) {
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 &&
o->rect(PredefinedRect::PREVIEW_TRACK).contains(mouseInCell))
m_tooltip = tr("Click and drag to play");
else if (m_levelExtenderRect.contains(pos))
m_tooltip = tr("Click and drag to repeat selected cells");
else if (isSoundColumn && rectContainsPos(m_soundLevelModifyRects, pos)) {
if (o->isVerticalTimeline())
setCursor(Qt::SplitVCursor);
else
setCursor(Qt::SplitHCursor);
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) {
const Orientation *o = m_viewer->orientation();
TPoint pos(event->pos().x(), event->pos().y());
CellPosition cellPosition = m_viewer->xyToPosition(event->pos());
int row = cellPosition.frame();
int col = cellPosition.layer();
// Se la colonna e' sound non devo fare nulla
TXshColumn *column = m_viewer->getXsheet()->getColumn(col);
if (column && (column->getSoundColumn() || column->getSoundTextColumn()))
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);
QPoint xy = m_viewer->positionToXY(CellPosition(r, c));
TPointD pos = notes->getNotePos(i) + TPointD(xy.x(), xy.y());
QRect rect(pos.x, pos.y, NoteWidth, NoteHeight);
if (!rect.contains(event->pos())) continue;
m_viewer->setCurrentNoteIndex(i);
m_viewer->getNotesWidget().at(i)->openNotePopup();
return;
}
TObjectHandle *oh = TApp::instance()->getCurrentObject();
oh->setObjectId(m_viewer->getObjectId(col));
if (Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled()) {
QPoint cellTopLeft = m_viewer->positionToXY(CellPosition(row, col));
QPoint mouseInCell = event->pos() - cellTopLeft;
TXsheet *xsh = m_viewer->getXsheet();
TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col));
int k0, k1;
bool isKeyframeFrame = pegbar && pegbar->getKeyframeRange(k0, k1) &&
k0 <= row && row <= k1 + 1;
bool isKeyFrameArea =
isKeyframeFrame &&
o->rect(PredefinedRect::KEYFRAME_AREA).contains(mouseInCell) &&
row < k1 + 1;
// If you are in the keyframe area, open a function editor
if (isKeyFrameArea) {
QAction *action =
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;
int colCount = m_viewer->getCellSelection()->getSelectedCells().getColCount();
m_renameCell->showInRowCol(row, col, colCount > 1);
}
//-----------------------------------------------------------------------------
void CellArea::contextMenuEvent(QContextMenuEvent *event) {
const Orientation *o = m_viewer->orientation();
TPoint pos(event->pos().x(), event->pos().y());
CellPosition cellPosition = m_viewer->xyToPosition(event->pos());
int row = cellPosition.frame();
int col = cellPosition.layer();
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);
QPoint xy = m_viewer->positionToXY(CellPosition(r, c));
TPointD pos = notes->getNotePos(i) + TPointD(xy.x(), xy.y());
QRect rect(pos.x, pos.y, NoteWidth, NoteHeight);
if (!rect.contains(event->pos())) continue;
m_viewer->setCurrentNoteIndex(i);
createNoteMenu(menu);
if (!menu.isEmpty()) menu.exec(event->globalPos());
return;
}
TXsheet *xsh = m_viewer->getXsheet();
int x0 = m_viewer->positionToXY(cellPosition).x();
int x = pos.x - x0;
TStageObject *pegbar = xsh->getStageObject(m_viewer->getObjectId(col));
int k0, k1;
int r0, r1, c0, c1;
if (col >= 0) m_viewer->getCellSelection()->getSelectedCells(r0, c0, r1, c1);
QPoint cellTopLeft = m_viewer->positionToXY(CellPosition(row, col));
QPoint mouseInCell = event->pos() - cellTopLeft;
bool isKeyframeFrame =
Preferences::instance()->isShowKeyframesOnXsheetCellAreaEnabled() &&
pegbar && pegbar->getKeyframeRange(k0, k1) && k0 <= row && row <= k1 + 1;
bool isKeyFrameArea =
isKeyframeFrame &&
o->rect(PredefinedRect::KEYFRAME_AREA).contains(mouseInCell) &&
row < k1 + 1;
if (isKeyFrameArea) {
TStageObjectId objectId;
if (col < 0)
objectId = TStageObjectId::CameraId(0);
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();
}
//-----------------------------------------------------------------------------
const bool CellArea::isControlPressed() { return isCtrlPressed; }
//-----------------------------------------------------------------------------
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) {
menu.addAction(cmdManager->getAction(MI_LevelSettings));
menu.addSeparator();
if (!soundCellsSelected) {
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));
reframeSubMenu->addAction(
cmdManager->getAction(MI_ReframeWithEmptyInbetweens));
}
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);
QMenu *editCellNumbersMenu = new QMenu(tr("Edit Cell Numbers"), this);
{
editCellNumbersMenu->addAction(cmdManager->getAction(MI_Reverse));
editCellNumbersMenu->addAction(cmdManager->getAction(MI_Swing));
editCellNumbersMenu->addAction(cmdManager->getAction(MI_Random));
editCellNumbersMenu->addAction(cmdManager->getAction(MI_Dup));
editCellNumbersMenu->addAction(cmdManager->getAction(MI_Rollup));
editCellNumbersMenu->addAction(cmdManager->getAction(MI_Rolldown));
editCellNumbersMenu->addAction(cmdManager->getAction(MI_TimeStretch));
editCellNumbersMenu->addAction(
cmdManager->getAction(MI_AutoInputCellNumber));
}
menu.addMenu(editCellNumbersMenu);
menu.addSeparator();
menu.addAction(cmdManager->getAction(MI_Autorenumber));
}
QMenu *replaceLevelMenu = new QMenu(tr("Replace Level"), this);
menu.addMenu(replaceLevelMenu);
replaceLevelMenu->addAction(cmdManager->getAction(MI_ReplaceLevel));
replaceLevelMenu->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 = replaceLevelMenu->addMenu(tr("Replace with"));
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()))
replaceLevelMenu->addAction(
cmdManager->getAction(MI_RevertToCleanedUp));
if (selectionContainLevelImage(m_viewer->getCellSelection(),
m_viewer->getXsheet()))
replaceLevelMenu->addAction(
cmdManager->getAction(MI_RevertToLastSaved));
menu.addAction(cmdManager->getAction(MI_SetKeyframes));
}
menu.addSeparator();
menu.addAction(cmdManager->getAction(MI_Cut));
menu.addAction(cmdManager->getAction(MI_Copy));
menu.addAction(cmdManager->getAction(MI_Paste));
QMenu *pasteSpecialMenu = new QMenu(tr("Paste Special"), this);
{
pasteSpecialMenu->addAction(cmdManager->getAction(MI_PasteInto));
pasteSpecialMenu->addAction(cmdManager->getAction(MI_PasteNumbers));
}
menu.addMenu(pasteSpecialMenu);
menu.addAction(cmdManager->getAction(MI_Clear));
menu.addAction(cmdManager->getAction(MI_Insert));
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())) {
QMenu *editImageMenu = new QMenu(tr("Edit Image"), this);
{
editImageMenu->addAction(cmdManager->getAction(MI_AdjustLevels));
editImageMenu->addAction(cmdManager->getAction(MI_LinesFade));
editImageMenu->addAction(
cmdManager->getAction(MI_BrightnessAndContrast));
editImageMenu->addAction(cmdManager->getAction(MI_Antialias));
editImageMenu->addAction(cmdManager->getAction(MI_CanvasSize));
}
menu.addMenu(editImageMenu);
} 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;