1 /***************************************************************************
2 * Copyright (C) 2007 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 ***************************************************************************/
20 #include "projectlist.h"
21 #include "projectitem.h"
22 #include "addfoldercommand.h"
23 #include "kdenlivesettings.h"
24 #include "slideshowclip.h"
25 #include "ui_colorclip_ui.h"
26 #include "titlewidget.h"
27 #include "definitions.h"
28 #include "clipmanager.h"
29 #include "docclipbase.h"
30 #include "kdenlivedoc.h"
33 #include "projectlistview.h"
34 #include "timecodedisplay.h"
35 #include "editclipcommand.h"
36 #include "editclipcutcommand.h"
37 #include "editfoldercommand.h"
38 #include "addclipcutcommand.h"
40 #include "ui_templateclip_ui.h"
45 #include <KFileDialog>
46 #include <KInputDialog>
47 #include <KMessageBox>
48 #include <KIO/NetAccess>
51 #include <nepomuk/global.h>
52 #include <nepomuk/resourcemanager.h>
53 //#include <nepomuk/tag.h>
56 #include <QMouseEvent>
57 #include <QStylePainter>
62 #include <QHeaderView>
64 ProjectList::ProjectList(QWidget *parent) :
73 m_transcodeAction(NULL),
80 m_listView = new ProjectListView(this);;
81 QVBoxLayout *layout = new QVBoxLayout;
82 layout->setContentsMargins(0, 0, 0, 0);
83 layout->setSpacing(0);
86 KTreeWidgetSearchLine *searchView = new KTreeWidgetSearchLine(this);
87 m_toolbar = new QToolBar("projectToolBar", this);
88 m_toolbar->addWidget(searchView);
89 int s = style()->pixelMetric(QStyle::PM_SmallIconSize);
90 m_toolbar->setIconSize(QSize(s, s));
91 searchView->setTreeWidget(m_listView);
93 m_addButton = new QToolButton(m_toolbar);
94 m_addButton->setPopupMode(QToolButton::MenuButtonPopup);
95 m_toolbar->addWidget(m_addButton);
97 layout->addWidget(m_toolbar);
98 layout->addWidget(m_listView);
101 m_queueTimer.setInterval(100);
102 connect(&m_queueTimer, SIGNAL(timeout()), this, SLOT(slotProcessNextClipInQueue()));
103 m_queueTimer.setSingleShot(true);
106 connect(m_listView, SIGNAL(projectModified()), this, SIGNAL(projectModified()));
107 connect(m_listView, SIGNAL(itemSelectionChanged()), this, SLOT(slotClipSelected()));
108 connect(m_listView, SIGNAL(focusMonitor()), this, SLOT(slotClipSelected()));
109 connect(m_listView, SIGNAL(pauseMonitor()), this, SLOT(slotPauseMonitor()));
110 connect(m_listView, SIGNAL(requestMenu(const QPoint &, QTreeWidgetItem *)), this, SLOT(slotContextMenu(const QPoint &, QTreeWidgetItem *)));
111 connect(m_listView, SIGNAL(addClip()), this, SLOT(slotAddClip()));
112 connect(m_listView, SIGNAL(addClip(const QList <QUrl>, const QString &, const QString &)), this, SLOT(slotAddClip(const QList <QUrl>, const QString &, const QString &)));
113 connect(m_listView, SIGNAL(addClipCut(const QString &, int, int)), this, SLOT(slotAddClipCut(const QString &, int, int)));
114 connect(m_listView, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(slotItemEdited(QTreeWidgetItem *, int)));
115 connect(m_listView, SIGNAL(showProperties(DocClipBase *)), this, SIGNAL(showClipProperties(DocClipBase *)));
117 m_listViewDelegate = new ItemDelegate(m_listView);
118 m_listView->setItemDelegate(m_listViewDelegate);
120 if (KdenliveSettings::activate_nepomuk()) {
121 Nepomuk::ResourceManager::instance()->init();
122 if (!Nepomuk::ResourceManager::instance()->initialized()) {
123 kDebug() << "Cannot communicate with Nepomuk, DISABLING it";
124 KdenliveSettings::setActivate_nepomuk(false);
130 ProjectList::~ProjectList()
134 m_listView->blockSignals(true);
136 delete m_listViewDelegate;
139 void ProjectList::focusTree() const
141 m_listView->setFocus();
144 void ProjectList::setupMenu(QMenu *addMenu, QAction *defaultAction)
146 QList <QAction *> actions = addMenu->actions();
147 for (int i = 0; i < actions.count(); i++) {
148 if (actions.at(i)->data().toString() == "clip_properties") {
149 m_editAction = actions.at(i);
150 m_toolbar->addAction(m_editAction);
153 } else if (actions.at(i)->data().toString() == "delete_clip") {
154 m_deleteAction = actions.at(i);
155 m_toolbar->addAction(m_deleteAction);
158 } else if (actions.at(i)->data().toString() == "edit_clip") {
159 m_openAction = actions.at(i);
162 } else if (actions.at(i)->data().toString() == "reload_clip") {
163 m_reloadAction = actions.at(i);
169 QMenu *m = new QMenu();
170 m->addActions(actions);
171 m_addButton->setMenu(m);
172 m_addButton->setDefaultAction(defaultAction);
173 m_menu = new QMenu();
174 m_menu->addActions(addMenu->actions());
177 void ProjectList::setupGeneratorMenu(QMenu *addMenu, QMenu *transcodeMenu, QMenu *inTimelineMenu)
181 QMenu *menu = m_addButton->menu();
182 menu->addMenu(addMenu);
183 m_addButton->setMenu(menu);
185 m_menu->addMenu(addMenu);
186 if (addMenu->isEmpty())
187 addMenu->setEnabled(false);
188 m_menu->addMenu(transcodeMenu);
189 if (transcodeMenu->isEmpty())
190 transcodeMenu->setEnabled(false);
191 m_transcodeAction = transcodeMenu;
192 m_menu->addAction(m_reloadAction);
193 m_menu->addMenu(inTimelineMenu);
194 inTimelineMenu->setEnabled(false);
195 m_menu->addAction(m_editAction);
196 m_menu->addAction(m_openAction);
197 m_menu->addAction(m_deleteAction);
198 m_menu->insertSeparator(m_deleteAction);
202 QByteArray ProjectList::headerInfo() const
204 return m_listView->header()->saveState();
207 void ProjectList::setHeaderInfo(const QByteArray &state)
209 m_listView->header()->restoreState(state);
212 void ProjectList::updateProjectFormat(Timecode t)
217 void ProjectList::slotEditClip()
219 QList<QTreeWidgetItem *> list = m_listView->selectedItems();
220 if (list.count() > 1) {
221 editClipSelection(list);
225 if (!m_listView->currentItem() || m_listView->currentItem()->type() == PROJECTFOLDERTYPE)
227 if (m_listView->currentItem()->type() == PROJECTSUBCLIPTYPE)
228 item = static_cast <ProjectItem*>(m_listView->currentItem()->parent());
230 item = static_cast <ProjectItem*>(m_listView->currentItem());
231 if (item && (item->flags() & Qt::ItemIsDragEnabled)) {
232 emit clipSelected(item->referencedClip());
233 emit showClipProperties(item->referencedClip());
237 void ProjectList::editClipSelection(QList<QTreeWidgetItem *> list)
239 // Gather all common properties
240 QMap <QString, QString> commonproperties;
241 QList <DocClipBase *> clipList;
242 commonproperties.insert("force_aspect_ratio", "-");
243 commonproperties.insert("force_fps", "-");
244 commonproperties.insert("force_progressive", "-");
245 commonproperties.insert("threads", "-");
246 commonproperties.insert("video_index", "-");
247 commonproperties.insert("audio_index", "-");
249 bool allowDurationChange = true;
250 int commonDuration = -1;
252 for (int i = 0; i < list.count(); i++) {
254 if (list.at(i)->type() == PROJECTFOLDERTYPE)
256 if (list.at(i)->type() == PROJECTSUBCLIPTYPE)
257 item = static_cast <ProjectItem*>(list.at(i)->parent());
259 item = static_cast <ProjectItem*>(list.at(i));
260 if (!(item->flags() & Qt::ItemIsDragEnabled))
264 DocClipBase *clip = item->referencedClip();
265 if (clipList.contains(clip)) continue;
266 if (clip->clipType() != COLOR && clip->clipType() != IMAGE && clip->clipType() != TEXT)
267 allowDurationChange = false;
268 if (allowDurationChange && commonDuration != 0) {
269 if (commonDuration == -1)
270 commonDuration = clip->duration().frames(m_fps);
271 else if (commonDuration != clip->duration().frames(m_fps))
274 clipList.append(clip);
275 QMap <QString, QString> clipprops = clip->properties();
276 QMapIterator<QString, QString> p(commonproperties);
277 while (p.hasNext()) {
279 if (p.value().isEmpty()) continue;
280 if (clipprops.contains(p.key())) {
281 if (p.value() == "-")
282 commonproperties.insert(p.key(), clipprops.value(p.key()));
283 else if (p.value() != clipprops.value(p.key()))
284 commonproperties.insert(p.key(), QString());
286 commonproperties.insert(p.key(), QString());
291 if (allowDurationChange)
292 commonproperties.insert("out", QString::number(commonDuration));
293 QMapIterator<QString, QString> p(commonproperties);
294 while (p.hasNext()) {
296 kDebug() << "Result: " << p.key() << " = " << p.value();
298 emit showClipProperties(clipList, commonproperties);
301 void ProjectList::slotOpenClip()
304 if (!m_listView->currentItem() || m_listView->currentItem()->type() == PROJECTFOLDERTYPE)
306 if (m_listView->currentItem()->type() == QTreeWidgetItem::UserType + 1)
307 item = static_cast <ProjectItem*>(m_listView->currentItem()->parent());
309 item = static_cast <ProjectItem*>(m_listView->currentItem());
311 if (item->clipType() == IMAGE) {
312 if (KdenliveSettings::defaultimageapp().isEmpty())
313 KMessageBox::sorry(this, i18n("Please set a default application to open images in the Settings dialog"));
315 QProcess::startDetached(KdenliveSettings::defaultimageapp(), QStringList() << item->clipUrl().path());
317 if (item->clipType() == AUDIO) {
318 if (KdenliveSettings::defaultaudioapp().isEmpty())
319 KMessageBox::sorry(this, i18n("Please set a default application to open audio files in the Settings dialog"));
321 QProcess::startDetached(KdenliveSettings::defaultaudioapp(), QStringList() << item->clipUrl().path());
326 void ProjectList::cleanup()
328 m_listView->clearSelection();
329 QTreeWidgetItemIterator it(m_listView);
332 if ((*it)->type() != PROJECTCLIPTYPE) {
336 item = static_cast <ProjectItem *>(*it);
337 if (item->numReferences() == 0)
338 item->setSelected(true);
344 void ProjectList::trashUnusedClips()
346 QTreeWidgetItemIterator it(m_listView);
351 if ((*it)->type() != PROJECTCLIPTYPE) {
355 item = static_cast <ProjectItem *>(*it);
356 if (item->numReferences() == 0) {
357 ids << item->clipId();
358 KUrl url = item->clipUrl();
359 if (!url.isEmpty() && !urls.contains(url.path()))
365 // Check that we don't use the URL in another clip
366 QTreeWidgetItemIterator it2(m_listView);
368 if ((*it2)->type() != PROJECTCLIPTYPE) {
372 item = static_cast <ProjectItem *>(*it2);
373 if (item->numReferences() > 0) {
374 KUrl url = item->clipUrl();
375 if (!url.isEmpty() && urls.contains(url.path())) urls.removeAll(url.path());
380 emit deleteProjectClips(ids, QMap <QString, QString>());
381 for (int i = 0; i < urls.count(); i++)
382 KIO::NetAccess::del(KUrl(urls.at(i)), this);
385 void ProjectList::slotReloadClip(const QString &id)
387 QList<QTreeWidgetItem *> selected;
389 selected = m_listView->selectedItems();
391 selected.append(getItemById(id));
393 for (int i = 0; i < selected.count(); i++) {
394 if (selected.at(i)->type() != PROJECTCLIPTYPE) {
395 if (selected.at(i)->type() == PROJECTFOLDERTYPE) {
396 for (int j = 0; j < selected.at(i)->childCount(); j++)
397 selected.append(selected.at(i)->child(j));
401 item = static_cast <ProjectItem *>(selected.at(i));
403 if (item->clipType() == TEXT) {
404 if (!item->referencedClip()->getProperty("xmltemplate").isEmpty())
405 regenerateTemplate(item);
406 } else if (item->clipType() != COLOR && item->clipType() != SLIDESHOW && item->referencedClip() && item->referencedClip()->checkHash() == false) {
407 item->referencedClip()->setPlaceHolder(true);
408 item->setProperty("file_hash", QString());
409 } else if (item->clipType() == IMAGE) {
410 item->referencedClip()->producer()->set("force_reload", 1);
412 //requestClipInfo(item->toXml(), item->clipId(), true);
413 // Clear the file_hash value, which will cause a complete reload of the clip
414 emit getFileProperties(item->toXml(), item->clipId(), m_listView->iconSize().height(), true);
419 void ProjectList::slotModifiedClip(const QString &id)
421 ProjectItem *item = getItemById(id);
423 QPixmap pixmap = qVariantValue<QPixmap>(item->data(0, Qt::DecorationRole));
424 if (!pixmap.isNull()) {
426 p.fillRect(0, 0, pixmap.width(), pixmap.height(), QColor(255, 255, 255, 200));
427 p.drawPixmap(0, 0, KIcon("view-refresh").pixmap(m_listView->iconSize()));
430 pixmap = KIcon("view-refresh").pixmap(m_listView->iconSize());
432 item->setData(0, Qt::DecorationRole, pixmap);
436 void ProjectList::slotMissingClip(const QString &id)
438 ProjectItem *item = getItemById(id);
440 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
441 if (item->referencedClip()) {
442 item->referencedClip()->setPlaceHolder(true);
443 if (m_render == NULL) kDebug() << "********* ERROR, NULL RENDR";
444 item->referencedClip()->setProducer(m_render->invalidProducer(id), true);
445 item->slotSetToolTip();
446 emit clipNeedsReload(id, true);
450 emit displayMessage(i18n("Check missing clips"), -2);
451 emit updateRenderStatus();
454 void ProjectList::slotAvailableClip(const QString &id)
456 ProjectItem *item = getItemById(id);
459 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable);
460 if (item->referencedClip()) { // && item->referencedClip()->checkHash() == false) {
461 item->setProperty("file_hash", QString());
465 item->referencedClip()->setValid();
466 item->slotSetToolTip();
469 emit updateRenderStatus();
472 bool ProjectList::hasMissingClips()
474 bool missing = false;
475 QTreeWidgetItemIterator it(m_listView);
477 if ((*it)->type() == PROJECTCLIPTYPE && !((*it)->flags() & Qt::ItemIsDragEnabled)) {
486 void ProjectList::setRenderer(Render *projectRender)
488 m_render = projectRender;
489 m_listView->setIconSize(QSize((ProjectItem::itemDefaultHeight() - 2) * m_render->dar(), ProjectItem::itemDefaultHeight() - 2));
492 void ProjectList::slotClipSelected()
494 if (!m_listView->isEnabled()) return;
495 if (m_listView->currentItem()) {
496 if (m_listView->currentItem()->type() == PROJECTFOLDERTYPE) {
497 emit clipSelected(NULL);
498 m_editAction->setEnabled(false);
499 m_deleteAction->setEnabled(true);
500 m_openAction->setEnabled(false);
501 m_reloadAction->setEnabled(false);
502 m_transcodeAction->setEnabled(false);
505 if (m_listView->currentItem()->type() == PROJECTSUBCLIPTYPE) {
506 // this is a sub item, use base clip
507 m_deleteAction->setEnabled(true);
508 clip = static_cast <ProjectItem*>(m_listView->currentItem()->parent());
509 if (clip == NULL) kDebug() << "-----------ERROR";
510 SubProjectItem *sub = static_cast <SubProjectItem*>(m_listView->currentItem());
511 emit clipSelected(clip->referencedClip(), sub->zone());
512 m_transcodeAction->setEnabled(false);
515 clip = static_cast <ProjectItem*>(m_listView->currentItem());
517 emit clipSelected(clip->referencedClip());
518 m_editAction->setEnabled(true);
519 m_deleteAction->setEnabled(true);
520 m_reloadAction->setEnabled(true);
521 m_transcodeAction->setEnabled(true);
522 if (clip && clip->clipType() == IMAGE && !KdenliveSettings::defaultimageapp().isEmpty()) {
523 m_openAction->setIcon(KIcon(KdenliveSettings::defaultimageapp()));
524 m_openAction->setEnabled(true);
525 } else if (clip && clip->clipType() == AUDIO && !KdenliveSettings::defaultaudioapp().isEmpty()) {
526 m_openAction->setIcon(KIcon(KdenliveSettings::defaultaudioapp()));
527 m_openAction->setEnabled(true);
529 m_openAction->setEnabled(false);
531 // Display relevant transcoding actions only
532 adjustTranscodeActions(clip);
533 // Display uses in timeline
534 emit findInTimeline(clip->clipId());
537 emit clipSelected(NULL);
538 m_editAction->setEnabled(false);
539 m_deleteAction->setEnabled(false);
540 m_openAction->setEnabled(false);
541 m_reloadAction->setEnabled(false);
542 m_transcodeAction->setEnabled(false);
546 void ProjectList::adjustTranscodeActions(ProjectItem *clip) const
548 if (clip == NULL || clip->type() != PROJECTCLIPTYPE || clip->clipType() == COLOR || clip->clipType() == TEXT || clip->clipType() == PLAYLIST || clip->clipType() == SLIDESHOW) {
549 m_transcodeAction->setEnabled(false);
552 m_transcodeAction->setEnabled(true);
553 QList<QAction *> transcodeActions = m_transcodeAction->actions();
556 for (int i = 0; i < transcodeActions.count(); i++) {
557 data = transcodeActions.at(i)->data().toStringList();
558 if (data.count() > 2) {
559 condition = data.at(2);
560 if (condition.startsWith("vcodec"))
561 transcodeActions.at(i)->setEnabled(clip->referencedClip()->hasVideoCodec(condition.section("=", 1, 1)));
562 else if (condition.startsWith("acodec"))
563 transcodeActions.at(i)->setEnabled(clip->referencedClip()->hasVideoCodec(condition.section("=", 1, 1)));
569 void ProjectList::slotPauseMonitor()
575 void ProjectList::slotUpdateClipProperties(const QString &id, QMap <QString, QString> properties)
577 ProjectItem *item = getItemById(id);
579 slotUpdateClipProperties(item, properties);
580 if (properties.contains("out") || properties.contains("force_fps")) {
582 } else if (properties.contains("colour") || properties.contains("resource") || properties.contains("xmldata") || properties.contains("force_aspect_ratio") || properties.contains("templatetext")) {
583 slotRefreshClipThumbnail(item);
589 void ProjectList::slotUpdateClipProperties(ProjectItem *clip, QMap <QString, QString> properties)
593 clip->setProperties(properties);
594 if (properties.contains("name")) {
595 m_listView->blockSignals(true);
596 clip->setText(0, properties.value("name"));
597 m_listView->blockSignals(false);
598 emit clipNameChanged(clip->clipId(), properties.value("name"));
600 if (properties.contains("description")) {
601 CLIPTYPE type = clip->clipType();
602 m_listView->blockSignals(true);
603 clip->setText(1, properties.value("description"));
604 m_listView->blockSignals(false);
606 if (KdenliveSettings::activate_nepomuk() && (type == AUDIO || type == VIDEO || type == AV || type == IMAGE || type == PLAYLIST)) {
607 // Use Nepomuk system to store clip description
608 Nepomuk::Resource f(clip->clipUrl().path());
609 f.setDescription(properties.value("description"));
612 emit projectModified();
616 void ProjectList::slotItemEdited(QTreeWidgetItem *item, int column)
618 if (item->type() == PROJECTSUBCLIPTYPE) {
619 // this is a sub-item
621 // user edited description
622 SubProjectItem *sub = static_cast <SubProjectItem*>(item);
623 ProjectItem *item = static_cast <ProjectItem *>(sub->parent());
624 EditClipCutCommand *command = new EditClipCutCommand(this, item->clipId(), sub->zone(), sub->zone(), sub->description(), sub->text(1), true);
625 m_commandStack->push(command);
626 //slotUpdateCutClipProperties(sub->clipId(), sub->zone(), sub->text(1), sub->text(1));
630 if (item->type() == PROJECTFOLDERTYPE) {
632 FolderProjectItem *folder = static_cast <FolderProjectItem*>(item);
633 editFolder(item->text(0), folder->groupName(), folder->clipId());
634 folder->setGroupName(item->text(0));
635 m_doc->clipManager()->addFolder(folder->clipId(), item->text(0));
636 const int children = item->childCount();
637 for (int i = 0; i < children; i++) {
638 ProjectItem *child = static_cast <ProjectItem *>(item->child(i));
639 child->setProperty("groupname", item->text(0));
645 ProjectItem *clip = static_cast <ProjectItem*>(item);
647 if (clip->referencedClip()) {
648 QMap <QString, QString> oldprops;
649 QMap <QString, QString> newprops;
650 oldprops["description"] = clip->referencedClip()->getProperty("description");
651 newprops["description"] = item->text(1);
653 if (clip->clipType() == TEXT) {
654 // This is a text template clip, update the image
655 /*oldprops.insert("xmldata", clip->referencedClip()->getProperty("xmldata"));
656 newprops.insert("xmldata", generateTemplateXml(clip->referencedClip()->getProperty("xmltemplate"), item->text(2)).toString());*/
657 oldprops.insert("templatetext", clip->referencedClip()->getProperty("templatetext"));
658 newprops.insert("templatetext", item->text(1));
660 slotUpdateClipProperties(clip->clipId(), newprops);
661 EditClipCommand *command = new EditClipCommand(this, clip->clipId(), oldprops, newprops, false);
662 m_commandStack->push(command);
664 } else if (column == 0) {
665 if (clip->referencedClip()) {
666 QMap <QString, QString> oldprops;
667 QMap <QString, QString> newprops;
668 oldprops["name"] = clip->referencedClip()->getProperty("name");
669 newprops["name"] = item->text(0);
670 slotUpdateClipProperties(clip, newprops);
671 emit projectModified();
672 EditClipCommand *command = new EditClipCommand(this, clip->clipId(), oldprops, newprops, false);
673 m_commandStack->push(command);
678 void ProjectList::slotContextMenu(const QPoint &pos, QTreeWidgetItem *item)
680 bool enable = item ? true : false;
681 m_editAction->setEnabled(enable);
682 m_deleteAction->setEnabled(enable);
683 m_reloadAction->setEnabled(enable);
684 m_transcodeAction->setEnabled(enable);
686 ProjectItem *clip = NULL;
687 if (m_listView->currentItem()->type() == PROJECTSUBCLIPTYPE) {
688 clip = static_cast <ProjectItem*>(item->parent());
689 m_transcodeAction->setEnabled(false);
690 } else if (m_listView->currentItem()->type() == PROJECTCLIPTYPE) {
691 clip = static_cast <ProjectItem*>(item);
692 // Display relevant transcoding actions only
693 adjustTranscodeActions(clip);
694 // Display uses in timeline
695 emit findInTimeline(clip->clipId());
697 m_transcodeAction->setEnabled(false);
699 if (clip && clip->clipType() == IMAGE && !KdenliveSettings::defaultimageapp().isEmpty()) {
700 m_openAction->setIcon(KIcon(KdenliveSettings::defaultimageapp()));
701 m_openAction->setEnabled(true);
702 } else if (clip && clip->clipType() == AUDIO && !KdenliveSettings::defaultaudioapp().isEmpty()) {
703 m_openAction->setIcon(KIcon(KdenliveSettings::defaultaudioapp()));
704 m_openAction->setEnabled(true);
706 m_openAction->setEnabled(false);
710 m_openAction->setEnabled(false);
715 void ProjectList::slotRemoveClip()
717 if (!m_listView->currentItem())
720 QMap <QString, QString> folderids;
721 QList<QTreeWidgetItem *> selected = m_listView->selectedItems();
723 QUndoCommand *delCommand = new QUndoCommand();
724 delCommand->setText(i18n("Delete Clip Zone"));
726 for (int i = 0; i < selected.count(); i++) {
727 if (selected.at(i)->type() == PROJECTSUBCLIPTYPE) {
729 SubProjectItem *sub = static_cast <SubProjectItem *>(selected.at(i));
730 ProjectItem *item = static_cast <ProjectItem *>(sub->parent());
731 new AddClipCutCommand(this, item->clipId(), sub->zone().x(), sub->zone().y(), sub->description(), false, true, delCommand);
732 } else if (selected.at(i)->type() == PROJECTFOLDERTYPE) {
734 FolderProjectItem *folder = static_cast <FolderProjectItem *>(selected.at(i));
735 folderids[folder->groupName()] = folder->clipId();
736 int children = folder->childCount();
738 if (children > 0 && KMessageBox::questionYesNo(this, i18np("Delete folder <b>%2</b>?<br />This will also remove the clip in that folder", "Delete folder <b>%2</b>?<br />This will also remove the %1 clips in that folder", children, folder->text(1)), i18n("Delete Folder")) != KMessageBox::Yes)
740 for (int i = 0; i < children; ++i) {
741 ProjectItem *child = static_cast <ProjectItem *>(folder->child(i));
742 ids << child->clipId();
745 ProjectItem *item = static_cast <ProjectItem *>(selected.at(i));
746 ids << item->clipId();
747 if (item->numReferences() > 0 &&KMessageBox::questionYesNo(this, i18np("Delete clip <b>%2</b>?<br />This will also remove the clip in timeline", "Delete clip <b>%2</b>?<br />This will also remove its %1 clips in timeline", item->numReferences(), item->names().at(1)), i18n("Delete Clip")) != KMessageBox::Yes)
752 if (delCommand->childCount() == 0)
755 m_commandStack->push(delCommand);
756 emit deleteProjectClips(ids, folderids);
759 void ProjectList::updateButtons() const
761 if (m_listView->topLevelItemCount() == 0) {
762 m_deleteAction->setEnabled(false);
764 m_deleteAction->setEnabled(true);
765 if (!m_listView->currentItem())
766 m_listView->setCurrentItem(m_listView->topLevelItem(0));
767 QTreeWidgetItem *item = m_listView->currentItem();
768 if (item && item->type() == PROJECTCLIPTYPE) {
769 m_editAction->setEnabled(true);
770 m_openAction->setEnabled(true);
771 m_reloadAction->setEnabled(true);
772 m_transcodeAction->setEnabled(true);
777 m_editAction->setEnabled(false);
778 m_openAction->setEnabled(false);
779 m_reloadAction->setEnabled(false);
780 m_transcodeAction->setEnabled(false);
783 void ProjectList::selectItemById(const QString &clipId)
785 ProjectItem *item = getItemById(clipId);
787 m_listView->setCurrentItem(item);
791 void ProjectList::slotDeleteClip(const QString &clipId)
793 ProjectItem *item = getItemById(clipId);
795 kDebug() << "/// Cannot find clip to delete";
798 m_listView->blockSignals(true);
799 QTreeWidgetItem *newSelectedItem = m_listView->itemAbove(item);
800 if (!newSelectedItem)
801 newSelectedItem = m_listView->itemBelow(item);
803 m_doc->clipManager()->deleteClip(clipId);
804 m_listView->blockSignals(false);
805 if (newSelectedItem) {
806 m_listView->setCurrentItem(newSelectedItem);
809 emit clipSelected(NULL);
814 void ProjectList::editFolder(const QString folderName, const QString oldfolderName, const QString &clipId)
816 EditFolderCommand *command = new EditFolderCommand(this, folderName, oldfolderName, clipId, false);
817 m_commandStack->push(command);
818 m_doc->setModified(true);
821 void ProjectList::slotAddFolder()
823 AddFolderCommand *command = new AddFolderCommand(this, i18n("Folder"), QString::number(m_doc->clipManager()->getFreeFolderId()), true);
824 m_commandStack->push(command);
827 void ProjectList::slotAddFolder(const QString foldername, const QString &clipId, bool remove, bool edit)
830 FolderProjectItem *item = getFolderItemById(clipId);
832 m_doc->clipManager()->deleteFolder(clipId);
833 QTreeWidgetItem *newSelectedItem = m_listView->itemAbove(item);
834 if (!newSelectedItem)
835 newSelectedItem = m_listView->itemBelow(item);
838 m_listView->setCurrentItem(newSelectedItem);
844 FolderProjectItem *item = getFolderItemById(clipId);
846 m_listView->blockSignals(true);
847 item->setGroupName(foldername);
848 m_listView->blockSignals(false);
849 m_doc->clipManager()->addFolder(clipId, foldername);
850 const int children = item->childCount();
851 for (int i = 0; i < children; i++) {
852 ProjectItem *child = static_cast <ProjectItem *>(item->child(i));
853 child->setProperty("groupname", foldername);
857 m_listView->blockSignals(true);
858 m_listView->setCurrentItem(new FolderProjectItem(m_listView, QStringList() << foldername, clipId));
859 m_doc->clipManager()->addFolder(clipId, foldername);
860 m_listView->blockSignals(false);
861 m_listView->editItem(m_listView->currentItem(), 0);
865 m_doc->setModified(true);
870 void ProjectList::deleteProjectFolder(QMap <QString, QString> map)
872 QMapIterator<QString, QString> i(map);
873 QUndoCommand *delCommand = new QUndoCommand();
874 delCommand->setText(i18n("Delete Folder"));
875 while (i.hasNext()) {
877 new AddFolderCommand(this, i.key(), i.value(), false, delCommand);
879 m_commandStack->push(delCommand);
882 void ProjectList::slotAddClip(DocClipBase *clip, bool getProperties)
884 m_listView->setEnabled(false);
886 m_listView->blockSignals(true);
888 // remove file_hash so that we load all properties for the clip
889 QDomElement e = clip->toXML().cloneNode().toElement();
890 e.removeAttribute("file_hash");
891 m_infoQueue.insert(clip->getId(), e);
892 //m_render->getFileProperties(clip->toXML(), clip->getId(), true);
894 clip->askForAudioThumbs();
895 const QString parent = clip->getProperty("groupid");
896 ProjectItem *item = NULL;
897 if (!parent.isEmpty()) {
898 FolderProjectItem *parentitem = getFolderItemById(parent);
901 QString groupName = clip->getProperty("groupname");
902 //kDebug() << "Adding clip to new group: " << groupName;
903 if (groupName.isEmpty()) groupName = i18n("Folder");
905 parentitem = new FolderProjectItem(m_listView, text, parent);
909 item = new ProjectItem(parentitem, clip);
912 item = new ProjectItem(m_listView, clip);
913 KUrl url = clip->fileURL();
915 if (getProperties == false && !clip->getClipHash().isEmpty()) {
916 QString cachedPixmap = m_doc->projectFolder().path(KUrl::AddTrailingSlash) + "thumbs/" + clip->getClipHash() + ".png";
917 if (QFile::exists(cachedPixmap)) {
918 QPixmap pix(cachedPixmap);
920 KIO::NetAccess::del(KUrl(cachedPixmap), this);
921 item->setData(0, Qt::DecorationRole, pix);
925 if (!url.isEmpty() && KdenliveSettings::activate_nepomuk()) {
926 // if file has Nepomuk comment, use it
927 Nepomuk::Resource f(url.path());
928 QString annotation = f.description();
929 if (!annotation.isEmpty()) item->setText(1, annotation);
930 item->setText(2, QString::number(f.rating()));
934 QList <CutZoneInfo> cuts = clip->cutZones();
935 if (!cuts.isEmpty()) {
936 for (int i = 0; i < cuts.count(); i++) {
937 SubProjectItem *sub = new SubProjectItem(item, cuts.at(i).zone.x(), cuts.at(i).zone.y(), cuts.at(i).description);
938 if (!clip->getClipHash().isEmpty()) {
939 QString cachedPixmap = m_doc->projectFolder().path(KUrl::AddTrailingSlash) + "thumbs/" + clip->getClipHash() + '#' + QString::number(cuts.at(i).zone.x()) + ".png";
940 if (QFile::exists(cachedPixmap)) {
941 QPixmap pix(cachedPixmap);
943 KIO::NetAccess::del(KUrl(cachedPixmap), this);
944 sub->setData(0, Qt::DecorationRole, pix);
949 if (m_listView->isEnabled()) {
952 m_listView->blockSignals(false);
954 if (getProperties && !m_queueTimer.isActive())
955 m_queueTimer.start();
958 void ProjectList::slotResetProjectList()
961 emit clipSelected(NULL);
962 m_thumbnailQueue.clear();
967 void ProjectList::requestClipInfo(const QDomElement xml, const QString id)
970 m_infoQueue.insert(id, xml);
971 //if (m_infoQueue.count() == 1 || ) QTimer::singleShot(300, this, SLOT(slotProcessNextClipInQueue()));
974 void ProjectList::slotProcessNextClipInQueue()
976 if (m_infoQueue.isEmpty()) {
977 slotProcessNextThumbnail();
981 QMap<QString, QDomElement>::const_iterator j = m_infoQueue.constBegin();
982 if (j != m_infoQueue.constEnd()) {
983 const QDomElement dom = j.value();
984 const QString id = j.key();
985 m_infoQueue.remove(j.key());
986 emit getFileProperties(dom, id, m_listView->iconSize().height(), false);
990 void ProjectList::slotUpdateClip(const QString &id)
992 ProjectItem *item = getItemById(id);
993 m_listView->blockSignals(true);
994 if (item) item->setData(0, UsageRole, QString::number(item->numReferences()));
995 m_listView->blockSignals(false);
998 void ProjectList::updateAllClips()
1000 m_listView->setSortingEnabled(false);
1001 kDebug() << "// UPDATE ALL CLPY";
1003 QTreeWidgetItemIterator it(m_listView);
1006 m_listView->blockSignals(true);
1008 if ((*it)->type() == PROJECTSUBCLIPTYPE) {
1010 SubProjectItem *sub = static_cast <SubProjectItem *>(*it);
1011 if (sub->data(0, Qt::DecorationRole).isNull()) {
1012 item = static_cast <ProjectItem *>((*it)->parent());
1013 requestClipThumbnail(item->clipId() + '#' + QString::number(sub->zone().x()));
1017 } else if ((*it)->type() == PROJECTFOLDERTYPE) {
1022 item = static_cast <ProjectItem *>(*it);
1023 clip = item->referencedClip();
1024 if (item->referencedClip()->producer() == NULL) {
1025 if (clip->isPlaceHolder() == false)
1026 requestClipInfo(clip->toXML(), clip->getId());
1027 else if (!clip->isPlaceHolder())
1028 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
1030 if (item->data(0, Qt::DecorationRole).isNull())
1031 requestClipThumbnail(clip->getId());
1032 if (item->data(0, DurationRole).toString().isEmpty())
1033 item->changeDuration(item->referencedClip()->producer()->get_playtime());
1035 item->setData(0, UsageRole, QString::number(item->numReferences()));
1037 //qApp->processEvents();
1040 if (!m_queueTimer.isActive())
1041 m_queueTimer.start();
1042 if (m_listView->isEnabled())
1043 m_listView->blockSignals(false);
1044 m_listView->setSortingEnabled(true);
1045 if (m_infoQueue.isEmpty())
1046 slotProcessNextThumbnail();
1050 QString ProjectList::getExtensions()
1052 // Build list of mime types
1053 QStringList mimeTypes = QStringList() << "application/x-kdenlive" << "application/x-kdenlivetitle" << "video/mlt-playlist" << "text/plain"
1054 << "video/x-flv" << "application/vnd.rn-realmedia" << "video/x-dv" << "video/dv" << "video/x-msvideo" << "video/x-matroska" << "video/mpeg" << "video/ogg" << "video/x-ms-wmv" << "video/mp4" << "video/quicktime"
1055 << "audio/x-flac" << "audio/x-matroska" << "audio/mp4" << "audio/mpeg" << "audio/x-mp3" << "audio/ogg" << "audio/x-wav" << "application/ogg"
1056 << "image/gif" << "image/jpeg" << "image/png" << "image/x-tga" << "image/x-bmp" << "image/svg+xml" << "image/tiff" << "image/x-xcf" << "image/x-xcf-gimp" << "image/x-vnd.adobe.photoshop" << "image/x-pcx" << "image/x-exr";
1058 QString allExtensions;
1059 foreach(const QString& mimeType, mimeTypes) {
1060 KMimeType::Ptr mime(KMimeType::mimeType(mimeType));
1062 allExtensions.append(mime->patterns().join(" "));
1063 allExtensions.append(' ');
1066 return allExtensions.simplified();
1069 void ProjectList::slotAddClip(const QList <QUrl> givenList, const QString &groupName, const QString &groupId)
1071 if (!m_commandStack)
1072 kDebug() << "!!!!!!!!!!!!!!!! NO CMD STK";
1075 if (givenList.isEmpty()) {
1076 QString allExtensions = getExtensions();
1077 const QString dialogFilter = allExtensions + ' ' + QLatin1Char('|') + i18n("All Supported Files") + "\n* " + QLatin1Char('|') + i18n("All Files");
1078 list = KFileDialog::getOpenUrls(KUrl("kfiledialog:///clipfolder"), dialogFilter, this);
1081 for (int i = 0; i < givenList.count(); i++)
1082 list << givenList.at(i);
1085 foreach(const KUrl &file, list) {
1086 // Check there is no folder here
1087 KMimeType::Ptr type = KMimeType::findByUrl(file);
1088 if (type->is("inode/directory")) {
1089 // user dropped a folder
1090 list.removeAll(file);
1097 if (givenList.isEmpty()) {
1098 QStringList groupInfo = getGroup();
1099 m_doc->slotAddClipList(list, groupInfo.at(0), groupInfo.at(1));
1101 m_doc->slotAddClipList(list, groupName, groupId);
1105 void ProjectList::slotRemoveInvalidClip(const QString &id, bool replace)
1107 ProjectItem *item = getItemById(id);
1108 QTimer::singleShot(300, this, SLOT(slotProcessNextClipInQueue()));
1110 const QString path = item->referencedClip()->fileURL().path();
1111 if (item->referencedClip()->isPlaceHolder()) replace = false;
1112 if (!path.isEmpty()) {
1114 KMessageBox::sorry(this, i18n("Clip <b>%1</b><br />is invalid, will be removed from project.", path));
1115 else if (KMessageBox::questionYesNo(this, i18n("Clip <b>%1</b><br />is missing or invalid. Remove it from project?", path), i18n("Invalid clip")) == KMessageBox::Yes)
1119 emit deleteProjectClips(QStringList() << id, QMap <QString, QString>());
1123 void ProjectList::slotAddColorClip()
1125 if (!m_commandStack)
1126 kDebug() << "!!!!!!!!!!!!!!!! NO CMD STK";
1128 QDialog *dia = new QDialog(this);
1129 Ui::ColorClip_UI dia_ui;
1130 dia_ui.setupUi(dia);
1131 dia->setWindowTitle(i18n("Color Clip"));
1132 dia_ui.clip_name->setText(i18n("Color Clip"));
1134 TimecodeDisplay *t = new TimecodeDisplay(m_timecode);
1135 t->setValue(KdenliveSettings::color_duration());
1136 t->setTimeCodeFormat(false);
1137 dia_ui.clip_durationBox->addWidget(t);
1138 dia_ui.clip_color->setColor(KdenliveSettings::colorclipcolor());
1140 if (dia->exec() == QDialog::Accepted) {
1141 QString color = dia_ui.clip_color->color().name();
1142 KdenliveSettings::setColorclipcolor(color);
1143 color = color.replace(0, 1, "0x") + "ff";
1144 QStringList groupInfo = getGroup();
1145 m_doc->slotCreateColorClip(dia_ui.clip_name->text(), color, m_timecode.getTimecode(t->gentime()), groupInfo.at(0), groupInfo.at(1));
1152 void ProjectList::slotAddSlideshowClip()
1154 if (!m_commandStack)
1155 kDebug() << "!!!!!!!!!!!!!!!! NO CMD STK";
1157 SlideshowClip *dia = new SlideshowClip(m_timecode, this);
1159 if (dia->exec() == QDialog::Accepted) {
1160 QStringList groupInfo = getGroup();
1161 m_doc->slotCreateSlideshowClipFile(dia->clipName(), dia->selectedPath(), dia->imageCount(), dia->clipDuration(), dia->loop(), dia->fade(),
1162 dia->lumaDuration(), dia->lumaFile(), dia->softness(), groupInfo.at(0), groupInfo.at(1));
1167 void ProjectList::slotAddTitleClip()
1169 QStringList groupInfo = getGroup();
1170 m_doc->slotCreateTextClip(groupInfo.at(0), groupInfo.at(1));
1173 void ProjectList::slotAddTitleTemplateClip()
1175 if (!m_commandStack)
1176 kDebug() << "!!!!!!!!!!!!!!!! NO CMD STK";
1178 QStringList groupInfo = getGroup();
1180 // Get the list of existing templates
1182 filter << "*.kdenlivetitle";
1183 const QString path = m_doc->projectFolder().path(KUrl::AddTrailingSlash) + "titles/";
1184 QStringList templateFiles = QDir(path).entryList(filter, QDir::Files);
1186 QDialog *dia = new QDialog(this);
1187 Ui::TemplateClip_UI dia_ui;
1188 dia_ui.setupUi(dia);
1189 for (int i = 0; i < templateFiles.size(); ++i)
1190 dia_ui.template_list->comboBox()->addItem(templateFiles.at(i), path + templateFiles.at(i));
1192 if (!templateFiles.isEmpty())
1193 dia_ui.buttonBox->button(QDialogButtonBox::Ok)->setFocus();
1194 dia_ui.template_list->fileDialog()->setFilter("*.kdenlivetitle");
1195 //warning: setting base directory doesn't work??
1196 KUrl startDir(path);
1197 dia_ui.template_list->fileDialog()->setUrl(startDir);
1198 dia_ui.text_box->setHidden(true);
1199 if (dia->exec() == QDialog::Accepted) {
1200 QString textTemplate = dia_ui.template_list->comboBox()->itemData(dia_ui.template_list->comboBox()->currentIndex()).toString();
1201 if (textTemplate.isEmpty()) textTemplate = dia_ui.template_list->comboBox()->currentText();
1202 // Create a cloned template clip
1203 m_doc->slotCreateTextTemplateClip(groupInfo.at(0), groupInfo.at(1), KUrl(textTemplate));
1208 QStringList ProjectList::getGroup() const
1211 QTreeWidgetItem *item = m_listView->currentItem();
1212 while (item && item->type() != PROJECTFOLDERTYPE)
1213 item = item->parent();
1216 FolderProjectItem *folder = static_cast <FolderProjectItem *>(item);
1217 result << folder->groupName() << folder->clipId();
1219 result << QString() << QString();
1224 void ProjectList::setDocument(KdenliveDoc *doc)
1226 m_listView->blockSignals(true);
1227 m_listView->clear();
1228 m_listView->setSortingEnabled(false);
1229 emit clipSelected(NULL);
1230 m_thumbnailQueue.clear();
1231 m_infoQueue.clear();
1232 m_refreshed = false;
1234 m_timecode = doc->timecode();
1235 m_commandStack = doc->commandStack();
1238 QMap <QString, QString> flist = doc->clipManager()->documentFolderList();
1239 QMapIterator<QString, QString> f(flist);
1240 while (f.hasNext()) {
1242 (void) new FolderProjectItem(m_listView, QStringList() << f.value(), f.key());
1245 QList <DocClipBase*> list = doc->clipManager()->documentClipList();
1246 for (int i = 0; i < list.count(); i++)
1247 slotAddClip(list.at(i), false);
1249 m_listView->blockSignals(false);
1250 m_toolbar->setEnabled(true);
1251 connect(m_doc->clipManager(), SIGNAL(reloadClip(const QString &)), this, SLOT(slotReloadClip(const QString &)));
1252 connect(m_doc->clipManager(), SIGNAL(modifiedClip(const QString &)), this, SLOT(slotModifiedClip(const QString &)));
1253 connect(m_doc->clipManager(), SIGNAL(missingClip(const QString &)), this, SLOT(slotMissingClip(const QString &)));
1254 connect(m_doc->clipManager(), SIGNAL(availableClip(const QString &)), this, SLOT(slotAvailableClip(const QString &)));
1255 connect(m_doc->clipManager(), SIGNAL(checkAllClips()), this, SLOT(updateAllClips()));
1258 QList <DocClipBase*> ProjectList::documentClipList() const
1261 return QList <DocClipBase*> ();
1263 return m_doc->clipManager()->documentClipList();
1266 QDomElement ProjectList::producersList()
1269 QDomElement prods = doc.createElement("producerlist");
1270 doc.appendChild(prods);
1271 kDebug() << "//////////// PRO LISTÂ BUILD PRDSLIST ";
1272 QTreeWidgetItemIterator it(m_listView);
1274 if ((*it)->type() != PROJECTCLIPTYPE) {
1279 prods.appendChild(doc.importNode(((ProjectItem *)(*it))->toXml(), true));
1285 void ProjectList::slotCheckForEmptyQueue()
1287 if (!m_refreshed && m_thumbnailQueue.isEmpty() && m_infoQueue.isEmpty()) {
1289 emit loadingIsOver();
1290 emit displayMessage(QString(), -1);
1291 m_listView->blockSignals(false);
1292 m_listView->setEnabled(true);
1294 } else if (!m_refreshed) {
1295 QTimer::singleShot(300, this, SLOT(slotCheckForEmptyQueue()));
1299 void ProjectList::reloadClipThumbnails()
1301 kDebug() << "////////////// RELOAD CLIPS THUMBNAILS!!!";
1302 m_thumbnailQueue.clear();
1303 QTreeWidgetItemIterator it(m_listView);
1305 if ((*it)->type() != PROJECTCLIPTYPE) {
1310 m_thumbnailQueue << ((ProjectItem *)(*it))->clipId();
1313 QTimer::singleShot(300, this, SLOT(slotProcessNextThumbnail()));
1316 void ProjectList::requestClipThumbnail(const QString id)
1318 if (!m_thumbnailQueue.contains(id)) m_thumbnailQueue.append(id);
1321 void ProjectList::slotProcessNextThumbnail()
1323 if (m_thumbnailQueue.isEmpty() && m_infoQueue.isEmpty()) {
1324 slotCheckForEmptyQueue();
1327 if (!m_infoQueue.isEmpty()) {
1328 //QTimer::singleShot(300, this, SLOT(slotProcessNextThumbnail()));
1331 if (m_thumbnailQueue.count() > 1) {
1332 int max = m_doc->clipManager()->clipsCount();
1333 emit displayMessage(i18n("Loading thumbnails"), (int)(100 *(max - m_thumbnailQueue.count()) / max));
1335 slotRefreshClipThumbnail(m_thumbnailQueue.takeFirst(), false);
1338 void ProjectList::slotRefreshClipThumbnail(const QString &clipId, bool update)
1340 QTreeWidgetItem *item = getAnyItemById(clipId);
1342 slotRefreshClipThumbnail(item, update);
1344 slotProcessNextThumbnail();
1347 void ProjectList::slotRefreshClipThumbnail(QTreeWidgetItem *it, bool update)
1349 if (it == NULL) return;
1350 ProjectItem *item = NULL;
1351 bool isSubItem = false;
1353 if (it->type() == PROJECTFOLDERTYPE) return;
1354 if (it->type() == PROJECTSUBCLIPTYPE) {
1355 item = static_cast <ProjectItem *>(it->parent());
1356 frame = static_cast <SubProjectItem *>(it)->zone().x();
1359 item = static_cast <ProjectItem *>(it);
1360 frame = item->referencedClip()->getClipThumbFrame();
1364 DocClipBase *clip = item->referencedClip();
1366 slotProcessNextThumbnail();
1370 int height = m_listView->iconSize().height();
1371 int width = (int)(height * m_render->dar());
1372 if (clip->clipType() == AUDIO)
1373 pix = KIcon("audio-x-generic").pixmap(QSize(width, height));
1374 else if (clip->clipType() == IMAGE)
1375 pix = QPixmap::fromImage(KThumb::getFrame(item->referencedClip()->producer(), 0, width, height));
1377 pix = item->referencedClip()->thumbProducer()->extractImage(frame, width, height);
1379 if (!pix.isNull()) {
1380 m_listView->blockSignals(true);
1381 it->setData(0, Qt::DecorationRole, pix);
1382 if (m_listView->isEnabled())
1383 m_listView->blockSignals(false);
1385 m_doc->cachePixmap(item->getClipHash(), pix);
1387 m_doc->cachePixmap(item->getClipHash() + '#' + QString::number(frame), pix);
1390 emit projectModified();
1391 QTimer::singleShot(30, this, SLOT(slotProcessNextThumbnail()));
1395 void ProjectList::slotReplyGetFileProperties(const QString &clipId, Mlt::Producer *producer, const QMap < QString, QString > &properties, const QMap < QString, QString > &metadata, bool replace)
1398 ProjectItem *item = getItemById(clipId);
1399 if (item && producer) {
1400 m_listView->blockSignals(true);
1401 item->setProperties(properties, metadata);
1402 if (item->referencedClip()->isPlaceHolder() && producer->is_valid()) {
1403 item->referencedClip()->setValid();
1404 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable);
1407 //Q_ASSERT_X(item->referencedClip(), "void ProjectList::slotReplyGetFileProperties", QString("Item with groupName %1 does not have a clip associated").arg(item->groupName()).toLatin1());
1408 item->referencedClip()->setProducer(producer, replace);
1409 item->referencedClip()->askForAudioThumbs();
1410 if (!replace && item->data(0, Qt::DecorationRole).isNull())
1411 requestClipThumbnail(clipId);
1412 if (!toReload.isEmpty())
1413 item->slotSetToolTip();
1415 //emit receivedClipDuration(clipId);
1416 if (m_listView->isEnabled() && replace) {
1417 // update clip in clip monitor
1418 emit clipSelected(NULL);
1419 emit clipSelected(item->referencedClip());
1420 //TODO: Make sure the line below has no side effect
1424 // Check if duration changed.
1425 emit receivedClipDuration(clipId);
1428 if (m_listView->isEnabled())
1429 m_listView->blockSignals(false);
1430 /*if (item->icon(0).isNull()) {
1431 requestClipThumbnail(clipId);
1433 } else kDebug() << "//////// COULD NOT FIND CLIP TO UPDATE PRPS...";
1434 int max = m_doc->clipManager()->clipsCount();
1435 if (item && m_infoQueue.isEmpty() && m_thumbnailQueue.isEmpty()) {
1436 m_listView->setCurrentItem(item);
1437 emit clipSelected(item->referencedClip());
1439 emit displayMessage(i18n("Loading clips"), (int)(100 *(max - m_infoQueue.count()) / max));
1441 if (!toReload.isEmpty())
1442 emit clipNeedsReload(toReload, true);
1443 // small delay so that the app can display the progress info
1444 QTimer::singleShot(30, this, SLOT(slotProcessNextClipInQueue()));
1447 void ProjectList::slotReplyGetImage(const QString &clipId, const QPixmap &pix)
1449 ProjectItem *item = getItemById(clipId);
1450 if (item && !pix.isNull()) {
1451 m_listView->blockSignals(true);
1452 item->setData(0, Qt::DecorationRole, pix);
1453 m_doc->cachePixmap(item->getClipHash(), pix);
1454 if (m_listView->isEnabled())
1455 m_listView->blockSignals(false);
1459 QTreeWidgetItem *ProjectList::getAnyItemById(const QString &id)
1461 QTreeWidgetItemIterator it(m_listView);
1462 QString lookId = id;
1463 if (id.contains('#'))
1464 lookId = id.section('#', 0, 0);
1466 ProjectItem *result = NULL;
1468 if ((*it)->type() != PROJECTCLIPTYPE) {
1473 ProjectItem *item = static_cast<ProjectItem *>(*it);
1474 if (item->clipId() == lookId) {
1480 if (result == NULL || !id.contains('#')) {
1483 for (int i = 0; i < result->childCount(); i++) {
1484 SubProjectItem *sub = static_cast <SubProjectItem *>(result->child(i));
1485 if (sub && sub->zone().x() == id.section('#', 1, 1).toInt())
1494 ProjectItem *ProjectList::getItemById(const QString &id)
1497 QTreeWidgetItemIterator it(m_listView);
1499 if ((*it)->type() != PROJECTCLIPTYPE) {
1504 item = static_cast<ProjectItem *>(*it);
1505 if (item->clipId() == id)
1512 FolderProjectItem *ProjectList::getFolderItemById(const QString &id)
1514 FolderProjectItem *item;
1515 QTreeWidgetItemIterator it(m_listView);
1517 if ((*it)->type() == PROJECTFOLDERTYPE) {
1518 item = static_cast<FolderProjectItem *>(*it);
1519 if (item->clipId() == id)
1527 void ProjectList::slotSelectClip(const QString &ix)
1529 ProjectItem *clip = getItemById(ix);
1531 m_listView->setCurrentItem(clip);
1532 m_listView->scrollToItem(clip);
1533 m_editAction->setEnabled(true);
1534 m_deleteAction->setEnabled(true);
1535 m_reloadAction->setEnabled(true);
1536 m_transcodeAction->setEnabled(true);
1537 if (clip->clipType() == IMAGE && !KdenliveSettings::defaultimageapp().isEmpty()) {
1538 m_openAction->setIcon(KIcon(KdenliveSettings::defaultimageapp()));
1539 m_openAction->setEnabled(true);
1540 } else if (clip->clipType() == AUDIO && !KdenliveSettings::defaultaudioapp().isEmpty()) {
1541 m_openAction->setIcon(KIcon(KdenliveSettings::defaultaudioapp()));
1542 m_openAction->setEnabled(true);
1544 m_openAction->setEnabled(false);
1549 QString ProjectList::currentClipUrl() const
1552 if (!m_listView->currentItem() || m_listView->currentItem()->type() == PROJECTFOLDERTYPE) return QString();
1553 if (m_listView->currentItem()->type() == PROJECTSUBCLIPTYPE) {
1555 item = static_cast <ProjectItem*>(m_listView->currentItem()->parent());
1557 item = static_cast <ProjectItem*>(m_listView->currentItem());
1561 return item->clipUrl().path();
1564 KUrl::List ProjectList::getConditionalUrls(const QString &condition) const
1568 QList<QTreeWidgetItem *> list = m_listView->selectedItems();
1569 for (int i = 0; i < list.count(); i++) {
1570 if (list.at(i)->type() == PROJECTFOLDERTYPE)
1572 if (list.at(i)->type() == PROJECTSUBCLIPTYPE) {
1574 item = static_cast <ProjectItem*>(list.at(i)->parent());
1576 item = static_cast <ProjectItem*>(list.at(i));
1578 if (item == NULL || item->type() == COLOR || item->type() == SLIDESHOW || item->type() == TEXT)
1580 DocClipBase *clip = item->referencedClip();
1581 if (!condition.isEmpty()) {
1582 if (condition.startsWith("vcodec") && !clip->hasVideoCodec(condition.section("=", 1, 1)))
1584 else if (condition.startsWith("acodec") && !clip->hasAudioCodec(condition.section("=", 1, 1)))
1587 result.append(item->clipUrl());
1592 void ProjectList::regenerateTemplate(const QString &id)
1594 ProjectItem *clip = getItemById(id);
1596 regenerateTemplate(clip);
1599 void ProjectList::regenerateTemplate(ProjectItem *clip)
1601 //TODO: remove this unused method, only force_reload is necessary
1602 clip->referencedClip()->producer()->set("force_reload", 1);
1605 QDomDocument ProjectList::generateTemplateXml(QString path, const QString &replaceString)
1609 if (!file.open(QIODevice::ReadOnly)) {
1610 kWarning() << "ERROR, CANNOT READ: " << path;
1613 if (!doc.setContent(&file)) {
1614 kWarning() << "ERROR, CANNOT READ: " << path;
1619 QDomNodeList texts = doc.elementsByTagName("content");
1620 for (int i = 0; i < texts.count(); i++) {
1621 QString data = texts.item(i).firstChild().nodeValue();
1622 data.replace("%s", replaceString);
1623 texts.item(i).firstChild().setNodeValue(data);
1629 void ProjectList::slotAddClipCut(const QString &id, int in, int out)
1631 ProjectItem *clip = getItemById(id);
1632 if (clip == NULL || clip->referencedClip()->hasCutZone(QPoint(in, out)))
1634 AddClipCutCommand *command = new AddClipCutCommand(this, id, in, out, QString(), true, false);
1635 m_commandStack->push(command);
1638 void ProjectList::addClipCut(const QString &id, int in, int out, const QString desc, bool newItem)
1640 ProjectItem *clip = getItemById(id);
1642 DocClipBase *base = clip->referencedClip();
1643 base->addCutZone(in, out);
1644 m_listView->blockSignals(true);
1645 SubProjectItem *sub = new SubProjectItem(clip, in, out, desc);
1646 if (newItem && desc.isEmpty() && !m_listView->isColumnHidden(1)) {
1647 if (!clip->isExpanded())
1648 clip->setExpanded(true);
1649 m_listView->scrollToItem(sub);
1650 m_listView->editItem(sub, 1);
1652 QPixmap p = clip->referencedClip()->thumbProducer()->extractImage(in, (int)(sub->sizeHint(0).height() * m_render->dar()), sub->sizeHint(0).height() - 2);
1653 sub->setData(0, Qt::DecorationRole, p);
1654 m_doc->cachePixmap(clip->getClipHash() + '#' + QString::number(in), p);
1655 m_listView->blockSignals(false);
1657 emit projectModified();
1660 void ProjectList::removeClipCut(const QString &id, int in, int out)
1662 ProjectItem *clip = getItemById(id);
1664 DocClipBase *base = clip->referencedClip();
1665 base->removeCutZone(in, out);
1666 SubProjectItem *sub = getSubItem(clip, QPoint(in, out));
1668 m_listView->blockSignals(true);
1670 m_listView->blockSignals(false);
1673 emit projectModified();
1676 SubProjectItem *ProjectList::getSubItem(ProjectItem *clip, QPoint zone)
1678 SubProjectItem *sub = NULL;
1680 for (int i = 0; i < clip->childCount(); i++) {
1681 QTreeWidgetItem *it = clip->child(i);
1682 if (it->type() == PROJECTSUBCLIPTYPE) {
1683 sub = static_cast <SubProjectItem*>(it);
1684 if (sub->zone() == zone)
1694 void ProjectList::slotUpdateClipCut(QPoint p)
1696 if (!m_listView->currentItem() || m_listView->currentItem()->type() != PROJECTSUBCLIPTYPE)
1698 SubProjectItem *sub = static_cast <SubProjectItem*>(m_listView->currentItem());
1699 ProjectItem *item = static_cast <ProjectItem *>(sub->parent());
1700 EditClipCutCommand *command = new EditClipCutCommand(this, item->clipId(), sub->zone(), p, sub->text(1), sub->text(1), true);
1701 m_commandStack->push(command);
1704 void ProjectList::doUpdateClipCut(const QString &id, const QPoint oldzone, const QPoint zone, const QString &comment)
1706 ProjectItem *clip = getItemById(id);
1707 SubProjectItem *sub = getSubItem(clip, oldzone);
1708 if (sub == NULL || clip == NULL)
1710 DocClipBase *base = clip->referencedClip();
1711 base->updateCutZone(oldzone.x(), oldzone.y(), zone.x(), zone.y(), comment);
1712 m_listView->blockSignals(true);
1714 sub->setDescription(comment);
1715 m_listView->blockSignals(false);
1716 emit projectModified();
1719 #include "projectlist.moc"