]> git.sesse.net Git - kdenlive/blob - src/effectstack/effectstackview2.cpp
Effects can now be dragged from the effect stack to another clip
[kdenlive] / src / effectstack / effectstackview2.cpp
1 /***************************************************************************
2                           effecstackview.cpp2  -  description
3                              -------------------
4     begin                : Feb 15 2008
5     copyright            : (C) 2008 by Marco Gittler
6     email                : g.marco@freenet.de
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17
18
19 #include "effectstackview2.h"
20 #include "effectslist.h"
21 #include "clipitem.h"
22 #include "mainwindow.h"
23 #include "docclipbase.h"
24 #include "projectlist.h"
25 #include "kthumb.h"
26 #include "monitoreditwidget.h"
27 #include "monitorscene.h"
28 #include "kdenlivesettings.h"
29
30 #include <KDebug>
31 #include <KLocale>
32 #include <KMessageBox>
33 #include <KStandardDirs>
34 #include <KFileDialog>
35 #include <KColorScheme>
36
37 #include <QMenu>
38 #include <QTextStream>
39 #include <QFile>
40 #include <QInputDialog>
41
42
43 EffectStackView2::EffectStackView2(Monitor *monitor, QWidget *parent) :
44         QWidget(parent),
45         m_clipref(NULL),
46         m_trackindex(-1),
47         m_draggedEffect(NULL)
48 {
49     m_effectMetaInfo.trackMode = false;
50     m_effectMetaInfo.monitor = monitor;
51
52     m_ui.setupUi(this);
53     setFont(KGlobalSettings::smallestReadableFont());
54     m_ui.checkAll->setToolTip(i18n("Enable/Disable all effects"));
55     m_ui.buttonShowComments->setIcon(KIcon("help-about"));
56     m_ui.buttonShowComments->setToolTip(i18n("Show additional information for the parameters"));
57
58
59     setEnabled(false);
60
61     QPalette p = palette();
62     KColorScheme scheme(p.currentColorGroup(), KColorScheme::View, KSharedConfig::openConfig(KdenliveSettings::colortheme()));
63     QColor dark_bg = scheme.shade(KColorScheme::DarkShade);
64     QColor selected_bg = scheme.decoration(KColorScheme::FocusColor).color();
65     QColor hover_bg = scheme.decoration(KColorScheme::HoverColor).color();
66     QColor light_bg = scheme.shade(KColorScheme::LightShade);
67     
68     QString stylesheet(QString("QProgressBar:horizontal {border: 1px solid %1;border-radius:0px;border-top-left-radius: 4px;border-bottom-left-radius: 4px;border-right: 0px;background:%4;padding: 0px;text-align:left center}\
69                                 QProgressBar:horizontal#dragOnly {background: %1} QProgressBar:horizontal:hover#dragOnly {background: %3} QProgressBar:horizontal:hover {border: 1px solid %3;border-right: 0px;}\
70                                 QProgressBar::chunk:horizontal {background: %1;} QProgressBar::chunk:horizontal:hover {background: %3;}\
71                                 QProgressBar:horizontal[inTimeline=\"true\"] { border: 1px solid %2;border-right: 0px;background: %4;padding: 0px;text-align:left center } QProgressBar::chunk:horizontal[inTimeline=\"true\"] {background: %2;}\
72                                 QAbstractSpinBox#dragBox {border: 1px solid %1;border-top-right-radius: 4px;border-bottom-right-radius: 4px;padding-right:0px;} QAbstractSpinBox::down-button#dragBox {width:0px;padding:0px;}\
73                                 QAbstractSpinBox::up-button#dragBox {width:0px;padding:0px;} QAbstractSpinBox[inTimeline=\"true\"]#dragBox { border: 1px solid %2;} QAbstractSpinBox:hover#dragBox {border: 1px solid %3;} ")
74                                 .arg(dark_bg.name()).arg(selected_bg.name()).arg(hover_bg.name()).arg(light_bg.name()));
75     setStyleSheet(stylesheet);
76 }
77
78 EffectStackView2::~EffectStackView2()
79 {
80 }
81
82
83 void EffectStackView2::slotRenderPos(int pos)
84 {
85     if (m_effects.isEmpty()) return;
86     if (!m_effectMetaInfo.trackMode && m_clipref) pos = pos - m_clipref->startPos().frames(KdenliveSettings::project_fps());
87
88     for (int i = 0; i< m_effects.count(); i++)
89         m_effects.at(i)->slotSyncEffectsPos(pos);
90 }
91
92 void EffectStackView2::setMenu(QMenu *menu)
93 {
94     //m_ui.buttonNew->setMenu(menu);
95 }
96
97 void EffectStackView2::slotClipItemSelected(ClipItem* c, int ix)
98 {
99     if (c && !c->isEnabled()) return;
100     if (c && c == m_clipref) {
101         
102     } else {
103         m_clipref = c;
104         if (c) {
105             QString cname = m_clipref->clipName();
106             if (cname.length() > 30) {
107                 m_ui.checkAll->setToolTip(i18n("Effects for %1").arg(cname));
108                 cname.truncate(27);
109                 m_ui.checkAll->setText(i18n("Effects for %1").arg(cname) + "...");
110             } else {
111                 m_ui.checkAll->setToolTip(QString());
112                 m_ui.checkAll->setText(i18n("Effects for %1").arg(cname));
113             }
114             ix = c->selectedEffectIndex();
115             QString size = c->baseClip()->getProperty("frame_size");
116             double factor = c->baseClip()->getProperty("aspect_ratio").toDouble();
117             m_effectMetaInfo.frameSize = QPoint((int)(size.section('x', 0, 0).toInt() * factor + 0.5), size.section('x', 1, 1).toInt());
118         } else {
119             ix = 0;
120         }
121     }
122     if (m_clipref == NULL) {
123         //TODO: clear list, reset paramdesc and info
124         //ItemInfo info;
125         //m_effectedit->transferParamDesc(QDomElement(), info);
126         m_effects.clear();
127         QWidget *view = m_ui.container->takeWidget();
128         if (view) {
129             delete view;
130         }
131         m_ui.checkAll->setToolTip(QString());
132         m_ui.checkAll->setText(QString());
133         setEnabled(false);
134         return;
135     }
136     setEnabled(true);
137     m_effectMetaInfo.trackMode = false;
138     m_currentEffectList = m_clipref->effectList();
139     setupListView(ix);
140 }
141
142 void EffectStackView2::slotTrackItemSelected(int ix, const TrackInfo info)
143 {
144     m_clipref = NULL;
145     m_effectMetaInfo.trackMode = true;
146     m_currentEffectList = info.effectsList;
147     m_trackInfo = info;
148     setEnabled(true);
149     m_ui.checkAll->setToolTip(QString());
150     m_ui.checkAll->setText(i18n("Effects for track %1").arg(info.trackName.isEmpty() ? QString::number(ix) : info.trackName));
151     m_trackindex = ix;
152     setupListView(0);
153 }
154
155
156 void EffectStackView2::setupListView(int ix)
157 {
158     //TODO: clear list
159
160     blockSignals(true);
161     m_draggedEffect = NULL;
162     disconnect(m_effectMetaInfo.monitor, SIGNAL(renderPosition(int)), this, SLOT(slotRenderPos(int)));
163     m_effects.clear();
164     QWidget *view = m_ui.container->takeWidget();
165     if (view) {
166         delete view;
167         //view->deleteLater();
168     }
169     blockSignals(false);
170     view = new QWidget(m_ui.container);
171     m_ui.container->setWidget(view);
172
173     QVBoxLayout *vbox1 = new QVBoxLayout(view);
174     vbox1->setContentsMargins(0, 0, 0, 0);
175     vbox1->setSpacing(0);
176
177     for (int i = 0; i < m_currentEffectList.count(); i++) {
178         QDomElement d = m_currentEffectList.at(i).cloneNode().toElement();
179         if (d.isNull()) {
180             kDebug() << " . . . . WARNING, NULL EFFECT IN STACK!!!!!!!!!";
181             continue;
182         }
183
184         /*QDomDocument doc;
185         doc.appendChild(doc.importNode(d, true));
186         kDebug() << "IMPORTED STK: " << doc.toString();*/
187         
188         ItemInfo info;
189         if (m_effectMetaInfo.trackMode) { 
190             info.track = m_trackInfo.type;
191             info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
192             info.cropStart = GenTime(0);
193             info.startPos = GenTime(-1);
194             info.track = 0;
195         }
196         else info = m_clipref->info();
197
198         CollapsibleEffect *currentEffect = new CollapsibleEffect(d, m_currentEffectList.at(i), info, i, &m_effectMetaInfo, i == m_currentEffectList.count() - 1, view);
199         m_effects.append(currentEffect);
200         vbox1->addWidget(currentEffect);
201
202         // Check drag & drop
203         currentEffect->installEventFilter( this );
204
205         connect(currentEffect, SIGNAL(parameterChanged(const QDomElement, const QDomElement, int)), this , SLOT(slotUpdateEffectParams(const QDomElement, const QDomElement, int)));
206         connect(currentEffect, SIGNAL(startFilterJob(QString,QString,QString,QString,QString,QString)), this , SLOT(slotStartFilterJob(QString,QString,QString,QString,QString,QString)));
207         connect(currentEffect, SIGNAL(deleteEffect(const QDomElement, int)), this , SLOT(slotDeleteEffect(const QDomElement, int)));
208         connect(currentEffect, SIGNAL(reloadEffects()), this , SIGNAL(reloadEffects()));
209         connect(currentEffect, SIGNAL(resetEffect(int)), this , SLOT(slotResetEffect(int)));
210         connect(currentEffect, SIGNAL(changeEffectPosition(int,bool)), this , SLOT(slotMoveEffect(int , bool)));
211         connect(currentEffect, SIGNAL(effectStateChanged(bool, int)), this, SLOT(slotUpdateEffectState(bool, int)));
212         connect(currentEffect, SIGNAL(activateEffect(int)), this, SLOT(slotSetCurrentEffect(int)));
213         connect(currentEffect, SIGNAL(checkMonitorPosition(int)), this, SLOT(slotCheckMonitorPosition(int)));
214         connect(currentEffect, SIGNAL(seekTimeline(int)), this , SLOT(slotSeekTimeline(int)));
215             //ui.title->setPixmap(icon.pixmap(QSize(12, 12)));
216     }
217     vbox1->addStretch(10);
218     connect(m_effectMetaInfo.monitor, SIGNAL(renderPosition(int)), this, SLOT(slotRenderPos(int)));
219 }
220
221 bool EffectStackView2::eventFilter( QObject * o, QEvent * e ) 
222 {
223     // Check if user clicked in an effect's top bar to start dragging it
224     if (e->type() == QEvent::MouseButtonPress)  {
225         m_draggedEffect = qobject_cast<CollapsibleEffect*>(o);
226         if (m_draggedEffect) {
227             QMouseEvent *me = static_cast<QMouseEvent *>(e);
228             if (me->button() == Qt::LeftButton && (m_draggedEffect->frame->underMouse() || m_draggedEffect->title->underMouse()))
229                 m_clickPoint = me->pos();
230             else {
231                 m_clickPoint = QPoint();
232                 m_draggedEffect = NULL;
233             }
234             e->accept();
235             return false;
236         }
237     }  
238     if (e->type() == QEvent::MouseMove)  {
239         if (qobject_cast<CollapsibleEffect*>(o)) {
240             CollapsibleEffect *effect = qobject_cast<CollapsibleEffect*>(o);
241             QMouseEvent *me = static_cast<QMouseEvent *>(e);
242             if (me->buttons() != Qt::LeftButton) {
243                 e->accept();
244                 return false;
245             }
246             else {
247                 e->ignore();
248                 return true;
249             }
250         }
251     }
252     return QWidget::eventFilter(o, e);
253 }
254
255 void EffectStackView2::mouseMoveEvent(QMouseEvent * event)
256 {
257     if (m_draggedEffect && (event->buttons() & Qt::LeftButton) && (m_clickPoint != QPoint()) && ((event->pos() - m_clickPoint).manhattanLength() >= QApplication::startDragDistance()))
258         startDrag();
259 }
260
261 void EffectStackView2::mouseReleaseEvent(QMouseEvent * event)
262 {
263     m_draggedEffect = NULL;
264     QWidget::mouseReleaseEvent(event);
265 }
266
267 void EffectStackView2::startDrag()
268 {
269     QDrag *drag = new QDrag(this);
270     // The data to be transferred by the drag and drop operation is contained in a QMimeData object
271     QDomElement effect = m_draggedEffect->effect().cloneNode().toElement();
272     QPixmap pixmap = QPixmap::grabWidget(m_draggedEffect->title);
273     drag->setPixmap(pixmap);
274     effect.setAttribute("kdenlive_ix", 0);
275     QDomDocument doc;
276     doc.appendChild(doc.importNode(effect, true));
277     QMimeData *mime = new QMimeData;
278     QByteArray data;
279     data.append(doc.toString().toUtf8());
280     mime->setData("kdenlive/effectslist", data);
281
282     // Assign ownership of the QMimeData object to the QDrag object.
283     drag->setMimeData(mime);
284     // Start the drag and drop operation
285     drag->exec(Qt::CopyAction);// | Qt::MoveAction);
286 }
287
288
289 void EffectStackView2::slotUpdateEffectState(bool disable, int index)
290 {
291     if (m_effectMetaInfo.trackMode)
292         emit changeEffectState(NULL, m_trackindex, index, disable);
293     else
294         emit changeEffectState(m_clipref, -1, index, disable);
295 }
296
297
298 void EffectStackView2::raiseWindow(QWidget* dock)
299 {
300     if ((m_clipref || m_effectMetaInfo.trackMode) && dock)
301         dock->raise();
302 }
303
304
305 void EffectStackView2::slotSeekTimeline(int pos)
306 {
307     if (m_effectMetaInfo.trackMode) {
308         emit seekTimeline(pos);
309     } else if (m_clipref) {
310         emit seekTimeline(m_clipref->startPos().frames(KdenliveSettings::project_fps()) + pos);
311     }
312 }
313
314
315 /*void EffectStackView2::slotRegionChanged()
316 {
317     if (!m_trackMode) emit updateClipRegion(m_clipref, m_ui.effectlist->currentRow(), m_ui.region_url->text());
318 }*/
319
320 void EffectStackView2::slotCheckMonitorPosition(int renderPos)
321 {
322     if (m_effectMetaInfo.trackMode || (m_clipref && renderPos >= m_clipref->startPos().frames(KdenliveSettings::project_fps()) && renderPos <= m_clipref->endPos().frames(KdenliveSettings::project_fps()))) {
323         if (!m_effectMetaInfo.monitor->getEffectEdit()->getScene()->views().at(0)->isVisible())
324             m_effectMetaInfo.monitor->slotEffectScene(true);
325     } else {
326         m_effectMetaInfo.monitor->slotEffectScene(false);
327     }
328 }
329
330 int EffectStackView2::isTrackMode(bool *ok) const
331 {
332     *ok = m_effectMetaInfo.trackMode;
333     return m_trackindex;
334 }
335
336 void EffectStackView2::clear()
337 {
338 }
339
340 void EffectStackView2::updateProjectFormat(MltVideoProfile profile, Timecode t)
341 {
342     m_effectMetaInfo.profile = profile;
343     m_effectMetaInfo.timecode = t;
344 }
345
346 void EffectStackView2::slotItemDel()
347 {
348
349 }
350
351 void EffectStackView2::updateTimecodeFormat()
352 {
353     for (int i = 0; i< m_effects.count(); i++)
354         m_effects.at(i)->updateTimecodeFormat();
355 }
356
357 void EffectStackView2::slotUpdateEffectParams(const QDomElement old, const QDomElement e, int ix)
358 {
359     if (m_effectMetaInfo.trackMode)
360         emit updateEffect(NULL, m_trackindex, old, e, ix);
361     else if (m_clipref) {
362         emit updateEffect(m_clipref, -1, old, e, ix);
363         // Make sure the changed effect is currently displayed
364         slotSetCurrentEffect(ix);
365     }
366 }
367
368 void EffectStackView2::slotSetCurrentEffect(int ix)
369 {
370     if (m_clipref && ix != m_clipref->selectedEffectIndex())
371         m_clipref->setSelectedEffect(ix);
372     for (int i = 0; i < m_effects.count(); i++) {
373         m_effects.at(i)->setActive(i == ix);
374     }
375 }
376
377 void EffectStackView2::slotDeleteEffect(const QDomElement effect, int index)
378 {
379     
380     if (m_effectMetaInfo.trackMode)
381         emit removeEffect(NULL, m_trackindex, effect);
382     else
383         emit removeEffect(m_clipref, -1, effect);
384 }
385
386 void EffectStackView2::slotMoveEffect(int index, bool up)
387 {
388     if (up && index <= 0) return;
389     if (!up && index >= m_currentEffectList.count() - 1) return;
390     int startPos;
391     int endPos;
392     if (up) {
393         startPos =  index + 1;
394         endPos = index;
395     }
396     else {
397         startPos =  index + 1;
398         endPos =  index + 2;
399     }
400     if (m_effectMetaInfo.trackMode) emit changeEffectPosition(NULL, m_trackindex, startPos, endPos);
401     else emit changeEffectPosition(m_clipref, -1, startPos, endPos);
402 }
403
404 void EffectStackView2::slotStartFilterJob(const QString&filterName, const QString&filterParams, const QString&finalFilterName, const QString&consumer, const QString&consumerParams, const QString&properties)
405 {
406     if (!m_clipref) return;
407     emit startFilterJob(m_clipref->info(), m_clipref->clipProducer(), filterName, filterParams, finalFilterName, consumer, consumerParams, properties);
408 }
409
410 void EffectStackView2::slotResetEffect(int ix)
411 {
412     QDomElement old = m_currentEffectList.at(ix);
413     QDomElement dom;
414     QString effectId = old.attribute("id");
415     QMap<QString, EffectsList*> effectLists;
416     effectLists["audio"] = &MainWindow::audioEffects;
417     effectLists["video"] = &MainWindow::videoEffects;
418     effectLists["custom"] = &MainWindow::customEffects;
419     foreach(const QString &type, effectLists.keys()) {
420         EffectsList *list = effectLists[type];
421         dom = list->getEffectByTag(QString(), effectId).cloneNode().toElement();
422         if (!dom.isNull()) break;
423     }
424     if (!dom.isNull()) {
425         dom.setAttribute("kdenlive_ix", old.attribute("kdenlive_ix"));
426         //TODO: Track mode
427         if (m_effectMetaInfo.trackMode) {
428             EffectsList::setParameter(dom, "in", QString::number(0));
429             EffectsList::setParameter(dom, "out", QString::number(m_trackInfo.duration));
430             ItemInfo info;
431             info.track = m_trackInfo.type;
432             info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
433             info.cropStart = GenTime(0);
434             info.startPos = GenTime(-1);
435             info.track = 0;
436             m_effects.at(ix)->updateWidget(info, ix, dom, &m_effectMetaInfo);
437             emit updateEffect(NULL, m_trackindex, old, dom, ix);
438         } else {
439             m_clipref->initEffect(dom);
440             m_effects.at(ix)->updateWidget(m_clipref->info(), ix, dom, &m_effectMetaInfo);
441             //m_effectedit->transferParamDesc(dom, m_clipref->info());
442             //m_ui.region_url->setUrl(KUrl(dom.attribute("region")));
443             emit updateEffect(m_clipref, -1, old, dom, ix);
444         }
445     }
446
447     /*emit showComments(m_ui.buttonShowComments->isChecked());
448     m_ui.labelComment->setHidden(!m_ui.buttonShowComments->isChecked() || !m_ui.labelComment->text().count());*/
449 }
450
451 #include "effectstackview2.moc"