Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F16568826
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
117 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/kio/kfile/kpropertiesdialog.cpp b/kio/kfile/kpropertiesdialog.cpp
index 3382daa269..d05cf14abe 100644
--- a/kio/kfile/kpropertiesdialog.cpp
+++ b/kio/kfile/kpropertiesdialog.cpp
@@ -1,3435 +1,3434 @@
/* 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 KDialog by
* Simon Hausmann <hausmann@kde.org>
* KDesktopPropsPlugin by
* Waldo Bastian <bastian@kde.org>
*/
#include "kpropertiesdialog.h"
#include "kpropertiesdialog_p.h"
#include <config.h>
#include <config-acl.h>
extern "C" {
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
}
#include <unistd.h>
#include <errno.h>
-#include <assert.h>
#include <algorithm>
#include <functional>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtGui/QLabel>
#include <QtGui/QPushButton>
#include <QtGui/QCheckBox>
#include <QtCore/QMutableStringListIterator>
#include <QtCore/QTextIStream>
#include <QtGui/QPainter>
#include <QtGui/QLayout>
#include <QtGui/QStyle>
#include <QtGui/QProgressBar>
#include <QVector>
#include <QFileInfo>
#ifdef HAVE_POSIX_ACL
extern "C" {
# include <sys/xattr.h>
}
#endif
#include <kauthorized.h>
#include <kdialog.h>
#include <kdirnotify.h>
#include <kdiskfreespaceinfo.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 <kjobuidelegate.h>
#include <kio/job.h>
#include <kio/copyjob.h>
#include <kio/chmodjob.h>
#include <kio/directorysizejob.h>
#include <kio/renamedialog.h>
#include <kio/netaccess.h>
#include <kio/jobuidelegate.h>
#include <kfiledialog.h>
#include <kmimetype.h>
#include <kmountpoint.h>
#include <kiconloader.h>
#include <kmessagebox.h>
#include <kservice.h>
#include <kcombobox.h>
#include <kcompletion.h>
#include <klineedit.h>
#include <kseparator.h>
#include <ksqueezedtextlabel.h>
#include <kmimetypetrader.h>
#include <kmetaprops.h>
#include <kpreviewprops.h>
#include <krun.h>
#include <kvbox.h>
#include <kacl.h>
#include <kconfiggroup.h>
#include <kshell.h>
#include <kcapacitybar.h>
#include <kfileitemlistproperties.h>
#ifndef Q_OS_WIN
#include "kfilesharedialog.h"
#endif
#include "ui_kpropertiesdesktopbase.h"
#include "ui_kpropertiesdesktopadvbase.h"
#ifdef HAVE_POSIX_ACL
#include "kacleditwidget.h"
#endif
#include <kbuildsycocaprogressdialog.h>
#include <kmimetypechooser.h>
#ifdef Q_WS_WIN
# include <kkernel_win.h>
#ifdef __GNUC__
# warning TODO: port completely to win32
#endif
#endif
using namespace KDEPrivate;
static QString nameFromFileName(QString nameStr)
{
if ( nameStr.endsWith(QLatin1String(".desktop")) )
nameStr.truncate( nameStr.length() - 8 );
if ( nameStr.endsWith(QLatin1String(".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(KPropertiesDialog *qq)
{
q = qq;
m_aborted = false;
fileSharePage = 0;
}
~KPropertiesDialogPrivate()
{
}
/**
* Common initialization for all constructors
*/
void init();
/**
* Inserts all pages in the dialog.
*/
void insertPages();
KPropertiesDialog *q;
bool m_aborted:1;
QWidget* fileSharePage;
/**
* The URL of the props dialog (when shown for only one file)
*/
KUrl m_singleUrl;
/**
* List of items this props dialog is shown for
*/
KFileItemList m_items;
/**
* For templates
*/
QString m_defaultName;
KUrl m_currentDir;
/**
* List of all plugins inserted ( first one first )
*/
QList<KPropertiesDialogPlugin*> m_pageList;
};
KPropertiesDialog::KPropertiesDialog (const KFileItem& item,
QWidget* parent)
: KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
{
setCaption( i18n( "Properties for %1" , KIO::decodeFileName(item.url().fileName())) );
- assert( !item.isNull() );
+ Q_ASSERT( !item.isNull() );
d->m_items.append(item);
d->m_singleUrl = item.url();
- assert(!d->m_singleUrl.isEmpty());
+ Q_ASSERT(!d->m_singleUrl.isEmpty());
d->init();
}
KPropertiesDialog::KPropertiesDialog (const QString& title,
QWidget* parent)
: KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
{
setCaption( i18n( "Properties for %1", title ) );
d->init();
}
KPropertiesDialog::KPropertiesDialog(const KFileItemList& _items,
QWidget* parent)
: KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
{
if ( _items.count() > 1 )
setCaption( i18np( "Properties for 1 item", "Properties for %1 Selected Items", _items.count() ) );
else
setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_items.first().url().fileName())) );
- assert( !_items.isEmpty() );
+ Q_ASSERT( !_items.isEmpty() );
d->m_singleUrl = _items.first().url();
- assert(!d->m_singleUrl.isEmpty());
+ Q_ASSERT(!d->m_singleUrl.isEmpty());
d->m_items = _items;
d->init();
}
KPropertiesDialog::KPropertiesDialog (const KUrl& _url,
QWidget* parent)
: KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
{
setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_url.fileName())) );
d->m_singleUrl = _url;
KIO::UDSEntry entry;
KIO::NetAccess::stat(_url, entry, parent);
d->m_items.append(KFileItem(entry, _url));
d->init();
}
KPropertiesDialog::KPropertiesDialog (const KUrl& _tempUrl, const KUrl& _currentDir,
const QString& _defaultName,
QWidget* parent)
: KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
{
setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_tempUrl.fileName())) );
d->m_singleUrl = _tempUrl;
d->m_defaultName = _defaultName;
d->m_currentDir = _currentDir;
- assert(!d->m_singleUrl.isEmpty());
+ Q_ASSERT(!d->m_singleUrl.isEmpty());
// Create the KFileItem for the _template_ file, in order to read from it.
d->m_items.append(KFileItem(KFileItem::Unknown, KFileItem::Unknown, d->m_singleUrl));
d->init();
}
bool KPropertiesDialog::showDialog(const KFileItem& item, QWidget* parent,
bool modal)
{
// TODO: do we really want to show the win32 property dialog?
// This means we lose metainfo, support for .desktop files, etc. (DF)
#ifdef Q_WS_WIN
QString localPath = item.localPath();
if (!localPath.isEmpty())
return showWin32FilePropertyDialog(localPath);
#endif
KPropertiesDialog* dlg = new KPropertiesDialog(item, parent);
if (modal) {
dlg->exec();
} else {
dlg->show();
}
return true;
}
bool KPropertiesDialog::showDialog(const KUrl& _url, QWidget* parent,
bool modal)
{
#ifdef Q_WS_WIN
if (_url.isLocalFile())
return showWin32FilePropertyDialog( _url.toLocalFile() );
#endif
KPropertiesDialog* dlg = new KPropertiesDialog(_url, parent);
if (modal) {
dlg->exec();
} else {
dlg->show();
}
return true;
}
bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent,
bool modal)
{
if (_items.count()==1) {
const KFileItem item = _items.first();
if (item.entry().count() == 0 && item.localPath().isEmpty()) // this remote item wasn't listed by a slave
// Let's stat to get more info on the file
return KPropertiesDialog::showDialog(item.url(), parent, modal);
else
return KPropertiesDialog::showDialog(_items.first(), parent, modal);
}
KPropertiesDialog* dlg = new KPropertiesDialog(_items, parent);
if (modal) {
dlg->exec();
} else {
dlg->show();
}
return true;
}
void KPropertiesDialog::KPropertiesDialogPrivate::init()
{
q->setFaceType(KPageDialog::Tabbed);
q->setButtons(KDialog::Ok | KDialog::Cancel);
q->setDefaultButton(KDialog::Ok);
connect(q, SIGNAL(okClicked()), q, SLOT(slotOk()));
connect(q, SIGNAL(cancelClicked()), q, SLOT(slotCancel()));
insertPages();
KConfigGroup group(KGlobal::config(), "KPropertiesDialog");
q->restoreDialogSize(group);
}
void KPropertiesDialog::showFileSharingPage()
{
if (d->fileSharePage) {
// FIXME: this showFileSharingPage thingy looks broken! (tokoe)
// showPage( pageIndex( d->fileSharePage));
}
}
void KPropertiesDialog::setFileSharingPage(QWidget* page) {
d->fileSharePage = page;
}
void KPropertiesDialog::setFileNameReadOnly( bool ro )
{
foreach(KPropertiesDialogPlugin *it, d->m_pageList) {
KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it);
if ( plugin ) {
plugin->setFileNameReadOnly( ro );
break;
}
}
}
KPropertiesDialog::~KPropertiesDialog()
{
qDeleteAll(d->m_pageList);
delete d;
KConfigGroup group(KGlobal::config(), "KPropertiesDialog");
saveDialogSize(group, KConfigBase::Persistent);
}
void KPropertiesDialog::insertPlugin (KPropertiesDialogPlugin* plugin)
{
connect (plugin, SIGNAL (changed ()),
plugin, SLOT (setDirty ()));
d->m_pageList.append(plugin);
}
KUrl KPropertiesDialog::kurl() const
{
return d->m_singleUrl;
}
KFileItem& KPropertiesDialog::item()
{
return d->m_items.first();
}
KFileItemList KPropertiesDialog::items() const
{
return d->m_items;
}
KUrl KPropertiesDialog::currentDir() const
{
return d->m_currentDir;
}
QString KPropertiesDialog::defaultName() const
{
return d->m_defaultName;
}
bool KPropertiesDialog::canDisplay( const 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 ) ||
KUrlPropsPlugin::supports( _items ) ||
KDevicePropsPlugin::supports( _items ) ||
KFileMetaPropsPlugin::supports( _items ) ||
KPreviewPropsPlugin::supports( _items );
}
void KPropertiesDialog::slotOk()
{
QList<KPropertiesDialogPlugin*>::const_iterator pageListIt;
d->m_aborted = false;
KFilePropsPlugin * filePropsPlugin = qobject_cast<KFilePropsPlugin*>(d->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 (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd(); ++pageListIt) {
if ( (*pageListIt)->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 (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd() && !d->m_aborted; ++pageListIt) {
if ( (*pageListIt)->isDirty() )
{
kDebug( 250 ) << "applying changes for " << (*pageListIt)->metaObject()->className();
(*pageListIt)->applyChanges();
// applyChanges may change d->m_aborted.
}
else {
kDebug( 250 ) << "skipping page " << (*pageListIt)->metaObject()->className();
}
}
if ( !d->m_aborted && filePropsPlugin )
filePropsPlugin->postApplyChanges();
if ( !d->m_aborted )
{
emit applied();
emit propertiesClosed();
deleteLater(); // somewhat like Qt::WA_DeleteOnClose would do.
accept();
} // else, keep dialog open for user to fix the problem.
}
void KPropertiesDialog::slotCancel()
{
emit canceled();
emit propertiesClosed();
deleteLater();
done( Rejected );
}
void KPropertiesDialog::KPropertiesDialogPrivate::insertPages()
{
if (m_items.isEmpty())
return;
if ( KFilePropsPlugin::supports( m_items ) ) {
KPropertiesDialogPlugin *p = new KFilePropsPlugin(q);
q->insertPlugin(p);
}
if ( KFilePermissionsPropsPlugin::supports( m_items ) ) {
KPropertiesDialogPlugin *p = new KFilePermissionsPropsPlugin(q);
q->insertPlugin(p);
}
if ( KDesktopPropsPlugin::supports( m_items ) ) {
KPropertiesDialogPlugin *p = new KDesktopPropsPlugin(q);
q->insertPlugin(p);
}
if ( KUrlPropsPlugin::supports( m_items ) ) {
KPropertiesDialogPlugin *p = new KUrlPropsPlugin(q);
q->insertPlugin(p);
}
if ( KDevicePropsPlugin::supports( m_items ) ) {
KPropertiesDialogPlugin *p = new KDevicePropsPlugin(q);
q->insertPlugin(p);
}
if ( KFileMetaPropsPlugin::supports( m_items ) ) {
KPropertiesDialogPlugin *p = new KFileMetaPropsPlugin(q);
q->insertPlugin(p);
}
if ( KPreviewPropsPlugin::supports( m_items ) ) {
KPropertiesDialogPlugin *p = new KPreviewPropsPlugin(q);
q->insertPlugin(p);
}
//plugins
if ( m_items.count() != 1 )
return;
const KFileItem item = m_items.first();
const QString mimetype = item.mimetype();
if ( mimetype.isEmpty() )
return;
QString query = QString::fromLatin1(
"((not exist [X-KDE-Protocol]) or "
" ([X-KDE-Protocol] == '%1' ) )"
).arg(item.url().protocol());
kDebug( 250 ) << "trader query: " << query;
const KService::List offers = KMimeTypeTrader::self()->query( mimetype, "KPropertiesDialog/Plugin", query );
foreach (const KService::Ptr &ptr, offers) {
KPropertiesDialogPlugin *plugin = ptr->createInstance<KPropertiesDialogPlugin>(q);
if (!plugin)
continue;
plugin->setObjectName(ptr->name());
q->insertPlugin(plugin);
}
}
void KPropertiesDialog::updateUrl( const KUrl& _newUrl )
{
Q_ASSERT(d->m_items.count() == 1);
kDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url();
KUrl newUrl = _newUrl;
emit saveAs(d->m_singleUrl, newUrl);
kDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url();
d->m_singleUrl = newUrl;
d->m_items.first().setUrl(newUrl);
- assert(!d->m_singleUrl.isEmpty());
+ Q_ASSERT(!d->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)
foreach (KPropertiesDialogPlugin *it, d->m_pageList) {
if ( qobject_cast<KUrlPropsPlugin*>(it) ||
qobject_cast<KDesktopPropsPlugin*>(it) )
{
//kDebug(250) << "Setting page dirty";
it->setDirty();
break;
}
}
}
void KPropertiesDialog::rename( const QString& _name )
{
Q_ASSERT(d->m_items.count() == 1);
kDebug(250) << "KPropertiesDialog::rename " << _name;
KUrl newUrl;
// if we're creating from a template : use currentdir
if (!d->m_currentDir.isEmpty()) {
newUrl = d->m_currentDir;
newUrl.addPath(_name);
} else {
QString tmpurl = d->m_singleUrl.url();
if (!tmpurl.isEmpty() && 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 KPropertiesDialogPlugin::KPropertiesDialogPluginPrivate
{
public:
KPropertiesDialogPluginPrivate()
{
}
~KPropertiesDialogPluginPrivate()
{
}
bool m_bDirty;
int fontHeight;
};
KPropertiesDialogPlugin::KPropertiesDialogPlugin( KPropertiesDialog *_props )
: QObject( _props ),d(new KPropertiesDialogPluginPrivate)
{
properties = _props;
d->fontHeight = 2*properties->fontMetrics().height();
d->m_bDirty = false;
}
KPropertiesDialogPlugin::~KPropertiesDialogPlugin()
{
delete d;
}
#ifndef KDE_NO_DEPRECATED
bool KPropertiesDialogPlugin::isDesktopFile( const KFileItem& _item )
{
return _item.isDesktopFile();
}
#endif
void KPropertiesDialogPlugin::setDirty( bool b )
{
d->m_bDirty = b;
}
void KPropertiesDialogPlugin::setDirty()
{
d->m_bDirty = true;
}
bool KPropertiesDialogPlugin::isDirty() const
{
return d->m_bDirty;
}
void KPropertiesDialogPlugin::applyChanges()
{
kWarning(250) << "applyChanges() not implemented in page !";
}
int KPropertiesDialogPlugin::fontHeight() const
{
return d->fontHeight;
}
///////////////////////////////////////////////////////////////////////////////
class KFilePropsPlugin::KFilePropsPluginPrivate
{
public:
KFilePropsPluginPrivate()
{
dirSizeJob = 0L;
dirSizeUpdateTimer = 0L;
m_lined = 0;
m_capacityBar = 0;
m_linkTargetLineEdit = 0;
}
~KFilePropsPluginPrivate()
{
if ( dirSizeJob )
dirSizeJob->kill();
}
KIO::DirectorySizeJob * dirSizeJob;
QTimer *dirSizeUpdateTimer;
QFrame *m_frame;
bool bMultiple;
bool bIconChanged;
bool bKDesktopMode;
bool bDesktopFile;
KCapacityBar *m_capacityBar;
QString mimeType;
QString oldFileName;
KLineEdit* m_lined;
QWidget *iconArea;
QWidget *nameArea;
QLabel *m_sizeLabel;
QPushButton *m_sizeDetermineButton;
QPushButton *m_sizeStopButton;
KLineEdit* m_linkTargetLineEdit;
QString m_sRelativePath;
bool m_bFromTemplate;
/**
* The initial filename
*/
QString oldName;
};
KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props )
: KPropertiesDialogPlugin( _props ),d(new KFilePropsPluginPrivate)
{
d->bMultiple = (properties->items().count() > 1);
d->bIconChanged = false;
d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items());
kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple;
// We set this data from the first item, and we'll
// check that the other items match against it, resetting when not.
bool isLocal;
const KFileItem item = properties->item();
KUrl url = item.mostLocalUrl( isLocal );
bool isReallyLocal = item.url().isLocalFile();
bool bDesktopFile = item.isDesktopFile();
mode_t mode = item.mode();
bool hasDirs = item.isDir() && !item.isLink();
bool hasRoot = url.path() == QLatin1String("/");
QString iconStr = KMimeType::iconNameForUrl(url, mode);
QString directory = properties->kurl().directory();
QString protocol = properties->kurl().protocol();
d->bKDesktopMode = protocol == QLatin1String("desktop") ||
properties->currentDir().protocol() == QLatin1String("desktop");
QString mimeComment = item.mimeComment();
d->mimeType = item.mimetype();
KIO::filesize_t totalSize = item.size();
QString magicMimeComment;
if ( isLocal ) {
KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
if ( magicMimeType->name() != KMimeType::defaultMimeType() )
magicMimeComment = magicMimeType->comment();
}
#ifdef Q_WS_WIN
if ( isReallyLocal ) {
directory = QDir::toNativeSeparators( directory.mid( 1 ) );
}
#endif
// Those things only apply to 'single file' mode
QString filename;
bool isTrash = false;
d->m_bFromTemplate = false;
// And those only to 'multiple' mode
uint iDirCount = hasDirs ? 1 : 0;
uint iFileCount = 1-iDirCount;
d->m_frame = new QFrame();
properties->addPage(d->m_frame, i18nc("@title:tab File properties", "&General"));
QVBoxLayout *vbl = new QVBoxLayout( d->m_frame );
vbl->setMargin( 0 );
vbl->setObjectName( QLatin1String( "vbl" ) );
QGridLayout *grid = new QGridLayout(); // unknown rows
grid->setColumnStretch(0, 0);
grid->setColumnStretch(1, 0);
grid->setColumnStretch(2, 1);
grid->addItem(new QSpacerItem(KDialog::spacingHint(),0), 0, 1);
vbl->addLayout(grid);
int curRow = 0;
if ( !d->bMultiple )
{
QString path;
if ( !d->m_bFromTemplate ) {
isTrash = ( properties->kurl().protocol().toLower() == "trash" );
// Extract the full name, but without file: for local files
if ( isReallyLocal )
path = properties->kurl().toLocalFile();
else
path = properties->kurl().prettyUrl();
} else {
path = properties->currentDir().path(KUrl::AddTrailingSlash) + properties->defaultName();
directory = properties->currentDir().prettyUrl();
}
if (d->bDesktopFile) {
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 {
d->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() );
if ( config.desktopGroup().hasKey( "Name" ) ) {
filename = config.readName();
}
}
d->oldName = filename;
}
else
{
// Multiple items: see what they have in common
const KFileItemList items = properties->items();
KFileItemList::const_iterator kit = items.begin();
const KFileItemList::const_iterator kend = items.end();
for ( ++kit /*no need to check the first one again*/ ; kit != kend; ++kit )
{
const KUrl url = (*kit).url();
kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyUrl();
// 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 && (*kit).isDesktopFile() != bDesktopFile )
bDesktopFile = false; // not all desktop files
if ( (*kit).mode() != mode )
mode = (mode_t)0;
if ( KMimeType::iconNameForUrl(url, mode) != iconStr )
iconStr = "document-multiple";
if ( url.directory() != directory )
directory.clear();
if ( url.protocol() != protocol )
protocol.clear();
if ( !mimeComment.isNull() && (*kit).mimeComment() != mimeComment )
mimeComment.clear();
if ( isLocal && !magicMimeComment.isNull() ) {
KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
if ( magicMimeType->comment() != magicMimeComment )
magicMimeComment.clear();
}
if ( isLocal && url.path() == QLatin1String("/") )
hasRoot = true;
if ( (*kit).isDir() && !(*kit).isLink() )
{
iDirCount++;
hasDirs = true;
}
else
{
iFileCount++;
totalSize += (*kit).size();
}
}
}
if (!isReallyLocal && !protocol.isEmpty())
{
directory += ' ';
directory += '(';
directory += protocol;
directory += ')';
}
if (!isTrash && (bDesktopFile || S_ISDIR(mode))
&& !d->bMultiple // not implemented for multiple
&& enableIconButton()) // #56857
{
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);
QString iconStr = KMimeType::findByUrl(url, mode)->iconName(url);
if (bDesktopFile && isLocal) {
KDesktopFile config( url.path() );
KConfigGroup group = config.desktopGroup();
iconStr = group.readEntry( "Icon" );
if ( config.hasDeviceType() )
iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Device );
else
iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Application );
} else {
iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Place );
}
iconButton->setIcon(iconStr);
d->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( KIconLoader::global()->loadIcon( iconStr, KIconLoader::Desktop, 48) );
d->iconArea = iconLabel;
}
grid->addWidget(d->iconArea, curRow, 0, Qt::AlignLeft);
if (d->bMultiple || isTrash || 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 );
d->nameArea = lab;
} else
{
d->m_lined = new KLineEdit( d->m_frame );
d->m_lined->setText(filename);
d->nameArea = d->m_lined;
d->m_lined->setFocus();
//if we don't have permissions to rename, we need to make "m_lined" read only.
KFileItemListProperties itemList(KFileItemList()<< item);
setFileNameReadOnly(!itemList.supportsMoving());
// Enhanced rename: Don't highlight the file extension.
QString extension = KMimeType::extractKnownExtension( filename );
if ( !extension.isEmpty() )
d->m_lined->setSelection( 0, filename.length() - extension.length() - 1 );
else
{
int lastDot = filename.lastIndexOf('.');
if (lastDot > 0)
d->m_lined->setSelection(0, lastDot);
}
connect( d->m_lined, SIGNAL( textChanged( const QString & ) ),
this, SLOT( nameFileChanged(const QString & ) ) );
}
grid->addWidget(d->nameArea, curRow++, 2);
KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame);
grid->addWidget(sep, curRow, 0, 1, 3);
++curRow;
QLabel *l;
if (!mimeComment.isEmpty() && !isTrash) {
l = new QLabel(i18n("Type:"), d->m_frame );
grid->addWidget(l, curRow, 0, Qt::AlignRight);
KHBox *box = new KHBox(d->m_frame);
box->setSpacing(20); // ### why 20?
l = new QLabel(mimeComment, box );
QPushButton *button = new QPushButton(box);
button->setIcon( KIcon(QString::fromLatin1("configure")) );
const int pixmapSize = button->style()->pixelMetric(QStyle::PM_SmallIconSize);
button->setFixedSize( pixmapSize+8, pixmapSize+8 );
if ( d->mimeType == KMimeType::defaultMimeType() )
button->setToolTip(i18n("Create new file type"));
else
button->setToolTip(i18n("Edit file type"));
connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() ));
if (!KAuthorized::authorizeKAction("editfiletype"))
button->hide();
grid->addWidget(box, curRow++, 2);
}
if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
{
l = new QLabel(i18n("Contents:"), d->m_frame );
grid->addWidget(l, curRow, 0, Qt::AlignRight);
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, Qt::AlignRight);
l = new KSqueezedTextLabel( directory, d->m_frame );
// force the layout direction to be always LTR
l->setLayoutDirection(Qt::LeftToRight);
// but if we are in RTL mode, align the text to the right
// otherwise the text is on the wrong side of the dialog
if (properties->layoutDirection() == Qt::RightToLeft)
l->setAlignment( Qt::AlignRight );
l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
grid->addWidget(l, curRow++, 2);
}
l = new QLabel(i18n("Size:"), d->m_frame );
grid->addWidget(l, curRow, 0, Qt::AlignRight);
d->m_sizeLabel = new QLabel( d->m_frame );
grid->addWidget( d->m_sizeLabel, curRow++, 2 );
if ( !hasDirs ) // Only files [and symlinks]
{
d->m_sizeLabel->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize))
.arg(KGlobal::locale()->formatNumber(totalSize, 0)));
d->m_sizeDetermineButton = 0L;
d->m_sizeStopButton = 0L;
}
else // Directory
{
QHBoxLayout * sizelay = new QHBoxLayout();
grid->addLayout( sizelay, curRow++, 2 );
// buttons
d->m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
d->m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
connect( d->m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) );
connect( d->m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) );
sizelay->addWidget(d->m_sizeDetermineButton, 0);
sizelay->addWidget(d->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 )
{
d->m_sizeDetermineButton->setText( i18n("Refresh") );
slotSizeDetermine();
}
else
d->m_sizeStopButton->setEnabled( false );
}
if (!d->bMultiple && item.isLink()) {
l = new QLabel(i18n("Points to:"), d->m_frame );
grid->addWidget(l, curRow, 0, Qt::AlignRight);
d->m_linkTargetLineEdit = new KLineEdit(item.linkDest(), d->m_frame );
grid->addWidget(d->m_linkTargetLineEdit, curRow++, 2);
connect(d->m_linkTargetLineEdit, SIGNAL(textChanged(QString)), this, SLOT(setDirty()));
}
if (!d->bMultiple) // Dates for multiple don't make much sense...
{
KDateTime dt = item.time(KFileItem::CreationTime);
if ( !dt.isNull() )
{
l = new QLabel(i18n("Created:"), d->m_frame );
grid->addWidget(l, curRow, 0, Qt::AlignRight);
l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
grid->addWidget(l, curRow++, 2);
}
dt = item.time(KFileItem::ModificationTime);
if ( !dt.isNull() )
{
l = new QLabel(i18n("Modified:"), d->m_frame );
grid->addWidget(l, curRow, 0, Qt::AlignRight);
l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
grid->addWidget(l, curRow++, 2);
}
dt = item.time(KFileItem::AccessTime);
if ( !dt.isNull() )
{
l = new QLabel(i18n("Accessed:"), d->m_frame );
grid->addWidget(l, curRow, 0, Qt::AlignRight);
l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
grid->addWidget(l, curRow++, 2);
}
}
if ( isLocal && hasDirs ) // only for directories
{
KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() );
if (mp) {
KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() );
if(info.size() != 0 )
{
sep = new KSeparator( Qt::Horizontal, d->m_frame);
grid->addWidget(sep, curRow, 0, 1, 3);
++curRow;
if (mp->mountPoint() != "/")
{
l = new QLabel(i18n("Mounted on:"), d->m_frame );
grid->addWidget(l, curRow, 0, Qt::AlignRight);
l = new KSqueezedTextLabel( mp->mountPoint(), d->m_frame );
l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
grid->addWidget( l, curRow++, 2 );
}
l = new QLabel(i18n("Device usage:"), d->m_frame );
grid->addWidget(l, curRow, 0, Qt::AlignRight);
d->m_capacityBar = new KCapacityBar( KCapacityBar::DrawTextOutline, d->m_frame );
grid->addWidget( d->m_capacityBar, curRow++, 2);
slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
}
}
}
vbl->addStretch(1);
}
bool KFilePropsPlugin::enableIconButton() const
{
bool iconEnabled = false;
const KFileItem item = properties->item();
// If the current item is a directory, check if it's writable,
// so we can create/update a .directory
// Current item is a file, same thing: check if it is writable
if (item.isWritable()) {
iconEnabled = true;
}
return iconEnabled;
}
// QString KFilePropsPlugin::tabName () const
// {
// return i18n ("&General");
// }
void KFilePropsPlugin::setFileNameReadOnly( bool ro )
{
if ( d->m_lined && !d->m_bFromTemplate )
{
d->m_lined->setReadOnly( ro );
if (ro)
{
// Don't put the initial focus on the line edit when it is ro
properties->setButtonFocus(KDialog::Ok);
}
}
}
void KFilePropsPlugin::slotEditFileType()
{
QString mime;
if (d->mimeType == KMimeType::defaultMimeType()) {
const int pos = d->oldFileName.lastIndexOf('.');
if (pos != -1)
mime = '*' + d->oldFileName.mid(pos);
else
mime = '*';
} else {
mime = d->mimeType;
}
QString keditfiletype = QString::fromLatin1("keditfiletype");
KRun::runCommand( keditfiletype
#ifdef Q_WS_X11
+ " --parent " + QString::number( (ulong)properties->window()->winId())
#endif
+ ' ' + KShell::quoteArg(mime),
keditfiletype, keditfiletype /*unused*/, properties->window());
}
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
d->m_sRelativePath = KGlobal::dirs()->relativeLocation("apps", path);
if (d->m_sRelativePath.startsWith('/'))
{
d->m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
if (d->m_sRelativePath.startsWith('/'))
d->m_sRelativePath.clear();
else
d->m_sRelativePath = path;
}
}
void KFilePropsPlugin::slotFoundMountPoint( const QString&,
quint64 kibSize,
quint64 /*kibUsed*/,
quint64 kibAvail )
{
d->m_capacityBar->setText(
i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
KIO::convertSizeFromKiB(kibAvail),
KIO::convertSizeFromKiB(kibSize),
100 - (int)(100.0 * kibAvail / kibSize) ));
d->m_capacityBar->setValue(100 - (int)(100.0 * kibAvail / kibSize));
}
void KFilePropsPlugin::slotDirSizeUpdate()
{
KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
d->m_sizeLabel->setText(
i18n("Calculating... %1 (%2)\n%3, %4",
KIO::convertSize(totalSize),
totalSize,
i18np("1 file", "%1 files", totalFiles),
i18np("1 sub-folder", "%1 sub-folders", totalSubdirs)));
}
void KFilePropsPlugin::slotDirSizeFinished( KJob * job )
{
if (job->error())
d->m_sizeLabel->setText( job->errorString() );
else
{
KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
d->m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4")
.arg(KIO::convertSize(totalSize))
.arg(KGlobal::locale()->formatNumber(totalSize, 0))
.arg(i18np("1 file","%1 files",totalFiles))
.arg(i18np("1 sub-folder","%1 sub-folders",totalSubdirs)));
}
d->m_sizeStopButton->setEnabled(false);
// just in case you change something and try again :)
d->m_sizeDetermineButton->setText( i18n("Refresh") );
d->m_sizeDetermineButton->setEnabled(true);
d->dirSizeJob = 0;
delete d->dirSizeUpdateTimer;
d->dirSizeUpdateTimer = 0;
}
void KFilePropsPlugin::slotSizeDetermine()
{
d->m_sizeLabel->setText( i18n("Calculating...") );
kDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" << properties->item();
kDebug(250) << " URL=" << properties->item().url().url();
d->dirSizeJob = KIO::directorySize( properties->items() );
d->dirSizeUpdateTimer = new QTimer(this);
connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ),
SLOT( slotDirSizeUpdate() ) );
d->dirSizeUpdateTimer->start(500);
connect( d->dirSizeJob, SIGNAL( result( KJob * ) ),
SLOT( slotDirSizeFinished( KJob * ) ) );
d->m_sizeStopButton->setEnabled(true);
d->m_sizeDetermineButton->setEnabled(false);
// also update the "Free disk space" display
if ( d->m_capacityBar )
{
bool isLocal;
const KFileItem item = properties->item();
KUrl url = item.mostLocalUrl( isLocal );
KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() );
if (mp) {
KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() );
slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
}
}
}
void KFilePropsPlugin::slotSizeStop()
{
if ( d->dirSizeJob )
{
KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
d->m_sizeLabel->setText(i18n("At least %1",
KIO::convertSize(totalSize)));
d->dirSizeJob->kill();
d->dirSizeJob = 0;
}
if ( d->dirSizeUpdateTimer )
d->dirSizeUpdateTimer->stop();
d->m_sizeStopButton->setEnabled(false);
d->m_sizeDetermineButton->setEnabled(true);
}
KFilePropsPlugin::~KFilePropsPlugin()
{
delete d;
}
bool KFilePropsPlugin::supports( const KFileItemList& /*_items*/ )
{
return true;
}
void KFilePropsPlugin::applyChanges()
{
if ( d->dirSizeJob )
slotSizeStop();
kDebug(250) << "KFilePropsPlugin::applyChanges";
if (qobject_cast<QLineEdit*>(d->nameArea))
{
QString n = ((QLineEdit *) d->nameArea)->text();
// Remove trailing spaces (#4345)
while ( ! n.isEmpty() && 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 ?
kDebug(250) << "oldname = " << d->oldName;
kDebug(250) << "newname = " << n;
if ( d->oldName != n || d->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(QLatin1String(".desktop")) &&
!newFileName.endsWith(QLatin1String(".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 ( !d->m_sRelativePath.isEmpty() )
determineRelativePath( properties->kurl().toLocalFile() );
kDebug(250) << "New URL = " << properties->kurl().url();
kDebug(250) << "old = " << oldurl.url();
// Don't remove the template !!
if ( !d->m_bFromTemplate ) // (normal renaming)
- job = KIO::move( oldurl, properties->kurl() );
+ job = KIO::moveAs( oldurl, properties->kurl() );
else // Copying a template
- job = KIO::copy( oldurl, properties->kurl() );
+ job = KIO::copyAs( oldurl, properties->kurl() );
connect( job, SIGNAL( result( KJob * ) ),
SLOT( slotCopyFinished( KJob * ) ) );
connect( job, SIGNAL( renamed( KIO::Job *, const KUrl &, const KUrl & ) ),
SLOT( slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & ) ) );
// wait for job
QEventLoop eventLoop;
connect(this, SIGNAL(leaveModality()),
&eventLoop, SLOT(quit()));
eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
return;
}
properties->updateUrl(properties->kurl());
// Update also relative path (for apps and mimetypes)
if ( !d->m_sRelativePath.isEmpty() )
determineRelativePath( properties->kurl().toLocalFile() );
}
// No job, keep going
slotCopyFinished( 0L );
}
void KFilePropsPlugin::slotCopyFinished( KJob * job )
{
kDebug(250) << "KFilePropsPlugin::slotCopyFinished";
if (job)
{
// allow apply() to return
emit leaveModality();
if ( job->error() )
{
job->uiDelegate()->showErrorMessage();
// 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().isNull() );
- assert( !properties->item().url().isEmpty() );
+ Q_ASSERT( !properties->item().isNull() );
+ Q_ASSERT( !properties->item().url().isEmpty() );
// Save the file where we can -> usually in ~/.kde/...
if (d->bDesktopFile && !d->m_sRelativePath.isEmpty())
{
kDebug(250) << "KFilePropsPlugin::slotCopyFinished " << d->m_sRelativePath;
KUrl newURL;
newURL.setPath( KDesktopFile::locateLocal(d->m_sRelativePath) );
kDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path();
properties->updateUrl( newURL );
}
if ( d->bKDesktopMode && d->bDesktopFile ) {
// Renamed? Update Name field
// Note: The desktop ioslave does this as well, but not when
// the file is copied from a template.
if ( d->m_bFromTemplate ) {
KIO::UDSEntry entry;
KIO::NetAccess::stat( properties->kurl(), entry, 0 );
KFileItem item( entry, properties->kurl() );
KDesktopFile config( item.localPath() );
KConfigGroup cg = config.desktopGroup();
QString nameStr = nameFromFileName(properties->kurl().fileName());
cg.writeEntry( "Name", nameStr );
cg.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized);
}
}
if (d->m_linkTargetLineEdit && !d->bMultiple) {
const KFileItem item = properties->item();
const QString newTarget = d->m_linkTargetLineEdit->text();
if (newTarget != item.linkDest()) {
kDebug(250) << "Updating target of symlink to" << newTarget;
KIO::Job* job = KIO::symlink(newTarget, item.url(), KIO::Overwrite);
job->ui()->setAutoErrorHandlingEnabled(true);
job->exec();
}
}
// "Link to Application" templates need to be made executable
// Instead of matching against a filename we check if the destination
// is an Application now.
if ( d->m_bFromTemplate ) {
// destination is not necessarily local, use the src template
KDesktopFile templateResult ( static_cast<KIO::CopyJob*>(job)->srcUrls().first().toLocalFile() );
if ( templateResult.hasApplicationType() ) {
// We can either stat the file and add the +x bit or use the larger chmod() job
// with a umask designed to only touch u+x. This is only one KIO job, so let's
// do that.
KFileItem appLink ( properties->item() );
KFileItemList fileItemList;
fileItemList << appLink;
// first 0100 adds u+x, second 0100 only allows chmod to change u+x
KIO::Job* chmodJob = KIO::chmod( fileItemList, 0100, 0100, QString(), QString(), KIO::HideProgressInfo );
chmodJob->exec();
}
}
}
void KFilePropsPlugin::applyIconChanges()
{
KIconButton *iconButton = qobject_cast<KIconButton*>(d->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.toLocalFile(KUrl::AddTrailingSlash) + 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.toLocalFile();
// Get the default image
QString str = KMimeType::findByUrl( url,
properties->item().mode(),
true )->iconName();
// Is it another one than the default ?
QString sIcon;
if ( str != iconButton->icon() )
sIcon = iconButton->icon();
// (otherwise write empty value)
kDebug(250) << "**" << path << "**";
// If default icon and no .directory file -> don't create one
if ( !sIcon.isEmpty() || QFile::exists(path) )
{
KDesktopFile cfg(path);
kDebug(250) << "sIcon = " << (sIcon);
kDebug(250) << "str = " << (str);
cfg.desktopGroup().writeEntry( "Icon", sIcon );
cfg.sync();
cfg.reparseConfiguration();
if ( cfg.desktopGroup().readEntry("Icon") != sIcon ) {
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
"have sufficient access to write to <b>%1</b>.</qt>", path));
}
}
}
}
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();
const KFileItemList items = properties->items();
const KUrl::List lst = items.urlList();
org::kde::KDirNotify::emitFilesChanged( lst.toStringList() );
}
class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
{
public:
KFilePermissionsPropsPluginPrivate()
{
}
~KFilePermissionsPropsPluginPrivate()
{
}
QFrame *m_frame;
QCheckBox *cbRecursive;
QLabel *explanationLabel;
KComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo;
QCheckBox *extraCheckbox;
mode_t partialPermissions;
KFilePermissionsPropsPlugin::PermissionsMode pmode;
bool canChangePermissions;
bool isIrregular;
bool hasExtendedACL;
KACL extendedACL;
KACL defaultACL;
bool fileSystemSupportsACLs;
KComboBox *grpCombo;
KLineEdit *usrEdit;
KLineEdit *grpEdit;
// Old permissions
mode_t permissions;
// Old group
QString strGroup;
// Old owner
QString strOwner;
};
#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 )
: KPropertiesDialogPlugin( _props ),d(new KFilePermissionsPropsPluginPrivate)
{
d->cbRecursive = 0L;
d->grpCombo = 0L; d->grpEdit = 0;
d->usrEdit = 0L;
QString path = properties->kurl().path(KUrl::RemoveTrailingSlash);
QString fname = properties->kurl().fileName();
bool isLocal = properties->kurl().isLocalFile();
bool isTrash = ( properties->kurl().protocol().toLower() == "trash" );
bool IamRoot = (geteuid() == 0);
const KFileItem item = properties->item();
bool isLink = item.isLink();
bool isDir = item.isDir(); // all dirs
bool hasDir = item.isDir(); // at least one dir
d->permissions = item.permissions(); // common permissions to all files
d->partialPermissions = d->permissions; // permissions that only some files have (at first we take everything)
d->isIrregular = isIrregular(d->permissions, isDir, isLink);
d->strOwner = item.user();
d->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
const KFileItemList items = properties->items();
KFileItemList::const_iterator it = items.begin();
const KFileItemList::const_iterator kend = items.end();
for ( ++it /*no need to check the first one again*/ ; it != kend; ++it )
{
const KUrl url = (*it).url();
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() != d->permissions )
{
d->permissions &= (*it).permissions();
d->partialPermissions |= (*it).permissions();
}
if ( (*it).user() != d->strOwner )
d->strOwner.clear();
if ( (*it).group() != d->strGroup )
d->strGroup.clear();
}
}
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 & ~d->permissions;
bool isMyFile = false;
if (isLocal && !d->strOwner.isEmpty()) { // local files, and all owned by the same person
struct passwd *myself = getpwuid( geteuid() );
if ( myself != 0L )
{
isMyFile = (d->strOwner == QString::fromLocal8Bit(myself->pw_name));
} else
kWarning() << "I don't exist ?! geteuid=" << geteuid();
} 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 = new QFrame();
properties->addPage( d->m_frame, i18n("&Permissions") );
QBoxLayout *box = new QVBoxLayout( d->m_frame );
box->setMargin( 0 );
QWidget *l;
QLabel *lbl;
QGroupBox *gb;
QGridLayout *gl;
QPushButton* pbAdvancedPerm = 0;
/* Group: Access Permissions */
gb = new QGroupBox ( i18n("Access Permissions"), d->m_frame );
box->addWidget (gb);
gl = new QGridLayout (gb);
gl->setColumnStretch(1, 1);
l = d->explanationLabel = new QLabel( "", gb );
if (isLink)
d->explanationLabel->setText(i18np("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->addWidget(l, 0, 0, 1, 2);
lbl = new QLabel( i18n("O&wner:"), gb);
gl->addWidget(lbl, 1, 0, Qt::AlignRight);
l = d->ownerPermCombo = new KComboBox(gb);
lbl->setBuddy(l);
gl->addWidget(l, 1, 1);
connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
l->setWhatsThis(i18n("Specifies the actions that the owner is allowed to do."));
lbl = new QLabel( i18n("Gro&up:"), gb);
gl->addWidget(lbl, 2, 0, Qt::AlignRight);
l = d->groupPermCombo = new KComboBox(gb);
lbl->setBuddy(l);
gl->addWidget(l, 2, 1);
connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
l->setWhatsThis(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, Qt::AlignRight);
l = d->othersPermCombo = new KComboBox(gb);
lbl->setBuddy(l);
gl->addWidget(l, 3, 1);
connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
l->setWhatsThis(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);
l->setWhatsThis(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->addItem(spacer, 5, 0, 1, 3);
pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb);
gl->addWidget(pbAdvancedPerm, 6, 0, 1, 2, Qt::AlignRight);
connect(pbAdvancedPerm, SIGNAL( clicked() ), this, SLOT( slotShowAdvancedPermissions() ));
}
else
d->extraCheckbox = 0;
/**** Group: Ownership ****/
gb = new QGroupBox ( i18n("Ownership"), d->m_frame );
box->addWidget (gb);
gl = new QGridLayout (gb);
gl->addItem(new QSpacerItem(0, 10), 0, 0);
/*** Set Owner ***/
l = new QLabel( i18n("User:"), gb );
gl->addWidget (l, 1, 0, Qt::AlignRight);
/* 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;
/* File owner: For root, offer a KLineEdit with autocompletion.
* For a user, who can never chown() a file, offer a QLabel.
*/
if (IamRoot && isLocal)
{
d->usrEdit = new KLineEdit( gb );
KCompletion *kcom = d->usrEdit->completionObject();
kcom->setOrder(KCompletion::Sorted);
setpwent();
for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); ++i)
kcom->addItem(QString::fromLatin1(user->pw_name));
endpwent();
d->usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto :
KGlobalSettings::CompletionNone);
d->usrEdit->setText(d->strOwner);
gl->addWidget(d->usrEdit, 1, 1);
connect( d->usrEdit, SIGNAL( textChanged( const QString & ) ),
this, SIGNAL( changed() ) );
}
else
{
l = new QLabel(d->strOwner, gb);
gl->addWidget(l, 1, 1);
}
/*** Set Group ***/
QStringList groupList;
QByteArray strUser;
user = getpwuid(geteuid());
if (user != 0L)
strUser = user->pw_name;
#ifdef HAVE_GETGROUPLIST
// pick the groups to which the user belongs
int groupCount = 0;
#ifdef Q_OS_MAC
QVarLengthArray<int> groups;
#else
QVarLengthArray<gid_t> groups;
#endif
if (getgrouplist(strUser, user->pw_gid, NULL, &groupCount) < 0) {
groups.resize(groupCount);
if (groups.data())
getgrouplist(strUser, user->pw_gid, groups.data(), &groupCount);
else
groupCount = 0;
}
for (i = 0; i < groupCount; i++) {
struct group *mygroup = getgrgid(groups[i]);
if (mygroup)
groupList += QString::fromLocal8Bit(mygroup->gr_name);
}
#endif // HAVE_GETGROUPLIST
bool isMyGroup = groupList.contains(d->strGroup);
/* add the group the file currently belongs to ..
* .. if it is not there already
*/
if (!isMyGroup)
groupList += d->strGroup;
l = new QLabel( i18n("Group:"), gb );
gl->addWidget (l, 2, 0, Qt::AlignRight);
/* Set group: if possible to change:
* - Offer a KLineEdit for root, since he can change to any group.
* - Offer a KComboBox 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)
{
d->grpEdit = new KLineEdit(gb);
KCompletion *kcom = new KCompletion;
kcom->setItems(groupList);
d->grpEdit->setCompletionObject(kcom, true);
d->grpEdit->setAutoDeleteCompletionObject( true );
d->grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
d->grpEdit->setText(d->strGroup);
gl->addWidget(d->grpEdit, 2, 1);
connect( d->grpEdit, SIGNAL( textChanged( const QString & ) ),
this, SIGNAL( changed() ) );
}
else if ((groupList.count() > 1) && isMyFile && isLocal)
{
d->grpCombo = new KComboBox(gb);
d->grpCombo->setObjectName(QLatin1String("combogrouplist"));
d->grpCombo->addItems(groupList);
d->grpCombo->setCurrentIndex(groupList.indexOf(d->strGroup));
gl->addWidget(d->grpCombo, 2, 1);
connect( d->grpCombo, SIGNAL( activated( int ) ),
this, SIGNAL( changed() ) );
}
else
{
l = new QLabel(d->strGroup, gb);
gl->addWidget(l, 2, 1);
}
gl->setColumnStretch(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 )
{
//don't allow to change properties for file into trash
enableAccessControls(false);
if ( pbAdvancedPerm)
pbAdvancedPerm->setEnabled(false);
}
box->addStretch (10);
}
#ifdef HAVE_POSIX_ACL
static bool fileSystemSupportsACL( const QByteArray& path )
{
bool fileSystemSupportsACLs = false;
#ifdef Q_OS_FREEBSD
struct statfs buf;
fileSystemSupportsACLs = ( statfs( path.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS );
#else
fileSystemSupportsACLs =
getxattr( path.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);
KDialog dlg( properties );
dlg.setModal( true );
dlg.setCaption( i18n("Advanced Permissions") );
dlg.setButtons( KDialog::Ok | KDialog::Cancel );
QLabel *l, *cl[3];
QGroupBox *gb;
QGridLayout *gl;
QWidget *mainw = new QWidget( &dlg );
QVBoxLayout *vbox = new QVBoxLayout(mainw);
// Group: Access Permissions
gb = new QGroupBox ( i18n("Access Permissions"), mainw );
vbox->addWidget(gb);
gl = new QGridLayout (gb);
gl->addItem(new QSpacerItem(0, 10), 0, 0);
QVector<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.");
l->setWhatsThis(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.");
l->setWhatsThis(writeWhatsThis);
QString execWhatsThis;
if (isDir) {
l = new QLabel( i18nc("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.");
}
l->setWhatsThis(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->addWidget(l, 1, 4, 1, 2);
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.");
l->setWhatsThis(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.");
l->setWhatsThis(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.");
l->setWhatsThis(setGidWhatsThis);
l = new QLabel(i18nc("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");
l->setWhatsThis(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 = d->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->setCheckState(Qt::PartiallyChecked);
}
else if (d->cbRecursive && d->cbRecursive->isChecked())
cb->setTristate();
cb->setEnabled( d->canChangePermissions );
gl->addWidget (cb, row+2, col+1);
switch(col) {
case 0:
cb->setWhatsThis(readWhatsThis);
break;
case 1:
cb->setWhatsThis(writeWhatsThis);
break;
case 2:
cb->setWhatsThis(execWhatsThis);
break;
case 3:
switch(row) {
case 0:
cb->setWhatsThis(setUidWhatsThis);
break;
case 1:
cb->setWhatsThis(setGidWhatsThis);
break;
case 2:
cb->setWhatsThis(stickyWhatsThis);
break;
}
break;
}
}
}
gl->setColumnStretch(6, 10);
#ifdef HAVE_POSIX_ACL
KACLEditWidget *extendedACLs = 0;
// FIXME make it work with partial entries
if ( properties->items().count() == 1 ) {
QByteArray path = QFile::encodeName( properties->item().url().toLocalFile() );
d->fileSystemSupportsACLs = fileSystemSupportsACL( path );
}
if ( d->fileSystemSupportsACLs ) {
std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) );
extendedACLs = new KACLEditWidget( mainw );
vbox->addWidget(extendedACLs);
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 );
}
#endif
dlg.setMainWidget( mainw );
if (dlg.exec() != KDialog::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]->checkState())
{
case Qt::Checked:
orPermissions |= fperm[row][col];
//fall through
case Qt::Unchecked:
andPermissions &= ~fperm[row][col];
break;
default: // NoChange
break;
}
}
d->isIrregular = false;
const KFileItemList items = properties->items();
KFileItemList::const_iterator it = items.begin();
const KFileItemList::const_iterator kend = items.end();
for ( ; it != kend; ++it ) {
if (isIrregular(((*it).permissions() & andPermissions) | orPermissions,
(*it).isDir(), (*it).isLink())) {
d->isIrregular = true;
break;
}
}
d->permissions = orPermissions;
d->partialPermissions = andPermissions;
#ifdef HAVE_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();
d->permissions = d->extendedACL.basePermissions();
d->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( const KFileItemList& /*_items*/ )
{
return true;
}
// 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->isIrregular) //#176876
return;
if (d->pmode == PermissionsOnlyLinks) {
combo->addItem(i18n("Link"));
combo->setCurrentIndex(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->addItem(i18n(permissionsTexts[(int)d->pmode][i]));
if (partial & tMask & ~UniExec) {
combo->addItem(i18n("Varying (No Change)"));
combo->setCurrentIndex(3);
}
else {
combo->setCurrentIndex(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,
d->permissions, d->partialPermissions);
setComboContent(d->groupPermCombo, PermissionsGroup,
d->permissions, d->partialPermissions);
setComboContent(d->othersPermCombo, PermissionsOthers,
d->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 ?
i18np("This file uses advanced permissions",
"These files use advanced permissions.",
properties->items().count()) : "");
if (d->partialPermissions & UniExec) {
d->extraCheckbox->setTristate();
d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
}
else {
d->extraCheckbox->setTristate(false);
d->extraCheckbox->setChecked(d->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 ?
i18np("This folder uses advanced permissions.",
"These folders use advanced permissions.",
properties->items().count()) : "");
if (d->partialPermissions & S_ISVTX) {
d->extraCheckbox->setTristate();
d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
}
else {
d->extraCheckbox->setTristate(false);
d->extraCheckbox->setChecked(d->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->setCheckState(Qt::PartiallyChecked);
}
else {
d->extraCheckbox->setTristate(false);
d->extraCheckbox->setChecked(d->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->currentIndex()];
if (m != (mode_t) -1) {
orFilePermissions |= m & UniOwner;
if ((m & UniOwner) &&
((d->pmode == PermissionsMixed) ||
((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
andFilePermissions &= ~(S_IRUSR | S_IWUSR);
else {
andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
if ((m & S_IRUSR) && (d->extraCheckbox->checkState() == Qt::Checked))
orFilePermissions |= S_IXUSR;
}
orDirPermissions |= m & UniOwner;
if (m & S_IRUSR)
orDirPermissions |= S_IXUSR;
andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
}
m = standardPermissions[d->groupPermCombo->currentIndex()];
if (m != (mode_t) -1) {
orFilePermissions |= m & UniGroup;
if ((m & UniGroup) &&
((d->pmode == PermissionsMixed) ||
((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
andFilePermissions &= ~(S_IRGRP | S_IWGRP);
else {
andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
if ((m & S_IRGRP) && (d->extraCheckbox->checkState() == Qt::Checked))
orFilePermissions |= S_IXGRP;
}
orDirPermissions |= m & UniGroup;
if (m & S_IRGRP)
orDirPermissions |= S_IXGRP;
andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
}
m = d->othersPermCombo->currentIndex() >= 0 ? standardPermissions[d->othersPermCombo->currentIndex()] : (mode_t)-1;
if (m != (mode_t) -1) {
orFilePermissions |= m & UniOthers;
if ((m & UniOthers) &&
((d->pmode == PermissionsMixed) ||
((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
andFilePermissions &= ~(S_IROTH | S_IWOTH);
else {
andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
if ((m & S_IROTH) && (d->extraCheckbox->checkState() == Qt::Checked))
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->checkState() != Qt::PartiallyChecked)) {
andDirPermissions &= ~S_ISVTX;
if (d->extraCheckbox->checkState() == Qt::Checked)
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 = d->permissions;
andFilePermissions = d->partialPermissions;
orDirPermissions = d->permissions;
andDirPermissions = d->partialPermissions;
}
QString owner, group;
if (d->usrEdit)
owner = d->usrEdit->text();
if (d->grpEdit)
group = d->grpEdit->text();
else if (d->grpCombo)
group = d->grpCombo->currentText();
if (owner == d->strOwner)
owner.clear(); // no change
if (group == d->strGroup)
group.clear();
bool recursive = d->cbRecursive && d->cbRecursive->isChecked();
bool permissionChange = false;
KFileItemList files, dirs;
const KFileItemList items = properties->items();
KFileItemList::const_iterator it = items.begin();
const KFileItemList::const_iterator kend = items.end();
for ( ; it != kend; ++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( KJob * ) ),
SLOT( slotChmodResult( KJob * ) ) );
QEventLoop eventLoop;
connect(this, SIGNAL(leaveModality()),
&eventLoop, SLOT(quit()));
eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
}
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( KJob * ) ),
SLOT( slotChmodResult( KJob * ) ) );
QEventLoop eventLoop;
connect(this, SIGNAL(leaveModality()),
&eventLoop, SLOT(quit()));
eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
}
}
void KFilePermissionsPropsPlugin::slotChmodResult( KJob * job )
{
kDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult";
if (job->error())
job->uiDelegate()->showErrorMessage();
// allow apply() to return
emit leaveModality();
}
class KUrlPropsPlugin::KUrlPropsPluginPrivate
{
public:
KUrlPropsPluginPrivate()
{
}
~KUrlPropsPluginPrivate()
{
}
QFrame *m_frame;
KUrlRequester *URLEdit;
QString URLStr;
};
KUrlPropsPlugin::KUrlPropsPlugin( KPropertiesDialog *_props )
: KPropertiesDialogPlugin( _props ),d(new KUrlPropsPluginPrivate)
{
d->m_frame = new QFrame();
properties->addPage(d->m_frame, i18n("U&RL"));
QVBoxLayout *layout = new QVBoxLayout(d->m_frame);
layout->setMargin(0);
QLabel *l;
l = new QLabel( d->m_frame );
l->setObjectName( QLatin1String( "Label_1" ) );
l->setText( i18n("URL:") );
layout->addWidget(l, Qt::AlignRight);
d->URLEdit = new KUrlRequester( d->m_frame );
layout->addWidget(d->URLEdit);
KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
if (url.isLocalFile()) {
QString path = url.toLocalFile();
QFile f( path );
if ( !f.open( QIODevice::ReadOnly ) ) {
return;
}
f.close();
KDesktopFile config( path );
const KConfigGroup dg = config.desktopGroup();
d->URLStr = dg.readPathEntry( "URL", QString() );
if (!d->URLStr.isEmpty()) {
d->URLEdit->setUrl( KUrl(d->URLStr) );
}
}
connect( d->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( const KFileItemList& _items )
{
if ( _items.count() != 1 )
return false;
const KFileItem item = _items.first();
// check if desktop file
if (!item.isDesktopFile())
return false;
// open file and check type
bool isLocal;
KUrl url = item.mostLocalUrl(isLocal);
if (!isLocal) {
return false;
}
KDesktopFile config( url.path() );
return config.hasLinkType();
}
void KUrlPropsPlugin::applyChanges()
{
KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
if (!url.isLocalFile()) {
//FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
return;
}
QString path = url.path();
QFile f( path );
if ( !f.open( QIODevice::ReadWrite ) ) {
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
"sufficient access to write to <b>%1</b>.</qt>", path));
return;
}
f.close();
KDesktopFile config( path );
KConfigGroup dg = config.desktopGroup();
dg.writeEntry( "Type", QString::fromLatin1("Link"));
dg.writePathEntry( "URL", d->URLEdit->url().url() );
// Users can't create a Link .desktop file with a Name field,
// but distributions can. Update the Name field in that case.
if ( dg.hasKey("Name") )
{
QString nameStr = nameFromFileName(properties->kurl().fileName());
dg.writeEntry( "Name", nameStr );
dg.writeEntry( "Name", nameStr, KConfigBase::Persistent|KConfigBase::Localized );
}
}
/* ----------------------------------------------------
*
* KDevicePropsPlugin
*
* -------------------------------------------------- */
class KDevicePropsPlugin::KDevicePropsPluginPrivate
{
public:
KDevicePropsPluginPrivate()
{
}
~KDevicePropsPluginPrivate()
{
}
bool isMounted() const {
const QString dev = device->currentText();
return !dev.isEmpty() && KMountPoint::currentMountPoints().findByDevice(dev);
}
QFrame *m_frame;
QStringList mountpointlist;
QLabel *m_freeSpaceText;
QLabel *m_freeSpaceLabel;
QProgressBar *m_freeSpaceBar;
KComboBox* device;
QLabel* mountpoint;
QCheckBox* readonly;
QStringList m_devicelist;
};
KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KDevicePropsPluginPrivate)
{
d->m_frame = new QFrame();
properties->addPage(d->m_frame, i18n("De&vice"));
QStringList devices;
const KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
for(KMountPoint::List::ConstIterator it = mountPoints.begin();
it != mountPoints.end(); ++it)
{
const KMountPoint::Ptr mp = (*it);
QString mountPoint = mp->mountPoint();
QString device = mp->mountedFrom();
kDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType();
if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty()
&& device != "none")
{
devices.append( device + QString::fromLatin1(" (")
+ mountPoint + QString::fromLatin1(")") );
d->m_devicelist.append(device);
d->mountpointlist.append(mountPoint);
}
}
QGridLayout *layout = new QGridLayout( d->m_frame );
layout->setMargin(0);
layout->setColumnStretch(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, Qt::AlignRight);
d->device = new KComboBox( d->m_frame );
d->device->setObjectName( QLatin1String( "ComboBox_device" ) );
d->device->setEditable( true );
d->device->addItems( devices );
layout->addWidget(d->device, 0, 1);
connect( d->device, SIGNAL( activated( int ) ),
this, SLOT( slotActivated( int ) ) );
d->readonly = new QCheckBox( d->m_frame );
d->readonly->setObjectName( QLatin1String( "CheckBox_readonly" ) );
d->readonly->setText( i18n("Read only") );
layout->addWidget(d->readonly, 1, 1);
label = new QLabel( d->m_frame );
label->setText( i18n("File system:") );
layout->addWidget(label, 2, 0, Qt::AlignRight);
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, Qt::AlignRight);
d->mountpoint = new QLabel( d->m_frame );
d->mountpoint->setObjectName( QLatin1String( "LineEdit_mountpoint" ) );
layout->addWidget(d->mountpoint, 3, 1);
// show disk free
d->m_freeSpaceText = new QLabel(i18n("Device usage:"), d->m_frame );
layout->addWidget(d->m_freeSpaceText, 4, 0, Qt::AlignRight);
d->m_freeSpaceLabel = new QLabel( d->m_frame );
layout->addWidget( d->m_freeSpaceLabel, 4, 1 );
d->m_freeSpaceBar = new QProgressBar( d->m_frame );
d->m_freeSpaceBar->setObjectName( "freeSpaceBar" );
layout->addWidget(d->m_freeSpaceBar, 5, 0, 1, 2);
// 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( Qt::Horizontal, d->m_frame);
layout->addWidget(sep, 6, 0, 1, 2);
layout->setRowStretch(7, 1);
KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
if (!url.isLocalFile()) {
return;
}
QString path = url.toLocalFile();
QFile f( path );
if ( !f.open( QIODevice::ReadOnly ) )
return;
f.close();
const KDesktopFile _config( path );
const KConfigGroup config = _config.desktopGroup();
QString deviceStr = config.readEntry( "Dev" );
QString mountPointStr = config.readEntry( "MountPoint" );
bool ro = config.readEntry( "ReadOnly", false );
fileSystem->setText(config.readEntry("FSType"));
d->device->setEditText( deviceStr );
if ( !deviceStr.isEmpty() ) {
// Set default options for this device (first matching entry)
int index = d->m_devicelist.indexOf(deviceStr);
if (index != -1)
{
//kDebug(250) << "found it" << index;
slotActivated( index );
}
}
if ( !mountPointStr.isEmpty() )
{
d->mountpoint->setText( mountPointStr );
updateInfo();
}
d->readonly->setChecked( ro );
connect( d->device, SIGNAL( activated( int ) ),
this, SIGNAL( changed() ) );
connect( d->device, SIGNAL( textChanged( const QString & ) ),
this, SIGNAL( changed() ) );
connect( d->readonly, SIGNAL( toggled( bool ) ),
this, SIGNAL( changed() ) );
connect( d->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 (!d->mountpoint->text().isEmpty() && d->isMounted()) {
KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( d->mountpoint->text() );
slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
}
}
void KDevicePropsPlugin::slotActivated( int index )
{
// index can be more than the number of known devices, when the user types
// a "custom" device.
if (index < d->m_devicelist.count()) {
// Update mountpoint so that it matches the device that was selected in the combo
d->device->setEditText(d->m_devicelist[index]);
d->mountpoint->setText(d->mountpointlist[index]);
}
updateInfo();
}
void KDevicePropsPlugin::slotDeviceChanged()
{
// Update mountpoint so that it matches the typed device
int index = d->m_devicelist.indexOf( d->device->currentText() );
if ( index != -1 )
d->mountpoint->setText( d->mountpointlist[index] );
else
d->mountpoint->setText( QString() );
updateInfo();
}
void KDevicePropsPlugin::slotFoundMountPoint( const QString&,
quint64 kibSize,
quint64 /*kibUsed*/,
quint64 kibAvail )
{
d->m_freeSpaceText->show();
d->m_freeSpaceLabel->show();
const int percUsed = kibSize != 0 ? (100 - (int)(100.0 * kibAvail / kibSize)) : 100;
d->m_freeSpaceLabel->setText(
i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
KIO::convertSizeFromKiB(kibAvail),
KIO::convertSizeFromKiB(kibSize),
percUsed ));
d->m_freeSpaceBar->setRange(0, 100);
d->m_freeSpaceBar->setValue(percUsed);
d->m_freeSpaceBar->show();
}
bool KDevicePropsPlugin::supports( const KFileItemList& _items )
{
if ( _items.count() != 1 )
return false;
const KFileItem item = _items.first();
// check if desktop file
if (!item.isDesktopFile())
return false;
// open file and check type
bool isLocal;
KUrl url = item.mostLocalUrl(isLocal);
if (!isLocal) {
return false;
}
KDesktopFile config( url.path() );
return config.hasDeviceType();
}
void KDevicePropsPlugin::applyChanges()
{
KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
if ( !url.isLocalFile() )
return;
QString path = url.toLocalFile();
QFile f( path );
if ( !f.open( QIODevice::ReadWrite ) )
{
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient "
"access to write to <b>%1</b>.</qt>", path));
return;
}
f.close();
KDesktopFile _config( path );
KConfigGroup config = _config.desktopGroup();
config.writeEntry( "Type", QString::fromLatin1("FSDevice") );
config.writeEntry( "Dev", d->device->currentText() );
config.writeEntry( "MountPoint", d->mountpoint->text() );
config.writeEntry( "ReadOnly", d->readonly->isChecked() );
config.sync();
}
/* ----------------------------------------------------
*
* KDesktopPropsPlugin
*
* -------------------------------------------------- */
class KDesktopPropsPlugin::KDesktopPropsPluginPrivate
{
public:
KDesktopPropsPluginPrivate()
: w( new Ui_KPropertiesDesktopBase )
, m_frame( new QFrame() )
{
}
~KDesktopPropsPluginPrivate()
{
delete w;
}
Ui_KPropertiesDesktopBase* w;
QWidget *m_frame;
QString m_origCommandStr;
QString m_terminalOptionStr;
QString m_suidUserStr;
QString m_dbusStartupType;
QString m_dbusServiceName;
bool m_terminalBool;
bool m_suidBool;
bool m_startupBool;
bool m_systrayBool;
};
KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props )
: KPropertiesDialogPlugin( _props ), d( new KDesktopPropsPluginPrivate )
{
d->w->setupUi(d->m_frame);
properties->addPage(d->m_frame, i18n("&Application"));
bool bKDesktopMode = properties->kurl().protocol() == QLatin1String("desktop") ||
properties->currentDir().protocol() == QLatin1String("desktop");
if (bKDesktopMode)
{
// Hide Name entry
d->w->nameEdit->hide();
d->w->nameLabel->hide();
}
d->w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
d->w->pathEdit->lineEdit()->setAcceptDrops(false);
connect( d->w->nameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
connect( d->w->genNameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
connect( d->w->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
connect( d->w->commandEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
connect( d->w->pathEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
connect( d->w->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
connect( d->w->addFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotAddFiletype() ) );
connect( d->w->delFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotDelFiletype() ) );
connect( d->w->advancedButton, SIGNAL( clicked() ), this, SLOT( slotAdvanced() ) );
// now populate the page
KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
if (!url.isLocalFile()) {
return;
}
QString path = url.toLocalFile();
QFile f( path );
if ( !f.open( QIODevice::ReadOnly ) )
return;
f.close();
KDesktopFile _config( path );
KConfigGroup config = _config.desktopGroup();
QString nameStr = _config.readName();
QString genNameStr = _config.readGenericName();
QString commentStr = _config.readComment();
QString commandStr = config.readEntry( "Exec", QString() );
if (commandStr.startsWith(QLatin1String("ksystraycmd ")))
{
commandStr.remove(0, 12);
d->m_systrayBool = true;
}
else
d->m_systrayBool = false;
d->m_origCommandStr = commandStr;
QString pathStr = config.readEntry( "Path", QString() ); // not readPathEntry, see kservice.cpp
d->m_terminalBool = config.readEntry( "Terminal", false );
d->m_terminalOptionStr = config.readEntry( "TerminalOptions" );
d->m_suidBool = config.readEntry( "X-KDE-SubstituteUID", false );
d->m_suidUserStr = config.readEntry( "X-KDE-Username" );
if( config.hasKey( "StartupNotify" ))
d->m_startupBool = config.readEntry( "StartupNotify", true );
else
d->m_startupBool = config.readEntry( "X-KDE-StartupNotify", true );
d->m_dbusStartupType = config.readEntry("X-DBUS-StartupType").toLower();
// ### should there be a GUI for this setting?
// At least we're copying it over to the local file, to avoid side effects (#157853)
d->m_dbusServiceName = config.readEntry("X-DBUS-ServiceName");
const QStringList mimeTypes = config.readXdgListEntry( "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 )
d->w->nameEdit->setText(nameStr);
d->w->genNameEdit->setText( genNameStr );
d->w->commentEdit->setText( commentStr );
d->w->commandEdit->setText( commandStr );
d->w->pathEdit->lineEdit()->setText( pathStr );
// was: d->w->filetypeList->setFullWidth(true);
// d->w->filetypeList->header()->setStretchEnabled(true, d->w->filetypeList->columns()-1);
KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
for(QStringList::ConstIterator it = mimeTypes.begin();
it != mimeTypes.end(); )
{
KMimeType::Ptr p = KMimeType::mimeType(*it, KMimeType::ResolveAliases);
++it;
QString preference;
if (it != mimeTypes.end())
{
bool numeric;
(*it).toInt(&numeric);
if (numeric)
{
preference = *it;
++it;
}
}
if (p)
{
QTreeWidgetItem *item = new QTreeWidgetItem();
item->setText(0, p->name());
item->setText(1, p->comment());
item->setText(2, preference);
d->w->filetypeList->addTopLevelItem(item);
}
}
d->w->filetypeList->resizeColumnToContents(0);
}
KDesktopPropsPlugin::~KDesktopPropsPlugin()
{
delete d;
}
void KDesktopPropsPlugin::slotAddFiletype()
{
KMimeTypeChooserDialog dlg( i18n("Add File Type for %1", properties->kurl().fileName()),
i18n("Select one or more file types to add:"),
QStringList(), // no preselected mimetypes
QString(),
QStringList(),
KMimeTypeChooser::Comments|KMimeTypeChooser::Patterns,
d->m_frame );
if (dlg.exec() == KDialog::Accepted)
{
foreach(const QString &mimetype, dlg.chooser()->mimeTypes())
{
KMimeType::Ptr p = KMimeType::mimeType(mimetype);
if (!p)
continue;
bool found = false;
int count = d->w->filetypeList->topLevelItemCount();
for (int i = 0; !found && i < count; ++i) {
if (d->w->filetypeList->topLevelItem(i)->text(0) == mimetype) {
found = true;
}
}
if (!found) {
QTreeWidgetItem *item = new QTreeWidgetItem();
item->setText(0, p->name());
item->setText(1, p->comment());
d->w->filetypeList->addTopLevelItem(item);
}
d->w->filetypeList->resizeColumnToContents(0);
}
}
emit changed();
}
void KDesktopPropsPlugin::slotDelFiletype()
{
QTreeWidgetItem *cur = d->w->filetypeList->currentItem();
if (cur) {
delete cur;
emit changed();
}
}
void KDesktopPropsPlugin::checkCommandChanged()
{
if (KRun::binaryName(d->w->commandEdit->text(), true) !=
KRun::binaryName(d->m_origCommandStr, true))
{
d->m_origCommandStr = d->w->commandEdit->text();
d->m_dbusStartupType.clear(); // Reset
d->m_dbusServiceName.clear();
}
}
void KDesktopPropsPlugin::applyChanges()
{
kDebug(250) << "KDesktopPropsPlugin::applyChanges";
KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
if (!url.isLocalFile()) {
//FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
return;
}
QString path = url.toLocalFile();
QFile f( path );
if ( !f.open( QIODevice::ReadWrite ) ) {
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
"sufficient access to write to <b>%1</b>.</qt>", path));
return;
}
f.close();
// If the command is changed we reset certain settings that are strongly
// coupled to the command.
checkCommandChanged();
KDesktopFile _config( path );
KConfigGroup config = _config.desktopGroup();
config.writeEntry( "Type", QString::fromLatin1("Application"));
config.writeEntry( "Comment", d->w->commentEdit->text() );
config.writeEntry( "Comment", d->w->commentEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
config.writeEntry( "GenericName", d->w->genNameEdit->text() );
config.writeEntry( "GenericName", d->w->genNameEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
if (d->m_systrayBool)
config.writeEntry( "Exec", d->w->commandEdit->text().prepend("ksystraycmd ") );
else
config.writeEntry( "Exec", d->w->commandEdit->text() );
config.writeEntry( "Path", d->w->pathEdit->lineEdit()->text() ); // not writePathEntry, see kservice.cpp
// Write mimeTypes
QStringList mimeTypes;
int count = d->w->filetypeList->topLevelItemCount();
for (int i = 0; i < count; ++i) {
QTreeWidgetItem *item = d->w->filetypeList->topLevelItem(i);
QString preference = item->text(2);
mimeTypes.append(item->text(0));
if (!preference.isEmpty())
mimeTypes.append(preference);
}
kDebug() << mimeTypes;
config.writeXdgListEntry( "MimeType", mimeTypes );
if ( !d->w->nameEdit->isHidden() ) {
QString nameStr = d->w->nameEdit->text();
config.writeEntry( "Name", nameStr );
config.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized );
}
config.writeEntry("Terminal", d->m_terminalBool);
config.writeEntry("TerminalOptions", d->m_terminalOptionStr);
config.writeEntry("X-KDE-SubstituteUID", d->m_suidBool);
config.writeEntry("X-KDE-Username", d->m_suidUserStr);
config.writeEntry("StartupNotify", d->m_startupBool);
config.writeEntry("X-DBUS-StartupType", d->m_dbusStartupType);
config.writeEntry("X-DBUS-ServiceName", d->m_dbusServiceName);
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)
KBuildSycocaProgressDialog::rebuildKSycoca(d->m_frame);
}
void KDesktopPropsPlugin::slotBrowseExec()
{
KUrl f = KFileDialog::getOpenUrl( KUrl(),
QString(), 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.toLocalFile();
path = KShell::quoteArg( path );
d->w->commandEdit->setText( path );
}
void KDesktopPropsPlugin::slotAdvanced()
{
KDialog dlg( d->m_frame );
dlg.setObjectName( "KPropertiesDesktopAdv" );
dlg.setModal( true );
dlg.setCaption( i18n("Advanced Options for %1", properties->kurl().fileName()) );
dlg.setButtons( KDialog::Ok | KDialog::Cancel );
dlg.setDefaultButton( KDialog::Ok );
Ui_KPropertiesDesktopAdvBase w;
w.setupUi(dlg.mainWidget());
// 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 = (d->m_terminalOptionStr.contains( "--noclose" ) > 0);
w.terminalCloseCheck->setChecked(terminalCloseBool);
d->m_terminalOptionStr.remove( "--noclose");
}
else
{
w.terminalCloseCheck->hide();
}
w.terminalCheck->setChecked(d->m_terminalBool);
w.terminalEdit->setText(d->m_terminalOptionStr);
w.terminalCloseCheck->setEnabled(d->m_terminalBool);
w.terminalEdit->setEnabled(d->m_terminalBool);
w.terminalEditLabel->setEnabled(d->m_terminalBool);
w.suidCheck->setChecked(d->m_suidBool);
w.suidEdit->setText(d->m_suidUserStr);
w.suidEdit->setEnabled(d->m_suidBool);
w.suidEditLabel->setEnabled(d->m_suidBool);
w.startupInfoCheck->setChecked(d->m_startupBool);
w.systrayCheck->setChecked(d->m_systrayBool);
if (d->m_dbusStartupType == "unique")
w.dbusCombo->setCurrentIndex(2);
else if (d->m_dbusStartupType == "multi")
w.dbusCombo->setCurrentIndex(1);
else if (d->m_dbusStartupType == "wait")
w.dbusCombo->setCurrentIndex(3);
else
w.dbusCombo->setCurrentIndex(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.dbusCombo, SIGNAL( activated( int ) ),
this, SIGNAL( changed() ) );
if ( dlg.exec() == QDialog::Accepted )
{
d->m_terminalOptionStr = w.terminalEdit->text().trimmed();
d->m_terminalBool = w.terminalCheck->isChecked();
d->m_suidBool = w.suidCheck->isChecked();
d->m_suidUserStr = w.suidEdit->text().trimmed();
d->m_startupBool = w.startupInfoCheck->isChecked();
d->m_systrayBool = w.systrayCheck->isChecked();
if (w.terminalCloseCheck->isChecked())
{
d->m_terminalOptionStr.append(" --noclose");
}
switch(w.dbusCombo->currentIndex())
{
case 1: d->m_dbusStartupType = "multi"; break;
case 2: d->m_dbusStartupType = "unique"; break;
case 3: d->m_dbusStartupType = "wait"; break;
default: d->m_dbusStartupType = "none"; break;
}
}
}
bool KDesktopPropsPlugin::supports( const KFileItemList& _items )
{
if ( _items.count() != 1 ) {
return false;
}
const KFileItem item = _items.first();
// check if desktop file
if (!item.isDesktopFile()) {
return false;
}
// open file and check type
bool isLocal;
KUrl url = item.mostLocalUrl( isLocal );
if (!isLocal) {
return false;
}
KDesktopFile config( url.path() );
return config.hasApplicationType() &&
KAuthorized::authorize("run_desktop_files") &&
KAuthorized::authorize("shell_access");
}
#include "kpropertiesdialog.moc"
#include "kpropertiesdialog_p.moc"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Nov 1, 7:59 AM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
10074630
Default Alt Text
(117 KB)
Attached To
Mode
rKL kdelibs
Attached
Detach File
Event Timeline
Log In to Comment