Merge pull request #942 from manongjohn/navigation_pins

Timeline/Xsheet navigation tags
This commit is contained in:
manongjohn 2022-04-01 07:37:45 -04:00 committed by GitHub
commit a750f74ae4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1043 additions and 8 deletions

View file

@ -139,7 +139,8 @@ enum class PredefinedRect {
// ADD_LEVEL_AREA,
// ADD_LEVEL,
FOOTER_NOTE_OBJ_AREA,
FOOTER_NOTE_AREA
FOOTER_NOTE_AREA,
NAVIGATION_TAG_AREA
};
enum class PredefinedLine {
LOCKED, //! dotted vertical line when cell is locked
@ -169,7 +170,8 @@ enum class PredefinedPath {
VOLUME_SLIDER_TRACK, //! slider track
VOLUME_SLIDER_HEAD, //! slider head
TIME_INDICATOR_HEAD, //! current time indicator head
FRAME_MARKER_DIAMOND
FRAME_MARKER_DIAMOND,
NAVIGATION_TAG
};
enum class PredefinedPoint {
KEY_HIDDEN, //! move extender handle that much if key icons are disabled

View file

@ -0,0 +1,71 @@
#pragma once
#ifndef NAVIGATION_TAGS_INCLUDED
#define NAVIGATION_TAGS_INCLUDED
#include "tcommon.h"
#undef DVAPI
#undef DVVAR
#ifdef TOONZLIB_EXPORTS
#define DVAPI DV_EXPORT_API
#define DVVAR DV_EXPORT_VAR
#else
#define DVAPI DV_IMPORT_API
#define DVVAR DV_IMPORT_VAR
#endif
#include <QString>
#include <QColor>
class TOStream;
class TIStream;
class DVAPI NavigationTags {
public:
struct Tag {
int m_frame;
QString m_label;
QColor m_color;
Tag() : m_frame(-1), m_label(), m_color(Qt::magenta) {}
Tag(int frame) : m_frame(frame), m_label(), m_color(Qt::magenta) {}
Tag(int frame, QString label)
: m_frame(frame), m_label(label), m_color(Qt::magenta) {}
Tag(int frame, QString label, QColor color)
: m_frame(frame), m_label(label), m_color(color) {}
~Tag() {}
bool operator<(const Tag &otherTag) const {
return (m_frame < otherTag.m_frame);
}
};
std::vector<Tag> m_tags;
NavigationTags() {}
~NavigationTags() {}
std::vector<Tag> getTags() { return m_tags; }
int getCount() const;
Tag getTag(int frame);
void addTag(int frame, QString label = "");
void removeTag(int frame);
void clearTags();
bool isTagged(int frame);
int getPrevTag(int currentFrame);
int getNextTag(int currentFrame);
void moveTag(int fromFrame, int toFrame);
void shiftTags(int startFrame, int shift);
QString getTagLabel(int frame);
void setTagLabel(int frame, QString label);
QColor getTagColor(int frame);
void setTagColor(int frame, QColor color);
void saveData(TOStream &os);
void loadData(TIStream &is);
};
#endif

View file

@ -53,6 +53,7 @@ class TFrameId;
class Orientation;
class TXsheetColumnChangeObserver;
class ExpressionReferenceMonitor;
class NavigationTags;
//=============================================================================
@ -158,6 +159,7 @@ private:
std::unique_ptr<TXsheetImp> m_imp;
TXshNoteSet *m_notes;
SoundProperties *m_soundProperties;
NavigationTags *m_navigationTags;
int m_cameraColumnIndex;
TXshColumn *m_cameraColumn;
@ -593,6 +595,10 @@ in TXsheetImp.
void convertToImplicitHolds();
void convertToExplicitHolds();
NavigationTags *getNavigationTags() const { return m_navigationTags; }
bool isFrameTagged(int frame) const;
void toggleTaggedFrame(int frame);
protected:
bool checkCircularReferences(TXsheet *childCandidate);

View file

@ -133,6 +133,7 @@ set(MOC_HEADERS
../stopmotion/stopmotionserial.h
../stopmotion/stopmotionlight.h
cameracapturelevelcontrol.h
navtageditorpopup.h
)
set(HEADERS
@ -369,6 +370,7 @@ set(SOURCES
../stopmotion/stopmotionserial.cpp
../stopmotion/stopmotionlight.cpp
cameracapturelevelcontrol.cpp
navtageditorpopup.cpp
)
if(WITH_TRANSLATION)

View file

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16px"
height="16px"
version="1.1"
xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"
id="svg20"
sodipodi:docname="next_nav_tag.svg"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs24" /><sodipodi:namedview
id="namedview22"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="true"
inkscape:zoom="44.9375"
inkscape:cx="8"
inkscape:cy="7.9888734"
inkscape:window-width="1920"
inkscape:window-height="1141"
inkscape:window-x="-7"
inkscape:window-y="-7"
inkscape:window-maximized="1"
inkscape:current-layer="svg20"><inkscape:grid
type="xygrid"
id="grid158" /></sodipodi:namedview>
<rect
x="0"
y="0"
width="16"
height="16"
style="clip-rule:evenodd;fill-opacity:0;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"
id="rect2" /><path
id="path10088"
inkscape:transform-center-y="0.39999918"
d="M 11.5,16.000001 9,13 V 7 h 5 v 6 z"
sodipodi:nodetypes="cccccc"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-width:0.640749;stroke-linejoin:round;stroke-miterlimit:2" /><g
id="bg-3"
transform="translate(-110,-390)"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><rect
x="110"
y="390"
width="16"
height="16"
style="fill-opacity:0"
id="rect2-5" /></g><g
transform="translate(-110,-510)"
id="g10"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><path
d="M 126,517.086 C 125.841,517.03 125.672,517 125.5,517 H 125 v -3 c 0,-0.552 -0.448,-1 -1,-1 -2.577,0 -9.423,0 -12,0 -0.552,0 -1,0.448 -1,1 0,1.916 0,6.084 0,8 0,0.552 0.448,1 1,1 h 5 v 2.5 c 0,0.175 0.03,0.344 0.085,0.5 H 110 v -16 h 16 z M 118,523 v 2.5 z m -5,1 h -2 v 1 h 2 z m 3,0 h -2 v 1 h 2 z m 0,-13 h -2 v 1 h 2 z m 3,0 h -2 v 1 h 2 z m -6,0 h -2 v 1 h 2 z m 9,0 h -2 v 1 h 2 z m 3,0 h -2 v 1 h 2 z"
id="path8" /></g><path
sodipodi:type="star"
style="clip-rule:evenodd;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"
id="path254"
inkscape:flatsided="false"
sodipodi:sides="3"
sodipodi:cx="2"
sodipodi:cy="8"
sodipodi:r1="3"
sodipodi:r2="1.5"
sodipodi:arg1="0"
sodipodi:arg2="1.0471976"
inkscape:rounded="0"
inkscape:randomized="0"
inkscape:transform-center-x="-0.83333335"
transform="matrix(1.1111111,0,0,1.5396007,1.4444446,-4.3168056)"
d="M 5,8 2.7499999,9.2990381 0.5,10.598076 l 0,-2.5980761 0,-2.5980761 2.2500001,1.2990381 z" /></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16px"
height="16px"
version="1.1"
xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"
id="svg20"
sodipodi:docname="prev_nav_tag.svg"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs24" /><sodipodi:namedview
id="namedview22"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="true"
inkscape:zoom="44.9375"
inkscape:cx="3.6161335"
inkscape:cy="7.9888734"
inkscape:window-width="1920"
inkscape:window-height="1141"
inkscape:window-x="-7"
inkscape:window-y="-7"
inkscape:window-maximized="1"
inkscape:current-layer="svg20"><inkscape:grid
type="xygrid"
id="grid230" /></sodipodi:namedview>
<g
id="bg"
transform="translate(-110,-390)"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><rect
x="110"
y="390"
width="16"
height="16"
style="fill-opacity:0"
id="rect2" /></g><path
id="path10088"
inkscape:transform-center-y="0.39999918"
d="M 11.5,16 9,13 V 7 h 5 v 6 z"
sodipodi:nodetypes="cccccc"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-width:0.640749;stroke-linejoin:round;stroke-miterlimit:2" /><g
id="g12307"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"
transform="translate(2,2.000002)"><g
id="bg-3"
transform="translate(-112,-392)"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><rect
x="110"
y="390"
width="16"
height="16"
style="fill-opacity:0"
id="rect2-5" /></g><g
transform="translate(-112,-512)"
id="g10"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><path
d="M 126,517.086 C 125.841,517.03 125.672,517 125.5,517 H 125 v -3 c 0,-0.552 -0.448,-1 -1,-1 -2.577,0 -9.423,0 -12,0 -0.552,0 -1,0.448 -1,1 0,1.916 0,6.084 0,8 0,0.552 0.448,1 1,1 h 5 v 2.5 c 0,0.175 0.03,0.344 0.085,0.5 H 110 v -16 h 16 z M 118,523 v 2.5 z m -5,1 h -2 v 1 h 2 z m 3,0 h -2 v 1 h 2 z m 0,-13 h -2 v 1 h 2 z m 3,0 h -2 v 1 h 2 z m -6,0 h -2 v 1 h 2 z m 9,0 h -2 v 1 h 2 z m 3,0 h -2 v 1 h 2 z"
id="path8" /></g></g><path
sodipodi:type="star"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd"
id="path254"
inkscape:flatsided="false"
sodipodi:sides="3"
sodipodi:cx="2"
sodipodi:cy="8"
sodipodi:r1="3"
sodipodi:r2="1.5"
sodipodi:arg1="0"
sodipodi:arg2="1.0471976"
inkscape:rounded="0"
inkscape:randomized="0"
inkscape:transform-center-x="0.83333356"
transform="matrix(-1.1111112,0,0,1.5396007,7.5555556,-4.3168052)"
d="M 5,8 2.7499999,9.2990381 0.5,10.598076 l 0,-2.5980761 0,-2.5980761 2.2500001,1.2990381 z" /></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16px"
height="16px"
version="1.1"
xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"
id="svg20"
sodipodi:docname="toggle_nav_tag.svg"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs24" /><sodipodi:namedview
id="namedview22"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="true"
inkscape:snap-to-guides="false"
inkscape:snap-grids="true"
inkscape:zoom="44.9375"
inkscape:cx="3.6161335"
inkscape:cy="7.9888734"
inkscape:window-width="1920"
inkscape:window-height="1141"
inkscape:window-x="-7"
inkscape:window-y="-7"
inkscape:window-maximized="1"
inkscape:current-layer="insert_frame"><inkscape:grid
type="xygrid"
id="grid355" /></sodipodi:namedview>
<g
id="insert_frame"
transform="translate(2,2)"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><rect
x="-2"
y="-2"
width="16"
height="16"
style="fill-opacity:0"
id="rect2" /><path
id="path10088"
inkscape:transform-center-y="0.39999918"
d="M 9.5,14 7,11 V 5 h 5 v 6 z"
sodipodi:nodetypes="cccccc"
style="stroke-width:0.640749" /><g
id="bg-3"
transform="translate(-112,-392)"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><rect
x="110"
y="390"
width="16"
height="16"
style="fill-opacity:0"
id="rect2-5" /></g><g
transform="translate(-112,-512)"
id="g10"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><path
d="M 126,517.086 C 125.841,517.03 125.672,517 125.5,517 H 125 v -3 c 0,-0.552 -0.448,-1 -1,-1 -2.577,0 -9.423,0 -12,0 -0.552,0 -1,0.448 -1,1 0,1.916 0,6.084 0,8 0,0.552 0.448,1 1,1 h 5 v 2.5 c 0,0.175 0.03,0.344 0.085,0.5 H 110 v -16 h 16 z M 118,523 v 2.5 z m -5,1 h -2 v 1 h 2 z m 3,0 h -2 v 1 h 2 z m 0,-13 h -2 v 1 h 2 z m 3,0 h -2 v 1 h 2 z m -6,0 h -2 v 1 h 2 z m 9,0 h -2 v 1 h 2 z m 3,0 h -2 v 1 h 2 z"
id="path8" /></g></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -2040,6 +2040,19 @@ void MainWindow::defineActions() {
createMenuXsheetAction(MI_SetAutoMarkers, QT_TR_NOOP("Set Auto Markers"), "");
createMenuXsheetAction(MI_PreviewThis,
QT_TR_NOOP("Set Markers to Current Frame"), "");
createMenuXsheetAction(MI_ToggleTaggedFrame,
QT_TR_NOOP("Toggle Navigation Tag"), "",
"toggle_nav_tag");
createMenuXsheetAction(MI_NextTaggedFrame, QT_TR_NOOP("Next Tag"), "",
"next_nav_tag");
createMenuXsheetAction(MI_PrevTaggedFrame, QT_TR_NOOP("Previous Tag"), "",
"prev_nav_tag");
createMenuXsheetAction(MI_EditTaggedFrame, QT_TR_NOOP("Edit Tag"), "", "");
createMenuXsheetAction(MI_ClearTags, QT_TR_NOOP("Remove Tags"), "", "");
CommandManager::instance()->enable(MI_NextTaggedFrame, false);
CommandManager::instance()->enable(MI_PrevTaggedFrame, false);
CommandManager::instance()->enable(MI_EditTaggedFrame, false);
CommandManager::instance()->enable(MI_ClearTags, false);
// Menu - Cells

View file

@ -485,4 +485,11 @@
#define MI_ToggleImplicitHold "MI_ToggleImplicitHold"
// Navigation tags
#define MI_ToggleTaggedFrame "MI_ToggleTaggedFrame"
#define MI_EditTaggedFrame "MI_EditTaggedFrame"
#define MI_NextTaggedFrame "MI_NextTaggedFrame"
#define MI_PrevTaggedFrame "MI_PrevTaggedFrame"
#define MI_ClearTags "MI_ClearTags"
#endif

View file

@ -0,0 +1,104 @@
#include "navtageditorpopup.h"
#include "../toonz/tapp.h"
#include <QPushButton>
#include <QMainWindow>
#include <QColor>
namespace {
const QIcon getColorChipIcon(QColor color) {
QPixmap pixmap(12, 12);
pixmap.fill(color);
return QIcon(pixmap);
}
}
void NavTagEditorPopup::accept() {
m_label = QString(m_labelFld->text());
Dialog::accept();
}
NavTagEditorPopup::NavTagEditorPopup(int frame, QString label, QColor color)
: Dialog(TApp::instance()->getMainWindow(), true, true, "Edit Tag")
, m_label(label)
, m_color(color) {
bool ret = true;
setWindowTitle(tr("Edit Tag"));
m_labelFld = new DVGui::LineEdit(m_label);
m_labelFld->setMaximumHeight(DVGui::WidgetHeight);
addWidget(tr("Frame %1 Label:").arg(frame + 1), m_labelFld);
m_colorCB = new QComboBox(this);
m_colorCB->addItem(getColorChipIcon(Qt::magenta), tr("Magenta"),
TagColors::Magenta);
m_colorCB->addItem(getColorChipIcon(Qt::red), tr("Red"), TagColors::Red);
m_colorCB->addItem(getColorChipIcon(Qt::green), tr("Green"),
TagColors::Green);
m_colorCB->addItem(getColorChipIcon(Qt::blue), tr("Blue"), TagColors::Blue);
m_colorCB->addItem(getColorChipIcon(Qt::yellow), tr("Yellow"),
TagColors::Yellow);
m_colorCB->addItem(getColorChipIcon(Qt::cyan), tr("Cyan"), TagColors::Cyan);
m_colorCB->addItem(getColorChipIcon(Qt::white), tr("White"),
TagColors::White);
addWidget(tr("Color:"), m_colorCB);
if (color == Qt::magenta)
m_colorCB->setCurrentIndex(0);
else if (color == Qt::red)
m_colorCB->setCurrentIndex(1);
else if (color == Qt::green)
m_colorCB->setCurrentIndex(2);
else if (color == Qt::blue)
m_colorCB->setCurrentIndex(3);
else if (color == Qt::yellow)
m_colorCB->setCurrentIndex(4);
else if (color == Qt::cyan)
m_colorCB->setCurrentIndex(5);
else if (color == Qt::white)
m_colorCB->setCurrentIndex(6);
ret = ret &&
connect(m_labelFld, SIGNAL(editingFinished()), SLOT(onLabelChanged()));
ret = ret &&
connect(m_colorCB, SIGNAL(activated(int)), SLOT(onColorChanged(int)));
QPushButton *okBtn = new QPushButton(tr("Ok"), this);
okBtn->setDefault(true);
QPushButton *cancelBtn = new QPushButton(tr("Cancel"), this);
connect(okBtn, SIGNAL(clicked()), this, SLOT(accept()));
connect(cancelBtn, SIGNAL(clicked()), this, SLOT(reject()));
addButtonBarWidget(okBtn, cancelBtn);
}
void NavTagEditorPopup::onColorChanged(int index) {
QColor color;
switch (index) {
case TagColors::Red:
color = Qt::red;
break;
case TagColors::Green:
color = Qt::green;
break;
case TagColors::Blue:
color = Qt::blue;
break;
case TagColors::Yellow:
color = Qt::yellow;
break;
case TagColors::Cyan:
color = Qt::cyan;
break;
case TagColors::Magenta:
default:
color = Qt::magenta;
break;
}
m_color = color;
}

View file

@ -0,0 +1,41 @@
#pragma once
#ifndef TAGEDITORPOPUP_INCLUDED
#define TAGEDITORPOPUP_INCLUDED
#include "tcommon.h"
#include "toonzqt/dvdialog.h"
#include "toonzqt/lineedit.h"
#include <QString>
#include <QColor>
#include <QComboBox>
class NavTagEditorPopup final : public DVGui::Dialog {
Q_OBJECT
DVGui::LineEdit *m_labelFld;
QComboBox *m_colorCB;
QString m_label;
QColor m_color;
public:
enum TagColors { Magenta = 0, Red, Green, Blue, Yellow, Cyan, White };
public:
NavTagEditorPopup(int frame, QString label, QColor color);
void accept() override;
QString getLabel() { return m_label; }
QColor getColor() { return m_color; }
protected slots:
void onLabelChanged() {}
void onColorChanged(int);
};
#endif

View file

@ -384,6 +384,10 @@
<file>icons/dark/actions/30/sound_header_on.svg</file>
<file>icons/dark/actions/74/notelevel.svg</file>
<file>icons/dark/actions/16/toggle_nav_tag.svg</file>
<file>icons/dark/actions/16/next_nav_tag.svg</file>
<file>icons/dark/actions/16/prev_nav_tag.svg</file>
<!-- Modes, Types, Options -->
<file>icons/dark/actions/16/ink_check.svg</file>
<file>icons/dark/actions/16/inks_only.svg</file>

View file

@ -43,6 +43,7 @@
#include "toonz/scenefx.h"
#include "toonz/preferences.h"
#include "toonz/txshlevelcolumn.h"
#include "toonz/navigationtags.h"
// TnzQt includes
#include "toonzqt/tselectionhandle.h"
@ -62,6 +63,7 @@
#include "menubarcommandids.h"
#include "columncommand.h"
#include "xshcellviewer.h" // SetCellMarkUndo
#include "navtageditorpopup.h"
// Qt includes
#include <QClipboard>
@ -181,6 +183,8 @@ void InsertSceneFrameUndo::doInsertSceneFrame(int frame) {
if (TStageObject *obj = xsh->getStageObject(objectId))
insertFrame(obj, frame);
}
xsh->getNavigationTags()->shiftTags(frame, 1);
}
//-----------------------------------------------------------------------------
@ -204,6 +208,9 @@ void InsertSceneFrameUndo::doRemoveSceneFrame(int frame) {
if (TStageObject *pegbar = xsh->getStageObject(objectId))
removeFrame(pegbar, frame);
}
if (xsh->isFrameTagged(frame)) xsh->getNavigationTags()->removeTag(frame);
xsh->getNavigationTags()->shiftTags(frame, -1);
}
//-----------------------------------------------------------------------------
@ -317,6 +324,7 @@ public:
class RemoveSceneFrameUndo final : public InsertSceneFrameUndo {
std::vector<TXshCell> m_cells;
std::vector<TStageObject::Keyframe> m_keyframes;
NavigationTags::Tag m_tag;
public:
RemoveSceneFrameUndo(int frame) : InsertSceneFrameUndo(frame) {
@ -327,6 +335,7 @@ public:
m_cells.resize(colsCount);
m_keyframes.resize(colsCount + 1);
m_tag = xsh->getNavigationTags()->getTag(frame);
// Inserting the eventual camera keyframe at the end
TStageObject *cameraObj = xsh->getStageObject(
@ -372,6 +381,10 @@ public:
}
}
// Restore tag if there was one
if (m_tag.m_frame != -1)
xsh->getNavigationTags()->addTag(m_tag.m_frame, m_tag.m_label);
TApp::instance()->getCurrentScene()->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
@ -2711,6 +2724,107 @@ public:
XsheetGUI::getPlayRange(r0, r1, step);
XsheetGUI::setPlayRange(row, row, step);
TApp::instance()->getCurrentXsheetViewer()->update();
}
} PreviewThis;
//============================================================
class ToggleTaggedFrame final : public MenuItemHandler {
public:
ToggleTaggedFrame() : MenuItemHandler(MI_ToggleTaggedFrame) {}
void execute() override {
TApp *app = TApp::instance();
int frame = app->getCurrentFrame()->getFrame();
assert(frame >= 0);
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
xsh->toggleTaggedFrame(frame);
NavigationTags *navTags = xsh->getNavigationTags();
CommandManager::instance()->enable(MI_EditTaggedFrame,
navTags->isTagged(frame));
CommandManager::instance()->enable(MI_ClearTags, (navTags->getCount() > 0));
TApp::instance()->getCurrentXsheetViewer()->update();
}
} ToggleTaggedFrame;
//============================================================
class EditTaggedFrame final : public MenuItemHandler {
public:
EditTaggedFrame() : MenuItemHandler(MI_EditTaggedFrame) {}
void execute() override {
TApp *app = TApp::instance();
int frame = app->getCurrentFrame()->getFrame();
assert(frame >= 0);
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
NavigationTags *tags = xsh->getNavigationTags();
QString label = tags->getTagLabel(frame);
QColor color = tags->getTagColor(frame);
NavTagEditorPopup navTagEditor(frame, label, color);
if (navTagEditor.exec() != QDialog::Accepted) return;
tags->setTagLabel(frame, navTagEditor.getLabel());
tags->setTagColor(frame, navTagEditor.getColor());
}
} EditTaggedFrame;
//============================================================
class NextTaggedFrame final : public MenuItemHandler {
public:
NextTaggedFrame() : MenuItemHandler(MI_NextTaggedFrame) {}
void execute() override {
TApp *app = TApp::instance();
int frame = app->getCurrentFrame()->getFrame();
assert(frame >= 0);
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
NavigationTags *navTags = xsh->getNavigationTags();
int nextFrame = navTags->getNextTag(frame);
if (nextFrame != -1)
app->getCurrentXsheetViewer()->setCurrentRow(nextFrame);
}
} NextTaggedFrame;
//============================================================
class PrevTaggedFrame final : public MenuItemHandler {
public:
PrevTaggedFrame() : MenuItemHandler(MI_PrevTaggedFrame) {}
void execute() override {
TApp *app = TApp::instance();
int frame = app->getCurrentFrame()->getFrame();
assert(frame >= 0);
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
NavigationTags *navTags = xsh->getNavigationTags();
int prevFrame = navTags->getPrevTag(frame);
if (prevFrame != -1)
app->getCurrentXsheetViewer()->setCurrentRow(prevFrame);
}
} PrevTaggedFrame;
//============================================================
class ClearTags final : public MenuItemHandler {
public:
ClearTags() : MenuItemHandler(MI_ClearTags) {}
void execute() override {
TApp *app = TApp::instance();
int frame = app->getCurrentFrame()->getFrame();
assert(frame >= 0);
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
NavigationTags *navTags = xsh->getNavigationTags();
navTags->clearTags();
CommandManager::instance()->enable(MI_NextTaggedFrame, false);
CommandManager::instance()->enable(MI_PrevTaggedFrame, false);
CommandManager::instance()->enable(MI_EditTaggedFrame, false);
CommandManager::instance()->enable(MI_ClearTags, false);
TApp::instance()->getCurrentXsheetViewer()->update();
}
} ClearTags;

View file

@ -50,6 +50,7 @@
#include "toutputproperties.h"
#include "toonz/preferences.h"
#include "toonz/columnfan.h"
#include "toonz/navigationtags.h"
// TnzBase includes
#include "tfx.h"
@ -2207,3 +2208,49 @@ XsheetGUI::DragTool *XsheetGUI::DragTool::makeDragAndDropDataTool(
XsheetViewer *viewer) {
return new DataDragTool(viewer);
}
//=============================================================================
// NavigationTagDragTool
//-----------------------------------------------------------------------------
namespace {
class NavigationTagDragTool final : public XsheetGUI::DragTool {
int m_taggedRow;
public:
NavigationTagDragTool(XsheetViewer *viewer) : DragTool(viewer) {}
void onClick(const CellPosition &pos) override {
int row = pos.frame();
m_taggedRow = row;
refreshRowsArea();
}
void onDrag(const CellPosition &pos) override {
int row = pos.frame();
if (row < 0) row = 0;
onRowChange(row);
refreshRowsArea();
}
void onRowChange(int row) {
if (row < 0) return;
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
NavigationTags *navTags = xsh->getNavigationTags();
if (m_taggedRow == row || navTags->isTagged(row)) return;
navTags->moveTag(m_taggedRow, row);
m_taggedRow = row;
}
};
//-----------------------------------------------------------------------------
} // namespace
//-----------------------------------------------------------------------------
XsheetGUI::DragTool *XsheetGUI::DragTool::makeNavigationTagDragTool(
XsheetViewer *viewer) {
return new NavigationTagDragTool(viewer);
}

View file

@ -69,6 +69,8 @@ public:
static DragTool *makeColumnLinkTool(XsheetViewer *viewer);
static DragTool *makeColumnMoveTool(XsheetViewer *viewer);
static DragTool *makeVolumeDragTool(XsheetViewer *viewer);
static DragTool *makeNavigationTagDragTool(XsheetViewer *viewer);
};
void setPlayRange(int r0, int r1, int step, bool withUndo = true);

