]> git.sesse.net Git - kdenlive/blob - src/projectsettings.cpp
Warn about missing images and fonts in title clips when opening a document:
[kdenlive] / src / projectsettings.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 #include "projectsettings.h"
21 #include "kdenlivesettings.h"
22 #include "profilesdialog.h"
23 #include "docclipbase.h"
24 #include "titlewidget.h"
25 #include "effectslist.h"
26
27 #include <KStandardDirs>
28 #include <KMessageBox>
29 #include <KDebug>
30 #include <kio/directorysizejob.h>
31 #include <KIO/NetAccess>
32
33 #include <QDir>
34 #include <kmessagebox.h>
35
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)
38 {
39     setupUi(this);
40
41     list_search->setListWidget(files_list);
42
43     QMap <QString, QString> profilesInfo = ProfilesDialog::getProfilesInfo();
44     QMapIterator<QString, QString> i(profilesInfo);
45     while (i.hasNext()) {
46         i.next();
47         profiles_list->addItem(i.key(), i.value());
48     }
49     project_folder->setMode(KFile::Directory);
50     project_folder->setUrl(KUrl(projectPath));
51     QString currentProf = KdenliveSettings::current_profile();
52
53     for (int i = 0; i < profiles_list->count(); i++) {
54         if (profiles_list->itemData(i).toString() == currentProf) {
55             profiles_list->setCurrentIndex(i);
56             break;
57         }
58     }
59
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);
66     if (readOnlyTracks) {
67         video_tracks->setEnabled(false);
68         audio_tracks->setEnabled(false);
69     }
70     slotUpdateDisplay();
71     if (m_projectList != NULL) {
72         slotUpdateFiles();
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 &)));
78 }
79
80 void ProjectSettings::slotDeleteUnused()
81 {
82     QStringList toDelete;
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();
89         }
90     }
91
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());
98         }
99     }
100
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();
105         slotUpdateFiles();
106         return;
107     }
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();
110     slotUpdateFiles();
111 }
112
113 void ProjectSettings::slotClearCache()
114 {
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);
120 }
121
122 void ProjectSettings::slotUpdateFiles(bool cacheOnly)
123 {
124     KIO::DirectorySizeJob * job = KIO::directorySize(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/");
125     job->exec();
126     thumbs_count->setText(QString::number(job->totalFiles()));
127     thumbs_size->setText(KIO::convertSize(job->totalSize()));
128     delete job;
129     if (cacheOnly) return;
130     int unused = 0;
131     int used = 0;
132     KIO::filesize_t usedSize = 0;
133     KIO::filesize_t unUsedSize = 0;
134     QList <DocClipBase*> list = m_projectList->documentClipList();
135     files_list->clear();
136
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?
140
141     QStringList allFiles;
142     QStringList allFonts;
143     allFiles << m_lumas;
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());
149             allFiles << files;
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"));
154             allFiles << images;
155             allFonts << fonts;
156         } else if (clip->clipType() == PLAYLIST) {
157             QStringList files = extractPlaylistUrls(clip->fileURL().path());
158             allFiles << files;
159         }
160
161         if (clip->numReferences() == 0) {
162             unused++;
163             unUsedSize += clip->fileSize();
164         } else {
165             used++;
166             usedSize += clip->fileSize();
167         }
168     }
169 #if QT_VERSION >= 0x040500
170     allFiles.removeDuplicates();
171     allFonts.removeDuplicates();
172 #endif
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);
181 }
182
183 void ProjectSettings::accept()
184 {
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;
187     QDialog::accept();
188 }
189
190 void ProjectSettings::slotUpdateDisplay()
191 {
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"));
200 }
201
202 void ProjectSettings::slotUpdateButton(const QString &path)
203 {
204     if (path.isEmpty()) m_buttonOk->setEnabled(false);
205     else {
206         m_buttonOk->setEnabled(true);
207         slotUpdateFiles(true);
208     }
209 }
210
211 QString ProjectSettings::selectedProfile() const
212 {
213     return profiles_list->itemData(profiles_list->currentIndex()).toString();
214 }
215
216 KUrl ProjectSettings::selectedFolder() const
217 {
218     return project_folder->url();
219 }
220
221 QPoint ProjectSettings::tracks()
222 {
223     QPoint p;
224     p.setX(video_tracks->value());
225     p.setY(audio_tracks->value());
226     return p;
227 }
228
229 bool ProjectSettings::enableVideoThumbs() const
230 {
231     return video_thumbs->isChecked();
232 }
233
234 bool ProjectSettings::enableAudioThumbs() const
235 {
236     return audio_thumbs->isChecked();
237 }
238
239 //static
240 QStringList ProjectSettings::extractPlaylistUrls(QString path)
241 {
242     QStringList urls;
243     QDomDocument doc;
244     QFile file(path);
245     if (!file.open(QIODevice::ReadOnly))
246         return urls;
247     if (!doc.setContent(&file)) {
248         file.close();
249         return urls;
250     }
251     file.close();
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));
265                 } else urls << 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);
269                 }
270             }
271         }
272     }
273
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);
281             urls << url;
282         }
283     }
284
285     return urls;
286 }
287
288
289 //static
290 QStringList ProjectSettings::extractSlideshowUrls(KUrl url)
291 {
292     QStringList urls;
293     QString path = url.directory(KUrl::AppendTrailingSlash);
294     QString ext = url.path().section('.', -1);
295     QDir dir(path);
296     QStringList filters;
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));
302     }
303     return urls;
304 }
305
306 #include "projectsettings.moc"
307
308