diff options
author | Philippe Mavridis <philippe.mavridis@yandex.com> | 2025-06-24 16:52:05 +0300 |
---|---|---|
committer | Philippe Mavridis <philippe.mavridis@yandex.com> | 2025-08-03 12:48:27 +0300 |
commit | 05da60e3b03b35a1f7aa537be39376350225e936 (patch) | |
tree | a54a6e6a7ade33be4156848b228447d4993f5446 /tdeio/tdefile | |
parent | 2e76346c68e5c1db6b6058014c024bb70fec5e34 (diff) | |
download | tdelibs-feat/extended-attributes.tar.gz tdelibs-feat/extended-attributes.zip |
Add basic extended attributes supportfeat/extended-attributes
This commit adds extended attributes support to TDEIO, UDS, tdeio_file and a read-write plugin for the file properties dialog.
It also adds attribute display support to TDEFileItem, used by KDesktop and Konqueror to show file tooltips.
Signed-off-by: Philippe Mavridis <philippe.mavridis@yandex.com>
Diffstat (limited to 'tdeio/tdefile')
-rw-r--r-- | tdeio/tdefile/kpropertiesdialog.cpp | 782 | ||||
-rw-r--r-- | tdeio/tdefile/kpropertiesdialog.h | 88 |
2 files changed, 826 insertions, 44 deletions
diff --git a/tdeio/tdefile/kpropertiesdialog.cpp b/tdeio/tdefile/kpropertiesdialog.cpp index c8e324a2d..1096d871d 100644 --- a/tdeio/tdefile/kpropertiesdialog.cpp +++ b/tdeio/tdefile/kpropertiesdialog.cpp @@ -124,6 +124,9 @@ extern "C" { #include <krun.h> #include <tdelistview.h> #include <kacl.h> +#include <kprotocolinfo.h> +#include <ktabwidget.h> +#include <tdeaccel.h> #include "tdefilesharedlg.h" #include "kpropertiesdesktopbase.h" @@ -139,6 +142,17 @@ extern "C" { # include <win32_utils.h> #endif +// This KDE3-era HACK ensures that TDEIO jobs are properly terminated in the +// applyChanges() slots. +void tqt_enter_modal( TQWidget *widget ); +void tqt_leave_modal( TQWidget *widget ); +#define WAIT_FOR_JOB \ + TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); \ + tqt_enter_modal(&dummy); \ + tqApp->enter_loop(); \ + tqt_leave_modal(&dummy); +#define JOB_DONE tqApp->exit_loop(); + static TQString nameFromFileName(TQString nameStr) { if ( nameStr.endsWith(".desktop") ) @@ -387,6 +401,7 @@ bool KPropertiesDialog::canDisplay( KFileItemList _items ) KBindingPropsPlugin::supports( _items ) || KURLPropsPlugin::supports( _items ) || KDevicePropsPlugin::supports( _items ) || + TDEAttrPropsPlugin::supports(_items) || KFileMetaPropsPlugin::supports( _items ) || KPreviewPropsPlugin::supports( _items ); } @@ -486,6 +501,12 @@ void KPropertiesDialog::insertPages() insertPlugin (p); } + if (TDEAttrPropsPlugin::supports(m_items)) + { + TDEAttrPropsPlugin *p = new TDEAttrPropsPlugin(this); + insertPlugin(p); + } + if ( KFileMetaPropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this ); @@ -1324,10 +1345,6 @@ bool KFilePropsPlugin::supports( KFileItemList /*_items*/ ) return true; } -// Don't do this at home -void tqt_enter_modal( TQWidget *widget ); -void tqt_leave_modal( TQWidget *widget ); - void KFilePropsPlugin::applyChanges() { if ( d->dirSizeJob ) { @@ -1383,11 +1400,7 @@ void KFilePropsPlugin::applyChanges() TQ_SLOT( slotCopyFinished( TDEIO::Job * ) ) ); connect( job, TQ_SIGNAL( renamed( TDEIO::Job *, const KURL &, const KURL & ) ), TQ_SLOT( slotFileRenamed( TDEIO::Job *, const KURL &, const KURL & ) ) ); - // wait for job - TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); - tqt_enter_modal(&dummy); - tqApp->enter_loop(); - tqt_leave_modal(&dummy); + WAIT_FOR_JOB; return; } properties->updateUrl(properties->kurl()); @@ -1406,8 +1419,7 @@ void KFilePropsPlugin::slotCopyFinished( TDEIO::Job * job ) kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl; if (job) { - // allow apply() to return - tqApp->exit_loop(); + JOB_DONE; if ( job->error() ) { job->showErrorDialog( d->m_frame ); @@ -2555,11 +2567,7 @@ void KFilePermissionsPropsPlugin::applyChanges() connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ), TQ_SLOT( slotChmodResult( TDEIO::Job * ) ) ); - // Wait for job - TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); - tqt_enter_modal(&dummy); - tqApp->enter_loop(); - tqt_leave_modal(&dummy); + WAIT_FOR_JOB; } if (dirs.count() > 0) { job = TDEIO::chmod( dirs, orDirPermissions, ~andDirPermissions, @@ -2571,11 +2579,7 @@ void KFilePermissionsPropsPlugin::applyChanges() connect( job, TQ_SIGNAL( result( TDEIO::Job * ) ), TQ_SLOT( slotChmodResult( TDEIO::Job * ) ) ); - // Wait for job - TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); - tqt_enter_modal(&dummy); - tqApp->enter_loop(); - tqt_leave_modal(&dummy); + WAIT_FOR_JOB; } } @@ -2584,8 +2588,7 @@ void KFilePermissionsPropsPlugin::slotChmodResult( TDEIO::Job * job ) kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl; if (job->error()) job->showErrorDialog( d->m_frame ); - // allow apply() to return - tqApp->exit_loop(); + JOB_DONE } @@ -2863,6 +2866,739 @@ void KBindingPropsPlugin::applyChanges() /* ---------------------------------------------------- * + * TDEAttrPropsPlugin + * + * -------------------------------------------------- */ +class TDEAttrPropsPlugin::TDEAttrEntry : public TQHBox +{ + friend class TDEAttrPropsPlugin; + public: + TDEAttrEntry(TQWidget *parent) + : TQHBox(parent, "tdeattrentry") + { + m_combo = new TQComboBox(this); + m_combo->setEditable(true); + m_combo->setDuplicatesEnabled(false); + m_combo->lineEdit()->setMaxLength(255); + m_combo->setSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed); + + m_value = new TQLineEdit(this); + m_value->setMaxLength(255); + m_value->setSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed); + + m_delete = new TQPushButton(this); + m_delete->setPixmap(SmallIcon("edittrash")); + m_delete->setFocusPolicy(TQWidget::NoFocus); + m_delete->setSizePolicy(TQSizePolicy::Maximum, TQSizePolicy::Expanding); + + setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Fixed); + } + + TQCString key() { return m_combo->currentText().local8Bit(); } + void setKey(const TQCString& key) + { + m_combo->setCurrentText(TQString::fromLocal8Bit(key)); + } + + TQCString value() { return m_value->text().local8Bit(); } + void setValue(const TQCString& value) + { + m_value->setText(TQString::fromLocal8Bit(value)); + } + + void setReadOnly(bool ro) + { + m_combo->setDisabled(ro); + m_value->setReadOnly(ro); + m_delete->setDisabled(ro); + } + + ~TDEAttrEntry() {} + + private: + TQComboBox *m_combo; + TQLineEdit *m_value; + TQPushButton *m_delete; +}; + +class TDEAttrPropsPlugin::TDEAttrNamespaceTab : public TQWidget +{ + friend class TDEAttrPropsPlugin; + public: + TDEAttrNamespaceTab(TQWidget *parent, TQCString ns) + : TQWidget(parent), m_namespace(ns), m_queryPending(true) + { + m_layout = new TQVBoxLayout(this); + m_entriesLayout = new TQVBoxLayout; + m_label = new TQLabel(i18n("Querying attributes..."), this); + + m_layout->setSpacing(KDialog::spacingHint()); + m_layout->setMargin(KDialog::marginHint()); + + m_layout->addWidget(m_label); + m_layout->addLayout(m_entriesLayout); + m_layout->addStretch(); + + // Read saved attributes + TDEConfig *config = new TDEConfig("kdeglobals"); + config->setGroup("TDE Extended Attributes"); + TQString key = "SavedAttributes_" + ns; + m_savedAttrs = config->readListEntry(key); + + m_entryWidgets.setAutoDelete(true); + } + + private: + bool m_queryPending; + TQPtrList<TDEAttrEntry> m_entryWidgets, m_pendingWidgets; + TQCString m_namespace; + TQVBoxLayout *m_layout, *m_entriesLayout; + TQMap<TQCString, TQCString> m_entries; + TQLabel *m_label; + TQStringList m_savedAttrs; +}; + +class TDEAttrPropsPlugin::TDEAttrPropsPluginPrivate +{ + public: + TDEAttrPropsPluginPrivate() {} + ~TDEAttrPropsPluginPrivate() {} + + TQFrame *m_frame, *m_tab; + KTabWidget *m_tabWidget; + TQMap<TQCString, TDEAttrNamespaceTab*> m_tabs; + TQHBox *m_buttonsBox; + TQPushButton *m_newEntry, *m_editSavedAttrs, *m_preferences; + bool m_showSystemNs; + TDEAccel *m_accel; +}; + +TDEAttrPropsPlugin::TDEAttrPropsPlugin(KPropertiesDialog *_props) + : KPropsDlgPlugin(_props) +{ + // Initialise tabs + d = new TDEAttrPropsPluginPrivate; + + d->m_frame = properties->addPage(i18n("A&ttributes")); + + d->m_tabWidget = new KTabWidget(d->m_frame); + d->m_tabWidget->setTabShape(TQTabWidget::Triangular); + + TDEConfig *config = new TDEConfig("kdeglobals"); + config->setGroup("TDE Extended Attributes"); + d->m_showSystemNs = config->readBoolEntry("ShowSystemNs"); + + TQTimer::singleShot(0, this, TQ_SLOT(slotUpdateTabs())); + + // Initialise button box + d->m_buttonsBox = new TQHBox(d->m_frame); + d->m_buttonsBox->setSpacing(KDialog::spacingHint()); + d->m_buttonsBox->setMargin(KDialog::marginHint()); + + d->m_newEntry = new TQPushButton( + SmallIcon("add"), + i18n("Add attribute"), + d->m_buttonsBox + ); + + if (!KProtocolInfo::supportsWritingAttrs(properties->kurl())) + { + d->m_newEntry->setEnabled(false); + } + + d->m_editSavedAttrs = new TQPushButton( + SmallIcon("document-save"), + i18n("Common attributes..."), + d->m_buttonsBox + ); + + d->m_preferences = new TQPushButton( + SmallIcon("configure"), + i18n("Preferences..."), + d->m_buttonsBox + ); + + // Initialise shortcuts + d->m_accel = new TDEAccel(d->m_frame); + d->m_accel->insert( + "add", i18n("Add attribute"), + i18n("Add a new attribute entry to the current namespace"), + TDEShortcut("Insert"), this, TQ_SLOT(slotAddEntry()) + ); + d->m_accel->insert( + "del", i18n("Remove attribute"), + i18n("Remove currently edited attribute entry"), + TDEShortcut("Alt+Delete"), this, TQ_SLOT(slotDelEntry()) + ); + + // Create module layout + TQVBoxLayout *layout = new TQVBoxLayout(d->m_frame); + layout->addWidget(d->m_tabWidget); + layout->addWidget(d->m_buttonsBox); + + // Connect signals + connect(d->m_newEntry, TQ_SIGNAL(clicked()), TQ_SLOT(slotAddEntry())); + connect(d->m_editSavedAttrs, TQ_SIGNAL(clicked()), TQ_SLOT(slotEditSavedAttrs())); + connect(d->m_preferences, TQ_SIGNAL(clicked()), TQ_SLOT(slotPreferences())); + connect(this, TQ_SIGNAL(changed()), TQ_SLOT(slotCheckNoAttrs())); + + connect(d->m_tabWidget, TQ_SIGNAL(currentChanged(TQWidget*)), + TQ_SLOT(slotTabChanged())); +} + +TDEAttrPropsPlugin::~TDEAttrPropsPlugin() +{ + delete d; +} + +void +TDEAttrPropsPlugin::slotUpdateTabs() +{ + TQString tip; + tip = i18n( + "<qt><b>User attributes</b> may be assigned to files and directories " + "for storing arbitrary additional information.</qt>" + ); + updateTab("user", i18n("User"), tip); + +#if defined(Q_OS_LINUX) + tip = i18n( + "<qt><p><b>System attributes</b> are used by the kernel to store " + "system objects such as Access Control Lists." + "<p>Read and write access permissions to system attributes depend on " + "the policy implemented for each system attribute implemented by " + "filesystems in the kernel.</qt>" + ); + updateTab("system", i18n("System"), tip); + + tip = i18n( + "<qt><p><b>Security attributes</b> are used by kernel security modules " + "such as SELinux, and also to implement file capabilities." + "<p>Read and write access permissions to security attributes depend on " + "the policy implemented for each security attribute by the security " + "module. When no security module is loaded, all processes have read " + "access to extended security attributes, and write access is limited " + "to processes that have the CAP_SYS_ADMIN capability.</qt>" + ); + updateTab("security", i18n("Security"), tip); + + tip = i18n( + "<qt><p><b>Trusted attributes</b> are visible and accessible only to " + "processes that have the CAP_SYS_ADMIN capability. Attributes in this " + "class are used to implement mechanisms in user space (i.e., outside " + "the kernel) which keep information in extended attributes to which " + "ordinary processes should not have access.</qt>" + ); + updateTab("trusted", i18n("Trusted"), tip); +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) + // From extattr(9) (FIXME: possibly find a more informative source?) + tip = i18n( + "<qt><p><b>System attributes</b> are protected such that appropriate " + "privilege is required to directly access or manipulate them.</qt>" + ); + updateTab("system", i18n("System"), tip); +#endif + + d->m_tabWidget->setTabBarHidden(d->m_tabWidget->count() == 1); + if (d->m_tabWidget->count() == 1) slotTabChanged(); +} + +void +TDEAttrPropsPlugin::updateTab(TQCString ns, TQString label, TQString tip) +{ + if (d->m_tabs.contains(ns)) + { + if (ns != "user" && !d->m_showSystemNs) + { + d->m_tabWidget->removePage(d->m_tabs[ns]); + d->m_tabs.remove(ns); + } + return; + } + else + { + if (ns != "user" && !d->m_showSystemNs) + { + return; + } + + TDEAttrNamespaceTab *tab = new TDEAttrNamespaceTab(d->m_tabWidget, ns); + d->m_tabWidget->addTab(tab, label); + d->m_tabWidget->setTabToolTip(tab, tip); + TQWhatsThis::add(tab, tip); + d->m_tabs[ns] = tab; + } +} + +TDEAttrPropsPlugin::TDEAttrNamespaceTab* +TDEAttrPropsPlugin::currentNamespaceTab() +{ + return static_cast<TDEAttrNamespaceTab*>(d->m_tabWidget->currentPage()); +} + +void TDEAttrPropsPlugin::slotTabChanged() +{ + TDEAttrNamespaceTab *tab = currentNamespaceTab(); + if (!tab->m_queryPending) + { + return; + } + + slotCheckNoAttrs(); + + // Query attributes + KURL url = properties->kurl(); + TDEIO::AttributeJob *listJob = TDEIO::listAttr(url, tab->m_namespace, true); + connect(listJob, TQ_SIGNAL(result(TDEIO::Job*)), + TQ_SLOT(slotListJobResult(TDEIO::Job*))); +} + +TDEAttrPropsPlugin::TDEAttrEntry * +TDEAttrPropsPlugin::addEntry(TDEAttrNamespaceTab *tab) +{ + if (!tab) tab = currentNamespaceTab(); + Q_ASSERT(tab); + + TDEAttrEntry *entryWidget = new TDEAttrEntry(tab); + tab->m_entriesLayout->addWidget(entryWidget); + + tab->m_entryWidgets.append(entryWidget); + + entryWidget->m_combo->insertStringList(tab->m_savedAttrs); + entryWidget->m_combo->setCurrentText(TQString::null); + + if (!KProtocolInfo::supportsWritingAttrs(properties->kurl())) + { + entryWidget->setReadOnly(true); + } + + entryWidget->show(); + + return entryWidget; +} + +void TDEAttrPropsPlugin::connectEntry(TDEAttrEntry *entry) +{ + connect(entry->m_combo, TQ_SIGNAL(textChanged(const TQString&)), + this, TQ_SIGNAL(changed())); + connect(entry->m_value, TQ_SIGNAL(textChanged(const TQString&)), + this, TQ_SIGNAL(changed())); + connect(entry->m_delete, TQ_SIGNAL(clicked()), + this, TQ_SLOT(slotDelButtonPressed())); +} + +void TDEAttrPropsPlugin::slotAddEntry() +{ + if (!KProtocolInfo::supportsWritingAttrs(properties->kurl())) + { + return; + } + TDEAttrEntry *entryWidget = addEntry(); + connectEntry(entryWidget); + entryWidget->m_combo->lineEdit()->setFocus(); + emit changed(); +} + +void TDEAttrPropsPlugin::slotDelEntry() +{ + TDEAttrNamespaceTab *tab = currentNamespaceTab(); + if (tab->focusWidget() && tab->focusWidget()->parentWidget()) + { + // From value lineedit + TQWidget *w = tab->focusWidget()->parentWidget(); + if (qstrcmp(w->name(), "tdeattrentry") == 0) + { + TDEAttrEntry *entry = static_cast<TDEAttrEntry*>(w); + delEntry(entry); + } + + // From combobox lineedit + else if (qstrcmp(w->parentWidget()->name(), "tdeattrentry") == 0) + { + TDEAttrEntry *entry = static_cast<TDEAttrEntry*>(w->parentWidget()); + delEntry(entry); + } + } +} + +void TDEAttrPropsPlugin::slotEditSavedAttrs() +{ + TDEAttrNamespaceTab *tab = currentNamespaceTab(); + TQString caption = i18n("Common attributes [namespace: %1]") + .arg(tab->m_namespace); + + KDialogBase *dlg = new KDialogBase( + properties, + "TDE Saved Attributes", + true, + caption, + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok + ); + + KEditListBox *elb = new KEditListBox(dlg); + elb->setTitle(caption); + elb->insertStringList(currentNamespaceTab()->m_savedAttrs); + connect(dlg, TQ_SIGNAL(okClicked()), this, TQ_SLOT(slotApplySavedAttrs())); + dlg->setMainWidget(elb); + dlg->exec(); +} + +void TDEAttrPropsPlugin::slotApplySavedAttrs() +{ + KDialogBase *dlg = static_cast<KDialogBase*>(const_cast<TQObject*>(TQObject::sender())); + Q_ASSERT(dlg); + KEditListBox *elb = static_cast<KEditListBox*>(dlg->mainWidget()); + Q_ASSERT(elb); + + TDEAttrNamespaceTab *tab = currentNamespaceTab(); + + tab->m_savedAttrs = elb->items(); + TDEConfig *config = new TDEConfig("kdeglobals"); + config->setGroup("TDE Extended Attributes"); + TQString key = "SavedAttributes_" + tab->m_namespace; + config->writeEntry(key, tab->m_savedAttrs); + config->sync(); + dlg->deleteLater(); + + // Update comboboxes immediately + TDEAttrEntry *entry; + for (entry = tab->m_entryWidgets.first(); entry; + entry = tab->m_entryWidgets.next()) + { + TQString currentText = entry->m_combo->currentText(); + entry->m_combo->clear(); + entry->m_combo->insertStringList(tab->m_savedAttrs); + entry->m_combo->setCurrentText(currentText); + } +} + +void +TDEAttrPropsPlugin::delEntry(TDEAttrPropsPlugin::TDEAttrEntry *entry) +{ + if (!KProtocolInfo::supportsWritingAttrs(properties->kurl())) + { + return; + } + + TQCString attr = entry->key(); + bool del = false; + + if (attr.isEmpty() && entry->value().isEmpty()) + { + del = true; + } + else + { + TQString msg; + if (!attr.isEmpty()) + { + msg = i18n("<qt>Are you sure you want to delete attribute <b>%1</b>?</qt>") + .arg(TQString::fromLocal8Bit(attr)); + } + else + { + msg = i18n("<qt>Are you sure you want to delete this unnamed attribute?</qt>"); + } + + int result = KMessageBox::warningYesNo( + properties, msg, i18n("Remove attribute?"), + KStdGuiItem::yes(), KStdGuiItem::no(), "ConfirmRemoveAttribute"); + del = (result == KMessageBox::Yes); + } + + if (del) + { + TDEAttrNamespaceTab *tab = static_cast<TDEAttrNamespaceTab*>(entry->parentWidget()); + Q_ASSERT(tab); + + tab->m_entryWidgets.remove(entry); + if (tab->m_entryWidgets.count() > 0) + { + tab->m_entryWidgets.last()->m_combo->setFocus(); + } + else + { + tab->setFocus(); + } + emit changed(); + } +} + +void TDEAttrPropsPlugin::slotDelButtonPressed() +{ + const TQObject *sender = TQObject::sender(); + TQWidget *button = static_cast<TQWidget*>(const_cast<TQObject*>(sender)); + Q_ASSERT(button); + TDEAttrEntry *entry = static_cast<TDEAttrEntry*>(button->parentWidget()); + Q_ASSERT(entry); + + delEntry(entry); +} + +void TDEAttrPropsPlugin::slotCheckNoAttrs() +{ + + TDEAttrNamespaceTab *tab = currentNamespaceTab(); + if (tab->m_entryWidgets.count() == 0) + { + tab->m_label->show(); + tab->m_label->setText(i18n("No attributes or insufficient permissions.")); + } + else tab->m_label->hide(); +} + +void TDEAttrPropsPlugin::slotListJobResult(TDEIO::Job *job) +{ + TDEIO::AttributeJob *listJob = static_cast<TDEIO::AttributeJob*>(job); + Q_ASSERT(listJob); + + const TQCString& ns = listJob->attributeNs(); + + TDEAttrNamespaceTab *tab = d->m_tabs[ns]; + Q_ASSERT(tab); + + if (job->error()) + { + job->showErrorDialog(); + tab->m_label->setText(i18n("Error reading attributes information!")); + } + else + { + TQValueList<TQCString> attrs = listJob->attributes(); + TQValueList<TQCString>::iterator it; + for (it = attrs.begin(); it != attrs.end(); ++it) + { + TQCString attr(*it); + TDEAttrEntry *entryWidget = addEntry(tab); + entryWidget->setKey(attr); + tab->m_pendingWidgets.append(entryWidget); + + TDEIO::AttributeJob *readJob = TDEIO::readAttr(properties->kurl(), + ns, attr, false); + connect(readJob, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotReadJobResult(TDEIO::Job*))); + } + + slotCheckNoAttrs(); + } +} + +void TDEAttrPropsPlugin::slotReadJobResult(TDEIO::Job *job) +{ + TDEIO::AttributeJob *readJob = static_cast<TDEIO::AttributeJob*>(job); + Q_ASSERT(readJob); + + if (job->error()) + { + job->showErrorDialog(); + } + else + { + const TQCString& ns = readJob->attributeNs(); + + TDEAttrNamespaceTab *tab = d->m_tabs[ns]; + Q_ASSERT(tab); + + tab->m_queryPending = false; + + const TQCString& attr = readJob->attribute(), val = readJob->value(); + TDEAttrEntry *entryWidget; + for (entryWidget = tab->m_pendingWidgets.first(); entryWidget; + entryWidget = tab->m_pendingWidgets.next()) + { + if (entryWidget->key() == attr) + { + entryWidget->setValue(val); + tab->m_pendingWidgets.remove(entryWidget); + tab->m_entries[attr] = val; + if (KProtocolInfo::supportsWritingAttrs(properties->kurl())) + { + entryWidget->setReadOnly(false); + connectEntry(entryWidget); + } + tab->m_pendingWidgets.remove(entryWidget); + tab->m_entries[attr] = val; + return; + } + } + + kdWarning() << "attribute widget not found: " << ns << "." << attr << endl; + } +} + +void TDEAttrPropsPlugin::slotWriteJobResult(TDEIO::Job *job) +{ + TDEIO::AttributeJob *writeJob = static_cast<TDEIO::AttributeJob*>(job); + Q_ASSERT(writeJob); + + if (job->error()) + { + job->showErrorDialog(); + } + else + { + TDEAttrNamespaceTab *tab = d->m_tabs[writeJob->attributeNs()]; + Q_ASSERT(tab); + tab->m_entries[writeJob->attribute()] = writeJob->value(); + } + JOB_DONE; +} + +void TDEAttrPropsPlugin::slotRemoveJobResult(TDEIO::Job *job) +{ + TDEIO::AttributeJob *removeJob = static_cast<TDEIO::AttributeJob*>(job); + Q_ASSERT(removeJob); + + if (job->error()) + { + job->showErrorDialog(); + } + else + { + TDEAttrNamespaceTab *tab = d->m_tabs[removeJob->attributeNs()]; + Q_ASSERT(tab); + tab->m_entries.remove(removeJob->attribute()); + } + JOB_DONE; +} + +void TDEAttrPropsPlugin::applyChanges() +{ + if (!KProtocolInfo::supportsWritingAttrs(properties->kurl())) + { + return; + } + TDEIO::AttributeJob *writeJob; + + TQMap<TQCString, TDEAttrNamespaceTab*>::Iterator it; + for (it = d->m_tabs.begin(); it != d->m_tabs.end(); ++it) + { + TDEAttrNamespaceTab *tab = (*it); + + // First do a validation + TQStringList existingKeys; + TDEAttrEntry *entry; + for (entry = tab->m_entryWidgets.first(); entry; + entry = tab->m_entryWidgets.next()) + { + if (entry->key().isEmpty() && !entry->value().isEmpty()) + { + KMessageBox::sorry(properties, + i18n("There is an attribute without a name.")); + properties->abortApplying(); + return; + } + + else if (existingKeys.contains(entry->key())) + { + KMessageBox::sorry(properties, + i18n("Duplicate attribute '%1'.") + .arg(TQString::fromLocal8Bit(entry->key()))); + properties->abortApplying(); + return; + } + + existingKeys << entry->key(); + } + + // Then actually apply the settings + for (entry = tab->m_entryWidgets.first(); entry; + entry = tab->m_entryWidgets.next()) + { + // Ignore empty fields + if (entry->key().isEmpty()) continue; + + // Ignore if the attribute value is unchanged + if (tab->m_entries.contains(entry->key()) && + tab->m_entries[entry->key()] == entry->value()) + { + continue; + } + + writeJob = TDEIO::writeAttr(properties->kurl(), tab->m_namespace, + entry->key(), entry->value(), true); + connect(writeJob, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotWriteJobResult(TDEIO::Job*))); + WAIT_FOR_JOB; + } + + TQMap<TQCString, TQCString> entries(tab->m_entries); + TQMap<TQCString, TQCString>::Iterator it; + for (it = entries.begin(); it != entries.end(); ++it) + { + if (!existingKeys.contains(it.key())) + { + TDEIO::AttributeJob *rmJob = TDEIO::removeAttr(properties->kurl(), + tab->m_namespace, + it.key(), true); + connect(rmJob, TQ_SIGNAL(result(TDEIO::Job*)), + this, TQ_SLOT(slotRemoveJobResult(TDEIO::Job*))); + WAIT_FOR_JOB; + } + } + } +} + +void TDEAttrPropsPlugin::slotPreferences() +{ + KDialogBase *dlg = new KDialogBase( + properties, + "TDE Attribute Properties Plugin Preferences", + true, + i18n("Preferences"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok + ); + + connect(dlg, TQ_SIGNAL(okClicked()), this, TQ_SLOT(slotApplyPreferences())); + TQVBox *vbox = dlg->makeVBoxMainWidget(); + + TQCheckBox *showSystemNs = new TQCheckBox( + i18n("Show &system attributes"), + vbox, + "showSystemNs" + ); + showSystemNs->setChecked(d->m_showSystemNs); + + dlg->exec(); +} + +void TDEAttrPropsPlugin::slotApplyPreferences() +{ + KDialogBase *dlg = static_cast<KDialogBase*>(const_cast<TQObject*>(TQObject::sender())); + Q_ASSERT(dlg); + TQCheckBox *showSystemNs = static_cast<TQCheckBox*>(dlg->child("showSystemNs", "TQCheckBox")); + Q_ASSERT(showSystemNs); + + d->m_showSystemNs = showSystemNs->isChecked(); + + TDEConfig *config = new TDEConfig("kdeglobals"); + config->setGroup("TDE Extended Attributes"); + config->writeEntry("ShowSystemNs", d->m_showSystemNs); + config->sync(); + + TQTimer::singleShot(0, this, TQ_SLOT(slotUpdateTabs())); +} + +bool TDEAttrPropsPlugin::supports(KFileItemList _items) +{ + if (_items.count() != 1) + { + return false; + } + + KFileItem *item = _items.first(); + if (item->isDir() || !KProtocolInfo::supportsReadingAttrs(item->url())) + { + return false; + } + + return true; +} + +/* ---------------------------------------------------- + * * KDevicePropsPlugin * * -------------------------------------------------- */ diff --git a/tdeio/tdefile/kpropertiesdialog.h b/tdeio/tdefile/kpropertiesdialog.h index 8fa1a5880..f30ba1494 100644 --- a/tdeio/tdefile/kpropertiesdialog.h +++ b/tdeio/tdefile/kpropertiesdialog.h @@ -63,7 +63,7 @@ namespace TDEIO { class Job; } * * This class must be created with (void)new KPropertiesDialog(...) * It will take care of deleting itself. - * + * * If you are looking for more flexibility, see KFileMetaInfo and * KFileMetaInfoWidget. */ @@ -82,9 +82,9 @@ public: static bool canDisplay( KFileItemList _items ); /** - * Brings up a Properties dialog, as shown above. + * Brings up a Properties dialog, as shown above. * This is the normal constructor for - * file-manager type applications, where you have a KFileItem instance + * file-manager type applications, where you have a KFileItem instance * to work with. Normally you will use this * method rather than the one below. * @@ -192,40 +192,40 @@ public: virtual ~KPropertiesDialog(); /** - * Immediately displays a Properties dialog using constructor with - * the same parameters. - * On MS Windows, if @p item points to a local file, native (non modal) property + * Immediately displays a Properties dialog using constructor with + * the same parameters. + * On MS Windows, if @p item points to a local file, native (non modal) property * dialog is displayed (@p parent and @p modal are ignored in this case). - * + * * @return true on succesfull dialog displaying (can be false on win32). * @since 3.4 */ - static bool showDialog(KFileItem* item, TQWidget* parent = 0, + static bool showDialog(KFileItem* item, TQWidget* parent = 0, const char* name = 0, bool modal = false); /** - * Immediately displays a Properties dialog using constructor with - * the same parameters. - * On MS Windows, if @p _url points to a local file, native (non modal) property + * Immediately displays a Properties dialog using constructor with + * the same parameters. + * On MS Windows, if @p _url points to a local file, native (non modal) property * dialog is displayed (@p parent and @p modal are ignored in this case). - * + * * @return true on succesfull dialog displaying (can be false on win32). * @since 3.4 */ - static bool showDialog(const KURL& _url, TQWidget* parent = 0, + static bool showDialog(const KURL& _url, TQWidget* parent = 0, const char* name = 0, bool modal = false); /** - * Immediately displays a Properties dialog using constructor with - * the same parameters. - * On MS Windows, if @p _items has one element and this element points - * to a local file, native (non modal) property dialog is displayed + * Immediately displays a Properties dialog using constructor with + * the same parameters. + * On MS Windows, if @p _items has one element and this element points + * to a local file, native (non modal) property dialog is displayed * (@p parent and @p modal are ignored in this case). - * + * * @return true on succesfull dialog displaying (can be false on win32). * @since 3.4 */ - static bool showDialog(const KFileItemList& _items, TQWidget* parent = 0, + static bool showDialog(const KFileItemList& _items, TQWidget* parent = 0, const char* name = 0, bool modal = false); /** @@ -244,7 +244,7 @@ public: void insertPlugin (KPropsDlgPlugin *plugin); /** - * The URL of the file that has its properties being displayed. + * The URL of the file that has its properties being displayed. * This is only valid if the KPropertiesDialog was created/shown * for one file or URL. * @@ -323,7 +323,7 @@ public: * @since 3.1 */ void showFileSharingPage(); - + /** * Sets the file sharing page. * This page is shown when calling showFileSharingPage(). @@ -716,6 +716,52 @@ private: }; /** + * Properties plugin for extended attributes + * @internal + */ +class TDEIO_EXPORT TDEAttrPropsPlugin : public KPropsDlgPlugin +{ + TQ_OBJECT + + public: + TDEAttrPropsPlugin(KPropertiesDialog *_props); + virtual ~TDEAttrPropsPlugin(); + + virtual void applyChanges(); + static bool supports(KFileItemList _items); + + private slots: + void slotListJobResult(TDEIO::Job *); + void slotReadJobResult(TDEIO::Job *); + void slotWriteJobResult(TDEIO::Job *); + void slotRemoveJobResult(TDEIO::Job *); + void slotAddEntry(); + void slotDelEntry(); + void slotDelButtonPressed(); + void slotEditSavedAttrs(); + void slotApplySavedAttrs(); + void slotCheckNoAttrs(); + void slotTabChanged(); + void slotPreferences(); + void slotApplyPreferences(); + void slotUpdateTabs(); + + private: + class TDEAttrEntry; + class TDEAttrNamespaceTab; + class TDEAttrPropsPluginPrivate; + TDEAttrPropsPluginPrivate *d; + + protected: + void updateTab(TQCString ns, TQString label, TQString tip); + TDEAttrNamespaceTab* currentNamespaceTab(); + void updateComboBoxes(); + TDEAttrEntry *addEntry(TDEAttrNamespaceTab *tab = nullptr); + void delEntry(TDEAttrPropsPlugin::TDEAttrEntry *entry); + void connectEntry(TDEAttrEntry *entry); +}; + +/** * Properties plugin for device .desktop files * @internal */ |