Warning, file /multimedia/kdenlive/src/dialogs/clipcreationdialog.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2015 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 This file is part of Kdenlive. See www.kdenlive.org. 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "clipcreationdialog.h" 0009 #include "bin/bin.h" 0010 #include "bin/bincommands.h" 0011 #include "bin/clipcreator.hpp" 0012 #include "bin/projectclip.h" 0013 #include "bin/projectitemmodel.h" 0014 #include "core.h" 0015 #include "doc/docundostack.hpp" 0016 #include "doc/kdenlivedoc.h" 0017 #include "glaxnimatelauncher.h" 0018 #include "kdenlive_debug.h" 0019 #include "kdenlivesettings.h" 0020 #include "project/dialogs/slideshowclip.h" 0021 #include "titler/titlewidget.h" 0022 #include "titletemplatedialog.h" 0023 #include "ui_colorclip_ui.h" 0024 #include "ui_qtextclip_ui.h" 0025 #include "utils/devices.hpp" 0026 #include "utils/qcolorutils.h" 0027 #include "widgets/timecodedisplay.h" 0028 #include "xml/xml.hpp" 0029 0030 #include <KFileWidget> 0031 #include <KIO/RenameDialog> 0032 #include <KLocalizedString> 0033 #include <KMessageBox> 0034 #include <KRecentDirs> 0035 #include <KWindowConfig> 0036 0037 #include <QDialog> 0038 #include <QDir> 0039 #include <QMimeDatabase> 0040 #include <QPointer> 0041 #include <QProcess> 0042 #include <QPushButton> 0043 #include <QStandardPaths> 0044 #include <QUndoCommand> 0045 #include <QWindow> 0046 0047 #include <unordered_map> 0048 #include <utility> 0049 0050 // static 0051 QStringList ClipCreationDialog::getExtensions() 0052 { 0053 // Build list of MIME types 0054 QStringList mimeTypes = QStringList() << QStringLiteral("application/x-kdenlivetitle") << QStringLiteral("video/mlt-playlist") 0055 << QStringLiteral("text/plain") << QStringLiteral("application/x-kdenlive"); 0056 0057 // Video MIMEs 0058 mimeTypes << QStringLiteral("video/x-flv") << QStringLiteral("application/vnd.rn-realmedia") << QStringLiteral("video/x-dv") << QStringLiteral("video/dv") 0059 << QStringLiteral("video/x-msvideo") << QStringLiteral("video/x-matroska") << QStringLiteral("video/mpeg") << QStringLiteral("video/ogg") 0060 << QStringLiteral("video/x-ms-wmv") << QStringLiteral("video/mp4") << QStringLiteral("video/quicktime") << QStringLiteral("video/webm") 0061 << QStringLiteral("video/3gpp") << QStringLiteral("video/mp2t"); 0062 0063 // Audio MIMEs 0064 mimeTypes << QStringLiteral("audio/AMR") << QStringLiteral("audio/x-flac") << QStringLiteral("audio/x-matroska") << QStringLiteral("audio/mp4") 0065 << QStringLiteral("audio/mpeg") << QStringLiteral("audio/x-mp3") << QStringLiteral("audio/ogg") << QStringLiteral("audio/x-wav") 0066 << QStringLiteral("audio/x-aiff") << QStringLiteral("audio/aiff") << QStringLiteral("application/ogg") << QStringLiteral("application/mxf") 0067 << QStringLiteral("application/x-shockwave-flash") << QStringLiteral("audio/ac3") << QStringLiteral("audio/aac"); 0068 0069 // Image MIMEs 0070 mimeTypes << QStringLiteral("image/gif") << QStringLiteral("image/jpeg") << QStringLiteral("image/png") << QStringLiteral("image/x-tga") 0071 << QStringLiteral("image/x-bmp") << QStringLiteral("image/svg+xml") << QStringLiteral("image/tiff") << QStringLiteral("image/x-xcf") 0072 << QStringLiteral("image/x-xcf-gimp") << QStringLiteral("image/x-vnd.adobe.photoshop") << QStringLiteral("image/x-pcx") 0073 << QStringLiteral("image/x-exr") << QStringLiteral("image/x-portable-pixmap") << QStringLiteral("application/x-krita") 0074 << QStringLiteral("image/webp") << QStringLiteral("image/jp2") << QStringLiteral("image/avif") << QStringLiteral("image/heif") 0075 << QStringLiteral("image/jxl"); 0076 0077 // Lottie animations 0078 bool allowLottie = KdenliveSettings::producerslist().contains(QLatin1String("glaxnimate")); 0079 if (allowLottie) { 0080 mimeTypes << QStringLiteral("application/json"); 0081 } 0082 0083 // Some newer mimetypes might not be registered on some older Operating Systems, so register manually 0084 QMap<QString, QString> manualMap; 0085 manualMap.insert(QStringLiteral("image/avif"), QStringLiteral("*.avif")); 0086 manualMap.insert(QStringLiteral("image/heif"), QStringLiteral("*.heif")); 0087 manualMap.insert(QStringLiteral("image/x-exr"), QStringLiteral("*.exr")); 0088 manualMap.insert(QStringLiteral("image/jp2"), QStringLiteral("*.jp2")); 0089 manualMap.insert(QStringLiteral("image/jxl"), QStringLiteral("*.jxl")); 0090 0091 QMimeDatabase db; 0092 QStringList allExtensions; 0093 for (const QString &mimeType : qAsConst(mimeTypes)) { 0094 QMimeType mime = db.mimeTypeForName(mimeType); 0095 if (mime.isValid()) { 0096 allExtensions.append(mime.globPatterns()); 0097 } else if (manualMap.contains(mimeType)) { 0098 allExtensions.append(manualMap.value(mimeType)); 0099 } 0100 } 0101 if (allowLottie) { 0102 allExtensions.append(QStringLiteral("*.rawr")); 0103 } 0104 // process custom user extensions 0105 const QStringList customs = KdenliveSettings::addedExtensions().split(' ', Qt::SkipEmptyParts); 0106 if (!customs.isEmpty()) { 0107 for (const QString &ext : customs) { 0108 if (ext.startsWith(QLatin1String("*."))) { 0109 allExtensions << ext; 0110 } else if (ext.startsWith(QLatin1String("."))) { 0111 allExtensions << QStringLiteral("*") + ext; 0112 } else if (!ext.contains(QLatin1Char('.'))) { 0113 allExtensions << QStringLiteral("*.") + ext; 0114 } else { 0115 // Unrecognized format 0116 qCDebug(KDENLIVE_LOG) << "Unrecognized custom format: " << ext; 0117 } 0118 } 0119 } 0120 allExtensions.removeDuplicates(); 0121 return allExtensions; 0122 } 0123 0124 QString ClipCreationDialog::getExtensionsFilter(const QStringList &additionalFilters) 0125 { 0126 const QString allExtensions = ClipCreationDialog::getExtensions().join(QLatin1Char(' ')); 0127 QString filter = i18n("All Supported Files") + " (" + allExtensions + ')'; 0128 if (!additionalFilters.isEmpty()) { 0129 filter += ";;"; 0130 filter.append(additionalFilters.join(";;")); 0131 } 0132 return filter; 0133 } 0134 0135 // static 0136 void ClipCreationDialog::createColorClip(KdenliveDoc *doc, const QString &parentFolder, std::shared_ptr<ProjectItemModel> model) 0137 { 0138 QScopedPointer<QDialog> dia(new QDialog(qApp->activeWindow())); 0139 Ui::ColorClip_UI dia_ui; 0140 dia_ui.setupUi(dia.data()); 0141 dia->setWindowTitle(i18nc("@title:window", "Color Clip")); 0142 dia_ui.clip_name->setText(i18n("Color Clip")); 0143 0144 dia_ui.clip_duration->setValue(KdenliveSettings::color_duration()); 0145 dia_ui.clip_color->setColor(KdenliveSettings::colorclipcolor()); 0146 0147 if (dia->exec() == QDialog::Accepted) { 0148 QString color = dia_ui.clip_color->color().name(); 0149 KdenliveSettings::setColorclipcolor(color); 0150 color = color.replace(0, 1, QStringLiteral("0x")) + "ff"; 0151 int duration = doc->getFramePos(doc->timecode().getTimecode(dia_ui.clip_duration->gentime())); 0152 QString name = dia_ui.clip_name->text(); 0153 0154 ClipCreator::createColorClip(color, duration, name, parentFolder, std::move(model)); 0155 } 0156 } 0157 0158 void ClipCreationDialog::createAnimationClip(KdenliveDoc *doc, const QString &parentId) 0159 { 0160 if (!GlaxnimateLauncher::instance().checkInstalled()) { 0161 return; 0162 } 0163 QDir dir(doc->projectDataFolder()); 0164 QString fileName("animation-0001.json"); 0165 if (dir.cd("animations")) { 0166 int ix = 2; 0167 while (dir.exists(fileName) && ix < 9999) { 0168 QString number = QString::number(ix).rightJustified(4, '0'); 0169 number.prepend(QStringLiteral("animation-")); 0170 number.append(QStringLiteral(".json")); 0171 fileName = number; 0172 ix++; 0173 } 0174 } else { 0175 dir.mkpath("animations"); 0176 dir.cd("animations"); 0177 } 0178 QDialog d(QApplication::activeWindow()); 0179 d.setWindowTitle(i18n("Create animation")); 0180 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0181 auto *l = new QVBoxLayout; 0182 d.setLayout(l); 0183 KUrlRequester fileUrl(&d); 0184 ; 0185 fileUrl.setMode(KFile::File); 0186 fileUrl.setUrl(QUrl::fromLocalFile(dir.absoluteFilePath(fileName))); 0187 l->addWidget(new QLabel(i18n("Save animation as"), &d)); 0188 l->addWidget(&fileUrl); 0189 QHBoxLayout *lay = new QHBoxLayout; 0190 lay->addWidget(new QLabel(i18n("Animation duration"), &d)); 0191 TimecodeDisplay tCode(&d); 0192 tCode.setValue(QStringLiteral("00:00:05:00")); 0193 lay->addWidget(&tCode); 0194 l->addLayout(lay); 0195 l->addWidget(buttonBox); 0196 d.connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject); 0197 d.connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept); 0198 if (d.exec() != QDialog::Accepted) { 0199 return; 0200 } 0201 fileName = fileUrl.url().toLocalFile(); 0202 if (QFile::exists(fileName)) { 0203 KIO::RenameDialog renameDialog(QApplication::activeWindow(), i18n("File already exists"), fileUrl.url(), fileUrl.url(), 0204 KIO::RenameDialog_Option::RenameDialog_Overwrite); 0205 if (renameDialog.exec() != QDialog::Rejected) { 0206 QUrl final = renameDialog.newDestUrl(); 0207 if (final.isValid()) { 0208 fileName = final.toLocalFile(); 0209 } 0210 } else { 0211 return; 0212 } 0213 } 0214 int frameLength = tCode.getValue() - 1; 0215 // Params: duration, framerate, width, height 0216 const QString templateJson = 0217 QString("{\"v\":\"5.7.1\",\"ip\":0,\"op\":%1,\"nm\":\"Animation\",\"mn\":\"{c9eac49f-b1f0-482f-a8d8-302293bd1e46}\",\"fr\":%2,\"w\":%3,\"h\":%4," 0218 "\"assets\":[],\"layers\":[{\"ddd\":0,\"ty\":3,\"ind\":0,\"st\":0,\"ip\":0,\"op\":90,\"nm\":\"Layer\",\"mn\":\"{4d7c9721-b5ef-4075-a89c-" 0219 "c4c5629423db}\",\"ks\":{\"a\":{\"a\":0,\"k\":[960,540]},\"p\":{\"a\":0,\"k\":[960,540]},\"s\":{\"a\":0,\"k\":[100,100]},\"r\":{\"a\":0,\"k\":" 0220 "0},\"o\":{\"a\":0,\"k\":100}}}],\"meta\":{\"g\":\"Glaxnimate 0.5.0-52-g36d6269d\"}}") 0221 .arg(frameLength) 0222 .arg(QString::number(doc->timecode().fps())) 0223 .arg(pCore->getCurrentFrameSize().width()) 0224 .arg(pCore->getCurrentFrameSize().height()); 0225 QFile file(fileName); 0226 file.open(QIODevice::WriteOnly | QIODevice::Text); 0227 QTextStream out(&file); 0228 out << templateJson; 0229 file.close(); 0230 GlaxnimateLauncher::instance().openFile(fileName); 0231 // Add clip to project 0232 QDomDocument xml; 0233 QDomElement prod = xml.createElement(QStringLiteral("producer")); 0234 xml.appendChild(prod); 0235 prod.setAttribute(QStringLiteral("type"), int(ClipType::Animation)); 0236 int id = pCore->projectItemModel()->getFreeClipId(); 0237 prod.setAttribute(QStringLiteral("id"), QString::number(id)); 0238 QMap<QString, QString> properties; 0239 if (!parentId.isEmpty()) { 0240 properties.insert(QStringLiteral("kdenlive:folderid"), parentId); 0241 } 0242 properties.insert(QStringLiteral("mlt_service"), QStringLiteral("glaxnimate")); 0243 properties.insert(QStringLiteral("resource"), fileName); 0244 Xml::addXmlProperties(prod, properties); 0245 QString clipId = QString::number(id); 0246 pCore->projectItemModel()->requestAddBinClip(clipId, xml.documentElement(), parentId, i18n("Create Animation clip")); 0247 } 0248 0249 void ClipCreationDialog::createQTextClip(KdenliveDoc *doc, const QString &parentId, Bin *bin, ProjectClip *clip) 0250 { 0251 KSharedConfigPtr config = KSharedConfig::openConfig(); 0252 KConfigGroup titleConfig(config, "TitleWidget"); 0253 QScopedPointer<QDialog> dia(new QDialog(bin)); 0254 Ui::QTextClip_UI dia_ui; 0255 dia_ui.setupUi(dia.data()); 0256 dia->setWindowTitle(i18nc("@title:window", "Text Clip")); 0257 dia_ui.fgColor->setAlphaChannelEnabled(true); 0258 dia_ui.lineColor->setAlphaChannelEnabled(true); 0259 dia_ui.bgColor->setAlphaChannelEnabled(true); 0260 if (clip) { 0261 dia_ui.name->setText(clip->clipName()); 0262 dia_ui.text->setPlainText(clip->getProducerProperty(QStringLiteral("text"))); 0263 dia_ui.fgColor->setColor(QColorUtils::stringToColor(clip->getProducerProperty(QStringLiteral("fgcolour")))); 0264 dia_ui.bgColor->setColor(QColorUtils::stringToColor(clip->getProducerProperty(QStringLiteral("bgcolour")))); 0265 dia_ui.pad->setValue(clip->getProducerProperty(QStringLiteral("pad")).toInt()); 0266 dia_ui.lineColor->setColor(QColorUtils::stringToColor(clip->getProducerProperty(QStringLiteral("olcolour")))); 0267 dia_ui.lineWidth->setValue(clip->getProducerProperty(QStringLiteral("outline")).toInt()); 0268 dia_ui.font->setCurrentFont(QFont(clip->getProducerProperty(QStringLiteral("family")))); 0269 dia_ui.fontSize->setValue(clip->getProducerProperty(QStringLiteral("size")).toInt()); 0270 dia_ui.weight->setValue(clip->getProducerProperty(QStringLiteral("weight")).toInt()); 0271 dia_ui.italic->setChecked(clip->getProducerProperty(QStringLiteral("style")) == QStringLiteral("italic")); 0272 dia_ui.duration->setValue(clip->frameDuration()); 0273 } else { 0274 dia_ui.name->setText(i18n("Text Clip")); 0275 dia_ui.fgColor->setColor(titleConfig.readEntry(QStringLiteral("font_color"))); 0276 dia_ui.bgColor->setColor(titleConfig.readEntry(QStringLiteral("background_color"))); 0277 dia_ui.lineColor->setColor(titleConfig.readEntry(QStringLiteral("font_outline_color"))); 0278 dia_ui.lineWidth->setValue(titleConfig.readEntry(QStringLiteral("font_outline")).toInt()); 0279 dia_ui.font->setCurrentFont(QFont(titleConfig.readEntry(QStringLiteral("font_family")))); 0280 dia_ui.fontSize->setValue(titleConfig.readEntry(QStringLiteral("font_pixel_size")).toInt()); 0281 dia_ui.weight->setValue(titleConfig.readEntry(QStringLiteral("font_weight")).toInt()); 0282 dia_ui.italic->setChecked(titleConfig.readEntry(QStringLiteral("font_italic")).toInt() != 0); 0283 } 0284 if (dia->exec() == QDialog::Accepted) { 0285 // KdenliveSettings::setColorclipcolor(color); 0286 QMap<QString, QString> properties; 0287 properties.insert(QStringLiteral("kdenlive:clipname"), dia_ui.name->text()); 0288 if (!parentId.isEmpty()) { 0289 properties.insert(QStringLiteral("kdenlive:folderid"), parentId); 0290 } 0291 int newDuration = dia_ui.duration->getValue(); 0292 if (clip && (newDuration != clip->getFramePlaytime())) { 0293 // duration changed, we need to update duration 0294 properties.insert(QStringLiteral("out"), clip->framesToTime(newDuration - 1)); 0295 int currentLength = clip->getProducerDuration(); 0296 if (currentLength != newDuration) { 0297 properties.insert(QStringLiteral("kdenlive:duration"), clip->framesToTime(newDuration)); 0298 properties.insert(QStringLiteral("length"), QString::number(newDuration)); 0299 } 0300 } 0301 0302 properties.insert(QStringLiteral("mlt_service"), QStringLiteral("qtext")); 0303 // properties.insert(QStringLiteral("scale"), QStringLiteral("off")); 0304 // properties.insert(QStringLiteral("fill"), QStringLiteral("0")); 0305 properties.insert(QStringLiteral("text"), dia_ui.text->document()->toPlainText()); 0306 properties.insert(QStringLiteral("fgcolour"), dia_ui.fgColor->color().name(QColor::HexArgb)); 0307 properties.insert(QStringLiteral("bgcolour"), dia_ui.bgColor->color().name(QColor::HexArgb)); 0308 properties.insert(QStringLiteral("olcolour"), dia_ui.lineColor->color().name(QColor::HexArgb)); 0309 properties.insert(QStringLiteral("outline"), QString::number(dia_ui.lineWidth->value())); 0310 properties.insert(QStringLiteral("pad"), QString::number(dia_ui.pad->value())); 0311 properties.insert(QStringLiteral("family"), dia_ui.font->currentFont().family()); 0312 properties.insert(QStringLiteral("size"), QString::number(dia_ui.fontSize->value())); 0313 properties.insert(QStringLiteral("style"), dia_ui.italic->isChecked() ? QStringLiteral("italic") : QStringLiteral("normal")); 0314 properties.insert(QStringLiteral("weight"), QString::number(dia_ui.weight->value())); 0315 if (clip) { 0316 bin->slotEditClipCommand(clip->AbstractProjectItem::clipId(), clip->currentProperties(properties), properties); 0317 } else { 0318 QDomDocument xml; 0319 QDomElement prod = xml.createElement(QStringLiteral("producer")); 0320 xml.appendChild(prod); 0321 prod.setAttribute(QStringLiteral("type"), int(ClipType::QText)); 0322 int id = pCore->projectItemModel()->getFreeClipId(); 0323 prod.setAttribute(QStringLiteral("id"), QString::number(id)); 0324 0325 prod.setAttribute(QStringLiteral("in"), QStringLiteral("0")); 0326 prod.setAttribute(QStringLiteral("out"), newDuration); 0327 Xml::addXmlProperties(prod, properties); 0328 QString clipId = QString::number(id); 0329 pCore->projectItemModel()->requestAddBinClip(clipId, xml.documentElement(), parentId, i18n("Create Text clip")); 0330 } 0331 } 0332 } 0333 0334 // static 0335 void ClipCreationDialog::createSlideshowClip(KdenliveDoc *doc, const QString &parentId, std::shared_ptr<ProjectItemModel> model) 0336 { 0337 QScopedPointer<SlideshowClip> dia( 0338 new SlideshowClip(doc->timecode(), KRecentDirs::dir(QStringLiteral(":KdenliveSlideShowFolder")), nullptr, QApplication::activeWindow())); 0339 0340 if (dia->exec() == QDialog::Accepted) { 0341 // Ready, create xml 0342 KRecentDirs::add(QStringLiteral(":KdenliveSlideShowFolder"), QUrl::fromLocalFile(dia->selectedPath()).adjusted(QUrl::RemoveFilename).toLocalFile()); 0343 KdenliveSettings::setSlideshowmimeextension(dia->extension()); 0344 std::unordered_map<QString, QString> properties; 0345 properties[QStringLiteral("ttl")] = QString::number(doc->getFramePos(dia->clipDuration())); 0346 properties[QStringLiteral("loop")] = QString::number(static_cast<int>(dia->loop())); 0347 properties[QStringLiteral("crop")] = QString::number(static_cast<int>(dia->crop())); 0348 properties[QStringLiteral("fade")] = QString::number(static_cast<int>(dia->fade())); 0349 properties[QStringLiteral("luma_duration")] = QString::number(doc->getFramePos(dia->lumaDuration())); 0350 properties[QStringLiteral("luma_file")] = dia->lumaFile(); 0351 properties[QStringLiteral("softness")] = QString::number(dia->softness()); 0352 properties[QStringLiteral("animation")] = dia->animation(); 0353 properties[QStringLiteral("low-pass")] = QString::number(dia->lowPass()); 0354 0355 int duration = doc->getFramePos(dia->clipDuration()) * dia->imageCount(); 0356 ClipCreator::createSlideshowClip(dia->selectedPath(), duration, dia->clipName(), parentId, properties, std::move(model)); 0357 } 0358 } 0359 0360 void ClipCreationDialog::createTitleClip(KdenliveDoc *doc, const QString &parentFolder, const QString &templatePath, std::shared_ptr<ProjectItemModel> model) 0361 { 0362 // Make sure the titles folder exists 0363 QDir dir(doc->projectDataFolder() + QStringLiteral("/titles")); 0364 dir.mkpath(QStringLiteral(".")); 0365 QPointer<TitleWidget> dia_ui = 0366 new TitleWidget(QUrl::fromLocalFile(templatePath), dir.absolutePath(), pCore->getMonitor(Kdenlive::ProjectMonitor), pCore->bin()); 0367 if (dia_ui->exec() == QDialog::Accepted) { 0368 // Ready, create clip xml 0369 std::unordered_map<QString, QString> properties; 0370 properties[QStringLiteral("xmldata")] = dia_ui->xml().toString(); 0371 QString titleSuggestion = dia_ui->titleSuggest(); 0372 0373 ClipCreator::createTitleClip(properties, dia_ui->duration(), titleSuggestion.isEmpty() ? i18n("Title clip") : titleSuggestion, parentFolder, 0374 std::move(model)); 0375 } 0376 delete dia_ui; 0377 } 0378 0379 void ClipCreationDialog::createTitleTemplateClip(KdenliveDoc *doc, const QString &parentFolder, std::shared_ptr<ProjectItemModel> model) 0380 { 0381 0382 QScopedPointer<TitleTemplateDialog> dia(new TitleTemplateDialog(doc->projectDataFolder(), QApplication::activeWindow())); 0383 0384 if (dia->exec() == QDialog::Accepted) { 0385 ClipCreator::createTitleTemplate(dia->selectedTemplate(), dia->selectedText(), i18n("Template title clip"), parentFolder, std::move(model)); 0386 } 0387 } 0388 0389 // void ClipCreationDialog::createClipsCommand(KdenliveDoc *doc, const QList<QUrl> &urls, const QStringList &groupInfo, Bin *bin, 0390 // const QMap<QString, QString> &data) 0391 // { 0392 // auto *addClips = new QUndoCommand(); 0393 0394 // TODO: check files on removable volume 0395 /*listRemovableVolumes(); 0396 for (const QUrl &file : urls) { 0397 if (QFile::exists(file.path())) { 0398 //TODO check for duplicates 0399 if (!data.contains("bypassDuplicate") && !getClipByResource(file.path()).empty()) { 0400 if (KMessageBox::warningContinueCancel(QApplication::activeWindow(), i18n("Clip <b>%1</b><br />already exists in project, what do you want to 0401 do?", file.path()), i18n("Clip already exists")) == KMessageBox::Cancel) 0402 continue; 0403 } 0404 if (isOnRemovableDevice(file) && !isOnRemovableDevice(m_doc->projectFolder())) { 0405 int answer = KMessageBox::warningYesNoCancel(QApplication::activeWindow(), i18n("Clip <b>%1</b><br /> is on a removable device, will not be 0406 available when device is unplugged", file.path()), i18n("File on a Removable Device"), KGuiItem(i18n("Copy file to project folder")), 0407 KGuiItem(i18n("Continue")), KStandardGuiItem::cancel(), QString("copyFilesToProjectFolder")); 0408 0409 if (answer == KMessageBox::Cancel) continue; 0410 else if (answer == KMessageBox::Yes) { 0411 // Copy files to project folder 0412 QDir sourcesFolder(m_doc->projectFolder().toLocalFile()); 0413 sourcesFolder.cd("clips"); 0414 KIO::MkdirJob *mkdirJob = KIO::mkdir(QUrl::fromLocalFile(sourcesFolder.absolutePath())); 0415 KJobWidgets::setWindow(mkdirJob, QApplication::activeWindow()); 0416 if (!mkdirJob->exec()) { 0417 KMessageBox::error(QApplication::activeWindow(), i18n("Cannot create directory %1", sourcesFolder.absolutePath())); 0418 continue; 0419 } 0420 //KIO::filesize_t m_requestedSize; 0421 KIO::CopyJob *copyjob = KIO::copy(file, QUrl::fromLocalFile(sourcesFolder.absolutePath())); 0422 //TODO: for some reason, passing metadata does not work... 0423 copyjob->addMetaData("group", data.value("group")); 0424 copyjob->addMetaData("groupId", data.value("groupId")); 0425 copyjob->addMetaData("comment", data.value("comment")); 0426 KJobWidgets::setWindow(copyjob, QApplication::activeWindow()); 0427 connect(copyjob, &KIO::CopyJob::copyingDone, this, &ClipManager::slotAddCopiedClip); 0428 continue; 0429 } 0430 }*/ 0431 0432 // TODO check folders 0433 /*QList< QList<QUrl> > foldersList; 0434 QMimeDatabase db; 0435 for (const QUrl & file : list) { 0436 // Check there is no folder here 0437 QMimeType type = db.mimeTypeForUrl(file); 0438 if (type.inherits("inode/directory")) { 0439 // user dropped a folder, import its files 0440 list.removeAll(file); 0441 QDir dir(file.path()); 0442 QStringList result = dir.entryList(QDir::Files); 0443 QList<QUrl> folderFiles; 0444 folderFiles << file; 0445 for (const QString & path : result) { 0446 // TODO: create folder command 0447 folderFiles.append(QUrl::fromLocalFile(dir.absoluteFilePath(path))); 0448 } 0449 if (folderFiles.count() > 1) foldersList.append(folderFiles); 0450 } 0451 }*/ 0452 //} 0453 0454 void ClipCreationDialog::createClipsCommand(KdenliveDoc *doc, const QString &parentFolder, const std::shared_ptr<ProjectItemModel> &model) 0455 { 0456 qDebug() << "/////////// starting to add bin clips"; 0457 QList<QUrl> list; 0458 QString allExtensions = getExtensions().join(QLatin1Char(' ')); 0459 QString dialogFilter = allExtensions + QLatin1Char('|') + i18n("All Supported Files") + QStringLiteral("\n*|") + i18n("All Files"); 0460 QCheckBox *b = new QCheckBox(i18n("Import image sequence")); 0461 b->setChecked(KdenliveSettings::autoimagesequence()); 0462 QCheckBox *bf = new QCheckBox(i18n("Ignore subfolder structure")); 0463 bf->setChecked(KdenliveSettings::ignoresubdirstructure()); 0464 QFrame *f = new QFrame(); 0465 f->setFrameShape(QFrame::NoFrame); 0466 auto *l = new QHBoxLayout; 0467 l->addWidget(b); 0468 l->addWidget(bf); 0469 l->addStretch(5); 0470 f->setLayout(l); 0471 QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder")); 0472 QScopedPointer<QDialog> dlg(new QDialog(static_cast<QWidget *>(doc->parent()))); 0473 QScopedPointer<KFileWidget> fileWidget(new KFileWidget(QUrl::fromLocalFile(clipFolder), dlg.data())); 0474 auto *layout = new QVBoxLayout; 0475 layout->addWidget(fileWidget.data()); 0476 fileWidget->setCustomWidget(f); 0477 fileWidget->okButton()->show(); 0478 fileWidget->cancelButton()->show(); 0479 QObject::connect(fileWidget->okButton(), &QPushButton::clicked, fileWidget.data(), &KFileWidget::slotOk); 0480 QObject::connect(fileWidget.data(), &KFileWidget::accepted, fileWidget.data(), &KFileWidget::accept); 0481 QObject::connect(fileWidget.data(), &KFileWidget::accepted, dlg.data(), &QDialog::accept); 0482 QObject::connect(fileWidget->cancelButton(), &QPushButton::clicked, dlg.data(), &QDialog::reject); 0483 dlg->setLayout(layout); 0484 fileWidget->setFilter(dialogFilter); 0485 fileWidget->setMode(KFile::Files | KFile::ExistingOnly | KFile::LocalOnly | KFile::Directory); 0486 KSharedConfig::Ptr conf = KSharedConfig::openConfig(); 0487 QWindow *handle = dlg->windowHandle(); 0488 if ((handle != nullptr) && conf->hasGroup("FileDialogSize")) { 0489 KWindowConfig::restoreWindowSize(handle, conf->group("FileDialogSize")); 0490 dlg->resize(handle->size()); 0491 } 0492 int result = dlg->exec(); 0493 if (result == QDialog::Accepted) { 0494 KdenliveSettings::setAutoimagesequence(b->isChecked()); 0495 KdenliveSettings::setIgnoresubdirstructure(bf->isChecked()); 0496 list = fileWidget->selectedUrls(); 0497 if (!list.isEmpty()) { 0498 KRecentDirs::add(QStringLiteral(":KdenliveClipFolder"), list.constFirst().adjusted(QUrl::RemoveFilename).toLocalFile()); 0499 } 0500 if (KdenliveSettings::autoimagesequence() && list.count() >= 1) { 0501 // Check for image sequence 0502 const QUrl &url = list.at(0); 0503 QString fileName = url.fileName().section(QLatin1Char('.'), 0, -2); 0504 if (!fileName.isEmpty() && fileName.at(fileName.size() - 1).isDigit()) { 0505 KFileItem item(url); 0506 if (item.mimetype().startsWith(QLatin1String("image"))) { 0507 // import as sequence if we found more than one image in the sequence 0508 QStringList patternlist; 0509 QString pattern = SlideshowClip::selectedPath(url, false, QString(), &patternlist); 0510 qCDebug(KDENLIVE_LOG) << " / // IMPORT PATTERN: " << pattern << " COUNT: " << patternlist.count(); 0511 int count = patternlist.count(); 0512 if (count > 1) { 0513 // get image sequence base name 0514 while (fileName.size() > 0 && fileName.at(fileName.size() - 1).isDigit()) { 0515 fileName.chop(1); 0516 } 0517 0518 QString duration = doc->timecode().reformatSeparators(KdenliveSettings::sequence_duration()); 0519 std::unordered_map<QString, QString> properties; 0520 properties[QStringLiteral("ttl")] = QString::number(doc->getFramePos(duration)); 0521 properties[QStringLiteral("loop")] = QString::number(0); 0522 properties[QStringLiteral("crop")] = QString::number(0); 0523 properties[QStringLiteral("fade")] = QString::number(0); 0524 properties[QStringLiteral("luma_duration")] = 0525 QString::number(doc->getFramePos(doc->timecode().getTimecodeFromFrames(int(ceil(doc->timecode().fps()))))); 0526 int frame_duration = doc->getFramePos(duration) * count; 0527 ClipCreator::createSlideshowClip(pattern, frame_duration, fileName, parentFolder, properties, model); 0528 return; 0529 } 0530 } 0531 } 0532 } 0533 } 0534 qDebug() << "/////////// found list" << list; 0535 KConfigGroup group = conf->group("FileDialogSize"); 0536 if (handle) { 0537 KWindowConfig::saveWindowSize(handle, group); 0538 } 0539 Fun undo = []() { return true; }; 0540 Fun redo = []() { return true; }; 0541 const QString id = ClipCreator::createClipsFromList(list, true, parentFolder, model, undo, redo); 0542 0543 // We reset the state of the "don't ask again" for the question about removable devices 0544 KMessageBox::enableMessage(QStringLiteral("removable")); 0545 if (!id.isEmpty()) { 0546 pCore->pushUndo(undo, redo, i18np("Add clip", "Add clips", list.size())); 0547 } 0548 } 0549 0550 void ClipCreationDialog::clipWidget(QDockWidget *m_DockClipWidget) 0551 { 0552 QString clipFolder = KRecentDirs::dir(QStringLiteral(":KdenliveClipFolder")); 0553 KFileWidget *fileWidget = new KFileWidget(QUrl::fromLocalFile(clipFolder), m_DockClipWidget); 0554 fileWidget->setMode(KFile::Files | KFile::ExistingOnly | KFile::LocalOnly | KFile::Directory); 0555 QString allExtensions = getExtensions().join(QLatin1Char(' ')); 0556 QString dialogFilter = allExtensions + QLatin1Char('|') + i18n("All Supported Files") + QStringLiteral("\n*|") + i18n("All Files"); 0557 0558 QPushButton *importseq = new QPushButton(i18n("Import image sequence")); 0559 // Make importseq checkable so that we can differentiate between a double click in filewidget and a click on the pushbutton 0560 importseq->setCheckable(true); 0561 QCheckBox *b = new QCheckBox(i18n("Ignore subfolder structure")); 0562 b->setChecked(KdenliveSettings::ignoresubdirstructure()); 0563 QFrame *f = new QFrame(); 0564 f->setFrameShape(QFrame::NoFrame); 0565 auto *l = new QHBoxLayout; 0566 l->addWidget(b); 0567 l->addStretch(5); 0568 l->addWidget(importseq); 0569 f->setLayout(l); 0570 fileWidget->setCustomWidget(f); 0571 // Required to only add file on double click and not on single click 0572 fileWidget->setOperationMode(KFileWidget::Saving); 0573 QObject::connect(fileWidget, &KFileWidget::accepted, fileWidget, [fileWidget, importseq]() { 0574 if (importseq->isChecked()) { 0575 // We are importing an image sequence, abort 0576 return; 0577 } 0578 fileWidget->accept(); 0579 QList<QUrl> urls = fileWidget->selectedUrls(); 0580 pCore->bin()->droppedUrls(urls); 0581 }); 0582 fileWidget->setFilter(dialogFilter); 0583 QObject::connect(b, &QCheckBox::toggled, [](bool checked) { KdenliveSettings::setIgnoresubdirstructure(checked); }); 0584 QObject::connect(importseq, &QPushButton::clicked, fileWidget, [=] { 0585 fileWidget->slotOk(); 0586 emit fileWidget->accepted(); 0587 fileWidget->accept(); 0588 QUrl url = fileWidget->selectedUrl(); 0589 QStringList patternlist; 0590 QString pattern = SlideshowClip::selectedPath(url, false, QString(), &patternlist); 0591 int count = patternlist.size(); 0592 0593 QString fileName = url.fileName().section(QLatin1Char('.'), 0, -2); 0594 importseq->setChecked(false); 0595 if (count >= 1) { 0596 while (fileName.size() > 0 && fileName.at(fileName.size() - 1).isDigit()) { 0597 fileName.chop(1); 0598 } 0599 QString parentFolder = "-1"; 0600 KdenliveDoc *doc = pCore->currentDoc(); 0601 QString duration = doc->timecode().reformatSeparators(KdenliveSettings::sequence_duration()); 0602 std::unordered_map<QString, QString> properties; 0603 properties[QStringLiteral("ttl")] = QString::number(doc->getFramePos(duration)); 0604 properties[QStringLiteral("loop")] = QString::number(0); 0605 properties[QStringLiteral("crop")] = QString::number(0); 0606 properties[QStringLiteral("fade")] = QString::number(0); 0607 properties[QStringLiteral("luma_duration")] = 0608 QString::number(doc->getFramePos(doc->timecode().getTimecodeFromFrames(int(ceil(doc->timecode().fps()))))); 0609 int frame_duration = doc->getFramePos(duration) * count; 0610 ClipCreator::createSlideshowClip(pattern, frame_duration, fileName, parentFolder, properties, pCore->projectItemModel()); 0611 return; 0612 } 0613 }); 0614 m_DockClipWidget->setWidget(fileWidget); 0615 }