]> git.sesse.net Git - kdenlive/blob - src/effectstack/effectstackview2.cpp
dc09c99ebb5ecea1bbbd320f14b382570fed742c
[kdenlive] / src / effectstack / effectstackview2.cpp
1 /***************************************************************************
2                           effecstackview.cpp2  -  description
3                              -------------------
4     begin                : Feb 15 2008
5     copyright            : (C) 2008 by Marco Gittler (g.marco@freenet.de)
6     copyright            : (C) 2012 by Jean-Baptiste Mardelle (jb@kdenlive.org)
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 #include "collapsibleeffect.h"
30 #include "collapsiblegroup.h"
31
32 #include <KDebug>
33 #include <KLocale>
34 #include <KMessageBox>
35 #include <KStandardDirs>
36 #include <KFileDialog>
37 #include <KColorScheme>
38
39 #include <QMenu>
40 #include <QTextStream>
41 #include <QFile>
42 #include <QInputDialog>
43 #include <QScrollBar>
44
45
46 EffectStackView2::EffectStackView2(Monitor *monitor, QWidget *parent) :
47         QWidget(parent),
48         m_clipref(NULL),
49         m_trackindex(-1),
50         m_draggedEffect(NULL),
51         m_draggedGroup(NULL),
52         m_groupIndex(0)
53 {
54     m_effectMetaInfo.trackMode = false;
55     m_effectMetaInfo.monitor = monitor;
56     m_effects = QList <CollapsibleEffect*>();
57
58     m_ui.setupUi(this);
59     setFont(KGlobalSettings::smallestReadableFont());
60     m_ui.checkAll->setToolTip(i18n("Enable/Disable all effects"));
61     m_ui.buttonShowComments->setIcon(KIcon("help-about"));
62     m_ui.buttonShowComments->setToolTip(i18n("Show additional information for the parameters"));
63     
64     connect(m_ui.checkAll, SIGNAL(stateChanged(int)), this, SLOT(slotCheckAll(int)));
65     connect(m_ui.buttonShowComments, SIGNAL(clicked()), this, SLOT(slotShowComments()));
66     m_ui.labelComment->setHidden(true);
67
68     setEnabled(false);
69
70     
71     setStyleSheet(CollapsibleEffect::getStyleSheet());
72 }
73
74 EffectStackView2::~EffectStackView2()
75 {
76 }
77
78 void EffectStackView2::updatePalette()
79 {
80     setStyleSheet(CollapsibleEffect::getStyleSheet());
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::slotClipItemSelected(ClipItem* c)
93 {
94     if (c && !c->isEnabled()) return;
95     if (c && c == m_clipref) {
96         
97     } else {
98         m_clipref = c;
99         if (c) {
100             QString cname = m_clipref->clipName();
101             if (cname.length() > 30) {
102                 m_ui.checkAll->setToolTip(i18n("Effects for %1").arg(cname));
103                 cname.truncate(27);
104                 m_ui.checkAll->setText(i18n("Effects for %1").arg(cname) + "...");
105             } else {
106                 m_ui.checkAll->setToolTip(QString());
107                 m_ui.checkAll->setText(i18n("Effects for %1").arg(cname));
108             }
109             m_ui.checkAll->setEnabled(true);
110             QString size = c->baseClip()->getProperty("frame_size");
111             double factor = c->baseClip()->getProperty("aspect_ratio").toDouble();
112             m_effectMetaInfo.frameSize = QPoint((int)(size.section('x', 0, 0).toInt() * factor + 0.5), size.section('x', 1, 1).toInt());
113         }
114     }
115     if (m_clipref == NULL) {
116         //TODO: clear list, reset paramdesc and info
117         //ItemInfo info;
118         //m_effectedit->transferParamDesc(QDomElement(), info);
119         clear();
120         return;
121     }
122     setEnabled(true);
123     m_effectMetaInfo.trackMode = false;
124     m_currentEffectList = m_clipref->effectList();
125     setupListView();
126 }
127
128 void EffectStackView2::slotTrackItemSelected(int ix, const TrackInfo info)
129 {
130     m_clipref = NULL;
131     m_effectMetaInfo.trackMode = true;
132     m_currentEffectList = info.effectsList;
133     m_trackInfo = info;
134     setEnabled(true);
135     m_ui.checkAll->setToolTip(QString());
136     m_ui.checkAll->setText(i18n("Effects for track %1").arg(info.trackName.isEmpty() ? QString::number(ix) : info.trackName));
137     m_trackindex = ix;
138     setupListView();
139 }
140
141
142 void EffectStackView2::setupListView()
143 {
144     blockSignals(true);
145     m_draggedEffect = NULL;
146     m_draggedGroup = NULL;
147     disconnect(m_effectMetaInfo.monitor, SIGNAL(renderPosition(int)), this, SLOT(slotRenderPos(int)));
148     m_effects.clear();
149     m_groupIndex = 0;
150     QWidget *view = m_ui.container->takeWidget();
151     if (view) {
152         delete view;
153     }
154     blockSignals(false);
155     view = new QWidget(m_ui.container);
156     m_ui.container->setWidget(view);
157
158     QVBoxLayout *vbox1 = new QVBoxLayout(view);
159     vbox1->setContentsMargins(0, 0, 0, 0);
160     vbox1->setSpacing(0);
161     
162     if (m_currentEffectList.isEmpty()) m_ui.labelComment->setHidden(true);
163
164     for (int i = 0; i < m_currentEffectList.count(); i++) {
165         QDomElement d = m_currentEffectList.at(i).cloneNode().toElement();
166         if (d.isNull()) {
167             kDebug() << " . . . . WARNING, NULL EFFECT IN STACK!!!!!!!!!";
168             continue;
169         }
170         
171         CollapsibleGroup *group = NULL;
172         EffectInfo effectInfo;
173         effectInfo.fromString(d.attribute("kdenlive_info"));
174         if (effectInfo.groupIndex >= 0) {
175             // effect is in a group    
176             for (int j = 0; j < vbox1->count(); j++) {
177                 CollapsibleGroup *eff = static_cast<CollapsibleGroup *>(vbox1->itemAt(j)->widget());
178                 if (eff->isGroup() &&  eff->groupIndex() == effectInfo.groupIndex) {
179                     group = eff;
180                     break;
181                 }
182             }
183             
184             if (group == NULL) {
185                 group = new CollapsibleGroup(effectInfo.groupIndex, i == 0, i == m_currentEffectList.count() - 1, effectInfo.groupName, m_ui.container->widget());
186                 connect(group, SIGNAL(moveEffect(int,int,int,QString)), this, SLOT(slotMoveEffect(int,int,int,QString)));
187                 connect(group, SIGNAL(unGroup(CollapsibleGroup*)), this , SLOT(slotUnGroup(CollapsibleGroup*)));
188                 connect(group, SIGNAL(groupRenamed(CollapsibleGroup *)), this, SLOT(slotRenameGroup(CollapsibleGroup*)));
189                 connect(group, SIGNAL(reloadEffects()), this , SIGNAL(reloadEffects()));
190                 connect(group, SIGNAL(deleteGroup(QDomDocument)), this , SLOT(slotDeleteGroup(QDomDocument)));
191                 vbox1->addWidget(group);
192                 group->installEventFilter( this );
193             }
194             if (effectInfo.groupIndex >= m_groupIndex) m_groupIndex = effectInfo.groupIndex + 1;
195         }
196
197         /*QDomDocument doc;
198         doc.appendChild(doc.importNode(d, true));
199         kDebug() << "IMPORTED STK: " << doc.toString();*/
200         
201         ItemInfo info;
202         bool isSelected = false;
203         if (m_effectMetaInfo.trackMode) { 
204             info.track = m_trackInfo.type;
205             info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
206             info.cropStart = GenTime(0);
207             info.startPos = GenTime(-1);
208             info.track = 0;
209         }
210         else {
211             info = m_clipref->info();
212         }
213
214         CollapsibleEffect *currentEffect = new CollapsibleEffect(d, m_currentEffectList.at(i), info, &m_effectMetaInfo, i == m_currentEffectList.count() - 1, view);
215         if (m_effectMetaInfo.trackMode) {
216             isSelected = currentEffect->effectIndex() == 1;
217         }
218         else {
219             isSelected = currentEffect->effectIndex() == m_clipref->selectedEffectIndex();
220         }
221         if (isSelected) currentEffect->setActive(true);
222         m_effects.append(currentEffect);
223         if (group) {
224             group->addGroupEffect(currentEffect);
225         } else {
226             vbox1->addWidget(currentEffect);
227         }
228
229         // Check drag & drop
230         currentEffect->installEventFilter( this );
231
232         connect(currentEffect, SIGNAL(parameterChanged(const QDomElement, const QDomElement, int)), this , SLOT(slotUpdateEffectParams(const QDomElement, const QDomElement, int)));
233         connect(currentEffect, SIGNAL(startFilterJob(QString,QString,QString,QString,QString,QString)), this , SLOT(slotStartFilterJob(QString,QString,QString,QString,QString,QString)));
234         connect(currentEffect, SIGNAL(deleteEffect(const QDomElement)), this , SLOT(slotDeleteEffect(const QDomElement)));
235         connect(currentEffect, SIGNAL(reloadEffects()), this , SIGNAL(reloadEffects()));
236         connect(currentEffect, SIGNAL(resetEffect(int)), this , SLOT(slotResetEffect(int)));
237         connect(currentEffect, SIGNAL(changeEffectPosition(int,bool)), this , SLOT(slotMoveEffectUp(int , bool)));
238         connect(currentEffect, SIGNAL(effectStateChanged(bool,int,bool)), this, SLOT(slotUpdateEffectState(bool,int,bool)));
239         connect(currentEffect, SIGNAL(activateEffect(int)), this, SLOT(slotSetCurrentEffect(int)));
240         connect(currentEffect, SIGNAL(checkMonitorPosition(int)), this, SLOT(slotCheckMonitorPosition(int)));
241         connect(currentEffect, SIGNAL(seekTimeline(int)), this , SLOT(slotSeekTimeline(int)));
242         connect(currentEffect, SIGNAL(createGroup(int)), this , SLOT(slotCreateGroup(int)));
243         connect(currentEffect, SIGNAL(moveEffect(int,int,int,QString)), this , SLOT(slotMoveEffect(int,int,int,QString)));
244         connect(currentEffect, SIGNAL(addEffect(QDomElement)), this , SLOT(slotAddEffect(QDomElement)));
245         connect(currentEffect, SIGNAL(createRegion(int,KUrl)), this, SLOT(slotCreateRegion(int,KUrl)));
246         
247         //ui.title->setPixmap(icon.pixmap(QSize(12, 12)));
248     }
249     vbox1->addStretch(10);
250     slotUpdateCheckAllButton();
251     connect(m_effectMetaInfo.monitor, SIGNAL(renderPosition(int)), this, SLOT(slotRenderPos(int)));
252     
253     // Wait a little bit for the new layout to be ready, then check if we have a scrollbar
254     QTimer::singleShot(200, this, SLOT(slotCheckWheelEventFilter()));
255 }
256
257
258 void EffectStackView2::slotCheckWheelEventFilter()
259 {
260     // If the effect stack widget has no scrollbar, we will not filter the
261     // mouse wheel events, so that user can easily adjust effect params
262     bool filterWheelEvent = false;
263     if (m_ui.container->verticalScrollBar() && m_ui.container->verticalScrollBar()->isVisible()) {
264         // widget has scroll bar, 
265         filterWheelEvent = true;
266     }
267     for (int i = 0; i < m_effects.count(); i++) {
268         m_effects.at(i)->filterWheelEvent = filterWheelEvent;
269     }    
270 }
271
272 void EffectStackView2::resizeEvent ( QResizeEvent * event )
273 {
274     slotCheckWheelEventFilter();
275     QWidget::resizeEvent(event);
276 }
277
278 bool EffectStackView2::eventFilter( QObject * o, QEvent * e ) 
279 {
280     // Check if user clicked in an effect's top bar to start dragging it
281     if (e->type() == QEvent::MouseButtonPress)  {
282         m_draggedEffect = qobject_cast<CollapsibleEffect*>(o);
283         if (m_draggedEffect) {
284             QMouseEvent *me = static_cast<QMouseEvent *>(e);
285             if (me->button() == Qt::LeftButton && (m_draggedEffect->frame->underMouse() || m_draggedEffect->title->underMouse())) {
286                 m_clickPoint = me->globalPos();
287             }
288             else {
289                 m_clickPoint = QPoint();
290                 m_draggedEffect = NULL;
291             }
292             e->accept();
293             return true;
294         }
295         m_draggedGroup = qobject_cast<CollapsibleGroup*>(o);
296         if (m_draggedGroup) {
297             QMouseEvent *me = static_cast<QMouseEvent *>(e);
298             if (me->button() == Qt::LeftButton && (m_draggedGroup->framegroup->underMouse() || m_draggedGroup->title()->underMouse()))
299                 m_clickPoint = me->globalPos();
300             else {
301                 m_clickPoint = QPoint();
302                 m_draggedGroup = NULL;
303             }
304             e->accept();
305             return true;
306         }
307     }  
308     /*if (e->type() == QEvent::MouseMove)  {
309         if (qobject_cast<CollapsibleEffect*>(o)) {
310             QMouseEvent *me = static_cast<QMouseEvent *>(e);
311             if (me->buttons() != Qt::LeftButton) {
312                 e->accept();
313                 return false;
314             }
315             else {
316                 e->ignore();
317                 return true;
318             }
319         }
320     }*/
321     return QWidget::eventFilter(o, e);
322 }
323
324 void EffectStackView2::mouseMoveEvent(QMouseEvent * event)
325 {
326     if (m_draggedEffect || m_draggedGroup) {
327         if ((event->buttons() & Qt::LeftButton) && (m_clickPoint != QPoint()) && ((event->globalPos() - m_clickPoint).manhattanLength() >= QApplication::startDragDistance())) {
328             startDrag();
329         }
330     }
331 }
332
333 void EffectStackView2::mouseReleaseEvent(QMouseEvent * event)
334 {
335     m_draggedEffect = NULL;
336     m_draggedGroup = NULL;
337     QWidget::mouseReleaseEvent(event);
338 }
339
340 void EffectStackView2::startDrag()
341 {
342     // The data to be transferred by the drag and drop operation is contained in a QMimeData object
343     QDomDocument doc;
344     QPixmap pixmap;
345     if (m_draggedEffect) {
346         QDomElement effect = m_draggedEffect->effect().cloneNode().toElement();
347         doc.appendChild(doc.importNode(effect, true));
348         pixmap = QPixmap::grabWidget(m_draggedEffect->title);
349     }
350     else if (m_draggedGroup) {
351         doc = m_draggedGroup->effectsData();
352         pixmap = QPixmap::grabWidget(m_draggedGroup->title());
353     }
354     else return;
355     QDrag *drag = new QDrag(this);
356     drag->setPixmap(pixmap);
357     QMimeData *mime = new QMimeData;
358     QByteArray data;
359     data.append(doc.toString().toUtf8());
360     mime->setData("kdenlive/effectslist", data);
361
362     // Assign ownership of the QMimeData object to the QDrag object.
363     drag->setMimeData(mime);
364     // Start the drag and drop operation
365     drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
366 }
367
368
369 void EffectStackView2::slotUpdateEffectState(bool disable, int index, bool updateMainStatus)
370 {
371     if (m_effectMetaInfo.trackMode)
372         emit changeEffectState(NULL, m_trackindex, index, disable);
373     else
374         emit changeEffectState(m_clipref, -1, index, disable);
375     if (updateMainStatus) slotUpdateCheckAllButton();
376 }
377
378
379 void EffectStackView2::raiseWindow(QWidget* dock)
380 {
381     if ((m_clipref || m_effectMetaInfo.trackMode) && dock)
382         dock->raise();
383 }
384
385
386 void EffectStackView2::slotSeekTimeline(int pos)
387 {
388     if (m_effectMetaInfo.trackMode) {
389         emit seekTimeline(pos);
390     } else if (m_clipref) {
391         emit seekTimeline(m_clipref->startPos().frames(KdenliveSettings::project_fps()) + pos);
392     }
393 }
394
395
396 /*void EffectStackView2::slotRegionChanged()
397 {
398     if (!m_trackMode) emit updateClipRegion(m_clipref, m_ui.effectlist->currentRow(), m_ui.region_url->text());
399 }*/
400
401 void EffectStackView2::slotCheckMonitorPosition(int renderPos)
402 {
403     if (m_effectMetaInfo.trackMode || (m_clipref && renderPos >= m_clipref->startPos().frames(KdenliveSettings::project_fps()) && renderPos <= m_clipref->endPos().frames(KdenliveSettings::project_fps()))) {
404         if (!m_effectMetaInfo.monitor->getEffectEdit()->getScene()->views().at(0)->isVisible())
405             m_effectMetaInfo.monitor->slotEffectScene(true);
406     } else {
407         m_effectMetaInfo.monitor->slotEffectScene(false);
408     }
409 }
410
411 int EffectStackView2::isTrackMode(bool *ok) const
412 {
413     *ok = m_effectMetaInfo.trackMode;
414     return m_trackindex;
415 }
416
417 void EffectStackView2::clear()
418 {
419     m_effects.clear();
420     QWidget *view = m_ui.container->takeWidget();
421     if (view) {
422         delete view;
423     }
424     m_ui.checkAll->setToolTip(QString());
425     m_ui.checkAll->setText(QString());
426     m_ui.checkAll->setEnabled(false);
427     m_ui.labelComment->setText(QString());
428     setEnabled(false);
429 }
430
431 void EffectStackView2::slotCheckAll(int state)
432 {
433     if (state == Qt::PartiallyChecked) {
434         state = Qt::Checked;
435         m_ui.checkAll->blockSignals(true);
436         m_ui.checkAll->setCheckState(Qt::Checked);
437         m_ui.checkAll->blockSignals(false);
438     }
439
440     bool disabled = state == Qt::Unchecked;
441     for (int i = 0; i < m_effects.count(); i++) {
442         if (!m_effects.at(i)->isGroup()) {
443             m_effects.at(i)->slotEnable(disabled, false);
444         }
445     }
446 }
447
448 void EffectStackView2::slotUpdateCheckAllButton()
449 {
450     bool hasEnabled = false;
451     bool hasDisabled = false;
452     
453     for (int i = 0; i < m_effects.count(); i++) {
454         if (!m_effects.at(i)->enabledButton->isChecked()) hasEnabled = true;
455         else hasDisabled = true; 
456     }
457
458     m_ui.checkAll->blockSignals(true);
459     if (hasEnabled && hasDisabled)
460         m_ui.checkAll->setCheckState(Qt::PartiallyChecked);
461     else if (hasEnabled)
462         m_ui.checkAll->setCheckState(Qt::Checked);
463     else
464         m_ui.checkAll->setCheckState(Qt::Unchecked);
465     m_ui.checkAll->blockSignals(false);
466 }
467
468 void EffectStackView2::deleteCurrentEffect()
469 {
470     for (int i = 0; i < m_effects.count(); i++) {
471         if (m_effects.at(i)->isActive()) {
472             slotDeleteEffect(m_effects.at(i)->effect());
473             break;
474         }
475     }
476 }
477
478 void EffectStackView2::updateProjectFormat(MltVideoProfile profile, Timecode t)
479 {
480     m_effectMetaInfo.profile = profile;
481     m_effectMetaInfo.timecode = t;
482 }
483
484 void EffectStackView2::updateTimecodeFormat()
485 {
486     for (int i = 0; i< m_effects.count(); i++)
487         m_effects.at(i)->updateTimecodeFormat();
488 }
489
490 CollapsibleEffect *EffectStackView2::getEffectByIndex(int ix)
491 {
492     for (int i = 0; i< m_effects.count(); i++) {
493         if (m_effects.at(i)->effectIndex() == ix) {
494             return m_effects.at(i);
495         }
496     }
497     return NULL;
498 }
499
500 void EffectStackView2::slotUpdateEffectParams(const QDomElement old, const QDomElement e, int ix)
501 {
502     if (m_effectMetaInfo.trackMode)
503         emit updateEffect(NULL, m_trackindex, old, e, ix,false);
504     else if (m_clipref) {
505         emit updateEffect(m_clipref, -1, old, e, ix, false);
506         // Make sure the changed effect is currently displayed
507         slotSetCurrentEffect(ix);
508     }
509     QTimer::singleShot(200, this, SLOT(slotCheckWheelEventFilter()));
510 }
511
512 void EffectStackView2::slotSetCurrentEffect(int ix)
513 {
514     if (m_clipref && ix != m_clipref->selectedEffectIndex()) {
515         m_clipref->setSelectedEffect(ix);
516         for (int i = 0; i < m_effects.count(); i++) {
517             if (m_effects.at(i)->effectIndex() == ix) {
518                 m_effects.at(i)->setActive(true);
519                 m_ui.labelComment->setText(i18n(m_effects.at(i)->effect().firstChildElement("description").firstChildElement("full").text().toUtf8().data()));
520                 m_ui.labelComment->setHidden(!m_ui.buttonShowComments->isChecked() || m_ui.labelComment->text().isEmpty());
521             }
522             else m_effects.at(i)->setActive(false);
523         }
524     }
525 }
526
527 void EffectStackView2::slotDeleteGroup(QDomDocument doc)
528 {
529     QDomNodeList effects = doc.elementsByTagName("effect");
530     ClipItem * clip = NULL;
531     int ix;
532     if (m_effectMetaInfo.trackMode) {
533         ix = m_trackindex;
534     }
535     else {
536         clip = m_clipref;
537         ix = -1;
538     }
539     
540     for (int i = 0; i < effects.count(); i++)
541         emit removeEffect(clip, ix, effects.at(i).toElement());
542 }
543
544 void EffectStackView2::slotDeleteEffect(const QDomElement effect)
545 {
546     if (m_effectMetaInfo.trackMode)
547         emit removeEffect(NULL, m_trackindex, effect);
548     else
549         emit removeEffect(m_clipref, -1, effect);
550 }
551
552 void EffectStackView2::slotAddEffect(QDomElement effect)
553 {
554     emit addEffect(m_clipref, effect);
555 }
556
557 void EffectStackView2::slotMoveEffectUp(int index, bool up)
558 {
559     if (up && index <= 1) return;
560     if (!up && index >= m_currentEffectList.count()) return;
561     int endPos;
562     if (up) {
563         endPos = index - 1;
564     }
565     else {
566         endPos =  index + 1;
567     }
568     if (m_effectMetaInfo.trackMode) emit changeEffectPosition(NULL, m_trackindex, index, endPos);
569     else emit changeEffectPosition(m_clipref, -1, index, endPos);
570 }
571
572 void EffectStackView2::slotStartFilterJob(const QString&filterName, const QString&filterParams, const QString&finalFilterName, const QString&consumer, const QString&consumerParams, const QString&properties)
573 {
574     if (!m_clipref) return;
575     emit startFilterJob(m_clipref->info(), m_clipref->clipProducer(), filterName, filterParams, finalFilterName, consumer, consumerParams, properties);
576 }
577
578 void EffectStackView2::slotResetEffect(int ix)
579 {
580     QDomElement old = m_currentEffectList.itemFromIndex(ix);
581     QDomElement dom;
582     QString effectId = old.attribute("id");
583     QMap<QString, EffectsList*> effectLists;
584     effectLists["audio"] = &MainWindow::audioEffects;
585     effectLists["video"] = &MainWindow::videoEffects;
586     effectLists["custom"] = &MainWindow::customEffects;
587     foreach(const QString &type, effectLists.keys()) {
588         EffectsList *list = effectLists[type];
589         dom = list->getEffectByTag(QString(), effectId).cloneNode().toElement();
590         if (!dom.isNull()) break;
591     }
592     if (!dom.isNull()) {
593         dom.setAttribute("kdenlive_ix", old.attribute("kdenlive_ix"));
594         if (m_effectMetaInfo.trackMode) {
595             EffectsList::setParameter(dom, "in", QString::number(0));
596             EffectsList::setParameter(dom, "out", QString::number(m_trackInfo.duration));
597             ItemInfo info;
598             info.track = m_trackInfo.type;
599             info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
600             info.cropStart = GenTime(0);
601             info.startPos = GenTime(-1);
602             info.track = 0;
603             m_effects.at(ix)->updateWidget(info, dom, &m_effectMetaInfo);
604             emit updateEffect(NULL, m_trackindex, old, dom, ix,false);
605         } else {
606             m_clipref->initEffect(dom);
607             for (int i = 0; i < m_effects.count(); i++) {
608                 if (m_effects.at(i)->effectIndex() == ix) {
609                     m_effects.at(i)->updateWidget(m_clipref->info(), dom, &m_effectMetaInfo);
610                     break;
611                 }
612             }
613             //m_ui.region_url->setUrl(KUrl(dom.attribute("region")));
614             emit updateEffect(m_clipref, -1, old, dom, ix,false);
615         }
616     }
617
618     emit showComments(m_ui.buttonShowComments->isChecked());
619     m_ui.labelComment->setHidden(!m_ui.buttonShowComments->isChecked() || m_ui.labelComment->text().isEmpty());
620 }
621
622 void EffectStackView2::slotShowComments()
623 {
624     m_ui.labelComment->setHidden(!m_ui.buttonShowComments->isChecked() || m_ui.labelComment->text().isEmpty());
625     emit showComments(m_ui.buttonShowComments->isChecked());
626 }
627
628 void EffectStackView2::slotCreateRegion(int ix, KUrl url)
629 {
630     QDomElement oldeffect = m_currentEffectList.itemFromIndex(ix);
631     QDomElement neweffect = oldeffect.cloneNode().toElement();
632     QDomElement region = MainWindow::videoEffects.getEffectByTag("region", "region").cloneNode().toElement();
633     region.appendChild(region.ownerDocument().importNode(neweffect, true));
634     region.setAttribute("kdenlive_ix", ix);
635     EffectsList::setParameter(region, "resource", url.path());
636     if (m_effectMetaInfo.trackMode)
637         emit updateEffect(NULL, m_trackindex, oldeffect, region, ix,false);
638     else if (m_clipref) {
639         emit updateEffect(m_clipref, -1, oldeffect, region, ix, false);
640         // Make sure the changed effect is currently displayed
641         //slotSetCurrentEffect(ix);
642     }
643     // refresh effect stack
644     ItemInfo info;
645     bool isSelected = false;
646     if (m_effectMetaInfo.trackMode) { 
647         info.track = m_trackInfo.type;
648         info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
649         info.cropStart = GenTime(0);
650         info.startPos = GenTime(-1);
651         info.track = 0;
652     }
653     else {
654         info = m_clipref->info();
655     }
656     CollapsibleEffect *current = getEffectByIndex(ix);
657     m_effects.removeAll(current);
658     current->setEnabled(false);
659     m_currentEffectList.removeAt(ix);
660     m_currentEffectList.insert(region);
661     current->deleteLater();
662     CollapsibleEffect *currentEffect = new CollapsibleEffect(region, m_currentEffectList.itemFromIndex(ix), info, &m_effectMetaInfo, ix == m_currentEffectList.count() - 1, m_ui.container->widget());
663     connect(currentEffect, SIGNAL(parameterChanged(const QDomElement, const QDomElement, int)), this , SLOT(slotUpdateEffectParams(const QDomElement, const QDomElement, int)));
664     if (m_effectMetaInfo.trackMode) {
665         isSelected = currentEffect->effectIndex() == 1;
666     }
667     else {
668         isSelected = currentEffect->effectIndex() == m_clipref->selectedEffectIndex();
669     }
670     if (isSelected) currentEffect->setActive(true);
671     m_effects.append(currentEffect);
672     // TODO: region in group?
673     //if (group) {
674     //  group->addGroupEffect(currentEffect);
675     //} else {
676     QVBoxLayout *vbox = static_cast <QVBoxLayout *> (m_ui.container->widget()->layout());
677     vbox->insertWidget(ix, currentEffect);
678     //}
679
680     // Check drag & drop
681     currentEffect->installEventFilter( this );
682         
683     QTimer::singleShot(200, this, SLOT(slotCheckWheelEventFilter()));    
684     
685 }
686
687 void EffectStackView2::slotCreateGroup(int ix)
688 {
689     QDomElement oldeffect = m_currentEffectList.itemFromIndex(ix);
690     QDomElement neweffect = oldeffect.cloneNode().toElement();
691     EffectInfo effectinfo;
692     effectinfo.fromString(oldeffect.attribute("kdenlive_info"));
693     effectinfo.groupIndex = m_groupIndex;
694     neweffect.setAttribute("kdenlive_info", effectinfo.toString());
695
696     ItemInfo info;
697     if (m_effectMetaInfo.trackMode) { 
698         info.track = m_trackInfo.type;
699         info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
700         info.cropStart = GenTime(0);
701         info.startPos = GenTime(-1);
702         info.track = 0;
703         emit updateEffect(NULL, m_trackindex, oldeffect, neweffect, ix, false);
704     } else {
705         emit updateEffect(m_clipref, -1, oldeffect, neweffect, ix, false);
706     }
707     
708     QVBoxLayout *l = static_cast<QVBoxLayout *>(m_ui.container->widget()->layout());
709     int groupPos = 0;
710     CollapsibleEffect *effectToMove = NULL;
711     for (int i = 0; i < m_effects.count(); i++) {
712         if (m_effects.at(i)->effectIndex() == ix) {
713             effectToMove = m_effects.at(i);
714             groupPos = l->indexOf(effectToMove);
715             l->removeWidget(effectToMove);
716             break;
717         }
718     }
719     
720     CollapsibleGroup *group = new CollapsibleGroup(m_groupIndex, ix == 1, ix == m_currentEffectList.count() - 2, QString(), m_ui.container->widget());
721     m_groupIndex++;
722     connect(group, SIGNAL(moveEffect(int,int,int,QString)), this , SLOT(slotMoveEffect(int,int,int,QString)));
723     connect(group, SIGNAL(unGroup(CollapsibleGroup*)), this , SLOT(slotUnGroup(CollapsibleGroup*)));
724     connect(group, SIGNAL(groupRenamed(CollapsibleGroup *)), this , SLOT(slotRenameGroup(CollapsibleGroup*)));
725     connect(group, SIGNAL(reloadEffects()), this , SIGNAL(reloadEffects()));
726     connect(group, SIGNAL(deleteGroup(QDomDocument)), this , SLOT(slotDeleteGroup(QDomDocument)));
727     l->insertWidget(groupPos, group);
728     group->installEventFilter( this );
729     group->addGroupEffect(effectToMove);
730 }
731
732 void EffectStackView2::slotMoveEffect(int currentIndex, int newIndex, int groupIndex, QString groupName)
733 {
734     CollapsibleEffect *effectToMove = getEffectByIndex(currentIndex);
735     if (effectToMove == NULL) return;
736
737     QDomElement oldeffect = effectToMove->effect();
738     QDomElement neweffect = oldeffect.cloneNode().toElement();
739     
740     EffectInfo effectinfo;
741     effectinfo.fromString(oldeffect.attribute("kdenlive_info"));
742     effectinfo.groupIndex = groupIndex;
743     effectinfo.groupName = groupName;
744     neweffect.setAttribute("kdenlive_info", effectinfo.toString());
745
746     ItemInfo info;
747     if (m_effectMetaInfo.trackMode) { 
748         info.track = m_trackInfo.type;
749         info.cropDuration = GenTime(m_trackInfo.duration, KdenliveSettings::project_fps());
750         info.cropStart = GenTime(0);
751         info.startPos = GenTime(-1);
752         info.track = 0;
753         emit updateEffect(NULL, m_trackindex, oldeffect, neweffect, effectToMove->effectIndex(),false);
754     } else {
755         emit updateEffect(m_clipref, -1, oldeffect, neweffect, effectToMove->effectIndex(),false);
756     }
757     //if (currentIndex == newIndex) return;
758     // Update effect index with new position
759     if (m_effectMetaInfo.trackMode) {
760         emit changeEffectPosition(NULL, m_trackindex, currentIndex, newIndex);
761     }
762     else {
763         emit changeEffectPosition(m_clipref, -1, currentIndex, newIndex);
764     }
765 }
766
767 void EffectStackView2::slotUnGroup(CollapsibleGroup* group)
768 {
769     QVBoxLayout *l = static_cast<QVBoxLayout *>(m_ui.container->widget()->layout());
770     int ix = l->indexOf(group);
771     group->removeGroup(ix, l);
772     group->deleteLater();
773 }
774
775 void EffectStackView2::slotRenameGroup(CollapsibleGroup *group)
776 {
777     QList <CollapsibleEffect*> effects = group->effects();
778     for (int i = 0; i < effects.count(); i++) {
779         QDomElement origin = effects.at(i)->effect();
780         QDomElement changed = origin.cloneNode().toElement();
781         changed.setAttribute("kdenlive_info", effects.at(i)->infoString());
782         if (m_effectMetaInfo.trackMode) { 
783             emit updateEffect(NULL, m_trackindex, origin, changed, effects.at(i)->effectIndex(),false);
784         } else {
785             emit updateEffect(m_clipref, -1, origin, changed, effects.at(i)->effectIndex(),false);
786         }
787     }
788 }
789
790 #include "effectstackview2.moc"