File indexing completed on 2024-05-05 04:54:13
0001 /* 0002 SPDX-FileCopyrightText: 2017 Nicolas Carion 0003 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 */ 0005 0006 #include "timelinetabs.hpp" 0007 #include "assets/model/assetparametermodel.hpp" 0008 #include "audiomixer/mixermanager.hpp" 0009 #include "bin/projectclip.h" 0010 #include "bin/projectitemmodel.h" 0011 #include "core.h" 0012 #include "doc/docundostack.hpp" 0013 #include "doc/kdenlivedoc.h" 0014 #include "mainwindow.h" 0015 #include "monitor/monitor.h" 0016 #include "monitor/monitormanager.h" 0017 #include "monitor/monitorproxy.h" 0018 #include "project/projectmanager.h" 0019 #include "timelinecontroller.h" 0020 #include "timelinewidget.h" 0021 0022 #include <KMessageBox> 0023 #include <KXMLGUIFactory> 0024 #include <QInputDialog> 0025 #include <QMenu> 0026 #include <QQmlContext> 0027 0028 TimelineContainer::TimelineContainer(QWidget *parent) 0029 : QWidget(parent) 0030 { 0031 } 0032 0033 QSize TimelineContainer::sizeHint() const 0034 { 0035 return QSize(800, pCore->window()->height() / 2); 0036 } 0037 0038 TimelineTabs::TimelineTabs(QWidget *parent) 0039 : QTabWidget(parent) 0040 , m_activeTimeline(nullptr) 0041 { 0042 setTabBarAutoHide(true); 0043 setTabsClosable(false); 0044 setDocumentMode(true); 0045 setMovable(true); 0046 QToolButton *pb = new QToolButton(this); 0047 pb->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0048 pb->setAutoRaise(true); 0049 pb->setToolTip(i18n("Add Timeline Sequence")); 0050 pb->setWhatsThis( 0051 i18n("Add Timeline Sequence. This will create a new timeline for editing. Each timeline corresponds to a Sequence Clip in the Project Bin")); 0052 connect(pb, &QToolButton::clicked, [=]() { pCore->triggerAction(QStringLiteral("add_playlist_clip")); }); 0053 setCornerWidget(pb); 0054 connect(this, &TimelineTabs::currentChanged, this, &TimelineTabs::connectCurrent); 0055 connect(this, &TimelineTabs::tabCloseRequested, this, &TimelineTabs::closeTimelineByIndex); 0056 connect(tabBar(), &QTabBar::tabBarDoubleClicked, this, &TimelineTabs::onTabBarDoubleClicked); 0057 } 0058 0059 TimelineTabs::~TimelineTabs() 0060 { 0061 // clear source 0062 for (int i = 0; i < count(); i++) { 0063 TimelineWidget *timeline = static_cast<TimelineWidget *>(widget(i)); 0064 timeline->setSource(QUrl()); 0065 }; 0066 } 0067 0068 void TimelineTabs::updateWindowTitle() 0069 { 0070 // Show current timeline name in Window title if we have multiple sequences but only one opened 0071 if (m_activeTimeline == nullptr || pCore->currentDoc()->closing) { 0072 return; 0073 } 0074 if (count() == 1 && pCore->projectItemModel()->sequenceCount() > 1) { 0075 pCore->window()->setWindowTitle(pCore->currentDoc()->description(KLocalizedString::removeAcceleratorMarker(tabText(0)))); 0076 m_activeTimeline->model()->updateVisibleSequenceName(tabText(0)); 0077 } else { 0078 pCore->window()->setWindowTitle(pCore->currentDoc()->description()); 0079 m_activeTimeline->model()->updateVisibleSequenceName(QString()); 0080 } 0081 } 0082 0083 bool TimelineTabs::raiseTimeline(const QUuid &uuid) 0084 { 0085 QMutexLocker lk(&m_lock); 0086 for (int i = 0; i < count(); i++) { 0087 TimelineWidget *timeline = static_cast<TimelineWidget *>(widget(i)); 0088 if (timeline->getUuid() == uuid) { 0089 if (i != currentIndex()) { 0090 setCurrentIndex(i); 0091 } 0092 return true; 0093 } 0094 } 0095 return false; 0096 } 0097 0098 void TimelineTabs::setModified(const QUuid &uuid, bool modified) 0099 { 0100 for (int i = 0; i < count(); i++) { 0101 TimelineWidget *timeline = static_cast<TimelineWidget *>(widget(i)); 0102 if (timeline->getUuid() == uuid) { 0103 setTabIcon(i, modified ? QIcon::fromTheme(QStringLiteral("document-save")) : QIcon()); 0104 break; 0105 } 0106 } 0107 } 0108 0109 TimelineWidget *TimelineTabs::addTimeline(const QUuid uuid, const QString &tabName, std::shared_ptr<TimelineItemModel> timelineModel, MonitorProxy *proxy) 0110 { 0111 QMutexLocker lk(&m_lock); 0112 if (count() == 1 && m_activeTimeline) { 0113 m_activeTimeline->model()->updateVisibleSequenceName(QString()); 0114 } 0115 disconnect(this, &TimelineTabs::currentChanged, this, &TimelineTabs::connectCurrent); 0116 TimelineWidget *newTimeline = new TimelineWidget(uuid, this); 0117 newTimeline->setTimelineMenu(m_timelineClipMenu, m_timelineCompositionMenu, m_timelineMenu, m_guideMenu, m_timelineRulerMenu, m_editGuideAction, 0118 m_headerMenu, m_thumbsMenu, m_timelineSubtitleClipMenu); 0119 newTimeline->setModel(timelineModel, proxy); 0120 int newIndex = addTab(newTimeline, tabName); 0121 setCurrentIndex(newIndex); 0122 connectCurrent(newIndex); 0123 setTabsClosable(count() > 1); 0124 if (count() == 2) { 0125 updateWindowTitle(); 0126 } 0127 connect(this, &TimelineTabs::currentChanged, this, &TimelineTabs::connectCurrent); 0128 return newTimeline; 0129 } 0130 0131 void TimelineTabs::connectCurrent(int ix) 0132 { 0133 QUuid previousTab = QUuid(); 0134 if (m_activeTimeline && m_activeTimeline->model()) { 0135 previousTab = m_activeTimeline->getUuid(); 0136 qDebug() << "===== DISCONNECTING PREVIOUS: " << previousTab; 0137 pCore->window()->disableMulticam(); 0138 int pos = pCore->getMonitorPosition(); 0139 m_activeTimeline->model()->updateDuration(); 0140 int duration = m_activeTimeline->model()->duration(); 0141 m_activeTimeline->controller()->saveSequenceProperties(); 0142 pCore->bin()->updateSequenceClip(previousTab, duration, pos); 0143 pCore->window()->disconnectTimeline(m_activeTimeline); 0144 disconnectTimeline(m_activeTimeline); 0145 } else { 0146 qDebug() << "==== NO PREVIOUS TIMELINE"; 0147 } 0148 if (ix < 0 || ix >= count() || pCore->currentDoc()->closing) { 0149 m_activeTimeline = nullptr; 0150 qDebug() << "==== ABORTING NO TIMELINE AVAILABLE"; 0151 return; 0152 } 0153 m_activeTimeline = static_cast<TimelineWidget *>(widget(ix)); 0154 if (m_activeTimeline->model() == nullptr || m_activeTimeline->model()->m_closing) { 0155 // Closing app 0156 qDebug() << "++++++++++++\n\nCLOSING APP\n\n+++++++++++++"; 0157 return; 0158 } 0159 pCore->window()->connectTimeline(); 0160 connectTimeline(m_activeTimeline); 0161 updateWindowTitle(); 0162 if (!m_activeTimeline->model()->isLoading) { 0163 pCore->bin()->sequenceActivated(); 0164 } 0165 } 0166 0167 void TimelineTabs::renameTab(const QUuid &uuid, const QString &name) 0168 { 0169 qDebug() << "==== READY TO RENAME!!!!!!!!!"; 0170 for (int i = 0; i < count(); i++) { 0171 if (static_cast<TimelineWidget *>(widget(i))->getUuid() == uuid) { 0172 tabBar()->setTabText(i, name); 0173 pCore->projectManager()->setTimelinePropery(uuid, QStringLiteral("kdenlive:clipname"), name); 0174 updateWindowTitle(); 0175 break; 0176 } 0177 } 0178 } 0179 0180 void TimelineTabs::closeTimelineByIndex(int ix) 0181 { 0182 TimelineWidget *timeline = static_cast<TimelineWidget *>(widget(ix)); 0183 if (timeline == m_activeTimeline) { 0184 Q_EMIT timeline->model()->requestClearAssetView(-1); 0185 pCore->clearTimeRemap(); 0186 pCore->mixer()->unsetModel(); 0187 pCore->window()->disableMulticam(); 0188 m_activeTimeline->model()->updateDuration(); 0189 timeline->controller()->saveSequenceProperties(); 0190 } 0191 const QString seqName = tabText(ix); 0192 std::shared_ptr<TimelineItemModel> model = timeline->model(); 0193 const QUuid uuid = timeline->getUuid(); 0194 const QString id = pCore->projectItemModel()->getSequenceId(uuid); 0195 Fun undo = [uuid, id, model]() { 0196 model->registerTimeline(); 0197 return pCore->projectManager()->openTimeline(id, uuid, -1, false, model); 0198 }; 0199 Fun redo = [this, ix, uuid]() { 0200 pCore->projectManager()->closeTimeline(uuid, false, false); 0201 TimelineWidget *timeline = static_cast<TimelineWidget *>(widget(ix)); 0202 timeline->blockSignals(true); 0203 timeline->setSource(QUrl()); 0204 if (timeline == m_activeTimeline) { 0205 pCore->window()->disconnectTimeline(timeline); 0206 disconnectTimeline(timeline); 0207 } 0208 timeline->unsetModel(); 0209 if (m_activeTimeline == timeline) { 0210 m_activeTimeline = nullptr; 0211 } 0212 delete timeline; 0213 updateWindowTitle(); 0214 return true; 0215 }; 0216 redo(); 0217 pCore->pushUndo(undo, redo, i18n("Close %1", seqName)); 0218 } 0219 0220 TimelineWidget *TimelineTabs::getCurrentTimeline() const 0221 { 0222 return m_activeTimeline; 0223 } 0224 0225 void TimelineTabs::closeTimelines() 0226 { 0227 for (int i = 0; i < count(); i++) { 0228 static_cast<TimelineWidget *>(widget(i))->unsetModel(); 0229 } 0230 } 0231 0232 void TimelineTabs::closeTimelineTab(const QUuid uuid) 0233 { 0234 QMutexLocker lk(&m_lock); 0235 int currentCount = count(); 0236 for (int i = 0; i < currentCount; i++) { 0237 TimelineWidget *timeline = static_cast<TimelineWidget *>(widget(i)); 0238 if (uuid == timeline->getUuid()) { 0239 timeline->blockSignals(true); 0240 timeline->setSource(QUrl()); 0241 timeline->blockSignals(true); 0242 if (timeline == m_activeTimeline) { 0243 Q_EMIT showSubtitle(-1); 0244 pCore->window()->disconnectTimeline(timeline); 0245 disconnectTimeline(timeline); 0246 } 0247 timeline->unsetModel(); 0248 if (m_activeTimeline == timeline) { 0249 m_activeTimeline = nullptr; 0250 } 0251 delete timeline; 0252 // pCore->projectManager()->closeTimeline(uuid); 0253 setTabsClosable(count() > 1); 0254 if (currentCount == 2) { 0255 updateWindowTitle(); 0256 } 0257 break; 0258 } 0259 } 0260 } 0261 0262 void TimelineTabs::connectTimeline(TimelineWidget *timeline) 0263 { 0264 connect(timeline, &TimelineWidget::focusProjectMonitor, pCore->monitorManager(), &MonitorManager::focusProjectMonitor, Qt::DirectConnection); 0265 connect(this, &TimelineTabs::audioThumbFormatChanged, timeline->controller(), &TimelineController::audioThumbFormatChanged); 0266 connect(this, &TimelineTabs::showThumbnailsChanged, timeline->controller(), &TimelineController::showThumbnailsChanged); 0267 connect(this, &TimelineTabs::showAudioThumbnailsChanged, timeline->controller(), &TimelineController::showAudioThumbnailsChanged); 0268 connect(this, &TimelineTabs::changeZoom, timeline, &TimelineWidget::slotChangeZoom); 0269 connect(this, &TimelineTabs::fitZoom, timeline, &TimelineWidget::slotFitZoom); 0270 connect(timeline->controller(), &TimelineController::showTransitionModel, this, &TimelineTabs::showTransitionModel); 0271 connect(timeline->controller(), &TimelineController::showMixModel, this, &TimelineTabs::showMixModel); 0272 connect(timeline->controller(), &TimelineController::updateZoom, this, [&](double value) { Q_EMIT updateZoom(getCurrentTimeline()->zoomForScale(value)); }); 0273 connect(timeline->controller(), &TimelineController::showItemEffectStack, this, &TimelineTabs::showItemEffectStack); 0274 connect(timeline->controller(), &TimelineController::showSubtitle, this, &TimelineTabs::showSubtitle); 0275 connect(timeline->controller(), &TimelineController::updateAssetPosition, this, &TimelineTabs::updateAssetPosition); 0276 connect(timeline->controller(), &TimelineController::centerView, timeline, &TimelineWidget::slotCenterView); 0277 0278 connect(pCore->monitorManager()->projectMonitor(), &Monitor::zoneUpdated, m_activeTimeline, &TimelineWidget::zoneUpdated); 0279 connect(pCore->monitorManager()->projectMonitor(), &Monitor::zoneUpdatedWithUndo, m_activeTimeline, &TimelineWidget::zoneUpdatedWithUndo); 0280 connect(m_activeTimeline, &TimelineWidget::zoneMoved, pCore->monitorManager()->projectMonitor(), &Monitor::slotLoadClipZone); 0281 connect(pCore->monitorManager()->projectMonitor(), &Monitor::addTimelineEffect, m_activeTimeline->controller(), 0282 &TimelineController::addEffectToCurrentClip); 0283 timeline->rootContext()->setContextProperty("proxy", pCore->monitorManager()->projectMonitor()->getControllerProxy()); 0284 Q_EMIT timeline->controller()->selectionChanged(); 0285 } 0286 0287 void TimelineTabs::disconnectTimeline(TimelineWidget *timeline) 0288 { 0289 timeline->rootContext()->setContextProperty("proxy", nullptr); 0290 disconnect(timeline, &TimelineWidget::focusProjectMonitor, pCore->monitorManager(), &MonitorManager::focusProjectMonitor); 0291 disconnect(this, &TimelineTabs::audioThumbFormatChanged, timeline->controller(), &TimelineController::audioThumbFormatChanged); 0292 disconnect(this, &TimelineTabs::showThumbnailsChanged, timeline->controller(), &TimelineController::showThumbnailsChanged); 0293 disconnect(this, &TimelineTabs::showAudioThumbnailsChanged, timeline->controller(), &TimelineController::showAudioThumbnailsChanged); 0294 disconnect(this, &TimelineTabs::changeZoom, timeline, &TimelineWidget::slotChangeZoom); 0295 disconnect(this, &TimelineTabs::fitZoom, timeline, &TimelineWidget::slotFitZoom); 0296 disconnect(timeline->controller(), &TimelineController::showTransitionModel, this, &TimelineTabs::showTransitionModel); 0297 disconnect(timeline->controller(), &TimelineController::showMixModel, this, &TimelineTabs::showMixModel); 0298 disconnect(timeline->controller(), &TimelineController::showItemEffectStack, this, &TimelineTabs::showItemEffectStack); 0299 disconnect(timeline->controller(), &TimelineController::showSubtitle, this, &TimelineTabs::showSubtitle); 0300 disconnect(timeline->controller(), &TimelineController::updateAssetPosition, this, &TimelineTabs::updateAssetPosition); 0301 0302 disconnect(pCore->monitorManager()->projectMonitor(), &Monitor::zoneUpdated, timeline, &TimelineWidget::zoneUpdated); 0303 disconnect(pCore->monitorManager()->projectMonitor(), &Monitor::zoneUpdatedWithUndo, timeline, &TimelineWidget::zoneUpdatedWithUndo); 0304 disconnect(timeline, &TimelineWidget::zoneMoved, pCore->monitorManager()->projectMonitor(), &Monitor::slotLoadClipZone); 0305 disconnect(pCore->monitorManager()->projectMonitor(), &Monitor::addTimelineEffect, timeline->controller(), &TimelineController::addEffectToCurrentClip); 0306 } 0307 0308 void TimelineTabs::buildClipMenu() 0309 { 0310 // Timeline clip menu 0311 delete m_timelineClipMenu; 0312 m_timelineClipMenu = new QMenu(this); 0313 KActionCollection *coll = pCore->window()->actionCollection(); 0314 m_timelineClipMenu->addAction(coll->action(QStringLiteral("edit_copy"))); 0315 m_timelineClipMenu->addAction(coll->action(QStringLiteral("paste_effects"))); 0316 m_timelineClipMenu->addAction(coll->action(QStringLiteral("delete_effects"))); 0317 m_timelineClipMenu->addAction(coll->action(QStringLiteral("group_clip"))); 0318 m_timelineClipMenu->addAction(coll->action(QStringLiteral("ungroup_clip"))); 0319 m_timelineClipMenu->addAction(coll->action(QStringLiteral("edit_item_duration"))); 0320 m_timelineClipMenu->addAction(coll->action(QStringLiteral("clip_split"))); 0321 m_timelineClipMenu->addAction(coll->action(QStringLiteral("clip_switch"))); 0322 m_timelineClipMenu->addAction(coll->action(QStringLiteral("delete_timeline_clip"))); 0323 m_timelineClipMenu->addAction(coll->action(QStringLiteral("extract_clip"))); 0324 m_timelineClipMenu->addAction(coll->action(QStringLiteral("save_to_bin"))); 0325 m_timelineClipMenu->addAction(coll->action(QStringLiteral("send_sequence"))); 0326 0327 QMenu *markerMenu = static_cast<QMenu *>(pCore->window()->factory()->container(QStringLiteral("marker_menu"), pCore->window())); 0328 m_timelineClipMenu->addMenu(markerMenu); 0329 0330 m_timelineClipMenu->addAction(coll->action(QStringLiteral("set_audio_align_ref"))); 0331 m_timelineClipMenu->addAction(coll->action(QStringLiteral("align_audio"))); 0332 m_timelineClipMenu->addAction(coll->action(QStringLiteral("edit_item_speed"))); 0333 m_timelineClipMenu->addAction(coll->action(QStringLiteral("edit_item_remap"))); 0334 m_timelineClipMenu->addAction(coll->action(QStringLiteral("clip_in_project_tree"))); 0335 m_timelineClipMenu->addAction(coll->action(QStringLiteral("cut_timeline_clip"))); 0336 } 0337 0338 void TimelineTabs::setTimelineMenu(QMenu *compositionMenu, QMenu *timelineMenu, QMenu *guideMenu, QMenu *timelineRulerMenu, QAction *editGuideAction, 0339 QMenu *headerMenu, QMenu *thumbsMenu, QMenu *subtitleClipMenu) 0340 { 0341 buildClipMenu(); 0342 m_timelineCompositionMenu = compositionMenu; 0343 m_timelineMenu = timelineMenu; 0344 m_timelineRulerMenu = timelineRulerMenu; 0345 m_guideMenu = guideMenu; 0346 m_headerMenu = headerMenu; 0347 m_thumbsMenu = thumbsMenu; 0348 m_headerMenu->addMenu(m_thumbsMenu); 0349 m_timelineSubtitleClipMenu = subtitleClipMenu; 0350 m_editGuideAction = editGuideAction; 0351 } 0352 0353 const QStringList TimelineTabs::openedSequences() 0354 { 0355 QStringList result; 0356 for (int i = 0; i < count(); i++) { 0357 result << static_cast<TimelineWidget *>(widget(i))->getUuid().toString(); 0358 } 0359 return result; 0360 } 0361 0362 TimelineWidget *TimelineTabs::getTimeline(const QUuid uuid) const 0363 { 0364 for (int i = 0; i < count(); i++) { 0365 TimelineWidget *tl = static_cast<TimelineWidget *>(widget(i)); 0366 if (tl->getUuid() == uuid) { 0367 return tl; 0368 } 0369 } 0370 return nullptr; 0371 } 0372 0373 void TimelineTabs::slotNextSequence() 0374 { 0375 int max = count(); 0376 int focus = currentIndex() + 1; 0377 if (focus >= max) { 0378 focus = 0; 0379 } 0380 setCurrentIndex(focus); 0381 } 0382 0383 void TimelineTabs::slotPreviousSequence() 0384 { 0385 int focus = currentIndex() - 1; 0386 if (focus < 0) { 0387 focus = count() - 1; 0388 } 0389 setCurrentIndex(focus); 0390 } 0391 0392 void TimelineTabs::onTabBarDoubleClicked(int index) 0393 { 0394 const QString currentTabName = KLocalizedString::removeAcceleratorMarker(tabBar()->tabText(index)); 0395 bool ok = false; 0396 const QString newName = QInputDialog::getText(this, i18n("Rename Sequence"), i18n("Rename Sequence"), QLineEdit::Normal, currentTabName, &ok); 0397 if (ok && !newName.isEmpty()) { 0398 TimelineWidget *timeline = static_cast<TimelineWidget *>(widget(index)); 0399 if (timeline) { 0400 const QString id = pCore->projectItemModel()->getSequenceId(timeline->getUuid()); 0401 std::shared_ptr<ProjectClip> clip = pCore->projectItemModel()->getClipByBinID(id); 0402 if (clip) { 0403 clip->rename(newName, 0); 0404 } 0405 } 0406 } 0407 }