]> git.sesse.net Git - kdenlive/blob - src/effectstack/collapsiblegroup.cpp
Effect groups can now be dropped onto another clip
[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
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 }
53
54
55 CollapsibleGroup::CollapsibleGroup(int ix, bool firstGroup, bool lastGroup, QString groupName, QWidget * parent) :
56         AbstractCollapsibleWidget(parent),
57         m_index(ix)
58 {
59     setupUi(this);
60     m_subWidgets = QList <CollapsibleEffect *> ();
61     setFont(KGlobalSettings::smallestReadableFont());
62     QHBoxLayout *l = static_cast <QHBoxLayout *>(framegroup->layout());
63     m_title = new MyEditableLabel(this);
64     l->insertWidget(4, m_title);
65     m_title->setText(groupName.isEmpty() ? i18n("Effect Group") : groupName);
66     connect(m_title, SIGNAL(editingFinished()), this, SLOT(slotRenameGroup()));
67     buttonUp->setIcon(KIcon("kdenlive-up"));
68     buttonUp->setToolTip(i18n("Move effect up"));
69     buttonDown->setIcon(KIcon("kdenlive-down"));
70     buttonDown->setToolTip(i18n("Move effect down"));
71
72     buttonDel->setIcon(KIcon("kdenlive-deleffect"));
73     buttonDel->setToolTip(i18n("Delete effect"));
74     if (firstGroup) buttonUp->setVisible(false);
75     if (lastGroup) buttonDown->setVisible(false);
76     m_menu = new QMenu;
77     m_menu->addAction(KIcon("view-refresh"), i18n("Reset effect"), this, SLOT(slotResetEffect()));
78     m_menu->addAction(KIcon("document-save"), i18n("Save effect"), this, SLOT(slotSaveEffect()));
79     
80     effecticon->setPixmap(KIcon("folder").pixmap(16,16));
81     m_menu->addAction(KIcon("list-remove"), i18n("Ungroup"), this, SLOT(slotUnGroup()));
82     setAcceptDrops(true);
83     menuButton->setIcon(KIcon("kdenlive-menu"));
84     menuButton->setMenu(m_menu);
85     
86     enabledBox->setChecked(true);
87
88     connect(collapseButton, SIGNAL(clicked()), this, SLOT(slotSwitch()));
89     connect(enabledBox, SIGNAL(toggled(bool)), this, SLOT(slotEnable(bool)));
90     connect(buttonUp, SIGNAL(clicked()), this, SLOT(slotEffectUp()));
91     connect(buttonDown, SIGNAL(clicked()), this, SLOT(slotEffectDown()));
92     connect(buttonDel, SIGNAL(clicked()), this, SLOT(slotDeleteEffect()));
93
94 }
95
96 CollapsibleGroup::~CollapsibleGroup()
97 {
98     delete m_menu;
99 }
100
101 void CollapsibleGroup::slotUnGroup()
102 {
103     emit unGroup(this);
104 }
105
106 bool CollapsibleGroup::isActive() const
107 {
108     return decoframegroup->property("active").toBool();
109 }
110
111 void CollapsibleGroup::setActive(bool activate)
112 {
113     decoframegroup->setProperty("active", activate);
114     decoframegroup->setStyleSheet(decoframegroup->styleSheet());
115 }
116
117 void CollapsibleGroup::mouseDoubleClickEvent ( QMouseEvent * event )
118 {
119     if (framegroup->underMouse() && collapseButton->isEnabled()) slotSwitch();
120     QWidget::mouseDoubleClickEvent(event);
121 }
122
123
124 void CollapsibleGroup::slotEnable(bool enable)
125 {
126     m_title->setEnabled(enable);
127     enabledBox->blockSignals(true);
128     enabledBox->setChecked(enable);
129     enabledBox->blockSignals(false);
130     QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
131     if (vbox == NULL) return;
132     for (int i = 0; i < vbox->count(); i++) {
133         CollapsibleGroup *e = static_cast<CollapsibleGroup *>(vbox->itemAt(i)->widget());
134         if (e) e->enabledBox->setChecked(enable);// slotEnable(enable);
135     }
136 }
137
138 void CollapsibleGroup::slotDeleteEffect()
139 {
140     emit deleteGroup(groupIndex());
141 }
142
143 void CollapsibleGroup::slotEffectUp()
144 {
145     emit changeGroupPosition(groupIndex(), true);
146 }
147
148 void CollapsibleGroup::slotEffectDown()
149 {
150     emit changeGroupPosition(groupIndex(), false);
151 }
152
153 void CollapsibleGroup::slotSaveEffect()
154 {
155     QString name = QInputDialog::getText(this, i18n("Save Effect"), i18n("Name for saved effect: "));
156     if (name.isEmpty()) return;
157     QString path = KStandardDirs::locateLocal("appdata", "effects/", true);
158     path = path + name + ".xml";
159     if (QFile::exists(path)) if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", path)) == KMessageBox::No) return;
160
161     /*TODO
162     QDomDocument doc;
163     QDomElement effect = m_effect.cloneNode().toElement();
164     doc.appendChild(doc.importNode(effect, true));
165     effect = doc.firstChild().toElement();
166     effect.removeAttribute("kdenlive_ix");
167     effect.setAttribute("id", name);
168     effect.setAttribute("type", "custom");
169     QDomElement effectname = effect.firstChildElement("name");
170     effect.removeChild(effectname);
171     effectname = doc.createElement("name");
172     QDomText nametext = doc.createTextNode(name);
173     effectname.appendChild(nametext);
174     effect.insertBefore(effectname, QDomNode());
175     QDomElement effectprops = effect.firstChildElement("properties");
176     effectprops.setAttribute("id", name);
177     effectprops.setAttribute("type", "custom");
178
179     QFile file(path);
180     if (file.open(QFile::WriteOnly | QFile::Truncate)) {
181         QTextStream out(&file);
182         out << doc.toString();
183     }
184     file.close();
185     emit reloadEffects();*/
186 }
187
188 void CollapsibleGroup::slotResetEffect()
189 {
190     //TODO: emit resetEffect(effectIndex());
191 }
192
193 void CollapsibleGroup::slotSwitch()
194 {
195     bool enable = !widgetFrame->isVisible();
196     slotShow(enable);
197 }
198
199 void CollapsibleGroup::slotShow(bool show)
200 {
201     widgetFrame->setVisible(show);
202     if (show) {
203         collapseButton->setArrowType(Qt::DownArrow);
204         m_info.isCollapsed = false;
205     }
206     else {
207         collapseButton->setArrowType(Qt::RightArrow);
208         m_info.isCollapsed = true;
209     }
210     //emit parameterChanged(m_original_effect, m_effect, effectIndex());   
211 }
212
213 QWidget *CollapsibleGroup::title() const
214 {
215     return m_title;
216 }
217
218 void CollapsibleGroup::addGroupEffect(CollapsibleEffect *effect)
219 {
220     QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
221     if (vbox == NULL) {
222         vbox = new QVBoxLayout();
223         vbox->setContentsMargins(0, 0, 0, 0);
224         vbox->setSpacing(2);
225         widgetFrame->setLayout(vbox);
226     }
227     effect->setGroupIndex(groupIndex());
228     effect->setGroupName(m_title->text());
229     m_subWidgets.append(effect);
230     vbox->addWidget(effect);
231 }
232
233 QString CollapsibleGroup::infoString() const
234 {
235     return m_info.toString();
236 }
237
238 void CollapsibleGroup::removeGroup(int ix, QVBoxLayout *layout)
239 {
240     QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
241     if (vbox == NULL) return;
242     for (int i = m_subWidgets.count() - 1; i >= 0 ; i--) {
243         vbox->removeWidget(m_subWidgets.at(i));
244         layout->insertWidget(ix, m_subWidgets.at(i));
245     }
246     m_subWidgets.clear();
247 }
248
249 int CollapsibleGroup::groupIndex() const
250 {
251     return m_index;
252 }
253
254 bool CollapsibleGroup::isGroup() const
255 {
256     return true;
257 }
258
259 void CollapsibleGroup::updateTimecodeFormat()
260 {
261     QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
262     if (vbox == NULL) return;
263     for (int j = vbox->count() - 1; j >= 0; j--) {
264         CollapsibleEffect *e = static_cast<CollapsibleEffect *>(vbox->itemAt(j)->widget());
265         if (e) e->updateTimecodeFormat();
266     }
267 }
268
269 void CollapsibleGroup::dragEnterEvent(QDragEnterEvent *event)
270 {
271     if (event->mimeData()->hasFormat("kdenlive/effectslist")) {
272         framegroup->setProperty("active", true);
273         framegroup->setStyleSheet(framegroup->styleSheet());
274         event->acceptProposedAction();
275     }
276 }
277
278 void CollapsibleGroup::dragLeaveEvent(QDragLeaveEvent */*event*/)
279 {
280     framegroup->setProperty("active", false);
281     framegroup->setStyleSheet(framegroup->styleSheet());
282 }
283
284 void CollapsibleGroup::dropEvent(QDropEvent *event)
285 {
286     framegroup->setProperty("active", false);
287     framegroup->setStyleSheet(framegroup->styleSheet());
288     const QString effects = QString::fromUtf8(event->mimeData()->data("kdenlive/effectslist"));
289     //event->acceptProposedAction();
290     QDomDocument doc;
291     doc.setContent(effects, true);
292     QDomElement e = doc.documentElement();
293     int ix = e.attribute("kdenlive_ix").toInt();
294     if (ix == 0) {
295         // effect dropped from effects list, add it
296         e.setAttribute("kdenlive_ix", ix);
297         event->setDropAction(Qt::CopyAction);
298         event->accept();
299         emit addEffect(e);
300         return;
301     }
302     if (m_subWidgets.isEmpty()) return;
303     int new_index = m_subWidgets.at(m_subWidgets.count() - 1)->effectIndex();
304     emit moveEffect(ix, new_index, m_index, m_title->text());
305     event->setDropAction(Qt::MoveAction);
306     event->accept();
307 }
308
309 void CollapsibleGroup::slotRenameGroup()
310 {
311     m_title->setReadOnly(true);
312     if (m_title->text().isEmpty()) m_title->setText(i18n("Effect Group"));
313     for (int j = 0; j < m_subWidgets.count(); j++) {
314         m_subWidgets.at(j)->setGroupName(m_title->text());
315     }
316     emit groupRenamed(this);
317 }
318
319 QList <CollapsibleEffect*> CollapsibleGroup::effects()
320 {
321     return m_subWidgets;
322 }
323
324 QDomDocument CollapsibleGroup::effectsData()
325 {
326     QDomDocument doc;
327     QDomElement list = doc.createElement("list");
328     list.setAttribute("name", m_title->text());
329     doc.appendChild(list);
330     for (int j = 0; j < m_subWidgets.count(); j++) {
331         list.appendChild(doc.importNode(m_subWidgets.at(j)->effect(), true));
332     }
333     return doc;
334 }
335