X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fprojectsettings.cpp;h=75f5956cd39a22d71986fb0d50caddbead7118b8;hb=c3302003093710ee247ad84c0fe2ef3c579d417f;hp=f7a71abb694adb1d50e1908cf7306c1c893b0387;hpb=6d2e3e28674988bd0911e8a6810ab50c6c25821d;p=kdenlive diff --git a/src/projectsettings.cpp b/src/projectsettings.cpp index f7a71abb..75f5956c 100644 --- a/src/projectsettings.cpp +++ b/src/projectsettings.cpp @@ -22,22 +22,25 @@ #include "profilesdialog.h" #include "docclipbase.h" #include "titlewidget.h" +#include "effectslist.h" #include #include #include #include #include +#include +#include #include #include -ProjectSettings::ProjectSettings(ProjectList *projectlist, int videotracks, int audiotracks, const QString projectPath, bool readOnlyTracks, bool savedProject, QWidget * parent) : - QDialog(parent), m_savedProject(savedProject), m_projectList(projectlist) +ProjectSettings::ProjectSettings(ProjectList *projectlist, QMap metadata, const QStringList &lumas, int videotracks, int audiotracks, const QString &projectPath, bool readOnlyTracks, bool savedProject, QWidget * parent) : + QDialog(parent), m_savedProject(savedProject), m_projectList(projectlist), m_lumas(lumas) { setupUi(this); - - list_search->setListWidget(files_list); + + list_search->setTreeWidget(files_list); QMap profilesInfo = ProfilesDialog::getProfilesInfo(); QMapIterator i(profilesInfo); @@ -49,7 +52,7 @@ ProjectSettings::ProjectSettings(ProjectList *projectlist, int videotracks, int project_folder->setUrl(KUrl(projectPath)); QString currentProf = KdenliveSettings::current_profile(); - for (int i = 0; i < profiles_list->count(); i++) { + for (int i = 0; i < profiles_list->count(); ++i) { if (profiles_list->itemData(i).toString() == currentProf) { profiles_list->setCurrentIndex(i); break; @@ -62,35 +65,155 @@ ProjectSettings::ProjectSettings(ProjectList *projectlist, int videotracks, int video_thumbs->setChecked(KdenliveSettings::videothumbnails()); audio_tracks->setValue(audiotracks); video_tracks->setValue(videotracks); + connect(generate_proxy, SIGNAL(toggled(bool)), proxy_minsize, SLOT(setEnabled(bool))); + connect(generate_imageproxy, SIGNAL(toggled(bool)), proxy_imageminsize, SLOT(setEnabled(bool))); + QString proxyparameters; + QString proxyextension; + if (projectlist) { + enable_proxy->setChecked(projectlist->getDocumentProperty("enableproxy").toInt()); + generate_proxy->setChecked(projectlist->getDocumentProperty("generateproxy").toInt()); + proxy_minsize->setValue(projectlist->getDocumentProperty("proxyminsize").toInt()); + proxyparameters = projectlist->getDocumentProperty("proxyparams"); + generate_imageproxy->setChecked(projectlist->getDocumentProperty("generateimageproxy").toInt()); + proxy_imageminsize->setValue(projectlist->getDocumentProperty("proxyimageminsize").toInt()); + proxyextension = projectlist->getDocumentProperty("proxyextension"); + } + else { + enable_proxy->setChecked(KdenliveSettings::enableproxy()); + generate_proxy->setChecked(KdenliveSettings::generateproxy()); + proxy_minsize->setValue(KdenliveSettings::proxyminsize()); + proxyparameters = KdenliveSettings::proxyparams(); + generate_imageproxy->setChecked(KdenliveSettings::generateimageproxy()); + proxy_imageminsize->setValue(KdenliveSettings::proxyimageminsize()); + proxyextension = KdenliveSettings::proxyextension(); + + } + + proxy_minsize->setEnabled(generate_proxy->isChecked()); + proxy_imageminsize->setEnabled(generate_imageproxy->isChecked()); + + + // load proxy profiles + KConfig conf("encodingprofiles.rc", KConfig::CascadeConfig, "appdata"); + KConfigGroup group(&conf, "proxy"); + QMap values = group.entryMap(); + QMapIterator k(values); + int ix = -1; + while (k.hasNext()) { + k.next(); + if (!k.key().isEmpty()) { + QString params = k.value().section(';', 0, 0); + QString extension = k.value().section(';', 1, 1); + if (ix == -1 && ((params == proxyparameters && extension == proxyextension) || (proxyparameters.isEmpty() || proxyextension.isEmpty()))) { + // this is the current profile + ix = proxy_profile->count(); + } + proxy_profile->addItem(k.key(), k.value()); + } + } + if (ix == -1) { + // Current project proxy settings not found + ix = proxy_profile->count(); + proxy_profile->addItem(i18n("Current Settings"), QString(proxyparameters + ';' + proxyextension)); + } + proxy_profile->setCurrentIndex(ix); + slotUpdateProxyParams(); + + // Proxy GUI stuff + proxy_showprofileinfo->setIcon(KIcon("help-about")); + connect(proxy_profile, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateProxyParams())); + proxyparams->setVisible(false); + proxyparams->setMaximumHeight(QFontMetrics(font()).lineSpacing() * 5); + connect(proxy_showprofileinfo, SIGNAL(clicked(bool)), proxyparams, SLOT(setVisible(bool))); + if (readOnlyTracks) { video_tracks->setEnabled(false); audio_tracks->setEnabled(false); } + + + // Metadata list + QTreeWidgetItem *item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Title")); + item->setData(0, Qt::UserRole, QString("meta.attr.title.markup")); + if (metadata.contains("meta.attr.title.markup")) { + item->setText(1, metadata.value("meta.attr.title.markup")); + metadata.remove("meta.attr.title.markup"); + } + item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); + item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Author")); + item->setData(0, Qt::UserRole, QString("meta.attr.author.markup")); + if (metadata.contains("meta.attr.author.markup")) { + item->setText(1, metadata.value("meta.attr.author.markup")); + metadata.remove("meta.attr.author.markup"); + } + else if (metadata.contains("meta.attr.artist.markup")) { + item->setText(0, i18n("Artist")); + item->setData(0, Qt::UserRole, QString("meta.attr.artist.markup")); + item->setText(1, metadata.value("meta.attr.artist.markup")); + metadata.remove("meta.attr.artist.markup"); + } + item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); + item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Copyright")); + item->setData(0, Qt::UserRole, QString("meta.attr.copyright.markup")); + if (metadata.contains("meta.attr.copyright.markup")) { + item->setText(1, metadata.value("meta.attr.copyright.markup")); + metadata.remove("meta.attr.copyright.markup"); + } + item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); + item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("Year")); + item->setData(0, Qt::UserRole, QString("meta.attr.year.markup")); + if (metadata.contains("meta.attr.year.markup")) { + item->setText(1, metadata.value("meta.attr.year.markup")); + metadata.remove("meta.attr.year.markup"); + } + else if (metadata.contains("meta.attr.date.markup")) { + item->setText(0, i18n("Date")); + item->setData(0, Qt::UserRole, QString("meta.attr.date.markup")); + item->setText(1, metadata.value("meta.attr.date.markup")); + metadata.remove("meta.attr.date.markup"); + } + item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); + + QMap::const_iterator meta = metadata.constBegin(); + while (meta != metadata.constEnd()) { + item = new QTreeWidgetItem(metadata_list, QStringList() << meta.key().section('.', 2,2)); + item->setData(0, Qt::UserRole, meta.key()); + item->setText(1, meta.value()); + item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); + ++meta; + } + + connect(add_metadata, SIGNAL(clicked()), this, SLOT(slotAddMetadataField())); + connect(delete_metadata, SIGNAL(clicked()), this, SLOT(slotDeleteMetadataField())); + add_metadata->setIcon(KIcon("list-add")); + delete_metadata->setIcon(KIcon("list-remove")); + slotUpdateDisplay(); if (m_projectList != NULL) { slotUpdateFiles(); connect(clear_cache, SIGNAL(clicked()), this, SLOT(slotClearCache())); connect(delete_unused, SIGNAL(clicked()), this, SLOT(slotDeleteUnused())); + connect(delete_proxies, SIGNAL(clicked()), this, SLOT(slotDeleteProxies())); } else tabWidget->widget(1)->setEnabled(false); connect(profiles_list, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateDisplay())); - connect(project_folder, SIGNAL(textChanged(const QString &)), this, SLOT(slotUpdateButton(const QString &))); + connect(project_folder, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateButton(QString))); + connect(button_export, SIGNAL(clicked()), this, SLOT(slotExportToText())); } void ProjectSettings::slotDeleteUnused() { QStringList toDelete; QList list = m_projectList->documentClipList(); - for (int i = 0; i < list.count(); i++) { + for (int i = 0; i < list.count(); ++i) { DocClipBase *clip = list.at(i); if (clip->numReferences() == 0 && clip->clipType() != SLIDESHOW) { KUrl url = clip->fileURL(); - if (!url.isEmpty()) toDelete << url.path(); + if (!url.isEmpty() && !toDelete.contains(url.path())) toDelete << url.path(); } } - toDelete.removeDuplicates(); // make sure our urls are not used in another clip - for (int i = 0; i < list.count(); i++) { + for (int i = 0; i < list.count(); ++i) { DocClipBase *clip = list.at(i); if (clip->numReferences() > 0) { KUrl url = clip->fileURL(); @@ -119,12 +242,28 @@ void ProjectSettings::slotClearCache() slotUpdateFiles(true); } +void ProjectSettings::slotDeleteProxies() +{ + if (KMessageBox::warningContinueCancel(this, i18n("Deleting proxy clips will disable proxies for this project.")) != KMessageBox::Continue) return; + buttonBox->setEnabled(false); + enable_proxy->setChecked(false); + emit disableProxies(); + KIO::NetAccess::del(KUrl(project_folder->url().path(KUrl::AddTrailingSlash) + "proxy/"), this); + KStandardDirs::makeDir(project_folder->url().path(KUrl::AddTrailingSlash) + "proxy/"); + buttonBox->setEnabled(true); + slotUpdateFiles(true); +} + void ProjectSettings::slotUpdateFiles(bool cacheOnly) { - KIO::DirectorySizeJob * job = KIO::directorySize(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/"); + KIO::DirectorySizeJob *job = KIO::directorySize(project_folder->url().path(KUrl::AddTrailingSlash) + "thumbs/"); job->exec(); thumbs_count->setText(QString::number(job->totalFiles())); thumbs_size->setText(KIO::convertSize(job->totalSize())); + job = KIO::directorySize(project_folder->url().path(KUrl::AddTrailingSlash) + "proxy/"); + job->exec(); + proxy_count->setText(QString::number(job->totalFiles())); + proxy_size->setText(KIO::convertSize(job->totalSize())); delete job; if (cacheOnly) return; int unused = 0; @@ -133,33 +272,88 @@ void ProjectSettings::slotUpdateFiles(bool cacheOnly) KIO::filesize_t unUsedSize = 0; QList list = m_projectList->documentClipList(); files_list->clear(); - + // List all files that are used in the project. That also means: - // images included in slideshow, titles - // TODO: images used in luma transitions, files used in playlist clips, files used for LADSPA effects - - QStringList allFiles; - for (int i = 0; i < list.count(); i++) { + // images included in slideshow and titles, files in playlist clips + // TODO: images used in luma transitions? + + // Setup categories + QTreeWidgetItem *videos = new QTreeWidgetItem(files_list, QStringList() << i18n("Video clips")); + videos->setIcon(0, KIcon("video-x-generic")); + videos->setExpanded(true); + QTreeWidgetItem *sounds = new QTreeWidgetItem(files_list, QStringList() << i18n("Audio clips")); + sounds->setIcon(0, KIcon("audio-x-generic")); + sounds->setExpanded(true); + QTreeWidgetItem *images = new QTreeWidgetItem(files_list, QStringList() << i18n("Image clips")); + images->setIcon(0, KIcon("image-x-generic")); + images->setExpanded(true); + QTreeWidgetItem *slideshows = new QTreeWidgetItem(files_list, QStringList() << i18n("Slideshow clips")); + slideshows->setIcon(0, KIcon("image-x-generic")); + slideshows->setExpanded(true); + QTreeWidgetItem *texts = new QTreeWidgetItem(files_list, QStringList() << i18n("Text clips")); + texts->setIcon(0, KIcon("text-plain")); + texts->setExpanded(true); + QTreeWidgetItem *playlists = new QTreeWidgetItem(files_list, QStringList() << i18n("Playlist clips")); + playlists->setIcon(0, KIcon("video-mlt-playlist")); + playlists->setExpanded(true); + QTreeWidgetItem *others = new QTreeWidgetItem(files_list, QStringList() << i18n("Other clips")); + others->setIcon(0, KIcon("unknown")); + others->setExpanded(true); + int count = 0; + QStringList allFonts; + foreach(const QString & file, m_lumas) { + count++; + new QTreeWidgetItem(images, QStringList() << file); + } + + for (int i = 0; i < list.count(); ++i) { DocClipBase *clip = list.at(i); - if (clip->clipType() == SLIDESHOW) { - // special case, list all images - QString path = clip->fileURL().directory(); - QString ext = clip->fileURL().path().section('.', -1); - QDir dir(path); - QStringList filters; - filters << "*." + ext; - dir.setNameFilters(filters); - QStringList result = dir.entryList(QDir::Files); - for (int j = 0; j < result.count(); j++) { - allFiles.append(path + result.at(j)); - } - } - else if (!clip->fileURL().isEmpty()) allFiles.append(clip->fileURL().path()); - if (clip->clipType() == TEXT) { - QStringList images = TitleWidget::extractImageList(clip->getProperty("xmldata")); - allFiles << images; - } - + if (clip->clipType() == SLIDESHOW) { + QStringList subfiles = extractSlideshowUrls(clip->fileURL()); + foreach(const QString & file, subfiles) { + count++; + new QTreeWidgetItem(slideshows, QStringList() << file); + } + } else if (!clip->fileURL().isEmpty()) { + //allFiles.append(clip->fileURL().path()); + switch (clip->clipType()) { + case TEXT: + new QTreeWidgetItem(texts, QStringList() << clip->fileURL().path()); + break; + case AUDIO: + new QTreeWidgetItem(sounds, QStringList() << clip->fileURL().path()); + break; + case IMAGE: + new QTreeWidgetItem(images, QStringList() << clip->fileURL().path()); + break; + case PLAYLIST: + new QTreeWidgetItem(playlists, QStringList() << clip->fileURL().path()); + break; + case UNKNOWN: + new QTreeWidgetItem(others, QStringList() << clip->fileURL().path()); + break; + default: + new QTreeWidgetItem(videos, QStringList() << clip->fileURL().path()); + break; + } + count++; + } + if (clip->clipType() == TEXT) { + QStringList imagefiles = TitleWidget::extractImageList(clip->getProperty("xmldata")); + QStringList fonts = TitleWidget::extractFontList(clip->getProperty("xmldata")); + foreach(const QString & file, imagefiles) { + count++; + new QTreeWidgetItem(images, QStringList() << file); + } + allFonts << fonts; + } else if (clip->clipType() == PLAYLIST) { + QStringList files = extractPlaylistUrls(clip->fileURL().path()); + foreach(const QString & file, files) { + count++; + new QTreeWidgetItem(others, QStringList() << file); + } + } + if (clip->numReferences() == 0) { unused++; unUsedSize += clip->fileSize(); @@ -168,9 +362,19 @@ void ProjectSettings::slotUpdateFiles(bool cacheOnly) usedSize += clip->fileSize(); } } - allFiles.removeDuplicates(); - files_count->setText(QString::number(allFiles.count())); - files_list->addItems(allFiles); + allFonts.removeDuplicates(); + // Hide unused categories + for (int i = 0; i < files_list->topLevelItemCount(); ++i) { + if (files_list->topLevelItem(i)->childCount() == 0) { + files_list->topLevelItem(i)->setHidden(true); + } + } + files_count->setText(QString::number(count)); + fonts_list->addItems(allFonts); + if (allFonts.isEmpty()) { + fonts_list->setHidden(true); + label_fonts->setHidden(true); + } used_count->setText(QString::number(used)); used_size->setText(KIO::convertSize(usedSize)); unused_count->setText(QString::number(unused)); @@ -187,14 +391,20 @@ void ProjectSettings::accept() void ProjectSettings::slotUpdateDisplay() { + QLocale locale; QString currentProfile = profiles_list->itemData(profiles_list->currentIndex()).toString(); QMap< QString, QString > values = ProfilesDialog::getSettingsFromFile(currentProfile); p_size->setText(values.value("width") + 'x' + values.value("height")); p_fps->setText(values.value("frame_rate_num") + '/' + values.value("frame_rate_den")); p_aspect->setText(values.value("sample_aspect_num") + '/' + values.value("sample_aspect_den")); p_display->setText(values.value("display_aspect_num") + '/' + values.value("display_aspect_den")); - if (values.value("progressive").toInt() == 0) p_progressive->setText(i18n("Interlaced")); - else p_progressive->setText(i18n("Progressive")); + if (values.value("progressive").toInt() == 0) { + p_progressive->setText(i18n("Interlaced (%1 fields per second)", + locale.toString((double)2 * values.value("frame_rate_num").toInt() / values.value("frame_rate_den").toInt(), 'f', 2))); + } else { + p_progressive->setText(i18n("Progressive")); + } + p_colorspace->setText(ProfilesDialog::getColorspaceDescription(values.value("colorspace").toInt())); } void ProjectSettings::slotUpdateButton(const QString &path) @@ -216,7 +426,7 @@ KUrl ProjectSettings::selectedFolder() const return project_folder->url(); } -QPoint ProjectSettings::tracks() +QPoint ProjectSettings::tracks() const { QPoint p; p.setX(video_tracks->value()); @@ -234,6 +444,195 @@ bool ProjectSettings::enableAudioThumbs() const return audio_thumbs->isChecked(); } +bool ProjectSettings::useProxy() const +{ + return enable_proxy->isChecked(); +} + +bool ProjectSettings::generateProxy() const +{ + return generate_proxy->isChecked(); +} + +bool ProjectSettings::generateImageProxy() const +{ + return generate_imageproxy->isChecked(); +} + +int ProjectSettings::proxyMinSize() const +{ + return proxy_minsize->value(); +} + +int ProjectSettings::proxyImageMinSize() const +{ + return proxy_imageminsize->value(); +} + +QString ProjectSettings::proxyParams() const +{ + QString params = proxy_profile->itemData(proxy_profile->currentIndex()).toString(); + return params.section(';', 0, 0); +} + +QString ProjectSettings::proxyExtension() const +{ + QString params = proxy_profile->itemData(proxy_profile->currentIndex()).toString(); + return params.section(';', 1, 1); +} + +//static +QStringList ProjectSettings::extractPlaylistUrls(const QString &path) +{ + QStringList urls; + QDomDocument doc; + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) + return urls; + if (!doc.setContent(&file)) { + file.close(); + return urls; + } + file.close(); + QString root = doc.documentElement().attribute("root"); + if (!root.isEmpty() && !root.endsWith('/')) root.append('/'); + QDomNodeList files = doc.elementsByTagName("producer"); + for (int i = 0; i < files.count(); ++i) { + QDomElement e = files.at(i).toElement(); + QString type = EffectsList::property(e, "mlt_service"); + if (type != "colour") { + QString url = EffectsList::property(e, "resource"); + if (type == "framebuffer") { + url = url.section('?', 0, 0); + } + if (!url.isEmpty()) { + if (!url.startsWith('/')) url.prepend(root); + if (url.section('.', 0, -2).endsWith("/.all")) { + // slideshow clip, extract image urls + urls << extractSlideshowUrls(KUrl(url)); + } else urls << url; + if (url.endsWith(".mlt") || url.endsWith(".kdenlive")) { + //TODO: Do something to avoid infinite loops if 2 files reference themselves... + urls << extractPlaylistUrls(url); + } + } + } + } + + // luma files for transitions + files = doc.elementsByTagName("transition"); + for (int i = 0; i < files.count(); ++i) { + QDomElement e = files.at(i).toElement(); + QString url = EffectsList::property(e, "luma"); + if (!url.isEmpty()) { + if (!url.startsWith('/')) url.prepend(root); + urls << url; + } + } + + return urls; +} + + +//static +QStringList ProjectSettings::extractSlideshowUrls(const KUrl &url) +{ + QStringList urls; + QString path = url.directory(KUrl::AppendTrailingSlash); + QString ext = url.path().section('.', -1); + QDir dir(path); + if (url.path().contains(".all.")) { + // this is a mime slideshow, like *.jpeg + QStringList filters; + filters << "*." + ext; + dir.setNameFilters(filters); + QStringList result = dir.entryList(QDir::Files); + urls.append(path + filters.at(0) + " (" + i18np("1 image found", "%1 images found", result.count()) + ')'); + } else { + // this is a pattern slideshow, like sequence%4d.jpg + QString filter = url.fileName(); + QString ext = filter.section('.', -1); + filter = filter.section('%', 0, -2); + QString regexp = '^' + filter + "\\d+\\." + ext + '$'; + QRegExp rx(regexp); + int count = 0; + QStringList result = dir.entryList(QDir::Files); + foreach(const QString & path, result) { + if (rx.exactMatch(path)) count++; + } + urls.append(url.path() + " (" + i18np("1 image found", "%1 images found", count) + ')'); + } + return urls; +} + +void ProjectSettings::slotExportToText() +{ + QString savePath = KFileDialog::getSaveFileName(project_folder->url(), "text/plain", this); + if (savePath.isEmpty()) return; + QString data; + data.append(i18n("Project folder: %1", project_folder->url().path()) + '\n'); + data.append(i18n("Project profile: %1", profiles_list->currentText()) + '\n'); + data.append(i18n("Total clips: %1 (%2 used in timeline).", files_count->text(), used_count->text()) + "\n\n"); + for (int i = 0; i < files_list->topLevelItemCount(); ++i) { + if (files_list->topLevelItem(i)->childCount() > 0) { + data.append('\n' + files_list->topLevelItem(i)->text(0) + ":\n\n"); + for (int j = 0; j < files_list->topLevelItem(i)->childCount(); j++) { + data.append(files_list->topLevelItem(i)->child(j)->text(0) + '\n'); + } + } + } + KTemporaryFile tmpfile; + if (!tmpfile.open()) { + kWarning() << "///// CANNOT CREATE TMP FILE in: " << tmpfile.fileName(); + return; + } + QFile xmlf(tmpfile.fileName()); + if (!xmlf.open(QIODevice::WriteOnly)) + return; + xmlf.write(data.toUtf8()); + if (xmlf.error() != QFile::NoError) { + xmlf.close(); + return; + } + xmlf.close(); + KIO::NetAccess::upload(tmpfile.fileName(), savePath, 0); +} + +void ProjectSettings::slotUpdateProxyParams() +{ + QString params = proxy_profile->itemData(proxy_profile->currentIndex()).toString(); + proxyparams->setPlainText(params.section(';', 0, 0)); +} + +const QMap ProjectSettings::metadata() const +{ + QMap metadata; + for (int i = 0; i < metadata_list->topLevelItemCount(); ++i) + { + QTreeWidgetItem *item = metadata_list->topLevelItem(i); + if (!item->text(1).simplified().isEmpty()) { + // Insert metadata entry + QString key = item->data(0, Qt::UserRole).toString(); + if (key.isEmpty()) key = "meta.attr." + item->text(0).simplified() + ".markup"; + QString value = item->text(1); + metadata.insert(key, value); + } + } + return metadata; +} + +void ProjectSettings::slotAddMetadataField() +{ + QTreeWidgetItem *item = new QTreeWidgetItem(metadata_list, QStringList() << i18n("field_name")); + item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); +} + +void ProjectSettings::slotDeleteMetadataField() +{ + QTreeWidgetItem *item = metadata_list->currentItem(); + if (item) delete item; +} + #include "projectsettings.moc"