View file

@ -35,6 +35,7 @@
#include "toonz/txshlevelhandle.h"
#include "toonz/tproject.h"
#include "tconvert.h"
#include "toonz/navigationtags.h"
#include "tenv.h"
@ -1408,6 +1409,18 @@ void XsheetViewer::onSceneSwitched() {
void XsheetViewer::onXsheetChanged() {
refreshContentSize(0, 0);
updateAllAree();
int row = TApp::instance()->getCurrentFrame()->getFrame();
TXsheet *xsh = getXsheet();
NavigationTags *navTags = xsh->getNavigationTags();
int lastTag = navTags->getPrevTag(INT_MAX);
int firstTag = navTags->getNextTag(-1);
CommandManager::instance()->enable(MI_NextTaggedFrame, (row < lastTag));
CommandManager::instance()->enable(MI_PrevTaggedFrame,
firstTag != -1 && row > firstTag);
CommandManager::instance()->enable(MI_EditTaggedFrame,
navTags->isTagged(row));
CommandManager::instance()->enable(MI_ClearTags, (navTags->getCount() > 0));
}
//-----------------------------------------------------------------------------
@ -1432,6 +1445,16 @@ void XsheetViewer::onCurrentFrameSwitched() {
}
m_isCurrentFrameSwitched = false;
scrollToRow(row);
TXsheet *xsh = getXsheet();
NavigationTags *navTags = xsh->getNavigationTags();
int lastTag = navTags->getPrevTag(INT_MAX);
int firstTag = navTags->getNextTag(-1);
CommandManager::instance()->enable(MI_NextTaggedFrame, (row < lastTag));
CommandManager::instance()->enable(MI_PrevTaggedFrame,
firstTag != -1 && row > firstTag);
CommandManager::instance()->enable(MI_EditTaggedFrame,
navTags->isTagged(row));
}
//-----------------------------------------------------------------------------

