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 ***************************************************************************/
20 #include "projectsettings.h"
21 #include "kdenlivesettings.h"
22 #include "profilesdialog.h"
23 #include "docclipbase.h"
24 #include "titlewidget.h"
25 #include "effectslist.h"
27 #include <KStandardDirs>
28 #include <KMessageBox>
30 #include <kio/directorysizejob.h>
31 #include <KIO/NetAccess>
34 #include <kmessagebox.h>
36 ProjectSettings::ProjectSettings(ProjectList *projectlist, QStringList lumas, int videotracks, int audiotracks, const QString projectPath, bool readOnlyTracks, bool savedProject, QWidget * parent) :
37 QDialog(parent), m_savedProject(savedProject), m_projectList(projectlist), m_lumas(lumas)
41 list_search->setListWidget(files_list);
43 QMap <QString, QString> profilesInfo = ProfilesDialog::getProfilesInfo();
44 QMapIterator<QString, QString> i(profilesInfo);
47 profiles_list->addItem(i.key(), i.value());
49 project_folder->setMode(KFile::Directory);
50 project_folder->setUrl(KUrl(projectPath));
51 QString currentProf = KdenliveSettings::current_profile();
53 for (int i = 0; i < profiles_list->count(); i++) {
54 if (profiles_list->itemData(i).toString() == currentProf) {
55 profiles_list->setCurrentIndex(i);
60 m_buttonOk = buttonBox->button(QDialogButtonBox::Ok);
61 //buttonOk->setEnabled(false);
62 audio_thumbs->setChecked(KdenliveSettings::audiothumbnails());
63 video_thumbs->setChecked(KdenliveSettings::videothumbnails());
64 audio_tracks->setValue(audiotracks);
65 video_tracks->setValue(videotracks);
67 video_tracks->setEnabled(false);
68 audio_tracks->setEnabled(false);
71 if (m_projectList != NULL) {
73 connect(clear_cache, SIGNAL(clicked()), this, SLOT(slotClearCache()));
74 connect(delete_unused, SIGNAL(clicked()), this, SLOT(slotDeleteUnused()));
75 } else tabWidget->widget(1)->setEnabled(false);
76 connect(profiles_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateDisplay()));
77 connect(project_folder, SIGNAL(textChanged(const QString &)), this, SLOT(slotUpdateButton(const QString &)));
80 void ProjectSettings::slotDeleteUnused()
83 QList <DocClipBase*> list = m_projectList->documentClipList();
84 for (int i = 0; i < list.count(); i++) {
85 DocClipBase *clip = list.at(i);
86 if (clip->numReferences() == 0 && clip->clipType() != SLIDESHOW) {
87 KUrl url = clip->fileURL();
88 if (!url.isEmpty() && !toDelete.contains(url.path())) toDelete << url.path();
92 // make sure our urls are not used in another clip
93 for (int i = 0; i < list.count(); i++) {
94 DocClipBase *clip = list.at(i);
95 if (clip->numReferences() > 0) {
96 KUrl url = clip->fileURL();
97 if (!url.isEmpty() && toDelete.contains(url.path())) toDelete.removeAll(url.path());
101 if (toDelete.count() == 0) {
102 // No physical url to delete, we only remove unused clips from project (color clips for example have no physical url)
103 if (KMessageBox::warningContinueCancel(this, i18n("This will remove all unused clips from your project."), i18n("Clean up project")) == KMessageBox::Cancel) return;
104 m_projectList->cleanup();
108 if (KMessageBox::warningYesNoList(this, i18n("This will remove the following files from your hard drive.\nThis action cannot be undone, only use if you know what you are doing.\nAre you sure you want to continue?"), toDelete, i18n("Delete unused clips")) != KMessageBox::Yes) return;
109 m_projectList->trashUnusedClips();
113 void ProjectSettings::slotClearCache()
115 buttonBox->setEnabled(false);
116 KIO::NetAccess::del(KUrl(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/"), this);
117 KStandardDirs::makeDir(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/");
118 buttonBox->setEnabled(true);
119 slotUpdateFiles(true);
122 void ProjectSettings::slotUpdateFiles(bool cacheOnly)
124 KIO::DirectorySizeJob * job = KIO::directorySize(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/");
126 thumbs_count->setText(QString::number(job->totalFiles()));
127 thumbs_size->setText(KIO::convertSize(job->totalSize()));
129 if (cacheOnly) return;
132 KIO::filesize_t usedSize = 0;
133 KIO::filesize_t unUsedSize = 0;
134 QList <DocClipBase*> list = m_projectList->documentClipList();
137 // List all files that are used in the project. That also means:
138 // images included in slideshow and titles, files in playlist clips
139 // TODO: images used in luma transitions, files used for LADSPA effects?
141 QStringList allFiles;
142 QStringList allFonts;
144 for (int i = 0; i < list.count(); i++) {
145 DocClipBase *clip = list.at(i);
146 if (clip->clipType() == SLIDESHOW) {
147 // special case, list all images
148 QStringList files = extractSlideshowUrls(clip->fileURL());
150 } else if (!clip->fileURL().isEmpty()) allFiles.append(clip->fileURL().path());
151 if (clip->clipType() == TEXT) {
152 QStringList images = TitleWidget::extractImageList(clip->getProperty("xmldata"));
153 QStringList fonts = TitleWidget::extractFontList(clip->getProperty("xmldata"));
156 } else if (clip->clipType() == PLAYLIST) {
157 QStringList files = extractPlaylistUrls(clip->fileURL().path());
161 if (clip->numReferences() == 0) {
163 unUsedSize += clip->fileSize();
166 usedSize += clip->fileSize();
169 #if QT_VERSION >= 0x040500
170 allFiles.removeDuplicates();
171 allFonts.removeDuplicates();
173 files_count->setText(QString::number(allFiles.count()));
174 files_list->addItems(allFiles);
175 fonts_list->addItems(allFonts);
176 used_count->setText(QString::number(used));
177 used_size->setText(KIO::convertSize(usedSize));
178 unused_count->setText(QString::number(unused));
179 unused_size->setText(KIO::convertSize(unUsedSize));
180 delete_unused->setEnabled(unused > 0);
183 void ProjectSettings::accept()
185 if (!m_savedProject && selectedProfile() != KdenliveSettings::current_profile())
186 if (KMessageBox::warningContinueCancel(this, i18n("Changing the profile of your project cannot be undone.\nIt is recommended to save your project before attempting this operation that might cause some corruption in transitions.\n Are you sure you want to proceed?"), i18n("Confirm profile change")) == KMessageBox::Cancel) return;
190 void ProjectSettings::slotUpdateDisplay()
192 QString currentProfile = profiles_list->itemData(profiles_list->currentIndex()).toString();
193 QMap< QString, QString > values = ProfilesDialog::getSettingsFromFile(currentProfile);
194 p_size->setText(values.value("width") + 'x' + values.value("height"));
195 p_fps->setText(values.value("frame_rate_num") + '/' + values.value("frame_rate_den"));
196 p_aspect->setText(values.value("sample_aspect_num") + '/' + values.value("sample_aspect_den"));
197 p_display->setText(values.value("display_aspect_num") + '/' + values.value("display_aspect_den"));
198 if (values.value("progressive").toInt() == 0) p_progressive->setText(i18n("Interlaced"));
199 else p_progressive->setText(i18n("Progressive"));
202 void ProjectSettings::slotUpdateButton(const QString &path)
204 if (path.isEmpty()) m_buttonOk->setEnabled(false);
206 m_buttonOk->setEnabled(true);
207 slotUpdateFiles(true);
211 QString ProjectSettings::selectedProfile() const
213 return profiles_list->itemData(profiles_list->currentIndex()).toString();
216 KUrl ProjectSettings::selectedFolder() const
218 return project_folder->url();
221 QPoint ProjectSettings::tracks()
224 p.setX(video_tracks->value());
225 p.setY(audio_tracks->value());
229 bool ProjectSettings::enableVideoThumbs() const
231 return video_thumbs->isChecked();
234 bool ProjectSettings::enableAudioThumbs() const
236 return audio_thumbs->isChecked();
240 QStringList ProjectSettings::extractPlaylistUrls(QString path)
245 if (!file.open(QIODevice::ReadOnly))
247 if (!doc.setContent(&file)) {
252 QString root = doc.documentElement().attribute("root");
253 if (!root.isEmpty() && !root.endsWith('/')) root.append('/');
254 QDomNodeList files = doc.elementsByTagName("producer");
255 for (int i = 0; i < files.count(); i++) {
256 QDomElement e = files.at(i).toElement();
257 QString type = EffectsList::property(e, "mlt_service");
258 if (type != "colour") {
259 QString url = EffectsList::property(e, "resource");
260 if (!url.isEmpty()) {
261 if (!url.startsWith('/')) url.prepend(root);
262 if (url.section('.', 0, -2).endsWith("/.all")) {
263 // slideshow clip, extract image urls
264 urls << extractSlideshowUrls(KUrl(url));
266 if (url.endsWith(".mlt") || url.endsWith(".kdenlive")) {
267 //TODO: Do something to avoid infinite loops if 2 files reference themselves...
268 urls << extractPlaylistUrls(url);
274 // luma files for transitions
275 files = doc.elementsByTagName("transition");
276 for (int i = 0; i < files.count(); i++) {
277 QDomElement e = files.at(i).toElement();
278 QString url = EffectsList::property(e, "luma");
279 if (!url.isEmpty()) {
280 if (!url.startsWith('/')) url.prepend(root);
290 QStringList ProjectSettings::extractSlideshowUrls(KUrl url)
293 QString path = url.directory(KUrl::AppendTrailingSlash);
294 QString ext = url.path().section('.', -1);
297 filters << "*." + ext;
298 dir.setNameFilters(filters);
299 QStringList result = dir.entryList(QDir::Files);
300 for (int j = 0; j < result.count(); j++) {
301 urls.append(path + result.at(j));
306 #include "projectsettings.moc"