1 /*
2 SPDX-FileCopyrightText: 2015 Jean-Baptiste Mardelle <jb@kdenlive.org>
3 This file is part of Kdenlive. See www.kdenlive.org.
4
5 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
6 */
7
8 #include "clipcreationdialog.h"
9 #include "bin/bin.h"
10 #include "bin/bincommands.h"
11 #include "bin/clipcreator.hpp"
12 #include "bin/projectclip.h"
13 #include "bin/projectitemmodel.h"
14 #include "core.h"
15 #include "definitions.h"
16 #include "doc/docundostack.hpp"
17 #include "doc/kdenlivedoc.h"
18 #include "kdenlive_debug.h"
19 #include "kdenlivesettings.h"
20 #include "project/dialogs/slideshowclip.h"
21 #include "timecodedisplay.h"
22 #include "titler/titlewidget.h"
23 #include "titletemplatedialog.h"
24 #include "ui_colorclip_ui.h"
25 #include "ui_qtextclip_ui.h"
26 #include "utils/devices.hpp"
27 #include "xml/xml.hpp"
28
29 #include "klocalizedstring.h"
30 #include <KFileWidget>
31 #include <KMessageBox>
32 #include <KRecentDirs>
33 #include <KWindowConfig>
34 #include <KDirOperator>
35
36 #include <QDialog>
37 #include <QDir>
38 #include <QMimeDatabase>
39 #include <QPointer>
40 #include <QPushButton>
41 #include <QStandardPaths>
42 #include <QUndoCommand>
43 #include <QWindow>
44 #include <unordered_map>
45 #include <utility>
46
47 // static
getExtensions()48 QStringList ClipCreationDialog::getExtensions()
49 {
50 // Build list of MIME types
51 QStringList mimeTypes = QStringList() << QLatin1String("") << QStringLiteral("application/x-kdenlivetitle") << QStringLiteral("video/mlt-playlist")
52 << QStringLiteral("text/plain") << QStringLiteral("application/x-kdenlive");
53
54 // Video MIMEs
55 mimeTypes << QStringLiteral("video/x-flv") << QStringLiteral("application/vnd.rn-realmedia") << QStringLiteral("video/x-dv") << QStringLiteral("video/dv")
56 << QStringLiteral("video/x-msvideo") << QStringLiteral("video/x-matroska") << QStringLiteral("video/mpeg") << QStringLiteral("video/ogg")
57 << QStringLiteral("video/x-ms-wmv") << QStringLiteral("video/mp4") << QStringLiteral("video/quicktime") << QStringLiteral("video/webm")
58 << QStringLiteral("video/3gpp") << QStringLiteral("video/mp2t");
59
60 // Audio MIMEs
61 mimeTypes << QStringLiteral("audio/AMR") << QStringLiteral("audio/x-flac") << QStringLiteral("audio/x-matroska") << QStringLiteral("audio/mp4")
62 << QStringLiteral("audio/mpeg") << QStringLiteral("audio/x-mp3") << QStringLiteral("audio/ogg") << QStringLiteral("audio/x-wav")
63 << QStringLiteral("audio/x-aiff") << QStringLiteral("audio/aiff") << QStringLiteral("application/ogg") << QStringLiteral("application/mxf")
64 << QStringLiteral("application/x-shockwave-flash") << QStringLiteral("audio/ac3") << QStringLiteral("audio/aac");
65
66 // Image MIMEs
67 mimeTypes << QStringLiteral("image/gif") << QStringLiteral("image/jpeg") << QStringLiteral("image/png") << QStringLiteral("image/x-tga")
68 << QStringLiteral("image/x-bmp") << QStringLiteral("image/svg+xml") << QStringLiteral("image/tiff") << QStringLiteral("image/x-xcf")
69 << QStringLiteral("image/x-xcf-gimp") << QStringLiteral("image/x-vnd.adobe.photoshop") << QStringLiteral("image/x-pcx")
70 << QStringLiteral("image/x-exr") << QStringLiteral("image/x-portable-pixmap") << QStringLiteral("application/x-krita") << QStringLiteral("image/webp");
71
72 QMimeDatabase db;
73 QStringList allExtensions;
74 for (const QString &mimeType : qAsConst(mimeTypes)) {
75 QMimeType mime = db.mimeTypeForName(mimeType);
76 if (mime.isValid()) {
77 allExtensions.append(mime.globPatterns());
78 }
79 }
80 // process custom user extensions
81 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
82 const QStringList customs = KdenliveSettings::addedExtensions().split(' ', QString::SkipEmptyParts);
83 #else
84 const QStringList customs = KdenliveSettings::addedExtensions().split(' ', Qt::SkipEmptyParts);
85 #endif
86 if (!customs.isEmpty()) {
87 for (const QString &ext : customs) {
88 if (ext.startsWith(QLatin1String("*."))) {
89 allExtensions << ext;
90 } else if (ext.startsWith(QLatin1String("."))) {
91 allExtensions << QStringLiteral("*") + ext;
92 } else if (!ext.contains(QLatin1Char('.'))) {
93 allExtensions << QStringLiteral("*.") + ext;
94 } else {
95 // Unrecognized format
96 qCDebug(KDENLIVE_LOG) << "Unrecognized custom format: " << ext;
97 }
98 }
99 }
100 allExtensions.removeDuplicates();
101 return allExtensions;
102 }
103
getExtensionsFilter(const QStringList & additionalFilters)104 QString ClipCreationDialog::getExtensionsFilter(const QStringList& additionalFilters)
105 {
106 const QString allExtensions = ClipCreationDialog::getExtensions().join(QLatin1Char(' '));
107 QString filter = i18n("All Supported Files") + " (" + allExtensions + ')';
108 if (!additionalFilters.isEmpty()) {
109 filter += ";;";
110 filter.append(additionalFilters.join(";;"));
111 }
112 return filter;
113 }
114
115 // static
createColorClip(KdenliveDoc * doc,const QString & parentFolder,std::shared_ptr<ProjectItemModel> model)116 void ClipCreationDialog::createColorClip(KdenliveDoc *doc, const QString &parentFolder, std::shared_ptr<ProjectItemModel> model)
117 {
118 QScopedPointer<QDialog> dia(new QDialog(qApp->activeWindow()));
119 Ui::ColorClip_UI dia_ui;
120 dia_ui.setupUi(dia.data());
121 dia->setWindowTitle(i18n("Color Clip"));
122 dia_ui.clip_name->setText(i18n("Color Clip"));
123
124 QScopedPointer<TimecodeDisplay> t(new TimecodeDisplay(doc->timecode(), dia.get()));
125 t->setValue(KdenliveSettings::color_duration());
126 dia_ui.clip_durationBox->addWidget(t.data());
127 dia_ui.clip_color->setColor(KdenliveSettings::colorclipcolor());
128
129 if (dia->exec() == QDialog::Accepted) {
130 QString color = dia_ui.clip_color->color().name();
131 KdenliveSettings::setColorclipcolor(color);
132 color = color.replace(0, 1, QStringLiteral("0x")) + "ff";
133 int duration = doc->getFramePos(doc->timecode().getTimecode(t->gentime()));
134 QString name = dia_ui.clip_name->text();
135
136 ClipCreator::createColorClip(color, duration, name, parentFolder, std::move(model));
137 }
138 }
139
createQTextClip(KdenliveDoc * doc,const QString & parentId,Bin * bin,ProjectClip * clip)140 void ClipCreationDialog::createQTextClip(KdenliveDoc *doc, const QString &parentId, Bin *bin, ProjectClip *clip)
141 {
142 KSharedConfigPtr config = KSharedConfig::openConfig();
143 KConfigGroup titleConfig(config, "TitleWidget");
144 QScopedPointer<QDialog> dia(new QDialog(bin));
145 Ui::QTextClip_UI dia_ui;
146 dia_ui.setupUi(dia.data());
147 dia->setWindowTitle(i18n("Text Clip"));
148 dia_ui.fgColor->setAlphaChannelEnabled(true);
149 dia_ui.lineColor->setAlphaChannelEnabled(true);
150 dia_ui.bgColor->setAlphaChannelEnabled(true);
151 if (clip) {
152 dia_ui.name->setText(clip->getProducerProperty(QStringLiteral("kdenlive:clipname")));
153 dia_ui.text->setPlainText(clip->getProducerProperty(QStringLiteral("text")));
154 dia_ui.fgColor->setColor(clip->getProducerProperty(QStringLiteral("fgcolour")));
155 dia_ui.bgColor->setColor(clip->getProducerProperty(QStringLiteral("bgcolour")));
156 dia_ui.pad->setValue(clip->getProducerProperty(QStringLiteral("pad")).toInt());
157 dia_ui.lineColor->setColor(clip->getProducerProperty(QStringLiteral("olcolour")));
158 dia_ui.lineWidth->setValue(clip->getProducerProperty(QStringLiteral("outline")).toInt());
159 dia_ui.font->setCurrentFont(QFont(clip->getProducerProperty(QStringLiteral("family"))));
160 dia_ui.fontSize->setValue(clip->getProducerProperty(QStringLiteral("size")).toInt());
161 dia_ui.weight->setValue(clip->getProducerProperty(QStringLiteral("weight")).toInt());
162 dia_ui.italic->setChecked(clip->getProducerProperty(QStringLiteral("style")) == QStringLiteral("italic"));
163 dia_ui.duration->setText(doc->timecode().getTimecodeFromFrames(clip->getProducerProperty(QStringLiteral("out")).toInt()));
164 } else {
165 dia_ui.name->setText(i18n("Text Clip"));
166 dia_ui.fgColor->setColor(titleConfig.readEntry(QStringLiteral("font_color")));
167 dia_ui.bgColor->setColor(titleConfig.readEntry(QStringLiteral("background_color")));
168 dia_ui.lineColor->setColor(titleConfig.readEntry(QStringLiteral("font_outline_color")));
169 dia_ui.lineWidth->setValue(titleConfig.readEntry(QStringLiteral("font_outline")).toInt());
170 dia_ui.font->setCurrentFont(QFont(titleConfig.readEntry(QStringLiteral("font_family"))));
171 dia_ui.fontSize->setValue(titleConfig.readEntry(QStringLiteral("font_pixel_size")).toInt());
172 dia_ui.weight->setValue(titleConfig.readEntry(QStringLiteral("font_weight")).toInt());
173 dia_ui.italic->setChecked(titleConfig.readEntry(QStringLiteral("font_italic")).toInt() != 0);
174 dia_ui.duration->setText(titleConfig.readEntry(QStringLiteral("title_duration")));
175 }
176 if (dia->exec() == QDialog::Accepted) {
177 // KdenliveSettings::setColorclipcolor(color);
178 QDomDocument xml;
179 QDomElement prod = xml.createElement(QStringLiteral("producer"));
180 xml.appendChild(prod);
181 prod.setAttribute(QStringLiteral("type"), int(ClipType::QText));
182 int id = pCore->projectItemModel()->getFreeClipId();
183 prod.setAttribute(QStringLiteral("id"), QString::number(id));
184
185 prod.setAttribute(QStringLiteral("in"), QStringLiteral("0"));
186 prod.setAttribute(QStringLiteral("out"), doc->timecode().getFrameCount(dia_ui.duration->text()));
187
188 QMap<QString, QString> properties;
189 properties.insert(QStringLiteral("kdenlive:clipname"), dia_ui.name->text());
190 if (!parentId.isEmpty()) {
191 properties.insert(QStringLiteral("kdenlive:folderid"), parentId);
192 }
193
194 properties.insert(QStringLiteral("mlt_service"), QStringLiteral("qtext"));
195 properties.insert(QStringLiteral("out"), QString::number(doc->timecode().getFrameCount(dia_ui.duration->text())));
196 properties.insert(QStringLiteral("length"), dia_ui.duration->text());
197 // properties.insert(QStringLiteral("scale"), QStringLiteral("off"));
198 // properties.insert(QStringLiteral("fill"), QStringLiteral("0"));
199 properties.insert(QStringLiteral("text"), dia_ui.text->document()->toPlainText());
200 properties.insert(QStringLiteral("fgcolour"), dia_ui.fgColor->color().name(QColor::HexArgb));
201 properties.insert(QStringLiteral("bgcolour"), dia_ui.bgColor->color().name(QColor::HexArgb));
202 properties.insert(QStringLiteral("olcolour"), dia_ui.lineColor->color().name(QColor::HexArgb));
203 properties.insert(QStringLiteral("outline"), QString::number(dia_ui.lineWidth->value()));
204 properties.insert(QStringLiteral("pad"), QString::number(dia_ui.pad->value()));
205 properties.insert(QStringLiteral("family"), dia_ui.font->currentFont().family());
206 properties.insert(QStringLiteral("size"), QString::number(dia_ui.fontSize->value()));
207 properties.insert(QStringLiteral("style"), dia_ui.italic->isChecked() ? QStringLiteral("italic") : QStringLiteral("normal"));
208 properties.insert(QStringLiteral("weight"), QString::number(dia_ui.weight->value()));
209 if (clip) {
210 QMap<QString, QString> oldProperties;
211 oldProperties.insert(QStringLiteral("out"), clip->getProducerProperty(QStringLiteral("out")));
212 oldProperties.insert(QStringLiteral("length"), clip->getProducerProperty(QStringLiteral("length")));
213 oldProperties.insert(QStringLiteral("kdenlive:clipname"), clip->name());
214 oldProperties.insert(QStringLiteral("ttl"), clip->getProducerProperty(QStringLiteral("ttl")));
215 oldProperties.insert(QStringLiteral("loop"), clip->getProducerProperty(QStringLiteral("loop")));
216 oldProperties.insert(QStringLiteral("crop"), clip->getProducerProperty(QStringLiteral("crop")));
217 oldProperties.insert(QStringLiteral("fade"), clip->getProducerProperty(QStringLiteral("fade")));
218 oldProperties.insert(QStringLiteral("luma_duration"), clip->getProducerProperty(QStringLiteral("luma_duration")));
219 oldProperties.insert(QStringLiteral("luma_file"), clip->getProducerProperty(QStringLiteral("luma_file")));
220 oldProperties.insert(QStringLiteral("softness"), clip->getProducerProperty(QStringLiteral("softness")));
221 oldProperties.insert(QStringLiteral("animation"), clip->getProducerProperty(QStringLiteral("animation")));
222 bin->slotEditClipCommand(clip->AbstractProjectItem::clipId(), oldProperties, properties);
223 } else {
224 Xml::addXmlProperties(prod, properties);
225 QString clipId = QString::number(id);
226 pCore->projectItemModel()->requestAddBinClip(clipId, xml.documentElement(), parentId, i18n("Create Title clip"));
227 }
228 }
229 }
230
231 // static
createSlideshowClip(KdenliveDoc * doc,const QString & parentId,std::shared_ptr<ProjectItemModel> model)232 void ClipCreationDialog::createSlideshowClip(KdenliveDoc *doc, const QString &parentId, std::shared_ptr<ProjectItemModel> model)
233 {
234 QScopedPointer<SlideshowClip> dia(
235 new SlideshowClip(doc->timecode(), KRecentDirs::dir(QStringLiteral(":KdenliveSlideShowFolder")), nullptr, QApplication::activeWindow()));
236
237 if (dia->exec() == QDialog::Accepted) {
238 // Ready, create xml
239 KRecentDirs::add(QStringLiteral(":KdenliveSlideShowFolder"), QUrl::fromLocalFile(dia->selectedPath()).adjusted(QUrl::RemoveFilename).toLocalFile());
240 std::unordered_map<QString, QString> properties;
241 properties[QStringLiteral("ttl")] = QString::number(doc->getFramePos(dia->clipDuration()));
242 properties[QStringLiteral("loop")] = QString::number(static_cast<int>(dia->loop()));
243 properties[QStringLiteral("crop")] = QString::number(static_cast<int>(dia->crop()));
244 properties[QStringLiteral("fade")] = QString::number(static_cast<int>(dia->fade()));
245 properties[QStringLiteral("luma_duration")] = QString::number(doc->getFramePos(dia->lumaDuration()));
246 properties[QStringLiteral("luma_file")] = dia->lumaFile();
247 properties[QStringLiteral("softness")] = QString::number(dia->softness());
248 properties[QStringLiteral("animation")] = dia->animation();
249
250 int duration = doc->getFramePos(dia->clipDuration()) * dia->imageCount();
251 ClipCreator::createSlideshowClip(dia->selectedPath(), duration, dia->clipName(), parentId, properties, std::move(model));
252 }
253 }
254
createTitleClip(KdenliveDoc * doc,const QString & parentFolder,const QString & templatePath,std::shared_ptr<ProjectItemModel> model)255 void ClipCreationDialog::createTitleClip(KdenliveDoc *doc, const QString &parentFolder, const QString &templatePath, std::shared_ptr<ProjectItemModel> model)
256 {
257 // Make sure the titles folder exists
258 QDir dir(doc->projectDataFolder() + QStringLiteral("/titles"));
259 dir.mkpath(QStringLiteral("."));
260 QPointer<TitleWidget> dia_ui =
261 new TitleWidget(QUrl::fromLocalFile(templatePath), dir.absolutePath(), pCore->getMonitor(Kdenlive::ProjectMonitor), pCore->bin());
262 if (dia_ui->exec() == QDialog::Accepted) {
263 // Ready, create clip xml
264 std::unordered_map<QString, QString> properties;
265 properties[QStringLiteral("xmldata")] = dia_ui->xml().toString();
266 QString titleSuggestion = dia_ui->titleSuggest();
267
268 ClipCreator::createTitleClip(properties, dia_ui->duration(), titleSuggestion.isEmpty() ? i18n("Title clip") : titleSuggestion, parentFolder,
269 std::move(model));
270 }
271 delete dia_ui;
272 }
273
createTitleTemplateClip(KdenliveDoc * doc,const QString & parentFolder,std::shared_ptr<ProjectItemModel> model)274 void ClipCreationDialog::createTitleTemplateClip(KdenliveDoc *doc, const QString &parentFolder, std::shared_ptr<ProjectItemModel> model)
275 {
276
277 QScopedPointer<TitleTemplateDialog> dia(new TitleTemplateDialog(doc->projectDataFolder(), QApplication::activeWindow()));
278
279 if (dia->exec() == QDialog::Accepted) {
280 ClipCreator::createTitleTemplate(dia->selectedTemplate(), dia->selectedText(), i18n("Template title clip"), parentFolder, std::move(model));
281 }
282 }
283
284 // void ClipCreationDialog::createClipsCommand(KdenliveDoc *doc, const QList<QUrl> &urls, const QStringList &groupInfo, Bin *bin,
285 // const QMap<QString, QString> &data)
286 // {
287 // auto *addClips = new QUndoCommand();
288
289 // TODO: check files on removable volume
290 /*listRemovableVolumes();
291 for (const QUrl &file : urls) {
292 if (QFile::exists(file.path())) {
293 //TODO check for duplicates
294 if (!data.contains("bypassDuplicate") && !getClipByResource(file.path()).empty()) {
295 if (KMessageBox::warningContinueCancel(QApplication::activeWindow(), i18n("Clip <b>%1</b><br />already exists in project, what do you want to
296 do?", file.path()), i18n("Clip already exists")) == KMessageBox::Cancel)
297 continue;
298 }
299 if (isOnRemovableDevice(file) && !isOnRemovableDevice(m_doc->projectFolder())) {
300 int answer = KMessageBox::warningYesNoCancel(QApplication::activeWindow(), i18n("Clip <b>%1</b><br /> is on a removable device, will not be
301 available when device is unplugged", file.path()), i18n("File on a Removable Device"), KGuiItem(i18n("Copy file to project folder")),
302 KGuiItem(i18n("Continue")), KStandardGuiItem::cancel(), QString("copyFilesToProjectFolder"));
303
304 if (answer == KMessageBox::Cancel) continue;
305 else if (answer == KMessageBox::Yes) {
306 // Copy files to project folder
307 QDir sourcesFolder(m_doc->projectFolder().toLocalFile());
308 sourcesFolder.cd("clips");
309 KIO::MkdirJob *mkdirJob = KIO::mkdir(QUrl::fromLocalFile(sourcesFolder.absolutePath()));
310 KJobWidgets::setWindow(mkdirJob, QApplication::activeWindow());
311 if (!mkdirJob->exec()) {
312 KMessageBox::sorry(QApplication::activeWindow(), i18n("Cannot create directory %1", sourcesFolder.absolutePath()));
313 continue;
314 }
315 //KIO::filesize_t m_requestedSize;
316 KIO::CopyJob *copyjob = KIO::copy(file, QUrl::fromLocalFile(sourcesFolder.absolutePath()));
317 //TODO: for some reason, passing metadata does not work...
318 copyjob->addMetaData("group", data.value("group"));
319 copyjob->addMetaData("groupId", data.value("groupId"));
320 copyjob->addMetaData("comment", data.value("comment"));
321 KJobWidgets::setWindow(copyjob, QApplication::activeWindow());
322 connect(copyjob, &KIO::CopyJob::copyingDone, this, &ClipManager::slotAddCopiedClip);
323 continue;
324 }
325 }*/
326
327 // TODO check folders
328 /*QList< QList<QUrl> > foldersList;
329 QMimeDatabase db;
330 for (const QUrl & file : list) {
331 // Check there is no folder here
332 QMimeType type = db.mimeTypeForUrl(file);
333 if (type.inherits("inode/directory")) {
334 // user dropped a folder, import its files
335 list.removeAll(file);
336 QDir dir(file.path());
337 QStringList result = dir.entryList(QDir::Files);
338 QList<QUrl> folderFiles;
339 folderFiles << file;
340 for (const QString & path : result) {
341 // TODO: create folder command
342 folderFiles.append(QUrl::fromLocalFile(dir.absoluteFilePath(path)));
343 }
344 if (folderFiles.count() > 1) foldersList.append(folderFiles);
345 }
346 }*/
347 //}
348
createClipsCommand(KdenliveDoc * doc,const QString & parentFolder,const std::shared_ptr<ProjectItemModel> & model)349 void ClipCreationDialog::createClipsCommand(KdenliveDoc *doc, const QString &parentFolder, const std::shared_ptr<ProjectItemModel> &model)
350 {
351 qDebug() << "/////////// starting to add bin clips";
352 QList<QUrl> list;
353 QString allExtensions = getExtensions().join(QLatin1Char(' '));
354 QString dialogFilter = allExtensions + QLatin1Char('|') + i18n("All Supported Files") + QStringLiteral("\n*|") + i18n("All Files");
355 QCheckBox *b = new QCheckBox(i18n("Import image sequence"));
356 b->setChecked(KdenliveSettings::autoimagesequence());
357 QCheckBox *bf = new QCheckBox(i18n("Ignore subfolder structure"));
358 bf->setChecked(KdenliveSettings::ignoresubdirstructure());
359 QFrame *f = new QFrame();
360 f->setFrameShape(QFrame::NoFrame);
361 auto *l = new QHBoxLayout;
362 l->addWidget(b);
363 l->addWidget(bf);
364 l->addStretch(5);
365 f->setLayout(l);
366 QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder"));
367 QScopedPointer<QDialog> dlg(new QDialog(static_cast<QWidget *>(doc->parent())));
368 QScopedPointer<KFileWidget> fileWidget(new KFileWidget(QUrl::fromLocalFile(clipFolder), dlg.data()));
369 auto *layout = new QVBoxLayout;
370 layout->addWidget(fileWidget.data());
371 fileWidget->setCustomWidget(f);
372 fileWidget->okButton()->show();
373 fileWidget->cancelButton()->show();
374 QObject::connect(fileWidget->okButton(), &QPushButton::clicked, fileWidget.data(), &KFileWidget::slotOk);
375 QObject::connect(fileWidget.data(), &KFileWidget::accepted, fileWidget.data(), &KFileWidget::accept);
376 QObject::connect(fileWidget.data(), &KFileWidget::accepted, dlg.data(), &QDialog::accept);
377 QObject::connect(fileWidget->cancelButton(), &QPushButton::clicked, dlg.data(), &QDialog::reject);
378 dlg->setLayout(layout);
379 fileWidget->setFilter(dialogFilter);
380 fileWidget->setMode(KFile::Files | KFile::ExistingOnly | KFile::LocalOnly | KFile::Directory);
381 KSharedConfig::Ptr conf = KSharedConfig::openConfig();
382 QWindow *handle = dlg->windowHandle();
383 if ((handle != nullptr) && conf->hasGroup("FileDialogSize")) {
384 KWindowConfig::restoreWindowSize(handle, conf->group("FileDialogSize"));
385 dlg->resize(handle->size());
386 }
387 int result = dlg->exec();
388 if (result == QDialog::Accepted) {
389 KdenliveSettings::setAutoimagesequence(b->isChecked());
390 KdenliveSettings::setIgnoresubdirstructure(bf->isChecked());
391 list = fileWidget->selectedUrls();
392 if (!list.isEmpty()) {
393 KRecentDirs::add(QStringLiteral(":KdenliveClipFolder"), list.constFirst().adjusted(QUrl::RemoveFilename).toLocalFile());
394 }
395 if (KdenliveSettings::autoimagesequence() && list.count() >= 1) {
396 // Check for image sequence
397 const QUrl &url = list.at(0);
398 QString fileName = url.fileName().section(QLatin1Char('.'), 0, -2);
399 if (!fileName.isEmpty() && fileName.at(fileName.size() - 1).isDigit()) {
400 KFileItem item(url);
401 if (item.mimetype().startsWith(QLatin1String("image"))) {
402 // import as sequence if we found more than one image in the sequence
403 QStringList patternlist;
404 QString pattern = SlideshowClip::selectedPath(url, false, QString(), &patternlist);
405 qCDebug(KDENLIVE_LOG) << " / // IMPORT PATTERN: " << pattern << " COUNT: " << patternlist.count();
406 int count = patternlist.count();
407 if (count > 1) {
408 // get image sequence base name
409 while (fileName.size() > 0 && fileName.at(fileName.size() - 1).isDigit()) {
410 fileName.chop(1);
411 }
412
413 QString duration = doc->timecode().reformatSeparators(KdenliveSettings::sequence_duration());
414 std::unordered_map<QString, QString> properties;
415 properties[QStringLiteral("ttl")] = QString::number(doc->getFramePos(duration));
416 properties[QStringLiteral("loop")] = QString::number(0);
417 properties[QStringLiteral("crop")] = QString::number(0);
418 properties[QStringLiteral("fade")] = QString::number(0);
419 properties[QStringLiteral("luma_duration")] =
420 QString::number(doc->getFramePos(doc->timecode().getTimecodeFromFrames(int(ceil(doc->timecode().fps())))));
421 int frame_duration = doc->getFramePos(duration) * count;
422 ClipCreator::createSlideshowClip(pattern, frame_duration, fileName, parentFolder, properties, model);
423 return;
424 }
425 }
426 }
427 }
428 }
429 qDebug() << "/////////// found list" << list;
430 KConfigGroup group = conf->group("FileDialogSize");
431 if (handle) {
432 KWindowConfig::saveWindowSize(handle, group);
433 }
434 Fun undo = []() { return true; };
435 Fun redo = []() { return true; };
436 const QString id = ClipCreator::createClipsFromList(list, true, parentFolder, model, undo, redo);
437
438 // We reset the state of the "don't ask again" for the question about removable devices
439 KMessageBox::enableMessage(QStringLiteral("removable"));
440 if (!id.isEmpty()) {
441 pCore->pushUndo(undo, redo, i18np("Add clip", "Add clips", list.size()));
442 }
443 }
444
clipWidget(QDockWidget * m_DockClipWidget)445 void ClipCreationDialog::clipWidget(QDockWidget* m_DockClipWidget)
446 {
447 QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder"));
448 KFileWidget* fileWidget = new KFileWidget(QUrl::fromLocalFile(clipFolder), m_DockClipWidget);
449 fileWidget->setMode(KFile::Files | KFile::ExistingOnly | KFile::LocalOnly | KFile::Directory);
450 QString allExtensions = getExtensions().join(QLatin1Char(' '));
451 QString dialogFilter = allExtensions + QLatin1Char('|') + i18n("All Supported Files") + QStringLiteral("\n*|") + i18n("All Files");
452
453 QPushButton* importseq = new QPushButton(i18n("Import image sequence"));
454 // Make importseq checkable so that we can differentiate between a double click in filewidget and a click on the pushbutton
455 importseq->setCheckable(true);
456 QCheckBox *b = new QCheckBox(i18n("Ignore subfolder structure"));
457 b->setChecked(KdenliveSettings::ignoresubdirstructure());
458 QFrame *f = new QFrame();
459 f->setFrameShape(QFrame::NoFrame);
460 auto *l = new QHBoxLayout;
461 l->addWidget(b);
462 l->addStretch(5);
463 l->addWidget(importseq);
464 f->setLayout(l);
465 fileWidget->setCustomWidget(f);
466 // Required to only add file on double click and not on single click
467 fileWidget->setOperationMode(KFileWidget::Saving);
468 QObject::connect(fileWidget, &KFileWidget::accepted, fileWidget, [fileWidget, importseq]() {
469 if (importseq->isChecked()) {
470 // We are importing an image sequence, abort
471 return;
472 }
473 fileWidget->accept();
474 QList <QUrl> urls = fileWidget->selectedUrls();
475 pCore->bin()->droppedUrls(urls);
476 });
477 fileWidget->setFilter(dialogFilter);
478 QObject::connect(b, &QCheckBox::toggled , [](bool checked) {
479 KdenliveSettings::setIgnoresubdirstructure(checked);
480 });
481 QObject::connect(importseq, &QPushButton::clicked, fileWidget, [=]{
482 fileWidget->slotOk();
483 emit fileWidget->accepted();
484 fileWidget->accept();
485 QUrl url = fileWidget->selectedUrl();
486 QStringList patternlist;
487 QString pattern = SlideshowClip::selectedPath(url, false, QString(), &patternlist);
488 int count = patternlist.size();
489
490 QString fileName = url.fileName().section(QLatin1Char('.'), 0, -2);
491 importseq->setChecked(false);
492 if (count >= 1) {
493 while (fileName.size() > 0 && fileName.at(fileName.size() - 1).isDigit()) {
494 fileName.chop(1);
495 }
496 QString parentFolder = "-1";
497 KdenliveDoc *doc = pCore->currentDoc();
498 QString duration = doc->timecode().reformatSeparators(KdenliveSettings::sequence_duration());
499 std::unordered_map<QString, QString> properties;
500 properties[QStringLiteral("ttl")] = QString::number(doc->getFramePos(duration));
501 properties[QStringLiteral("loop")] = QString::number(0);
502 properties[QStringLiteral("crop")] = QString::number(0);
503 properties[QStringLiteral("fade")] = QString::number(0);
504 properties[QStringLiteral("luma_duration")] =
505 QString::number(doc->getFramePos(doc->timecode().getTimecodeFromFrames(int(ceil(doc->timecode().fps())))));
506 int frame_duration = doc->getFramePos(duration) * count;
507 ClipCreator::createSlideshowClip(pattern, frame_duration, fileName, parentFolder, properties, pCore->projectItemModel());
508 return;
509
510 }
511 });
512 m_DockClipWidget->setWidget(fileWidget);
513 }
514