X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fclipmanager.cpp;h=a706e5ade50076606625b0fed4842a2756c9b062;hb=71e41ad0c197e2e2eb033dff7800427f64d3428f;hp=608087f4470c1c2d11186786051f4921324c4af3;hpb=b4fef878fdcf0cfb2fb384a59fb2d1888f1d5f4e;p=kdenlive diff --git a/src/clipmanager.cpp b/src/clipmanager.cpp index 608087f4..a706e5ad 100644 --- a/src/clipmanager.cpp +++ b/src/clipmanager.cpp @@ -26,6 +26,7 @@ #include "abstractclipitem.h" #include "abstractgroupitem.h" #include "titledocument.h" +#include "subprojectitem.h" #include "kthumb.h" #include @@ -35,12 +36,19 @@ #include #include #include +#include + +#include +#include +#include +#include #include #include #include + ClipManager::ClipManager(KdenliveDoc *doc) : QObject(), m_audioThumbsQueue(), @@ -54,7 +62,9 @@ ClipManager::ClipManager(KdenliveDoc *doc) : m_modifiedTimer.setInterval(1500); connect(&m_fileWatcher, SIGNAL(dirty(const QString &)), this, SLOT(slotClipModified(const QString &))); connect(&m_fileWatcher, SIGNAL(deleted(const QString &)), this, SLOT(slotClipMissing(const QString &))); - connect(&m_fileWatcher, SIGNAL(created(const QString &)), this, SLOT(slotClipAvailable(const QString &))); + + // Seems like a dirty signal is emitted anyways when a watched file is created, so don't react twice. + //connect(&m_fileWatcher, SIGNAL(created(const QString &)), this, SLOT(slotClipAvailable(const QString &))); connect(&m_modifiedTimer, SIGNAL(timeout()), this, SLOT(slotProcessModifiedClips())); #if KDE_IS_VERSION(4,5,0) @@ -158,19 +168,42 @@ void ClipManager::slotGetThumbs() QMap::const_iterator i; int max; int done = 0; + int thumbType = 0; // 0 = timeline thumb, 1 = project clip zone thumb, 2 = clip properties thumb + while (!m_requestedThumbs.isEmpty() && !m_abortThumb) { m_thumbsMutex.lock(); i = m_requestedThumbs.constBegin(); m_processingThumbId = i.key(); QList values = m_requestedThumbs.values(m_processingThumbId); m_requestedThumbs.remove(m_processingThumbId); + if (m_processingThumbId.startsWith("?")) { + // if id starts with ?, it means the request comes from a clip property widget + thumbType = 2; + m_processingThumbId.remove(0, 1); + } + if (m_processingThumbId.startsWith("#")) { + // if id starts with #, it means the request comes from project tree + thumbType = 1; + m_processingThumbId.remove(0, 1); + } m_thumbsMutex.unlock(); qSort(values); DocClipBase *clip = getClipById(m_processingThumbId); if (!clip) continue; max = m_requestedThumbs.size() + values.count(); + int pos; while (!values.isEmpty() && clip->thumbProducer() && !m_abortThumb) { - clip->thumbProducer()->getThumb(values.takeFirst()); + pos = values.takeFirst(); + switch (thumbType) { + case 1: + clip->thumbProducer()->getGenericThumb(pos, SubProjectItem::itemDefaultHeight(), thumbType); + break; + case 2: + clip->thumbProducer()->getGenericThumb(pos, 180, thumbType); + break; + default: + clip->thumbProducer()->getThumb(pos); + } done++; if (max > 3) emit displayMessage(i18n("Loading thumbnails"), 100 * done / max); } @@ -462,15 +495,47 @@ void ClipManager::resetProducersList(const QList prods, bool d emit checkAllClips(displayRatioChanged, fpsChanged, brokenClips); } -void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, const QString &groupId, const QString &comment) +void ClipManager::slotAddClip(KIO::Job *job, const KUrl &, const KUrl &dst) +{ + KIO::MetaData meta = job->metaData(); + QMap data; + data.insert("group", meta.value("group")); + data.insert("groupid", meta.value("groupid")); + data.insert("comment", meta.value("comment")); + kDebug()<<"Finished copying: "< data) { QUndoCommand *addClips = new QUndoCommand(); + // Update list of removable volumes + //TODO: update only when new volume is plugged / unplugged + listRemovableVolumes(); foreach(const KUrl & file, urls) { if (QFile::exists(file.path())) {//KIO::NetAccess::exists(file, KIO::NetAccess::SourceSide, NULL)) { - if (!getClipByResource(file.path()).empty()) { + if (!data.contains("bypassDuplicate") && !getClipByResource(file.path()).empty()) { if (KMessageBox::warningContinueCancel(kapp->activeWindow(), i18n("Clip %1
already exists in project, what do you want to do?", file.path()), i18n("Clip already exists")) == KMessageBox::Cancel) continue; } + if (isOnRemovableDevice(file)) { + int answer = KMessageBox::warningYesNoCancel(kapp->activeWindow(), i18n("Clip %1
is on a removable device, will not be available when device is unplugged", file.path()), i18n("File on a Removable Device"), KGuiItem(i18n("Copy file to project folder")), KGuiItem(i18n("Continue")), KStandardGuiItem::cancel(), QString("copyFilesToProjectFolder")); + if (answer == KMessageBox::Cancel) continue; + else if (answer == KMessageBox::Yes) { + // Copy files to project folder + QString sourcesFolder = m_doc->projectFolder().path(KUrl::AddTrailingSlash) + "clips/"; + KIO::NetAccess::mkdir(sourcesFolder, kapp->activeWindow()); + //KIO::filesize_t m_requestedSize; + KIO::CopyJob *copyjob = KIO::copy (file, KUrl(sourcesFolder)); + //TODO: for some reason, passing metadata does not work... + copyjob->addMetaData("group", data.value("group")); + copyjob->addMetaData("groupId", data.value("groupId")); + copyjob->addMetaData("comment", data.value("comment")); + copyjob->ui()->setWindow(kapp->activeWindow()); + connect(copyjob, SIGNAL(copyingDone(KIO::Job *, const KUrl &, const KUrl &, time_t, bool, bool)), this, SLOT(slotAddClip(KIO::Job *, const KUrl &, const KUrl &))); + continue; + } + } kDebug() << "Adding clip: " << file.path(); QDomDocument doc; QDomElement prod = doc.createElement("producer"); @@ -478,11 +543,14 @@ void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, c prod.setAttribute("resource", file.path()); uint id = m_clipIdCounter++; prod.setAttribute("id", QString::number(id)); - if (!comment.isEmpty()) prod.setAttribute("description", comment); - if (!group.isEmpty()) { - prod.setAttribute("groupname", group); - prod.setAttribute("groupid", groupId); + if (data.contains("comment")) prod.setAttribute("description", data.value("comment")); + if (data.contains("group")) { + prod.setAttribute("groupname", data.value("group")); + prod.setAttribute("groupid", data.value("groupId")); } + if (data.contains("video_index")) prod.setAttribute("video_index", data.value("video_index")); + if (data.contains("audio_index")) prod.setAttribute("audio_index", data.value("audio_index")); + KMimeType::Ptr type = KMimeType::findByUrl(file); if (type->name().startsWith("image/")) { prod.setAttribute("type", (int) IMAGE); @@ -494,7 +562,7 @@ void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, c KFileMetaInfo metaInfo(file.path(), QString("image/jpeg"), KFileMetaInfo::TechnicalInfo); const QHash metaInfoItems = metaInfo.items(); foreach(const KFileMetaInfoItem & metaInfoItem, metaInfoItems) { - prod.setAttribute("meta.attr." + metaInfoItem.name().section("#", 1), metaInfoItem.value().toString()); + prod.setAttribute("meta.attr." + metaInfoItem.name().section('#', 1), metaInfoItem.value().toString()); } } } else if (type->is("application/x-kdenlivetitle")) { @@ -539,9 +607,9 @@ void ClipManager::slotAddClipList(const KUrl::List urls, const QString &group, c } } -void ClipManager::slotAddClipFile(const KUrl &url, const QString &group, const QString &groupId, const QString &comment) +void ClipManager::slotAddClipFile(const KUrl &url, QMap data) { - slotAddClipList(KUrl::List(url), group, groupId, comment); + slotAddClipList(KUrl::List(url), data); } void ClipManager::slotAddXmlClipFile(const QString &name, const QDomElement &xml, const QString &group, const QString &groupId) @@ -789,3 +857,99 @@ int ClipManager::clipsCount() const } +void ClipManager::listRemovableVolumes() +{ + QList volumes; + m_removableVolumes.clear(); + + QList devices = Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess); + + foreach(const Solid::Device &accessDevice, devices) + { + // check for StorageAccess + if (!accessDevice.is()) + continue; + + const Solid::StorageAccess *access = accessDevice.as(); + + if (!access->isAccessible()) + continue; + + // check for StorageDrive + Solid::Device driveDevice; + for (Solid::Device currentDevice = accessDevice; currentDevice.isValid(); currentDevice = currentDevice.parent()) + { + if (currentDevice.is()) + { + driveDevice = currentDevice; + break; + } + } + if (!driveDevice.isValid()) + continue; + + Solid::StorageDrive *drive = driveDevice.as(); + if (!drive->isRemovable()) continue; + + // check for StorageVolume + Solid::Device volumeDevice; + for (Solid::Device currentDevice = accessDevice; currentDevice.isValid(); currentDevice = currentDevice.parent()) + { + if (currentDevice.is()) + { + volumeDevice = currentDevice; + break; + } + } + if (!volumeDevice.isValid()) + continue; + + Solid::StorageVolume *volume = volumeDevice.as(); + + SolidVolumeInfo info; + info.path = access->filePath(); + info.isMounted = access->isAccessible(); + if (!info.path.isEmpty() && !info.path.endsWith('/')) + info.path += '/'; + info.uuid = volume->uuid(); + info.label = volume->label(); + info.isRemovable = drive->isRemovable(); + m_removableVolumes << info; + } +} + +bool ClipManager::isOnRemovableDevice(const KUrl &url) +{ + SolidVolumeInfo volume; + QString path = url.path(KUrl::RemoveTrailingSlash); + int volumeMatch = 0; + + //FIXME: Network shares! Here we get only the volume of the mount path... + // This is probably not really clean. But Solid does not help us. + foreach (const SolidVolumeInfo &v, m_removableVolumes) + { + if (v.isMounted && !v.path.isEmpty() && path.startsWith(v.path)) + { + int length = v.path.length(); + if (length > volumeMatch) + { + volumeMatch = v.path.length(); + volume = v; + } + } + } + + return volumeMatch; +} + +void ClipManager::projectTreeThumbReady(const QString &id, int frame, QImage img, int type) +{ + switch (type) { + case 2: + emit gotClipPropertyThumbnail(id, img); + break; + default: + emit thumbReady(id, frame, img); + } +} +