Commit 7008e1d9 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Cleanup and reintroduce clip analysis, add drag&drop from analysis to transition panel

parent 9b11a9cd
......@@ -347,6 +347,9 @@ Bin::~Bin()
{
blockSignals(true);
setEnabled(false);
foreach (QWidget * w, m_propertiesPanel->findChildren<ClipPropertiesController*>()) {
delete w;
}
if (m_rootFolder) {
while (!m_rootFolder->isEmpty()) {
AbstractProjectItem *child = m_rootFolder->at(0);
......@@ -1232,6 +1235,9 @@ void Bin::showClipProperties(ProjectClip *clip)
connect(panel, SIGNAL(updateClipProperties(const QString &, QMap<QString, QString>, QMap<QString, QString>)), this, SLOT(slotEditClipCommand(const QString &, QMap<QString, QString>, QMap<QString, QString>)));
connect(panel, SIGNAL(seekToFrame(int)), m_monitor, SLOT(slotSeek(int)));
connect(panel, SIGNAL(addMarkers(QString,QList<CommentedTime>)), this, SLOT(slotAddClipMarker(QString,QList<CommentedTime>)));
connect(panel, SIGNAL(editAnalysis(QString,QString,QString)), this, SLOT(slotAddClipExtraData(QString,QString,QString)));
connect(panel, SIGNAL(loadMarkers(QString)), this, SLOT(slotLoadClipMarkers(QString)));
connect(panel, SIGNAL(saveMarkers(QString)), this, SLOT(slotSaveClipMarkers(QString)));
lay->addWidget(panel);
......@@ -1240,7 +1246,7 @@ void Bin::showClipProperties(ProjectClip *clip)
void Bin::slotEditClipCommand(const QString &id, QMap<QString, QString>oldProps, QMap<QString, QString>newProps)
{
EditClipCommand *command = new EditClipCommand(m_doc, id, oldProps, newProps, true);
EditClipCommand *command = new EditClipCommand(this, id, oldProps, newProps, true);
m_doc->commandStack()->push(command);
}
......@@ -1850,7 +1856,7 @@ void Bin::slotCancelRunningJob(const QString &id, const QMap<QString, QString> &
oldProps.insert(i.key(), value);
}
if (newProps == oldProps) return;
EditClipCommand *command = new EditClipCommand(m_doc, id, oldProps, newProps, true);
EditClipCommand *command = new EditClipCommand(this, id, oldProps, newProps, true);
m_doc->commandStack()->push(command);
}
......@@ -2040,9 +2046,8 @@ void Bin::slotGotFilterJobResults(QString id, int , int , stringMap results, str
}
if (!dataProcessed || filterInfo.contains("storedata")) {
// Store returned data as clip extra data
//TODO find a way to store analysis data into clip controller
//clip->setAnalysisData(filterInfo.contains("displaydataname") ? filterInfo.value("displaydataname") : key, results.value(key), filterInfo.value("offset").toInt());
//emit updateAnalysisData(clip->referencedClip());
QStringList newValue = clip->updatedAnalysisData(key, results.value(key), filterInfo.value("offset").toInt());
slotAddClipExtraData(id, newValue.at(0), newValue.at(1));
}
}
......@@ -2057,7 +2062,6 @@ void Bin::slotAddClipMarker(const QString &id, QList <CommentedTime> newMarkers,
clip->addClipMarker(newMarkers, groupCommand);
if (groupCommand->childCount() > 0) m_doc->commandStack()->push(groupCommand);
else delete groupCommand;
}
void Bin::slotLoadClipMarkers(const QString &id)
......@@ -2335,3 +2339,25 @@ void Bin::slotRefreshClipThumbnail(const QString &id)
if (!clip) return;
clip->reloadProducer(true);
}
void Bin::slotAddClipExtraData(const QString &id, const QString &key, const QString &data, QUndoCommand *groupCommand)
{
ProjectClip *clip = m_rootFolder->clip(id);
if (!clip) return;
QString oldValue = clip->getProducerProperty(key);
QMap <QString, QString> oldProps;
oldProps.insert(key, oldValue);
QMap <QString, QString> newProps;
newProps.insert(key, data);
EditClipCommand *command = new EditClipCommand(this, id, oldProps, newProps, true, groupCommand);
if (!groupCommand) m_doc->commandStack()->push(command);
}
void Bin::slotUpdateClipProperties(const QString &id, QMap <QString, QString> properties, bool refreshPropertiesPanel)
{
ProjectClip *clip = m_rootFolder->clip(id);
if (clip) {
clip->setProperties(properties, refreshPropertiesPanel);
}
}
......@@ -519,6 +519,9 @@ public slots:
void slotCreateAudioThumb(const QString &id);
/** @brief Abort audio thumbnail for clip with id */
void slotAbortAudioThumb(const QString &id);
/** @brief Add extra data to a clip. */
void slotAddClipExtraData(const QString &id, const QString &key, const QString &data = QString(), QUndoCommand *groupCommand = 0);
void slotUpdateClipProperties(const QString &id, QMap <QString, QString> properties, bool refreshPropertiesPanel);
protected:
void contextMenuEvent(QContextMenuEvent *event);
......@@ -598,6 +601,8 @@ signals:
void requesteInvalidRemoval(const QString &, QUrl);
/** @brief Markers changed, refresh panel. */
void refreshPanelMarkers();
/** @brief Analysis data changed, refresh panel. */
void updateAnalysisData(const QString &);
};
#endif
......@@ -204,3 +204,28 @@ void AddBinClipCutCommand::redo()
m_bin->removeClipCut(m_clipId, m_in, m_out);
}
}
EditClipCommand::EditClipCommand(Bin *bin, const QString &id, const QMap <QString, QString> &oldparams, const QMap <QString, QString> &newparams, bool doIt, QUndoCommand * parent) :
QUndoCommand(parent),
m_bin(bin),
m_oldparams(oldparams),
m_newparams(newparams),
m_id(id),
m_doIt(doIt),
m_firstExec(true)
{
setText(i18n("Edit clip"));
}
// virtual
void EditClipCommand::undo()
{
m_bin->slotUpdateClipProperties(m_id, m_oldparams, true);
}
// virtual
void EditClipCommand::redo()
{
if (m_doIt)
m_bin->slotUpdateClipProperties(m_id, m_newparams, !m_firstExec);
m_doIt = true;
m_firstExec = false;
}
......@@ -23,6 +23,7 @@
#include <QUndoCommand>
#include <QDomElement>
#include <QMap>
class Bin;
......@@ -134,5 +135,24 @@ private:
bool m_addCut;
};
class EditClipCommand : public QUndoCommand
{
public:
EditClipCommand(Bin *bin, const QString &id, const QMap <QString, QString> &oldparams, const QMap <QString, QString> &newparams, bool doIt, QUndoCommand * parent = 0);
void undo();
void redo();
private:
Bin *m_bin;
QMap <QString, QString> m_oldparams;
QMap <QString, QString> m_newparams;
QString m_id;
/** @brief Should this command be executed on first redo ? TODO: we should refactor the code to get rid of this and always execute actions through the command system.
*. */
bool m_doIt;
/** @brief This value is true is this is the first time we execute the command, false otherwise. This allows us to refresh the properties panel
* only on the later executions of the command, since on the first execution, the properties panel already contains the correct info. */
bool m_firstExec;
};
#endif
......@@ -39,7 +39,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QCryptographicHash>
#include <QtConcurrent>
#include <KLocalizedString>
#include <KMessageBox>
ProjectClip::ProjectClip(const QString &id, QIcon thumb, ClipController *controller, ProjectFolder* parent) :
......@@ -58,7 +58,7 @@ ProjectClip::ProjectClip(const QString &id, QIcon thumb, ClipController *control
m_type = m_controller->clipType();
getFileHash();
setParent(parent);
bin()->loadSubClips(id, m_controller->getSubClips());
bin()->loadSubClips(id, m_controller->getPropertiesFromPrefix("kdenlive:clipzone."));
if (KdenliveSettings::audiothumbnails()) {
m_audioThumbsThread = QtConcurrent::run(this, &ProjectClip::slotCreateAudioThumbs);
}
......@@ -479,12 +479,14 @@ void ProjectClip::setProperties(QMap <QString, QString> properties, bool refresh
{
QMapIterator<QString, QString> i(properties);
bool refreshProducer = false;
bool refreshAnalysis = false;
QStringList keys;
keys << "luma_duration" << "luma_file" << "fade" << "ttl" << "softness" << "crop" << "animation";
while (i.hasNext()) {
i.next();
setProducerProperty(i.key(), i.value());
if (m_type == SlideShow && keys.contains(i.key())) refreshProducer = true;
if (i.key().startsWith("kdenlive:clipanalysis")) refreshAnalysis = true;
}
if (properties.contains("kdenlive:proxy")) {
QString value = properties.value("kdenlive:proxy");
......@@ -514,6 +516,7 @@ void ProjectClip::setProperties(QMap <QString, QString> properties, bool refresh
if (properties.contains("xmldata")) {
refreshProducer = true;
}
if (refreshAnalysis) emit refreshAnalysisPanel();
if (properties.contains("length")) {
m_duration = m_controller->getStringDuration();
bin()->emitItemUpdated(this);
......@@ -556,6 +559,7 @@ ClipPropertiesController *ProjectClip::buildProperties(QWidget *parent)
{
ClipPropertiesController *panel = new ClipPropertiesController(bin()->projectTimecode(), m_controller, parent);
connect(this, SIGNAL(refreshPropertiesPanel()), panel, SLOT(slotReloadProperties()));
connect(this, SIGNAL(refreshAnalysisPanel()), panel, SLOT(slotFillAnalysisData()));
return panel;
}
......@@ -885,3 +889,70 @@ bool ProjectClip::isTransparent() const
if (m_type == Image && m_controller->int_property("kdenlive:transparency") == 1) return true;
return false;
}
QStringList ProjectClip::updatedAnalysisData(const QString &name, const QString &data, int offset)
{
if (data.isEmpty()) {
// Remove data
return QStringList() << QString("kdenlive:clipanalysis." + name) << QString();
//m_controller->resetProperty("kdenlive:clipanalysis." + name);
}
else {
QString current = m_controller->property("kdenlive:clipanalysis." + name);
if (!current.isEmpty()) {
if (KMessageBox::questionYesNo(QApplication::activeWindow(), i18n("Clip already contains analysis data %1", name), QString(), KGuiItem(i18n("Merge")), KGuiItem(i18n("Add"))) == KMessageBox::Yes) {
// Merge data
Mlt::Profile *profile = m_controller->profile();
Mlt::Geometry geometry(current.toUtf8().data(), duration().frames(profile->fps()), profile->width(), profile->height());
Mlt::Geometry newGeometry(data.toUtf8().data(), duration().frames(profile->fps()), profile->width(), profile->height());
Mlt::GeometryItem item;
int pos = 0;
while (!newGeometry.next_key(&item, pos)) {
pos = item.frame();
item.frame(pos + offset);
pos++;
geometry.insert(item);
}
return QStringList() << QString("kdenlive:clipanalysis." + name) << geometry.serialise();
//m_controller->setProperty("kdenlive:clipanalysis." + name, geometry.serialise());
}
else {
// Add data with another name
int i = 1;
QString data = m_controller->property("kdenlive:clipanalysis." + name + ' ' + QString::number(i));
while (!data.isEmpty()) {
++i;
data = m_controller->property("kdenlive:clipanalysis." + name + ' ' + QString::number(i));
}
return QStringList() << QString("kdenlive:clipanalysis." + name + ' ' + QString::number(i)) << geometryWithOffset(data, offset);
//m_controller->setProperty("kdenlive:clipanalysis." + name + ' ' + QString::number(i), geometryWithOffset(data, offset));
}
}
else {
return QStringList() << QString("kdenlive:clipanalysis." + name) << geometryWithOffset(data, offset);
//m_controller->setProperty("kdenlive:clipanalysis." + name, geometryWithOffset(data, offset));
}
}
}
QMap <QString, QString> ProjectClip::analysisData(bool withPrefix)
{
return m_controller->getPropertiesFromPrefix("kdenlive:clipanalysis.", withPrefix);
}
const QString ProjectClip::geometryWithOffset(const QString &data, int offset)
{
if (offset == 0) return data;
Mlt::Profile *profile = m_controller->profile();
Mlt::Geometry geometry(data.toUtf8().data(), duration().frames(profile->fps()), profile->width(), profile->height());
Mlt::Geometry newgeometry(NULL, duration().frames(profile->fps()), profile->width(), profile->height());
Mlt::GeometryItem item;
int pos = 0;
while (!geometry.next_key(&item, pos)) {
pos = item.frame();
item.frame(pos + offset);
pos++;
newgeometry.insert(item);
}
return newgeometry.serialise();
}
......@@ -199,6 +199,9 @@ public:
void abortAudioThumbs();
/** @brief Returns the number of audio channels. */
int audioChannels() const;
/** @brief get data analysis value. */
QStringList updatedAnalysisData(const QString &name, const QString &data, int offset);
QMap <QString, QString> analysisData(bool withPrefix = false);
public slots:
void updateAudioThumbnail(QVariantList* audioLevels);
......@@ -225,10 +228,12 @@ private:
/** @brief Indicates whether audio thumbnail creation is running. */
QFuture<void> m_audioThumbsThread;
ClipType m_type;
const QString geometryWithOffset(const QString &data, int offset);
signals:
void gotAudioData();
void refreshPropertiesPanel();
void refreshAnalysisPanel();
void refreshClipDisplay();
void thumbReady(int, QImage);
};
......
......@@ -28,6 +28,7 @@
#include "mainwindow.h"
#include "project/clipmanager.h"
#include "project/projectcommands.h"
#include "bin/bincommands.h"
#include "project/projectlist.h"
#include "effectslist/initeffects.h"
#include "dialogs/profilesdialog.h"
......@@ -1555,7 +1556,7 @@ void KdenliveDoc::slotProxyCurrentItem(bool doProxy)
//TODO: how to handle clip properties
//oldProps = clip->currentProperties(newProps);
if (doProxy) oldProps.insert("kdenlive:proxy", "-");
new EditClipCommand(this, item->clipId(), oldProps, newProps, true, command);
new EditClipCommand(pCore->bin(), item->clipId(), oldProps, newProps, true, command);
}
}
if (command->childCount() > 0) {
......@@ -1564,13 +1565,6 @@ void KdenliveDoc::slotProxyCurrentItem(bool doProxy)
else delete command;
}
void KdenliveDoc::slotUpdateClipProperties(const QString &id, QMap <QString, QString> properties, bool refreshPropertiesPanel)
{
ProjectClip *item = pCore->bin()->getBinClip(id);
if (item) {
item->setProperties(properties, refreshPropertiesPanel);
}
}
//TODO put all file watching stuff in own class
......
......@@ -149,7 +149,6 @@ public:
const QMap <QString, QString> metadata() const;
/** @brief Set the document metadata (author, copyright, ...) */
void setMetadata(const QMap <QString, QString>& meta);
void slotUpdateClipProperties(const QString &id, QMap <QString, QString> properties, bool refreshPropertiesPanel);
/** @brief Get frame size of the renderer (profile)*/
const QSize getRenderSize() const;
/** @brief Add url to the file watcher so that we monitor changes */
......
......@@ -83,7 +83,7 @@ signals:
void effectStateChanged(bool enabled);
/** @brief Start an MLT filter job on this clip. */
void startFilterJob(QMap<QString,QString>&, QMap<QString,QString>&,QMap <QString, QString>&);
void importClipKeyframes(GraphicsRectItem = AVWidget);
void importClipKeyframes(GraphicsRectItem = AVWidget, QMap<QString,QString> data = QMap<QString,QString>());
};
#endif
......@@ -242,7 +242,7 @@ signals:
void showComments(bool show);
void startFilterJob(const ItemInfo &info, const QString &clipId, QMap<QString, QString>&, QMap<QString, QString>&, QMap<QString, QString>&);
void addEffect(ClipItem*,const QDomElement &);
void importClipKeyframes(GraphicsRectItem = AVWidget);
void importClipKeyframes(GraphicsRectItem = AVWidget, QMap<QString,QString> data = QMap<QString,QString>());
};
#endif
......@@ -1546,13 +1546,13 @@ void MainWindow::connectDocument()
connect(m_effectStack, SIGNAL(refreshEffectStack(ClipItem*)), trackView->projectView(), SLOT(slotRefreshEffects(ClipItem*)));
connect(m_effectStack, SIGNAL(seekTimeline(int)), trackView->projectView(), SLOT(seekCursorPos(int)));
connect(m_effectStack, SIGNAL(importClipKeyframes(GraphicsRectItem)), trackView->projectView(), SLOT(slotImportClipKeyframes(GraphicsRectItem)));
connect(m_effectStack, SIGNAL(importClipKeyframes(GraphicsRectItem, QMap<QString,QString>)), trackView->projectView(), SLOT(slotImportClipKeyframes(GraphicsRectItem, QMap<QString,QString>)));
connect(m_effectStack, SIGNAL(reloadEffects()), this, SLOT(slotReloadEffects()));
connect(m_effectStack, SIGNAL(displayMessage(QString,int)), this, SLOT(slotGotProgressInfo(QString,int)));
// Transition config signals
connect(m_transitionConfig, SIGNAL(transitionUpdated(Transition*,QDomElement)), trackView->projectView() , SLOT(slotTransitionUpdated(Transition*,QDomElement)));
connect(m_transitionConfig, SIGNAL(importClipKeyframes(GraphicsRectItem)), trackView->projectView() , SLOT(slotImportClipKeyframes(GraphicsRectItem)));
connect(m_transitionConfig, SIGNAL(importClipKeyframes(GraphicsRectItem, QMap<QString,QString>)), trackView->projectView() , SLOT(slotImportClipKeyframes(GraphicsRectItem, QMap<QString,QString>)));
connect(m_transitionConfig, SIGNAL(seekTimeline(int)), trackView->projectView() , SLOT(seekCursorPos(int)));
connect(trackView->projectView(), SIGNAL(activateDocumentMonitor()), m_projectMonitor, SLOT(slotActivateMonitor()));
......
......@@ -192,17 +192,18 @@ const char *ClipController::getPassPropertiesList() const
return "kdenlive:proxy,kdenlive:originalurl,force_aspect_ratio,force_aspect_num,force_aspect_den,force_aspect_ratio,force_fps,force_progressive,force_tff,threads,force_colorspace,set.force_full_luma,templatetext,file_hash";
}
QMap <QString, QString> ClipController::getSubClips()
QMap <QString, QString> ClipController::getPropertiesFromPrefix(const QString &prefix, bool withPrefix)
{
Mlt::Properties subProperties;
subProperties.pass_values(*m_properties, "kdenlive:clipzone.");
subProperties.pass_values(*m_properties, prefix.toUtf8().constData());
QMap <QString,QString> subclipsData;
for (int i = 0; i < subProperties.count(); i++) {
subclipsData.insert(subProperties.get_name(i), subProperties.get(i));
subclipsData.insert(withPrefix ? QString(prefix + subProperties.get_name(i)) : subProperties.get_name(i), subProperties.get(i));
}
return subclipsData;
}
void ClipController::updateProducer(const QString &id, Mlt::Producer* producer)
{
//TODO replace all track producers
......@@ -356,7 +357,10 @@ void ClipController::setProperty(const QString& name, double value)
void ClipController::setProperty(const QString& name, const QString& value)
{
//TODO: also set property on all track producers
m_masterProducer->parent().set(name.toUtf8().constData(), value.toUtf8().constData());
if (value.isEmpty()) {
m_masterProducer->parent().set(name.toUtf8().constData(), (char *)NULL);
}
else m_masterProducer->parent().set(name.toUtf8().constData(), value.toUtf8().constData());
}
void ClipController::resetProperty(const QString& name)
......@@ -583,6 +587,11 @@ Mlt::Properties &ClipController::properties()
return *m_properties;
}
Mlt::Profile *ClipController::profile()
{
return m_binController->profile();
}
void ClipController::addEffect(const ProfileInfo pInfo, QDomElement &effect)
{
QDomDocument doc = effect.ownerDocument();
......
......@@ -103,10 +103,10 @@ public:
void resetProperty(const QString& name);
/**
* @brief Returns the list of SubClips defined for this clip. the list is of this type:
* @brief Returns the list of all properties starting with prefix. For subclips, the list is of this type:
* { subclip name , subclip in/out } where the subclip in/ou value is a semi-colon separated in/out value, like "25;220"
*/
QMap <QString, QString> getSubClips();
QMap <QString, QString> getPropertiesFromPrefix(const QString &prefix, bool withPrefix = false);
/**
* @brief Returns the value of a property.
......@@ -178,6 +178,7 @@ public:
AudioStreamInfo *audioInfo() const;
/** @brief Returns true if audio thumbnails for this clip are cached */
bool audioThumbCreated;
Mlt::Profile *profile();
private:
Mlt::Producer *m_masterProducer;
......
......@@ -42,6 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QCheckBox>
#include <QFontDatabase>
#include <QToolBar>
#include <QFileDialog>
ClipPropertiesController::ClipPropertiesController(Timecode tc, ClipController *controller, QWidget *parent) : QTabWidget(parent)
, m_controller(controller)
......@@ -55,6 +56,7 @@ ClipPropertiesController::ClipPropertiesController(Timecode tc, ClipController *
m_propertiesPage = new QWidget(this);
m_markersPage = new QWidget(this);
m_metaPage = new QWidget(this);
m_analysisPage = new QWidget(this);
// Clip properties
QVBoxLayout *propsBox = new QVBoxLayout;
......@@ -82,12 +84,7 @@ ClipPropertiesController::ClipPropertiesController(Timecode tc, ClipController *
bar->addAction(QIcon::fromTheme("document-save-as"), i18n("Export markers"), this, SLOT(slotSaveMarkers()));
bar->addAction(QIcon::fromTheme("document-open"), i18n("Import markers"), this, SLOT(slotLoadMarkers()));
mBox->addWidget(bar);
/*m_view.analysis_delete->setToolTip(i18n("Delete analysis data"));
m_view.analysis_load->setIcon(QIcon::fromTheme("document-open"));
m_view.analysis_load->setToolTip(i18n("Load analysis data"));
m_view.analysis_save->setIcon(QIcon::fromTheme("document-save-as"));
m_view.analysis_save->setToolTip(i18n("Save analysis data"));*/
slotFillMarkers();
m_markersPage->setLayout(mBox);
connect(m_markerTree, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotSeekToMarker()));
......@@ -101,7 +98,26 @@ ClipPropertiesController::ClipPropertiesController(Timecode tc, ClipController *
metaTree->setHeaderHidden(true);
m2Box->addWidget(metaTree);
slotFillMeta(metaTree);
m_metaPage->setLayout(m2Box);
m_metaPage->setLayout(m2Box );
// Clip analysis
QVBoxLayout *aBox = new QVBoxLayout;
m_analysisTree = new QTreeWidget;
m_analysisTree->setRootIsDecorated(false);
m_analysisTree->setColumnCount(2);
m_analysisTree->setAlternatingRowColors(true);
m_analysisTree->setHeaderHidden(true);
m_analysisTree->setDragEnabled(true);
aBox ->addWidget(new QLabel(i18n("Analysis data")));
aBox ->addWidget(m_analysisTree);
QToolBar *bar2 = new QToolBar;
bar2->addAction(QIcon::fromTheme("trash-empty"), i18n("Delete analysis"), this, SLOT(slotDeleteAnalysis()));
bar2->addAction(QIcon::fromTheme("document-save-as"), i18n("Export analysis"), this, SLOT(slotSaveAnalysis()));
bar2->addAction(QIcon::fromTheme("document-open"), i18n("Import analysis"), this, SLOT(slotLoadAnalysis()));
aBox->addWidget(bar2);
slotFillAnalysisData();
m_analysisPage->setLayout(aBox );
// Force properties
QVBoxLayout *vbox = new QVBoxLayout;
......@@ -152,6 +168,8 @@ ClipPropertiesController::ClipPropertiesController(Timecode tc, ClipController *
}
if (m_type == AV || m_type == Video) {
QLocale locale;
// Fps
QString force_fps = m_properties.get("force_fps");
m_originalProperties.insert("force_fps", force_fps.isEmpty() ? "-" : force_fps);
QHBoxLayout *hlay = new QHBoxLayout;
......@@ -174,6 +192,56 @@ ClipPropertiesController::ClipPropertiesController(Timecode tc, ClipController *
hlay->addWidget(spin);
vbox->addLayout(hlay);
// Aspect ratio
int force_ar_num = m_properties.get_int("force_aspect_num");
int force_ar_den = m_properties.get_int("force_aspect_den");
m_originalProperties.insert("force_aspect_den", (force_ar_den == 0) ? "-" : QString::number(force_ar_den));
m_originalProperties.insert("force_aspect_num", (force_ar_num == 0) ? "-" : QString::number(force_ar_num));
hlay = new QHBoxLayout;
box = new QCheckBox(i18n("Aspect Ratio"), this);
box->setObjectName("force_ar");
vbox->addWidget(box);
connect(box, SIGNAL(stateChanged(int)), this, SLOT(slotEnableRatio(int)));
QSpinBox *spin1 = new QSpinBox(this);
connect(spin1, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int)));
spin1->setObjectName("force_aspect_num_value");
hlay->addWidget(spin1);
hlay->addWidget(new QLabel(":"));
QSpinBox *spin2 = new QSpinBox(this);
connect(spin2, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int)));
spin2->setObjectName("force_aspect_den_value");
hlay->addWidget(spin2);
if (force_ar_num == 0) {
// calculate current ratio
box->setChecked(false);
spin1->setEnabled(false);
spin2->setEnabled(false);
int width = m_controller->int_property("meta.media.width");
int height = m_controller->int_property("meta.media.height");
if (width > 0 && height > 0) {
if ((width / height * 100) == 133) {
width = 4;
height = 3;
}
else if (int(width / height * 100) == 177) {
width = 16;
height = 9;
}
spin1->setValue(width);
spin2->setValue(height);
}
}
else {
box->setChecked(true);
spin1->setEnabled(true);
spin2->setEnabled(true);
spin1->setValue(force_ar_num);
spin2->setValue(force_ar_den);
}
connect(box, SIGNAL(toggled(bool)), spin1, SLOT(setEnabled(bool)));
connect(box, SIGNAL(toggled(bool)), spin2, SLOT(setEnabled(bool)));
vbox->addLayout(hlay);
hlay = new QHBoxLayout;
box = new QCheckBox(i18n("Colorspace"), this);
box->setObjectName("force_colorspace");
......@@ -207,6 +275,7 @@ ClipPropertiesController::ClipPropertiesController(Timecode tc, ClipController *
addTab(m_markersPage, QString());
addTab(m_forcePage, QString());
addTab(m_metaPage, QString());
addTab(m_analysisPage, QString());
setTabIcon(0, QIcon::fromTheme("edit-find"));
setTabToolTip(0, i18n("Properties"));
setTabIcon(1, QIcon::fromTheme("bookmark-toolbar"));
......@@ -215,6 +284,8 @@ ClipPropertiesController::ClipPropertiesController(Timecode tc, ClipController *
setTabToolTip(2, i18n("Force properties"));
setTabIcon(3, QIcon::fromTheme("view-list-details"));
setTabToolTip(3, i18n("Metadata"));
setTabIcon(4, QIcon::fromTheme("archive-extract"));
setTabToolTip(4, i18n("Analysis"));
setCurrentIndex(KdenliveSettings::properties_panel_page());
if (m_type == Color) setTabEnabled(0, false);
}
......@@ -603,3 +674,49 @@ void ClipPropertiesController::slotFillMeta(QTreeWidget *tree)