View file

@ -26,6 +26,7 @@
#include "tools/toolcommandids.h"
#include "toonz/tstageobject.h"
#include "toonz/tpinnedrangeset.h"
#include "toonz/navigationtags.h"
#include <QPainter>
#include <QMouseEvent>
@ -387,6 +388,50 @@ void RowArea::drawStopMotionCameraIndicator(QPainter &p) {
//-----------------------------------------------------------------------------
void RowArea::drawNavigationTags(QPainter &p, int r0, int r1) {
TApp *app = TApp::instance();
TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
assert(xsh);
QPoint frameAdj = m_viewer->getFrameZoomAdjustment();
NavigationTags *tags = xsh->getNavigationTags();
for (int r = r0; r <= r1; r++) {
if (!xsh->isFrameTagged(r)) continue;
QPoint topLeft = m_viewer->positionToXY(CellPosition(r, -1));
if (!m_viewer->orientation()->isVerticalTimeline())
topLeft.setY(0);
else
topLeft.setX(0);
QRect tagRect = m_viewer->orientation()
->rect(PredefinedRect::NAVIGATION_TAG_AREA)
.translated(topLeft)
.translated(-frameAdj / 2);
int frameMid, frameTop;
if (m_viewer->orientation()->isVerticalTimeline()) {
frameMid = tagRect.left() - 3;
frameTop = tagRect.top() + (tagRect.height() / 2);
} else {
frameMid = tagRect.left() + (tagRect.height() / 2) - 3;
frameTop = tagRect.top() + 1;
}
QPainterPath tag = m_viewer->orientation()
->path(PredefinedPath::NAVIGATION_TAG)
.translated(QPoint(frameMid, frameTop));
p.setPen(Qt::black);
p.setBrush(tags->getTagColor(r));
p.drawPath(tag);
p.setBrush(Qt::NoBrush);
}
}
//-----------------------------------------------------------------------------
void RowArea::drawOnionSkinBackground(QPainter &p, int r0, int r1) {
const Orientation *o = m_viewer->orientation();
@ -886,6 +931,8 @@ void RowArea::paintEvent(QPaintEvent *event) {
!m_viewer->orientation()->isVerticalTimeline())
drawCurrentTimeLine(p);
drawNavigationTags(p, r0, r1);
drawRows(p, r0, r1);
if (TApp::instance()->getCurrentFrame()->isEditingScene()) {
@ -993,7 +1040,13 @@ void RowArea::mousePressEvent(QMouseEvent *event) {
playR0 = 0;
}
if (playR1 == -1) { // getFrameCount = 0 i.e. xsheet is empty
if (xsh->getNavigationTags()->isTagged(row) &&
o->rect(PredefinedRect::NAVIGATION_TAG_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell)) {
setDragTool(XsheetGUI::DragTool::makeNavigationTagDragTool(m_viewer));
frameAreaIsClicked = true;
} else if (playR1 == -1) { // getFrameCount = 0 i.e. xsheet is empty
setDragTool(
XsheetGUI::DragTool::makeCurrentFrameModifierTool(m_viewer));
frameAreaIsClicked = true;
@ -1180,7 +1233,14 @@ void RowArea::mouseMoveEvent(QMouseEvent *event) {
m_tooltip = tr("Pinned Center : Col%1%2")
.arg(pinnedCenterColumnId + 1)
.arg((isRootBonePinned) ? " (Root)" : "");
else if (row == currentRow) {
else if (o->rect(PredefinedRect::NAVIGATION_TAG_AREA)
.adjusted(0, 0, -frameAdj.x(), -frameAdj.y())
.contains(mouseInCell)) {
TXsheet *xsh = m_viewer->getXsheet();
QString label = xsh->getNavigationTags()->getTagLabel(m_row);
if (label.isEmpty()) label = "-";
if (xsh->isFrameTagged(m_row)) m_tooltip = tr("Tag: %1").arg(label);
} else if (row == currentRow) {
if (Preferences::instance()->isOnionSkinEnabled() &&
o->rect(PredefinedRect::ONION)
.translated(-frameAdj / 2)
@ -1265,6 +1325,34 @@ void RowArea::contextMenuEvent(QContextMenuEvent *event) {
menu->addAction(cmdManager->getAction(MI_NoShift));
menu->addAction(cmdManager->getAction(MI_ResetShift));
// Tags
menu->addSeparator();
menu->addAction(cmdManager->getAction(MI_ToggleTaggedFrame));
menu->addAction(cmdManager->getAction(MI_EditTaggedFrame));
QMenu *tagMenu = menu->addMenu(tr("Tags"));
NavigationTags *navTags = m_viewer->getXsheet()->getNavigationTags();
QAction *tagAction;
if (!navTags->getCount()) {
tagAction = tagMenu->addAction("Empty");
tagAction->setEnabled(false);
} else {
std::vector<NavigationTags::Tag> tags = navTags->getTags();
for (int i = 0; i < tags.size(); i++) {
int frame = tags[i].m_frame;
QString label = tr("Frame %1").arg(frame + 1);
if (!tags[i].m_label.isEmpty()) label += ": " + tags[i].m_label;
tagAction = tagMenu->addAction(label);
tagAction->setData(frame);
connect(tagAction, SIGNAL(triggered()), this, SLOT(onJumpToTag()));
}
tagMenu->addSeparator();
tagMenu->addAction(cmdManager->getAction(MI_ClearTags));
}
menu->addAction(cmdManager->getAction(MI_NextTaggedFrame));
menu->addAction(cmdManager->getAction(MI_PrevTaggedFrame));
menu->exec(event->globalPos());
}
@ -1320,4 +1408,11 @@ bool RowArea::event(QEvent *event) {
//-----------------------------------------------------------------------------
void RowArea::onJumpToTag() {
QAction *senderAction = qobject_cast<QAction *>(sender());
assert(senderAction);
int frame = senderAction->data().toInt();
m_viewer->setCurrentRow(frame);
}
} // namespace XsheetGUI

View file

@ -55,6 +55,7 @@ class RowArea final : public QWidget {
void drawCurrentTimeLine(QPainter &p);
void drawShiftTraceMarker(QPainter &p);
void drawStopMotionCameraIndicator(QPainter &p);
void drawNavigationTags(QPainter &p, int r0, int r1);
DragTool *getDragTool() const;
void setDragTool(DragTool *dragTool);
@ -80,6 +81,8 @@ protected:
void mouseDoubleClickEvent(QMouseEvent *event) override;
bool event(QEvent *event) override;
protected slots:
void onJumpToTag();
};
} // namespace XsheetGUI;

View file

@ -163,6 +163,7 @@ set(HEADERS
../include/toonz/txsheetcolumnchange.h
../include/toonz/expressionreferencemonitor.h
../include/toonz/filepathproperties.h
../include/toonz/navigationtags.h
)
set(SOURCES
@ -322,6 +323,7 @@ set(SOURCES
textureutils.cpp
boardsettings.cpp
filepathproperties.cpp
navigationtags.cpp
)
if(BUILD_TARGET_WIN)

View file

@ -0,0 +1,215 @@
#include "toonz/navigationtags.h"
#include "tstream.h"
#include "texception.h"
#ifndef _WIN32
#include <limits.h>
#endif
//-----------------------------------------------------------------------------
int NavigationTags::getCount() const {
if (m_tags.empty()) return 0;
return m_tags.size();
}
//-----------------------------------------------------------------------------
NavigationTags::Tag NavigationTags::getTag(int frame) {
for (int i = 0; i < m_tags.size(); i++)
if (m_tags[i].m_frame == frame) return m_tags[i];
return Tag();
}
//-----------------------------------------------------------------------------
void NavigationTags::addTag(int frame, QString label) {
if (frame < 0 || isTagged(frame)) return;
m_tags.push_back(Tag(frame, label));
std::sort(m_tags.begin(), m_tags.end());
}
//-----------------------------------------------------------------------------
void NavigationTags::removeTag(int frame) {
if (frame < 0) return;
Tag tag = getTag(frame);
if (tag.m_frame == -1) return;
std::vector<Tag>::iterator it;
for (it = m_tags.begin(); it != m_tags.end(); it++)
if (it->m_frame == frame) {
m_tags.erase(it);
break;
}
}
//-----------------------------------------------------------------------------
void NavigationTags::clearTags() { m_tags.clear(); }
//-----------------------------------------------------------------------------
bool NavigationTags::isTagged(int frame) {
if (frame < 0) return false;
for (int i = 0; i < m_tags.size(); i++)
if (m_tags[i].m_frame == frame) return true;
return false;
}
//-----------------------------------------------------------------------------
void NavigationTags::moveTag(int fromFrame, int toFrame) {
if (fromFrame < 0 || toFrame < 0 || isTagged(toFrame)) return;
for (int i = 0; i < m_tags.size(); i++)
if (m_tags[i].m_frame == fromFrame) {
m_tags[i].m_frame = toFrame;
std::sort(m_tags.begin(), m_tags.end());
break;
}
}
//-----------------------------------------------------------------------------
// WARNING: When shifting left, shiftTag callers MUST take care of shifting tags
// to frame < 0 or handle possible frame collisions. This will not do it
// for you!
void NavigationTags::shiftTags(int startFrame, int shift) {
if (!m_tags.size()) return;
for (int i = 0; i < m_tags.size(); i++) {
if (m_tags[i].m_frame < startFrame) continue;
m_tags[i].m_frame += shift;
}
}
//-----------------------------------------------------------------------------
int NavigationTags::getPrevTag(int currentFrame) {
if (currentFrame < 0) return -1;
int index = -1;
int closestFrame = -1;
for (int i = 0; i < m_tags.size(); i++)
if (m_tags[i].m_frame < currentFrame && m_tags[i].m_frame > closestFrame) {
index = i;
closestFrame = m_tags[i].m_frame;
}
return index >= 0 ? m_tags[index].m_frame : -1;
}
//-----------------------------------------------------------------------------
int NavigationTags::getNextTag(int currentFrame) {
int index = -1;
int closestFrame = INT_MAX;
for (int i = 0; i < m_tags.size(); i++)
if (m_tags[i].m_frame > currentFrame && m_tags[i].m_frame < closestFrame) {
index = i;
closestFrame = m_tags[i].m_frame;
}
return index >= 0 ? m_tags[index].m_frame : -1;
}
//-----------------------------------------------------------------------------
QString NavigationTags::getTagLabel(int frame) {
Tag tag = getTag(frame);
return tag.m_label;
}
//-----------------------------------------------------------------------------
void NavigationTags::setTagLabel(int frame, QString label) {
if (frame < 0) return;
for (int i = 0; i < m_tags.size(); i++)
if (m_tags[i].m_frame == frame) {
m_tags[i].m_label = label;
break;
}
}
//-----------------------------------------------------------------------------
QColor NavigationTags::getTagColor(int frame) {
Tag tag = getTag(frame);
return tag.m_color;
}
//-----------------------------------------------------------------------------
void NavigationTags::setTagColor(int frame, QColor color) {
if (frame < 0) return;
for (int i = 0; i < m_tags.size(); i++)
if (m_tags[i].m_frame == frame) {
m_tags[i].m_color = color;
break;
}
}
//-----------------------------------------------------------------------------
void NavigationTags::saveData(TOStream &os) {
int i;
os.openChild("Tags");
for (i = 0; i < getCount(); i++) {
os.openChild("tag");
Tag tag = m_tags.at(i);
os << tag.m_frame;
os << tag.m_label;
os << tag.m_color.red();
os << tag.m_color.green();
os << tag.m_color.blue();
os.closeChild();
}
os.closeChild();
}
//-----------------------------------------------------------------------------
void NavigationTags::loadData(TIStream &is) {
while (!is.eos()) {
std::string tagName;
if (is.matchTag(tagName)) {
if (tagName == "Tags") {
while (!is.eos()) {
std::string tagName;
if (is.matchTag(tagName)) {
if (tagName == "tag") {
Tag tag;
is >> tag.m_frame;
std::wstring text;
is >> text;
tag.m_label = QString::fromStdWString(text);
int r, g, b;
is >> r;
is >> g;
is >> b;
tag.m_color = QColor(r, g, b);
m_tags.push_back(tag);
}
} else
throw TException("expected <tag>");
is.closeChild();
}
} else
throw TException("expected <Tags>");
is.closeChild();
} else
throw TException("expected tag");
}
}

View file

@ -12,7 +12,9 @@ const int KEY_ICON_WIDTH = 11;
const int KEY_ICON_HEIGHT = 11;
const int EASE_TRIANGLE_SIZE = 4;
const int PLAY_MARKER_SIZE = 10;
const int PINNED_SIZE = 10;
const int PINNED_SIZE = 11;
const int NAV_TAG_WIDTH = 7;
const int NAV_TAG_HEIGHT = 13;
const int FRAME_MARKER_SIZE = 4;
const int FOLDED_CELL_SIZE = 9;
const int SHIFTTRACE_DOT_SIZE = 12;
@ -415,6 +417,10 @@ TopToBottomOrientation::TopToBottomOrientation() {
SHIFTTRACE_DOT_SIZE, SHIFTTRACE_DOT_SIZE));
addRect(PredefinedRect::SHIFTTRACE_DOT_AREA,
QRect(SHIFTTRACE_DOT_OFFSET, 0, SHIFTTRACE_DOT_SIZE, CELL_HEIGHT));
addRect(
PredefinedRect::NAVIGATION_TAG_AREA,
QRect((FRAME_HEADER_WIDTH - NAV_TAG_HEIGHT) / 2,
(CELL_HEIGHT - NAV_TAG_WIDTH) / 2, NAV_TAG_HEIGHT, NAV_TAG_WIDTH));
// Column viewer
addRect(PredefinedRect::LAYER_HEADER,
@ -1004,6 +1010,14 @@ TopToBottomOrientation::TopToBottomOrientation() {
}
addPath(PredefinedPath::VOLUME_SLIDER_HEAD, head);
QPainterPath tag(QPointF(0, -3));
tag.lineTo(QPointF(9, -3));
tag.lineTo(QPointF(13, 0));
tag.lineTo(QPointF(9, 3));
tag.lineTo(QPointF(0, 3));
tag.lineTo(QPointF(0, -3));
addPath(PredefinedPath::NAVIGATION_TAG, tag);
//
// Points
//
@ -1202,6 +1216,11 @@ LeftToRightOrientation::LeftToRightOrientation() {
addRect(PredefinedRect::SHIFTTRACE_DOT_AREA,
QRect(0, SHIFTTRACE_DOT_OFFSET, CELL_WIDTH, SHIFTTRACE_DOT_SIZE));
addRect(PredefinedRect::NAVIGATION_TAG_AREA,
QRect((CELL_WIDTH - NAV_TAG_WIDTH) / 2,
(FRAME_HEADER_HEIGHT - NAV_TAG_HEIGHT) / 2, NAV_TAG_WIDTH,
NAV_TAG_HEIGHT));
// Column viewer
addRect(PredefinedRect::LAYER_HEADER,
QRect(1, 0, LAYER_HEADER_WIDTH - 2, CELL_HEIGHT));
@ -1422,6 +1441,14 @@ LeftToRightOrientation::LeftToRightOrientation() {
timeIndicator.lineTo(QPointF(0, 0));
addPath(PredefinedPath::TIME_INDICATOR_HEAD, timeIndicator);
QPainterPath tag(QPointF(-3, 0));
tag.lineTo(QPointF(3, 0));
tag.lineTo(QPointF(3, 9));
tag.lineTo(QPointF(0, 13));
tag.lineTo(QPointF(-3, 9));
tag.lineTo(QPointF(-3, 0));
addPath(PredefinedPath::NAVIGATION_TAG, tag);
//
// Points
//

View file

@ -33,6 +33,7 @@
#include "xshhandlemanager.h"
#include "orientation.h"
#include "toonz/expressionreferencemonitor.h"
#include "toonz/navigationtags.h"
#include "toonz/txsheet.h"
#include "toonz/preferences.h"
@ -197,7 +198,8 @@ TXsheet::TXsheet()
, m_imp(new TXsheet::TXsheetImp)
, m_notes(new TXshNoteSet())
, m_cameraColumnIndex(0)
, m_observer(nullptr) {
, m_observer(nullptr)
, m_navigationTags(new NavigationTags()) {
// extern TSyntax::Grammar *createXsheetGrammar(TXsheet*);
m_soundProperties = new TXsheet::SoundProperties();
m_imp->m_handleManager = new XshHandleManager(this);
@ -218,6 +220,7 @@ TXsheet::~TXsheet() {
assert(m_imp);
if (m_notes) delete m_notes;
if (m_soundProperties) delete m_soundProperties;
if (m_navigationTags) delete m_navigationTags;
}
//-----------------------------------------------------------------------------
@ -1337,6 +1340,8 @@ void TXsheet::loadData(TIStream &is) {
m_imp->copyFoldedState();
} else if (tagName == "noteSet") {
m_notes->loadData(is);
} else if (tagName == "navigationTags") {
m_navigationTags->loadData(is);
} else {
throw TException("xsheet, unknown tag: " + tagName);
}
@ -1386,6 +1391,13 @@ void TXsheet::saveData(TOStream &os) {
notes->saveData(os);
os.closeChild();
}
NavigationTags *navigationTags = getNavigationTags();
if (navigationTags->getCount() > 0) {
os.openChild("navigationTags");
navigationTags->saveData(os);
os.closeChild();
}
}
//-----------------------------------------------------------------------------
@ -2049,3 +2061,22 @@ void TXsheet::convertToExplicitHolds() {
}
}
}
//---------------------------------------------------------
bool TXsheet::isFrameTagged(int frame) const {
if (frame < 0) return false;
return m_navigationTags->isTagged(frame);
}
//---------------------------------------------------------
void TXsheet::toggleTaggedFrame(int frame) {
if (frame < 0) return;
if (isFrameTagged(frame))
m_navigationTags->removeTag(frame);
else
m_navigationTags->addTag(frame);
}