1 #include <memory>
2 
3 #include "timestretchpopup.h"
4 
5 // Tnz6 includes
6 #include "filmstripcommand.h"
7 #include "cellselection.h"
8 #include "tapp.h"
9 #include "menubarcommandids.h"
10 
11 // TnzQt includes
12 #include "toonzqt/tselectionhandle.h"
13 #include "historytypes.h"
14 
15 // TnzLib includes
16 #include "toonz/tscenehandle.h"
17 #include "toonz/txsheet.h"
18 #include "toonz/txsheethandle.h"
19 #include "toonz/txshcell.h"
20 #include "toonz/txshcolumn.h"
21 
22 // TnzCore includes
23 #include "tundo.h"
24 
25 // Qt includes
26 #include <QPushButton>
27 #include <QLabel>
28 #include <QHBoxLayout>
29 #include <QComboBox>
30 #include <QMainWindow>
31 
32 //=============================================================================
33 // TimeStretch
34 //-----------------------------------------------------------------------------
35 
36 //-----------------------------------------------------------------------------
37 namespace {
38 //-----------------------------------------------------------------------------
39 
40 class TimeStretchUndo final : public TUndo {
41   int m_r0, m_r1;
42   int m_c0, m_c1;
43   int m_newRange;
44   std::unique_ptr<TXshCell[]> m_cells;
45 
46   // servono per modificare la selezione
47   TimeStretchPopup::STRETCH_TYPE m_type;
48   int m_c0Old;
49   int m_c1Old;
50 
51 public:
TimeStretchUndo(int r0,int c0,int r1,int c1,int newRange,TimeStretchPopup::STRETCH_TYPE type)52   TimeStretchUndo(int r0, int c0, int r1, int c1, int newRange,
53                   TimeStretchPopup::STRETCH_TYPE type)
54       : m_r0(r0)
55       , m_c0(c0)
56       , m_r1(r1)
57       , m_c1(c1)
58       , m_newRange(newRange)
59       , m_type(type)
60       , m_c0Old(0)
61       , m_c1Old(0) {
62     int nr = m_r1 - m_r0 + 1;
63     int nc = m_c1 - m_c0 + 1;
64     assert(nr > 0 && nc > 0);
65     m_cells.reset(new TXshCell[nr * nc]);
66     assert(m_cells);
67     int k = 0;
68     for (int c = c0; c <= c1; c++)
69       for (int r = r0; r <= r1; r++)
70         m_cells[k++] =
71             TApp::instance()->getCurrentXsheet()->getXsheet()->getCell(r, c);
72   }
73 
~TimeStretchUndo()74   ~TimeStretchUndo() {}
75 
setOldColumnRange(int c0,int c1)76   void setOldColumnRange(int c0, int c1) {
77     m_c0Old = c0;
78     m_c1Old = c1;
79   }
80 
undo() const81   void undo() const override {
82     TApp *app    = TApp::instance();
83     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
84     int oldNr    = m_newRange;
85     int nr       = m_r1 - m_r0 + 1;
86 
87     if (nr > oldNr)  // Se avevo cancellato delle celle devo reinserirle
88     {
89       int c;
90       for (c = m_c0; c <= m_c1; c++) {
91         int dn = nr - oldNr;
92         xsh->insertCells(m_r0, c, dn);
93         int i;
94         for (i = 0; i < nr; i++)
95           xsh->setCell(i + m_r0, c, m_cells[i + nr * (c - m_c0)]);
96       }
97     } else  // Altrimenti devo rimuoverle
98     {
99       int c;
100       for (c = m_c0; c <= m_c1; c++) {
101         int i;
102         for (i = 0; i < nr; i++)
103           xsh->setCell(i + m_r0, c, m_cells[i + nr * (c - m_c0)]);
104         int dn = oldNr - nr;
105         xsh->removeCells(m_r1, c, dn);
106       }
107     }
108 
109     TCellSelection *selection = dynamic_cast<TCellSelection *>(
110         app->getCurrentSelection()->getSelection());
111     if (selection) {
112       if (m_type == TimeStretchPopup::eRegion)
113         selection->selectCells(m_r0, m_c0, m_r1, m_c1);
114       else if (m_type == TimeStretchPopup::eFrameRange)
115         selection->selectCells(m_r0, m_c0Old, m_r1, m_c1Old);
116     }
117 
118     app->getCurrentXsheet()->notifyXsheetChanged();
119     app->getCurrentXsheet()->notifyXsheetSoundChanged();
120   }
121 
redo() const122   void redo() const override {
123     if (m_r1 - m_r0 < 0 || m_c1 - m_c0 < 0) return;
124     TApp *app = TApp::instance();
125     app->getCurrentXsheet()->getXsheet()->timeStretch(m_r0, m_c0, m_r1, m_c1,
126                                                       m_newRange);
127 
128     TCellSelection *selection = dynamic_cast<TCellSelection *>(
129         app->getCurrentSelection()->getSelection());
130     if (selection) {
131       if (m_type == TimeStretchPopup::eRegion)
132         selection->selectCells(m_r0, m_c0, m_r0 + m_newRange - 1, m_c1);
133       else if (m_type == TimeStretchPopup::eFrameRange)
134         selection->selectCells(m_r0, m_c0Old, m_r0 + m_newRange - 1, m_c1Old);
135     }
136     app->getCurrentXsheet()->notifyXsheetChanged();
137     app->getCurrentXsheet()->notifyXsheetSoundChanged();
138   }
139 
getSize() const140   int getSize() const override { return sizeof(*this); }
141 
getHistoryString()142   QString getHistoryString() override { return QObject::tr("Time Stretch"); }
getHistoryType()143   int getHistoryType() override { return HistoryType::Xsheet; }
144 };
145 
146 //-----------------------------------------------------------------------------
147 
timeStretch(int newRange,TimeStretchPopup::STRETCH_TYPE type)148 void timeStretch(int newRange, TimeStretchPopup::STRETCH_TYPE type) {
149   TCellSelection::Range range;
150   TCellSelection *selection = dynamic_cast<TCellSelection *>(
151       TApp::instance()->getCurrentSelection()->getSelection());
152   if (type != TimeStretchPopup::eWholeXsheet) {
153     if (!selection) {
154       DVGui::error(QObject::tr("The current selection is invalid."));
155       return;
156     }
157     range = selection->getSelectedCells();
158   }
159   int r0 = range.m_r0;
160   int r1 = range.m_r1;
161   int c0 = 0, c1 = 0;
162   TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
163   if (type == TimeStretchPopup::eRegion) {
164     c0 = range.m_c0;
165     c1 = range.m_c1;
166   } else {
167     if (type == TimeStretchPopup::eWholeXsheet) {
168       r0 = 0;
169       r1 = xsheet->getFrameCount() - 1;
170     }
171     c1 = xsheet->getColumnCount() - 1;
172     int c;
173     for (c = c1; c >= c0; c--)
174       if (!xsheet->getColumn(c) && !xsheet->getColumn(c)->isEmpty()) break;
175   }
176   assert(r1 >= r0 && c1 >= c0);
177   if (r1 - r0 + 1 == newRange) return;
178 
179   TimeStretchUndo *undo = new TimeStretchUndo(r0, c0, r1, c1, newRange, type);
180 
181   xsheet->timeStretch(r0, c0, r1, c1, newRange);
182 
183   if (type == TimeStretchPopup::eFrameRange)
184     undo->setOldColumnRange(range.m_c0, range.m_c1);
185   TUndoManager::manager()->add(undo);
186 
187   // Modifico la selezione in base al tipo di comando effettuato
188   if (type != TimeStretchPopup::eWholeXsheet) {
189     assert(selection);
190     selection->selectCells(r0, range.m_c0, r0 + newRange - 1, range.m_c1);
191   }
192 
193   TApp::instance()->getCurrentScene()->setDirtyFlag(true);
194   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
195   TApp::instance()->getCurrentXsheet()->notifyXsheetSoundChanged();
196 }
197 
198 //-----------------------------------------------------------------------------
199 }  // namespace
200 //-----------------------------------------------------------------------------
201 
202 //=============================================================================
203 // TimeStretchPopup
204 //-----------------------------------------------------------------------------
205 
TimeStretchPopup()206 TimeStretchPopup::TimeStretchPopup()
207     : Dialog(TApp::instance()->getMainWindow(), true, true, "TimeStretch")
208     , m_currentStretchType(eRegion) {
209   setModal(false);
210   setWindowTitle(tr("Time Stretch"));
211 
212   beginVLayout();
213 
214   m_stretchType = new QComboBox(this);
215   m_stretchType->setFixedHeight(DVGui::WidgetHeight);
216   QStringList viewType;
217   viewType << tr("Selected Cells") << tr("Selected Frame Range")
218            << tr("Whole Xsheet");
219   m_stretchType->addItems(viewType);
220   connect(m_stretchType, SIGNAL(currentIndexChanged(int)),
221           SLOT(setCurrentStretchType(int)));
222   addWidget(tr("Stretch:"), m_stretchType);
223 
224   QHBoxLayout *rangeLayout = new QHBoxLayout(this);
225   m_oldRange               = new QLabel("0", this);
226   m_oldRange->setFixedSize(43, DVGui::WidgetHeight);
227   rangeLayout->addWidget(m_oldRange);
228   rangeLayout->addSpacing(10);
229   m_newRangeFld = new DVGui::IntLineEdit(this);
230   rangeLayout->addWidget(new QLabel(tr("New Range:")), 1, Qt::AlignRight);
231   rangeLayout->addWidget(m_newRangeFld, 0, Qt::AlignRight);
232   addLayout(tr("Old Range:"), rangeLayout);
233 
234   endVLayout();
235 
236   m_okBtn = new QPushButton(tr("Stretch"), this);
237   m_okBtn->setDefault(true);
238   m_cancelBtn = new QPushButton(tr("Cancel"), this);
239   bool ret    = connect(m_okBtn, SIGNAL(clicked()), this, SLOT(stretch()));
240   ret = ret && connect(m_cancelBtn, SIGNAL(clicked()), this, SLOT(reject()));
241   assert(ret);
242 
243   addButtonBarWidget(m_okBtn, m_cancelBtn);
244 }
245 
246 //-------------------------------------------------------------------
247 
showEvent(QShowEvent *)248 void TimeStretchPopup::showEvent(QShowEvent *) {
249   TSelectionHandle *selectionHandle = TApp::instance()->getCurrentSelection();
250   bool ret = connect(selectionHandle, SIGNAL(selectionChanged(TSelection *)),
251                      this, SLOT(updateValues(TSelection *)));
252   ret = ret && connect(selectionHandle,
253                        SIGNAL(selectionSwitched(TSelection *, TSelection *)),
254                        this, SLOT(updateValues(TSelection *, TSelection *)));
255   TXsheetHandle *xsheetHandle = TApp::instance()->getCurrentXsheet();
256   ret = ret && connect(xsheetHandle, SIGNAL(xsheetChanged()), this,
257                        SLOT(updateValues()));
258   assert(ret);
259   updateValues(selectionHandle->getSelection());
260 }
261 
262 //-------------------------------------------------------------------
263 
hideEvent(QHideEvent * e)264 void TimeStretchPopup::hideEvent(QHideEvent *e) {
265   TSelectionHandle *selectionHandle = TApp::instance()->getCurrentSelection();
266   bool ret = disconnect(selectionHandle, SIGNAL(selectionChanged(TSelection *)),
267                         this, SLOT(updateValues(TSelection *)));
268   ret = ret && connect(selectionHandle,
269                        SIGNAL(selectionSwitched(TSelection *, TSelection *)),
270                        this, SLOT(updateValues(TSelection *, TSelection *)));
271   TXsheetHandle *xsheetHandle = TApp::instance()->getCurrentXsheet();
272   ret = ret && disconnect(xsheetHandle, SIGNAL(xsheetChanged()), this,
273                           SLOT(updateValues()));
274   assert(ret);
275   Dialog::hideEvent(e);
276 }
277 //-------------------------------------------------------------------
278 
updateValues()279 void TimeStretchPopup::updateValues() {
280   updateValues(TApp::instance()->getCurrentSelection()->getSelection());
281 }
282 
283 //-------------------------------------------------------------------
284 
updateValues(TSelection * selection)285 void TimeStretchPopup::updateValues(TSelection *selection) {
286   int from = 0, to = 0, newRange = 0;
287   if (m_currentStretchType == eWholeXsheet) {
288     TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
289     newRange        = xsheet->getFrameCount();
290   } else {
291     TCellSelection *cellCelection = dynamic_cast<TCellSelection *>(selection);
292     if (cellCelection) {
293       int c0, c1;
294       cellCelection->getSelectedCells(from, c0, to, c1);
295       newRange = to - from + 1;
296     }
297   }
298 
299   m_newRangeFld->setText(QString::number(newRange));
300   m_oldRange->setText(QString::number(newRange));
301 }
302 
303 //-------------------------------------------------------------------
304 
setCurrentStretchType(int index)305 void TimeStretchPopup::setCurrentStretchType(int index) {
306   if (m_currentStretchType == STRETCH_TYPE(index)) return;
307   m_currentStretchType = STRETCH_TYPE(index);
308   updateValues();
309 }
310 
311 //-------------------------------------------------------------------
312 
stretch()313 void TimeStretchPopup::stretch() {
314   timeStretch(m_newRangeFld->text().toInt(), m_currentStretchType);
315   accept();
316 }
317