1 /***************************************************************************
2 * Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org) *
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. *
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. *
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 ***************************************************************************/
21 #include "collapsiblegroup.h"
25 #include <QVBoxLayout>
26 #include <QInputDialog>
27 #include <QDragEnterEvent>
29 #include <QMutexLocker>
32 #include <KGlobalSettings>
34 #include <KMessageBox>
35 #include <KStandardDirs>
36 #include <KFileDialog>
37 #include <KUrlRequester>
38 #include <KColorScheme>
40 MyEditableLabel::MyEditableLabel(QWidget * parent):
45 setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
48 void MyEditableLabel::mouseDoubleClickEvent ( QMouseEvent * e )
56 CollapsibleGroup::CollapsibleGroup(int ix, bool firstGroup, bool lastGroup, QString groupName, QWidget * parent) :
57 AbstractCollapsibleWidget(parent),
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(3, 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"));
73 buttonDel->setIcon(KIcon("kdenlive-deleffect"));
74 buttonDel->setToolTip(i18n("Delete effect"));
75 if (firstGroup) buttonUp->setVisible(false);
76 if (lastGroup) buttonDown->setVisible(false);
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()));
81 m_menu->addAction(KIcon("list-remove"), i18n("Ungroup"), this, SLOT(slotUnGroup()));
83 menuButton->setIcon(KIcon("kdenlive-menu"));
84 menuButton->setMenu(m_menu);
86 enabledButton->setChecked(false);
87 enabledButton->setIcon(KIcon("visible"));
89 connect(collapseButton, SIGNAL(clicked()), this, SLOT(slotSwitch()));
90 connect(enabledButton, 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()));
97 CollapsibleGroup::~CollapsibleGroup()
102 void CollapsibleGroup::slotUnGroup()
107 bool CollapsibleGroup::isActive() const
109 return decoframegroup->property("active").toBool();
112 void CollapsibleGroup::setActive(bool activate)
114 decoframegroup->setProperty("active", activate);
115 decoframegroup->setStyleSheet(decoframegroup->styleSheet());
118 void CollapsibleGroup::mouseDoubleClickEvent ( QMouseEvent * event )
120 if (framegroup->underMouse() && collapseButton->isEnabled()) slotSwitch();
121 QWidget::mouseDoubleClickEvent(event);
125 void CollapsibleGroup::slotEnable(bool disable)
127 m_title->setEnabled(!disable);
128 enabledButton->blockSignals(true);
129 enabledButton->setChecked(disable);
130 enabledButton->setIcon(disable ? KIcon("novisible") : KIcon("visible"));
131 enabledButton->blockSignals(false);
132 for (int i = 0; i < m_subWidgets.count(); i++)
133 m_subWidgets.at(i)->slotEnable(disable);
136 void CollapsibleGroup::slotDeleteGroup()
139 // delete effects from the last one to the first, otherwise each deletion would trigger an update
140 // in other effects's kdenlive_ix index.
141 for (int i = m_subWidgets.count() - 1; i >= 0; i--)
142 doc.appendChild(doc.importNode(m_subWidgets.at(i)->effect(), true));
143 emit deleteGroup(m_index, doc);
146 void CollapsibleGroup::slotEffectUp()
148 emit changeGroupPosition(groupIndex(), true);
151 void CollapsibleGroup::slotEffectDown()
153 emit changeGroupPosition(groupIndex(), false);
156 void CollapsibleGroup::slotSaveGroup()
158 QString name = QInputDialog::getText(this, i18n("Save Group"), i18n("Name for saved group: "), QLineEdit::Normal, m_title->text());
159 if (name.isEmpty()) return;
160 QString path = KStandardDirs::locateLocal("appdata", "effects/", true);
161 path = path + name + ".xml";
162 if (QFile::exists(path)) if (KMessageBox::questionYesNo(this, i18n("File %1 already exists.\nDo you want to overwrite it?", path)) == KMessageBox::No) return;
164 QDomDocument doc = effectsData();
165 QDomElement base = doc.documentElement();
166 QDomNodeList effects = base.elementsByTagName("effect");
167 for (int i = 0; i < effects.count(); i++) {
168 QDomElement eff = effects.at(i).toElement();
169 eff.removeAttribute("kdenlive_ix");
170 QString kdenliveInfo = eff.attribute("kdenlive_info");
171 // Make sure all effects have the correct new group name
172 if (kdenliveInfo.count('/') >= 2) {
173 eff.setAttribute("kdenlive_info", kdenliveInfo.section('/', 0, 1) + "/" + name);
177 base.setAttribute("name", name);
178 base.setAttribute("id", name);
179 base.setAttribute("type", "custom");
182 if (file.open(QFile::WriteOnly | QFile::Truncate)) {
183 QTextStream out(&file);
184 out << doc.toString();
187 emit reloadEffects();
190 void CollapsibleGroup::slotResetGroup()
192 QMutexLocker lock(&m_mutex);
193 for (int i = 0; i < m_subWidgets.count(); i++)
194 m_subWidgets.at(i)->slotResetEffect();
197 void CollapsibleGroup::slotSwitch()
199 bool enable = !widgetFrame->isVisible();
203 void CollapsibleGroup::slotShow(bool show)
205 widgetFrame->setVisible(show);
207 collapseButton->setArrowType(Qt::DownArrow);
208 m_info.isCollapsed = false;
211 collapseButton->setArrowType(Qt::RightArrow);
212 m_info.isCollapsed = true;
214 //emit parameterChanged(m_original_effect, m_effect, effectIndex());
217 QWidget *CollapsibleGroup::title() const
222 void CollapsibleGroup::addGroupEffect(CollapsibleEffect *effect)
224 QMutexLocker lock(&m_mutex);
225 QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
227 vbox = new QVBoxLayout();
228 vbox->setContentsMargins(0, 0, 0, 0);
230 widgetFrame->setLayout(vbox);
232 effect->setGroupIndex(groupIndex());
233 effect->setGroupName(m_title->text());
234 effect->decoframe->setObjectName("decoframesub");
235 m_subWidgets.append(effect);
236 vbox->addWidget(effect);
239 QString CollapsibleGroup::infoString() const
241 return m_info.toString();
244 void CollapsibleGroup::removeGroup(int ix, QVBoxLayout *layout)
246 QMutexLocker lock(&m_mutex);
247 QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
248 if (vbox == NULL) return;
249 for (int i = m_subWidgets.count() - 1; i >= 0 ; i--) {
250 vbox->removeWidget(m_subWidgets.at(i));
251 layout->insertWidget(ix, m_subWidgets.at(i));
252 m_subWidgets.at(i)->decoframe->setObjectName("decoframe");
253 m_subWidgets.at(i)->removeFromGroup();
255 m_subWidgets.clear();
258 int CollapsibleGroup::groupIndex() const
263 bool CollapsibleGroup::isGroup() const
268 void CollapsibleGroup::updateTimecodeFormat()
270 QVBoxLayout *vbox = static_cast<QVBoxLayout *>(widgetFrame->layout());
271 if (vbox == NULL) return;
272 for (int j = vbox->count() - 1; j >= 0; j--) {
273 CollapsibleEffect *e = static_cast<CollapsibleEffect *>(vbox->itemAt(j)->widget());
274 if (e) e->updateTimecodeFormat();
278 void CollapsibleGroup::dragEnterEvent(QDragEnterEvent *event)
280 if (event->mimeData()->hasFormat("kdenlive/effectslist")) {
281 framegroup->setProperty("target", true);
282 framegroup->setStyleSheet(framegroup->styleSheet());
283 event->acceptProposedAction();
287 void CollapsibleGroup::dragLeaveEvent(QDragLeaveEvent */*event*/)
289 framegroup->setProperty("target", false);
290 framegroup->setStyleSheet(framegroup->styleSheet());
293 void CollapsibleGroup::dropEvent(QDropEvent *event)
295 framegroup->setProperty("target", false);
296 framegroup->setStyleSheet(framegroup->styleSheet());
297 const QString effects = QString::fromUtf8(event->mimeData()->data("kdenlive/effectslist"));
298 //event->acceptProposedAction();
300 doc.setContent(effects, true);
301 QDomElement e = doc.documentElement();
302 int ix = e.attribute("kdenlive_ix").toInt();
304 // effect dropped from effects list, add it
305 e.setAttribute("kdenlive_ix", ix);
306 event->setDropAction(Qt::CopyAction);
311 if (m_subWidgets.isEmpty()) return;
312 int new_index = m_subWidgets.last()->effectIndex();
313 emit moveEffect(ix, new_index, m_index, m_title->text());
314 event->setDropAction(Qt::MoveAction);
318 void CollapsibleGroup::slotRenameGroup()
320 m_title->setReadOnly(true);
321 if (m_title->text().isEmpty()) m_title->setText(i18n("Effect Group"));
322 for (int j = 0; j < m_subWidgets.count(); j++) {
323 m_subWidgets.at(j)->setGroupName(m_title->text());
325 emit groupRenamed(this);
328 QList <CollapsibleEffect*> CollapsibleGroup::effects()
330 QMutexLocker lock(&m_mutex);
334 QDomDocument CollapsibleGroup::effectsData()
336 QMutexLocker lock(&m_mutex);
338 QDomElement list = doc.createElement("effectgroup");
339 list.setAttribute("name", m_title->text());
340 doc.appendChild(list);
341 for (int j = 0; j < m_subWidgets.count(); j++) {
342 list.appendChild(doc.importNode(m_subWidgets.at(j)->effect(), true));