]> git.sesse.net Git - kdenlive/blob - src/effectstack/collapsiblegroup.cpp
Implement deletion of an effect group
[kdenlive] / src / effectstack / collapsiblegroup.cpp
1 /***************************************************************************
2  *   Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20
21 #include "collapsiblegroup.h"
22
23
24 #include <QMenu>
25 #include <QVBoxLayout>
26 #include <QInputDialog>
27 #include <QDragEnterEvent>
28 #include <QDropEvent>
29 #include <QMutexLocker>
30
31 #include <KDebug>
32 #include <KGlobalSettings>
33 #include <KLocale>
34 #include <KMessageBox>
35 #include <KStandardDirs>
36 #include <KFileDialog>
37 #include <KUrlRequester>
38 #include <KColorScheme>
39
40 MyEditableLabel::MyEditableLabel(QWidget * parent):
41     QLineEdit(parent)
42 {
43     setFrame(false);
44     setReadOnly(true);
45     setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
46 }
47
48 void MyEditableLabel::mouseDoubleClickEvent ( QMouseEvent * e )
49 {
50     setReadOnly(false);
51     selectAll();
52     e->accept();
53 }
54
55
56 CollapsibleGroup::CollapsibleGroup(int ix, bool firstGroup, bool lastGroup, QString groupName, QWidget * parent) :
57         AbstractCollapsibleWidget(parent),
58         m_index(ix)
59 {
60     setupUi(this);
61     m_subWidgets = QList <CollapsibleEffect *> ();
62     setFont(KGlobalSettings::smallestReadableFont());
63     QHBoxLayout *l = static_cast <QHBoxLayout *>(framegroup->layout());
64     m_title = new MyEditableLabel(this);
65     l->insertWidget(4, m_title);
66     m_title->setText(groupName.isEmpty() ? i18n("Effect Group") : groupName);
67     connect(m_title, SIGNAL(editingFinished()), this, SLOT(slotRenameGroup()));
68     buttonUp->setIcon(KIcon("kdenlive-up"));
69     buttonUp->setToolTip(i18n("Move effect up"));
70     buttonDown->setIcon(KIcon("kdenlive-down"));
71     buttonDown->setToolTip(i18n("Move effect down"));
72
73     buttonDel->setIcon(KIcon("kdenlive-deleffect"));
74     buttonDel->setToolTip(i18n("Delete effect"));
75     if (firstGroup) buttonUp->setVisible(false);
76     if (lastGroup) buttonDown->setVisible(false);
77     m_menu = new QMenu;
78     m_menu->addAction(KIcon("view-refresh"), i18n("Reset Group"), this, SLOT(slotResetGroup()));
79     m_menu->addAction(KIcon("document-save"), i18n("Save Group"), this, SLOT(slotSaveGroup()));
80     
81     effecticon->setPixmap(KIcon("folder").pixmap(16,16));
82     m_menu->addAction(KIcon("list-remove"), i18n("Ungroup"), this, SLOT(slotUnGroup()));
83     setAcceptDrops(true);
84     menuButton->setIcon(KIcon("kdenlive-menu"));
85     menuButton->setMenu(m_menu);
86     
87     enabledBox->setChecked(true);
88
89     connect(collapseButton, SIGNAL(clicked()), this, SLOT(slotSwitch()));
90     connect(enabledBox, SIGNAL(toggled(bool)), this, SLOT(slotEnable(bool)));
91     connect(buttonUp, SIGNAL(clicked()), this, SLOT(slotEffectUp()));
92     connect(buttonDown, SIGNAL(clicked()), this, SLOT(slotEffectDown()));
93     connect(buttonDel, SIGNAL(clicked()), this, SLOT(slotDeleteGroup()));
94
95 }
96
97 CollapsibleGroup::~CollapsibleGroup()
98 {
99     delete m_menu;
100 }
101
102 void CollapsibleGroup::slotUnGroup()
103 {
104     emit unGroup(this);
105 }
106
107 bool CollapsibleGroup::isActive() const
108 {
109     return decoframegroup->property("active").toBool();
110 }
111
112 void CollapsibleGroup::setActive(bool activate)
113 {
114     decoframegroup->setProperty("active", activate);
115     decoframegroup->setStyleSheet(decoframegroup->styleSheet());
116 }
117
118 void CollapsibleGroup::mouseDoubleClickEvent ( QMouseEvent * event )
119 {
120     if (framegroup->underMouse() && collapseButton->isEnabled()) slotSwitch();
121     QWidget::mouseDoubleClickEvent(event);
122 }
123
124
125 void CollapsibleGroup::slotEnable(bool enable)
126 {
127     m_title->setEnabled(enable);
128     enabledBox->blockSignals(true);
129     enabledBox->setChecked(enable);
130     enabledBox->blockSignals(false);
131     QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
132     if (vbox == NULL) return;
133     for (int i = 0; i < vbox->count(); i++) {
134         CollapsibleGroup *e = static_cast<CollapsibleGroup *>(vbox->itemAt(i)->widget());
135         if (e) e->enabledBox->setChecked(enable);// slotEnable(enable);
136     }
137 }
138
139 void CollapsibleGroup::slotDeleteGroup()
140 {
141     QDomDocument doc;
142     // delete effects from the last one to the first, otherwise each deletion would trigger an update
143     // in other effects's kdenlive_ix index.
144     for (int i = m_subWidgets.count() - 1; i >= 0; i--)
145         doc.appendChild(doc.importNode(m_subWidgets.at(i)->effect(), true));
146     emit deleteGroup(m_index, doc);
147 }
148
149 void CollapsibleGroup::slotEffectUp()
150 {
151     emit changeGroupPosition(groupIndex(), true);
152 }
153
154 void CollapsibleGroup::slotEffectDown()
155 {
156     emit changeGroupPosition(groupIndex(), false);
157 }
158
159 void CollapsibleGroup::slotSaveGroup()
160 {
161     QString name = QInputDialog::getText(this, i18n("Save Group"), i18n("Name for saved group: "), QLineEdit::Normal, m_title->text());
162     if (name.isEmpty()) return;
163     QString path = KStandardDirs::locateLocal("appdata", "effects/", true);
164     path = path + name + ".xml";
165     if (QFile::exists(path)) if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", path)) == KMessageBox::No) return;
166
167     QDomDocument doc = effectsData();
168     QDomElement base = doc.documentElement();
169     QDomNodeList effects = base.elementsByTagName("effect");
170     for (int i = 0; i < effects.count(); i++) {
171         QDomElement eff = effects.at(i).toElement();
172         eff.removeAttribute("kdenlive_ix");
173         QString kdenliveInfo = eff.attribute("kdenlive_info");
174         // Make sure all effects have the correct new group name
175         if (kdenliveInfo.count('/') >= 2) {
176             eff.setAttribute("kdenlive_info", kdenliveInfo.section('/', 0, 1) + "/" + name);
177         }
178     }
179     
180     base.setAttribute("name", name);
181     base.setAttribute("id", name);
182     base.setAttribute("type", "custom");  
183
184     QFile file(path);
185     if (file.open(QFile::WriteOnly | QFile::Truncate)) {
186         QTextStream out(&file);
187         out << doc.toString();
188     }
189     file.close();
190     emit reloadEffects();
191 }
192
193 void CollapsibleGroup::slotResetGroup()
194 {
195     QMutexLocker lock(&m_mutex);
196     for (int i = 0; i < m_subWidgets.count(); i++)
197         m_subWidgets.at(i)->slotResetEffect();
198 }
199
200 void CollapsibleGroup::slotSwitch()
201 {
202     bool enable = !widgetFrame->isVisible();
203     slotShow(enable);
204 }
205
206 void CollapsibleGroup::slotShow(bool show)
207 {
208     widgetFrame->setVisible(show);
209     if (show) {
210         collapseButton->setArrowType(Qt::DownArrow);
211         m_info.isCollapsed = false;
212     }
213     else {
214         collapseButton->setArrowType(Qt::RightArrow);
215         m_info.isCollapsed = true;
216     }
217     //emit parameterChanged(m_original_effect, m_effect, effectIndex());   
218 }
219
220 QWidget *CollapsibleGroup::title() const
221 {
222     return m_title;
223 }
224
225 void CollapsibleGroup::addGroupEffect(CollapsibleEffect *effect)
226 {
227     QMutexLocker lock(&m_mutex);
228     QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
229     if (vbox == NULL) {
230         vbox = new QVBoxLayout();
231         vbox->setContentsMargins(0, 0, 0, 0);
232         vbox->setSpacing(2);
233         widgetFrame->setLayout(vbox);
234     }
235     effect->setGroupIndex(groupIndex());
236     effect->setGroupName(m_title->text());
237     m_subWidgets.append(effect);
238     vbox->addWidget(effect);
239 }
240
241 QString CollapsibleGroup::infoString() const
242 {
243     return m_info.toString();
244 }
245
246 void CollapsibleGroup::removeGroup(int ix, QVBoxLayout *layout)
247 {
248     QMutexLocker lock(&m_mutex);
249     QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
250     if (vbox == NULL) return;
251     for (int i = m_subWidgets.count() - 1; i >= 0 ; i--) {
252         vbox->removeWidget(m_subWidgets.at(i));
253         layout->insertWidget(ix, m_subWidgets.at(i));
254         m_subWidgets.at(i)->removeFromGroup();
255     }
256     m_subWidgets.clear();
257 }
258
259 int CollapsibleGroup::groupIndex() const
260 {
261     return m_index;
262 }
263
264 bool CollapsibleGroup::isGroup() const
265 {
266     return true;
267 }
268
269 void CollapsibleGroup::updateTimecodeFormat()
270 {
271     QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
272     if (vbox == NULL) return;
273     for (int j = vbox->count() - 1; j >= 0; j--) {
274         CollapsibleEffect *e = static_cast<CollapsibleEffect *>(vbox->itemAt(j)->widget());
275         if (e) e->updateTimecodeFormat();
276     }
277 }
278
279 void CollapsibleGroup::dragEnterEvent(QDragEnterEvent *event)
280 {
281     if (event->mimeData()->hasFormat("kdenlive/effectslist")) {
282         framegroup->setProperty("active", true);
283         framegroup->setStyleSheet(framegroup->styleSheet());
284         event->acceptProposedAction();
285     }
286 }
287
288 void CollapsibleGroup::dragLeaveEvent(QDragLeaveEvent */*event*/)
289 {
290     framegroup->setProperty("active", false);
291     framegroup->setStyleSheet(framegroup->styleSheet());
292 }
293
294 void CollapsibleGroup::dropEvent(QDropEvent *event)
295 {
296     framegroup->setProperty("active", false);
297     framegroup->setStyleSheet(framegroup->styleSheet());
298     const QString effects = QString::fromUtf8(event->mimeData()->data("kdenlive/effectslist"));
299     //event->acceptProposedAction();
300     QDomDocument doc;
301     doc.setContent(effects, true);
302     QDomElement e = doc.documentElement();
303     int ix = e.attribute("kdenlive_ix").toInt();
304     if (ix == 0) {
305         // effect dropped from effects list, add it
306         e.setAttribute("kdenlive_ix", ix);
307         event->setDropAction(Qt::CopyAction);
308         event->accept();
309         emit addEffect(e);
310         return;
311     }
312     if (m_subWidgets.isEmpty()) return;
313     int new_index = m_subWidgets.last()->effectIndex();
314     emit moveEffect(ix, new_index, m_index, m_title->text());
315     event->setDropAction(Qt::MoveAction);
316     event->accept();
317 }
318
319 void CollapsibleGroup::slotRenameGroup()
320 {
321     m_title->setReadOnly(true);
322     if (m_title->text().isEmpty()) m_title->setText(i18n("Effect Group"));
323     for (int j = 0; j < m_subWidgets.count(); j++) {
324         m_subWidgets.at(j)->setGroupName(m_title->text());
325     }
326     emit groupRenamed(this);
327 }
328
329 QList <CollapsibleEffect*> CollapsibleGroup::effects()
330 {
331     QMutexLocker lock(&m_mutex);
332     return m_subWidgets;
333 }
334
335 QDomDocument CollapsibleGroup::effectsData()
336 {
337     QMutexLocker lock(&m_mutex);
338     QDomDocument doc;
339     QDomElement list = doc.createElement("effectgroup");
340     list.setAttribute("name", m_title->text());
341     doc.appendChild(list);
342     for (int j = 0; j < m_subWidgets.count(); j++) {
343         list.appendChild(doc.importNode(m_subWidgets.at(j)->effect(), true));
344     }
345     return doc;
346 }
347