summaryrefslogtreecommitdiffstats
path: root/kio/kfile/kpropertiesdialog.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kio/kfile/kpropertiesdialog.cpp
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kio/kfile/kpropertiesdialog.cpp')
-rw-r--r--kio/kfile/kpropertiesdialog.cpp4157
1 files changed, 4157 insertions, 0 deletions
diff --git a/kio/kfile/kpropertiesdialog.cpp b/kio/kfile/kpropertiesdialog.cpp
new file mode 100644
index 000000000..fd08d906d
--- /dev/null
+++ b/kio/kfile/kpropertiesdialog.cpp
@@ -0,0 +1,4157 @@
+/* This file is part of the KDE project
+
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright (c) 1999, 2000 Preston Brown <pbrown@kde.org>
+ Copyright (c) 2000 Simon Hausmann <hausmann@kde.org>
+ Copyright (c) 2000 David Faure <faure@kde.org>
+ Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * kpropertiesdialog.cpp
+ * View/Edit Properties of files, locally or remotely
+ *
+ * some FilePermissionsPropsPlugin-changes by
+ * Henner Zeller <zeller@think.de>
+ * some layout management by
+ * Bertrand Leconte <B.Leconte@mail.dotcom.fr>
+ * the rest of the layout management, bug fixes, adaptation to libkio,
+ * template feature by
+ * David Faure <faure@kde.org>
+ * More layout, cleanups, and fixes by
+ * Preston Brown <pbrown@kde.org>
+ * Plugin capability, cleanups and port to KDialogBase by
+ * Simon Hausmann <hausmann@kde.org>
+ * KDesktopPropsPlugin by
+ * Waldo Bastian <bastian@kde.org>
+ */
+
+#include <config.h>
+extern "C" {
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <sys/types.h>
+}
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <algorithm>
+#include <functional>
+
+#include <qfile.h>
+#include <qdir.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qcheckbox.h>
+#include <qstrlist.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+#include <qpainter.h>
+#include <qlayout.h>
+#include <qcombobox.h>
+#include <qgroupbox.h>
+#include <qwhatsthis.h>
+#include <qtooltip.h>
+#include <qstyle.h>
+#include <qprogressbar.h>
+#include <qvbox.h>
+#include <qvaluevector.h>
+
+#ifdef USE_POSIX_ACL
+extern "C" {
+#include <sys/param.h>
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
+}
+#endif
+
+#include <kapplication.h>
+#include <kdialog.h>
+#include <kdirsize.h>
+#include <kdirwatch.h>
+#include <kdirnotify_stub.h>
+#include <kdiskfreesp.h>
+#include <kdebug.h>
+#include <kdesktopfile.h>
+#include <kicondialog.h>
+#include <kurl.h>
+#include <kurlrequester.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+#include <kstandarddirs.h>
+#include <kio/job.h>
+#include <kio/chmodjob.h>
+#include <kio/renamedlg.h>
+#include <kio/netaccess.h>
+#include <kio/kservicetypefactory.h>
+#include <kfiledialog.h>
+#include <kmimetype.h>
+#include <kmountpoint.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <kservice.h>
+#include <kcompletion.h>
+#include <klineedit.h>
+#include <kseparator.h>
+#include <ksqueezedtextlabel.h>
+#include <klibloader.h>
+#include <ktrader.h>
+#include <kparts/componentfactory.h>
+#include <kmetaprops.h>
+#include <kpreviewprops.h>
+#include <kprocess.h>
+#include <krun.h>
+#include <klistview.h>
+#include <kacl.h>
+#include "kfilesharedlg.h"
+
+#include "kpropertiesdesktopbase.h"
+#include "kpropertiesdesktopadvbase.h"
+#include "kpropertiesmimetypebase.h"
+#ifdef USE_POSIX_ACL
+#include "kacleditwidget.h"
+#endif
+
+#include "kpropertiesdialog.h"
+
+#ifdef Q_WS_WIN
+# include <win32_utils.h>
+#endif
+
+static QString nameFromFileName(QString nameStr)
+{
+ if ( nameStr.endsWith(".desktop") )
+ nameStr.truncate( nameStr.length() - 8 );
+ if ( nameStr.endsWith(".kdelnk") )
+ nameStr.truncate( nameStr.length() - 7 );
+ // Make it human-readable (%2F => '/', ...)
+ nameStr = KIO::decodeFileName( nameStr );
+ return nameStr;
+}
+
+mode_t KFilePermissionsPropsPlugin::fperm[3][4] = {
+ {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
+ {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
+ {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
+ };
+
+class KPropertiesDialog::KPropertiesDialogPrivate
+{
+public:
+ KPropertiesDialogPrivate()
+ {
+ m_aborted = false;
+ fileSharePage = 0;
+ }
+ ~KPropertiesDialogPrivate()
+ {
+ }
+ bool m_aborted:1;
+ QWidget* fileSharePage;
+};
+
+KPropertiesDialog::KPropertiesDialog (KFileItem* item,
+ QWidget* parent, const char* name,
+ bool modal, bool autoShow)
+ : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(KIO::decodeFileName(item->url().fileName())),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
+ parent, name, modal)
+{
+ d = new KPropertiesDialogPrivate;
+ assert( item );
+ m_items.append( new KFileItem(*item) ); // deep copy
+
+ m_singleUrl = item->url();
+ assert(!m_singleUrl.isEmpty());
+
+ init (modal, autoShow);
+}
+
+KPropertiesDialog::KPropertiesDialog (const QString& title,
+ QWidget* parent, const char* name, bool modal)
+ : KDialogBase (KDialogBase::Tabbed, i18n ("Properties for %1").arg(title),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
+ parent, name, modal)
+{
+ d = new KPropertiesDialogPrivate;
+
+ init (modal, false);
+}
+
+KPropertiesDialog::KPropertiesDialog (KFileItemList _items,
+ QWidget* parent, const char* name,
+ bool modal, bool autoShow)
+ : KDialogBase (KDialogBase::Tabbed,
+ // TODO: replace <never used> with "Properties for 1 item". It's very confusing how it has to be translated otherwise
+ // (empty translation before the "\n" is not allowed by msgfmt...)
+ _items.count()>1 ? i18n( "<never used>","Properties for %n Selected Items",_items.count()) :
+ i18n( "Properties for %1" ).arg(KIO::decodeFileName(_items.first()->url().fileName())),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
+ parent, name, modal)
+{
+ d = new KPropertiesDialogPrivate;
+
+ assert( !_items.isEmpty() );
+ m_singleUrl = _items.first()->url();
+ assert(!m_singleUrl.isEmpty());
+
+ KFileItemListIterator it ( _items );
+ // Deep copy
+ for ( ; it.current(); ++it )
+ m_items.append( new KFileItem( **it ) );
+
+ init (modal, autoShow);
+}
+
+#ifndef KDE_NO_COMPAT
+KPropertiesDialog::KPropertiesDialog (const KURL& _url, mode_t /* _mode is now unused */,
+ QWidget* parent, const char* name,
+ bool modal, bool autoShow)
+ : KDialogBase (KDialogBase::Tabbed,
+ i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
+ parent, name, modal),
+ m_singleUrl( _url )
+{
+ d = new KPropertiesDialogPrivate;
+
+ KIO::UDSEntry entry;
+
+ KIO::NetAccess::stat(_url, entry, parent);
+
+ m_items.append( new KFileItem( entry, _url ) );
+ init (modal, autoShow);
+}
+#endif
+
+KPropertiesDialog::KPropertiesDialog (const KURL& _url,
+ QWidget* parent, const char* name,
+ bool modal, bool autoShow)
+ : KDialogBase (KDialogBase::Tabbed,
+ i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
+ parent, name, modal),
+ m_singleUrl( _url )
+{
+ d = new KPropertiesDialogPrivate;
+
+ KIO::UDSEntry entry;
+
+ KIO::NetAccess::stat(_url, entry, parent);
+
+ m_items.append( new KFileItem( entry, _url ) );
+ init (modal, autoShow);
+}
+
+KPropertiesDialog::KPropertiesDialog (const KURL& _tempUrl, const KURL& _currentDir,
+ const QString& _defaultName,
+ QWidget* parent, const char* name,
+ bool modal, bool autoShow)
+ : KDialogBase (KDialogBase::Tabbed,
+ i18n( "Properties for %1" ).arg(KIO::decodeFileName(_tempUrl.fileName())),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
+ parent, name, modal),
+
+ m_singleUrl( _tempUrl ),
+ m_defaultName( _defaultName ),
+ m_currentDir( _currentDir )
+{
+ d = new KPropertiesDialogPrivate;
+
+ assert(!m_singleUrl.isEmpty());
+
+ // Create the KFileItem for the _template_ file, in order to read from it.
+ m_items.append( new KFileItem( KFileItem::Unknown, KFileItem::Unknown, m_singleUrl ) );
+ init (modal, autoShow);
+}
+
+bool KPropertiesDialog::showDialog(KFileItem* item, QWidget* parent,
+ const char* name, bool modal)
+{
+#ifdef Q_WS_WIN
+ QString localPath = item->localPath();
+ if (!localPath.isEmpty())
+ return showWin32FilePropertyDialog(localPath);
+#endif
+ new KPropertiesDialog(item, parent, name, modal);
+ return true;
+}
+
+bool KPropertiesDialog::showDialog(const KURL& _url, QWidget* parent,
+ const char* name, bool modal)
+{
+#ifdef Q_WS_WIN
+ if (_url.isLocalFile())
+ return showWin32FilePropertyDialog( _url.path() );
+#endif
+ new KPropertiesDialog(_url, parent, name, modal);
+ return true;
+}
+
+bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent,
+ const char* name, bool modal)
+{
+ if (_items.count()==1)
+ return KPropertiesDialog::showDialog(_items.getFirst(), parent, name, modal);
+ new KPropertiesDialog(_items, parent, name, modal);
+ return true;
+}
+
+void KPropertiesDialog::init (bool modal, bool autoShow)
+{
+ m_pageList.setAutoDelete( true );
+ m_items.setAutoDelete( true );
+
+ insertPages();
+
+ if (autoShow)
+ {
+ if (!modal)
+ show();
+ else
+ exec();
+ }
+}
+
+void KPropertiesDialog::showFileSharingPage()
+{
+ if (d->fileSharePage) {
+ showPage( pageIndex( d->fileSharePage));
+ }
+}
+
+void KPropertiesDialog::setFileSharingPage(QWidget* page) {
+ d->fileSharePage = page;
+}
+
+
+void KPropertiesDialog::setFileNameReadOnly( bool ro )
+{
+ KPropsDlgPlugin *it;
+
+ for ( it=m_pageList.first(); it != 0L; it=m_pageList.next() )
+ {
+ KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it);
+ if ( plugin ) {
+ plugin->setFileNameReadOnly( ro );
+ break;
+ }
+ }
+}
+
+void KPropertiesDialog::slotStatResult( KIO::Job * )
+{
+}
+
+KPropertiesDialog::~KPropertiesDialog()
+{
+ m_pageList.clear();
+ delete d;
+}
+
+void KPropertiesDialog::insertPlugin (KPropsDlgPlugin* plugin)
+{
+ connect (plugin, SIGNAL (changed ()),
+ plugin, SLOT (setDirty ()));
+
+ m_pageList.append (plugin);
+}
+
+bool KPropertiesDialog::canDisplay( KFileItemList _items )
+{
+ // TODO: cache the result of those calls. Currently we parse .desktop files far too many times
+ return KFilePropsPlugin::supports( _items ) ||
+ KFilePermissionsPropsPlugin::supports( _items ) ||
+ KDesktopPropsPlugin::supports( _items ) ||
+ KBindingPropsPlugin::supports( _items ) ||
+ KURLPropsPlugin::supports( _items ) ||
+ KDevicePropsPlugin::supports( _items ) ||
+ KFileMetaPropsPlugin::supports( _items ) ||
+ KPreviewPropsPlugin::supports( _items );
+}
+
+void KPropertiesDialog::slotOk()
+{
+ KPropsDlgPlugin *page;
+ d->m_aborted = false;
+
+ KFilePropsPlugin * filePropsPlugin = 0L;
+ if ( m_pageList.first()->isA("KFilePropsPlugin") )
+ filePropsPlugin = static_cast<KFilePropsPlugin *>(m_pageList.first());
+
+ // If any page is dirty, then set the main one (KFilePropsPlugin) as
+ // dirty too. This is what makes it possible to save changes to a global
+ // desktop file into a local one. In other cases, it doesn't hurt.
+ for ( page = m_pageList.first(); page != 0L; page = m_pageList.next() )
+ if ( page->isDirty() && filePropsPlugin )
+ {
+ filePropsPlugin->setDirty();
+ break;
+ }
+
+ // Apply the changes in the _normal_ order of the tabs now
+ // This is because in case of renaming a file, KFilePropsPlugin will call
+ // KPropertiesDialog::rename, so other tab will be ok with whatever order
+ // BUT for file copied from templates, we need to do the renaming first !
+ for ( page = m_pageList.first(); page != 0L && !d->m_aborted; page = m_pageList.next() )
+ if ( page->isDirty() )
+ {
+ kdDebug( 250 ) << "applying changes for " << page->className() << endl;
+ page->applyChanges();
+ // applyChanges may change d->m_aborted.
+ }
+ else
+ kdDebug( 250 ) << "skipping page " << page->className() << endl;
+
+ if ( !d->m_aborted && filePropsPlugin )
+ filePropsPlugin->postApplyChanges();
+
+ if ( !d->m_aborted )
+ {
+ emit applied();
+ emit propertiesClosed();
+ deleteLater();
+ accept();
+ } // else, keep dialog open for user to fix the problem.
+}
+
+void KPropertiesDialog::slotCancel()
+{
+ emit canceled();
+ emit propertiesClosed();
+
+ deleteLater();
+ done( Rejected );
+}
+
+void KPropertiesDialog::insertPages()
+{
+ if (m_items.isEmpty())
+ return;
+
+ if ( KFilePropsPlugin::supports( m_items ) )
+ {
+ KPropsDlgPlugin *p = new KFilePropsPlugin( this );
+ insertPlugin (p);
+ }
+
+ if ( KFilePermissionsPropsPlugin::supports( m_items ) )
+ {
+ KPropsDlgPlugin *p = new KFilePermissionsPropsPlugin( this );
+ insertPlugin (p);
+ }
+
+ if ( KDesktopPropsPlugin::supports( m_items ) )
+ {
+ KPropsDlgPlugin *p = new KDesktopPropsPlugin( this );
+ insertPlugin (p);
+ }
+
+ if ( KBindingPropsPlugin::supports( m_items ) )
+ {
+ KPropsDlgPlugin *p = new KBindingPropsPlugin( this );
+ insertPlugin (p);
+ }
+
+ if ( KURLPropsPlugin::supports( m_items ) )
+ {
+ KPropsDlgPlugin *p = new KURLPropsPlugin( this );
+ insertPlugin (p);
+ }
+
+ if ( KDevicePropsPlugin::supports( m_items ) )
+ {
+ KPropsDlgPlugin *p = new KDevicePropsPlugin( this );
+ insertPlugin (p);
+ }
+
+ if ( KFileMetaPropsPlugin::supports( m_items ) )
+ {
+ KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this );
+ insertPlugin (p);
+ }
+
+ if ( KPreviewPropsPlugin::supports( m_items ) )
+ {
+ KPropsDlgPlugin *p = new KPreviewPropsPlugin( this );
+ insertPlugin (p);
+ }
+
+ if ( kapp->authorizeKAction("sharefile") &&
+ KFileSharePropsPlugin::supports( m_items ) )
+ {
+ KPropsDlgPlugin *p = new KFileSharePropsPlugin( this );
+ insertPlugin (p);
+ }
+
+ //plugins
+
+ if ( m_items.count() != 1 )
+ return;
+
+ KFileItem *item = m_items.first();
+ QString mimetype = item->mimetype();
+
+ if ( mimetype.isEmpty() )
+ return;
+
+ QString query = QString::fromLatin1(
+ "('KPropsDlg/Plugin' in ServiceTypes) and "
+ "((not exist [X-KDE-Protocol]) or "
+ " ([X-KDE-Protocol] == '%1' ) )" ).arg(item->url().protocol());
+
+ kdDebug( 250 ) << "trader query: " << query << endl;
+ KTrader::OfferList offers = KTrader::self()->query( mimetype, query );
+ KTrader::OfferList::ConstIterator it = offers.begin();
+ KTrader::OfferList::ConstIterator end = offers.end();
+ for (; it != end; ++it )
+ {
+ KPropsDlgPlugin *plugin = KParts::ComponentFactory
+ ::createInstanceFromLibrary<KPropsDlgPlugin>( (*it)->library().local8Bit().data(),
+ this,
+ (*it)->name().latin1() );
+ if ( !plugin )
+ continue;
+
+ insertPlugin( plugin );
+ }
+}
+
+void KPropertiesDialog::updateUrl( const KURL& _newUrl )
+{
+ Q_ASSERT( m_items.count() == 1 );
+ kdDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url() << endl;
+ KURL newUrl = _newUrl;
+ emit saveAs(m_singleUrl, newUrl);
+ kdDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url() << endl;
+
+ m_singleUrl = newUrl;
+ m_items.first()->setURL( newUrl );
+ assert(!m_singleUrl.isEmpty());
+ // If we have an Desktop page, set it dirty, so that a full file is saved locally
+ // Same for a URL page (because of the Name= hack)
+ for ( QPtrListIterator<KPropsDlgPlugin> it(m_pageList); it.current(); ++it )
+ if ( it.current()->isA("KExecPropsPlugin") || // KDE4 remove me
+ it.current()->isA("KURLPropsPlugin") ||
+ it.current()->isA("KDesktopPropsPlugin"))
+ {
+ //kdDebug(250) << "Setting page dirty" << endl;
+ it.current()->setDirty();
+ break;
+ }
+}
+
+void KPropertiesDialog::rename( const QString& _name )
+{
+ Q_ASSERT( m_items.count() == 1 );
+ kdDebug(250) << "KPropertiesDialog::rename " << _name << endl;
+ KURL newUrl;
+ // if we're creating from a template : use currentdir
+ if ( !m_currentDir.isEmpty() )
+ {
+ newUrl = m_currentDir;
+ newUrl.addPath( _name );
+ }
+ else
+ {
+ QString tmpurl = m_singleUrl.url();
+ if ( tmpurl.at(tmpurl.length() - 1) == '/')
+ // It's a directory, so strip the trailing slash first
+ tmpurl.truncate( tmpurl.length() - 1);
+ newUrl = tmpurl;
+ newUrl.setFileName( _name );
+ }
+ updateUrl( newUrl );
+}
+
+void KPropertiesDialog::abortApplying()
+{
+ d->m_aborted = true;
+}
+
+class KPropsDlgPlugin::KPropsDlgPluginPrivate
+{
+public:
+ KPropsDlgPluginPrivate()
+ {
+ }
+ ~KPropsDlgPluginPrivate()
+ {
+ }
+
+ bool m_bDirty;
+};
+
+KPropsDlgPlugin::KPropsDlgPlugin( KPropertiesDialog *_props )
+: QObject( _props, 0L )
+{
+ d = new KPropsDlgPluginPrivate;
+ properties = _props;
+ fontHeight = 2*properties->fontMetrics().height();
+ d->m_bDirty = false;
+}
+
+KPropsDlgPlugin::~KPropsDlgPlugin()
+{
+ delete d;
+}
+
+bool KPropsDlgPlugin::isDesktopFile( KFileItem * _item )
+{
+ // only local files
+ bool isLocal;
+ KURL url = _item->mostLocalURL( isLocal );
+ if ( !isLocal )
+ return false;
+
+ // only regular files
+ if ( !S_ISREG( _item->mode() ) )
+ return false;
+
+ QString t( url.path() );
+
+ // only if readable
+ FILE *f = fopen( QFile::encodeName(t), "r" );
+ if ( f == 0L )
+ return false;
+ fclose(f);
+
+ // return true if desktop file
+ return ( _item->mimetype() == "application/x-desktop" );
+}
+
+void KPropsDlgPlugin::setDirty( bool b )
+{
+ d->m_bDirty = b;
+}
+
+void KPropsDlgPlugin::setDirty()
+{
+ d->m_bDirty = true;
+}
+
+bool KPropsDlgPlugin::isDirty() const
+{
+ return d->m_bDirty;
+}
+
+void KPropsDlgPlugin::applyChanges()
+{
+ kdWarning(250) << "applyChanges() not implemented in page !" << endl;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class KFilePropsPlugin::KFilePropsPluginPrivate
+{
+public:
+ KFilePropsPluginPrivate()
+ {
+ dirSizeJob = 0L;
+ dirSizeUpdateTimer = 0L;
+ m_lined = 0;
+ m_freeSpaceLabel = 0;
+ }
+ ~KFilePropsPluginPrivate()
+ {
+ if ( dirSizeJob )
+ dirSizeJob->kill();
+ }
+
+ KDirSize * dirSizeJob;
+ QTimer *dirSizeUpdateTimer;
+ QFrame *m_frame;
+ bool bMultiple;
+ bool bIconChanged;
+ bool bKDesktopMode;
+ bool bDesktopFile;
+ QLabel *m_freeSpaceLabel;
+ QString mimeType;
+ QString oldFileName;
+ KLineEdit* m_lined;
+};
+
+KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props )
+ : KPropsDlgPlugin( _props )
+{
+ d = new KFilePropsPluginPrivate;
+ d->bMultiple = (properties->items().count() > 1);
+ d->bIconChanged = false;
+ d->bKDesktopMode = (QCString(qApp->name()) == "kdesktop"); // nasty heh?
+ d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items());
+ kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple << endl;
+
+ // We set this data from the first item, and we'll
+ // check that the other items match against it, resetting when not.
+ bool isLocal;
+ KFileItem * item = properties->item();
+ KURL url = item->mostLocalURL( isLocal );
+ bool isReallyLocal = item->url().isLocalFile();
+ bool bDesktopFile = isDesktopFile(item);
+ kdDebug() << "url=" << url << " bDesktopFile=" << bDesktopFile << " isLocal=" << isLocal << " isReallyLocal=" << isReallyLocal << endl;
+ mode_t mode = item->mode();
+ bool hasDirs = item->isDir() && !item->isLink();
+ bool hasRoot = url.path() == QString::fromLatin1("/");
+ QString iconStr = KMimeType::iconForURL(url, mode);
+ QString directory = properties->kurl().directory();
+ QString protocol = properties->kurl().protocol();
+ QString mimeComment = item->mimeComment();
+ d->mimeType = item->mimetype();
+ bool hasTotalSize;
+ KIO::filesize_t totalSize = item->size(hasTotalSize);
+ QString magicMimeComment;
+ if ( isLocal ) {
+ KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
+ if ( magicMimeType->name() != KMimeType::defaultMimeType() )
+ magicMimeComment = magicMimeType->comment();
+ }
+
+ // Those things only apply to 'single file' mode
+ QString filename = QString::null;
+ bool isTrash = false;
+ bool isDevice = false;
+ m_bFromTemplate = false;
+
+ // And those only to 'multiple' mode
+ uint iDirCount = hasDirs ? 1 : 0;
+ uint iFileCount = 1-iDirCount;
+
+ d->m_frame = properties->addPage (i18n("&General"));
+
+ QVBoxLayout *vbl = new QVBoxLayout( d->m_frame, 0,
+ KDialog::spacingHint(), "vbl");
+ QGridLayout *grid = new QGridLayout(0, 3); // unknown rows
+ grid->setColStretch(0, 0);
+ grid->setColStretch(1, 0);
+ grid->setColStretch(2, 1);
+ grid->addColSpacing(1, KDialog::spacingHint());
+ vbl->addLayout(grid);
+ int curRow = 0;
+
+ if ( !d->bMultiple )
+ {
+ QString path;
+ if ( !m_bFromTemplate ) {
+ isTrash = ( properties->kurl().protocol().find( "trash", 0, false)==0 );
+ if ( properties->kurl().protocol().find("device", 0, false)==0)
+ isDevice = true;
+ // Extract the full name, but without file: for local files
+ if ( isReallyLocal )
+ path = properties->kurl().path();
+ else
+ path = properties->kurl().prettyURL();
+ } else {
+ path = properties->currentDir().path(1) + properties->defaultName();
+ directory = properties->currentDir().prettyURL();
+ }
+
+ if (KExecPropsPlugin::supports(properties->items()) || // KDE4 remove me
+ d->bDesktopFile ||
+ KBindingPropsPlugin::supports(properties->items())) {
+ determineRelativePath( path );
+ }
+
+ // Extract the file name only
+ filename = properties->defaultName();
+ if ( filename.isEmpty() ) { // no template
+ filename = item->name(); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system
+ } else {
+ m_bFromTemplate = true;
+ setDirty(); // to enforce that the copy happens
+ }
+ d->oldFileName = filename;
+
+ // Make it human-readable
+ filename = nameFromFileName( filename );
+
+ if ( d->bKDesktopMode && d->bDesktopFile ) {
+ KDesktopFile config( url.path(), true /* readonly */ );
+ if ( config.hasKey( "Name" ) ) {
+ filename = config.readName();
+ }
+ }
+
+ oldName = filename;
+ }
+ else
+ {
+ // Multiple items: see what they have in common
+ KFileItemList items = properties->items();
+ KFileItemListIterator it( items );
+ for ( ++it /*no need to check the first one again*/ ; it.current(); ++it )
+ {
+ KURL url = (*it)->url();
+ kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyURL() << endl;
+ // The list of things we check here should match the variables defined
+ // at the beginning of this method.
+ if ( url.isLocalFile() != isLocal )
+ isLocal = false; // not all local
+ if ( bDesktopFile && isDesktopFile(*it) != bDesktopFile )
+ bDesktopFile = false; // not all desktop files
+ if ( (*it)->mode() != mode )
+ mode = (mode_t)0;
+ if ( KMimeType::iconForURL(url, mode) != iconStr )
+ iconStr = "kmultiple";
+ if ( url.directory() != directory )
+ directory = QString::null;
+ if ( url.protocol() != protocol )
+ protocol = QString::null;
+ if ( !mimeComment.isNull() && (*it)->mimeComment() != mimeComment )
+ mimeComment = QString::null;
+ if ( isLocal && !magicMimeComment.isNull() ) {
+ KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
+ if ( magicMimeType->comment() != magicMimeComment )
+ magicMimeComment = QString::null;
+ }
+
+ if ( url.path() == QString::fromLatin1("/") )
+ hasRoot = true;
+ if ( (*it)->isDir() && !(*it)->isLink() )
+ {
+ iDirCount++;
+ hasDirs = true;
+ }
+ else
+ {
+ iFileCount++;
+ bool hasSize;
+ totalSize += (*it)->size(hasSize);
+ hasTotalSize = hasTotalSize || hasSize;
+ }
+ }
+ }
+
+ if (!isReallyLocal && !protocol.isEmpty())
+ {
+ directory += ' ';
+ directory += '(';
+ directory += protocol;
+ directory += ')';
+ }
+
+ if ( !isDevice && !isTrash && (bDesktopFile || S_ISDIR(mode)) && !d->bMultiple /*not implemented for multiple*/ )
+ {
+ KIconButton *iconButton = new KIconButton( d->m_frame );
+ int bsize = 66 + 2 * iconButton->style().pixelMetric(QStyle::PM_ButtonMargin);
+ iconButton->setFixedSize(bsize, bsize);
+ iconButton->setIconSize(48);
+ iconButton->setStrictIconSize(false);
+ // This works for everything except Device icons on unmounted devices
+ // So we have to really open .desktop files
+ QString iconStr = KMimeType::findByURL( url, mode )->icon( url, isLocal );
+ if ( bDesktopFile && isLocal )
+ {
+ KDesktopFile config( url.path(), true );
+ config.setDesktopGroup();
+ iconStr = config.readEntry( "Icon" );
+ if ( config.hasDeviceType() )
+ iconButton->setIconType( KIcon::Desktop, KIcon::Device );
+ else
+ iconButton->setIconType( KIcon::Desktop, KIcon::Application );
+ } else
+ iconButton->setIconType( KIcon::Desktop, KIcon::FileSystem );
+ iconButton->setIcon(iconStr);
+ iconArea = iconButton;
+ connect( iconButton, SIGNAL( iconChanged(QString) ),
+ this, SLOT( slotIconChanged() ) );
+ } else {
+ QLabel *iconLabel = new QLabel( d->m_frame );
+ int bsize = 66 + 2 * iconLabel->style().pixelMetric(QStyle::PM_ButtonMargin);
+ iconLabel->setFixedSize(bsize, bsize);
+ iconLabel->setPixmap( KGlobal::iconLoader()->loadIcon( iconStr, KIcon::Desktop, 48) );
+ iconArea = iconLabel;
+ }
+ grid->addWidget(iconArea, curRow, 0, AlignLeft);
+
+ if (d->bMultiple || isTrash || isDevice || hasRoot)
+ {
+ QLabel *lab = new QLabel(d->m_frame );
+ if ( d->bMultiple )
+ lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) );
+ else
+ lab->setText( filename );
+ nameArea = lab;
+ } else
+ {
+ d->m_lined = new KLineEdit( d->m_frame );
+ d->m_lined->setText(filename);
+ nameArea = d->m_lined;
+ d->m_lined->setFocus();
+
+ // Enhanced rename: Don't highlight the file extension.
+ QString pattern;
+ KServiceTypeFactory::self()->findFromPattern( filename, &pattern );
+ if (!pattern.isEmpty() && pattern.at(0)=='*' && pattern.find('*',1)==-1)
+ d->m_lined->setSelection(0, filename.length()-pattern.stripWhiteSpace().length()+1);
+ else
+ {
+ int lastDot = filename.findRev('.');
+ if (lastDot > 0)
+ d->m_lined->setSelection(0, lastDot);
+ }
+
+ connect( d->m_lined, SIGNAL( textChanged( const QString & ) ),
+ this, SLOT( nameFileChanged(const QString & ) ) );
+ }
+
+ grid->addWidget(nameArea, curRow++, 2);
+
+ KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame);
+ grid->addMultiCellWidget(sep, curRow, curRow, 0, 2);
+ ++curRow;
+
+ QLabel *l;
+ if ( !mimeComment.isEmpty() && !isDevice && !isTrash)
+ {
+ l = new QLabel(i18n("Type:"), d->m_frame );
+
+ grid->addWidget(l, curRow, 0);
+
+ QHBox *box = new QHBox(d->m_frame);
+ box->setSpacing(20);
+ l = new QLabel(mimeComment, box );
+
+#ifdef Q_WS_X11
+ //TODO: wrap for win32 or mac?
+ QPushButton *button = new QPushButton(box);
+
+ QIconSet iconSet = SmallIconSet(QString::fromLatin1("configure"));
+ QPixmap pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal );
+ button->setIconSet( iconSet );
+ button->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
+ if ( d->mimeType == KMimeType::defaultMimeType() )
+ QToolTip::add(button, i18n("Create new file type"));
+ else
+ QToolTip::add(button, i18n("Edit file type"));
+
+ connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() ));
+
+ if (!kapp->authorizeKAction("editfiletype"))
+ button->hide();
+#endif
+
+ grid->addWidget(box, curRow++, 2);
+ }
+
+ if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
+ {
+ l = new QLabel(i18n("Contents:"), d->m_frame );
+ grid->addWidget(l, curRow, 0);
+
+ l = new QLabel(magicMimeComment, d->m_frame );
+ grid->addWidget(l, curRow++, 2);
+ }
+
+ if ( !directory.isEmpty() )
+ {
+ l = new QLabel( i18n("Location:"), d->m_frame );
+ grid->addWidget(l, curRow, 0);
+
+ l = new KSqueezedTextLabel( d->m_frame );
+ l->setText( directory );
+ grid->addWidget(l, curRow++, 2);
+ }
+
+ if( hasDirs || hasTotalSize ) {
+ l = new QLabel(i18n("Size:"), d->m_frame );
+ grid->addWidget(l, curRow, 0);
+
+ m_sizeLabel = new QLabel( d->m_frame );
+ grid->addWidget( m_sizeLabel, curRow++, 2 );
+ } else {
+ m_sizeLabel = 0;
+ }
+
+ if ( !hasDirs ) // Only files [and symlinks]
+ {
+ if(hasTotalSize) {
+ m_sizeLabel->setText(KIO::convertSizeWithBytes(totalSize));
+ }
+
+ m_sizeDetermineButton = 0L;
+ m_sizeStopButton = 0L;
+ }
+ else // Directory
+ {
+ QHBoxLayout * sizelay = new QHBoxLayout(KDialog::spacingHint());
+ grid->addLayout( sizelay, curRow++, 2 );
+
+ // buttons
+ m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
+ m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
+ connect( m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) );
+ connect( m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) );
+ sizelay->addWidget(m_sizeDetermineButton, 0);
+ sizelay->addWidget(m_sizeStopButton, 0);
+ sizelay->addStretch(10); // so that the buttons don't grow horizontally
+
+ // auto-launch for local dirs only, and not for '/'
+ if ( isLocal && !hasRoot )
+ {
+ m_sizeDetermineButton->setText( i18n("Refresh") );
+ slotSizeDetermine();
+ }
+ else
+ m_sizeStopButton->setEnabled( false );
+ }
+
+ if (!d->bMultiple && item->isLink()) {
+ l = new QLabel(i18n("Points to:"), d->m_frame );
+ grid->addWidget(l, curRow, 0);
+
+ l = new KSqueezedTextLabel(item->linkDest(), d->m_frame );
+ grid->addWidget(l, curRow++, 2);
+ }
+
+ if (!d->bMultiple) // Dates for multiple don't make much sense...
+ {
+ QDateTime dt;
+ bool hasTime;
+ time_t tim = item->time(KIO::UDS_CREATION_TIME, hasTime);
+ if ( hasTime )
+ {
+ l = new QLabel(i18n("Created:"), d->m_frame );
+ grid->addWidget(l, curRow, 0);
+
+ dt.setTime_t( tim );
+ l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
+ grid->addWidget(l, curRow++, 2);
+ }
+
+ tim = item->time(KIO::UDS_MODIFICATION_TIME, hasTime);
+ if ( hasTime )
+ {
+ l = new QLabel(i18n("Modified:"), d->m_frame );
+ grid->addWidget(l, curRow, 0);
+
+ dt.setTime_t( tim );
+ l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
+ grid->addWidget(l, curRow++, 2);
+ }
+
+ tim = item->time(KIO::UDS_ACCESS_TIME, hasTime);
+ if ( hasTime )
+ {
+ l = new QLabel(i18n("Accessed:"), d->m_frame );
+ grid->addWidget(l, curRow, 0);
+
+ dt.setTime_t( tim );
+ l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
+ grid->addWidget(l, curRow++, 2);
+ }
+ }
+
+ if ( isLocal && hasDirs ) // only for directories
+ {
+ sep = new KSeparator( KSeparator::HLine, d->m_frame);
+ grid->addMultiCellWidget(sep, curRow, curRow, 0, 2);
+ ++curRow;
+
+ QString mountPoint = KIO::findPathMountPoint( url.path() );
+
+ if (mountPoint != "/")
+ {
+ l = new QLabel(i18n("Mounted on:"), d->m_frame );
+ grid->addWidget(l, curRow, 0);
+
+ l = new KSqueezedTextLabel( mountPoint, d->m_frame );
+ grid->addWidget( l, curRow++, 2 );
+ }
+
+ l = new QLabel(i18n("Free disk space:"), d->m_frame );
+ grid->addWidget(l, curRow, 0);
+
+ d->m_freeSpaceLabel = new QLabel( d->m_frame );
+ grid->addWidget( d->m_freeSpaceLabel, curRow++, 2 );
+
+ KDiskFreeSp * job = new KDiskFreeSp;
+ connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&,
+ const unsigned long&, const QString& ) ),
+ this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&,
+ const unsigned long&, const QString& ) ) );
+ job->readDF( mountPoint );
+ }
+
+ vbl->addStretch(1);
+}
+
+// QString KFilePropsPlugin::tabName () const
+// {
+// return i18n ("&General");
+// }
+
+void KFilePropsPlugin::setFileNameReadOnly( bool ro )
+{
+ if ( d->m_lined )
+ {
+ d->m_lined->setReadOnly( ro );
+ if (ro)
+ {
+ // Don't put the initial focus on the line edit when it is ro
+ QPushButton *button = properties->actionButton(KDialogBase::Ok);
+ if (button)
+ button->setFocus();
+ }
+ }
+}
+
+void KFilePropsPlugin::slotEditFileType()
+{
+#ifdef Q_WS_X11
+ QString mime;
+ if ( d->mimeType == KMimeType::defaultMimeType() ) {
+ int pos = d->oldFileName.findRev( '.' );
+ if ( pos != -1 )
+ mime = "*" + d->oldFileName.mid(pos);
+ else
+ mime = "*";
+ }
+ else
+ mime = d->mimeType;
+ //TODO: wrap for win32 or mac?
+ QString keditfiletype = QString::fromLatin1("keditfiletype");
+ KRun::runCommand( keditfiletype
+ + " --parent " + QString::number( (ulong)properties->topLevelWidget()->winId())
+ + " " + KProcess::quote(mime),
+ keditfiletype, keditfiletype /*unused*/);
+#endif
+}
+
+void KFilePropsPlugin::slotIconChanged()
+{
+ d->bIconChanged = true;
+ emit changed();
+}
+
+void KFilePropsPlugin::nameFileChanged(const QString &text )
+{
+ properties->enableButtonOK(!text.isEmpty());
+ emit changed();
+}
+
+void KFilePropsPlugin::determineRelativePath( const QString & path )
+{
+ // now let's make it relative
+ QStringList dirs;
+ if (KBindingPropsPlugin::supports(properties->items()))
+ {
+ m_sRelativePath =KGlobal::dirs()->relativeLocation("mime", path);
+ if (m_sRelativePath.startsWith("/"))
+ m_sRelativePath = QString::null;
+ }
+ else
+ {
+ m_sRelativePath =KGlobal::dirs()->relativeLocation("apps", path);
+ if (m_sRelativePath.startsWith("/"))
+ {
+ m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
+ if (m_sRelativePath.startsWith("/"))
+ m_sRelativePath = QString::null;
+ else
+ m_sRelativePath = path;
+ }
+ }
+ if ( m_sRelativePath.isEmpty() )
+ {
+ if (KBindingPropsPlugin::supports(properties->items()))
+ kdWarning(250) << "Warning : editing a mimetype file out of the mimetype dirs!" << endl;
+ }
+}
+
+void KFilePropsPlugin::slotFoundMountPoint( const QString&,
+ unsigned long kBSize,
+ unsigned long /*kBUsed*/,
+ unsigned long kBAvail )
+{
+ d->m_freeSpaceLabel->setText(
+ // xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part.
+ i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
+ .arg(KIO::convertSizeFromKB(kBAvail))
+ .arg(KIO::convertSizeFromKB(kBSize))
+ .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
+}
+
+// attention: copy&paste below, due to compiler bug
+// it doesn't like those unsigned long parameters -- unsigned long& are ok :-/
+void KFilePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize,
+ const unsigned long& /*kBUsed*/,
+ const unsigned long& kBAvail,
+ const QString& )
+{
+ d->m_freeSpaceLabel->setText(
+ // xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part.
+ i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
+ .arg(KIO::convertSizeFromKB(kBAvail))
+ .arg(KIO::convertSizeFromKB(kBSize))
+ .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
+}
+
+void KFilePropsPlugin::slotDirSizeUpdate()
+{
+ KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
+ KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
+ KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
+ m_sizeLabel->setText( i18n("Calculating... %1 (%2)\n%3, %4")
+ .arg(KIO::convertSize(totalSize))
+ .arg(KGlobal::locale()->formatNumber(totalSize, 0))
+ .arg(i18n("1 file","%n files",totalFiles))
+ .arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs)));
+}
+
+void KFilePropsPlugin::slotDirSizeFinished( KIO::Job * job )
+{
+ if (job->error())
+ m_sizeLabel->setText( job->errorString() );
+ else
+ {
+ KIO::filesize_t totalSize = static_cast<KDirSize*>(job)->totalSize();
+ KIO::filesize_t totalFiles = static_cast<KDirSize*>(job)->totalFiles();
+ KIO::filesize_t totalSubdirs = static_cast<KDirSize*>(job)->totalSubdirs();
+ m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4")
+ .arg(KIO::convertSize(totalSize))
+ .arg(KGlobal::locale()->formatNumber(totalSize, 0))
+ .arg(i18n("1 file","%n files",totalFiles))
+ .arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs)));
+ }
+ m_sizeStopButton->setEnabled(false);
+ // just in case you change something and try again :)
+ m_sizeDetermineButton->setText( i18n("Refresh") );
+ m_sizeDetermineButton->setEnabled(true);
+ d->dirSizeJob = 0L;
+ delete d->dirSizeUpdateTimer;
+ d->dirSizeUpdateTimer = 0L;
+}
+
+void KFilePropsPlugin::slotSizeDetermine()
+{
+ m_sizeLabel->setText( i18n("Calculating...") );
+ kdDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" << properties->item() << endl;
+ kdDebug(250) << " URL=" << properties->item()->url().url() << endl;
+ d->dirSizeJob = KDirSize::dirSizeJob( properties->items() );
+ d->dirSizeUpdateTimer = new QTimer(this);
+ connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ),
+ SLOT( slotDirSizeUpdate() ) );
+ d->dirSizeUpdateTimer->start(500);
+ connect( d->dirSizeJob, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotDirSizeFinished( KIO::Job * ) ) );
+ m_sizeStopButton->setEnabled(true);
+ m_sizeDetermineButton->setEnabled(false);
+
+ // also update the "Free disk space" display
+ if ( d->m_freeSpaceLabel )
+ {
+ bool isLocal;
+ KFileItem * item = properties->item();
+ KURL url = item->mostLocalURL( isLocal );
+ QString mountPoint = KIO::findPathMountPoint( url.path() );
+
+ KDiskFreeSp * job = new KDiskFreeSp;
+ connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&,
+ const unsigned long&, const QString& ) ),
+ this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&,
+ const unsigned long&, const QString& ) ) );
+ job->readDF( mountPoint );
+ }
+}
+
+void KFilePropsPlugin::slotSizeStop()
+{
+ if ( d->dirSizeJob )
+ {
+ m_sizeLabel->setText( i18n("Stopped") );
+ d->dirSizeJob->kill();
+ d->dirSizeJob = 0;
+ }
+ if ( d->dirSizeUpdateTimer )
+ d->dirSizeUpdateTimer->stop();
+
+ m_sizeStopButton->setEnabled(false);
+ m_sizeDetermineButton->setEnabled(true);
+}
+
+KFilePropsPlugin::~KFilePropsPlugin()
+{
+ delete d;
+}
+
+bool KFilePropsPlugin::supports( KFileItemList /*_items*/ )
+{
+ return true;
+}
+
+// Don't do this at home
+void qt_enter_modal( QWidget *widget );
+void qt_leave_modal( QWidget *widget );
+
+void KFilePropsPlugin::applyChanges()
+{
+ if ( d->dirSizeJob )
+ slotSizeStop();
+
+ kdDebug(250) << "KFilePropsPlugin::applyChanges" << endl;
+
+ if (nameArea->inherits("QLineEdit"))
+ {
+ QString n = ((QLineEdit *) nameArea)->text();
+ // Remove trailing spaces (#4345)
+ while ( n[n.length()-1].isSpace() )
+ n.truncate( n.length() - 1 );
+ if ( n.isEmpty() )
+ {
+ KMessageBox::sorry( properties, i18n("The new file name is empty."));
+ properties->abortApplying();
+ return;
+ }
+
+ // Do we need to rename the file ?
+ kdDebug(250) << "oldname = " << oldName << endl;
+ kdDebug(250) << "newname = " << n << endl;
+ if ( oldName != n || m_bFromTemplate ) { // true for any from-template file
+ KIO::Job * job = 0L;
+ KURL oldurl = properties->kurl();
+
+ QString newFileName = KIO::encodeFileName(n);
+ if (d->bDesktopFile && !newFileName.endsWith(".desktop") && !newFileName.endsWith(".kdelnk"))
+ newFileName += ".desktop";
+
+ // Tell properties. Warning, this changes the result of properties->kurl() !
+ properties->rename( newFileName );
+
+ // Update also relative path (for apps and mimetypes)
+ if ( !m_sRelativePath.isEmpty() )
+ determineRelativePath( properties->kurl().path() );
+
+ kdDebug(250) << "New URL = " << properties->kurl().url() << endl;
+ kdDebug(250) << "old = " << oldurl.url() << endl;
+
+ // Don't remove the template !!
+ if ( !m_bFromTemplate ) // (normal renaming)
+ job = KIO::move( oldurl, properties->kurl() );
+ else // Copying a template
+ job = KIO::copy( oldurl, properties->kurl() );
+
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotCopyFinished( KIO::Job * ) ) );
+ connect( job, SIGNAL( renamed( KIO::Job *, const KURL &, const KURL & ) ),
+ SLOT( slotFileRenamed( KIO::Job *, const KURL &, const KURL & ) ) );
+ // wait for job
+ QWidget dummy(0,0,WType_Dialog|WShowModal);
+ qt_enter_modal(&dummy);
+ qApp->enter_loop();
+ qt_leave_modal(&dummy);
+ return;
+ }
+ properties->updateUrl(properties->kurl());
+ // Update also relative path (for apps and mimetypes)
+ if ( !m_sRelativePath.isEmpty() )
+ determineRelativePath( properties->kurl().path() );
+ }
+
+ // No job, keep going
+ slotCopyFinished( 0L );
+}
+
+void KFilePropsPlugin::slotCopyFinished( KIO::Job * job )
+{
+ kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl;
+ if (job)
+ {
+ // allow apply() to return
+ qApp->exit_loop();
+ if ( job->error() )
+ {
+ job->showErrorDialog( d->m_frame );
+ // Didn't work. Revert the URL to the old one
+ properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcURLs().first() );
+ properties->abortApplying(); // Don't apply the changes to the wrong file !
+ return;
+ }
+ }
+
+ assert( properties->item() );
+ assert( !properties->item()->url().isEmpty() );
+
+ // Save the file where we can -> usually in ~/.kde/...
+ if (KBindingPropsPlugin::supports(properties->items()) && !m_sRelativePath.isEmpty())
+ {
+ KURL newURL;
+ newURL.setPath( locateLocal("mime", m_sRelativePath) );
+ properties->updateUrl( newURL );
+ }
+ else if (d->bDesktopFile && !m_sRelativePath.isEmpty())
+ {
+ kdDebug(250) << "KFilePropsPlugin::slotCopyFinished " << m_sRelativePath << endl;
+ KURL newURL;
+ newURL.setPath( KDesktopFile::locateLocal(m_sRelativePath) );
+ kdDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path() << endl;
+ properties->updateUrl( newURL );
+ }
+
+ if ( d->bKDesktopMode && d->bDesktopFile ) {
+ // Renamed? Update Name field
+ if ( d->oldFileName != properties->kurl().fileName() || m_bFromTemplate ) {
+ KDesktopFile config( properties->kurl().path() );
+ QString nameStr = nameFromFileName(properties->kurl().fileName());
+ config.writeEntry( "Name", nameStr );
+ config.writeEntry( "Name", nameStr, true, false, true );
+ }
+ }
+}
+
+void KFilePropsPlugin::applyIconChanges()
+{
+ KIconButton *iconButton = ::qt_cast<KIconButton *>( iconArea );
+ if ( !iconButton || !d->bIconChanged )
+ return;
+ // handle icon changes - only local files (or pseudo-local) for now
+ // TODO: Use KTempFile and KIO::file_copy with overwrite = true
+ KURL url = properties->kurl();
+ url = KIO::NetAccess::mostLocalURL( url, properties );
+ if (url.isLocalFile()) {
+ QString path;
+
+ if (S_ISDIR(properties->item()->mode()))
+ {
+ path = url.path(1) + QString::fromLatin1(".directory");
+ // don't call updateUrl because the other tabs (i.e. permissions)
+ // apply to the directory, not the .directory file.
+ }
+ else
+ path = url.path();
+
+ // Get the default image
+ QString str = KMimeType::findByURL( url,
+ properties->item()->mode(),
+ true )->KServiceType::icon();
+ // Is it another one than the default ?
+ QString sIcon;
+ if ( str != iconButton->icon() )
+ sIcon = iconButton->icon();
+ // (otherwise write empty value)
+
+ kdDebug(250) << "**" << path << "**" << endl;
+ QFile f( path );
+
+ // If default icon and no .directory file -> don't create one
+ if ( !sIcon.isEmpty() || f.exists() )
+ {
+ if ( !f.open( IO_ReadWrite ) ) {
+ KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
+ "have sufficient access to write to <b>%1</b>.</qt>").arg(path));
+ return;
+ }
+ f.close();
+
+ KDesktopFile cfg(path);
+ kdDebug(250) << "sIcon = " << (sIcon) << endl;
+ kdDebug(250) << "str = " << (str) << endl;
+ cfg.writeEntry( "Icon", sIcon );
+ cfg.sync();
+ }
+ }
+}
+
+void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KURL &, const KURL & newUrl )
+{
+ // This is called in case of an existing local file during the copy/move operation,
+ // if the user chooses Rename.
+ properties->updateUrl( newUrl );
+}
+
+void KFilePropsPlugin::postApplyChanges()
+{
+ // Save the icon only after applying the permissions changes (#46192)
+ applyIconChanges();
+
+ KURL::List lst;
+ KFileItemList items = properties->items();
+ for ( KFileItemListIterator it( items ); it.current(); ++it )
+ lst.append((*it)->url());
+ KDirNotify_stub allDirNotify("*", "KDirNotify*");
+ allDirNotify.FilesChanged( lst );
+}
+
+class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
+{
+public:
+ KFilePermissionsPropsPluginPrivate()
+ {
+ }
+ ~KFilePermissionsPropsPluginPrivate()
+ {
+ }
+
+ QFrame *m_frame;
+ QCheckBox *cbRecursive;
+ QLabel *explanationLabel;
+ QComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo;
+ QCheckBox *extraCheckbox;
+ mode_t partialPermissions;
+ KFilePermissionsPropsPlugin::PermissionsMode pmode;
+ bool canChangePermissions;
+ bool isIrregular;
+ bool hasExtendedACL;
+ KACL extendedACL;
+ KACL defaultACL;
+ bool fileSystemSupportsACLs;
+};
+
+#define UniOwner (S_IRUSR|S_IWUSR|S_IXUSR)
+#define UniGroup (S_IRGRP|S_IWGRP|S_IXGRP)
+#define UniOthers (S_IROTH|S_IWOTH|S_IXOTH)
+#define UniRead (S_IRUSR|S_IRGRP|S_IROTH)
+#define UniWrite (S_IWUSR|S_IWGRP|S_IWOTH)
+#define UniExec (S_IXUSR|S_IXGRP|S_IXOTH)
+#define UniSpecial (S_ISUID|S_ISGID|S_ISVTX)
+
+// synced with PermissionsTarget
+const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers};
+const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 };
+
+// synced with PermissionsMode and standardPermissions
+const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = {
+ { I18N_NOOP("Forbidden"),
+ I18N_NOOP("Can Read"),
+ I18N_NOOP("Can Read & Write"),
+ 0 },
+ { I18N_NOOP("Forbidden"),
+ I18N_NOOP("Can View Content"),
+ I18N_NOOP("Can View & Modify Content"),
+ 0 },
+ { 0, 0, 0, 0}, // no texts for links
+ { I18N_NOOP("Forbidden"),
+ I18N_NOOP("Can View Content & Read"),
+ I18N_NOOP("Can View/Read & Modify/Write"),
+ 0 }
+};
+
+
+KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props )
+ : KPropsDlgPlugin( _props )
+{
+ d = new KFilePermissionsPropsPluginPrivate;
+ d->cbRecursive = 0L;
+ grpCombo = 0L; grpEdit = 0;
+ usrEdit = 0L;
+ QString path = properties->kurl().path(-1);
+ QString fname = properties->kurl().fileName();
+ bool isLocal = properties->kurl().isLocalFile();
+ bool isTrash = ( properties->kurl().protocol().find("trash", 0, false)==0 );
+ bool IamRoot = (geteuid() == 0);
+
+ KFileItem * item = properties->item();
+ bool isLink = item->isLink();
+ bool isDir = item->isDir(); // all dirs
+ bool hasDir = item->isDir(); // at least one dir
+ permissions = item->permissions(); // common permissions to all files
+ d->partialPermissions = permissions; // permissions that only some files have (at first we take everything)
+ d->isIrregular = isIrregular(permissions, isDir, isLink);
+ strOwner = item->user();
+ strGroup = item->group();
+ d->hasExtendedACL = item->ACL().isExtended() || item->defaultACL().isValid();
+ d->extendedACL = item->ACL();
+ d->defaultACL = item->defaultACL();
+ d->fileSystemSupportsACLs = false;
+
+ if ( properties->items().count() > 1 )
+ {
+ // Multiple items: see what they have in common
+ KFileItemList items = properties->items();
+ KFileItemListIterator it( items );
+ for ( ++it /*no need to check the first one again*/ ; it.current(); ++it )
+ {
+ if (!d->isIrregular)
+ d->isIrregular |= isIrregular((*it)->permissions(),
+ (*it)->isDir() == isDir,
+ (*it)->isLink() == isLink);
+ d->hasExtendedACL = d->hasExtendedACL || (*it)->hasExtendedACL();
+ if ( (*it)->isLink() != isLink )
+ isLink = false;
+ if ( (*it)->isDir() != isDir )
+ isDir = false;
+ hasDir |= (*it)->isDir();
+ if ( (*it)->permissions() != permissions )
+ {
+ permissions &= (*it)->permissions();
+ d->partialPermissions |= (*it)->permissions();
+ }
+ if ( (*it)->user() != strOwner )
+ strOwner = QString::null;
+ if ( (*it)->group() != strGroup )
+ strGroup = QString::null;
+ }
+ }
+
+ if (isLink)
+ d->pmode = PermissionsOnlyLinks;
+ else if (isDir)
+ d->pmode = PermissionsOnlyDirs;
+ else if (hasDir)
+ d->pmode = PermissionsMixed;
+ else
+ d->pmode = PermissionsOnlyFiles;
+
+ // keep only what's not in the common permissions
+ d->partialPermissions = d->partialPermissions & ~permissions;
+
+ bool isMyFile = false;
+
+ if (isLocal && !strOwner.isEmpty()) { // local files, and all owned by the same person
+ struct passwd *myself = getpwuid( geteuid() );
+ if ( myself != 0L )
+ {
+ isMyFile = (strOwner == QString::fromLocal8Bit(myself->pw_name));
+ } else
+ kdWarning() << "I don't exist ?! geteuid=" << geteuid() << endl;
+ } else {
+ //We don't know, for remote files, if they are ours or not.
+ //So we let the user change permissions, and
+ //KIO::chmod will tell, if he had no right to do it.
+ isMyFile = true;
+ }
+
+ d->canChangePermissions = (isMyFile || IamRoot) && (!isLink);
+
+
+ // create GUI
+
+ d->m_frame = properties->addPage(i18n("&Permissions"));
+
+ QBoxLayout *box = new QVBoxLayout( d->m_frame, 0, KDialog::spacingHint() );
+
+ QWidget *l;
+ QLabel *lbl;
+ QGroupBox *gb;
+ QGridLayout *gl;
+ QPushButton* pbAdvancedPerm = 0;
+
+ /* Group: Access Permissions */
+ gb = new QGroupBox ( 0, Qt::Vertical, i18n("Access Permissions"), d->m_frame );
+ gb->layout()->setSpacing(KDialog::spacingHint());
+ gb->layout()->setMargin(KDialog::marginHint());
+ box->addWidget (gb);
+
+ gl = new QGridLayout (gb->layout(), 7, 2);
+ gl->setColStretch(1, 1);
+
+ l = d->explanationLabel = new QLabel( "", gb );
+ if (isLink)
+ d->explanationLabel->setText(i18n("This file is a link and does not have permissions.",
+ "All files are links and do not have permissions.",
+ properties->items().count()));
+ else if (!d->canChangePermissions)
+ d->explanationLabel->setText(i18n("Only the owner can change permissions."));
+ gl->addMultiCellWidget(l, 0, 0, 0, 1);
+
+ lbl = new QLabel( i18n("O&wner:"), gb);
+ gl->addWidget(lbl, 1, 0);
+ l = d->ownerPermCombo = new QComboBox(gb);
+ lbl->setBuddy(l);
+ gl->addWidget(l, 1, 1);
+ connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() ));
+ QWhatsThis::add(l, i18n("Specifies the actions that the owner is allowed to do."));
+
+ lbl = new QLabel( i18n("Gro&up:"), gb);
+ gl->addWidget(lbl, 2, 0);
+ l = d->groupPermCombo = new QComboBox(gb);
+ lbl->setBuddy(l);
+ gl->addWidget(l, 2, 1);
+ connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() ));
+ QWhatsThis::add(l, i18n("Specifies the actions that the members of the group are allowed to do."));
+
+ lbl = new QLabel( i18n("O&thers:"), gb);
+ gl->addWidget(lbl, 3, 0);
+ l = d->othersPermCombo = new QComboBox(gb);
+ lbl->setBuddy(l);
+ gl->addWidget(l, 3, 1);
+ connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() ));
+ QWhatsThis::add(l, i18n("Specifies the actions that all users, who are neither "
+ "owner nor in the group, are allowed to do."));
+
+ if (!isLink) {
+ l = d->extraCheckbox = new QCheckBox(hasDir ?
+ i18n("Only own&er can rename and delete folder content") :
+ i18n("Is &executable"),
+ gb );
+ connect( d->extraCheckbox, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
+ gl->addWidget(l, 4, 1);
+ QWhatsThis::add(l, hasDir ? i18n("Enable this option to allow only the folder's owner to "
+ "delete or rename the contained files and folders. Other "
+ "users can only add new files, which requires the 'Modify "
+ "Content' permission.")
+ : i18n("Enable this option to mark the file as executable. This only makes "
+ "sense for programs and scripts. It is required when you want to "
+ "execute them."));
+
+ QLayoutItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
+ gl->addMultiCell(spacer, 5, 5, 0, 1);
+
+ pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb);
+ gl->addMultiCellWidget(pbAdvancedPerm, 6, 6, 0, 1, AlignRight);
+ connect(pbAdvancedPerm, SIGNAL( clicked() ), this, SLOT( slotShowAdvancedPermissions() ));
+ }
+ else
+ d->extraCheckbox = 0;
+
+
+ /**** Group: Ownership ****/
+ gb = new QGroupBox ( 0, Qt::Vertical, i18n("Ownership"), d->m_frame );
+ gb->layout()->setSpacing(KDialog::spacingHint());
+ gb->layout()->setMargin(KDialog::marginHint());
+ box->addWidget (gb);
+
+ gl = new QGridLayout (gb->layout(), 4, 3);
+ gl->addRowSpacing(0, 10);
+
+ /*** Set Owner ***/
+ l = new QLabel( i18n("User:"), gb );
+ gl->addWidget (l, 1, 0);
+
+ /* GJ: Don't autocomplete more than 1000 users. This is a kind of random
+ * value. Huge sites having 10.000+ user have a fair chance of using NIS,
+ * (possibly) making this unacceptably slow.
+ * OTOH, it is nice to offer this functionality for the standard user.
+ */
+ int i, maxEntries = 1000;
+ struct passwd *user;
+ struct group *ge;
+
+ /* File owner: For root, offer a KLineEdit with autocompletion.
+ * For a user, who can never chown() a file, offer a QLabel.
+ */
+ if (IamRoot && isLocal)
+ {
+ usrEdit = new KLineEdit( gb );
+ KCompletion *kcom = usrEdit->completionObject();
+ kcom->setOrder(KCompletion::Sorted);
+ setpwent();
+ for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); i++)
+ kcom->addItem(QString::fromLatin1(user->pw_name));
+ endpwent();
+ usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto :
+ KGlobalSettings::CompletionNone);
+ usrEdit->setText(strOwner);
+ gl->addWidget(usrEdit, 1, 1);
+ connect( usrEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ }
+ else
+ {
+ l = new QLabel(strOwner, gb);
+ gl->addWidget(l, 1, 1);
+ }
+
+ /*** Set Group ***/
+
+ QStringList groupList;
+ QCString strUser;
+ user = getpwuid(geteuid());
+ if (user != 0L)
+ strUser = user->pw_name;
+
+#ifdef Q_OS_UNIX
+ setgrent();
+ for (i=0; ((ge = getgrent()) != 0L) && (i < maxEntries); i++)
+ {
+ if (IamRoot)
+ groupList += QString::fromLatin1(ge->gr_name);
+ else
+ {
+ /* pick the groups to which the user belongs */
+ char ** members = ge->gr_mem;
+ char * member;
+ while ((member = *members) != 0L) {
+ if (strUser == member) {
+ groupList += QString::fromLocal8Bit(ge->gr_name);
+ break;
+ }
+ ++members;
+ }
+ }
+ }
+ endgrent();
+#endif //Q_OS_UNIX
+
+ /* add the effective Group to the list .. */
+ ge = getgrgid (getegid());
+ if (ge) {
+ QString name = QString::fromLatin1(ge->gr_name);
+ if (name.isEmpty())
+ name.setNum(ge->gr_gid);
+ if (groupList.find(name) == groupList.end())
+ groupList += name;
+ }
+
+ bool isMyGroup = groupList.contains(strGroup);
+
+ /* add the group the file currently belongs to ..
+ * .. if its not there already
+ */
+ if (!isMyGroup)
+ groupList += strGroup;
+
+ l = new QLabel( i18n("Group:"), gb );
+ gl->addWidget (l, 2, 0);
+
+ /* Set group: if possible to change:
+ * - Offer a KLineEdit for root, since he can change to any group.
+ * - Offer a QComboBox for a normal user, since he can change to a fixed
+ * (small) set of groups only.
+ * If not changeable: offer a QLabel.
+ */
+ if (IamRoot && isLocal)
+ {
+ grpEdit = new KLineEdit(gb);
+ KCompletion *kcom = new KCompletion;
+ kcom->setItems(groupList);
+ grpEdit->setCompletionObject(kcom, true);
+ grpEdit->setAutoDeleteCompletionObject( true );
+ grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
+ grpEdit->setText(strGroup);
+ gl->addWidget(grpEdit, 2, 1);
+ connect( grpEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ }
+ else if ((groupList.count() > 1) && isMyFile && isLocal)
+ {
+ grpCombo = new QComboBox(gb, "combogrouplist");
+ grpCombo->insertStringList(groupList);
+ grpCombo->setCurrentItem(groupList.findIndex(strGroup));
+ gl->addWidget(grpCombo, 2, 1);
+ connect( grpCombo, SIGNAL( activated( int ) ),
+ this, SIGNAL( changed() ) );
+ }
+ else
+ {
+ l = new QLabel(strGroup, gb);
+ gl->addWidget(l, 2, 1);
+ }
+
+ gl->setColStretch(2, 10);
+
+ // "Apply recursive" checkbox
+ if ( hasDir && !isLink && !isTrash )
+ {
+ d->cbRecursive = new QCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame );
+ connect( d->cbRecursive, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
+ box->addWidget( d->cbRecursive );
+ }
+
+ updateAccessControls();
+
+
+ if ( isTrash || !d->canChangePermissions )
+ {
+ //don't allow to change properties for file into trash
+ enableAccessControls(false);
+ if ( pbAdvancedPerm && !d->hasExtendedACL )
+ pbAdvancedPerm->setEnabled(false);
+ }
+
+ box->addStretch (10);
+}
+
+#ifdef USE_POSIX_ACL
+static bool fileSystemSupportsACL( const QCString& pathCString )
+{
+ bool fileSystemSupportsACLs = false;
+#ifdef Q_OS_FREEBSD
+ struct statfs buf;
+ fileSystemSupportsACLs = ( statfs( pathCString.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS );
+#else
+ fileSystemSupportsACLs =
+ getxattr( pathCString.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA;
+#endif
+ return fileSystemSupportsACLs;
+}
+#endif
+
+
+void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() {
+
+ bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed);
+ KDialogBase dlg(properties, 0, true, i18n("Advanced Permissions"),
+ KDialogBase::Ok|KDialogBase::Cancel);
+
+ QLabel *l, *cl[3];
+ QGroupBox *gb;
+ QGridLayout *gl;
+
+ QVBox *mainVBox = dlg.makeVBoxMainWidget();
+
+ // Group: Access Permissions
+ gb = new QGroupBox ( 0, Qt::Vertical, i18n("Access Permissions"), mainVBox );
+ gb->layout()->setSpacing(KDialog::spacingHint());
+ gb->layout()->setMargin(KDialog::marginHint());
+
+ gl = new QGridLayout (gb->layout(), 6, 6);
+ gl->addRowSpacing(0, 10);
+
+ QValueVector<QWidget*> theNotSpecials;
+
+ l = new QLabel(i18n("Class"), gb );
+ gl->addWidget(l, 1, 0);
+ theNotSpecials.append( l );
+
+ if (isDir)
+ l = new QLabel( i18n("Show\nEntries"), gb );
+ else
+ l = new QLabel( i18n("Read"), gb );
+ gl->addWidget (l, 1, 1);
+ theNotSpecials.append( l );
+ QString readWhatsThis;
+ if (isDir)
+ readWhatsThis = i18n("This flag allows viewing the content of the folder.");
+ else
+ readWhatsThis = i18n("The Read flag allows viewing the content of the file.");
+ QWhatsThis::add(l, readWhatsThis);
+
+ if (isDir)
+ l = new QLabel( i18n("Write\nEntries"), gb );
+ else
+ l = new QLabel( i18n("Write"), gb );
+ gl->addWidget (l, 1, 2);
+ theNotSpecials.append( l );
+ QString writeWhatsThis;
+ if (isDir)
+ writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. "
+ "Note that deleting and renaming can be limited using the Sticky flag.");
+ else
+ writeWhatsThis = i18n("The Write flag allows modifying the content of the file.");
+ QWhatsThis::add(l, writeWhatsThis);
+
+ QString execWhatsThis;
+ if (isDir) {
+ l = new QLabel( i18n("Enter folder", "Enter"), gb );
+ execWhatsThis = i18n("Enable this flag to allow entering the folder.");
+ }
+ else {
+ l = new QLabel( i18n("Exec"), gb );
+ execWhatsThis = i18n("Enable this flag to allow executing the file as a program.");
+ }
+ QWhatsThis::add(l, execWhatsThis);
+ theNotSpecials.append( l );
+ // GJ: Add space between normal and special modes
+ QSize size = l->sizeHint();
+ size.setWidth(size.width() + 15);
+ l->setFixedSize(size);
+ gl->addWidget (l, 1, 3);
+
+ l = new QLabel( i18n("Special"), gb );
+ gl->addMultiCellWidget(l, 1, 1, 4, 5);
+ QString specialWhatsThis;
+ if (isDir)
+ specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact "
+ "meaning of the flag can be seen in the right hand column.");
+ else
+ specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen "
+ "in the right hand column.");
+ QWhatsThis::add(l, specialWhatsThis);
+
+ cl[0] = new QLabel( i18n("User"), gb );
+ gl->addWidget (cl[0], 2, 0);
+ theNotSpecials.append( cl[0] );
+
+ cl[1] = new QLabel( i18n("Group"), gb );
+ gl->addWidget (cl[1], 3, 0);
+ theNotSpecials.append( cl[1] );
+
+ cl[2] = new QLabel( i18n("Others"), gb );
+ gl->addWidget (cl[2], 4, 0);
+ theNotSpecials.append( cl[2] );
+
+ l = new QLabel(i18n("Set UID"), gb);
+ gl->addWidget(l, 2, 5);
+ QString setUidWhatsThis;
+ if (isDir)
+ setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be "
+ "the owner of all new files.");
+ else
+ setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
+ "be executed with the permissions of the owner.");
+ QWhatsThis::add(l, setUidWhatsThis);
+
+ l = new QLabel(i18n("Set GID"), gb);
+ gl->addWidget(l, 3, 5);
+ QString setGidWhatsThis;
+ if (isDir)
+ setGidWhatsThis = i18n("If this flag is set, the group of this folder will be "
+ "set for all new files.");
+ else
+ setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
+ "be executed with the permissions of the group.");
+ QWhatsThis::add(l, setGidWhatsThis);
+
+ l = new QLabel(i18n("File permission", "Sticky"), gb);
+ gl->addWidget(l, 4, 5);
+ QString stickyWhatsThis;
+ if (isDir)
+ stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner "
+ "and root can delete or rename files. Otherwise everybody "
+ "with write permissions can do this.");
+ else
+ stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may "
+ "be used on some systems");
+ QWhatsThis::add(l, stickyWhatsThis);
+
+ mode_t aPermissions, aPartialPermissions;
+ mode_t dummy1, dummy2;
+
+ if (!d->isIrregular) {
+ switch (d->pmode) {
+ case PermissionsOnlyFiles:
+ getPermissionMasks(aPartialPermissions,
+ dummy1,
+ aPermissions,
+ dummy2);
+ break;
+ case PermissionsOnlyDirs:
+ case PermissionsMixed:
+ getPermissionMasks(dummy1,
+ aPartialPermissions,
+ dummy2,
+ aPermissions);
+ break;
+ case PermissionsOnlyLinks:
+ aPermissions = UniRead | UniWrite | UniExec | UniSpecial;
+ aPartialPermissions = 0;
+ break;
+ }
+ }
+ else {
+ aPermissions = permissions;
+ aPartialPermissions = d->partialPermissions;
+ }
+
+ // Draw Checkboxes
+ QCheckBox *cba[3][4];
+ for (int row = 0; row < 3 ; ++row) {
+ for (int col = 0; col < 4; ++col) {
+ QCheckBox *cb = new QCheckBox( gb );
+ if ( col != 3 ) theNotSpecials.append( cb );
+ cba[row][col] = cb;
+ cb->setChecked(aPermissions & fperm[row][col]);
+ if ( aPartialPermissions & fperm[row][col] )
+ {
+ cb->setTristate();
+ cb->setNoChange();
+ }
+ else if (d->cbRecursive && d->cbRecursive->isChecked())
+ cb->setTristate();
+
+ cb->setEnabled( d->canChangePermissions );
+ gl->addWidget (cb, row+2, col+1);
+ switch(col) {
+ case 0:
+ QWhatsThis::add(cb, readWhatsThis);
+ break;
+ case 1:
+ QWhatsThis::add(cb, writeWhatsThis);
+ break;
+ case 2:
+ QWhatsThis::add(cb, execWhatsThis);
+ break;
+ case 3:
+ switch(row) {
+ case 0:
+ QWhatsThis::add(cb, setUidWhatsThis);
+ break;
+ case 1:
+ QWhatsThis::add(cb, setGidWhatsThis);
+ break;
+ case 2:
+ QWhatsThis::add(cb, stickyWhatsThis);
+ break;
+ }
+ break;
+ }
+ }
+ }
+ gl->setColStretch(6, 10);
+
+#ifdef USE_POSIX_ACL
+ KACLEditWidget *extendedACLs = 0;
+
+ // FIXME make it work with partial entries
+ if ( properties->items().count() == 1 ) {
+ QCString pathCString = QFile::encodeName( properties->item()->url().path() );
+ d->fileSystemSupportsACLs = fileSystemSupportsACL( pathCString );
+ }
+ if ( d->fileSystemSupportsACLs ) {
+ std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) );
+ extendedACLs = new KACLEditWidget( mainVBox );
+ if ( d->extendedACL.isValid() && d->extendedACL.isExtended() )
+ extendedACLs->setACL( d->extendedACL );
+ else
+ extendedACLs->setACL( KACL( aPermissions ) );
+
+ if ( d->defaultACL.isValid() )
+ extendedACLs->setDefaultACL( d->defaultACL );
+
+ if ( properties->items().first()->isDir() )
+ extendedACLs->setAllowDefaults( true );
+ if ( !d->canChangePermissions )
+ extendedACLs->setReadOnly( true );
+
+ }
+#endif
+ if (dlg.exec() != KDialogBase::Accepted)
+ return;
+
+ mode_t andPermissions = mode_t(~0);
+ mode_t orPermissions = 0;
+ for (int row = 0; row < 3; ++row)
+ for (int col = 0; col < 4; ++col) {
+ switch (cba[row][col]->state())
+ {
+ case QCheckBox::On:
+ orPermissions |= fperm[row][col];
+ //fall through
+ case QCheckBox::Off:
+ andPermissions &= ~fperm[row][col];
+ break;
+ default: // NoChange
+ break;
+ }
+ }
+
+ d->isIrregular = false;
+ KFileItemList items = properties->items();
+ for (KFileItemListIterator it(items); it.current(); ++it) {
+ if (isIrregular(((*it)->permissions() & andPermissions) | orPermissions,
+ (*it)->isDir(), (*it)->isLink())) {
+ d->isIrregular = true;
+ break;
+ }
+ }
+
+ permissions = orPermissions;
+ d->partialPermissions = andPermissions;
+
+#ifdef USE_POSIX_ACL
+ // override with the acls, if present
+ if ( extendedACLs ) {
+ d->extendedACL = extendedACLs->getACL();
+ d->defaultACL = extendedACLs->getDefaultACL();
+ d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid();
+ permissions = d->extendedACL.basePermissions();
+ permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX );
+ }
+#endif
+
+ updateAccessControls();
+ emit changed();
+}
+
+// QString KFilePermissionsPropsPlugin::tabName () const
+// {
+// return i18n ("&Permissions");
+// }
+
+KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin()
+{
+ delete d;
+}
+
+bool KFilePermissionsPropsPlugin::supports( KFileItemList _items )
+{
+ KFileItemList::const_iterator it = _items.constBegin();
+ for ( ; it != _items.constEnd(); ++it ) {
+ KFileItem *item = *it;
+ if( !item->user().isEmpty() || !item->group().isEmpty() )
+ return true;
+ }
+ return false;
+}
+
+// sets a combo box in the Access Control frame
+void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target,
+ mode_t permissions, mode_t partial) {
+ combo->clear();
+ if (d->pmode == PermissionsOnlyLinks) {
+ combo->insertItem(i18n("Link"));
+ combo->setCurrentItem(0);
+ return;
+ }
+
+ mode_t tMask = permissionsMasks[target];
+ int textIndex;
+ for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++)
+ if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite)))
+ break;
+ Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar
+
+ for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++)
+ combo->insertItem(i18n(permissionsTexts[(int)d->pmode][i]));
+
+ if (partial & tMask & ~UniExec) {
+ combo->insertItem(i18n("Varying (No Change)"));
+ combo->setCurrentItem(3);
+ }
+ else
+ combo->setCurrentItem(textIndex);
+}
+
+// permissions are irregular if they cant be displayed in a combo box.
+bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) {
+ if (isLink) // links are always ok
+ return false;
+
+ mode_t p = permissions;
+ if (p & (S_ISUID | S_ISGID)) // setuid/setgid -> irregular
+ return true;
+ if (isDir) {
+ p &= ~S_ISVTX; // ignore sticky on dirs
+
+ // check supported flag combinations
+ mode_t p0 = p & UniOwner;
+ if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner))
+ return true;
+ p0 = p & UniGroup;
+ if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup))
+ return true;
+ p0 = p & UniOthers;
+ if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers))
+ return true;
+ return false;
+ }
+ if (p & S_ISVTX) // sticky on file -> irregular
+ return true;
+
+ // check supported flag combinations
+ mode_t p0 = p & UniOwner;
+ bool usrXPossible = !p0; // true if this file could be an executable
+ if (p0 & S_IXUSR) {
+ if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR)))
+ return true;
+ usrXPossible = true;
+ }
+ else if (p0 == S_IWUSR)
+ return true;
+
+ p0 = p & UniGroup;
+ bool grpXPossible = !p0; // true if this file could be an executable
+ if (p0 & S_IXGRP) {
+ if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP)))
+ return true;
+ grpXPossible = true;
+ }
+ else if (p0 == S_IWGRP)
+ return true;
+ if (p0 == 0)
+ grpXPossible = true;
+
+ p0 = p & UniOthers;
+ bool othXPossible = !p0; // true if this file could be an executable
+ if (p0 & S_IXOTH) {
+ if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH)))
+ return true;
+ othXPossible = true;
+ }
+ else if (p0 == S_IWOTH)
+ return true;
+
+ // check that there either all targets are executable-compatible, or none
+ return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible);
+}
+
+// enables/disabled the widgets in the Access Control frame
+void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) {
+ d->ownerPermCombo->setEnabled(enable);
+ d->groupPermCombo->setEnabled(enable);
+ d->othersPermCombo->setEnabled(enable);
+ if (d->extraCheckbox)
+ d->extraCheckbox->setEnabled(enable);
+ if ( d->cbRecursive )
+ d->cbRecursive->setEnabled(enable);
+}
+
+// updates all widgets in the Access Control frame
+void KFilePermissionsPropsPlugin::updateAccessControls() {
+ setComboContent(d->ownerPermCombo, PermissionsOwner,
+ permissions, d->partialPermissions);
+ setComboContent(d->groupPermCombo, PermissionsGroup,
+ permissions, d->partialPermissions);
+ setComboContent(d->othersPermCombo, PermissionsOthers,
+ permissions, d->partialPermissions);
+
+ switch(d->pmode) {
+ case PermissionsOnlyLinks:
+ enableAccessControls(false);
+ break;
+ case PermissionsOnlyFiles:
+ enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
+ if (d->canChangePermissions)
+ d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
+ i18n("This file uses advanced permissions",
+ "These files use advanced permissions.",
+ properties->items().count()) : "");
+ if (d->partialPermissions & UniExec) {
+ d->extraCheckbox->setTristate();
+ d->extraCheckbox->setNoChange();
+ }
+ else {
+ d->extraCheckbox->setTristate(false);
+ d->extraCheckbox->setChecked(permissions & UniExec);
+ }
+ break;
+ case PermissionsOnlyDirs:
+ enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
+ // if this is a dir, and we can change permissions, don't dis-allow
+ // recursive, we can do that for ACL setting.
+ if ( d->cbRecursive )
+ d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular );
+
+ if (d->canChangePermissions)
+ d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
+ i18n("This folder uses advanced permissions.",
+ "These folders use advanced permissions.",
+ properties->items().count()) : "");
+ if (d->partialPermissions & S_ISVTX) {
+ d->extraCheckbox->setTristate();
+ d->extraCheckbox->setNoChange();
+ }
+ else {
+ d->extraCheckbox->setTristate(false);
+ d->extraCheckbox->setChecked(permissions & S_ISVTX);
+ }
+ break;
+ case PermissionsMixed:
+ enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
+ if (d->canChangePermissions)
+ d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
+ i18n("These files use advanced permissions.") : "");
+ break;
+ if (d->partialPermissions & S_ISVTX) {
+ d->extraCheckbox->setTristate();
+ d->extraCheckbox->setNoChange();
+ }
+ else {
+ d->extraCheckbox->setTristate(false);
+ d->extraCheckbox->setChecked(permissions & S_ISVTX);
+ }
+ break;
+ }
+}
+
+// gets masks for files and dirs from the Access Control frame widgets
+void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions,
+ mode_t &andDirPermissions,
+ mode_t &orFilePermissions,
+ mode_t &orDirPermissions) {
+ andFilePermissions = mode_t(~UniSpecial);
+ andDirPermissions = mode_t(~(S_ISUID|S_ISGID));
+ orFilePermissions = 0;
+ orDirPermissions = 0;
+ if (d->isIrregular)
+ return;
+
+ mode_t m = standardPermissions[d->ownerPermCombo->currentItem()];
+ if (m != (mode_t) -1) {
+ orFilePermissions |= m & UniOwner;
+ if ((m & UniOwner) &&
+ ((d->pmode == PermissionsMixed) ||
+ ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange))))
+ andFilePermissions &= ~(S_IRUSR | S_IWUSR);
+ else {
+ andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
+ if ((m & S_IRUSR) && (d->extraCheckbox->state() == QButton::On))
+ orFilePermissions |= S_IXUSR;
+ }
+
+ orDirPermissions |= m & UniOwner;
+ if (m & S_IRUSR)
+ orDirPermissions |= S_IXUSR;
+ andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
+ }
+
+ m = standardPermissions[d->groupPermCombo->currentItem()];
+ if (m != (mode_t) -1) {
+ orFilePermissions |= m & UniGroup;
+ if ((m & UniGroup) &&
+ ((d->pmode == PermissionsMixed) ||
+ ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange))))
+ andFilePermissions &= ~(S_IRGRP | S_IWGRP);
+ else {
+ andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
+ if ((m & S_IRGRP) && (d->extraCheckbox->state() == QButton::On))
+ orFilePermissions |= S_IXGRP;
+ }
+
+ orDirPermissions |= m & UniGroup;
+ if (m & S_IRGRP)
+ orDirPermissions |= S_IXGRP;
+ andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
+ }
+
+ m = standardPermissions[d->othersPermCombo->currentItem()];
+ if (m != (mode_t) -1) {
+ orFilePermissions |= m & UniOthers;
+ if ((m & UniOthers) &&
+ ((d->pmode == PermissionsMixed) ||
+ ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange))))
+ andFilePermissions &= ~(S_IROTH | S_IWOTH);
+ else {
+ andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
+ if ((m & S_IROTH) && (d->extraCheckbox->state() == QButton::On))
+ orFilePermissions |= S_IXOTH;
+ }
+
+ orDirPermissions |= m & UniOthers;
+ if (m & S_IROTH)
+ orDirPermissions |= S_IXOTH;
+ andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
+ }
+
+ if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) &&
+ (d->extraCheckbox->state() != QButton::NoChange)) {
+ andDirPermissions &= ~S_ISVTX;
+ if (d->extraCheckbox->state() == QButton::On)
+ orDirPermissions |= S_ISVTX;
+ }
+}
+
+void KFilePermissionsPropsPlugin::applyChanges()
+{
+ mode_t orFilePermissions;
+ mode_t orDirPermissions;
+ mode_t andFilePermissions;
+ mode_t andDirPermissions;
+
+ if (!d->canChangePermissions)
+ return;
+
+ if (!d->isIrregular)
+ getPermissionMasks(andFilePermissions,
+ andDirPermissions,
+ orFilePermissions,
+ orDirPermissions);
+ else {
+ orFilePermissions = permissions;
+ andFilePermissions = d->partialPermissions;
+ orDirPermissions = permissions;
+ andDirPermissions = d->partialPermissions;
+ }
+
+ QString owner, group;
+ if (usrEdit)
+ owner = usrEdit->text();
+ if (grpEdit)
+ group = grpEdit->text();
+ else if (grpCombo)
+ group = grpCombo->currentText();
+
+ if (owner == strOwner)
+ owner = QString::null; // no change
+
+ if (group == strGroup)
+ group = QString::null;
+
+ bool recursive = d->cbRecursive && d->cbRecursive->isChecked();
+ bool permissionChange = false;
+
+ KFileItemList files, dirs;
+ KFileItemList items = properties->items();
+ for (KFileItemListIterator it(items); it.current(); ++it) {
+ if ((*it)->isDir()) {
+ dirs.append(*it);
+ if ((*it)->permissions() != (((*it)->permissions() & andDirPermissions) | orDirPermissions))
+ permissionChange = true;
+ }
+ else if ((*it)->isFile()) {
+ files.append(*it);
+ if ((*it)->permissions() != (((*it)->permissions() & andFilePermissions) | orFilePermissions))
+ permissionChange = true;
+ }
+ }
+
+ const bool ACLChange = ( d->extendedACL != properties->item()->ACL() );
+ const bool defaultACLChange = ( d->defaultACL != properties->item()->defaultACL() );
+
+ if ( owner.isEmpty() && group.isEmpty() && !recursive
+ && !permissionChange && !ACLChange && !defaultACLChange )
+ return;
+
+ KIO::Job * job;
+ if (files.count() > 0) {
+ job = KIO::chmod( files, orFilePermissions, ~andFilePermissions,
+ owner, group, false );
+ if ( ACLChange && d->fileSystemSupportsACLs )
+ job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
+ if ( defaultACLChange && d->fileSystemSupportsACLs )
+ job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
+
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotChmodResult( KIO::Job * ) ) );
+ // Wait for job
+ QWidget dummy(0,0,WType_Dialog|WShowModal);
+ qt_enter_modal(&dummy);
+ qApp->enter_loop();
+ qt_leave_modal(&dummy);
+ }
+ if (dirs.count() > 0) {
+ job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
+ owner, group, recursive );
+ if ( ACLChange && d->fileSystemSupportsACLs )
+ job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
+ if ( defaultACLChange && d->fileSystemSupportsACLs )
+ job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
+
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotChmodResult( KIO::Job * ) ) );
+ // Wait for job
+ QWidget dummy(0,0,WType_Dialog|WShowModal);
+ qt_enter_modal(&dummy);
+ qApp->enter_loop();
+ qt_leave_modal(&dummy);
+ }
+}
+
+void KFilePermissionsPropsPlugin::slotChmodResult( KIO::Job * job )
+{
+ kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl;
+ if (job->error())
+ job->showErrorDialog( d->m_frame );
+ // allow apply() to return
+ qApp->exit_loop();
+}
+
+
+
+
+class KURLPropsPlugin::KURLPropsPluginPrivate
+{
+public:
+ KURLPropsPluginPrivate()
+ {
+ }
+ ~KURLPropsPluginPrivate()
+ {
+ }
+
+ QFrame *m_frame;
+};
+
+KURLPropsPlugin::KURLPropsPlugin( KPropertiesDialog *_props )
+ : KPropsDlgPlugin( _props )
+{
+ d = new KURLPropsPluginPrivate;
+ d->m_frame = properties->addPage(i18n("U&RL"));
+ QVBoxLayout *layout = new QVBoxLayout(d->m_frame, 0, KDialog::spacingHint());
+
+ QLabel *l;
+ l = new QLabel( d->m_frame, "Label_1" );
+ l->setText( i18n("URL:") );
+ layout->addWidget(l);
+
+ URLEdit = new KURLRequester( d->m_frame, "URL Requester" );
+ layout->addWidget(URLEdit);
+
+ QString path = properties->kurl().path();
+
+ QFile f( path );
+ if ( !f.open( IO_ReadOnly ) )
+ return;
+ f.close();
+
+ KSimpleConfig config( path );
+ config.setDesktopGroup();
+ URLStr = config.readPathEntry( "URL" );
+
+ if ( !URLStr.isNull() )
+ URLEdit->setURL( URLStr );
+
+ connect( URLEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+
+ layout->addStretch (1);
+}
+
+KURLPropsPlugin::~KURLPropsPlugin()
+{
+ delete d;
+}
+
+// QString KURLPropsPlugin::tabName () const
+// {
+// return i18n ("U&RL");
+// }
+
+bool KURLPropsPlugin::supports( KFileItemList _items )
+{
+ if ( _items.count() != 1 )
+ return false;
+ KFileItem * item = _items.first();
+ // check if desktop file
+ if ( !KPropsDlgPlugin::isDesktopFile( item ) )
+ return false;
+
+ // open file and check type
+ KDesktopFile config( item->url().path(), true /* readonly */ );
+ return config.hasLinkType();
+}
+
+void KURLPropsPlugin::applyChanges()
+{
+ QString path = properties->kurl().path();
+
+ QFile f( path );
+ if ( !f.open( IO_ReadWrite ) ) {
+ KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
+ "sufficient access to write to <b>%1</b>.</qt>").arg(path));
+ return;
+ }
+ f.close();
+
+ KSimpleConfig config( path );
+ config.setDesktopGroup();
+ config.writeEntry( "Type", QString::fromLatin1("Link"));
+ config.writePathEntry( "URL", URLEdit->url() );
+ // Users can't create a Link .desktop file with a Name field,
+ // but distributions can. Update the Name field in that case.
+ if ( config.hasKey("Name") )
+ {
+ QString nameStr = nameFromFileName(properties->kurl().fileName());
+ config.writeEntry( "Name", nameStr );
+ config.writeEntry( "Name", nameStr, true, false, true );
+
+ }
+}
+
+
+/* ----------------------------------------------------
+ *
+ * KBindingPropsPlugin
+ *
+ * -------------------------------------------------- */
+
+class KBindingPropsPlugin::KBindingPropsPluginPrivate
+{
+public:
+ KBindingPropsPluginPrivate()
+ {
+ }
+ ~KBindingPropsPluginPrivate()
+ {
+ }
+
+ QFrame *m_frame;
+};
+
+KBindingPropsPlugin::KBindingPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props )
+{
+ d = new KBindingPropsPluginPrivate;
+ d->m_frame = properties->addPage(i18n("A&ssociation"));
+ patternEdit = new KLineEdit( d->m_frame, "LineEdit_1" );
+ commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" );
+ mimeEdit = new KLineEdit( d->m_frame, "LineEdit_3" );
+
+ QBoxLayout *mainlayout = new QVBoxLayout(d->m_frame, 0, KDialog::spacingHint());
+ QLabel* tmpQLabel;
+
+ tmpQLabel = new QLabel( d->m_frame, "Label_1" );
+ tmpQLabel->setText( i18n("Pattern ( example: *.html;*.htm )") );
+ tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
+ mainlayout->addWidget(tmpQLabel, 1);
+
+ //patternEdit->setGeometry( 10, 40, 210, 30 );
+ //patternEdit->setText( "" );
+ patternEdit->setMaxLength( 512 );
+ patternEdit->setMinimumSize( patternEdit->sizeHint() );
+ patternEdit->setFixedHeight( fontHeight );
+ mainlayout->addWidget(patternEdit, 1);
+
+ tmpQLabel = new QLabel( d->m_frame, "Label_2" );
+ tmpQLabel->setText( i18n("Mime Type") );
+ tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
+ mainlayout->addWidget(tmpQLabel, 1);
+
+ //mimeEdit->setGeometry( 10, 160, 210, 30 );
+ mimeEdit->setMaxLength( 256 );
+ mimeEdit->setMinimumSize( mimeEdit->sizeHint() );
+ mimeEdit->setFixedHeight( fontHeight );
+ mainlayout->addWidget(mimeEdit, 1);
+
+ tmpQLabel = new QLabel( d->m_frame, "Label_3" );
+ tmpQLabel->setText( i18n("Comment") );
+ tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
+ mainlayout->addWidget(tmpQLabel, 1);
+
+ //commentEdit->setGeometry( 10, 100, 210, 30 );
+ commentEdit->setMaxLength( 256 );
+ commentEdit->setMinimumSize( commentEdit->sizeHint() );
+ commentEdit->setFixedHeight( fontHeight );
+ mainlayout->addWidget(commentEdit, 1);
+
+ cbAutoEmbed = new QCheckBox( i18n("Left click previews"), d->m_frame, "cbAutoEmbed" );
+ mainlayout->addWidget(cbAutoEmbed, 1);
+
+ mainlayout->addStretch (10);
+ mainlayout->activate();
+
+ QFile f( _props->kurl().path() );
+ if ( !f.open( IO_ReadOnly ) )
+ return;
+ f.close();
+
+ KSimpleConfig config( _props->kurl().path() );
+ config.setDesktopGroup();
+ QString patternStr = config.readEntry( "Patterns" );
+ QString iconStr = config.readEntry( "Icon" );
+ QString commentStr = config.readEntry( "Comment" );
+ m_sMimeStr = config.readEntry( "MimeType" );
+
+ if ( !patternStr.isEmpty() )
+ patternEdit->setText( patternStr );
+ if ( !commentStr.isEmpty() )
+ commentEdit->setText( commentStr );
+ if ( !m_sMimeStr.isEmpty() )
+ mimeEdit->setText( m_sMimeStr );
+ cbAutoEmbed->setTristate();
+ if ( config.hasKey( "X-KDE-AutoEmbed" ) )
+ cbAutoEmbed->setChecked( config.readBoolEntry( "X-KDE-AutoEmbed" ) );
+ else
+ cbAutoEmbed->setNoChange();
+
+ connect( patternEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( commentEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( mimeEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( cbAutoEmbed, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed() ) );
+}
+
+KBindingPropsPlugin::~KBindingPropsPlugin()
+{
+ delete d;
+}
+
+// QString KBindingPropsPlugin::tabName () const
+// {
+// return i18n ("A&ssociation");
+// }
+
+bool KBindingPropsPlugin::supports( KFileItemList _items )
+{
+ if ( _items.count() != 1 )
+ return false;
+ KFileItem * item = _items.first();
+ // check if desktop file
+ if ( !KPropsDlgPlugin::isDesktopFile( item ) )
+ return false;
+
+ // open file and check type
+ KDesktopFile config( item->url().path(), true /* readonly */ );
+ return config.hasMimeTypeType();
+}
+
+void KBindingPropsPlugin::applyChanges()
+{
+ QString path = properties->kurl().path();
+ QFile f( path );
+
+ if ( !f.open( IO_ReadWrite ) )
+ {
+ KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
+ "sufficient access to write to <b>%1</b>.</qt>").arg(path));
+ return;
+ }
+ f.close();
+
+ KSimpleConfig config( path );
+ config.setDesktopGroup();
+ config.writeEntry( "Type", QString::fromLatin1("MimeType") );
+
+ config.writeEntry( "Patterns", patternEdit->text() );
+ config.writeEntry( "Comment", commentEdit->text() );
+ config.writeEntry( "Comment",
+ commentEdit->text(), true, false, true ); // for compat
+ config.writeEntry( "MimeType", mimeEdit->text() );
+ if ( cbAutoEmbed->state() == QButton::NoChange )
+ config.deleteEntry( "X-KDE-AutoEmbed", false );
+ else
+ config.writeEntry( "X-KDE-AutoEmbed", cbAutoEmbed->isChecked() );
+ config.sync();
+}
+
+/* ----------------------------------------------------
+ *
+ * KDevicePropsPlugin
+ *
+ * -------------------------------------------------- */
+
+class KDevicePropsPlugin::KDevicePropsPluginPrivate
+{
+public:
+ KDevicePropsPluginPrivate()
+ {
+ }
+ ~KDevicePropsPluginPrivate()
+ {
+ }
+
+ QFrame *m_frame;
+ QStringList mountpointlist;
+ QLabel *m_freeSpaceText;
+ QLabel *m_freeSpaceLabel;
+ QProgressBar *m_freeSpaceBar;
+};
+
+KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props )
+{
+ d = new KDevicePropsPluginPrivate;
+ d->m_frame = properties->addPage(i18n("De&vice"));
+
+ QStringList devices;
+ KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
+
+ for(KMountPoint::List::ConstIterator it = mountPoints.begin();
+ it != mountPoints.end(); ++it)
+ {
+ KMountPoint *mp = *it;
+ QString mountPoint = mp->mountPoint();
+ QString device = mp->mountedFrom();
+ kdDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType()<<endl;
+
+ if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty()
+ && device != "none")
+ {
+ devices.append( device + QString::fromLatin1(" (")
+ + mountPoint + QString::fromLatin1(")") );
+ m_devicelist.append(device);
+ d->mountpointlist.append(mountPoint);
+ }
+ }
+
+ QGridLayout *layout = new QGridLayout( d->m_frame, 0, 2, 0,
+ KDialog::spacingHint());
+ layout->setColStretch(1, 1);
+
+ QLabel* label;
+ label = new QLabel( d->m_frame );
+ label->setText( devices.count() == 0 ?
+ i18n("Device (/dev/fd0):") : // old style
+ i18n("Device:") ); // new style (combobox)
+ layout->addWidget(label, 0, 0);
+
+ device = new QComboBox( true, d->m_frame, "ComboBox_device" );
+ device->insertStringList( devices );
+ layout->addWidget(device, 0, 1);
+ connect( device, SIGNAL( activated( int ) ),
+ this, SLOT( slotActivated( int ) ) );
+
+ readonly = new QCheckBox( d->m_frame, "CheckBox_readonly" );
+ readonly->setText( i18n("Read only") );
+ layout->addWidget(readonly, 1, 1);
+
+ label = new QLabel( d->m_frame );
+ label->setText( i18n("File system:") );
+ layout->addWidget(label, 2, 0);
+
+ QLabel *fileSystem = new QLabel( d->m_frame );
+ layout->addWidget(fileSystem, 2, 1);
+
+ label = new QLabel( d->m_frame );
+ label->setText( devices.count()==0 ?
+ i18n("Mount point (/mnt/floppy):") : // old style
+ i18n("Mount point:")); // new style (combobox)
+ layout->addWidget(label, 3, 0);
+
+ mountpoint = new QLabel( d->m_frame, "LineEdit_mountpoint" );
+
+ layout->addWidget(mountpoint, 3, 1);
+
+ // show disk free
+ d->m_freeSpaceText = new QLabel(i18n("Free disk space:"), d->m_frame );
+ layout->addWidget(d->m_freeSpaceText, 4, 0);
+
+ d->m_freeSpaceLabel = new QLabel( d->m_frame );
+ layout->addWidget( d->m_freeSpaceLabel, 4, 1 );
+
+ d->m_freeSpaceBar = new QProgressBar( d->m_frame, "freeSpaceBar" );
+ layout->addMultiCellWidget(d->m_freeSpaceBar, 5, 5, 0, 1);
+
+ // we show it in the slot when we know the values
+ d->m_freeSpaceText->hide();
+ d->m_freeSpaceLabel->hide();
+ d->m_freeSpaceBar->hide();
+
+ KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame);
+ layout->addMultiCellWidget(sep, 6, 6, 0, 1);
+
+ unmounted = new KIconButton( d->m_frame );
+ int bsize = 66 + 2 * unmounted->style().pixelMetric(QStyle::PM_ButtonMargin);
+ unmounted->setFixedSize(bsize, bsize);
+ unmounted->setIconType(KIcon::Desktop, KIcon::Device);
+ layout->addWidget(unmounted, 7, 0);
+
+ label = new QLabel( i18n("Unmounted Icon"), d->m_frame );
+ layout->addWidget(label, 7, 1);
+
+ layout->setRowStretch(8, 1);
+
+ QString path( _props->kurl().path() );
+
+ QFile f( path );
+ if ( !f.open( IO_ReadOnly ) )
+ return;
+ f.close();
+
+ KSimpleConfig config( path );
+ config.setDesktopGroup();
+ QString deviceStr = config.readEntry( "Dev" );
+ QString mountPointStr = config.readEntry( "MountPoint" );
+ bool ro = config.readBoolEntry( "ReadOnly", false );
+ QString unmountedStr = config.readEntry( "UnmountIcon" );
+
+ fileSystem->setText( i18n(config.readEntry("FSType").local8Bit()) );
+
+ device->setEditText( deviceStr );
+ if ( !deviceStr.isEmpty() ) {
+ // Set default options for this device (first matching entry)
+ int index = m_devicelist.findIndex(deviceStr);
+ if (index != -1)
+ {
+ //kdDebug(250) << "found it " << index << endl;
+ slotActivated( index );
+ }
+ }
+
+ if ( !mountPointStr.isEmpty() )
+ {
+ mountpoint->setText( mountPointStr );
+ updateInfo();
+ }
+
+ readonly->setChecked( ro );
+
+ if ( unmountedStr.isEmpty() )
+ unmountedStr = KMimeType::defaultMimeTypePtr()->KServiceType::icon(); // default icon
+
+ unmounted->setIcon( unmountedStr );
+
+ connect( device, SIGNAL( activated( int ) ),
+ this, SIGNAL( changed() ) );
+ connect( device, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( readonly, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed() ) );
+ connect( unmounted, SIGNAL( iconChanged( QString ) ),
+ this, SIGNAL( changed() ) );
+
+ connect( device, SIGNAL( textChanged( const QString & ) ),
+ this, SLOT( slotDeviceChanged() ) );
+}
+
+KDevicePropsPlugin::~KDevicePropsPlugin()
+{
+ delete d;
+}
+
+// QString KDevicePropsPlugin::tabName () const
+// {
+// return i18n ("De&vice");
+// }
+
+void KDevicePropsPlugin::updateInfo()
+{
+ // we show it in the slot when we know the values
+ d->m_freeSpaceText->hide();
+ d->m_freeSpaceLabel->hide();
+ d->m_freeSpaceBar->hide();
+
+ if ( !mountpoint->text().isEmpty() )
+ {
+ KDiskFreeSp * job = new KDiskFreeSp;
+ connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&,
+ const unsigned long&, const QString& ) ),
+ this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&,
+ const unsigned long&, const QString& ) ) );
+
+ job->readDF( mountpoint->text() );
+ }
+}
+
+void KDevicePropsPlugin::slotActivated( int index )
+{
+ // Update mountpoint so that it matches the device that was selected in the combo
+ device->setEditText( m_devicelist[index] );
+ mountpoint->setText( d->mountpointlist[index] );
+
+ updateInfo();
+}
+
+void KDevicePropsPlugin::slotDeviceChanged()
+{
+ // Update mountpoint so that it matches the typed device
+ int index = m_devicelist.findIndex( device->currentText() );
+ if ( index != -1 )
+ mountpoint->setText( d->mountpointlist[index] );
+ else
+ mountpoint->setText( QString::null );
+
+ updateInfo();
+}
+
+void KDevicePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize,
+ const unsigned long& /*kBUsed*/,
+ const unsigned long& kBAvail,
+ const QString& )
+{
+ d->m_freeSpaceText->show();
+ d->m_freeSpaceLabel->show();
+
+ int percUsed = 100 - (int)(100.0 * kBAvail / kBSize);
+
+ d->m_freeSpaceLabel->setText(
+ // xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part.
+ i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
+ .arg(KIO::convertSizeFromKB(kBAvail))
+ .arg(KIO::convertSizeFromKB(kBSize))
+ .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
+
+ d->m_freeSpaceBar->setProgress(percUsed, 100);
+ d->m_freeSpaceBar->show();
+}
+
+bool KDevicePropsPlugin::supports( KFileItemList _items )
+{
+ if ( _items.count() != 1 )
+ return false;
+ KFileItem * item = _items.first();
+ // check if desktop file
+ if ( !KPropsDlgPlugin::isDesktopFile( item ) )
+ return false;
+ // open file and check type
+ KDesktopFile config( item->url().path(), true /* readonly */ );
+ return config.hasDeviceType();
+}
+
+void KDevicePropsPlugin::applyChanges()
+{
+ QString path = properties->kurl().path();
+ QFile f( path );
+ if ( !f.open( IO_ReadWrite ) )
+ {
+ KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient "
+ "access to write to <b>%1</b>.</qt>").arg(path));
+ return;
+ }
+ f.close();
+
+ KSimpleConfig config( path );
+ config.setDesktopGroup();
+ config.writeEntry( "Type", QString::fromLatin1("FSDevice") );
+
+ config.writeEntry( "Dev", device->currentText() );
+ config.writeEntry( "MountPoint", mountpoint->text() );
+
+ config.writeEntry( "UnmountIcon", unmounted->icon() );
+ kdDebug(250) << "unmounted->icon() = " << unmounted->icon() << endl;
+
+ config.writeEntry( "ReadOnly", readonly->isChecked() );
+
+ config.sync();
+}
+
+
+/* ----------------------------------------------------
+ *
+ * KDesktopPropsPlugin
+ *
+ * -------------------------------------------------- */
+
+
+KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props )
+ : KPropsDlgPlugin( _props )
+{
+ QFrame *frame = properties->addPage(i18n("&Application"));
+ QVBoxLayout *mainlayout = new QVBoxLayout( frame, 0, KDialog::spacingHint() );
+
+ w = new KPropertiesDesktopBase(frame);
+ mainlayout->addWidget(w);
+
+ bool bKDesktopMode = (QCString(qApp->name()) == "kdesktop"); // nasty heh?
+
+ if (bKDesktopMode)
+ {
+ // Hide Name entry
+ w->nameEdit->hide();
+ w->nameLabel->hide();
+ }
+
+ w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
+ w->pathEdit->lineEdit()->setAcceptDrops(false);
+
+ connect( w->nameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
+ connect( w->genNameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
+ connect( w->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
+ connect( w->commandEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
+ connect( w->pathEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
+
+ connect( w->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
+ connect( w->addFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotAddFiletype() ) );
+ connect( w->delFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotDelFiletype() ) );
+ connect( w->advancedButton, SIGNAL( clicked() ), this, SLOT( slotAdvanced() ) );
+
+ // now populate the page
+ QString path = _props->kurl().path();
+ QFile f( path );
+ if ( !f.open( IO_ReadOnly ) )
+ return;
+ f.close();
+
+ KDesktopFile config( path );
+ QString nameStr = config.readName();
+ QString genNameStr = config.readGenericName();
+ QString commentStr = config.readComment();
+ QString commandStr = config.readPathEntry( "Exec" );
+ if (commandStr.left(12) == "ksystraycmd ")
+ {
+ commandStr.remove(0, 12);
+ m_systrayBool = true;
+ }
+ else
+ m_systrayBool = false;
+
+ m_origCommandStr = commandStr;
+ QString pathStr = config.readPathEntry( "Path" );
+ m_terminalBool = config.readBoolEntry( "Terminal" );
+ m_terminalOptionStr = config.readEntry( "TerminalOptions" );
+ m_suidBool = config.readBoolEntry( "X-KDE-SubstituteUID" );
+ m_suidUserStr = config.readEntry( "X-KDE-Username" );
+ if( config.hasKey( "StartupNotify" ))
+ m_startupBool = config.readBoolEntry( "StartupNotify", true );
+ else
+ m_startupBool = config.readBoolEntry( "X-KDE-StartupNotify", true );
+ m_dcopServiceType = config.readEntry("X-DCOP-ServiceType").lower();
+
+ QStringList mimeTypes = config.readListEntry( "MimeType", ';' );
+
+ if ( nameStr.isEmpty() || bKDesktopMode ) {
+ // We'll use the file name if no name is specified
+ // because we _need_ a Name for a valid file.
+ // But let's do it in apply, not here, so that we pick up the right name.
+ setDirty();
+ }
+ if ( !bKDesktopMode )
+ w->nameEdit->setText(nameStr);
+
+ w->genNameEdit->setText( genNameStr );
+ w->commentEdit->setText( commentStr );
+ w->commandEdit->setText( commandStr );
+ w->pathEdit->lineEdit()->setText( pathStr );
+ w->filetypeList->setAllColumnsShowFocus(true);
+
+ KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
+ for(QStringList::ConstIterator it = mimeTypes.begin();
+ it != mimeTypes.end(); )
+ {
+ KMimeType::Ptr p = KMimeType::mimeType(*it);
+ ++it;
+ QString preference;
+ if (it != mimeTypes.end())
+ {
+ bool numeric;
+ (*it).toInt(&numeric);
+ if (numeric)
+ {
+ preference = *it;
+ ++it;
+ }
+ }
+ if (p && (p != defaultMimetype))
+ {
+ new QListViewItem(w->filetypeList, p->name(), p->comment(), preference);
+ }
+ }
+
+}
+
+KDesktopPropsPlugin::~KDesktopPropsPlugin()
+{
+}
+
+void KDesktopPropsPlugin::slotSelectMimetype()
+{
+ QListView *w = (QListView*)sender();
+ QListViewItem *item = w->firstChild();
+ while(item)
+ {
+ if (item->isSelected())
+ w->setSelected(item, false);
+ item = item->nextSibling();
+ }
+}
+
+void KDesktopPropsPlugin::slotAddFiletype()
+{
+ KDialogBase dlg(w, "KPropertiesMimetypes", true,
+ i18n("Add File Type for %1").arg(properties->kurl().fileName()),
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok);
+
+ KGuiItem okItem(i18n("&Add"), QString::null /* no icon */,
+ i18n("Add the selected file types to\nthe list of supported file types."),
+ i18n("Add the selected file types to\nthe list of supported file types."));
+ dlg.setButtonOK(okItem);
+
+ KPropertiesMimetypeBase *mw = new KPropertiesMimetypeBase(&dlg);
+
+ dlg.setMainWidget(mw);
+
+ {
+ mw->listView->setRootIsDecorated(true);
+ mw->listView->setSelectionMode(QListView::Extended);
+ mw->listView->setAllColumnsShowFocus(true);
+ mw->listView->setFullWidth(true);
+ mw->listView->setMinimumSize(500,400);
+
+ connect(mw->listView, SIGNAL(selectionChanged()),
+ this, SLOT(slotSelectMimetype()));
+ connect(mw->listView, SIGNAL(doubleClicked( QListViewItem *, const QPoint &, int )),
+ &dlg, SLOT( slotOk()));
+
+ QMap<QString,QListViewItem*> majorMap;
+ QListViewItem *majorGroup;
+ KMimeType::List mimetypes = KMimeType::allMimeTypes();
+ QValueListIterator<KMimeType::Ptr> it(mimetypes.begin());
+ for (; it != mimetypes.end(); ++it) {
+ QString mimetype = (*it)->name();
+ if (mimetype == KMimeType::defaultMimeType())
+ continue;
+ int index = mimetype.find("/");
+ QString maj = mimetype.left(index);
+ QString min = mimetype.mid(index+1);
+
+ QMapIterator<QString,QListViewItem*> mit = majorMap.find( maj );
+ if ( mit == majorMap.end() ) {
+ majorGroup = new QListViewItem( mw->listView, maj );
+ majorGroup->setExpandable(true);
+ mw->listView->setOpen(majorGroup, true);
+ majorMap.insert( maj, majorGroup );
+ }
+ else
+ {
+ majorGroup = mit.data();
+ }
+
+ QListViewItem *item = new QListViewItem(majorGroup, min, (*it)->comment());
+ item->setPixmap(0, (*it)->pixmap(KIcon::Small, IconSize(KIcon::Small)));
+ }
+ QMapIterator<QString,QListViewItem*> mit = majorMap.find( "all" );
+ if ( mit != majorMap.end())
+ {
+ mw->listView->setCurrentItem(mit.data());
+ mw->listView->ensureItemVisible(mit.data());
+ }
+ }
+
+ if (dlg.exec() == KDialogBase::Accepted)
+ {
+ KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
+ QListViewItem *majorItem = mw->listView->firstChild();
+ while(majorItem)
+ {
+ QString major = majorItem->text(0);
+
+ QListViewItem *minorItem = majorItem->firstChild();
+ while(minorItem)
+ {
+ if (minorItem->isSelected())
+ {
+ QString mimetype = major + "/" + minorItem->text(0);
+ KMimeType::Ptr p = KMimeType::mimeType(mimetype);
+ if (p && (p != defaultMimetype))
+ {
+ mimetype = p->name();
+ bool found = false;
+ QListViewItem *item = w->filetypeList->firstChild();
+ while (item)
+ {
+ if (mimetype == item->text(0))
+ {
+ found = true;
+ break;
+ }
+ item = item->nextSibling();
+ }
+ if (!found) {
+ new QListViewItem(w->filetypeList, p->name(), p->comment());
+ emit changed();
+ }
+ }
+ }
+ minorItem = minorItem->nextSibling();
+ }
+
+ majorItem = majorItem->nextSibling();
+ }
+
+ }
+}
+
+void KDesktopPropsPlugin::slotDelFiletype()
+{
+ delete w->filetypeList->currentItem();
+ emit changed();
+}
+
+void KDesktopPropsPlugin::checkCommandChanged()
+{
+ if (KRun::binaryName(w->commandEdit->text(), true) !=
+ KRun::binaryName(m_origCommandStr, true))
+ {
+ QString m_origCommandStr = w->commandEdit->text();
+ m_dcopServiceType= QString::null; // Reset
+ }
+}
+
+void KDesktopPropsPlugin::applyChanges()
+{
+ kdDebug(250) << "KDesktopPropsPlugin::applyChanges" << endl;
+ QString path = properties->kurl().path();
+
+ QFile f( path );
+
+ if ( !f.open( IO_ReadWrite ) ) {
+ KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
+ "sufficient access to write to <b>%1</b>.</qt>").arg(path));
+ return;
+ }
+ f.close();
+
+ // If the command is changed we reset certain settings that are strongly
+ // coupled to the command.
+ checkCommandChanged();
+
+ KSimpleConfig config( path );
+ config.setDesktopGroup();
+ config.writeEntry( "Type", QString::fromLatin1("Application"));
+ config.writeEntry( "Comment", w->commentEdit->text() );
+ config.writeEntry( "Comment", w->commentEdit->text(), true, false, true ); // for compat
+ config.writeEntry( "GenericName", w->genNameEdit->text() );
+ config.writeEntry( "GenericName", w->genNameEdit->text(), true, false, true ); // for compat
+
+ if (m_systrayBool)
+ config.writePathEntry( "Exec", w->commandEdit->text().prepend("ksystraycmd ") );
+ else
+ config.writePathEntry( "Exec", w->commandEdit->text() );
+ config.writePathEntry( "Path", w->pathEdit->lineEdit()->text() );
+
+ // Write mimeTypes
+ QStringList mimeTypes;
+ for( QListViewItem *item = w->filetypeList->firstChild();
+ item; item = item->nextSibling() )
+ {
+ QString preference = item->text(2);
+ mimeTypes.append(item->text(0));
+ if (!preference.isEmpty())
+ mimeTypes.append(preference);
+ }
+
+ config.writeEntry( "MimeType", mimeTypes, ';' );
+
+ if ( !w->nameEdit->isHidden() ) {
+ QString nameStr = w->nameEdit->text();
+ config.writeEntry( "Name", nameStr );
+ config.writeEntry( "Name", nameStr, true, false, true );
+ }
+
+ config.writeEntry("Terminal", m_terminalBool);
+ config.writeEntry("TerminalOptions", m_terminalOptionStr);
+ config.writeEntry("X-KDE-SubstituteUID", m_suidBool);
+ config.writeEntry("X-KDE-Username", m_suidUserStr);
+ config.writeEntry("StartupNotify", m_startupBool);
+ config.writeEntry("X-DCOP-ServiceType", m_dcopServiceType);
+ config.sync();
+
+ // KSycoca update needed?
+ QString sycocaPath = KGlobal::dirs()->relativeLocation("apps", path);
+ bool updateNeeded = !sycocaPath.startsWith("/");
+ if (!updateNeeded)
+ {
+ sycocaPath = KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
+ updateNeeded = !sycocaPath.startsWith("/");
+ }
+ if (updateNeeded)
+ KService::rebuildKSycoca(w);
+}
+
+
+void KDesktopPropsPlugin::slotBrowseExec()
+{
+ KURL f = KFileDialog::getOpenURL( QString::null,
+ QString::null, w );
+ if ( f.isEmpty() )
+ return;
+
+ if ( !f.isLocalFile()) {
+ KMessageBox::sorry(w, i18n("Only executables on local file systems are supported."));
+ return;
+ }
+
+ QString path = f.path();
+ KRun::shellQuote( path );
+ w->commandEdit->setText( path );
+}
+
+void KDesktopPropsPlugin::slotAdvanced()
+{
+ KDialogBase dlg(w, "KPropertiesDesktopAdv", true,
+ i18n("Advanced Options for %1").arg(properties->kurl().fileName()),
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok);
+ KPropertiesDesktopAdvBase *w = new KPropertiesDesktopAdvBase(&dlg);
+
+ dlg.setMainWidget(w);
+
+ // If the command is changed we reset certain settings that are strongly
+ // coupled to the command.
+ checkCommandChanged();
+
+ // check to see if we use konsole if not do not add the nocloseonexit
+ // because we don't know how to do this on other terminal applications
+ KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
+ QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
+ QString::fromLatin1("konsole"));
+
+ bool terminalCloseBool = false;
+
+ if (preferredTerminal == "konsole")
+ {
+ terminalCloseBool = (m_terminalOptionStr.contains( "--noclose" ) > 0);
+ w->terminalCloseCheck->setChecked(terminalCloseBool);
+ m_terminalOptionStr.replace( "--noclose", "");
+ }
+ else
+ {
+ w->terminalCloseCheck->hide();
+ }
+
+ w->terminalCheck->setChecked(m_terminalBool);
+ w->terminalEdit->setText(m_terminalOptionStr);
+ w->terminalCloseCheck->setEnabled(m_terminalBool);
+ w->terminalEdit->setEnabled(m_terminalBool);
+ w->terminalEditLabel->setEnabled(m_terminalBool);
+
+ w->suidCheck->setChecked(m_suidBool);
+ w->suidEdit->setText(m_suidUserStr);
+ w->suidEdit->setEnabled(m_suidBool);
+ w->suidEditLabel->setEnabled(m_suidBool);
+
+ w->startupInfoCheck->setChecked(m_startupBool);
+ w->systrayCheck->setChecked(m_systrayBool);
+
+ if (m_dcopServiceType == "unique")
+ w->dcopCombo->setCurrentItem(2);
+ else if (m_dcopServiceType == "multi")
+ w->dcopCombo->setCurrentItem(1);
+ else if (m_dcopServiceType == "wait")
+ w->dcopCombo->setCurrentItem(3);
+ else
+ w->dcopCombo->setCurrentItem(0);
+
+ // Provide username completion up to 1000 users.
+ KCompletion *kcom = new KCompletion;
+ kcom->setOrder(KCompletion::Sorted);
+ struct passwd *pw;
+ int i, maxEntries = 1000;
+ setpwent();
+ for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
+ kcom->addItem(QString::fromLatin1(pw->pw_name));
+ endpwent();
+ if (i < maxEntries)
+ {
+ w->suidEdit->setCompletionObject(kcom, true);
+ w->suidEdit->setAutoDeleteCompletionObject( true );
+ w->suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
+ }
+ else
+ {
+ delete kcom;
+ }
+
+ connect( w->terminalEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( w->terminalCloseCheck, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed() ) );
+ connect( w->terminalCheck, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed() ) );
+ connect( w->suidCheck, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed() ) );
+ connect( w->suidEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( w->startupInfoCheck, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed() ) );
+ connect( w->systrayCheck, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed() ) );
+ connect( w->dcopCombo, SIGNAL( highlighted( int ) ),
+ this, SIGNAL( changed() ) );
+
+ if ( dlg.exec() == QDialog::Accepted )
+ {
+ m_terminalOptionStr = w->terminalEdit->text().stripWhiteSpace();
+ m_terminalBool = w->terminalCheck->isChecked();
+ m_suidBool = w->suidCheck->isChecked();
+ m_suidUserStr = w->suidEdit->text().stripWhiteSpace();
+ m_startupBool = w->startupInfoCheck->isChecked();
+ m_systrayBool = w->systrayCheck->isChecked();
+
+ if (w->terminalCloseCheck->isChecked())
+ {
+ m_terminalOptionStr.append(" --noclose");
+ }
+
+ switch(w->dcopCombo->currentItem())
+ {
+ case 1: m_dcopServiceType = "multi"; break;
+ case 2: m_dcopServiceType = "unique"; break;
+ case 3: m_dcopServiceType = "wait"; break;
+ default: m_dcopServiceType = "none"; break;
+ }
+ }
+}
+
+bool KDesktopPropsPlugin::supports( KFileItemList _items )
+{
+ if ( _items.count() != 1 )
+ return false;
+ KFileItem * item = _items.first();
+ // check if desktop file
+ if ( !KPropsDlgPlugin::isDesktopFile( item ) )
+ return false;
+ // open file and check type
+ KDesktopFile config( item->url().path(), true /* readonly */ );
+ return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access");
+}
+
+void KPropertiesDialog::virtual_hook( int id, void* data )
+{ KDialogBase::virtual_hook( id, data ); }
+
+void KPropsDlgPlugin::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+
+
+
+
+/**
+ * The following code is obsolete and only kept for binary compatibility
+ * To be removed in KDE 4
+ */
+
+class KExecPropsPlugin::KExecPropsPluginPrivate
+{
+public:
+ KExecPropsPluginPrivate()
+ {
+ }
+ ~KExecPropsPluginPrivate()
+ {
+ }
+
+ QFrame *m_frame;
+ QCheckBox *nocloseonexitCheck;
+};
+
+KExecPropsPlugin::KExecPropsPlugin( KPropertiesDialog *_props )
+ : KPropsDlgPlugin( _props )
+{
+ d = new KExecPropsPluginPrivate;
+ d->m_frame = properties->addPage(i18n("E&xecute"));
+ QVBoxLayout * mainlayout = new QVBoxLayout( d->m_frame, 0,
+ KDialog::spacingHint());
+
+ // Now the widgets in the top layout
+
+ QLabel* l;
+ l = new QLabel( i18n( "Comman&d:" ), d->m_frame );
+ mainlayout->addWidget(l);
+
+ QHBoxLayout * hlayout;
+ hlayout = new QHBoxLayout(KDialog::spacingHint());
+ mainlayout->addLayout(hlayout);
+
+ execEdit = new KLineEdit( d->m_frame );
+ QWhatsThis::add(execEdit,i18n(
+ "Following the command, you can have several place holders which will be replaced "
+ "with the actual values when the actual program is run:\n"
+ "%f - a single file name\n"
+ "%F - a list of files; use for applications that can open several local files at once\n"
+ "%u - a single URL\n"
+ "%U - a list of URLs\n"
+ "%d - the folder of the file to open\n"
+ "%D - a list of folders\n"
+ "%i - the icon\n"
+ "%m - the mini-icon\n"
+ "%c - the caption"));
+ hlayout->addWidget(execEdit, 1);
+
+ l->setBuddy( execEdit );
+
+ execBrowse = new QPushButton( d->m_frame );
+ execBrowse->setText( i18n("&Browse...") );
+ hlayout->addWidget(execBrowse);
+
+ // The groupbox about swallowing
+ QGroupBox* tmpQGroupBox;
+ tmpQGroupBox = new QGroupBox( i18n("Panel Embedding"), d->m_frame );
+ tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
+
+ mainlayout->addWidget(tmpQGroupBox);
+
+ QGridLayout *grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2);
+ grid->setSpacing( KDialog::spacingHint() );
+ grid->setColStretch(1, 1);
+
+ l = new QLabel( i18n( "&Execute on click:" ), tmpQGroupBox );
+ grid->addWidget(l, 0, 0);
+
+ swallowExecEdit = new KLineEdit( tmpQGroupBox );
+ grid->addWidget(swallowExecEdit, 0, 1);
+
+ l->setBuddy( swallowExecEdit );
+
+ l = new QLabel( i18n( "&Window title:" ), tmpQGroupBox );
+ grid->addWidget(l, 1, 0);
+
+ swallowTitleEdit = new KLineEdit( tmpQGroupBox );
+ grid->addWidget(swallowTitleEdit, 1, 1);
+
+ l->setBuddy( swallowTitleEdit );
+
+ // The groupbox about run in terminal
+
+ tmpQGroupBox = new QGroupBox( d->m_frame );
+ tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
+
+ mainlayout->addWidget(tmpQGroupBox);
+
+ grid = new QGridLayout(tmpQGroupBox->layout(), 3, 2);
+ grid->setSpacing( KDialog::spacingHint() );
+ grid->setColStretch(1, 1);
+
+ terminalCheck = new QCheckBox( tmpQGroupBox );
+ terminalCheck->setText( i18n("&Run in terminal") );
+ grid->addMultiCellWidget(terminalCheck, 0, 0, 0, 1);
+
+ // check to see if we use konsole if not do not add the nocloseonexit
+ // because we don't know how to do this on other terminal applications
+ KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
+ QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
+ QString::fromLatin1("konsole"));
+
+ int posOptions = 1;
+ d->nocloseonexitCheck = 0L;
+ if (preferredTerminal == "konsole")
+ {
+ posOptions = 2;
+ d->nocloseonexitCheck = new QCheckBox( tmpQGroupBox );
+ d->nocloseonexitCheck->setText( i18n("Do not &close when command exits") );
+ grid->addMultiCellWidget(d->nocloseonexitCheck, 1, 1, 0, 1);
+ }
+
+ terminalLabel = new QLabel( i18n( "&Terminal options:" ), tmpQGroupBox );
+ grid->addWidget(terminalLabel, posOptions, 0);
+
+ terminalEdit = new KLineEdit( tmpQGroupBox );
+ grid->addWidget(terminalEdit, posOptions, 1);
+
+ terminalLabel->setBuddy( terminalEdit );
+
+ // The groupbox about run with substituted uid.
+
+ tmpQGroupBox = new QGroupBox( d->m_frame );
+ tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
+
+ mainlayout->addWidget(tmpQGroupBox);
+
+ grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2);
+ grid->setSpacing(KDialog::spacingHint());
+ grid->setColStretch(1, 1);
+
+ suidCheck = new QCheckBox(tmpQGroupBox);
+ suidCheck->setText(i18n("Ru&n as a different user"));
+ grid->addMultiCellWidget(suidCheck, 0, 0, 0, 1);
+
+ suidLabel = new QLabel(i18n( "&Username:" ), tmpQGroupBox);
+ grid->addWidget(suidLabel, 1, 0);
+
+ suidEdit = new KLineEdit(tmpQGroupBox);
+ grid->addWidget(suidEdit, 1, 1);
+
+ suidLabel->setBuddy( suidEdit );
+
+ mainlayout->addStretch(1);
+
+ // now populate the page
+ QString path = _props->kurl().path();
+ QFile f( path );
+ if ( !f.open( IO_ReadOnly ) )
+ return;
+ f.close();
+
+ KSimpleConfig config( path );
+ config.setDollarExpansion( false );
+ config.setDesktopGroup();
+ execStr = config.readPathEntry( "Exec" );
+ swallowExecStr = config.readPathEntry( "SwallowExec" );
+ swallowTitleStr = config.readEntry( "SwallowTitle" );
+ termBool = config.readBoolEntry( "Terminal" );
+ termOptionsStr = config.readEntry( "TerminalOptions" );
+ suidBool = config.readBoolEntry( "X-KDE-SubstituteUID" );
+ suidUserStr = config.readEntry( "X-KDE-Username" );
+
+ if ( !swallowExecStr.isNull() )
+ swallowExecEdit->setText( swallowExecStr );
+ if ( !swallowTitleStr.isNull() )
+ swallowTitleEdit->setText( swallowTitleStr );
+
+ if ( !execStr.isNull() )
+ execEdit->setText( execStr );
+
+ if ( d->nocloseonexitCheck )
+ {
+ d->nocloseonexitCheck->setChecked( (termOptionsStr.contains( "--noclose" ) > 0) );
+ termOptionsStr.replace( "--noclose", "");
+ }
+ if ( !termOptionsStr.isNull() )
+ terminalEdit->setText( termOptionsStr );
+
+ terminalCheck->setChecked( termBool );
+ enableCheckedEdit();
+
+ suidCheck->setChecked( suidBool );
+ suidEdit->setText( suidUserStr );
+ enableSuidEdit();
+
+ // Provide username completion up to 1000 users.
+ KCompletion *kcom = new KCompletion;
+ kcom->setOrder(KCompletion::Sorted);
+ struct passwd *pw;
+ int i, maxEntries = 1000;
+ setpwent();
+ for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
+ kcom->addItem(QString::fromLatin1(pw->pw_name));
+ endpwent();
+ if (i < maxEntries)
+ {
+ suidEdit->setCompletionObject(kcom, true);
+ suidEdit->setAutoDeleteCompletionObject( true );
+ suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
+ }
+ else
+ {
+ delete kcom;
+ }
+
+ connect( swallowExecEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( swallowTitleEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( execEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( terminalEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ if (d->nocloseonexitCheck)
+ connect( d->nocloseonexitCheck, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed() ) );
+ connect( terminalCheck, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed() ) );
+ connect( suidCheck, SIGNAL( toggled( bool ) ),
+ this, SIGNAL( changed() ) );
+ connect( suidEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+
+ connect( execBrowse, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
+ connect( terminalCheck, SIGNAL( clicked() ), this, SLOT( enableCheckedEdit() ) );
+ connect( suidCheck, SIGNAL( clicked() ), this, SLOT( enableSuidEdit() ) );
+
+}
+
+KExecPropsPlugin::~KExecPropsPlugin()
+{
+ delete d;
+}
+
+void KExecPropsPlugin::enableCheckedEdit()
+{
+ bool checked = terminalCheck->isChecked();
+ terminalLabel->setEnabled( checked );
+ if (d->nocloseonexitCheck)
+ d->nocloseonexitCheck->setEnabled( checked );
+ terminalEdit->setEnabled( checked );
+}
+
+void KExecPropsPlugin::enableSuidEdit()
+{
+ bool checked = suidCheck->isChecked();
+ suidLabel->setEnabled( checked );
+ suidEdit->setEnabled( checked );
+}
+
+bool KExecPropsPlugin::supports( KFileItemList _items )
+{
+ if ( _items.count() != 1 )
+ return false;
+ KFileItem * item = _items.first();
+ // check if desktop file
+ if ( !KPropsDlgPlugin::isDesktopFile( item ) )
+ return false;
+ // open file and check type
+ KDesktopFile config( item->url().path(), true /* readonly */ );
+ return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access");
+}
+
+void KExecPropsPlugin::applyChanges()
+{
+ kdDebug(250) << "KExecPropsPlugin::applyChanges" << endl;
+ QString path = properties->kurl().path();
+
+ QFile f( path );
+
+ if ( !f.open( IO_ReadWrite ) ) {
+ KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
+ "sufficient access to write to <b>%1</b>.</qt>").arg(path));
+ return;
+ }
+ f.close();
+
+ KSimpleConfig config( path );
+ config.setDesktopGroup();
+ config.writeEntry( "Type", QString::fromLatin1("Application"));
+ config.writePathEntry( "Exec", execEdit->text() );
+ config.writePathEntry( "SwallowExec", swallowExecEdit->text() );
+ config.writeEntry( "SwallowTitle", swallowTitleEdit->text() );
+ config.writeEntry( "Terminal", terminalCheck->isChecked() );
+ QString temp = terminalEdit->text();
+ if (d->nocloseonexitCheck )
+ if ( d->nocloseonexitCheck->isChecked() )
+ temp += QString::fromLatin1("--noclose ");
+ temp = temp.stripWhiteSpace();
+ config.writeEntry( "TerminalOptions", temp );
+ config.writeEntry( "X-KDE-SubstituteUID", suidCheck->isChecked() );
+ config.writeEntry( "X-KDE-Username", suidEdit->text() );
+}
+
+
+void KExecPropsPlugin::slotBrowseExec()
+{
+ KURL f = KFileDialog::getOpenURL( QString::null,
+ QString::null, d->m_frame );
+ if ( f.isEmpty() )
+ return;
+
+ if ( !f.isLocalFile()) {
+ KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported."));
+ return;
+ }
+
+ QString path = f.path();
+ KRun::shellQuote( path );
+ execEdit->setText( path );
+}
+
+class KApplicationPropsPlugin::KApplicationPropsPluginPrivate
+{
+public:
+ KApplicationPropsPluginPrivate()
+ {
+ m_kdesktopMode = QCString(qApp->name()) == "kdesktop"; // nasty heh?
+ }
+ ~KApplicationPropsPluginPrivate()
+ {
+ }
+
+ QFrame *m_frame;
+ bool m_kdesktopMode;
+};
+
+KApplicationPropsPlugin::KApplicationPropsPlugin( KPropertiesDialog *_props )
+ : KPropsDlgPlugin( _props )
+{
+ d = new KApplicationPropsPluginPrivate;
+ d->m_frame = properties->addPage(i18n("&Application"));
+ QVBoxLayout *toplayout = new QVBoxLayout( d->m_frame, 0, KDialog::spacingHint());
+
+ QIconSet iconSet;
+ QPixmap pixMap;
+
+ addExtensionButton = new QPushButton( QString::null, d->m_frame );
+ iconSet = SmallIconSet( "back" );
+ addExtensionButton->setIconSet( iconSet );
+ pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal );
+ addExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
+ connect( addExtensionButton, SIGNAL( clicked() ),
+ SLOT( slotAddExtension() ) );
+
+ delExtensionButton = new QPushButton( QString::null, d->m_frame );
+ iconSet = SmallIconSet( "forward" );
+ delExtensionButton->setIconSet( iconSet );
+ delExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
+ connect( delExtensionButton, SIGNAL( clicked() ),
+ SLOT( slotDelExtension() ) );
+
+ QLabel *l;
+
+ QGridLayout *grid = new QGridLayout(2, 2);
+ grid->setColStretch(1, 1);
+ toplayout->addLayout(grid);
+
+ if ( d->m_kdesktopMode )
+ {
+ // in kdesktop the name field comes from the first tab
+ nameEdit = 0L;
+ }
+ else
+ {
+ l = new QLabel(i18n("Name:"), d->m_frame, "Label_4" );
+ grid->addWidget(l, 0, 0);
+
+ nameEdit = new KLineEdit( d->m_frame, "LineEdit_3" );
+ grid->addWidget(nameEdit, 0, 1);
+ }
+
+ l = new QLabel(i18n("Description:"), d->m_frame, "Label_5" );
+ grid->addWidget(l, 1, 0);
+
+ genNameEdit = new KLineEdit( d->m_frame, "LineEdit_4" );
+ grid->addWidget(genNameEdit, 1, 1);
+
+ l = new QLabel(i18n("Comment:"), d->m_frame, "Label_3" );
+ grid->addWidget(l, 2, 0);
+
+ commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" );
+ grid->addWidget(commentEdit, 2, 1);
+
+ l = new QLabel(i18n("File types:"), d->m_frame);
+ toplayout->addWidget(l, 0, AlignLeft);
+
+ grid = new QGridLayout(4, 3);
+ grid->setColStretch(0, 1);
+ grid->setColStretch(2, 1);
+ grid->setRowStretch( 0, 1 );
+ grid->setRowStretch( 3, 1 );
+ toplayout->addLayout(grid, 2);
+
+ extensionsList = new QListBox( d->m_frame );
+ extensionsList->setSelectionMode( QListBox::Extended );
+ grid->addMultiCellWidget(extensionsList, 0, 3, 0, 0);
+
+ grid->addWidget(addExtensionButton, 1, 1);
+ grid->addWidget(delExtensionButton, 2, 1);
+
+ availableExtensionsList = new QListBox( d->m_frame );
+ availableExtensionsList->setSelectionMode( QListBox::Extended );
+ grid->addMultiCellWidget(availableExtensionsList, 0, 3, 2, 2);
+
+ QString path = properties->kurl().path() ;
+ QFile f( path );
+ if ( !f.open( IO_ReadOnly ) )
+ return;
+ f.close();
+
+ KDesktopFile config( path );
+ QString commentStr = config.readComment();
+ QString genNameStr = config.readGenericName();
+
+ QStringList selectedTypes = config.readListEntry( "ServiceTypes" );
+ // For compatibility with KDE 1.x
+ selectedTypes += config.readListEntry( "MimeType", ';' );
+
+ QString nameStr = config.readName();
+ if ( nameStr.isEmpty() || d->m_kdesktopMode ) {
+ // We'll use the file name if no name is specified
+ // because we _need_ a Name for a valid file.
+ // But let's do it in apply, not here, so that we pick up the right name.
+ setDirty();
+ }
+
+ commentEdit->setText( commentStr );
+ genNameEdit->setText( genNameStr );
+ if ( nameEdit )
+ nameEdit->setText( nameStr );
+
+ selectedTypes.sort();
+ QStringList::Iterator sit = selectedTypes.begin();
+ for( ; sit != selectedTypes.end(); ++sit ) {
+ if ( !((*sit).isEmpty()) )
+ extensionsList->insertItem( *sit );
+ }
+
+ KMimeType::List mimeTypes = KMimeType::allMimeTypes();
+ QValueListIterator<KMimeType::Ptr> it2 = mimeTypes.begin();
+ for ( ; it2 != mimeTypes.end(); ++it2 )
+ addMimeType ( (*it2)->name() );
+
+ updateButton();
+
+ connect( extensionsList, SIGNAL( highlighted( int ) ),
+ this, SLOT( updateButton() ) );
+ connect( availableExtensionsList, SIGNAL( highlighted( int ) ),
+ this, SLOT( updateButton() ) );
+
+ connect( addExtensionButton, SIGNAL( clicked() ),
+ this, SIGNAL( changed() ) );
+ connect( delExtensionButton, SIGNAL( clicked() ),
+ this, SIGNAL( changed() ) );
+ if ( nameEdit )
+ connect( nameEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( commentEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( genNameEdit, SIGNAL( textChanged( const QString & ) ),
+ this, SIGNAL( changed() ) );
+ connect( availableExtensionsList, SIGNAL( selected( int ) ),
+ this, SIGNAL( changed() ) );
+ connect( extensionsList, SIGNAL( selected( int ) ),
+ this, SIGNAL( changed() ) );
+}
+
+KApplicationPropsPlugin::~KApplicationPropsPlugin()
+{
+ delete d;
+}
+
+// QString KApplicationPropsPlugin::tabName () const
+// {
+// return i18n ("&Application");
+// }
+
+void KApplicationPropsPlugin::updateButton()
+{
+ addExtensionButton->setEnabled(availableExtensionsList->currentItem()>-1);
+ delExtensionButton->setEnabled(extensionsList->currentItem()>-1);
+}
+
+void KApplicationPropsPlugin::addMimeType( const QString & name )
+{
+ // Add a mimetype to the list of available mime types if not in the extensionsList
+
+ bool insert = true;
+
+ for ( uint i = 0; i < extensionsList->count(); i++ )
+ if ( extensionsList->text( i ) == name )
+ insert = false;
+
+ if ( insert )
+ {
+ availableExtensionsList->insertItem( name );
+ availableExtensionsList->sort();
+ }
+}
+
+bool KApplicationPropsPlugin::supports( KFileItemList _items )
+{
+ // same constraints as KExecPropsPlugin : desktop file with Type = Application
+ return KExecPropsPlugin::supports( _items );
+}
+
+void KApplicationPropsPlugin::applyChanges()
+{
+ QString path = properties->kurl().path();
+
+ QFile f( path );
+
+ if ( !f.open( IO_ReadWrite ) ) {
+ KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
+ "have sufficient access to write to <b>%1</b>.</qt>").arg(path));
+ return;
+ }
+ f.close();
+
+ KSimpleConfig config( path );
+ config.setDesktopGroup();
+ config.writeEntry( "Type", QString::fromLatin1("Application"));
+ config.writeEntry( "Comment", commentEdit->text() );
+ config.writeEntry( "Comment", commentEdit->text(), true, false, true ); // for compat
+ config.writeEntry( "GenericName", genNameEdit->text() );
+ config.writeEntry( "GenericName", genNameEdit->text(), true, false, true ); // for compat
+
+ QStringList selectedTypes;
+ for ( uint i = 0; i < extensionsList->count(); i++ )
+ selectedTypes.append( extensionsList->text( i ) );
+
+ config.writeEntry( "MimeType", selectedTypes, ';' );
+ config.writeEntry( "ServiceTypes", "" );
+ // hmm, actually it should probably be the contrary (but see also typeslistitem.cpp)
+
+ QString nameStr = nameEdit ? nameEdit->text() : QString::null;
+ if ( nameStr.isEmpty() ) // nothing entered, or widget not existing at all (kdesktop mode)
+ nameStr = nameFromFileName(properties->kurl().fileName());
+
+ config.writeEntry( "Name", nameStr );
+ config.writeEntry( "Name", nameStr, true, false, true );
+
+ config.sync();
+}
+
+void KApplicationPropsPlugin::slotAddExtension()
+{
+ QListBoxItem *item = availableExtensionsList->firstItem();
+ QListBoxItem *nextItem;
+
+ while ( item )
+ {
+ nextItem = item->next();
+
+ if ( item->isSelected() )
+ {
+ extensionsList->insertItem( item->text() );
+ availableExtensionsList->removeItem( availableExtensionsList->index( item ) );
+ }
+
+ item = nextItem;
+ }
+
+ extensionsList->sort();
+ updateButton();
+}
+
+void KApplicationPropsPlugin::slotDelExtension()
+{
+ QListBoxItem *item = extensionsList->firstItem();
+ QListBoxItem *nextItem;
+
+ while ( item )
+ {
+ nextItem = item->next();
+
+ if ( item->isSelected() )
+ {
+ availableExtensionsList->insertItem( item->text() );
+ extensionsList->removeItem( extensionsList->index( item ) );
+ }
+
+ item = nextItem;
+ }
+
+ availableExtensionsList->sort();
+ updateButton();
+}
+
+
+
+#include "kpropertiesdialog.moc"