übernahme Code Shortcut
This commit is contained in:
2984
src/docks/encodedock.cpp
Normal file
2984
src/docks/encodedock.cpp
Normal file
File diff suppressed because it is too large
Load Diff
205
src/docks/encodedock.h
Normal file
205
src/docks/encodedock.h
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2026 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ENCODEDOCK_H
|
||||
#define ENCODEDOCK_H
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#include <MltProperties.h>
|
||||
#include <QDockWidget>
|
||||
#include <QDomElement>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStandardItemModel>
|
||||
#include <QStringList>
|
||||
|
||||
class QTreeWidgetItem;
|
||||
class QTemporaryFile;
|
||||
namespace Ui {
|
||||
class EncodeDock;
|
||||
}
|
||||
class AbstractJob;
|
||||
class MeltJob;
|
||||
namespace Mlt {
|
||||
class Service;
|
||||
class Producer;
|
||||
class Filter;
|
||||
} // namespace Mlt
|
||||
|
||||
class PresetsProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
protected:
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
|
||||
};
|
||||
|
||||
class EncodeDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EncodeDock(QWidget *parent = 0);
|
||||
~EncodeDock();
|
||||
|
||||
void loadPresetFromProperties(Mlt::Properties &);
|
||||
bool isExportInProgress() const;
|
||||
|
||||
signals:
|
||||
void captureStateChanged(bool);
|
||||
void createOrEditFilterOnOutput(Mlt::Filter *, const QStringList & = {});
|
||||
|
||||
public slots:
|
||||
void onAudioChannelsChanged();
|
||||
void onProducerOpened();
|
||||
void onProfileChanged();
|
||||
void onReframeChanged();
|
||||
void on_hwencodeButton_clicked();
|
||||
bool detectHardwareEncoders();
|
||||
|
||||
private slots:
|
||||
void on_presetsTree_clicked(const QModelIndex &index);
|
||||
void on_presetsTree_activated(const QModelIndex &index);
|
||||
|
||||
void on_encodeButton_clicked();
|
||||
|
||||
void on_streamButton_clicked();
|
||||
|
||||
void on_addPresetButton_clicked();
|
||||
|
||||
void on_removePresetButton_clicked();
|
||||
|
||||
void onFinished(AbstractJob *, bool isSuccess);
|
||||
|
||||
void on_stopCaptureButton_clicked();
|
||||
|
||||
void on_videoRateControlCombo_activated(int index);
|
||||
|
||||
void on_audioRateControlCombo_activated(int index);
|
||||
|
||||
void on_scanModeCombo_currentIndexChanged(int index);
|
||||
|
||||
void on_presetsSearch_textChanged(const QString &search);
|
||||
|
||||
void on_resetButton_clicked();
|
||||
|
||||
void openCaptureFile();
|
||||
|
||||
void on_formatCombo_currentIndexChanged(int index);
|
||||
|
||||
void on_videoBufferDurationChanged();
|
||||
|
||||
void on_gopSpinner_valueChanged(int value);
|
||||
|
||||
void on_fromCombo_currentIndexChanged(int index);
|
||||
|
||||
void on_videoCodecCombo_currentIndexChanged(int index);
|
||||
|
||||
void on_audioCodecCombo_currentIndexChanged(int index);
|
||||
|
||||
void setAudioChannels(int channels);
|
||||
|
||||
void on_widthSpinner_editingFinished();
|
||||
|
||||
void on_heightSpinner_editingFinished();
|
||||
|
||||
void on_advancedButton_clicked(bool checked);
|
||||
|
||||
void on_hwencodeCheckBox_clicked(bool checked);
|
||||
|
||||
void on_hwdecodeCheckBox_clicked(bool checked);
|
||||
|
||||
void on_advancedCheckBox_clicked(bool checked);
|
||||
|
||||
void on_fpsSpinner_editingFinished();
|
||||
|
||||
void on_fpsComboBox_activated(int arg1);
|
||||
|
||||
void on_videoQualitySpinner_valueChanged(int vq);
|
||||
|
||||
void on_audioQualitySpinner_valueChanged(int aq);
|
||||
|
||||
void on_parallelCheckbox_clicked(bool checked);
|
||||
|
||||
void on_resolutionComboBox_activated(int arg1);
|
||||
|
||||
void on_reframeButton_clicked();
|
||||
|
||||
void on_aspectNumSpinner_valueChanged(int value);
|
||||
|
||||
void on_aspectDenSpinner_valueChanged(int value);
|
||||
|
||||
private:
|
||||
enum {
|
||||
RateControlAverage = 0,
|
||||
RateControlConstant,
|
||||
RateControlQuality,
|
||||
RateControlConstrained
|
||||
};
|
||||
enum {
|
||||
AudioChannels1 = 0,
|
||||
AudioChannels2,
|
||||
AudioChannels4,
|
||||
AudioChannels6,
|
||||
};
|
||||
Ui::EncodeDock *ui;
|
||||
Mlt::Properties *m_presets;
|
||||
QScopedPointer<MeltJob> m_immediateJob;
|
||||
QString m_extension;
|
||||
Mlt::Properties *m_profiles;
|
||||
PresetsProxyModel m_presetsModel;
|
||||
QStringList m_outputFilenames;
|
||||
bool m_isDefaultSettings;
|
||||
double m_fps;
|
||||
QStringList m_intraOnlyCodecs;
|
||||
QStringList m_losslessVideoCodecs;
|
||||
QStringList m_losslessAudioCodecs;
|
||||
|
||||
void loadPresets();
|
||||
Mlt::Properties *collectProperties(int realtime, bool includeProfile = false);
|
||||
void collectProperties(QDomElement &node, int realtime);
|
||||
void setSubtitleProperties(QDomElement &node, Mlt::Producer *service);
|
||||
QPoint addConsumerElement(
|
||||
Mlt::Producer *service, QDomDocument &dom, const QString &target, int realtime, int pass);
|
||||
MeltJob *convertReframe(Mlt::Producer *service,
|
||||
QTemporaryFile *tmp,
|
||||
const QString &target,
|
||||
int realtime,
|
||||
int pass,
|
||||
const QThread::Priority priority);
|
||||
MeltJob *createMeltJob(Mlt::Producer *service,
|
||||
const QString &target,
|
||||
int realtime,
|
||||
int pass = 0,
|
||||
const QThread::Priority priority = Settings.jobPriority());
|
||||
void runMelt(const QString &target, int realtime = -1);
|
||||
void enqueueAnalysis();
|
||||
void enqueueMelt(const QStringList &targets, int realtime);
|
||||
void encode(const QString &target);
|
||||
void resetOptions();
|
||||
Mlt::Producer *fromProducer(bool usePlaylistBin = false) const;
|
||||
static void filterCodecParams(const QString &vcodec, QStringList &other);
|
||||
void onVideoCodecComboChanged(int index, bool ignorePreset = false, bool resetBframes = true);
|
||||
bool checkForMissingFiles();
|
||||
QString &defaultFormatExtension();
|
||||
void initSpecialCodecLists();
|
||||
void setReframeEnabled(bool enabled);
|
||||
void showResampleWarning(const QString &message);
|
||||
void hideResampleWarning(bool hide = true);
|
||||
void checkFrameRate();
|
||||
void setResolutionAspectFromProfile();
|
||||
};
|
||||
|
||||
#endif // ENCODEDOCK_H
|
||||
2129
src/docks/encodedock.ui
Normal file
2129
src/docks/encodedock.ui
Normal file
File diff suppressed because it is too large
Load Diff
1400
src/docks/filesdock.cpp
Normal file
1400
src/docks/filesdock.cpp
Normal file
File diff suppressed because it is too large
Load Diff
111
src/docks/filesdock.h
Normal file
111
src/docks/filesdock.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILESDOCK_H
|
||||
#define FILESDOCK_H
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QFileSystemModel>
|
||||
#include <QHash>
|
||||
#include <QMutex>
|
||||
#include <QTimer>
|
||||
#include <QUndoCommand>
|
||||
|
||||
namespace Ui {
|
||||
class FilesDock;
|
||||
}
|
||||
|
||||
class QAbstractItemView;
|
||||
class QItemSelectionModel;
|
||||
class QMenu;
|
||||
class PlaylistIconView;
|
||||
class FilesModel;
|
||||
class FilesProxyModel;
|
||||
class QSortFilterProxyModel;
|
||||
class LineEditClear;
|
||||
class QLabel;
|
||||
|
||||
class FilesDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FilesDock(QWidget *parent = 0);
|
||||
~FilesDock();
|
||||
|
||||
struct CacheItem
|
||||
{
|
||||
int mediaType{-1}; // -1 = unknown
|
||||
};
|
||||
|
||||
int getCacheMediaType(const QString &key);
|
||||
void setCacheMediaType(const QString &key, int mediaType);
|
||||
|
||||
signals:
|
||||
void selectionChanged();
|
||||
|
||||
public slots:
|
||||
void onOpenActionTriggered();
|
||||
void changeDirectory(const QString &path, bool updateLocation = true);
|
||||
void changeFilesDirectory(const QModelIndex &index);
|
||||
|
||||
private slots:
|
||||
void viewCustomContextMenuRequested(const QPoint &pos);
|
||||
void onMediaTypeClicked();
|
||||
void onOpenOtherAdd();
|
||||
void onOpenOtherRemove();
|
||||
void clearStatus();
|
||||
void updateStatus();
|
||||
void onLocationsEditingFinished();
|
||||
|
||||
void on_locationsCombo_activated(int index);
|
||||
|
||||
void on_addLocationButton_clicked();
|
||||
|
||||
void on_removeLocationButton_clicked();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
void keyReleaseEvent(QKeyEvent *event);
|
||||
|
||||
private:
|
||||
void setupActions();
|
||||
void emitDataChanged(const QVector<int> &roles);
|
||||
void updateViewMode();
|
||||
void onUpdateThumbnailsActionTriggered();
|
||||
void onSelectAllActionTriggered();
|
||||
void incrementIndex(int step);
|
||||
void addOpenWithMenu(QMenu *menu);
|
||||
QString firstSelectedFilePath();
|
||||
QString firstSelectedMediaType();
|
||||
void openClip(const QString &filePath);
|
||||
|
||||
Ui::FilesDock *ui;
|
||||
QAbstractItemView *m_view;
|
||||
PlaylistIconView *m_iconsView;
|
||||
std::unique_ptr<QFileSystemModel> m_dirsModel;
|
||||
FilesModel *m_filesModel;
|
||||
QItemSelectionModel *m_selectionModel;
|
||||
QMenu *m_mainMenu;
|
||||
FilesProxyModel *m_filesProxyModel;
|
||||
QHash<QString, CacheItem> m_cache;
|
||||
QMutex m_cacheMutex;
|
||||
LineEditClear *m_searchField;
|
||||
QLabel *m_label;
|
||||
};
|
||||
|
||||
#endif // FILESDOCK_H
|
||||
210
src/docks/filesdock.ui
Normal file
210
src/docks/filesdock.ui
Normal file
@@ -0,0 +1,210 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FilesDock</class>
|
||||
<widget class="QDockWidget" name="FilesDock">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>460</width>
|
||||
<height>278</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Files</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="locationsLayout">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Policy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Location</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="locationsCombo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::SizeAdjustPolicy::AdjustToMinimumContentsLengthWithIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addLocationButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add the current folder to the saved locations</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-add" resource="../../icons/resources.qrc">
|
||||
<normaloff>:/icons/oxygen/32x32/actions/list-add.png</normaloff>:/icons/oxygen/32x32/actions/list-add.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeLocationButton">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove the selected location</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="list-remove" resource="../../icons/resources.qrc">
|
||||
<normaloff>:/icons/oxygen/32x32/actions/list-remove.png</normaloff>:/icons/oxygen/32x32/actions/list-remove.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="filtersLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QTreeView" name="treeView">
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="animated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="PlaylistTable" name="tableView">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SelectionMode::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollMode::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>200</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="PlaylistListView" name="listView">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SelectionMode::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Movement::Static</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>PlaylistTable</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>widgets/playlisttable.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>PlaylistListView</class>
|
||||
<extends>QListView</extends>
|
||||
<header>widgets/playlistlistview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../icons/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
271
src/docks/filtersdock.cpp
Normal file
271
src/docks/filtersdock.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2026 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "filtersdock.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "actions.h"
|
||||
#include "controllers/filtercontroller.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mltcontroller.h"
|
||||
#include "models/attachedfiltersmodel.h"
|
||||
#include "models/metadatamodel.h"
|
||||
#include "models/motiontrackermodel.h"
|
||||
#include "models/subtitlesmodel.h"
|
||||
#include "qmltypes/qmlapplication.h"
|
||||
#include "qmltypes/qmlfilter.h"
|
||||
#include "qmltypes/qmlutilities.h"
|
||||
#include "qmltypes/qmlview.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QDir>
|
||||
#include <QIcon>
|
||||
#include <QMenu>
|
||||
#include <QQmlContext>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
#include <QUrl>
|
||||
#include <QtWidgets/QScrollArea>
|
||||
|
||||
FiltersDock::FiltersDock(MetadataModel *metadataModel,
|
||||
AttachedFiltersModel *attachedModel,
|
||||
MotionTrackerModel *motionTrackerModel,
|
||||
SubtitlesModel *subtitlesModel,
|
||||
QWidget *parent)
|
||||
: QDockWidget(tr("Filters"), parent)
|
||||
, m_qview(QmlUtilities::sharedEngine(), this)
|
||||
{
|
||||
LOG_DEBUG() << "begin";
|
||||
setObjectName("FiltersDock");
|
||||
setWhatsThis("https://forum.shotcut.org/t/about-filters/48127/1");
|
||||
QIcon icon = QIcon::fromTheme("view-filter",
|
||||
QIcon(":/icons/oxygen/32x32/actions/view-filter.png"));
|
||||
toggleViewAction()->setIcon(icon);
|
||||
setMinimumSize(200, 200);
|
||||
setupActions();
|
||||
|
||||
m_qview.setFocusPolicy(Qt::StrongFocus);
|
||||
m_qview.quickWindow()->setPersistentSceneGraph(false);
|
||||
#ifndef Q_OS_MAC
|
||||
m_qview.setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
#endif
|
||||
setWidget(&m_qview);
|
||||
|
||||
QmlUtilities::setCommonProperties(m_qview.rootContext());
|
||||
m_qview.rootContext()->setContextProperty("view", new QmlView(&m_qview));
|
||||
m_qview.rootContext()->setContextProperty("metadatamodel", metadataModel);
|
||||
m_qview.rootContext()->setContextProperty("motionTrackerModel", motionTrackerModel);
|
||||
m_qview.rootContext()->setContextProperty("subtitlesModel", subtitlesModel);
|
||||
m_qview.rootContext()->setContextProperty("attachedfiltersmodel", attachedModel);
|
||||
m_qview.rootContext()->setContextProperty("producer", &m_producer);
|
||||
connect(&m_producer, SIGNAL(seeked(int)), SIGNAL(seeked(int)));
|
||||
connect(this, SIGNAL(producerInChanged(int)), &m_producer, SIGNAL(inChanged(int)));
|
||||
connect(this, SIGNAL(producerOutChanged(int)), &m_producer, SIGNAL(outChanged(int)));
|
||||
connect(m_qview.quickWindow(),
|
||||
&QQuickWindow::sceneGraphInitialized,
|
||||
this,
|
||||
&FiltersDock::load,
|
||||
Qt::QueuedConnection);
|
||||
|
||||
setCurrentFilter(0, 0, QmlFilter::NoCurrentFilter);
|
||||
|
||||
LOG_DEBUG() << "end";
|
||||
}
|
||||
|
||||
void FiltersDock::setCurrentFilter(QmlFilter *filter, QmlMetadata *meta, int index)
|
||||
{
|
||||
if (filter && filter->producer().is_valid()) {
|
||||
m_producer.setProducer(filter->producer());
|
||||
if (mlt_service_playlist_type != filter->producer().type() && MLT.producer()
|
||||
&& MLT.producer()->is_valid())
|
||||
onSeeked(MLT.producer()->position());
|
||||
} else {
|
||||
Mlt::Producer emptyProducer(mlt_producer(0));
|
||||
m_producer.setProducer(emptyProducer);
|
||||
}
|
||||
m_qview.rootContext()->setContextProperty("filter", filter);
|
||||
m_qview.rootContext()->setContextProperty("metadata", meta);
|
||||
if (filter)
|
||||
connect(filter, SIGNAL(changed(QString)), SIGNAL(changed()));
|
||||
else
|
||||
disconnect(this, SIGNAL(changed()));
|
||||
QMetaObject::invokeMethod(m_qview.rootObject(),
|
||||
"setCurrentFilter",
|
||||
Q_ARG(QVariant, QVariant(index)));
|
||||
m_qview.setWhatsThis(meta ? meta->helpText() : QString());
|
||||
}
|
||||
|
||||
bool FiltersDock::event(QEvent *event)
|
||||
{
|
||||
bool result = QDockWidget::event(event);
|
||||
if (event->type() == QEvent::PaletteChange || event->type() == QEvent::StyleChange
|
||||
|| event->type() == QEvent::Show) {
|
||||
load();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void FiltersDock::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
QDockWidget::keyPressEvent(event);
|
||||
if (event->key() == Qt::Key_F) {
|
||||
event->ignore();
|
||||
} else if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) {
|
||||
event->accept();
|
||||
}
|
||||
}
|
||||
|
||||
void FiltersDock::onSeeked(int position)
|
||||
{
|
||||
if (m_producer.producer().is_valid()) {
|
||||
if (MLT.isMultitrack()) {
|
||||
// Make the position relative to clip's position on a timeline track.
|
||||
position -= m_producer.producer().get_int(kPlaylistStartProperty);
|
||||
} else {
|
||||
// Make the position relative to the clip's in point.
|
||||
position -= m_producer.in();
|
||||
}
|
||||
m_producer.seek(position);
|
||||
}
|
||||
}
|
||||
|
||||
void FiltersDock::onShowFrame(const SharedFrame &frame)
|
||||
{
|
||||
if (m_producer.producer().is_valid()) {
|
||||
int position = frame.get_position();
|
||||
onSeeked(position);
|
||||
}
|
||||
}
|
||||
|
||||
void FiltersDock::openFilterMenu() const
|
||||
{
|
||||
QMetaObject::invokeMethod(m_qview.rootObject(), "openFilterMenu");
|
||||
}
|
||||
|
||||
void FiltersDock::showCopyFilterMenu()
|
||||
{
|
||||
QMenu menu;
|
||||
menu.addAction(Actions["filtersCopyCurrentFilterAction"]);
|
||||
menu.addAction(Actions["filtersCopyFiltersAction"]);
|
||||
menu.addAction(Actions["filtersCopyAllFilterAction"]);
|
||||
menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void FiltersDock::onServiceInChanged(int delta, Mlt::Service *service)
|
||||
{
|
||||
if (delta && service && m_producer.producer().is_valid()
|
||||
&& service->get_service() == m_producer.producer().get_service()) {
|
||||
emit producerInChanged(delta);
|
||||
}
|
||||
}
|
||||
|
||||
void FiltersDock::load()
|
||||
{
|
||||
if (!m_qview.quickWindow()->isSceneGraphInitialized() && loadTries++ < 5) {
|
||||
LOG_WARNING() << "scene graph not yet initialized";
|
||||
QTimer::singleShot(300, this, &FiltersDock::load);
|
||||
}
|
||||
|
||||
LOG_DEBUG() << "begin"
|
||||
<< "isVisible" << isVisible() << "qview.status" << m_qview.status();
|
||||
|
||||
emit currentFilterRequested(QmlFilter::NoCurrentFilter);
|
||||
|
||||
QDir viewPath = QmlUtilities::qmlDir();
|
||||
viewPath.cd("views");
|
||||
viewPath.cd("filter");
|
||||
m_qview.engine()->addImportPath(viewPath.path());
|
||||
|
||||
QDir modulePath = QmlUtilities::qmlDir();
|
||||
modulePath.cd("modules");
|
||||
m_qview.engine()->addImportPath(modulePath.path());
|
||||
|
||||
m_qview.setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
m_qview.quickWindow()->setColor(palette().window().color());
|
||||
QUrl source = QUrl::fromLocalFile(viewPath.absoluteFilePath("filterview.qml"));
|
||||
m_qview.setSource(source);
|
||||
|
||||
QObject::connect(m_qview.rootObject(),
|
||||
SIGNAL(currentFilterRequested(int)),
|
||||
SIGNAL(currentFilterRequested(int)));
|
||||
QObject::connect(m_qview.rootObject(),
|
||||
SIGNAL(copyFilterRequested()),
|
||||
SLOT(showCopyFilterMenu()));
|
||||
}
|
||||
|
||||
void FiltersDock::setupActions()
|
||||
{
|
||||
QIcon icon;
|
||||
QAction *action;
|
||||
|
||||
action = new QAction(tr("Add"), this);
|
||||
action->setShortcut(QKeySequence(Qt::Key_F));
|
||||
action->setToolTip(tr("Choose a filter to add"));
|
||||
icon = QIcon::fromTheme("list-add", QIcon(":/icons/oxygen/32x32/actions/list-add.png"));
|
||||
action->setIcon(icon);
|
||||
connect(action, &QAction::triggered, this, [=]() {
|
||||
show();
|
||||
raise();
|
||||
m_qview.setFocus();
|
||||
openFilterMenu();
|
||||
});
|
||||
addAction(action);
|
||||
Actions.add("filtersAddFilterAction", action, windowTitle());
|
||||
|
||||
action = new QAction(tr("Remove"), this);
|
||||
action->setShortcut(QKeySequence(Qt::SHIFT | Qt::Key_F));
|
||||
action->setToolTip(tr("Remove selected filter"));
|
||||
icon = QIcon::fromTheme("list-remove", QIcon(":/icons/oxygen/32x32/actions/list-remove.png"));
|
||||
action->setIcon(icon);
|
||||
connect(action, &QAction::triggered, this, [=]() { MAIN.filterController()->removeCurrent(); });
|
||||
addAction(action);
|
||||
Actions.add("filtersRemoveFilterAction", action, windowTitle());
|
||||
|
||||
action = new QAction(tr("Copy Enabled"), this);
|
||||
action->setToolTip(tr("Copy checked filters to the clipboard"));
|
||||
connect(action, &QAction::triggered, this, [=]() {
|
||||
QmlApplication::singleton().copyEnabledFilters();
|
||||
});
|
||||
addAction(action);
|
||||
Actions.add("filtersCopyFiltersAction", action, windowTitle());
|
||||
|
||||
action = new QAction(tr("Copy Current"), this);
|
||||
action->setToolTip(tr("Copy current filter to the clipboard"));
|
||||
connect(action, &QAction::triggered, this, [=]() {
|
||||
QmlApplication::singleton().copyCurrentFilter();
|
||||
});
|
||||
addAction(action);
|
||||
Actions.add("filtersCopyCurrentFilterAction", action, windowTitle());
|
||||
|
||||
action = new QAction(tr("Copy All"), this);
|
||||
action->setToolTip(tr("Copy all filters to the clipboard"));
|
||||
connect(action, &QAction::triggered, this, [=]() {
|
||||
QmlApplication::singleton().copyAllFilters();
|
||||
});
|
||||
addAction(action);
|
||||
Actions.add("filtersCopyAllFilterAction", action, windowTitle());
|
||||
|
||||
action = new QAction(tr("Paste Filters"), this);
|
||||
action->setToolTip(tr("Paste the filters from the clipboard"));
|
||||
icon = QIcon::fromTheme("edit-paste", QIcon(":/icons/oxygen/32x32/actions/edit-paste.png"));
|
||||
action->setIcon(icon);
|
||||
connect(action, &QAction::triggered, this, [=]() {
|
||||
MAIN.filterController()->attachedModel()->pasteFilters();
|
||||
});
|
||||
addAction(action);
|
||||
Actions.add("filtersPasteFiltersAction", action, windowTitle());
|
||||
}
|
||||
75
src/docks/filtersdock.h
Normal file
75
src/docks/filtersdock.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2024 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILTERSDOCK_H
|
||||
#define FILTERSDOCK_H
|
||||
|
||||
#include "qmltypes/qmlproducer.h"
|
||||
#include "sharedframe.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QObject>
|
||||
#include <QQuickWidget>
|
||||
|
||||
class QmlFilter;
|
||||
class QmlMetadata;
|
||||
class MetadataModel;
|
||||
class AttachedFiltersModel;
|
||||
class MotionTrackerModel;
|
||||
class SubtitlesModel;
|
||||
|
||||
class FiltersDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FiltersDock(MetadataModel *metadataModel,
|
||||
AttachedFiltersModel *attachedModel,
|
||||
MotionTrackerModel *motionTrackerModel,
|
||||
SubtitlesModel *subtitlesModel,
|
||||
QWidget *parent = 0);
|
||||
|
||||
QmlProducer *qmlProducer() { return &m_producer; }
|
||||
|
||||
signals:
|
||||
void currentFilterRequested(int attachedIndex);
|
||||
void changed(); /// Notifies when a filter parameter changes.
|
||||
void seeked(int);
|
||||
void producerInChanged(int delta);
|
||||
void producerOutChanged(int delta);
|
||||
|
||||
public slots:
|
||||
void setCurrentFilter(QmlFilter *filter, QmlMetadata *meta, int index);
|
||||
void onSeeked(int position);
|
||||
void onShowFrame(const SharedFrame &frame);
|
||||
void openFilterMenu() const;
|
||||
void showCopyFilterMenu();
|
||||
void onServiceInChanged(int delta, Mlt::Service *service);
|
||||
void load();
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event);
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
|
||||
private:
|
||||
void setupActions();
|
||||
QQuickWidget m_qview;
|
||||
QmlProducer m_producer;
|
||||
unsigned loadTries{0};
|
||||
};
|
||||
|
||||
#endif // FILTERSDOCK_H
|
||||
93
src/docks/findanalysisfilterparser.h
Normal file
93
src/docks/findanalysisfilterparser.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FIND_ANALYSIS_FILTER_PARSER_H
|
||||
#define FIND_ANALYSIS_FILTER_PARSER_H
|
||||
|
||||
#include "qmltypes/qmlapplication.h"
|
||||
|
||||
#include <Mlt.h>
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
class FindAnalysisFilterParser : public Mlt::Parser
|
||||
{
|
||||
private:
|
||||
QList<Mlt::Filter> m_filters;
|
||||
bool m_skipAnalyzed;
|
||||
|
||||
public:
|
||||
FindAnalysisFilterParser()
|
||||
: Mlt::Parser()
|
||||
, m_skipAnalyzed(true)
|
||||
{}
|
||||
|
||||
QList<Mlt::Filter> &filters() { return m_filters; }
|
||||
|
||||
void skipAnalyzed(bool skip) { m_skipAnalyzed = skip; }
|
||||
|
||||
int on_start_filter(Mlt::Filter *filter)
|
||||
{
|
||||
QString serviceName = filter->get("mlt_service");
|
||||
if (serviceName == "loudness" || serviceName == "vidstab") {
|
||||
// If the results property does not exist, empty, or file does not exist.
|
||||
QString results = filter->get("results");
|
||||
if (results.isEmpty() || !m_skipAnalyzed) {
|
||||
if (serviceName == "vidstab") {
|
||||
// vidstab requires a filename, which is only available when using a project folder.
|
||||
QString filename = filter->get("filename");
|
||||
if (filename.isEmpty() || filename.endsWith("vidstab.trf")) {
|
||||
filename = QmlApplication::getNextProjectFile("stab-");
|
||||
}
|
||||
if (!filename.isEmpty()) {
|
||||
filter->set("filename", filename.toUtf8().constData());
|
||||
m_filters << Mlt::Filter(*filter);
|
||||
|
||||
// Touch file to prevent overwriting the same file
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.resize(0);
|
||||
file.close();
|
||||
}
|
||||
} else {
|
||||
m_filters << Mlt::Filter(*filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int on_start_producer(Mlt::Producer *) { return 0; }
|
||||
int on_end_producer(Mlt::Producer *) { return 0; }
|
||||
int on_start_playlist(Mlt::Playlist *) { return 0; }
|
||||
int on_end_playlist(Mlt::Playlist *) { return 0; }
|
||||
int on_start_tractor(Mlt::Tractor *) { return 0; }
|
||||
int on_end_tractor(Mlt::Tractor *) { return 0; }
|
||||
int on_start_multitrack(Mlt::Multitrack *) { return 0; }
|
||||
int on_end_multitrack(Mlt::Multitrack *) { return 0; }
|
||||
int on_start_track() { return 0; }
|
||||
int on_end_track() { return 0; }
|
||||
int on_end_filter(Mlt::Filter *) { return 0; }
|
||||
int on_start_transition(Mlt::Transition *) { return 0; }
|
||||
int on_end_transition(Mlt::Transition *) { return 0; }
|
||||
int on_start_chain(Mlt::Chain *) { return 0; }
|
||||
int on_end_chain(Mlt::Chain *) { return 0; }
|
||||
int on_start_link(Mlt::Link *) { return 0; }
|
||||
int on_end_link(Mlt::Link *) { return 0; }
|
||||
};
|
||||
|
||||
#endif // FIND_ANALYSIS_FILTER_PARSER_H
|
||||
224
src/docks/jobsdock.cpp
Normal file
224
src/docks/jobsdock.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2026 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "jobsdock.h"
|
||||
#include "ui_jobsdock.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "dialogs/textviewerdialog.h"
|
||||
#include "jobqueue.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
JobsDock::JobsDock(QWidget *parent)
|
||||
: QDockWidget(parent)
|
||||
, ui(new Ui::JobsDock)
|
||||
{
|
||||
LOG_DEBUG() << "begin";
|
||||
ui->setupUi(this);
|
||||
QIcon icon = QIcon::fromTheme("run-build", QIcon(":/icons/oxygen/32x32/actions/run-builld.png"));
|
||||
toggleViewAction()->setIcon(icon);
|
||||
ui->treeView->setModel(&JOBS);
|
||||
QHeaderView *header = ui->treeView->header();
|
||||
header->setStretchLastSection(false);
|
||||
header->setSectionResizeMode(JobQueue::COLUMN_ICON, QHeaderView::Fixed);
|
||||
ui->treeView->setColumnWidth(JobQueue::COLUMN_ICON, 24);
|
||||
header->setSectionResizeMode(JobQueue::COLUMN_OUTPUT, QHeaderView::Stretch);
|
||||
header->setSectionResizeMode(JobQueue::COLUMN_STATUS, QHeaderView::ResizeToContents);
|
||||
ui->cleanButton->hide();
|
||||
LOG_DEBUG() << "end";
|
||||
}
|
||||
|
||||
JobsDock::~JobsDock()
|
||||
{
|
||||
JOBS.cleanup();
|
||||
delete ui;
|
||||
}
|
||||
|
||||
AbstractJob *JobsDock::currentJob() const
|
||||
{
|
||||
QModelIndex index = ui->treeView->currentIndex();
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
return JOBS.jobFromIndex(index);
|
||||
}
|
||||
|
||||
void JobsDock::onJobAdded()
|
||||
{
|
||||
QModelIndex index = JOBS.index(JOBS.rowCount() - 1, JobQueue::COLUMN_OUTPUT);
|
||||
QProgressBar *progressBar = new QProgressBar;
|
||||
progressBar->setMinimum(0);
|
||||
progressBar->setMaximum(100);
|
||||
progressBar->setAutoFillBackground(true);
|
||||
progressBar->setTextVisible(false);
|
||||
QHBoxLayout *layout = new QHBoxLayout(progressBar);
|
||||
QLabel *label = new QLabel;
|
||||
layout->addWidget(label);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
ui->treeView->setIndexWidget(index, progressBar);
|
||||
ui->treeView->resizeColumnToContents(JobQueue::COLUMN_STATUS);
|
||||
label->setToolTip(JOBS.data(index).toString());
|
||||
label->setText(
|
||||
label->fontMetrics().elidedText(JOBS.data(index).toString(),
|
||||
Qt::ElideMiddle,
|
||||
ui->treeView->columnWidth(JobQueue::COLUMN_OUTPUT)));
|
||||
connect(JOBS.jobFromIndex(index),
|
||||
SIGNAL(progressUpdated(QStandardItem *, int)),
|
||||
SLOT(onProgressUpdated(QStandardItem *, int)));
|
||||
show();
|
||||
raise();
|
||||
}
|
||||
|
||||
void JobsDock::onProgressUpdated(QStandardItem *item, int percent)
|
||||
{
|
||||
if (item) {
|
||||
QModelIndex index = JOBS.index(item->row(), JobQueue::COLUMN_OUTPUT);
|
||||
QProgressBar *progressBar = qobject_cast<QProgressBar *>(ui->treeView->indexWidget(index));
|
||||
if (progressBar && percent > progressBar->value())
|
||||
progressBar->setValue(percent);
|
||||
}
|
||||
}
|
||||
|
||||
void JobsDock::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QDockWidget::resizeEvent(event);
|
||||
foreach (QLabel *label, ui->treeView->findChildren<QLabel *>()) {
|
||||
label->setText(
|
||||
label->fontMetrics().elidedText(label->toolTip(),
|
||||
Qt::ElideMiddle,
|
||||
ui->treeView->columnWidth(JobQueue::COLUMN_OUTPUT)));
|
||||
}
|
||||
}
|
||||
|
||||
void JobsDock::on_treeView_customContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
QModelIndex index = ui->treeView->currentIndex();
|
||||
QMenu menu(this);
|
||||
AbstractJob *job = index.isValid() ? JOBS.jobFromIndex(index) : nullptr;
|
||||
if (job) {
|
||||
if (job->ran() && job->state() == QProcess::NotRunning
|
||||
&& job->exitStatus() == QProcess::NormalExit) {
|
||||
menu.addActions(job->successActions());
|
||||
}
|
||||
if (job->stopped() || (JOBS.isPaused() && !job->ran()))
|
||||
menu.addAction(ui->actionRun);
|
||||
if (job->state() == QProcess::Running)
|
||||
menu.addAction(ui->actionStopJob);
|
||||
else
|
||||
menu.addAction(ui->actionRemove);
|
||||
if (job->ran())
|
||||
menu.addAction(ui->actionViewLog);
|
||||
menu.addActions(job->standardActions());
|
||||
}
|
||||
for (auto job : JOBS.jobs()) {
|
||||
if (job->ran() && job->state() != QProcess::Running) {
|
||||
menu.addAction(ui->actionRemoveFinished);
|
||||
break;
|
||||
}
|
||||
}
|
||||
menu.exec(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void JobsDock::on_actionStopJob_triggered()
|
||||
{
|
||||
QModelIndex index = ui->treeView->currentIndex();
|
||||
if (!index.isValid())
|
||||
return;
|
||||
AbstractJob *job = JOBS.jobFromIndex(index);
|
||||
if (job)
|
||||
job->stop();
|
||||
}
|
||||
|
||||
void JobsDock::on_actionViewLog_triggered()
|
||||
{
|
||||
QModelIndex index = ui->treeView->currentIndex();
|
||||
if (!index.isValid())
|
||||
return;
|
||||
AbstractJob *job = JOBS.jobFromIndex(index);
|
||||
if (job) {
|
||||
TextViewerDialog dialog(this);
|
||||
dialog.setWindowTitle(tr("Job Log"));
|
||||
dialog.setText(job->log());
|
||||
auto connection = connect(job, &AbstractJob::progressUpdated, this, [&]() {
|
||||
dialog.setText(job->log(), true);
|
||||
});
|
||||
dialog.exec();
|
||||
disconnect(connection);
|
||||
}
|
||||
}
|
||||
|
||||
void JobsDock::on_pauseButton_toggled(bool checked)
|
||||
{
|
||||
if (checked)
|
||||
JOBS.pause();
|
||||
else
|
||||
JOBS.resume();
|
||||
}
|
||||
|
||||
void JobsDock::on_actionRun_triggered()
|
||||
{
|
||||
QModelIndex index = ui->treeView->currentIndex();
|
||||
if (!index.isValid())
|
||||
return;
|
||||
AbstractJob *job = JOBS.jobFromIndex(index);
|
||||
if (job)
|
||||
job->start();
|
||||
}
|
||||
|
||||
void JobsDock::on_menuButton_clicked()
|
||||
{
|
||||
on_treeView_customContextMenuRequested(ui->menuButton->mapToParent(QPoint(0, 0)));
|
||||
}
|
||||
|
||||
void JobsDock::on_treeView_doubleClicked(const QModelIndex &index)
|
||||
{
|
||||
AbstractJob *job = JOBS.jobFromIndex(index);
|
||||
if (job && job->ran() && job->state() == QProcess::NotRunning
|
||||
&& job->exitStatus() == QProcess::NormalExit) {
|
||||
foreach (QAction *action, job->successActions()) {
|
||||
if (action->data() == "Open") {
|
||||
action->trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JobsDock::on_actionRemove_triggered()
|
||||
{
|
||||
QModelIndex index = ui->treeView->currentIndex();
|
||||
if (!index.isValid())
|
||||
return;
|
||||
JOBS.remove(index);
|
||||
}
|
||||
|
||||
void JobsDock::on_actionRemoveFinished_triggered()
|
||||
{
|
||||
JOBS.removeFinished();
|
||||
}
|
||||
|
||||
void JobsDock::on_JobsDock_visibilityChanged(bool visible)
|
||||
{
|
||||
if (visible) {
|
||||
foreach (QLabel *label, ui->treeView->findChildren<QLabel *>()) {
|
||||
label->setText(label->fontMetrics().elidedText(label->toolTip(),
|
||||
Qt::ElideMiddle,
|
||||
ui->treeView->columnWidth(
|
||||
JobQueue::COLUMN_OUTPUT)));
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/docks/jobsdock.h
Normal file
62
src/docks/jobsdock.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2020 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef JOBSDOCK_H
|
||||
#define JOBSDOCK_H
|
||||
|
||||
#include <QDockWidget>
|
||||
|
||||
class AbstractJob;
|
||||
class QStandardItem;
|
||||
|
||||
namespace Ui {
|
||||
class JobsDock;
|
||||
}
|
||||
|
||||
class JobsDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit JobsDock(QWidget *parent = 0);
|
||||
~JobsDock();
|
||||
AbstractJob *currentJob() const;
|
||||
|
||||
public slots:
|
||||
void onJobAdded();
|
||||
void onProgressUpdated(QStandardItem *item, int percent);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
private:
|
||||
Ui::JobsDock *ui;
|
||||
|
||||
private slots:
|
||||
void on_treeView_customContextMenuRequested(const QPoint &pos);
|
||||
void on_actionStopJob_triggered();
|
||||
void on_actionViewLog_triggered();
|
||||
void on_pauseButton_toggled(bool checked);
|
||||
void on_actionRun_triggered();
|
||||
void on_menuButton_clicked();
|
||||
void on_treeView_doubleClicked(const QModelIndex &index);
|
||||
void on_actionRemove_triggered();
|
||||
void on_actionRemoveFinished_triggered();
|
||||
void on_JobsDock_visibilityChanged(bool visible);
|
||||
};
|
||||
|
||||
#endif // JOBSDOCK_H
|
||||
195
src/docks/jobsdock.ui
Normal file
195
src/docks/jobsdock.ui
Normal file
@@ -0,0 +1,195 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>JobsDock</class>
|
||||
<widget class="QDockWidget" name="JobsDock">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>283</width>
|
||||
<height>279</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string notr="true">https://forum.shotcut.org/t/jobs-panel/12945/1</string>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Jobs</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::NoFrame</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>283</width>
|
||||
<height>219</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeView">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textElideMode">
|
||||
<enum>Qt::TextElideMode::ElideMiddle</enum>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="menuButton">
|
||||
<property name="toolTip">
|
||||
<string>Jobs Menu</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="show-menu" resource="../../icons/resources.qrc">
|
||||
<normaloff>:/icons/light/32x32/show-menu.png</normaloff>:/icons/light/32x32/show-menu.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pauseButton">
|
||||
<property name="toolTip">
|
||||
<string>Stop automatically processing the next pending job in
|
||||
the list. This does not stop a currently running job. Right-
|
||||
-click a job to open a menu to stop a currently running job.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Pause Queue</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cleanButton">
|
||||
<property name="toolTip">
|
||||
<string>Remove all of the completed and failed jobs from the list</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clean</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<action name="actionStopJob">
|
||||
<property name="text">
|
||||
<string>Stop This Job</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Stop the currently selected job</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewLog">
|
||||
<property name="text">
|
||||
<string>View Log</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>View the messages of MLT and FFmpeg </string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRun">
|
||||
<property name="text">
|
||||
<string>Run</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Restart a stopped job</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRemove">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRemoveFinished">
|
||||
<property name="text">
|
||||
<string>Remove Finished</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove Finished</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../icons/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
1235
src/docks/keyframesdock.cpp
Normal file
1235
src/docks/keyframesdock.cpp
Normal file
File diff suppressed because it is too large
Load Diff
93
src/docks/keyframesdock.h
Normal file
93
src/docks/keyframesdock.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2023 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEYFRAMESDOCK_H
|
||||
#define KEYFRAMESDOCK_H
|
||||
|
||||
#include "models/keyframesmodel.h"
|
||||
#include "qmltypes/qmlfilter.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QQuickWidget>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class QmlFilter;
|
||||
class QmlMetadata;
|
||||
class AttachedFiltersModel;
|
||||
class QmlProducer;
|
||||
class QMenu;
|
||||
|
||||
class KeyframesDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(double timeScale READ timeScale WRITE setTimeScale NOTIFY timeScaleChanged)
|
||||
|
||||
public:
|
||||
explicit KeyframesDock(QmlProducer *qmlProducer, QWidget *parent = 0);
|
||||
|
||||
KeyframesModel &model() { return m_model; }
|
||||
Q_INVOKABLE int seekPrevious();
|
||||
Q_INVOKABLE int seekNext();
|
||||
int currentParameter() const;
|
||||
double timeScale() const { return m_timeScale; }
|
||||
void setTimeScale(double value);
|
||||
|
||||
signals:
|
||||
void changed(); /// Notifies when a filter parameter changes.
|
||||
void setZoom(double value);
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
void zoomToFit();
|
||||
void resetZoom();
|
||||
void seekPreviousSimple();
|
||||
void seekNextSimple();
|
||||
void newFilter(); // Notifies when the filter itself has been changed
|
||||
void timeScaleChanged();
|
||||
void dockClicked();
|
||||
|
||||
public slots:
|
||||
void setCurrentFilter(QmlFilter *filter, QmlMetadata *meta);
|
||||
void load(bool force = false);
|
||||
void reload();
|
||||
void onProducerModified();
|
||||
|
||||
protected:
|
||||
bool event(QEvent *event);
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
void keyReleaseEvent(QKeyEvent *event);
|
||||
|
||||
private slots:
|
||||
void onDockRightClicked();
|
||||
void onKeyframeRightClicked();
|
||||
void onClipRightClicked();
|
||||
|
||||
private:
|
||||
void setupActions();
|
||||
QQuickWidget m_qview;
|
||||
KeyframesModel m_model;
|
||||
QmlMetadata *m_metadata;
|
||||
QmlFilter *m_filter;
|
||||
QmlProducer *m_qmlProducer;
|
||||
QMenu *m_mainMenu;
|
||||
QMenu *m_keyMenu;
|
||||
QMenu *m_keyTypePrevMenu;
|
||||
QMenu *m_keyTypeNextMenu;
|
||||
QMenu *m_clipMenu;
|
||||
double m_timeScale{1.0};
|
||||
};
|
||||
|
||||
#endif // KEYFRAMESDOCK_H
|
||||
464
src/docks/markersdock.cpp
Normal file
464
src/docks/markersdock.cpp
Normal file
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2026 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "markersdock.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "actions.h"
|
||||
#include "mainwindow.h"
|
||||
#include "models/markersmodel.h"
|
||||
#include "settings.h"
|
||||
#include "util.h"
|
||||
#include "widgets/docktoolbar.h"
|
||||
#include "widgets/editmarkerwidget.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QIcon>
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QSpacerItem>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QToolButton>
|
||||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtWidgets/QScrollArea>
|
||||
|
||||
class ColorItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorItemDelegate(QAbstractItemView *view, QWidget *parent = nullptr)
|
||||
: QStyledItemDelegate(parent)
|
||||
, m_view(view)
|
||||
{}
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
const auto color = index.data(MarkersModel::ColorRole).value<QColor>();
|
||||
const auto textColor(Util::textColor(color));
|
||||
painter->fillRect(option.rect, color);
|
||||
const auto point = option.rect.topLeft()
|
||||
+ QPoint(2 * m_view->devicePixelRatioF(),
|
||||
option.fontMetrics.ascent() + m_view->devicePixelRatioF());
|
||||
painter->setPen(textColor);
|
||||
painter->drawText(point, color.name());
|
||||
}
|
||||
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
return QSize(m_view->viewport()->width(),
|
||||
option.fontMetrics.height() + 2 * m_view->devicePixelRatioF());
|
||||
}
|
||||
|
||||
private:
|
||||
QAbstractItemView *m_view;
|
||||
};
|
||||
|
||||
class MarkerTreeView : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Make this function public
|
||||
using QTreeView::selectedIndexes;
|
||||
void blockSelectionEvent(bool block) { m_blockSelectionEvent = block; }
|
||||
|
||||
signals:
|
||||
void rowClicked(const QModelIndex &index);
|
||||
void markerSelected(QModelIndex &index);
|
||||
|
||||
protected:
|
||||
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
||||
{
|
||||
QTreeView::selectionChanged(selected, deselected);
|
||||
if (!m_blockSelectionEvent) {
|
||||
QModelIndex signalIndex;
|
||||
QModelIndexList indices = selectedIndexes();
|
||||
if (indices.size() > 0) {
|
||||
signalIndex = indices[0];
|
||||
}
|
||||
emit markerSelected(signalIndex);
|
||||
}
|
||||
}
|
||||
void mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
QTreeView::mouseReleaseEvent(event);
|
||||
QModelIndex signalIndex = indexAt(event->pos());
|
||||
if (signalIndex.isValid()) {
|
||||
emit rowClicked(signalIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_blockSelectionEvent = false;
|
||||
};
|
||||
|
||||
// Include this so that MarkerTreeView can be declared in the source file.
|
||||
#include "markersdock.moc"
|
||||
|
||||
MarkersDock::MarkersDock(QWidget *parent)
|
||||
: QDockWidget(parent)
|
||||
, m_model(nullptr)
|
||||
, m_proxyModel(nullptr)
|
||||
, m_editInProgress(false)
|
||||
{
|
||||
LOG_DEBUG() << "begin";
|
||||
|
||||
setObjectName("MarkersDock");
|
||||
QDockWidget::setWindowTitle(tr("Markers"));
|
||||
QIcon icon = QIcon::fromTheme("marker", QIcon(":/icons/oxygen/32x32/actions/marker.png"));
|
||||
toggleViewAction()->setIcon(icon);
|
||||
setWhatsThis("https://forum.shotcut.org/t/timeline-markers/30535/1");
|
||||
|
||||
QScrollArea *scrollArea = new QScrollArea();
|
||||
scrollArea->setFrameShape(QFrame::NoFrame);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
QDockWidget::setWidget(scrollArea);
|
||||
|
||||
QVBoxLayout *vboxLayout = new QVBoxLayout();
|
||||
scrollArea->setLayout(vboxLayout);
|
||||
|
||||
m_treeView = new MarkerTreeView();
|
||||
m_treeView->setItemDelegateForColumn(0, new ColorItemDelegate(m_treeView));
|
||||
m_treeView->setItemsExpandable(false);
|
||||
m_treeView->setRootIsDecorated(false);
|
||||
m_treeView->setUniformRowHeights(true);
|
||||
m_treeView->setSortingEnabled(true);
|
||||
connect(m_treeView,
|
||||
SIGNAL(markerSelected(QModelIndex &)),
|
||||
this,
|
||||
SLOT(onSelectionChanged(QModelIndex &)));
|
||||
connect(m_treeView,
|
||||
SIGNAL(rowClicked(const QModelIndex &)),
|
||||
this,
|
||||
SLOT(onRowClicked(const QModelIndex &)));
|
||||
vboxLayout->addWidget(m_treeView, 1);
|
||||
|
||||
QMenu *mainMenu = new QMenu("Markers", this);
|
||||
mainMenu->addAction(Actions["timelineMarkerAction"]);
|
||||
mainMenu->addAction(Actions["timelinePrevMarkerAction"]);
|
||||
mainMenu->addAction(Actions["timelineNextMarkerAction"]);
|
||||
mainMenu->addAction(Actions["timelineDeleteMarkerAction"]);
|
||||
mainMenu->addAction(Actions["timelineMarkSelectedClipAction"]);
|
||||
mainMenu->addAction(Actions["timelineCycleMarkerColorAction"]);
|
||||
mainMenu->addAction(tr("Remove All Markers"), this, SLOT(onRemoveAllRequested()));
|
||||
QAction *action;
|
||||
QMenu *columnsMenu = new QMenu(tr("Columns"), this);
|
||||
action = columnsMenu->addAction(tr("Color"), this, SLOT(onColorColumnToggled(bool)));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(Settings.markersShowColumn("color"));
|
||||
action = columnsMenu->addAction(tr("Name"), this, SLOT(onTextColumnToggled(bool)));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(Settings.markersShowColumn("text"));
|
||||
action = columnsMenu->addAction(tr("Start"), this, SLOT(onStartColumnToggled(bool)));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(Settings.markersShowColumn("start"));
|
||||
action = columnsMenu->addAction(tr("End"), this, SLOT(onEndColumnToggled(bool)));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(Settings.markersShowColumn("end"));
|
||||
action = columnsMenu->addAction(tr("Duration"), this, SLOT(onDurationColumnToggled(bool)));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(Settings.markersShowColumn("duration"));
|
||||
mainMenu->addMenu(columnsMenu);
|
||||
Actions.loadFromMenu(mainMenu);
|
||||
|
||||
DockToolBar *toolbar = new DockToolBar(tr("Markers Controls"));
|
||||
toolbar->setAreaHint(Qt::BottomToolBarArea);
|
||||
QToolButton *menuButton = new QToolButton(this);
|
||||
menuButton->setIcon(
|
||||
QIcon::fromTheme("show-menu", QIcon(":/icons/oxygen/32x32/actions/show-menu.png")));
|
||||
menuButton->setToolTip(tr("Markers Menu"));
|
||||
menuButton->setAutoRaise(true);
|
||||
menuButton->setMenu(mainMenu);
|
||||
menuButton->setPopupMode(QToolButton::QToolButton::InstantPopup);
|
||||
toolbar->addWidget(menuButton);
|
||||
|
||||
m_addButton = new QToolButton(this);
|
||||
m_addButton->setIcon(
|
||||
QIcon::fromTheme("list-add", QIcon(":/icons/oxygen/32x32/actions/list-add.png")));
|
||||
m_addButton->setToolTip(tr("Add a marker at the current time"));
|
||||
m_addButton->setAutoRaise(true);
|
||||
if (!connect(m_addButton, &QAbstractButton::clicked, this, &MarkersDock::onAddRequested))
|
||||
connect(m_addButton, SIGNAL(clicked()), SLOT(onAddRequested()));
|
||||
toolbar->addWidget(m_addButton);
|
||||
|
||||
m_removeButton = new QToolButton(this);
|
||||
m_removeButton->setIcon(
|
||||
QIcon::fromTheme("list-remove", QIcon(":/icons/oxygen/32x32/actions/list-remove.png")));
|
||||
m_removeButton->setToolTip(tr("Remove the selected marker"));
|
||||
m_removeButton->setAutoRaise(true);
|
||||
if (!connect(m_removeButton, &QAbstractButton::clicked, this, &MarkersDock::onRemoveRequested))
|
||||
connect(m_removeButton, SIGNAL(clicked()), SLOT(onRemoveRequested()));
|
||||
toolbar->addWidget(m_removeButton);
|
||||
|
||||
m_clearButton = new QToolButton(this);
|
||||
m_clearButton->setIcon(
|
||||
QIcon::fromTheme("window-close", QIcon(":/icons/oxygen/32x32/actions/window-close.png")));
|
||||
m_clearButton->setToolTip(tr("Deselect the marker"));
|
||||
m_clearButton->setAutoRaise(true);
|
||||
if (!connect(m_clearButton,
|
||||
&QAbstractButton::clicked,
|
||||
this,
|
||||
&MarkersDock::onClearSelectionRequested))
|
||||
connect(m_clearButton, SIGNAL(clicked()), SLOT(onClearSelectionRequested()));
|
||||
toolbar->addWidget(m_clearButton);
|
||||
|
||||
m_searchField = new QLineEdit(this);
|
||||
m_searchField->setPlaceholderText(tr("search"));
|
||||
if (!connect(m_searchField, &QLineEdit::textChanged, this, &MarkersDock::onSearchChanged))
|
||||
connect(m_searchField, SIGNAL(textChanged(const QString &)), SLOT(onSearchChanged()));
|
||||
toolbar->addWidget(m_searchField);
|
||||
|
||||
m_clearSearchButton = new QToolButton(this);
|
||||
m_clearSearchButton->setIcon(
|
||||
QIcon::fromTheme("edit-clear", QIcon(":/icons/oxygen/32x32/actions/edit-clear.png")));
|
||||
m_clearSearchButton->setToolTip(tr("Clear search"));
|
||||
m_clearSearchButton->setAutoRaise(true);
|
||||
if (!connect(m_clearSearchButton, &QAbstractButton::clicked, m_searchField, &QLineEdit::clear))
|
||||
connect(m_clearSearchButton, SIGNAL(clicked()), m_searchField, SLOT(clear()));
|
||||
toolbar->addWidget(m_clearSearchButton);
|
||||
|
||||
vboxLayout->addWidget(toolbar);
|
||||
enableButtons(false);
|
||||
|
||||
m_editMarkerWidget = new EditMarkerWidget(this, "", "", 0, 0, 0);
|
||||
m_editMarkerWidget->setVisible(false);
|
||||
connect(m_editMarkerWidget, SIGNAL(valuesChanged()), SLOT(onValuesChanged()));
|
||||
vboxLayout->addWidget(m_editMarkerWidget);
|
||||
|
||||
vboxLayout->addStretch();
|
||||
|
||||
LOG_DEBUG() << "end";
|
||||
}
|
||||
|
||||
MarkersDock::~MarkersDock() {}
|
||||
|
||||
void MarkersDock::setModel(MarkersModel *model)
|
||||
{
|
||||
m_treeView->blockSelectionEvent(true);
|
||||
m_model = model;
|
||||
m_proxyModel = new QSortFilterProxyModel(this);
|
||||
m_proxyModel->setSourceModel(m_model);
|
||||
m_proxyModel->setFilterKeyColumn(1);
|
||||
m_treeView->setModel(m_proxyModel);
|
||||
m_treeView->setColumnHidden(0, !Settings.markersShowColumn("color"));
|
||||
m_treeView->setColumnHidden(1, !Settings.markersShowColumn("text"));
|
||||
m_treeView->setColumnHidden(2, !Settings.markersShowColumn("start"));
|
||||
m_treeView->setColumnHidden(3, !Settings.markersShowColumn("end"));
|
||||
m_treeView->setColumnHidden(4, !Settings.markersShowColumn("duration"));
|
||||
m_treeView->sortByColumn(Settings.getMarkerSortColumn(), Settings.getMarkerSortOrder());
|
||||
connect(m_model,
|
||||
SIGNAL(rowsInserted(const QModelIndex &, int, int)),
|
||||
this,
|
||||
SLOT(onRowsInserted(const QModelIndex &, int, int)));
|
||||
connect(m_model,
|
||||
SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &)),
|
||||
this,
|
||||
SLOT(onDataChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &)));
|
||||
connect(m_model, SIGNAL(modelReset()), this, SLOT(onModelReset()));
|
||||
connect(m_treeView->header(),
|
||||
SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)),
|
||||
this,
|
||||
SLOT(onSortIndicatorChanged(int, Qt::SortOrder)));
|
||||
m_treeView->blockSelectionEvent(false);
|
||||
}
|
||||
|
||||
void MarkersDock::onMarkerSelectionRequest(int markerIndex)
|
||||
{
|
||||
QModelIndex sourceIndex = m_model->modelIndexForRow(markerIndex);
|
||||
QModelIndex insertedIndex = m_proxyModel->mapFromSource(sourceIndex);
|
||||
if (insertedIndex.isValid()) {
|
||||
m_treeView->setCurrentIndex(insertedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkersDock::onSelectionChanged(QModelIndex &index)
|
||||
{
|
||||
if (m_model && m_proxyModel && MAIN.multitrack() && index.isValid()) {
|
||||
QModelIndex realIndex = m_proxyModel->mapToSource(index);
|
||||
if (realIndex.isValid()) {
|
||||
Markers::Marker marker = m_model->getMarker(realIndex.row());
|
||||
enableButtons(true);
|
||||
m_editMarkerWidget->setVisible(true);
|
||||
QSignalBlocker editBlocker(m_editMarkerWidget);
|
||||
m_editMarkerWidget->setValues(marker.text,
|
||||
marker.color,
|
||||
marker.start,
|
||||
marker.end,
|
||||
MAIN.multitrack()->get_length() - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_editMarkerWidget->setVisible(false);
|
||||
enableButtons(false);
|
||||
}
|
||||
|
||||
void MarkersDock::onRowClicked(const QModelIndex &index)
|
||||
{
|
||||
if (m_model && m_proxyModel && MAIN.multitrack() && index.isValid()) {
|
||||
QModelIndex realIndex = m_proxyModel->mapToSource(index);
|
||||
if (realIndex.isValid()) {
|
||||
Markers::Marker marker = m_model->getMarker(realIndex.row());
|
||||
emit seekRequested(marker.start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MarkersDock::onAddRequested()
|
||||
{
|
||||
emit addRequested();
|
||||
}
|
||||
|
||||
void MarkersDock::onRemoveRequested()
|
||||
{
|
||||
if (m_model && m_proxyModel) {
|
||||
QModelIndexList indices = m_treeView->selectedIndexes();
|
||||
if (indices.size() > 0) {
|
||||
QModelIndex realIndex = m_proxyModel->mapToSource(indices[0]);
|
||||
if (realIndex.isValid()) {
|
||||
m_model->remove(realIndex.row());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MarkersDock::onClearSelectionRequested()
|
||||
{
|
||||
m_treeView->clearSelection();
|
||||
}
|
||||
|
||||
void MarkersDock::onRemoveAllRequested()
|
||||
{
|
||||
m_model->clear();
|
||||
}
|
||||
|
||||
void MarkersDock::onSearchChanged()
|
||||
{
|
||||
if (m_proxyModel) {
|
||||
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
m_proxyModel->setFilterFixedString(m_searchField->text());
|
||||
}
|
||||
}
|
||||
|
||||
void MarkersDock::onColorColumnToggled(bool checked)
|
||||
{
|
||||
Settings.setMarkersShowColumn("color", checked);
|
||||
m_treeView->setColumnHidden(0, !checked);
|
||||
}
|
||||
|
||||
void MarkersDock::onTextColumnToggled(bool checked)
|
||||
{
|
||||
Settings.setMarkersShowColumn("text", checked);
|
||||
m_treeView->setColumnHidden(1, !checked);
|
||||
}
|
||||
|
||||
void MarkersDock::onStartColumnToggled(bool checked)
|
||||
{
|
||||
Settings.setMarkersShowColumn("start", checked);
|
||||
m_treeView->setColumnHidden(2, !checked);
|
||||
}
|
||||
|
||||
void MarkersDock::onEndColumnToggled(bool checked)
|
||||
{
|
||||
Settings.setMarkersShowColumn("end", checked);
|
||||
m_treeView->setColumnHidden(3, !checked);
|
||||
}
|
||||
|
||||
void MarkersDock::onDurationColumnToggled(bool checked)
|
||||
{
|
||||
Settings.setMarkersShowColumn("duration", checked);
|
||||
m_treeView->setColumnHidden(4, !checked);
|
||||
}
|
||||
|
||||
void MarkersDock::onRowsInserted(const QModelIndex &parent, int first, int last)
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
Q_UNUSED(last);
|
||||
QModelIndex sourceIndex = m_model->modelIndexForRow(first);
|
||||
QModelIndex insertedIndex = m_proxyModel->mapFromSource(sourceIndex);
|
||||
m_treeView->setCurrentIndex(insertedIndex);
|
||||
}
|
||||
|
||||
void MarkersDock::onDataChanged(const QModelIndex &topLeft,
|
||||
const QModelIndex &bottomRight,
|
||||
const QVector<int> &roles)
|
||||
{
|
||||
Q_UNUSED(topLeft);
|
||||
Q_UNUSED(bottomRight);
|
||||
Q_UNUSED(roles);
|
||||
if (m_model && m_proxyModel && !m_editInProgress) {
|
||||
QModelIndexList indices = m_treeView->selectedIndexes();
|
||||
if (indices.size() > 0) {
|
||||
QModelIndex realIndex = m_proxyModel->mapToSource(indices[0]);
|
||||
if (realIndex.isValid()) {
|
||||
Markers::Marker marker = m_model->getMarker(realIndex.row());
|
||||
m_editMarkerWidget->setVisible(true);
|
||||
QSignalBlocker editBlocker(m_editMarkerWidget);
|
||||
m_editMarkerWidget->setValues(marker.text,
|
||||
marker.color,
|
||||
marker.start,
|
||||
marker.end,
|
||||
MAIN.multitrack()->get_length() - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MarkersDock::onValuesChanged()
|
||||
{
|
||||
if (m_model && m_proxyModel) {
|
||||
QModelIndexList indices = m_treeView->selectedIndexes();
|
||||
if (indices.size() > 0) {
|
||||
QModelIndex realIndex = m_proxyModel->mapToSource(indices[0]);
|
||||
if (realIndex.isValid()) {
|
||||
Markers::Marker marker;
|
||||
marker.text = m_editMarkerWidget->getText();
|
||||
marker.color = m_editMarkerWidget->getColor();
|
||||
marker.start = m_editMarkerWidget->getStart();
|
||||
marker.end = m_editMarkerWidget->getEnd();
|
||||
m_editInProgress = true;
|
||||
m_model->update(realIndex.row(), marker);
|
||||
m_editInProgress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MarkersDock::onModelReset()
|
||||
{
|
||||
m_treeView->clearSelection();
|
||||
m_editMarkerWidget->setVisible(false);
|
||||
}
|
||||
|
||||
void MarkersDock::onSortIndicatorChanged(int logicalIndex, Qt::SortOrder order)
|
||||
{
|
||||
Settings.setMarkerSort(logicalIndex, order);
|
||||
}
|
||||
|
||||
void MarkersDock::enableButtons(bool enable)
|
||||
{
|
||||
m_removeButton->setEnabled(enable);
|
||||
m_clearButton->setEnabled(enable);
|
||||
}
|
||||
84
src/docks/markersdock.h
Normal file
84
src/docks/markersdock.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MARKERSDOCK_H
|
||||
#define MARKERSDOCK_H
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QItemSelectionModel>
|
||||
|
||||
class EditMarkerWidget;
|
||||
class MarkerTreeView;
|
||||
class MarkersModel;
|
||||
class QLineEdit;
|
||||
class QSortFilterProxyModel;
|
||||
class QToolButton;
|
||||
|
||||
class MarkersDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MarkersDock(QWidget *parent = 0);
|
||||
~MarkersDock();
|
||||
void setModel(MarkersModel *model);
|
||||
|
||||
signals:
|
||||
void seekRequested(int pos);
|
||||
void addRequested();
|
||||
void addAroundSelectionRequested();
|
||||
|
||||
public slots:
|
||||
void onMarkerSelectionRequest(int markerIndex);
|
||||
|
||||
private slots:
|
||||
void onSelectionChanged(QModelIndex &index);
|
||||
void onRowClicked(const QModelIndex &index);
|
||||
void onAddRequested();
|
||||
void onRemoveRequested();
|
||||
void onClearSelectionRequested();
|
||||
void onRemoveAllRequested();
|
||||
void onSearchChanged();
|
||||
void onColorColumnToggled(bool checked);
|
||||
void onTextColumnToggled(bool checked);
|
||||
void onStartColumnToggled(bool checked);
|
||||
void onEndColumnToggled(bool checked);
|
||||
void onDurationColumnToggled(bool checked);
|
||||
void onRowsInserted(const QModelIndex &parent, int first, int last);
|
||||
void onDataChanged(const QModelIndex &topLeft,
|
||||
const QModelIndex &bottomRight,
|
||||
const QVector<int> &roles = QVector<int>());
|
||||
void onValuesChanged();
|
||||
void onModelReset();
|
||||
void onSortIndicatorChanged(int logicalIndex, Qt::SortOrder order);
|
||||
|
||||
private:
|
||||
void enableButtons(bool enable);
|
||||
|
||||
MarkersModel *m_model;
|
||||
QSortFilterProxyModel *m_proxyModel;
|
||||
MarkerTreeView *m_treeView;
|
||||
QToolButton *m_addButton;
|
||||
QToolButton *m_removeButton;
|
||||
QToolButton *m_clearButton;
|
||||
QLineEdit *m_searchField;
|
||||
QToolButton *m_clearSearchButton;
|
||||
EditMarkerWidget *m_editMarkerWidget;
|
||||
bool m_editInProgress;
|
||||
};
|
||||
|
||||
#endif // MARKERSDOCK_H
|
||||
191
src/docks/notesdock.cpp
Normal file
191
src/docks/notesdock.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2026 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "notesdock.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "actions.h"
|
||||
#include "dialogs/speechdialog.h"
|
||||
#include "jobqueue.h"
|
||||
#include "jobs/kokorodokijob.h"
|
||||
#include "mltcontroller.h"
|
||||
#include "settings.h"
|
||||
#include "util.h"
|
||||
#include "widgets/docktoolbar.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QIcon>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWheelEvent>
|
||||
|
||||
class TextEditor : public QPlainTextEdit
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(TextEditor)
|
||||
|
||||
public:
|
||||
explicit TextEditor(QWidget *parent = nullptr)
|
||||
: QPlainTextEdit()
|
||||
{
|
||||
setObjectName("Notes");
|
||||
zoomIn(Settings.notesZoom());
|
||||
setTabChangesFocus(false);
|
||||
setTabStopDistance(fontMetrics().horizontalAdvance("XXXX")); // Tabstop = 4 spaces
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
auto icon = QIcon::fromTheme("zoom-out", QIcon(":/icons/oxygen/32x32/actions/zoom-out.png"));
|
||||
auto action = new QAction(icon, tr("Decrease Text Size"), this);
|
||||
action->setShortcut(Qt::CTRL | Qt::ALT | Qt::Key_Minus);
|
||||
addAction(action);
|
||||
Actions.add("notesDecreaseTextSize", action, objectName());
|
||||
connect(action, &QAction::triggered, this, [=]() { setZoom(-4); });
|
||||
|
||||
icon = QIcon::fromTheme("zoom-in", QIcon(":/icons/oxygen/32x32/actions/zoom-in.png"));
|
||||
action = new QAction(icon, tr("Increase Text Size"), this);
|
||||
action->setShortcut(Qt::CTRL | Qt::ALT | Qt::Key_Equal);
|
||||
addAction(action);
|
||||
Actions.add("notesIncreaseTextSize", action, objectName());
|
||||
connect(action, &QAction::triggered, this, [=]() { setZoom(4); });
|
||||
|
||||
#ifdef EXTERNAL_LAUNCHERS
|
||||
icon = QIcon::fromTheme("text-speak", QIcon(":/icons/oxygen/32x32/actions/text-speak.png"));
|
||||
action = new QAction(icon, tr("Text to Speech..."), this);
|
||||
addAction(action);
|
||||
Actions.add("notesTextToSpeech", action, objectName());
|
||||
connect(action, &QAction::triggered, this, [=] {
|
||||
if (toPlainText().isEmpty() || !KokorodokiJob::checkDockerImage(this))
|
||||
return;
|
||||
|
||||
m_speechDialog.reset(new SpeechDialog(this));
|
||||
if (m_speechDialog->exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
KokorodokiJob::prepareAndRun(this, [=]() {
|
||||
const auto outFile = m_speechDialog->outputFile();
|
||||
if (outFile.isEmpty())
|
||||
return;
|
||||
QFileInfo outInfo(outFile);
|
||||
auto txtFile = new QTemporaryFile(outInfo.dir().filePath("XXXXXX.txt"));
|
||||
|
||||
if (!txtFile->open()) {
|
||||
LOG_ERROR() << "Failed to create temp text file" << txtFile->fileName();
|
||||
txtFile->deleteLater();
|
||||
return;
|
||||
}
|
||||
txtFile->write(toPlainText().toUtf8());
|
||||
txtFile->close();
|
||||
|
||||
const auto lang = m_speechDialog->languageCode();
|
||||
const auto voice = m_speechDialog->voiceCode();
|
||||
const auto spd = m_speechDialog->speed();
|
||||
auto job = new KokorodokiJob(txtFile->fileName(), outFile, lang, voice, spd);
|
||||
|
||||
txtFile->setParent(job); // auto-delete with job
|
||||
job->setPostJobAction(new OpenPostJobAction(outFile, outFile, QString()));
|
||||
JOBS.add(job);
|
||||
});
|
||||
});
|
||||
#endif
|
||||
|
||||
connect(this, &QWidget::customContextMenuRequested, this, [=](const QPoint &pos) {
|
||||
std::unique_ptr<QMenu> menu{createStandardContextMenu()};
|
||||
actions().at(0)->setEnabled(Settings.notesZoom() > 0);
|
||||
menu->addActions(actions());
|
||||
menu->exec(mapToGlobal(pos));
|
||||
});
|
||||
}
|
||||
|
||||
void setZoom(int delta)
|
||||
{
|
||||
auto zoom = Settings.notesZoom();
|
||||
zoomIn((zoom + delta >= 0) ? delta : -zoom);
|
||||
Settings.setNotesZoom(std::max<int>(0, zoom + delta));
|
||||
}
|
||||
|
||||
protected:
|
||||
void wheelEvent(QWheelEvent *event) override
|
||||
{
|
||||
if (event->modifiers() & Qt::ControlModifier) {
|
||||
setZoom((event->angleDelta().y() < 0) ? -1 : 1);
|
||||
event->accept();
|
||||
} else {
|
||||
QPlainTextEdit::wheelEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<SpeechDialog> m_speechDialog;
|
||||
};
|
||||
|
||||
NotesDock::NotesDock(QWidget *parent)
|
||||
: QDockWidget(tr("Notes"), parent)
|
||||
, m_textEdit(new TextEditor(this))
|
||||
, m_blockUpdate(false)
|
||||
{
|
||||
LOG_DEBUG() << "begin";
|
||||
setObjectName("NotesDock");
|
||||
QIcon icon = QIcon::fromTheme("document-edit",
|
||||
QIcon(":/icons/oxygen/32x32/actions/document-edit.png"));
|
||||
toggleViewAction()->setIcon(icon);
|
||||
setWhatsThis("https://forum.shotcut.org/t/notes-panel/33110/1");
|
||||
|
||||
QObject::connect(m_textEdit, SIGNAL(textChanged()), SLOT(onTextChanged()));
|
||||
// Wrap the text editor with a container so we can place a toolbar beneath it.
|
||||
auto container = new QWidget(this);
|
||||
auto layout = new QVBoxLayout(container);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(m_textEdit, /*stretch*/ 1);
|
||||
|
||||
// Create a DockToolBar to hold custom actions defined in TextEditor.
|
||||
auto toolbar = new DockToolBar(tr("Notes Controls"), container);
|
||||
toolbar->setAreaHint(Qt::BottomToolBarArea);
|
||||
const auto actions = m_textEdit->actions();
|
||||
toolbar->addAction(actions.at(0)); // Decrease Text Size
|
||||
toolbar->addAction(actions.at(1)); // Increase Text Size
|
||||
#ifdef EXTERNAL_LAUNCHERS
|
||||
toolbar->addSeparator();
|
||||
toolbar->addAction(actions.at(2)); // Text to Speech
|
||||
#endif
|
||||
layout->addWidget(toolbar, /*stretch*/ 0);
|
||||
QDockWidget::setWidget(container);
|
||||
|
||||
LOG_DEBUG() << "end";
|
||||
}
|
||||
|
||||
QString NotesDock::getText()
|
||||
{
|
||||
return m_textEdit->toPlainText();
|
||||
}
|
||||
|
||||
void NotesDock::setText(const QString &text)
|
||||
{
|
||||
m_blockUpdate = true;
|
||||
m_textEdit->setPlainText(text);
|
||||
m_blockUpdate = false;
|
||||
}
|
||||
|
||||
void NotesDock::onTextChanged()
|
||||
{
|
||||
if (!m_blockUpdate) {
|
||||
emit modified();
|
||||
}
|
||||
}
|
||||
46
src/docks/notesdock.h
Normal file
46
src/docks/notesdock.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2022-2025 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef NOTESDOCK_H
|
||||
#define NOTESDOCK_H
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QObject>
|
||||
|
||||
class TextEditor;
|
||||
|
||||
class NotesDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NotesDock(QWidget *parent = 0);
|
||||
QString getText();
|
||||
void setText(const QString &text);
|
||||
|
||||
signals:
|
||||
void modified();
|
||||
|
||||
private slots:
|
||||
void onTextChanged();
|
||||
|
||||
private:
|
||||
TextEditor *m_textEdit;
|
||||
bool m_blockUpdate;
|
||||
};
|
||||
|
||||
#endif // NOTESDOCK_H
|
||||
2179
src/docks/playlistdock.cpp
Normal file
2179
src/docks/playlistdock.cpp
Normal file
File diff suppressed because it is too large
Load Diff
166
src/docks/playlistdock.h
Normal file
166
src/docks/playlistdock.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2025 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PLAYLISTDOCK_H
|
||||
#define PLAYLISTDOCK_H
|
||||
|
||||
#include "models/playlistmodel.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QTimer>
|
||||
#include <QTreeWidget>
|
||||
#include <QUndoCommand>
|
||||
|
||||
namespace Ui {
|
||||
class PlaylistDock;
|
||||
}
|
||||
|
||||
class QAbstractItemView;
|
||||
class QItemSelectionModel;
|
||||
class QLabel;
|
||||
class QMenu;
|
||||
class PlaylistIconView;
|
||||
class PlaylistProxyModel;
|
||||
class LineEditClear;
|
||||
|
||||
class BinTree : public QTreeWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BinTree(QWidget *parent = nullptr)
|
||||
: QTreeWidget(parent)
|
||||
{}
|
||||
|
||||
signals:
|
||||
void copied(QString);
|
||||
void moved(QList<int>, QPointF);
|
||||
|
||||
protected:
|
||||
void dropEvent(QDropEvent *event);
|
||||
};
|
||||
|
||||
class PlaylistDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum SmartBin {
|
||||
SmartBinNone = -1,
|
||||
SmartBinAll,
|
||||
SmartBinDuplicates,
|
||||
SmartBinNotInBin,
|
||||
SmartBinNotInTimeline,
|
||||
SmartBinCount
|
||||
};
|
||||
|
||||
explicit PlaylistDock(QWidget *parent = 0);
|
||||
~PlaylistDock();
|
||||
PlaylistModel *model() { return &m_model; }
|
||||
int position();
|
||||
void replaceClipsWithHash(const QString &hash, Mlt::Producer &producer);
|
||||
void getSelectionRange(int *start, int *end);
|
||||
Mlt::Playlist *binPlaylist();
|
||||
static void sortBins(QTreeWidget *treeWidget);
|
||||
|
||||
signals:
|
||||
void clipOpened(Mlt::Producer *producer, bool play = false);
|
||||
void itemActivated(int start);
|
||||
void showStatusMessage(QString);
|
||||
void addAllTimeline(Mlt::Playlist *, bool skipProxy = false, bool emptyTrack = false);
|
||||
void producerOpened();
|
||||
void selectionChanged();
|
||||
void enableUpdate(bool);
|
||||
|
||||
public slots:
|
||||
void onOpenActionTriggered();
|
||||
void onAppendCutActionTriggered();
|
||||
void onProducerOpened();
|
||||
void onInChanged();
|
||||
void onOutChanged();
|
||||
void onProducerChanged(Mlt::Producer *producer);
|
||||
void onProducerModified();
|
||||
void onPlayerDragStarted();
|
||||
void onPlaylistModified();
|
||||
void onPlaylistCreated();
|
||||
void onPlaylistLoaded();
|
||||
void onPlaylistCleared();
|
||||
void onPlaylistClosed();
|
||||
void refreshTimelineSmartBins();
|
||||
|
||||
private slots:
|
||||
|
||||
void viewCustomContextMenuRequested(const QPoint &pos);
|
||||
void viewDoubleClicked(const QModelIndex &index);
|
||||
void onDropped(const QMimeData *data, int row);
|
||||
void onMoveClip(int from, int to);
|
||||
void onMovedToEnd();
|
||||
void onInTimerFired();
|
||||
void onOutTimerFired();
|
||||
void onMediaTypeClicked();
|
||||
void on_treeWidget_itemSelectionChanged();
|
||||
void clearStatus();
|
||||
void updateStatus();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
void keyReleaseEvent(QKeyEvent *event);
|
||||
|
||||
private:
|
||||
void setupActions();
|
||||
void resetPlaylistIndex();
|
||||
void emitDataChanged(const QVector<int> &roles);
|
||||
void setPlaylistIndex(Mlt::Producer *producer, int row);
|
||||
void updateViewMode();
|
||||
void onAddFilesActionTriggered();
|
||||
void onUpdateThumbnailsActionTriggered();
|
||||
void onAddToTimelineActionTriggered();
|
||||
void onAddToSlideshowActionTriggered();
|
||||
void onSetFileDateActionTriggered();
|
||||
void onRemoveAllActionTriggered();
|
||||
void onGotoActionTriggered();
|
||||
void onCopyActionTriggered();
|
||||
void onSelectAllActionTriggered();
|
||||
void onInsertCutActionTriggered();
|
||||
void onUpdateActionTriggered();
|
||||
void onRemoveActionTriggered();
|
||||
void incrementIndex(int step);
|
||||
void setIndex(int row);
|
||||
void moveClipUp();
|
||||
void moveClipDown();
|
||||
void addFiles(int row, const QList<QUrl> &urls);
|
||||
void loadBins();
|
||||
void sortBins();
|
||||
void assignToBin(Mlt::Properties &properties, QString bin = QString());
|
||||
|
||||
Ui::PlaylistDock *ui;
|
||||
QAbstractItemView *m_view;
|
||||
PlaylistIconView *m_iconsView;
|
||||
PlaylistModel m_model;
|
||||
QItemSelectionModel *m_selectionModel;
|
||||
int m_defaultRowHeight;
|
||||
QTimer m_inChangedTimer;
|
||||
QTimer m_outChangedTimer;
|
||||
QMenu *m_mainMenu;
|
||||
bool m_blockResizeColumnsToContents;
|
||||
PlaylistProxyModel *m_proxyModel;
|
||||
Mlt::Playlist m_binPlaylist;
|
||||
LineEditClear *m_searchField;
|
||||
QLabel *m_label;
|
||||
};
|
||||
|
||||
#endif // PLAYLISTDOCK_H
|
||||
196
src/docks/playlistdock.ui
Normal file
196
src/docks/playlistdock.ui
Normal file
@@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PlaylistDock</class>
|
||||
<widget class="QDockWidget" name="PlaylistDock">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>460</width>
|
||||
<height>278</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string notr="true">https://forum.shotcut.org/t/about-the-playlist/12951/1</string>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Playlist</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="textBrowser">
|
||||
<property name="html">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body>
|
||||
<p style=" margin-top:12px; margin-bottom:16px; margin-left:-24px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Double-click</span> a playlist item to open it in the player.</p>
|
||||
<p style=" margin-top:0px; margin-bottom:16px; margin-left:-24px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You can freely preview clips without necessarily adding them to the playlist or closing it.</p>
|
||||
<p style=" margin-top:0px; margin-bottom:16px; margin-left:-24px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">To trim or adjust a playlist item <span style=" font-weight:600;">Double-click</span> to open it, make the changes, and click the <span style=" font-weight:600;">Update</span> icon.</p>
|
||||
<p style=" margin-top:0px; margin-bottom:16px; margin-left:-24px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Drag-n-drop</span> to rearrange the items.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="filtersLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="BinTree" name="treeWidget">
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QTreeWidget::item { padding: 10px }</string>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="PlaylistTable" name="tableView">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Double-click a playlist item to open it in the player.</p></body></html></string>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SelectionMode::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollMode::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="PlaylistListView" name="listView">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SelectionMode::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Movement::Static</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>PlaylistTable</class>
|
||||
<extends>QTableView</extends>
|
||||
<header>widgets/playlisttable.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>PlaylistListView</class>
|
||||
<extends>QListView</extends>
|
||||
<header>widgets/playlistlistview.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>BinTree</class>
|
||||
<extends>QTreeWidget</extends>
|
||||
<header>docks/playlistdock.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../icons/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
167
src/docks/recentdock.cpp
Normal file
167
src/docks/recentdock.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2026 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "recentdock.h"
|
||||
#include "ui_recentdock.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "settings.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QDir>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
|
||||
static const int MaxItems = 200;
|
||||
|
||||
RecentDock::RecentDock(QWidget *parent)
|
||||
: QDockWidget(parent)
|
||||
, ui(new Ui::RecentDock)
|
||||
{
|
||||
LOG_DEBUG() << "begin";
|
||||
ui->setupUi(this);
|
||||
QIcon icon = QIcon::fromTheme("document-open-recent",
|
||||
QIcon(":/icons/oxygen/32x32/actions/document-open-recent.png"));
|
||||
toggleViewAction()->setIcon(icon);
|
||||
m_recent = Settings.recent();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// Remove bad entries on Windows due to bug in v17.01.
|
||||
QStringList newList;
|
||||
bool isRepaired = false;
|
||||
foreach (QString s, m_recent) {
|
||||
if (s.size() >= 3 && s[0] == '/' && s[2] == ':') {
|
||||
s.remove(0, 1);
|
||||
isRepaired = true;
|
||||
}
|
||||
newList << s;
|
||||
}
|
||||
if (isRepaired) {
|
||||
m_recent = newList;
|
||||
Settings.setRecent(m_recent);
|
||||
Settings.sync();
|
||||
}
|
||||
#endif
|
||||
|
||||
ui->listWidget->setDragEnabled(true);
|
||||
ui->listWidget->setDragDropMode(QAbstractItemView::DragOnly);
|
||||
foreach (QString s, m_recent) {
|
||||
QStandardItem *item = new QStandardItem(Util::baseName(s));
|
||||
item->setToolTip(QDir::toNativeSeparators(s));
|
||||
m_model.appendRow(item);
|
||||
}
|
||||
m_proxyModel.setSourceModel(&m_model);
|
||||
m_proxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
ui->listWidget->setModel(&m_proxyModel);
|
||||
LOG_DEBUG() << "end";
|
||||
}
|
||||
|
||||
RecentDock::~RecentDock()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void RecentDock::add(const QString &s)
|
||||
{
|
||||
// Block anything big that can cause a performance problem
|
||||
if (s.size() > ShotcutSettings::MaxPath)
|
||||
return;
|
||||
|
||||
QString filePath = QDir::fromNativeSeparators(s);
|
||||
if (filePath.startsWith(QDir::tempPath()))
|
||||
return;
|
||||
QString name = remove(s);
|
||||
QStandardItem *item = new QStandardItem(name);
|
||||
item->setToolTip(QDir::toNativeSeparators(s));
|
||||
m_model.insertRow(0, item);
|
||||
m_recent.prepend(filePath);
|
||||
while (m_recent.count() > MaxItems)
|
||||
m_recent.removeLast();
|
||||
Settings.setRecent(m_recent);
|
||||
if (filePath.endsWith(".mlt")) {
|
||||
auto projects = Settings.projects();
|
||||
projects.removeOne(filePath);
|
||||
projects.prepend(filePath);
|
||||
Settings.setProjects(projects);
|
||||
}
|
||||
}
|
||||
|
||||
void RecentDock::on_listWidget_activated(const QModelIndex &i)
|
||||
{
|
||||
ui->listWidget->setCurrentIndex(QModelIndex());
|
||||
emit itemActivated(m_proxyModel.itemData(i)[Qt::ToolTipRole].toString());
|
||||
}
|
||||
|
||||
QString RecentDock::remove(const QString &s)
|
||||
{
|
||||
QString filePath = QDir::fromNativeSeparators(s);
|
||||
m_recent.removeOne(filePath);
|
||||
Settings.setRecent(m_recent);
|
||||
|
||||
QString name = Util::baseName(filePath);
|
||||
QList<QStandardItem *> items = m_model.findItems(name);
|
||||
if (items.count() > 0)
|
||||
m_model.removeRow(items.first()->row());
|
||||
return name;
|
||||
}
|
||||
|
||||
void RecentDock::find()
|
||||
{
|
||||
ui->lineEdit->setFocus();
|
||||
ui->lineEdit->selectAll();
|
||||
}
|
||||
|
||||
void RecentDock::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) {
|
||||
on_actionDelete_triggered();
|
||||
} else {
|
||||
QDockWidget::keyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void RecentDock::on_lineEdit_textChanged(const QString &search)
|
||||
{
|
||||
m_proxyModel.setFilterFixedString(search);
|
||||
}
|
||||
|
||||
void RecentDock::on_actionDelete_triggered()
|
||||
{
|
||||
if (ui->listWidget->currentIndex().isValid()) {
|
||||
auto row = ui->listWidget->currentIndex().row();
|
||||
auto url = m_recent[row];
|
||||
m_recent.removeAt(row);
|
||||
Settings.setRecent(m_recent);
|
||||
m_model.removeRow(row);
|
||||
if (url.endsWith(".mlt")) {
|
||||
auto ls = Settings.projects();
|
||||
if (ls.removeAll(url) > 0)
|
||||
Settings.setProjects(ls);
|
||||
}
|
||||
emit deleted(url);
|
||||
}
|
||||
}
|
||||
|
||||
void RecentDock::on_listWidget_customContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
if (ui->listWidget->currentIndex().isValid()) {
|
||||
QMenu menu(this);
|
||||
menu.addAction(ui->actionDelete);
|
||||
menu.exec(mapToGlobal(pos + QPoint(0, ui->lineEdit->height())));
|
||||
}
|
||||
}
|
||||
62
src/docks/recentdock.h
Normal file
62
src/docks/recentdock.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2020 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RECENTDOCK_H
|
||||
#define RECENTDOCK_H
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace Ui {
|
||||
class RecentDock;
|
||||
}
|
||||
|
||||
class RecentDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RecentDock(QWidget *parent = 0);
|
||||
~RecentDock();
|
||||
|
||||
signals:
|
||||
void itemActivated(const QString &url);
|
||||
void deleted(const QString &url);
|
||||
|
||||
public slots:
|
||||
void add(const QString &);
|
||||
QString remove(const QString &s);
|
||||
void find();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
|
||||
private:
|
||||
Ui::RecentDock *ui;
|
||||
QStringList m_recent;
|
||||
QStandardItemModel m_model;
|
||||
QSortFilterProxyModel m_proxyModel;
|
||||
|
||||
private slots:
|
||||
void on_listWidget_activated(const QModelIndex &i);
|
||||
void on_lineEdit_textChanged(const QString &search);
|
||||
void on_actionDelete_triggered();
|
||||
void on_listWidget_customContextMenuRequested(const QPoint &pos);
|
||||
};
|
||||
|
||||
#endif // RECENTDOCK_H
|
||||
93
src/docks/recentdock.ui
Normal file
93
src/docks/recentdock.ui
Normal file
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RecentDock</class>
|
||||
<widget class="QDockWidget" name="RecentDock">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>396</width>
|
||||
<height>296</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>141</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string notr="true">https://forum.shotcut.org/t/recent-panel/12941/1</string>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Recent</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="LineEditClear" name="lineEdit">
|
||||
<property name="toolTip">
|
||||
<string>Show only files with name matching text</string>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListView" name="listWidget">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<action name="actionDelete">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LineEditClear</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>widgets/lineeditclear.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../icons/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
70
src/docks/scopedock.cpp
Normal file
70
src/docks/scopedock.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2015-2023 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "scopedock.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "controllers/scopecontroller.h"
|
||||
#include "mltcontroller.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QtWidgets/QScrollArea>
|
||||
|
||||
ScopeDock::ScopeDock(ScopeController *scopeController, ScopeWidget *scopeWidget)
|
||||
: QDockWidget()
|
||||
, m_scopeController(scopeController)
|
||||
, m_scopeWidget(scopeWidget)
|
||||
{
|
||||
LOG_DEBUG() << "begin";
|
||||
setObjectName(m_scopeWidget->objectName() + "Dock");
|
||||
QScrollArea *scrollArea = new QScrollArea();
|
||||
scrollArea->setFrameShape(QFrame::NoFrame);
|
||||
scrollArea->setWidgetResizable(true);
|
||||
scrollArea->setWidget(m_scopeWidget);
|
||||
QDockWidget::setWidget(scrollArea);
|
||||
QDockWidget::setWindowTitle(m_scopeWidget->getTitle());
|
||||
|
||||
connect(toggleViewAction(), SIGNAL(toggled(bool)), this, SLOT(onActionToggled(bool)));
|
||||
connect(this, &QDockWidget::dockLocationChanged, m_scopeWidget, &ScopeWidget::moved);
|
||||
LOG_DEBUG() << "end";
|
||||
}
|
||||
|
||||
void ScopeDock::resizeEvent(QResizeEvent *e)
|
||||
{
|
||||
if (width() > height()) {
|
||||
m_scopeWidget->setOrientation(Qt::Horizontal);
|
||||
} else {
|
||||
m_scopeWidget->setOrientation(Qt::Vertical);
|
||||
}
|
||||
QDockWidget::resizeEvent(e);
|
||||
}
|
||||
|
||||
void ScopeDock::onActionToggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
connect(m_scopeController,
|
||||
SIGNAL(newFrame(const SharedFrame &)),
|
||||
m_scopeWidget,
|
||||
SLOT(onNewFrame(const SharedFrame &)));
|
||||
MLT.refreshConsumer();
|
||||
} else {
|
||||
disconnect(m_scopeController,
|
||||
SIGNAL(newFrame(const SharedFrame &)),
|
||||
m_scopeWidget,
|
||||
SLOT(onNewFrame(const SharedFrame &)));
|
||||
}
|
||||
}
|
||||
48
src/docks/scopedock.h
Normal file
48
src/docks/scopedock.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SCOPEDOCK_H
|
||||
#define SCOPEDOCK_H
|
||||
|
||||
#include "widgets/scopes/scopewidget.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QObject>
|
||||
|
||||
class ScopeController;
|
||||
|
||||
class ScopeDock Q_DECL_FINAL : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ScopeDock(ScopeController *scopeController, ScopeWidget *scopeWidget);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
ScopeController *m_scopeController;
|
||||
ScopeWidget *m_scopeWidget;
|
||||
|
||||
void setWidget(QWidget *widget); // Private to disallow use
|
||||
|
||||
private slots:
|
||||
void onActionToggled(bool checked);
|
||||
};
|
||||
|
||||
#endif // SCOPEDOCK_H
|
||||
1388
src/docks/subtitlesdock.cpp
Normal file
1388
src/docks/subtitlesdock.cpp
Normal file
File diff suppressed because it is too large
Load Diff
108
src/docks/subtitlesdock.h
Normal file
108
src/docks/subtitlesdock.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SUBTITLESDOCK_H
|
||||
#define SUBTITLESDOCK_H
|
||||
|
||||
#include <MltPlaylist.h>
|
||||
#include <QDockWidget>
|
||||
|
||||
class SubtitlesModel;
|
||||
class SubtitlesSelectionModel;
|
||||
class QComboBox;
|
||||
class QItemSelection;
|
||||
class QLabel;
|
||||
class QTextEdit;
|
||||
class QTreeView;
|
||||
class SpeechDialog;
|
||||
|
||||
class SubtitlesDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SubtitlesDock(QWidget *parent = 0);
|
||||
~SubtitlesDock();
|
||||
void setModel(SubtitlesModel *model, SubtitlesSelectionModel *selectionModel);
|
||||
void importSrtFromFile(const QString &srtPath,
|
||||
const QString &trackName,
|
||||
const QString &lang,
|
||||
bool includeNonspoken);
|
||||
|
||||
signals:
|
||||
void seekRequested(int pos);
|
||||
void addAllTimeline(Mlt::Playlist *, bool skipProxy, bool emptyTrack);
|
||||
void createOrEditFilterOnOutput(Mlt::Filter *, const QStringList &key_properties);
|
||||
|
||||
private slots:
|
||||
void onPositionChanged(int position);
|
||||
void onStartColumnToggled(bool checked);
|
||||
void onEndColumnToggled(bool checked);
|
||||
void onDurationColumnToggled(bool checked);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
void setupActions();
|
||||
void onCreateOrEditRequested();
|
||||
void onAddRequested();
|
||||
void onRemoveRequested();
|
||||
void onSetStartRequested();
|
||||
void onSetEndRequested();
|
||||
void onMoveRequested();
|
||||
void onTextEdited();
|
||||
void onModelReset();
|
||||
void updateActionAvailablity();
|
||||
void addSubtitleTrack();
|
||||
void removeSubtitleTrack();
|
||||
void editSubtitleTrack();
|
||||
void refreshTracksCombo();
|
||||
void importSubtitles();
|
||||
void exportSubtitles();
|
||||
void onItemDoubleClicked(const QModelIndex &index);
|
||||
void resizeTextWidgets();
|
||||
void updateTextWidgets();
|
||||
void setCurrentItem(int trackIndex, int itemIndex);
|
||||
void refreshWidgets();
|
||||
void selectItemForTime();
|
||||
QString availableTrackName();
|
||||
bool trackNameExists(const QString &name);
|
||||
void ensureTrackExists();
|
||||
void burnInOnTimeline();
|
||||
void generateTextOnTimeline();
|
||||
void speechToText();
|
||||
void textToSpeech();
|
||||
bool findWhisperExe();
|
||||
|
||||
SubtitlesModel *m_model;
|
||||
SubtitlesSelectionModel *m_selectionModel;
|
||||
QLabel *m_addToTimelineLabel;
|
||||
QComboBox *m_trackCombo;
|
||||
QTreeView *m_treeView;
|
||||
QTextEdit *m_text;
|
||||
QTextEdit *m_prev;
|
||||
QTextEdit *m_next;
|
||||
QLabel *m_prevLabel;
|
||||
QLabel *m_textLabel;
|
||||
QLabel *m_nextLabel;
|
||||
int m_pos;
|
||||
bool m_textEditInProgress;
|
||||
std::unique_ptr<SpeechDialog> m_speechDialog;
|
||||
};
|
||||
|
||||
#endif // SUBTITLESDOCK_H
|
||||
4472
src/docks/timelinedock.cpp
Normal file
4472
src/docks/timelinedock.cpp
Normal file
File diff suppressed because it is too large
Load Diff
286
src/docks/timelinedock.h
Normal file
286
src/docks/timelinedock.h
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 Meltytech, LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TIMELINEDOCK_H
|
||||
#define TIMELINEDOCK_H
|
||||
|
||||
#include "jobs/ffmpegjob.h"
|
||||
#include "models/markersmodel.h"
|
||||
#include "models/multitrackmodel.h"
|
||||
#include "models/subtitlesmodel.h"
|
||||
#include "models/subtitlesselectionmodel.h"
|
||||
#include "sharedframe.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDockWidget>
|
||||
#include <QQuickWidget>
|
||||
#include <QTimer>
|
||||
|
||||
namespace Timeline {
|
||||
class UpdateCommand;
|
||||
class TrimCommand;
|
||||
} // namespace Timeline
|
||||
class UndoHelper;
|
||||
class QMenu;
|
||||
|
||||
class TimelineDock : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int position READ position WRITE setPosition NOTIFY positionChanged)
|
||||
Q_PROPERTY(int currentTrack READ currentTrack WRITE setCurrentTrack NOTIFY currentTrackChanged)
|
||||
Q_PROPERTY(
|
||||
QVariantList selection READ selectionForJS WRITE setSelectionFromJS NOTIFY selectionChanged)
|
||||
Q_PROPERTY(bool isRecording READ isRecording NOTIFY isRecordingChanged)
|
||||
Q_PROPERTY(int loopStart READ loopStart NOTIFY loopChanged)
|
||||
Q_PROPERTY(int loopEnd READ loopEnd NOTIFY loopChanged)
|
||||
|
||||
public:
|
||||
explicit TimelineDock(QWidget *parent = 0);
|
||||
~TimelineDock();
|
||||
|
||||
enum TrimLocation { TrimInPoint, TrimOutPoint };
|
||||
|
||||
MultitrackModel *model() { return &m_model; }
|
||||
MarkersModel *markersModel() { return &m_markersModel; }
|
||||
SubtitlesModel *subtitlesModel() { return &m_subtitlesModel; }
|
||||
SubtitlesSelectionModel *subtitlesSelectionModel() { return &m_subtitlesSelectionModel; }
|
||||
int position() const { return m_position; }
|
||||
void setPosition(int position);
|
||||
Mlt::Producer producerForClip(int trackIndex, int clipIndex);
|
||||
int clipIndexAtPlayhead(int trackIndex = -1);
|
||||
int clipIndexAtPosition(int trackIndex, int position);
|
||||
void chooseClipAtPosition(int position, int &trackIndex, int &clipIndex);
|
||||
void setCurrentTrack(int currentTrack);
|
||||
int currentTrack() const;
|
||||
int clipCount(int trackIndex) const;
|
||||
void setSelectionFromJS(const QVariantList &list);
|
||||
void setSelection(QList<QPoint> selection = QList<QPoint>(),
|
||||
int trackIndex = -1,
|
||||
bool isMultitrack = false);
|
||||
QVariantList selectionForJS() const;
|
||||
const QList<QPoint> selection() const;
|
||||
const QVector<QUuid> selectionUuids();
|
||||
const QList<QPoint> uuidsToSelection(QVector<QUuid> uuids) const;
|
||||
void saveAndClearSelection();
|
||||
Q_INVOKABLE void restoreSelection();
|
||||
Q_INVOKABLE QVariantList getGroupForClip(int trackIndex, int clipIndex);
|
||||
void selectClipUnderPlayhead();
|
||||
int centerOfClip(int trackIndex, int clipIndex);
|
||||
bool isTrackLocked(int trackIndex) const;
|
||||
void trimClipAtPlayhead(TrimLocation location, bool ripple);
|
||||
Q_INVOKABLE bool isMultitrackSelected() const { return m_selection.isMultitrackSelected; }
|
||||
Q_INVOKABLE int selectedTrack() const { return m_selection.selectedTrack; }
|
||||
Q_INVOKABLE bool isFloating() const { return QDockWidget::isFloating(); }
|
||||
Q_INVOKABLE static void openProperties();
|
||||
void emitSelectedChanged(const QVector<int> &roles);
|
||||
void replaceClipsWithHash(const QString &hash, Mlt::Producer &producer);
|
||||
Q_INVOKABLE void recordAudio();
|
||||
Q_INVOKABLE void stopRecording();
|
||||
bool isRecording() const { return m_isRecording; }
|
||||
int addTrackIfNeeded(TrackType trackType);
|
||||
void getSelectionRange(int *start, int *end);
|
||||
int loopStart() const { return m_loopStart; }
|
||||
int loopEnd() const { return m_loopEnd; }
|
||||
|
||||
signals:
|
||||
void currentTrackChanged();
|
||||
void selectionChanged();
|
||||
void seeked(int position);
|
||||
void positionChanged(int position);
|
||||
void loopChanged();
|
||||
void clipOpened(Mlt::Producer *producer);
|
||||
void dragging(const QPointF &pos, int duration);
|
||||
void dropped();
|
||||
void dropAccepted(const QString &xml);
|
||||
void gainChanged(double gain);
|
||||
void fadeInChanged(int duration);
|
||||
void fadeOutChanged(int duration);
|
||||
void selected(Mlt::Producer *producer);
|
||||
void clipClicked();
|
||||
void showStatusMessage(QString);
|
||||
void clipCopied();
|
||||
void clipMoved(int fromTrack, int toTrack, int clipIndex, int position, bool ripple);
|
||||
void filteredClicked();
|
||||
void durationChanged();
|
||||
void transitionAdded(int trackIndex, int clipIndex, int position, bool ripple);
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
void zoomToFit();
|
||||
void setZoom(double value);
|
||||
void markerRangesChanged();
|
||||
void markerSeeked(int markerIndex);
|
||||
void isRecordingChanged(bool);
|
||||
void multitrackSelected();
|
||||
void warnTrackLocked(int trackIndex);
|
||||
void refreshWaveforms();
|
||||
void updateThumbnails(int trackIndex, int clipIndex);
|
||||
void trimStarted();
|
||||
void trimEnded();
|
||||
|
||||
public slots:
|
||||
int addAudioTrack();
|
||||
int addVideoTrack();
|
||||
void alignSelectedClips();
|
||||
void onShowFrame(const SharedFrame &frame);
|
||||
void onSeeked(int position);
|
||||
void append(int trackIndex);
|
||||
void remove(int trackIndex, int clipIndex, bool ignoreTransition = false);
|
||||
bool mergeClipWithNext(int trackIndex, int clipIndex, bool dryrun);
|
||||
void lift(int trackIndex, int clipIndex, bool ignoreTransition = false);
|
||||
void removeSelection(bool withCopy = false);
|
||||
void liftSelection();
|
||||
void incrementCurrentTrack(int by);
|
||||
void selectTrackHead(int trackIndex);
|
||||
void selectMultitrack();
|
||||
void copy(int trackIndex, int clipIndex);
|
||||
void setTrackName(int trackIndex, const QString &value);
|
||||
void toggleTrackMute(int trackIndex);
|
||||
void toggleTrackHidden(int trackIndex);
|
||||
void setTrackComposite(int trackIndex, bool composite);
|
||||
void setTrackLock(int trackIndex, bool lock);
|
||||
bool moveClip(int fromTrack, int toTrack, int clipIndex, int position, bool ripple);
|
||||
void onClipMoved(int fromTrack, int toTrack, int clipIndex, int position, bool ripple);
|
||||
bool trimClipIn(
|
||||
int trackIndex, int clipIndex, int oldClipIndex, int delta, bool ripple, bool roll);
|
||||
bool trimClipOut(int trackIndex, int clipIndex, int delta, bool ripple, bool roll);
|
||||
void insert(int trackIndex, int position = -1, const QString &xml = QString(), bool seek = true);
|
||||
void overwrite(int trackIndex,
|
||||
int position = -1,
|
||||
const QString &xml = QString(),
|
||||
bool seek = true);
|
||||
void appendFromPlaylist(Mlt::Playlist *playlist, bool skipProxy, bool emptyTrack);
|
||||
bool changeGain(int trackIndex, int clipIndex, double gain);
|
||||
void fadeIn(int trackIndex, int clipIndex = -1, int duration = -1);
|
||||
void fadeOut(int trackIndex, int clipIndex = -1, int duration = -1);
|
||||
void seekPreviousEdit();
|
||||
void seekNextEdit();
|
||||
void seekInPoint(int clipIndex);
|
||||
void clearSelectionIfInvalid();
|
||||
void insertTrack();
|
||||
void insertAudioTrack();
|
||||
void insertVideoTrack();
|
||||
void removeTrack();
|
||||
void moveTrack(int fromTrackIndex, int toTrackIndex);
|
||||
void moveTrackUp();
|
||||
void moveTrackDown();
|
||||
void onProducerChanged(Mlt::Producer *);
|
||||
void emitSelectedFromSelection();
|
||||
void remakeAudioLevels(int trackIndex, int clipIndex, bool force = true);
|
||||
void commitTrimCommand();
|
||||
void onRowsInserted(const QModelIndex &parent, int first, int last);
|
||||
void onRowsRemoved(const QModelIndex &parent, int first, int last);
|
||||
void onRowsMoved(
|
||||
const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
|
||||
void detachAudio(int trackIndex, int clipIndex);
|
||||
void selectAll();
|
||||
void selectAllOnCurrentTrack();
|
||||
void onProducerModified();
|
||||
void replace(int trackIndex, int clipIndex, const QString &xml = QString());
|
||||
void createOrEditMarker();
|
||||
void createOrEditSelectionMarker();
|
||||
void createMarker();
|
||||
void editMarker(int markerIndex);
|
||||
void deleteMarker(int markerIndex = -1);
|
||||
void seekNextMarker();
|
||||
void seekPrevMarker();
|
||||
void onFilterModelChanged();
|
||||
void trimClipIn(bool ripple = false);
|
||||
void trimClipOut(bool ripple = false);
|
||||
void initLoad();
|
||||
void handleDrop(int trackIndex, int position, QString xml);
|
||||
void onLoopChanged(int start, int end);
|
||||
|
||||
protected:
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
void dragMoveEvent(QDragMoveEvent *event);
|
||||
void dragLeaveEvent(QDragLeaveEvent *event);
|
||||
void dropEvent(QDropEvent *event);
|
||||
bool event(QEvent *event);
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
void keyReleaseEvent(QKeyEvent *event);
|
||||
|
||||
private:
|
||||
bool isBlank(int trackIndex, int clipIndex);
|
||||
bool clipsAreSelected();
|
||||
bool blankIsSelected();
|
||||
bool nothingIsSelected();
|
||||
bool isTransition(int trackIndex, int clipIndex);
|
||||
bool isEmptyTrack(Mlt::Playlist &playlist);
|
||||
void emitNonSeekableWarning();
|
||||
void addTrackIfNeeded(int mltTrackIndex, Mlt::Producer *srcTrack);
|
||||
void setupActions();
|
||||
bool isMultitrackValid() { return m_model.tractor() && !m_model.trackList().empty(); }
|
||||
void reportSelectionChange();
|
||||
void applyCopiedFiltersToSelectdClips();
|
||||
void insertOrOverwriteDrop(int trackIndex, int position, const QString &xml);
|
||||
void freezeFrame();
|
||||
void addGenerator(QWidget *widget);
|
||||
|
||||
QQuickWidget m_quickView;
|
||||
MultitrackModel m_model;
|
||||
MarkersModel m_markersModel;
|
||||
SubtitlesModel m_subtitlesModel;
|
||||
SubtitlesSelectionModel m_subtitlesSelectionModel;
|
||||
int m_position{-1};
|
||||
std::unique_ptr<Timeline::UpdateCommand> m_updateCommand;
|
||||
bool m_ignoreNextPositionChange{false};
|
||||
struct Selection
|
||||
{
|
||||
QList<QPoint> selectedClips; // x is the clip index, y is the track index
|
||||
int selectedTrack;
|
||||
bool isMultitrackSelected;
|
||||
};
|
||||
Selection m_selection;
|
||||
int m_savedSelectedTrack{-1};
|
||||
bool m_savedIsMultitrackSelected{false};
|
||||
QVector<QUuid> m_savedSelectionUuids;
|
||||
QTimer m_selectionSignalTimer;
|
||||
std::unique_ptr<Timeline::TrimCommand> m_trimCommand;
|
||||
std::unique_ptr<UndoHelper> m_undoHelper;
|
||||
int m_trimDelta{0};
|
||||
int m_transitionDelta{0};
|
||||
bool m_isRecording{false};
|
||||
std::unique_ptr<AbstractJob> m_recordJob;
|
||||
QTimer m_recordingTimer;
|
||||
QDateTime m_recordingTime;
|
||||
int m_recordingTrackIndex{-1};
|
||||
int m_recordingClipIndex{-1};
|
||||
int m_currentTrack{0};
|
||||
QMenu *m_mainMenu{nullptr};
|
||||
QMenu *m_clipMenu{nullptr};
|
||||
int m_loopStart{-1};
|
||||
int m_loopEnd{-1};
|
||||
|
||||
private slots:
|
||||
void load(bool force);
|
||||
void onTopLevelChanged(bool floating);
|
||||
void onTransitionAdded(int trackIndex, int clipIndex, int position, bool ripple);
|
||||
void selectClip(int trackIndex, int clipIndex);
|
||||
void onMultitrackClosed();
|
||||
void reloadTimelineModels();
|
||||
void onRecordStarted();
|
||||
void updateRecording();
|
||||
void onRecordFinished(AbstractJob *, bool);
|
||||
void onWarnTrackLocked();
|
||||
void onTimelineRightClicked();
|
||||
void onClipRightClicked();
|
||||
void onNoMoreEmptyTracks(bool isAudio);
|
||||
void addGenerator();
|
||||
};
|
||||
|
||||
#endif // TIMELINEDOCK_H
|
||||
Reference in New Issue
Block a user