• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KIO

kpropertiesdialog.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002 
00003    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00004    Copyright (c) 1999, 2000 Preston Brown <pbrown@kde.org>
00005    Copyright (c) 2000 Simon Hausmann <hausmann@kde.org>
00006    Copyright (c) 2000 David Faure <faure@kde.org>
00007    Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
00008 
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Library General Public
00011    License as published by the Free Software Foundation; either
00012    version 2 of the License, or (at your option) any later version.
00013 
00014    This library is distributed in the hope that it will be useful,
00015    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017    Library General Public License for more details.
00018 
00019    You should have received a copy of the GNU Library General Public License
00020    along with this library; see the file COPYING.LIB.  If not, write to
00021    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022    Boston, MA 02110-1301, USA.
00023 */
00024 
00025 /*
00026  * kpropertiesdialog.cpp
00027  * View/Edit Properties of files, locally or remotely
00028  *
00029  * some FilePermissionsPropsPlugin-changes by
00030  *  Henner Zeller <zeller@think.de>
00031  * some layout management by
00032  *  Bertrand Leconte <B.Leconte@mail.dotcom.fr>
00033  * the rest of the layout management, bug fixes, adaptation to libkio,
00034  * template feature by
00035  *  David Faure <faure@kde.org>
00036  * More layout, cleanups, and fixes by
00037  *  Preston Brown <pbrown@kde.org>
00038  * Plugin capability, cleanups and port to KDialog by
00039  *  Simon Hausmann <hausmann@kde.org>
00040  * KDesktopPropsPlugin by
00041  *  Waldo Bastian <bastian@kde.org>
00042  */
00043 
00044 #include "kpropertiesdialog.h"
00045 #include "kpropertiesdialog_p.h"
00046 
00047 
00048 #include <config.h>
00049 #include <config-acl.h>
00050 extern "C" {
00051 #include <pwd.h>
00052 #include <grp.h>
00053 #include <time.h>
00054 #include <sys/stat.h>
00055 #include <sys/types.h>
00056 }
00057 #include <unistd.h>
00058 #include <errno.h>
00059 #include <assert.h>
00060 #include <algorithm>
00061 #include <functional>
00062 
00063 #include <QtCore/QFile>
00064 #include <QtCore/QDir>
00065 #include <QtGui/QLabel>
00066 #include <QtGui/QPushButton>
00067 #include <QtGui/QCheckBox>
00068 #include <QtCore/QMutableStringListIterator>
00069 #include <QtCore/QTextIStream>
00070 #include <QtGui/QPainter>
00071 #include <QtGui/QLayout>
00072 #include <QtGui/QStyle>
00073 #include <QtGui/QProgressBar>
00074 #include <QVector>
00075 #include <QFileInfo>
00076 
00077 #ifdef HAVE_POSIX_ACL
00078 extern "C" {
00079 #  include <sys/xattr.h>
00080 }
00081 #endif
00082 
00083 #include <kauthorized.h>
00084 #include <kdialog.h>
00085 #include <kdirnotify.h>
00086 #include <kdiskfreespaceinfo.h>
00087 #include <kdebug.h>
00088 #include <kdesktopfile.h>
00089 #include <kicondialog.h>
00090 #include <kurl.h>
00091 #include <kurlrequester.h>
00092 #include <klocale.h>
00093 #include <kglobal.h>
00094 #include <kglobalsettings.h>
00095 #include <kstandarddirs.h>
00096 #include <kjobuidelegate.h>
00097 #include <kio/job.h>
00098 #include <kio/copyjob.h>
00099 #include <kio/chmodjob.h>
00100 #include <kio/directorysizejob.h>
00101 #include <kio/renamedialog.h>
00102 #include <kio/netaccess.h>
00103 #include <kio/jobuidelegate.h>
00104 #include <kfiledialog.h>
00105 #include <kmimetype.h>
00106 #include <kmountpoint.h>
00107 #include <kiconloader.h>
00108 #include <kmessagebox.h>
00109 #include <kservice.h>
00110 #include <kcombobox.h>
00111 #include <kcompletion.h>
00112 #include <klineedit.h>
00113 #include <kseparator.h>
00114 #include <ksqueezedtextlabel.h>
00115 #include <kmimetypetrader.h>
00116 #include <kmetaprops.h>
00117 #include <kpreviewprops.h>
00118 #include <krun.h>
00119 #include <kvbox.h>
00120 #include <kacl.h>
00121 #include <kconfiggroup.h>
00122 #include <kshell.h>
00123 #include <kcapacitybar.h>
00124 #include <kfileitemlistproperties.h>
00125 
00126 #ifndef Q_OS_WIN
00127 #include "kfilesharedialog.h"
00128 #endif
00129 
00130 #include "ui_kpropertiesdesktopbase.h"
00131 #include "ui_kpropertiesdesktopadvbase.h"
00132 #ifdef HAVE_POSIX_ACL
00133 #include "kacleditwidget.h"
00134 #endif
00135 
00136 #include <kbuildsycocaprogressdialog.h>
00137 #include <kmimetypechooser.h>
00138 
00139 #ifdef Q_WS_WIN
00140 # include <kkernel_win.h>
00141 #ifdef __GNUC__
00142 # warning TODO: port completely to win32
00143 #endif
00144 #endif
00145 
00146 using namespace KDEPrivate;
00147 
00148 static QString nameFromFileName(QString nameStr)
00149 {
00150     if ( nameStr.endsWith(QLatin1String(".desktop")) )
00151         nameStr.truncate( nameStr.length() - 8 );
00152     if ( nameStr.endsWith(QLatin1String(".kdelnk")) )
00153         nameStr.truncate( nameStr.length() - 7 );
00154     // Make it human-readable (%2F => '/', ...)
00155     nameStr = KIO::decodeFileName( nameStr );
00156     return nameStr;
00157 }
00158 
00159 mode_t KFilePermissionsPropsPlugin::fperm[3][4] = {
00160     {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
00161     {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
00162     {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
00163 };
00164 
00165 class KPropertiesDialog::KPropertiesDialogPrivate
00166 {
00167 public:
00168     KPropertiesDialogPrivate(KPropertiesDialog *qq)
00169     {
00170         q = qq;
00171         m_aborted = false;
00172         fileSharePage = 0;
00173     }
00174     ~KPropertiesDialogPrivate()
00175     {
00176     }
00177 
00181     void init();
00185     void insertPages();
00186 
00187     KPropertiesDialog *q;
00188     bool m_aborted:1;
00189     QWidget* fileSharePage;
00193     KUrl m_singleUrl;
00197     KFileItemList m_items;
00201     QString m_defaultName;
00202     KUrl m_currentDir;
00206     QList<KPropertiesDialogPlugin*> m_pageList;
00207 };
00208 
00209 KPropertiesDialog::KPropertiesDialog (const KFileItem& item,
00210                                       QWidget* parent)
00211     : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
00212 {
00213     setCaption( i18n( "Properties for %1" , KIO::decodeFileName(item.url().fileName())) );
00214 
00215     assert( !item.isNull() );
00216     d->m_items.append(item);
00217 
00218     d->m_singleUrl = item.url();
00219     assert(!d->m_singleUrl.isEmpty());
00220 
00221     d->init();
00222 }
00223 
00224 KPropertiesDialog::KPropertiesDialog (const QString& title,
00225                                       QWidget* parent)
00226     : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
00227 {
00228     setCaption( i18n( "Properties for %1", title ) );
00229 
00230     d->init();
00231 }
00232 
00233 KPropertiesDialog::KPropertiesDialog(const KFileItemList& _items,
00234                                      QWidget* parent)
00235     : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
00236 {
00237     if ( _items.count() > 1 )
00238         setCaption( i18np( "Properties for 1 item", "Properties for %1 Selected Items", _items.count() ) );
00239     else
00240         setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_items.first().url().fileName())) );
00241 
00242     assert( !_items.isEmpty() );
00243     d->m_singleUrl = _items.first().url();
00244     assert(!d->m_singleUrl.isEmpty());
00245 
00246     d->m_items = _items;
00247 
00248     d->init();
00249 }
00250 
00251 KPropertiesDialog::KPropertiesDialog (const KUrl& _url,
00252                                       QWidget* parent)
00253     : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
00254 {
00255     setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_url.fileName()))  );
00256 
00257     d->m_singleUrl = _url;
00258 
00259     KIO::UDSEntry entry;
00260     KIO::NetAccess::stat(_url, entry, parent);
00261 
00262     d->m_items.append(KFileItem(entry, _url));
00263     d->init();
00264 }
00265 
00266 KPropertiesDialog::KPropertiesDialog (const KUrl& _tempUrl, const KUrl& _currentDir,
00267                                       const QString& _defaultName,
00268                                       QWidget* parent)
00269     : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
00270 {
00271     setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_tempUrl.fileName()))  );
00272 
00273     d->m_singleUrl = _tempUrl;
00274     d->m_defaultName = _defaultName;
00275     d->m_currentDir = _currentDir;
00276     assert(!d->m_singleUrl.isEmpty());
00277 
00278     // Create the KFileItem for the _template_ file, in order to read from it.
00279     d->m_items.append(KFileItem(KFileItem::Unknown, KFileItem::Unknown, d->m_singleUrl));
00280     d->init();
00281 }
00282 
00283 bool KPropertiesDialog::showDialog(const KFileItem& item, QWidget* parent,
00284                                    bool modal)
00285 {
00286     // TODO: do we really want to show the win32 property dialog?
00287     // This means we lose metainfo, support for .desktop files, etc. (DF)
00288 #ifdef Q_WS_WIN
00289     QString localPath = item.localPath();
00290     if (!localPath.isEmpty())
00291         return showWin32FilePropertyDialog(localPath);
00292 #endif
00293     KPropertiesDialog* dlg = new KPropertiesDialog(item, parent);
00294     if (modal) {
00295         dlg->exec();
00296     } else {
00297         dlg->show();
00298     }
00299 
00300     return true;
00301 }
00302 
00303 bool KPropertiesDialog::showDialog(const KUrl& _url, QWidget* parent,
00304                                    bool modal)
00305 {
00306 #ifdef Q_WS_WIN
00307     if (_url.isLocalFile())
00308         return showWin32FilePropertyDialog( _url.toLocalFile() );
00309 #endif
00310     KPropertiesDialog* dlg = new KPropertiesDialog(_url, parent);
00311     if (modal) {
00312         dlg->exec();
00313     } else {
00314         dlg->show();
00315     }
00316 
00317     return true;
00318 }
00319 
00320 bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent,
00321                                    bool modal)
00322 {
00323     if (_items.count()==1) {
00324         const KFileItem item = _items.first();
00325         if (item.entry().count() == 0 && item.localPath().isEmpty()) // this remote item wasn't listed by a slave
00326             // Let's stat to get more info on the file
00327             return KPropertiesDialog::showDialog(item.url(), parent, modal);
00328         else
00329             return KPropertiesDialog::showDialog(_items.first(), parent, modal);
00330     }
00331     KPropertiesDialog* dlg = new KPropertiesDialog(_items, parent);
00332     if (modal) {
00333         dlg->exec();
00334     } else {
00335         dlg->show();
00336     }
00337     return true;
00338 }
00339 
00340 void KPropertiesDialog::KPropertiesDialogPrivate::init()
00341 {
00342     q->setFaceType(KPageDialog::Tabbed);
00343     q->setButtons(KDialog::Ok | KDialog::Cancel);
00344     q->setDefaultButton(KDialog::Ok);
00345 
00346     connect(q, SIGNAL(okClicked()), q, SLOT(slotOk()));
00347     connect(q, SIGNAL(cancelClicked()), q, SLOT(slotCancel()));
00348 
00349     insertPages();
00350 
00351     KConfigGroup group(KGlobal::config(), "KPropertiesDialog");
00352     q->restoreDialogSize(group);
00353 }
00354 
00355 void KPropertiesDialog::showFileSharingPage()
00356 {
00357     if (d->fileSharePage) {
00358         // FIXME: this showFileSharingPage thingy looks broken! (tokoe)
00359         // showPage( pageIndex( d->fileSharePage));
00360     }
00361 }
00362 
00363 void KPropertiesDialog::setFileSharingPage(QWidget* page) {
00364     d->fileSharePage = page;
00365 }
00366 
00367 
00368 void KPropertiesDialog::setFileNameReadOnly( bool ro )
00369 {
00370     foreach(KPropertiesDialogPlugin *it, d->m_pageList) {
00371         KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it);
00372         if ( plugin ) {
00373             plugin->setFileNameReadOnly( ro );
00374             break;
00375         }
00376     }
00377 }
00378 
00379 KPropertiesDialog::~KPropertiesDialog()
00380 {
00381     qDeleteAll(d->m_pageList);
00382     delete d;
00383 
00384     KConfigGroup group(KGlobal::config(), "KPropertiesDialog");
00385     saveDialogSize(group, KConfigBase::Persistent);
00386 }
00387 
00388 void KPropertiesDialog::insertPlugin (KPropertiesDialogPlugin* plugin)
00389 {
00390     connect (plugin, SIGNAL (changed ()),
00391              plugin, SLOT (setDirty ()));
00392 
00393     d->m_pageList.append(plugin);
00394 }
00395 
00396 KUrl KPropertiesDialog::kurl() const
00397 {
00398     return d->m_singleUrl;
00399 }
00400 
00401 KFileItem& KPropertiesDialog::item()
00402 {
00403     return d->m_items.first();
00404 }
00405 
00406 KFileItemList KPropertiesDialog::items() const
00407 {
00408     return d->m_items;
00409 }
00410 
00411 KUrl KPropertiesDialog::currentDir() const
00412 {
00413     return d->m_currentDir;
00414 }
00415 
00416 QString KPropertiesDialog::defaultName() const
00417 {
00418     return d->m_defaultName;
00419 }
00420 
00421 bool KPropertiesDialog::canDisplay( const KFileItemList& _items )
00422 {
00423     // TODO: cache the result of those calls. Currently we parse .desktop files far too many times
00424     return KFilePropsPlugin::supports( _items ) ||
00425             KFilePermissionsPropsPlugin::supports( _items ) ||
00426             KDesktopPropsPlugin::supports( _items ) ||
00427             KUrlPropsPlugin::supports( _items ) ||
00428             KDevicePropsPlugin::supports( _items ) ||
00429             KFileMetaPropsPlugin::supports( _items ) ||
00430             KPreviewPropsPlugin::supports( _items );
00431 }
00432 
00433 void KPropertiesDialog::slotOk()
00434 {
00435     QList<KPropertiesDialogPlugin*>::const_iterator pageListIt;
00436     d->m_aborted = false;
00437 
00438     KFilePropsPlugin * filePropsPlugin = qobject_cast<KFilePropsPlugin*>(d->m_pageList.first());
00439 
00440     // If any page is dirty, then set the main one (KFilePropsPlugin) as
00441     // dirty too. This is what makes it possible to save changes to a global
00442     // desktop file into a local one. In other cases, it doesn't hurt.
00443     for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd(); ++pageListIt) {
00444         if ( (*pageListIt)->isDirty() && filePropsPlugin )
00445         {
00446             filePropsPlugin->setDirty();
00447             break;
00448         }
00449     }
00450 
00451     // Apply the changes in the _normal_ order of the tabs now
00452     // This is because in case of renaming a file, KFilePropsPlugin will call
00453     // KPropertiesDialog::rename, so other tab will be ok with whatever order
00454     // BUT for file copied from templates, we need to do the renaming first !
00455     for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd() && !d->m_aborted; ++pageListIt) {
00456         if ( (*pageListIt)->isDirty() )
00457         {
00458             kDebug( 250 ) << "applying changes for " << (*pageListIt)->metaObject()->className();
00459             (*pageListIt)->applyChanges();
00460             // applyChanges may change d->m_aborted.
00461         }
00462         else {
00463             kDebug( 250 ) << "skipping page " << (*pageListIt)->metaObject()->className();
00464         }
00465     }
00466 
00467     if ( !d->m_aborted && filePropsPlugin )
00468         filePropsPlugin->postApplyChanges();
00469 
00470     if ( !d->m_aborted )
00471     {
00472         emit applied();
00473         emit propertiesClosed();
00474         deleteLater(); // somewhat like Qt::WA_DeleteOnClose would do.
00475         accept();
00476     } // else, keep dialog open for user to fix the problem.
00477 }
00478 
00479 void KPropertiesDialog::slotCancel()
00480 {
00481     emit canceled();
00482     emit propertiesClosed();
00483 
00484     deleteLater();
00485     done( Rejected );
00486 }
00487 
00488 void KPropertiesDialog::KPropertiesDialogPrivate::insertPages()
00489 {
00490     if (m_items.isEmpty())
00491         return;
00492 
00493     if ( KFilePropsPlugin::supports( m_items ) ) {
00494         KPropertiesDialogPlugin *p = new KFilePropsPlugin(q);
00495         q->insertPlugin(p);
00496     }
00497 
00498     if ( KFilePermissionsPropsPlugin::supports( m_items ) ) {
00499         KPropertiesDialogPlugin *p = new KFilePermissionsPropsPlugin(q);
00500         q->insertPlugin(p);
00501     }
00502 
00503     if ( KDesktopPropsPlugin::supports( m_items ) ) {
00504         KPropertiesDialogPlugin *p = new KDesktopPropsPlugin(q);
00505         q->insertPlugin(p);
00506     }
00507 
00508     if ( KUrlPropsPlugin::supports( m_items ) ) {
00509         KPropertiesDialogPlugin *p = new KUrlPropsPlugin(q);
00510         q->insertPlugin(p);
00511     }
00512 
00513     if ( KDevicePropsPlugin::supports( m_items ) ) {
00514         KPropertiesDialogPlugin *p = new KDevicePropsPlugin(q);
00515         q->insertPlugin(p);
00516     }
00517 
00518     if ( KFileMetaPropsPlugin::supports( m_items ) ) {
00519         KPropertiesDialogPlugin *p = new KFileMetaPropsPlugin(q);
00520         q->insertPlugin(p);
00521     }
00522 
00523     if ( KPreviewPropsPlugin::supports( m_items ) ) {
00524         KPropertiesDialogPlugin *p = new KPreviewPropsPlugin(q);
00525         q->insertPlugin(p);
00526     }
00527 
00528     //plugins
00529 
00530     if ( m_items.count() != 1 )
00531         return;
00532 
00533     const KFileItem item = m_items.first();
00534     const QString mimetype = item.mimetype();
00535 
00536     if ( mimetype.isEmpty() )
00537         return;
00538 
00539     QString query = QString::fromLatin1(
00540             "((not exist [X-KDE-Protocol]) or "
00541             " ([X-KDE-Protocol] == '%1'  )   )"
00542             ).arg(item.url().protocol());
00543 
00544     kDebug( 250 ) << "trader query: " << query;
00545     const KService::List offers = KMimeTypeTrader::self()->query( mimetype, "KPropertiesDialog/Plugin", query );
00546     foreach (const KService::Ptr &ptr, offers) {
00547         KPropertiesDialogPlugin *plugin = ptr->createInstance<KPropertiesDialogPlugin>(q);
00548         if (!plugin)
00549             continue;
00550         plugin->setObjectName(ptr->name());
00551 
00552         q->insertPlugin(plugin);
00553     }
00554 }
00555 
00556 void KPropertiesDialog::updateUrl( const KUrl& _newUrl )
00557 {
00558     Q_ASSERT(d->m_items.count() == 1);
00559     kDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url();
00560     KUrl newUrl = _newUrl;
00561     emit saveAs(d->m_singleUrl, newUrl);
00562     kDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url();
00563 
00564     d->m_singleUrl = newUrl;
00565     d->m_items.first().setUrl(newUrl);
00566     assert(!d->m_singleUrl.isEmpty());
00567     // If we have an Desktop page, set it dirty, so that a full file is saved locally
00568     // Same for a URL page (because of the Name= hack)
00569     foreach (KPropertiesDialogPlugin *it, d->m_pageList) {
00570         if ( qobject_cast<KUrlPropsPlugin*>(it) ||
00571              qobject_cast<KDesktopPropsPlugin*>(it) )
00572         {
00573             //kDebug(250) << "Setting page dirty";
00574             it->setDirty();
00575             break;
00576         }
00577     }
00578 }
00579 
00580 void KPropertiesDialog::rename( const QString& _name )
00581 {
00582     Q_ASSERT(d->m_items.count() == 1);
00583     kDebug(250) << "KPropertiesDialog::rename " << _name;
00584     KUrl newUrl;
00585     // if we're creating from a template : use currentdir
00586     if (!d->m_currentDir.isEmpty()) {
00587         newUrl = d->m_currentDir;
00588         newUrl.addPath(_name);
00589     } else {
00590         QString tmpurl = d->m_singleUrl.url();
00591         if (!tmpurl.isEmpty() && tmpurl.at(tmpurl.length() - 1) == '/') {
00592             // It's a directory, so strip the trailing slash first
00593             tmpurl.truncate(tmpurl.length() - 1);
00594         }
00595 
00596         newUrl = tmpurl;
00597         newUrl.setFileName(_name);
00598     }
00599     updateUrl(newUrl);
00600 }
00601 
00602 void KPropertiesDialog::abortApplying()
00603 {
00604     d->m_aborted = true;
00605 }
00606 
00607 class KPropertiesDialogPlugin::KPropertiesDialogPluginPrivate
00608 {
00609 public:
00610     KPropertiesDialogPluginPrivate()
00611     {
00612     }
00613     ~KPropertiesDialogPluginPrivate()
00614     {
00615     }
00616 
00617     bool m_bDirty;
00618     int fontHeight;
00619 };
00620 
00621 KPropertiesDialogPlugin::KPropertiesDialogPlugin( KPropertiesDialog *_props )
00622     : QObject( _props ),d(new KPropertiesDialogPluginPrivate)
00623 {
00624     properties = _props;
00625     d->fontHeight = 2*properties->fontMetrics().height();
00626     d->m_bDirty = false;
00627 }
00628 
00629 KPropertiesDialogPlugin::~KPropertiesDialogPlugin()
00630 {
00631     delete d;
00632 }
00633 
00634 #ifndef KDE_NO_DEPRECATED
00635 bool KPropertiesDialogPlugin::isDesktopFile( const KFileItem& _item )
00636 {
00637     return _item.isDesktopFile();
00638 }
00639 #endif
00640 
00641 void KPropertiesDialogPlugin::setDirty( bool b )
00642 {
00643     d->m_bDirty = b;
00644 }
00645 
00646 void KPropertiesDialogPlugin::setDirty()
00647 {
00648     d->m_bDirty = true;
00649 }
00650 
00651 bool KPropertiesDialogPlugin::isDirty() const
00652 {
00653     return d->m_bDirty;
00654 }
00655 
00656 void KPropertiesDialogPlugin::applyChanges()
00657 {
00658     kWarning(250) << "applyChanges() not implemented in page !";
00659 }
00660 
00661 int KPropertiesDialogPlugin::fontHeight() const
00662 {
00663     return d->fontHeight;
00664 }
00665 
00667 
00668 class KFilePropsPlugin::KFilePropsPluginPrivate
00669 {
00670 public:
00671     KFilePropsPluginPrivate()
00672     {
00673         dirSizeJob = 0L;
00674         dirSizeUpdateTimer = 0L;
00675         m_lined = 0;
00676         m_capacityBar = 0;
00677         m_linkTargetLineEdit = 0;
00678     }
00679     ~KFilePropsPluginPrivate()
00680     {
00681         if ( dirSizeJob )
00682             dirSizeJob->kill();
00683     }
00684 
00685     KIO::DirectorySizeJob * dirSizeJob;
00686     QTimer *dirSizeUpdateTimer;
00687     QFrame *m_frame;
00688     bool bMultiple;
00689     bool bIconChanged;
00690     bool bKDesktopMode;
00691     bool bDesktopFile;
00692     KCapacityBar *m_capacityBar;
00693     QString mimeType;
00694     QString oldFileName;
00695     KLineEdit* m_lined;
00696 
00697     QWidget *iconArea;
00698     QWidget *nameArea;
00699 
00700     QLabel *m_sizeLabel;
00701     QPushButton *m_sizeDetermineButton;
00702     QPushButton *m_sizeStopButton;
00703     KLineEdit* m_linkTargetLineEdit;
00704 
00705     QString m_sRelativePath;
00706     bool m_bFromTemplate;
00707 
00711     QString oldName;
00712 };
00713 
00714 KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props )
00715     : KPropertiesDialogPlugin( _props ),d(new KFilePropsPluginPrivate)
00716 {
00717     d->bMultiple = (properties->items().count() > 1);
00718     d->bIconChanged = false;
00719     d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items());
00720     kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple;
00721 
00722     // We set this data from the first item, and we'll
00723     // check that the other items match against it, resetting when not.
00724     bool isLocal;
00725     const KFileItem item = properties->item();
00726     KUrl url = item.mostLocalUrl( isLocal );
00727     bool isReallyLocal = item.url().isLocalFile();
00728     bool bDesktopFile = item.isDesktopFile();
00729     mode_t mode = item.mode();
00730     bool hasDirs = item.isDir() && !item.isLink();
00731     bool hasRoot = url.path() == QLatin1String("/");
00732     QString iconStr = KMimeType::iconNameForUrl(url, mode);
00733     QString directory = properties->kurl().directory();
00734     QString protocol = properties->kurl().protocol();
00735     d->bKDesktopMode = protocol == QLatin1String("desktop") ||
00736                 properties->currentDir().protocol() == QLatin1String("desktop");
00737     QString mimeComment = item.mimeComment();
00738     d->mimeType = item.mimetype();
00739     KIO::filesize_t totalSize = item.size();
00740     QString magicMimeComment;
00741     if ( isLocal ) {
00742         KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
00743         if ( magicMimeType->name() != KMimeType::defaultMimeType() )
00744             magicMimeComment = magicMimeType->comment();
00745     }
00746 #ifdef Q_WS_WIN
00747     if ( isReallyLocal ) {
00748         directory = QDir::toNativeSeparators( directory.mid( 1 ) );
00749     }
00750 #endif
00751 
00752     // Those things only apply to 'single file' mode
00753     QString filename;
00754     bool isTrash = false;
00755     d->m_bFromTemplate = false;
00756 
00757     // And those only to 'multiple' mode
00758     uint iDirCount = hasDirs ? 1 : 0;
00759     uint iFileCount = 1-iDirCount;
00760 
00761     d->m_frame = new QFrame();
00762     properties->addPage(d->m_frame, i18nc("@title:tab File properties", "&General"));
00763 
00764     QVBoxLayout *vbl = new QVBoxLayout( d->m_frame );
00765     vbl->setMargin( 0 );
00766     vbl->setObjectName( QLatin1String( "vbl" ) );
00767     QGridLayout *grid = new QGridLayout(); // unknown rows
00768     grid->setColumnStretch(0, 0);
00769     grid->setColumnStretch(1, 0);
00770     grid->setColumnStretch(2, 1);
00771     grid->addItem(new QSpacerItem(KDialog::spacingHint(),0), 0, 1);
00772     vbl->addLayout(grid);
00773     int curRow = 0;
00774 
00775     if ( !d->bMultiple )
00776     {
00777         QString path;
00778         if ( !d->m_bFromTemplate ) {
00779             isTrash = ( properties->kurl().protocol().toLower() == "trash" );
00780             // Extract the full name, but without file: for local files
00781             if ( isReallyLocal )
00782                 path = properties->kurl().toLocalFile();
00783             else
00784                 path = properties->kurl().prettyUrl();
00785         } else {
00786             path = properties->currentDir().path(KUrl::AddTrailingSlash) + properties->defaultName();
00787             directory = properties->currentDir().prettyUrl();
00788         }
00789 
00790         if (d->bDesktopFile) {
00791             determineRelativePath( path );
00792         }
00793 
00794         // Extract the file name only
00795         filename = properties->defaultName();
00796         if ( filename.isEmpty() ) { // no template
00797             filename = item.name(); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system
00798         } else {
00799             d->m_bFromTemplate = true;
00800             setDirty(); // to enforce that the copy happens
00801         }
00802         d->oldFileName = filename;
00803 
00804         // Make it human-readable
00805         filename = nameFromFileName( filename );
00806 
00807         if ( d->bKDesktopMode && d->bDesktopFile ) {
00808             KDesktopFile config( url.path() );
00809             if ( config.desktopGroup().hasKey( "Name" ) ) {
00810                 filename = config.readName();
00811             }
00812         }
00813 
00814         d->oldName = filename;
00815     }
00816     else
00817     {
00818         // Multiple items: see what they have in common
00819         const KFileItemList items = properties->items();
00820         KFileItemList::const_iterator kit = items.begin();
00821         const KFileItemList::const_iterator kend = items.end();
00822         for ( ++kit /*no need to check the first one again*/ ; kit != kend; ++kit )
00823         {
00824             const KUrl url = (*kit).url();
00825             kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyUrl();
00826             // The list of things we check here should match the variables defined
00827             // at the beginning of this method.
00828             if ( url.isLocalFile() != isLocal )
00829                 isLocal = false; // not all local
00830             if ( bDesktopFile && (*kit).isDesktopFile() != bDesktopFile )
00831                 bDesktopFile = false; // not all desktop files
00832             if ( (*kit).mode() != mode )
00833                 mode = (mode_t)0;
00834             if ( KMimeType::iconNameForUrl(url, mode) != iconStr )
00835                 iconStr = "document-multiple";
00836             if ( url.directory() != directory )
00837                 directory.clear();
00838             if ( url.protocol() != protocol )
00839                 protocol.clear();
00840             if ( !mimeComment.isNull() && (*kit).mimeComment() != mimeComment )
00841                 mimeComment.clear();
00842             if ( isLocal && !magicMimeComment.isNull() ) {
00843                 KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
00844                 if ( magicMimeType->comment() != magicMimeComment )
00845                     magicMimeComment.clear();
00846             }
00847 
00848             if ( isLocal && url.path() == QLatin1String("/") )
00849                 hasRoot = true;
00850             if ( (*kit).isDir() && !(*kit).isLink() )
00851             {
00852                 iDirCount++;
00853                 hasDirs = true;
00854             }
00855             else
00856             {
00857                 iFileCount++;
00858                 totalSize += (*kit).size();
00859             }
00860         }
00861     }
00862 
00863     if (!isReallyLocal && !protocol.isEmpty())
00864     {
00865         directory += ' ';
00866         directory += '(';
00867         directory += protocol;
00868         directory += ')';
00869     }
00870 
00871     if (!isTrash && (bDesktopFile || S_ISDIR(mode))
00872         && !d->bMultiple // not implemented for multiple
00873         && enableIconButton()) // #56857
00874     {
00875         KIconButton *iconButton = new KIconButton( d->m_frame );
00876         int bsize = 66 + 2 * iconButton->style()->pixelMetric(QStyle::PM_ButtonMargin);
00877         iconButton->setFixedSize(bsize, bsize);
00878         iconButton->setIconSize(48);
00879         iconButton->setStrictIconSize(false);
00880         QString iconStr = KMimeType::findByUrl(url, mode)->iconName(url);
00881         if (bDesktopFile && isLocal) {
00882             KDesktopFile config( url.path() );
00883             KConfigGroup group = config.desktopGroup();
00884             iconStr = group.readEntry( "Icon" );
00885             if ( config.hasDeviceType() )
00886                 iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Device );
00887             else
00888                 iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Application );
00889         } else {
00890             iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Place );
00891         }
00892         iconButton->setIcon(iconStr);
00893         d->iconArea = iconButton;
00894         connect(iconButton, SIGNAL(iconChanged(QString)),
00895                 this, SLOT(slotIconChanged()));
00896     } else {
00897         QLabel *iconLabel = new QLabel( d->m_frame );
00898         int bsize = 66 + 2 * iconLabel->style()->pixelMetric(QStyle::PM_ButtonMargin);
00899         iconLabel->setFixedSize(bsize, bsize);
00900         iconLabel->setPixmap( KIconLoader::global()->loadIcon( iconStr, KIconLoader::Desktop, 48) );
00901         d->iconArea = iconLabel;
00902     }
00903     grid->addWidget(d->iconArea, curRow, 0, Qt::AlignLeft);
00904 
00905     if (d->bMultiple || isTrash || hasRoot)
00906     {
00907         QLabel *lab = new QLabel(d->m_frame );
00908         if ( d->bMultiple )
00909             lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) );
00910         else
00911             lab->setText( filename );
00912         d->nameArea = lab;
00913     } else
00914     {
00915         d->m_lined = new KLineEdit( d->m_frame );
00916         d->m_lined->setText(filename);
00917         d->nameArea = d->m_lined;
00918         d->m_lined->setFocus();
00919 
00920         //if we don't have permissions to rename, we need to make "m_lined" read only.
00921         KFileItemListProperties itemList(KFileItemList()<< item);
00922         setFileNameReadOnly(!itemList.supportsMoving());
00923 
00924         // Enhanced rename: Don't highlight the file extension.
00925         QString extension = KMimeType::extractKnownExtension( filename );
00926         if ( !extension.isEmpty() )
00927             d->m_lined->setSelection( 0, filename.length() - extension.length() - 1 );
00928         else
00929         {
00930             int lastDot = filename.lastIndexOf('.');
00931             if (lastDot > 0)
00932                 d->m_lined->setSelection(0, lastDot);
00933         }
00934 
00935         connect( d->m_lined, SIGNAL( textChanged( const QString & ) ),
00936                  this, SLOT( nameFileChanged(const QString & ) ) );
00937     }
00938 
00939     grid->addWidget(d->nameArea, curRow++, 2);
00940 
00941     KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame);
00942     grid->addWidget(sep, curRow, 0, 1, 3);
00943     ++curRow;
00944 
00945     QLabel *l;
00946     if (!mimeComment.isEmpty() && !isTrash) {
00947         l = new QLabel(i18n("Type:"), d->m_frame );
00948 
00949         grid->addWidget(l, curRow, 0, Qt::AlignRight);
00950 
00951         KHBox *box = new KHBox(d->m_frame);
00952         box->setSpacing(20); // ### why 20?
00953         l = new QLabel(mimeComment, box );
00954 
00955         QPushButton *button = new QPushButton(box);
00956 
00957         button->setIcon( KIcon(QString::fromLatin1("configure")) );
00958         const int pixmapSize = button->style()->pixelMetric(QStyle::PM_SmallIconSize);
00959         button->setFixedSize( pixmapSize+8, pixmapSize+8 );
00960         if ( d->mimeType == KMimeType::defaultMimeType() )
00961             button->setToolTip(i18n("Create new file type"));
00962         else
00963             button->setToolTip(i18n("Edit file type"));
00964 
00965         connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() ));
00966 
00967         if (!KAuthorized::authorizeKAction("editfiletype"))
00968             button->hide();
00969 
00970         grid->addWidget(box, curRow++, 2);
00971     }
00972 
00973     if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
00974     {
00975         l = new QLabel(i18n("Contents:"), d->m_frame );
00976         grid->addWidget(l, curRow, 0, Qt::AlignRight);
00977 
00978         l = new QLabel(magicMimeComment, d->m_frame );
00979         grid->addWidget(l, curRow++, 2);
00980     }
00981 
00982     if ( !directory.isEmpty() )
00983     {
00984         l = new QLabel( i18n("Location:"), d->m_frame );
00985         grid->addWidget(l, curRow, 0, Qt::AlignRight);
00986 
00987         l = new KSqueezedTextLabel( directory, d->m_frame );
00988         // force the layout direction to be always LTR
00989         l->setLayoutDirection(Qt::LeftToRight);
00990         // but if we are in RTL mode, align the text to the right
00991         // otherwise the text is on the wrong side of the dialog
00992         if (properties->layoutDirection() == Qt::RightToLeft)
00993             l->setAlignment( Qt::AlignRight );
00994         l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
00995         grid->addWidget(l, curRow++, 2);
00996     }
00997 
00998     l = new QLabel(i18n("Size:"), d->m_frame );
00999     grid->addWidget(l, curRow, 0, Qt::AlignRight);
01000 
01001     d->m_sizeLabel = new QLabel( d->m_frame );
01002     grid->addWidget( d->m_sizeLabel, curRow++, 2 );
01003 
01004     if ( !hasDirs ) // Only files [and symlinks]
01005     {
01006         d->m_sizeLabel->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize))
01007                                 .arg(KGlobal::locale()->formatNumber(totalSize, 0)));
01008         d->m_sizeDetermineButton = 0L;
01009         d->m_sizeStopButton = 0L;
01010     }
01011     else // Directory
01012     {
01013         QHBoxLayout * sizelay = new QHBoxLayout();
01014         grid->addLayout( sizelay, curRow++, 2 );
01015 
01016         // buttons
01017         d->m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
01018         d->m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
01019         connect( d->m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) );
01020         connect( d->m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) );
01021         sizelay->addWidget(d->m_sizeDetermineButton, 0);
01022         sizelay->addWidget(d->m_sizeStopButton, 0);
01023         sizelay->addStretch(10); // so that the buttons don't grow horizontally
01024 
01025         // auto-launch for local dirs only, and not for '/'
01026         if ( isLocal && !hasRoot )
01027         {
01028             d->m_sizeDetermineButton->setText( i18n("Refresh") );
01029             slotSizeDetermine();
01030         }
01031         else
01032             d->m_sizeStopButton->setEnabled( false );
01033     }
01034 
01035     if (!d->bMultiple && item.isLink()) {
01036         l = new QLabel(i18n("Points to:"), d->m_frame );
01037         grid->addWidget(l, curRow, 0, Qt::AlignRight);
01038 
01039         d->m_linkTargetLineEdit = new KLineEdit(item.linkDest(), d->m_frame );
01040         grid->addWidget(d->m_linkTargetLineEdit, curRow++, 2);
01041         connect(d->m_linkTargetLineEdit, SIGNAL(textChanged(QString)), this, SLOT(setDirty()));
01042     }
01043 
01044     if (!d->bMultiple) // Dates for multiple don't make much sense...
01045     {
01046         KDateTime dt = item.time(KFileItem::CreationTime);
01047         if ( !dt.isNull() )
01048         {
01049             l = new QLabel(i18n("Created:"), d->m_frame );
01050             grid->addWidget(l, curRow, 0, Qt::AlignRight);
01051 
01052             l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
01053             grid->addWidget(l, curRow++, 2);
01054         }
01055 
01056         dt = item.time(KFileItem::ModificationTime);
01057         if ( !dt.isNull() )
01058         {
01059             l = new QLabel(i18n("Modified:"), d->m_frame );
01060             grid->addWidget(l, curRow, 0, Qt::AlignRight);
01061 
01062             l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
01063             grid->addWidget(l, curRow++, 2);
01064         }
01065 
01066         dt = item.time(KFileItem::AccessTime);
01067         if ( !dt.isNull() )
01068         {
01069             l = new QLabel(i18n("Accessed:"), d->m_frame );
01070             grid->addWidget(l, curRow, 0, Qt::AlignRight);
01071 
01072             l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
01073             grid->addWidget(l, curRow++, 2);
01074         }
01075     }
01076 
01077     if ( isLocal && hasDirs )  // only for directories
01078     {
01079 
01080         KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() );
01081         if (mp) {
01082             KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() );
01083             if(info.size() != 0 )
01084             {
01085                 sep = new KSeparator( Qt::Horizontal, d->m_frame);
01086                 grid->addWidget(sep, curRow, 0, 1, 3);
01087                 ++curRow;
01088                 if (mp->mountPoint() != "/")
01089                 {
01090                     l = new QLabel(i18n("Mounted on:"), d->m_frame );
01091                     grid->addWidget(l, curRow, 0, Qt::AlignRight);
01092 
01093                     l = new KSqueezedTextLabel( mp->mountPoint(), d->m_frame );
01094                     l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
01095                     grid->addWidget( l, curRow++, 2 );
01096                 }
01097 
01098                 l = new QLabel(i18n("Device usage:"), d->m_frame );
01099                 grid->addWidget(l, curRow, 0, Qt::AlignRight);
01100 
01101                 d->m_capacityBar = new KCapacityBar( KCapacityBar::DrawTextOutline, d->m_frame );
01102                 grid->addWidget( d->m_capacityBar, curRow++, 2);
01103 
01104                 slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
01105             }
01106         }
01107     }
01108 
01109     vbl->addStretch(1);
01110 }
01111 
01112 bool KFilePropsPlugin::enableIconButton() const
01113 {
01114     bool iconEnabled = false;
01115     const KFileItem item = properties->item();
01116     // If the current item is a directory, check if it's writable,
01117     // so we can create/update a .directory
01118     // Current item is a file, same thing: check if it is writable
01119     if (item.isWritable()) {
01120         iconEnabled = true;
01121     }
01122     return iconEnabled;
01123 }
01124 
01125 // QString KFilePropsPlugin::tabName () const
01126 // {
01127 //   return i18n ("&General");
01128 // }
01129 
01130 void KFilePropsPlugin::setFileNameReadOnly( bool ro )
01131 {
01132     if ( d->m_lined && !d->m_bFromTemplate )
01133     {
01134         d->m_lined->setReadOnly( ro );
01135         if (ro)
01136         {
01137             // Don't put the initial focus on the line edit when it is ro
01138             properties->setButtonFocus(KDialog::Ok);
01139         }
01140     }
01141 }
01142 
01143 void KFilePropsPlugin::slotEditFileType()
01144 {
01145     QString mime;
01146     if (d->mimeType == KMimeType::defaultMimeType()) {
01147         const int pos = d->oldFileName.lastIndexOf('.');
01148         if (pos != -1)
01149             mime = '*' + d->oldFileName.mid(pos);
01150         else
01151             mime = '*';
01152     }  else {
01153         mime = d->mimeType;
01154     }
01155     QString keditfiletype = QString::fromLatin1("keditfiletype");
01156     KRun::runCommand( keditfiletype
01157 #ifdef Q_WS_X11
01158                       + " --parent " + QString::number( (ulong)properties->window()->winId())
01159 #endif
01160                       + ' ' + KShell::quoteArg(mime),
01161                       keditfiletype, keditfiletype /*unused*/, properties->window());
01162 }
01163 
01164 void KFilePropsPlugin::slotIconChanged()
01165 {
01166     d->bIconChanged = true;
01167     emit changed();
01168 }
01169 
01170 void KFilePropsPlugin::nameFileChanged(const QString &text )
01171 {
01172     properties->enableButtonOk(!text.isEmpty());
01173     emit changed();
01174 }
01175 
01176 void KFilePropsPlugin::determineRelativePath( const QString & path )
01177 {
01178     // now let's make it relative
01179     d->m_sRelativePath = KGlobal::dirs()->relativeLocation("apps", path);
01180     if (d->m_sRelativePath.startsWith('/'))
01181     {
01182         d->m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
01183         if (d->m_sRelativePath.startsWith('/'))
01184             d->m_sRelativePath.clear();
01185         else
01186             d->m_sRelativePath = path;
01187     }
01188 }
01189 
01190 void KFilePropsPlugin::slotFoundMountPoint( const QString&,
01191                                             quint64 kibSize,
01192                                             quint64 /*kibUsed*/,
01193                                             quint64 kibAvail )
01194 {
01195     d->m_capacityBar->setText(
01196             i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
01197                   KIO::convertSizeFromKiB(kibAvail),
01198                   KIO::convertSizeFromKiB(kibSize),
01199                   100 - (int)(100.0 * kibAvail / kibSize) ));
01200 
01201     d->m_capacityBar->setValue(100 - (int)(100.0 * kibAvail / kibSize));
01202 }
01203 
01204 void KFilePropsPlugin::slotDirSizeUpdate()
01205 {
01206     KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
01207     KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
01208     KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
01209     d->m_sizeLabel->setText(
01210             i18n("Calculating... %1 (%2)\n%3, %4",
01211                  KIO::convertSize(totalSize),
01212                  totalSize,
01213                  i18np("1 file", "%1 files", totalFiles),
01214                  i18np("1 sub-folder", "%1 sub-folders", totalSubdirs)));
01215 }
01216 
01217 void KFilePropsPlugin::slotDirSizeFinished( KJob * job )
01218 {
01219     if (job->error())
01220         d->m_sizeLabel->setText( job->errorString() );
01221     else
01222     {
01223         KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
01224         KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
01225         KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
01226         d->m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4")
01227                                  .arg(KIO::convertSize(totalSize))
01228                                  .arg(KGlobal::locale()->formatNumber(totalSize, 0))
01229                                  .arg(i18np("1 file","%1 files",totalFiles))
01230                                  .arg(i18np("1 sub-folder","%1 sub-folders",totalSubdirs)));
01231     }
01232     d->m_sizeStopButton->setEnabled(false);
01233     // just in case you change something and try again :)
01234     d->m_sizeDetermineButton->setText( i18n("Refresh") );
01235     d->m_sizeDetermineButton->setEnabled(true);
01236     d->dirSizeJob = 0;
01237     delete d->dirSizeUpdateTimer;
01238     d->dirSizeUpdateTimer = 0;
01239 }
01240 
01241 void KFilePropsPlugin::slotSizeDetermine()
01242 {
01243     d->m_sizeLabel->setText( i18n("Calculating...") );
01244     kDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" <<  properties->item();
01245     kDebug(250) << " URL=" << properties->item().url().url();
01246 
01247     d->dirSizeJob = KIO::directorySize( properties->items() );
01248     d->dirSizeUpdateTimer = new QTimer(this);
01249     connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ),
01250              SLOT( slotDirSizeUpdate() ) );
01251     d->dirSizeUpdateTimer->start(500);
01252     connect( d->dirSizeJob, SIGNAL( result( KJob * ) ),
01253              SLOT( slotDirSizeFinished( KJob * ) ) );
01254     d->m_sizeStopButton->setEnabled(true);
01255     d->m_sizeDetermineButton->setEnabled(false);
01256 
01257     // also update the "Free disk space" display
01258     if ( d->m_capacityBar )
01259     {
01260         bool isLocal;
01261         const KFileItem item = properties->item();
01262         KUrl url = item.mostLocalUrl( isLocal );
01263         KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() );
01264         if (mp) {
01265             KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() );
01266             slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
01267         }
01268     }
01269 }
01270 
01271 void KFilePropsPlugin::slotSizeStop()
01272 {
01273     if ( d->dirSizeJob )
01274     {
01275         KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
01276         d->m_sizeLabel->setText(i18n("At least %1",
01277                                      KIO::convertSize(totalSize)));
01278         d->dirSizeJob->kill();
01279         d->dirSizeJob = 0;
01280     }
01281     if ( d->dirSizeUpdateTimer )
01282         d->dirSizeUpdateTimer->stop();
01283 
01284     d->m_sizeStopButton->setEnabled(false);
01285     d->m_sizeDetermineButton->setEnabled(true);
01286 }
01287 
01288 KFilePropsPlugin::~KFilePropsPlugin()
01289 {
01290     delete d;
01291 }
01292 
01293 bool KFilePropsPlugin::supports( const KFileItemList& /*_items*/ )
01294 {
01295     return true;
01296 }
01297 
01298 void KFilePropsPlugin::applyChanges()
01299 {
01300     if ( d->dirSizeJob )
01301         slotSizeStop();
01302 
01303     kDebug(250) << "KFilePropsPlugin::applyChanges";
01304 
01305     if (qobject_cast<QLineEdit*>(d->nameArea))
01306     {
01307         QString n = ((QLineEdit *) d->nameArea)->text();
01308         // Remove trailing spaces (#4345)
01309         while ( ! n.isEmpty() && n[n.length()-1].isSpace() )
01310             n.truncate( n.length() - 1 );
01311         if ( n.isEmpty() )
01312         {
01313             KMessageBox::sorry( properties, i18n("The new file name is empty."));
01314             properties->abortApplying();
01315             return;
01316         }
01317 
01318         // Do we need to rename the file ?
01319         kDebug(250) << "oldname = " << d->oldName;
01320         kDebug(250) << "newname = " << n;
01321         if ( d->oldName != n || d->m_bFromTemplate ) { // true for any from-template file
01322             KIO::Job * job = 0L;
01323             KUrl oldurl = properties->kurl();
01324 
01325             QString newFileName = KIO::encodeFileName(n);
01326             if (d->bDesktopFile && !newFileName.endsWith(QLatin1String(".desktop")) &&
01327                 !newFileName.endsWith(QLatin1String(".kdelnk")))
01328                 newFileName += ".desktop";
01329 
01330             // Tell properties. Warning, this changes the result of properties->kurl() !
01331             properties->rename( newFileName );
01332 
01333             // Update also relative path (for apps and mimetypes)
01334             if ( !d->m_sRelativePath.isEmpty() )
01335                 determineRelativePath( properties->kurl().toLocalFile() );
01336 
01337             kDebug(250) << "New URL = " << properties->kurl().url();
01338             kDebug(250) << "old = " << oldurl.url();
01339 
01340             // Don't remove the template !!
01341             if ( !d->m_bFromTemplate ) // (normal renaming)
01342                 job = KIO::move( oldurl, properties->kurl() );
01343             else // Copying a template
01344                 job = KIO::copy( oldurl, properties->kurl() );
01345 
01346             connect( job, SIGNAL( result( KJob * ) ),
01347                      SLOT( slotCopyFinished( KJob * ) ) );
01348             connect( job, SIGNAL( renamed( KIO::Job *, const KUrl &, const KUrl & ) ),
01349                      SLOT( slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & ) ) );
01350             // wait for job
01351             QEventLoop eventLoop;
01352             connect(this, SIGNAL(leaveModality()),
01353                     &eventLoop, SLOT(quit()));
01354             eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
01355             return;
01356         }
01357         properties->updateUrl(properties->kurl());
01358         // Update also relative path (for apps and mimetypes)
01359         if ( !d->m_sRelativePath.isEmpty() )
01360             determineRelativePath( properties->kurl().toLocalFile() );
01361     }
01362 
01363     // No job, keep going
01364     slotCopyFinished( 0L );
01365 }
01366 
01367 void KFilePropsPlugin::slotCopyFinished( KJob * job )
01368 {
01369     kDebug(250) << "KFilePropsPlugin::slotCopyFinished";
01370     if (job)
01371     {
01372         // allow apply() to return
01373         emit leaveModality();
01374         if ( job->error() )
01375         {
01376             job->uiDelegate()->showErrorMessage();
01377             // Didn't work. Revert the URL to the old one
01378             properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcUrls().first() );
01379             properties->abortApplying(); // Don't apply the changes to the wrong file !
01380             return;
01381         }
01382     }
01383 
01384     assert( !properties->item().isNull() );
01385     assert( !properties->item().url().isEmpty() );
01386 
01387     // Save the file where we can -> usually in ~/.kde/...
01388     if (d->bDesktopFile && !d->m_sRelativePath.isEmpty())
01389     {
01390         kDebug(250) << "KFilePropsPlugin::slotCopyFinished " << d->m_sRelativePath;
01391         KUrl newURL;
01392         newURL.setPath( KDesktopFile::locateLocal(d->m_sRelativePath) );
01393         kDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path();
01394         properties->updateUrl( newURL );
01395     }
01396 
01397     if ( d->bKDesktopMode && d->bDesktopFile ) {
01398         // Renamed? Update Name field
01399         // Note: The desktop ioslave does this as well, but not when
01400         //       the file is copied from a template.
01401         if ( d->m_bFromTemplate ) {
01402             KIO::UDSEntry entry;
01403             KIO::NetAccess::stat( properties->kurl(), entry, 0 );
01404             KFileItem item( entry, properties->kurl() );
01405             KDesktopFile config( item.localPath() );
01406             KConfigGroup cg = config.desktopGroup();
01407             QString nameStr = nameFromFileName(properties->kurl().fileName());
01408             cg.writeEntry( "Name", nameStr );
01409             cg.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized);
01410         }
01411     }
01412 
01413     if (d->m_linkTargetLineEdit && !d->bMultiple) {
01414         const KFileItem item = properties->item();
01415         const QString newTarget = d->m_linkTargetLineEdit->text();
01416         if (newTarget != item.linkDest()) {
01417             kDebug(250) << "Updating target of symlink to" << newTarget;
01418             KIO::Job* job = KIO::symlink(newTarget, item.url(), KIO::Overwrite);
01419             job->ui()->setAutoErrorHandlingEnabled(true);
01420             job->exec();
01421         }
01422     }
01423 
01424     // "Link to Application" templates need to be made executable
01425     // Instead of matching against a filename we check if the destination
01426     // is an Application now.
01427     if ( d->m_bFromTemplate ) {
01428         // destination is not necessarily local, use the src template
01429         KDesktopFile templateResult ( static_cast<KIO::CopyJob*>(job)->srcUrls().first().toLocalFile() );
01430         if ( templateResult.hasApplicationType() ) {
01431             // We can either stat the file and add the +x bit or use the larger chmod() job
01432             // with a umask designed to only touch u+x.  This is only one KIO job, so let's
01433             // do that.
01434 
01435             KFileItem appLink ( properties->item() );
01436             KFileItemList fileItemList;
01437             fileItemList << appLink;
01438 
01439             // first 0100 adds u+x, second 0100 only allows chmod to change u+x
01440             KIO::Job* chmodJob = KIO::chmod( fileItemList, 0100, 0100, QString(), QString(), KIO::HideProgressInfo );
01441             chmodJob->exec();
01442         }
01443     }
01444 }
01445 
01446 void KFilePropsPlugin::applyIconChanges()
01447 {
01448     KIconButton *iconButton = qobject_cast<KIconButton*>(d->iconArea);
01449     if ( !iconButton || !d->bIconChanged )
01450         return;
01451     // handle icon changes - only local files (or pseudo-local) for now
01452     // TODO: Use KTempFile and KIO::file_copy with overwrite = true
01453     KUrl url = properties->kurl();
01454     url = KIO::NetAccess::mostLocalUrl( url, properties );
01455     if ( url.isLocalFile()) {
01456         QString path;
01457 
01458         if (S_ISDIR(properties->item().mode()))
01459         {
01460             path = url.toLocalFile(KUrl::AddTrailingSlash) + QString::fromLatin1(".directory");
01461             // don't call updateUrl because the other tabs (i.e. permissions)
01462             // apply to the directory, not the .directory file.
01463         }
01464         else
01465             path = url.toLocalFile();
01466 
01467         // Get the default image
01468         QString str = KMimeType::findByUrl( url,
01469                                             properties->item().mode(),
01470                                             true )->iconName();
01471         // Is it another one than the default ?
01472         QString sIcon;
01473         if ( str != iconButton->icon() )
01474             sIcon = iconButton->icon();
01475         // (otherwise write empty value)
01476 
01477         kDebug(250) << "**" << path << "**";
01478 
01479         // If default icon and no .directory file -> don't create one
01480         if ( !sIcon.isEmpty() || QFile::exists(path) )
01481         {
01482             KDesktopFile cfg(path);
01483             kDebug(250) << "sIcon = " << (sIcon);
01484             kDebug(250) << "str = " << (str);
01485             cfg.desktopGroup().writeEntry( "Icon", sIcon );
01486             cfg.sync();
01487 
01488             cfg.reparseConfiguration();
01489             if ( cfg.desktopGroup().readEntry("Icon") != sIcon ) {
01490                 KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
01491                                             "have sufficient access to write to <b>%1</b>.</qt>", path));
01492             }
01493         }
01494     }
01495 }
01496 
01497 void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & newUrl )
01498 {
01499     // This is called in case of an existing local file during the copy/move operation,
01500     // if the user chooses Rename.
01501     properties->updateUrl( newUrl );
01502 }
01503 
01504 void KFilePropsPlugin::postApplyChanges()
01505 {
01506     // Save the icon only after applying the permissions changes (#46192)
01507     applyIconChanges();
01508 
01509     const KFileItemList items = properties->items();
01510     const KUrl::List lst = items.urlList();
01511     org::kde::KDirNotify::emitFilesChanged( lst.toStringList() );
01512 }
01513 
01514 class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
01515 {
01516 public:
01517     KFilePermissionsPropsPluginPrivate()
01518     {
01519     }
01520     ~KFilePermissionsPropsPluginPrivate()
01521     {
01522     }
01523 
01524     QFrame *m_frame;
01525     QCheckBox *cbRecursive;
01526     QLabel *explanationLabel;
01527     KComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo;
01528     QCheckBox *extraCheckbox;
01529     mode_t partialPermissions;
01530     KFilePermissionsPropsPlugin::PermissionsMode pmode;
01531     bool canChangePermissions;
01532     bool isIrregular;
01533     bool hasExtendedACL;
01534     KACL extendedACL;
01535     KACL defaultACL;
01536     bool fileSystemSupportsACLs;
01537 
01538     KComboBox *grpCombo;
01539 
01540     KLineEdit *usrEdit;
01541     KLineEdit *grpEdit;
01542 
01543     // Old permissions
01544     mode_t permissions;
01545     // Old group
01546     QString strGroup;
01547     // Old owner
01548     QString strOwner;
01549 };
01550 
01551 #define UniOwner    (S_IRUSR|S_IWUSR|S_IXUSR)
01552 #define UniGroup    (S_IRGRP|S_IWGRP|S_IXGRP)
01553 #define UniOthers   (S_IROTH|S_IWOTH|S_IXOTH)
01554 #define UniRead     (S_IRUSR|S_IRGRP|S_IROTH)
01555 #define UniWrite    (S_IWUSR|S_IWGRP|S_IWOTH)
01556 #define UniExec     (S_IXUSR|S_IXGRP|S_IXOTH)
01557 #define UniSpecial  (S_ISUID|S_ISGID|S_ISVTX)
01558 
01559 // synced with PermissionsTarget
01560 const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers};
01561 const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 };
01562 
01563 // synced with PermissionsMode and standardPermissions
01564 const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = {
01565     { I18N_NOOP("Forbidden"),
01566       I18N_NOOP("Can Read"),
01567       I18N_NOOP("Can Read & Write"),
01568       0 },
01569 { I18N_NOOP("Forbidden"),
01570   I18N_NOOP("Can View Content"),
01571   I18N_NOOP("Can View & Modify Content"),
01572   0 },
01573 { 0, 0, 0, 0}, // no texts for links
01574 { I18N_NOOP("Forbidden"),
01575   I18N_NOOP("Can View Content & Read"),
01576   I18N_NOOP("Can View/Read & Modify/Write"),
01577   0 }
01578 };
01579 
01580 
01581 KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props )
01582     : KPropertiesDialogPlugin( _props ),d(new KFilePermissionsPropsPluginPrivate)
01583 {
01584     d->cbRecursive = 0L;
01585     d->grpCombo = 0L; d->grpEdit = 0;
01586     d->usrEdit = 0L;
01587     QString path = properties->kurl().path(KUrl::RemoveTrailingSlash);
01588     QString fname = properties->kurl().fileName();
01589     bool isLocal = properties->kurl().isLocalFile();
01590     bool isTrash = ( properties->kurl().protocol().toLower() == "trash" );
01591     bool IamRoot = (geteuid() == 0);
01592 
01593     const KFileItem item = properties->item();
01594     bool isLink = item.isLink();
01595     bool isDir = item.isDir(); // all dirs
01596     bool hasDir = item.isDir(); // at least one dir
01597     d->permissions = item.permissions(); // common permissions to all files
01598     d->partialPermissions = d->permissions; // permissions that only some files have (at first we take everything)
01599     d->isIrregular = isIrregular(d->permissions, isDir, isLink);
01600     d->strOwner = item.user();
01601     d->strGroup = item.group();
01602     d->hasExtendedACL = item.ACL().isExtended() || item.defaultACL().isValid();
01603     d->extendedACL = item.ACL();
01604     d->defaultACL = item.defaultACL();
01605     d->fileSystemSupportsACLs = false;
01606 
01607     if ( properties->items().count() > 1 )
01608     {
01609         // Multiple items: see what they have in common
01610         const KFileItemList items = properties->items();
01611         KFileItemList::const_iterator it = items.begin();
01612         const KFileItemList::const_iterator kend = items.end();
01613         for ( ++it /*no need to check the first one again*/ ; it != kend; ++it )
01614         {
01615             const KUrl url = (*it).url();
01616             if (!d->isIrregular)
01617                 d->isIrregular |= isIrregular((*it).permissions(),
01618                                               (*it).isDir() == isDir,
01619                                               (*it).isLink() == isLink);
01620             d->hasExtendedACL = d->hasExtendedACL || (*it).hasExtendedACL();
01621             if ( (*it).isLink() != isLink )
01622                 isLink = false;
01623             if ( (*it).isDir() != isDir )
01624                 isDir = false;
01625             hasDir |= (*it).isDir();
01626             if ( (*it).permissions() != d->permissions )
01627             {
01628                 d->permissions &= (*it).permissions();
01629                 d->partialPermissions |= (*it).permissions();
01630             }
01631             if ( (*it).user() != d->strOwner )
01632                 d->strOwner.clear();
01633             if ( (*it).group() != d->strGroup )
01634                 d->strGroup.clear();
01635         }
01636     }
01637 
01638     if (isLink)
01639         d->pmode = PermissionsOnlyLinks;
01640     else if (isDir)
01641         d->pmode = PermissionsOnlyDirs;
01642     else if (hasDir)
01643         d->pmode = PermissionsMixed;
01644     else
01645         d->pmode = PermissionsOnlyFiles;
01646 
01647     // keep only what's not in the common permissions
01648     d->partialPermissions = d->partialPermissions & ~d->permissions;
01649 
01650     bool isMyFile = false;
01651 
01652     if (isLocal && !d->strOwner.isEmpty()) { // local files, and all owned by the same person
01653         struct passwd *myself = getpwuid( geteuid() );
01654         if ( myself != 0L )
01655         {
01656             isMyFile = (d->strOwner == QString::fromLocal8Bit(myself->pw_name));
01657         } else
01658             kWarning() << "I don't exist ?! geteuid=" << geteuid();
01659     } else {
01660         //We don't know, for remote files, if they are ours or not.
01661         //So we let the user change permissions, and
01662         //KIO::chmod will tell, if he had no right to do it.
01663         isMyFile = true;
01664     }
01665 
01666     d->canChangePermissions = (isMyFile || IamRoot) && (!isLink);
01667 
01668 
01669     // create GUI
01670 
01671     d->m_frame = new QFrame();
01672     properties->addPage( d->m_frame, i18n("&Permissions") );
01673 
01674     QBoxLayout *box = new QVBoxLayout( d->m_frame );
01675     box->setMargin( 0 );
01676 
01677     QWidget *l;
01678     QLabel *lbl;
01679     QGroupBox *gb;
01680     QGridLayout *gl;
01681     QPushButton* pbAdvancedPerm = 0;
01682 
01683     /* Group: Access Permissions */
01684     gb = new QGroupBox ( i18n("Access Permissions"), d->m_frame );
01685     box->addWidget (gb);
01686 
01687     gl = new QGridLayout (gb);
01688     gl->setColumnStretch(1, 1);
01689 
01690     l = d->explanationLabel = new QLabel( "", gb );
01691     if (isLink)
01692         d->explanationLabel->setText(i18np("This file is a link and does not have permissions.",
01693                                            "All files are links and do not have permissions.",
01694                                            properties->items().count()));
01695     else if (!d->canChangePermissions)
01696         d->explanationLabel->setText(i18n("Only the owner can change permissions."));
01697     gl->addWidget(l, 0, 0, 1, 2);
01698 
01699     lbl = new QLabel( i18n("O&wner:"), gb);
01700     gl->addWidget(lbl, 1, 0, Qt::AlignRight);
01701     l = d->ownerPermCombo = new KComboBox(gb);
01702     lbl->setBuddy(l);
01703     gl->addWidget(l, 1, 1);
01704     connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
01705     l->setWhatsThis(i18n("Specifies the actions that the owner is allowed to do."));
01706 
01707     lbl = new QLabel( i18n("Gro&up:"), gb);
01708     gl->addWidget(lbl, 2, 0, Qt::AlignRight);
01709     l = d->groupPermCombo = new KComboBox(gb);
01710     lbl->setBuddy(l);
01711     gl->addWidget(l, 2, 1);
01712     connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
01713     l->setWhatsThis(i18n("Specifies the actions that the members of the group are allowed to do."));
01714 
01715     lbl = new QLabel( i18n("O&thers:"), gb);
01716     gl->addWidget(lbl, 3, 0, Qt::AlignRight);
01717     l = d->othersPermCombo = new KComboBox(gb);
01718     lbl->setBuddy(l);
01719     gl->addWidget(l, 3, 1);
01720     connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
01721     l->setWhatsThis(i18n("Specifies the actions that all users, who are neither "
01722                          "owner nor in the group, are allowed to do."));
01723 
01724     if (!isLink) {
01725         l = d->extraCheckbox = new QCheckBox(hasDir ?
01726                                              i18n("Only own&er can rename and delete folder content") :
01727                                              i18n("Is &executable"),
01728                                              gb );
01729         connect( d->extraCheckbox, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
01730         gl->addWidget(l, 4, 1);
01731         l->setWhatsThis(hasDir ? i18n("Enable this option to allow only the folder's owner to "
01732                                       "delete or rename the contained files and folders. Other "
01733                                       "users can only add new files, which requires the 'Modify "
01734                                       "Content' permission.")
01735                     : i18n("Enable this option to mark the file as executable. This only makes "
01736                            "sense for programs and scripts. It is required when you want to "
01737                            "execute them."));
01738 
01739         QLayoutItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
01740         gl->addItem(spacer, 5, 0, 1, 3);
01741 
01742         pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb);
01743         gl->addWidget(pbAdvancedPerm, 6, 0, 1, 2, Qt::AlignRight);
01744         connect(pbAdvancedPerm, SIGNAL( clicked() ), this, SLOT( slotShowAdvancedPermissions() ));
01745     }
01746     else
01747         d->extraCheckbox = 0;
01748 
01749 
01750     /**** Group: Ownership ****/
01751     gb = new QGroupBox ( i18n("Ownership"), d->m_frame );
01752     box->addWidget (gb);
01753 
01754     gl = new QGridLayout (gb);
01755     gl->addItem(new QSpacerItem(0, 10), 0, 0);
01756 
01757     /*** Set Owner ***/
01758     l = new QLabel( i18n("User:"), gb );
01759     gl->addWidget (l, 1, 0, Qt::AlignRight);
01760 
01761     /* GJ: Don't autocomplete more than 1000 users. This is a kind of random
01762    * value. Huge sites having 10.000+ user have a fair chance of using NIS,
01763    * (possibly) making this unacceptably slow.
01764    * OTOH, it is nice to offer this functionality for the standard user.
01765    */
01766     int i, maxEntries = 1000;
01767     struct passwd *user;
01768 
01769     /* File owner: For root, offer a KLineEdit with autocompletion.
01770    * For a user, who can never chown() a file, offer a QLabel.
01771    */
01772     if (IamRoot && isLocal)
01773     {
01774         d->usrEdit = new KLineEdit( gb );
01775         KCompletion *kcom = d->usrEdit->completionObject();
01776         kcom->setOrder(KCompletion::Sorted);
01777         setpwent();
01778         for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); ++i)
01779             kcom->addItem(QString::fromLatin1(user->pw_name));
01780         endpwent();
01781         d->usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto :
01782                                       KGlobalSettings::CompletionNone);
01783         d->usrEdit->setText(d->strOwner);
01784         gl->addWidget(d->usrEdit, 1, 1);
01785         connect( d->usrEdit, SIGNAL( textChanged( const QString & ) ),
01786                  this, SIGNAL( changed() ) );
01787     }
01788     else
01789     {
01790         l = new QLabel(d->strOwner, gb);
01791         gl->addWidget(l, 1, 1);
01792     }
01793 
01794     /*** Set Group ***/
01795 
01796     QStringList groupList;
01797     QByteArray strUser;
01798     user = getpwuid(geteuid());
01799     if (user != 0L)
01800         strUser = user->pw_name;
01801 
01802 #ifdef HAVE_GETGROUPLIST
01803     // pick the groups to which the user belongs
01804     int groupCount = 0;
01805 #ifdef Q_OS_MAC
01806     QVarLengthArray<int> groups;
01807 #else
01808     QVarLengthArray<gid_t> groups;
01809 #endif
01810     if (getgrouplist(strUser, user->pw_gid, NULL, &groupCount) < 0) {
01811         groups.resize(groupCount);
01812         if (groups.data())
01813             getgrouplist(strUser, user->pw_gid, groups.data(), &groupCount);
01814         else
01815             groupCount = 0;
01816     }
01817 
01818     for (i = 0; i < groupCount; i++) {
01819         struct group *mygroup = getgrgid(groups[i]);
01820         if (mygroup)
01821             groupList += QString::fromLocal8Bit(mygroup->gr_name);
01822     }
01823 #endif // HAVE_GETGROUPLIST
01824 
01825     bool isMyGroup = groupList.contains(d->strGroup);
01826 
01827     /* add the group the file currently belongs to ..
01828    * .. if it is not there already
01829    */
01830     if (!isMyGroup)
01831         groupList += d->strGroup;
01832 
01833     l = new QLabel( i18n("Group:"), gb );
01834     gl->addWidget (l, 2, 0, Qt::AlignRight);
01835 
01836     /* Set group: if possible to change:
01837    * - Offer a KLineEdit for root, since he can change to any group.
01838    * - Offer a KComboBox for a normal user, since he can change to a fixed
01839    *   (small) set of groups only.
01840    * If not changeable: offer a QLabel.
01841    */
01842     if (IamRoot && isLocal)
01843     {
01844         d->grpEdit = new KLineEdit(gb);
01845         KCompletion *kcom = new KCompletion;
01846         kcom->setItems(groupList);
01847         d->grpEdit->setCompletionObject(kcom, true);
01848         d->grpEdit->setAutoDeleteCompletionObject( true );
01849         d->grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
01850         d->grpEdit->setText(d->strGroup);
01851         gl->addWidget(d->grpEdit, 2, 1);
01852         connect( d->grpEdit, SIGNAL( textChanged( const QString & ) ),
01853                  this, SIGNAL( changed() ) );
01854     }
01855     else if ((groupList.count() > 1) && isMyFile && isLocal)
01856     {
01857         d->grpCombo = new KComboBox(gb);
01858         d->grpCombo->setObjectName(QLatin1String("combogrouplist"));
01859         d->grpCombo->addItems(groupList);
01860         d->grpCombo->setCurrentIndex(groupList.indexOf(d->strGroup));
01861         gl->addWidget(d->grpCombo, 2, 1);
01862         connect( d->grpCombo, SIGNAL( activated( int ) ),
01863                  this, SIGNAL( changed() ) );
01864     }
01865     else
01866     {
01867         l = new QLabel(d->strGroup, gb);
01868         gl->addWidget(l, 2, 1);
01869     }
01870 
01871     gl->setColumnStretch(2, 10);
01872 
01873     // "Apply recursive" checkbox
01874     if ( hasDir && !isLink && !isTrash  )
01875     {
01876         d->cbRecursive = new QCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame );
01877         connect( d->cbRecursive, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
01878         box->addWidget( d->cbRecursive );
01879     }
01880 
01881     updateAccessControls();
01882 
01883 
01884     if ( isTrash )
01885     {
01886         //don't allow to change properties for file into trash
01887         enableAccessControls(false);
01888         if ( pbAdvancedPerm)
01889             pbAdvancedPerm->setEnabled(false);
01890     }
01891 
01892     box->addStretch (10);
01893 }
01894 
01895 #ifdef HAVE_POSIX_ACL
01896 static bool fileSystemSupportsACL( const QByteArray& path )
01897 {
01898     bool fileSystemSupportsACLs = false;
01899 #ifdef Q_OS_FREEBSD
01900     struct statfs buf;
01901     fileSystemSupportsACLs = ( statfs( path.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS );
01902 #else
01903     fileSystemSupportsACLs =
01904             getxattr( path.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA;
01905 #endif
01906     return fileSystemSupportsACLs;
01907 }
01908 #endif
01909 
01910 
01911 void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() {
01912 
01913     bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed);
01914     KDialog dlg( properties );
01915     dlg.setModal( true );
01916     dlg.setCaption( i18n("Advanced Permissions") );
01917     dlg.setButtons( KDialog::Ok | KDialog::Cancel );
01918 
01919     QLabel *l, *cl[3];
01920     QGroupBox *gb;
01921     QGridLayout *gl;
01922 
01923     QWidget *mainw = new QWidget( &dlg );
01924     QVBoxLayout *vbox = new QVBoxLayout(mainw);
01925     // Group: Access Permissions
01926     gb = new QGroupBox ( i18n("Access Permissions"), mainw );
01927     vbox->addWidget(gb);
01928 
01929     gl = new QGridLayout (gb);
01930     gl->addItem(new QSpacerItem(0, 10), 0, 0);
01931 
01932     QVector<QWidget*> theNotSpecials;
01933 
01934     l = new QLabel(i18n("Class"), gb );
01935     gl->addWidget(l, 1, 0);
01936     theNotSpecials.append( l );
01937 
01938     if (isDir)
01939         l = new QLabel( i18n("Show\nEntries"), gb );
01940     else
01941         l = new QLabel( i18n("Read"), gb );
01942     gl->addWidget (l, 1, 1);
01943     theNotSpecials.append( l );
01944     QString readWhatsThis;
01945     if (isDir)
01946         readWhatsThis = i18n("This flag allows viewing the content of the folder.");
01947     else
01948         readWhatsThis = i18n("The Read flag allows viewing the content of the file.");
01949     l->setWhatsThis(readWhatsThis);
01950 
01951     if (isDir)
01952         l = new QLabel( i18n("Write\nEntries"), gb );
01953     else
01954         l = new QLabel( i18n("Write"), gb );
01955     gl->addWidget (l, 1, 2);
01956     theNotSpecials.append( l );
01957     QString writeWhatsThis;
01958     if (isDir)
01959         writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. "
01960                               "Note that deleting and renaming can be limited using the Sticky flag.");
01961     else
01962         writeWhatsThis = i18n("The Write flag allows modifying the content of the file.");
01963     l->setWhatsThis(writeWhatsThis);
01964 
01965     QString execWhatsThis;
01966     if (isDir) {
01967         l = new QLabel( i18nc("Enter folder", "Enter"), gb );
01968         execWhatsThis = i18n("Enable this flag to allow entering the folder.");
01969     }
01970     else {
01971         l = new QLabel( i18n("Exec"), gb );
01972         execWhatsThis = i18n("Enable this flag to allow executing the file as a program.");
01973     }
01974     l->setWhatsThis(execWhatsThis);
01975     theNotSpecials.append( l );
01976     // GJ: Add space between normal and special modes
01977     QSize size = l->sizeHint();
01978     size.setWidth(size.width() + 15);
01979     l->setFixedSize(size);
01980     gl->addWidget (l, 1, 3);
01981 
01982     l = new QLabel( i18n("Special"), gb );
01983     gl->addWidget(l, 1, 4, 1, 2);
01984     QString specialWhatsThis;
01985     if (isDir)
01986         specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact "
01987                                 "meaning of the flag can be seen in the right hand column.");
01988     else
01989         specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen "
01990                                 "in the right hand column.");
01991     l->setWhatsThis(specialWhatsThis);
01992 
01993     cl[0] = new QLabel( i18n("User"), gb );
01994     gl->addWidget (cl[0], 2, 0);
01995     theNotSpecials.append( cl[0] );
01996 
01997     cl[1] = new QLabel( i18n("Group"), gb );
01998     gl->addWidget (cl[1], 3, 0);
01999     theNotSpecials.append( cl[1] );
02000 
02001     cl[2] = new QLabel( i18n("Others"), gb );
02002     gl->addWidget (cl[2], 4, 0);
02003     theNotSpecials.append( cl[2] );
02004 
02005     l = new QLabel(i18n("Set UID"), gb);
02006     gl->addWidget(l, 2, 5);
02007     QString setUidWhatsThis;
02008     if (isDir)
02009         setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be "
02010                                "the owner of all new files.");
02011     else
02012         setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
02013                                "be executed with the permissions of the owner.");
02014     l->setWhatsThis(setUidWhatsThis);
02015 
02016     l = new QLabel(i18n("Set GID"), gb);
02017     gl->addWidget(l, 3, 5);
02018     QString setGidWhatsThis;
02019     if (isDir)
02020         setGidWhatsThis = i18n("If this flag is set, the group of this folder will be "
02021                                "set for all new files.");
02022     else
02023         setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
02024                                "be executed with the permissions of the group.");
02025     l->setWhatsThis(setGidWhatsThis);
02026 
02027     l = new QLabel(i18nc("File permission", "Sticky"), gb);
02028     gl->addWidget(l, 4, 5);
02029     QString stickyWhatsThis;
02030     if (isDir)
02031         stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner "
02032                                "and root can delete or rename files. Otherwise everybody "
02033                                "with write permissions can do this.");
02034     else
02035         stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may "
02036                                "be used on some systems");
02037     l->setWhatsThis(stickyWhatsThis);
02038 
02039     mode_t aPermissions, aPartialPermissions;
02040     mode_t dummy1, dummy2;
02041 
02042     if (!d->isIrregular) {
02043         switch (d->pmode) {
02044         case PermissionsOnlyFiles:
02045             getPermissionMasks(aPartialPermissions,
02046                                dummy1,
02047                                aPermissions,
02048                                dummy2);
02049             break;
02050         case PermissionsOnlyDirs:
02051         case PermissionsMixed:
02052             getPermissionMasks(dummy1,
02053                                aPartialPermissions,
02054                                dummy2,
02055                                aPermissions);
02056             break;
02057         case PermissionsOnlyLinks:
02058             aPermissions = UniRead | UniWrite | UniExec | UniSpecial;
02059             aPartialPermissions = 0;
02060             break;
02061         }
02062     }
02063     else {
02064         aPermissions = d->permissions;
02065         aPartialPermissions = d->partialPermissions;
02066     }
02067 
02068     // Draw Checkboxes
02069     QCheckBox *cba[3][4];
02070     for (int row = 0; row < 3 ; ++row) {
02071         for (int col = 0; col < 4; ++col) {
02072             QCheckBox *cb = new QCheckBox(gb);
02073             if ( col != 3 ) theNotSpecials.append( cb );
02074             cba[row][col] = cb;
02075             cb->setChecked(aPermissions & fperm[row][col]);
02076             if ( aPartialPermissions & fperm[row][col] )
02077             {
02078                 cb->setTristate();
02079                 cb->setCheckState(Qt::PartiallyChecked);
02080             }
02081             else if (d->cbRecursive && d->cbRecursive->isChecked())
02082                 cb->setTristate();
02083 
02084             cb->setEnabled( d->canChangePermissions );
02085             gl->addWidget (cb, row+2, col+1);
02086             switch(col) {
02087             case 0:
02088                 cb->setWhatsThis(readWhatsThis);
02089                 break;
02090             case 1:
02091                 cb->setWhatsThis(writeWhatsThis);
02092                 break;
02093             case 2:
02094                 cb->setWhatsThis(execWhatsThis);
02095                 break;
02096             case 3:
02097                 switch(row) {
02098                 case 0:
02099                     cb->setWhatsThis(setUidWhatsThis);
02100                     break;
02101                 case 1:
02102                     cb->setWhatsThis(setGidWhatsThis);
02103                     break;
02104                 case 2:
02105                     cb->setWhatsThis(stickyWhatsThis);
02106                     break;
02107                 }
02108                 break;
02109             }
02110         }
02111     }
02112     gl->setColumnStretch(6, 10);
02113 
02114 #ifdef HAVE_POSIX_ACL
02115     KACLEditWidget *extendedACLs = 0;
02116 
02117     // FIXME make it work with partial entries
02118     if ( properties->items().count() == 1 ) {
02119         QByteArray path = QFile::encodeName( properties->item().url().toLocalFile() );
02120         d->fileSystemSupportsACLs = fileSystemSupportsACL( path );
02121     }
02122     if ( d->fileSystemSupportsACLs  ) {
02123         std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) );
02124         extendedACLs = new KACLEditWidget( mainw );
02125         vbox->addWidget(extendedACLs);
02126         if ( d->extendedACL.isValid() && d->extendedACL.isExtended() )
02127             extendedACLs->setACL( d->extendedACL );
02128         else
02129             extendedACLs->setACL( KACL( aPermissions ) );
02130 
02131         if ( d->defaultACL.isValid() )
02132             extendedACLs->setDefaultACL( d->defaultACL );
02133 
02134         if ( properties->items().first().isDir() )
02135             extendedACLs->setAllowDefaults( true );
02136     }
02137 #endif
02138     dlg.setMainWidget( mainw );
02139     if (dlg.exec() != KDialog::Accepted)
02140         return;
02141 
02142     mode_t andPermissions = mode_t(~0);
02143     mode_t orPermissions = 0;
02144     for (int row = 0; row < 3; ++row)
02145         for (int col = 0; col < 4; ++col) {
02146         switch (cba[row][col]->checkState())
02147         {
02148         case Qt::Checked:
02149             orPermissions |= fperm[row][col];
02150             //fall through
02151         case Qt::Unchecked:
02152             andPermissions &= ~fperm[row][col];
02153             break;
02154         default: // NoChange
02155             break;
02156         }
02157     }
02158 
02159     d->isIrregular = false;
02160     const KFileItemList items = properties->items();
02161     KFileItemList::const_iterator it = items.begin();
02162     const KFileItemList::const_iterator kend = items.end();
02163     for ( ; it != kend; ++it ) {
02164         if (isIrregular(((*it).permissions() & andPermissions) | orPermissions,
02165                         (*it).isDir(), (*it).isLink())) {
02166             d->isIrregular = true;
02167             break;
02168         }
02169     }
02170 
02171     d->permissions = orPermissions;
02172     d->partialPermissions = andPermissions;
02173 
02174 #ifdef HAVE_POSIX_ACL
02175     // override with the acls, if present
02176     if ( extendedACLs ) {
02177         d->extendedACL = extendedACLs->getACL();
02178         d->defaultACL = extendedACLs->getDefaultACL();
02179         d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid();
02180         d->permissions = d->extendedACL.basePermissions();
02181         d->permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX );
02182     }
02183 #endif
02184 
02185     updateAccessControls();
02186     emit changed();
02187 }
02188 
02189 // QString KFilePermissionsPropsPlugin::tabName () const
02190 // {
02191 //   return i18n ("&Permissions");
02192 // }
02193 
02194 KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin()
02195 {
02196     delete d;
02197 }
02198 
02199 bool KFilePermissionsPropsPlugin::supports( const KFileItemList& /*_items*/ )
02200 {
02201     return true;
02202 }
02203 
02204 // sets a combo box in the Access Control frame
02205 void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target,
02206                                                   mode_t permissions, mode_t partial) {
02207     combo->clear();
02208     if (d->isIrregular) //#176876
02209         return;
02210 
02211     if (d->pmode == PermissionsOnlyLinks) {
02212         combo->addItem(i18n("Link"));
02213         combo->setCurrentIndex(0);
02214         return;
02215     }
02216 
02217     mode_t tMask = permissionsMasks[target];
02218     int textIndex;
02219     for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++) {
02220         if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite)))
02221             break;
02222     }
02223     Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar
02224 
02225     for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++)
02226         combo->addItem(i18n(permissionsTexts[(int)d->pmode][i]));
02227 
02228     if (partial & tMask & ~UniExec) {
02229         combo->addItem(i18n("Varying (No Change)"));
02230         combo->setCurrentIndex(3);
02231     }
02232     else {
02233         combo->setCurrentIndex(textIndex);
02234     }
02235 }
02236 
02237 // permissions are irregular if they cant be displayed in a combo box.
02238 bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) {
02239     if (isLink)                             // links are always ok
02240         return false;
02241 
02242     mode_t p = permissions;
02243     if (p & (S_ISUID | S_ISGID))  // setuid/setgid -> irregular
02244         return true;
02245     if (isDir) {
02246         p &= ~S_ISVTX;          // ignore sticky on dirs
02247 
02248         // check supported flag combinations
02249         mode_t p0 = p & UniOwner;
02250         if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner))
02251             return true;
02252         p0 = p & UniGroup;
02253         if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup))
02254             return true;
02255         p0 = p & UniOthers;
02256         if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers))
02257             return true;
02258         return false;
02259     }
02260     if (p & S_ISVTX) // sticky on file -> irregular
02261         return true;
02262 
02263     // check supported flag combinations
02264     mode_t p0 = p & UniOwner;
02265     bool usrXPossible = !p0; // true if this file could be an executable
02266     if (p0 & S_IXUSR) {
02267         if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR)))
02268             return true;
02269         usrXPossible = true;
02270     }
02271     else if (p0 == S_IWUSR)
02272         return true;
02273 
02274     p0 = p & UniGroup;
02275     bool grpXPossible = !p0; // true if this file could be an executable
02276     if (p0 & S_IXGRP) {
02277         if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP)))
02278             return true;
02279         grpXPossible = true;
02280     }
02281     else if (p0 == S_IWGRP)
02282         return true;
02283     if (p0 == 0)
02284         grpXPossible = true;
02285 
02286     p0 = p & UniOthers;
02287     bool othXPossible = !p0; // true if this file could be an executable
02288     if (p0 & S_IXOTH) {
02289         if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH)))
02290             return true;
02291         othXPossible = true;
02292     }
02293     else if (p0 == S_IWOTH)
02294         return true;
02295 
02296     // check that there either all targets are executable-compatible, or none
02297     return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible);
02298 }
02299 
02300 // enables/disabled the widgets in the Access Control frame
02301 void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) {
02302     d->ownerPermCombo->setEnabled(enable);
02303     d->groupPermCombo->setEnabled(enable);
02304     d->othersPermCombo->setEnabled(enable);
02305     if (d->extraCheckbox)
02306         d->extraCheckbox->setEnabled(enable);
02307     if ( d->cbRecursive )
02308         d->cbRecursive->setEnabled(enable);
02309 }
02310 
02311 // updates all widgets in the Access Control frame
02312 void KFilePermissionsPropsPlugin::updateAccessControls() {
02313     setComboContent(d->ownerPermCombo, PermissionsOwner,
02314                     d->permissions, d->partialPermissions);
02315     setComboContent(d->groupPermCombo, PermissionsGroup,
02316                     d->permissions, d->partialPermissions);
02317     setComboContent(d->othersPermCombo, PermissionsOthers,
02318                     d->permissions, d->partialPermissions);
02319 
02320     switch(d->pmode) {
02321     case PermissionsOnlyLinks:
02322         enableAccessControls(false);
02323         break;
02324     case PermissionsOnlyFiles:
02325         enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
02326         if (d->canChangePermissions)
02327             d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
02328                                          i18np("This file uses advanced permissions",
02329                                                "These files use advanced permissions.",
02330                                                properties->items().count()) : "");
02331         if (d->partialPermissions & UniExec) {
02332             d->extraCheckbox->setTristate();
02333             d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
02334         }
02335         else {
02336             d->extraCheckbox->setTristate(false);
02337             d->extraCheckbox->setChecked(d->permissions & UniExec);
02338         }
02339         break;
02340     case PermissionsOnlyDirs:
02341         enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
02342         // if this is a dir, and we can change permissions, don't dis-allow
02343         // recursive, we can do that for ACL setting.
02344         if ( d->cbRecursive )
02345             d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular );
02346 
02347         if (d->canChangePermissions)
02348             d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
02349                                          i18np("This folder uses advanced permissions.",
02350                                                "These folders use advanced permissions.",
02351                                                properties->items().count()) : "");
02352         if (d->partialPermissions & S_ISVTX) {
02353             d->extraCheckbox->setTristate();
02354             d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
02355         }
02356         else {
02357             d->extraCheckbox->setTristate(false);
02358             d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
02359         }
02360         break;
02361     case PermissionsMixed:
02362         enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
02363         if (d->canChangePermissions)
02364             d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
02365                                          i18n("These files use advanced permissions.") : "");
02366         break;
02367         if (d->partialPermissions & S_ISVTX) {
02368             d->extraCheckbox->setTristate();
02369             d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
02370         }
02371         else {
02372             d->extraCheckbox->setTristate(false);
02373             d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
02374         }
02375         break;
02376     }
02377 }
02378 
02379 // gets masks for files and dirs from the Access Control frame widgets
02380 void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions,
02381                                                      mode_t &andDirPermissions,
02382                                                      mode_t &orFilePermissions,
02383                                                      mode_t &orDirPermissions) {
02384     andFilePermissions = mode_t(~UniSpecial);
02385     andDirPermissions = mode_t(~(S_ISUID|S_ISGID));
02386     orFilePermissions = 0;
02387     orDirPermissions = 0;
02388     if (d->isIrregular)
02389         return;
02390 
02391     mode_t m = standardPermissions[d->ownerPermCombo->currentIndex()];
02392     if (m != (mode_t) -1) {
02393         orFilePermissions |= m & UniOwner;
02394         if ((m & UniOwner) &&
02395             ((d->pmode == PermissionsMixed) ||
02396              ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
02397             andFilePermissions &= ~(S_IRUSR | S_IWUSR);
02398         else {
02399             andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
02400             if ((m & S_IRUSR) && (d->extraCheckbox->checkState() == Qt::Checked))
02401                 orFilePermissions |= S_IXUSR;
02402         }
02403 
02404         orDirPermissions |= m & UniOwner;
02405         if (m & S_IRUSR)
02406             orDirPermissions |= S_IXUSR;
02407         andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
02408     }
02409 
02410     m = standardPermissions[d->groupPermCombo->currentIndex()];
02411     if (m != (mode_t) -1) {
02412         orFilePermissions |= m & UniGroup;
02413         if ((m & UniGroup) &&
02414             ((d->pmode == PermissionsMixed) ||
02415              ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
02416             andFilePermissions &= ~(S_IRGRP | S_IWGRP);
02417         else {
02418             andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
02419             if ((m & S_IRGRP) && (d->extraCheckbox->checkState() == Qt::Checked))
02420                 orFilePermissions |= S_IXGRP;
02421         }
02422 
02423         orDirPermissions |= m & UniGroup;
02424         if (m & S_IRGRP)
02425             orDirPermissions |= S_IXGRP;
02426         andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
02427     }
02428 
02429     m = d->othersPermCombo->currentIndex() >= 0 ? standardPermissions[d->othersPermCombo->currentIndex()] : (mode_t)-1;
02430     if (m != (mode_t) -1) {
02431         orFilePermissions |= m & UniOthers;
02432         if ((m & UniOthers) &&
02433             ((d->pmode == PermissionsMixed) ||
02434              ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
02435             andFilePermissions &= ~(S_IROTH | S_IWOTH);
02436         else {
02437             andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
02438             if ((m & S_IROTH) && (d->extraCheckbox->checkState() == Qt::Checked))
02439                 orFilePermissions |= S_IXOTH;
02440         }
02441 
02442         orDirPermissions |= m & UniOthers;
02443         if (m & S_IROTH)
02444             orDirPermissions |= S_IXOTH;
02445         andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
02446     }
02447 
02448     if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) &&
02449         (d->extraCheckbox->checkState() != Qt::PartiallyChecked)) {
02450         andDirPermissions &= ~S_ISVTX;
02451         if (d->extraCheckbox->checkState() == Qt::Checked)
02452             orDirPermissions |= S_ISVTX;
02453     }
02454 }
02455 
02456 void KFilePermissionsPropsPlugin::applyChanges()
02457 {
02458     mode_t orFilePermissions;
02459     mode_t orDirPermissions;
02460     mode_t andFilePermissions;
02461     mode_t andDirPermissions;
02462 
02463     if (!d->canChangePermissions)
02464         return;
02465 
02466     if (!d->isIrregular)
02467         getPermissionMasks(andFilePermissions,
02468                            andDirPermissions,
02469                            orFilePermissions,
02470                            orDirPermissions);
02471     else {
02472         orFilePermissions = d->permissions;
02473         andFilePermissions = d->partialPermissions;
02474         orDirPermissions = d->permissions;
02475         andDirPermissions = d->partialPermissions;
02476     }
02477 
02478     QString owner, group;
02479     if (d->usrEdit)
02480         owner = d->usrEdit->text();
02481     if (d->grpEdit)
02482         group = d->grpEdit->text();
02483     else if (d->grpCombo)
02484         group = d->grpCombo->currentText();
02485 
02486     if (owner == d->strOwner)
02487         owner.clear(); // no change
02488 
02489     if (group == d->strGroup)
02490         group.clear();
02491 
02492     bool recursive = d->cbRecursive && d->cbRecursive->isChecked();
02493     bool permissionChange = false;
02494 
02495     KFileItemList files, dirs;
02496     const KFileItemList items = properties->items();
02497     KFileItemList::const_iterator it = items.begin();
02498     const KFileItemList::const_iterator kend = items.end();
02499     for ( ; it != kend; ++it ) {
02500         if ((*it).isDir()) {
02501             dirs.append(*it);
02502             if ((*it).permissions() != (((*it).permissions() & andDirPermissions) | orDirPermissions))
02503                 permissionChange = true;
02504         }
02505         else if ((*it).isFile()) {
02506             files.append(*it);
02507             if ((*it).permissions() != (((*it).permissions() & andFilePermissions) | orFilePermissions))
02508                 permissionChange = true;
02509         }
02510     }
02511 
02512     const bool ACLChange = ( d->extendedACL !=  properties->item().ACL() );
02513     const bool defaultACLChange = ( d->defaultACL != properties->item().defaultACL() );
02514 
02515     if (owner.isEmpty() && group.isEmpty() && !recursive
02516         && !permissionChange && !ACLChange && !defaultACLChange)
02517         return;
02518 
02519     KIO::Job * job;
02520     if (files.count() > 0) {
02521         job = KIO::chmod( files, orFilePermissions, ~andFilePermissions,
02522                           owner, group, false );
02523         if ( ACLChange && d->fileSystemSupportsACLs )
02524             job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
02525         if ( defaultACLChange && d->fileSystemSupportsACLs )
02526             job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
02527 
02528         connect( job, SIGNAL( result( KJob * ) ),
02529                  SLOT( slotChmodResult( KJob * ) ) );
02530         QEventLoop eventLoop;
02531         connect(this, SIGNAL(leaveModality()),
02532                 &eventLoop, SLOT(quit()));
02533         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
02534     }
02535     if (dirs.count() > 0) {
02536         job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
02537                           owner, group, recursive );
02538         if ( ACLChange && d->fileSystemSupportsACLs )
02539             job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
02540         if ( defaultACLChange && d->fileSystemSupportsACLs )
02541             job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
02542 
02543         connect( job, SIGNAL( result( KJob * ) ),
02544                  SLOT( slotChmodResult( KJob * ) ) );
02545         QEventLoop eventLoop;
02546         connect(this, SIGNAL(leaveModality()),
02547                 &eventLoop, SLOT(quit()));
02548         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
02549     }
02550 }
02551 
02552 void KFilePermissionsPropsPlugin::slotChmodResult( KJob * job )
02553 {
02554     kDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult";
02555     if (job->error())
02556         job->uiDelegate()->showErrorMessage();
02557     // allow apply() to return
02558     emit leaveModality();
02559 }
02560 
02561 
02562 
02563 
02564 class KUrlPropsPlugin::KUrlPropsPluginPrivate
02565 {
02566 public:
02567     KUrlPropsPluginPrivate()
02568     {
02569     }
02570     ~KUrlPropsPluginPrivate()
02571     {
02572     }
02573 
02574     QFrame *m_frame;
02575     KUrlRequester *URLEdit;
02576     QString URLStr;
02577 };
02578 
02579 KUrlPropsPlugin::KUrlPropsPlugin( KPropertiesDialog *_props )
02580     : KPropertiesDialogPlugin( _props ),d(new KUrlPropsPluginPrivate)
02581 {
02582     d->m_frame = new QFrame();
02583     properties->addPage(d->m_frame, i18n("U&RL"));
02584     QVBoxLayout *layout = new QVBoxLayout(d->m_frame);
02585     layout->setMargin(0);
02586 
02587     QLabel *l;
02588     l = new QLabel( d->m_frame );
02589     l->setObjectName( QLatin1String( "Label_1" ) );
02590     l->setText( i18n("URL:") );
02591     layout->addWidget(l, Qt::AlignRight);
02592 
02593     d->URLEdit = new KUrlRequester( d->m_frame );
02594     layout->addWidget(d->URLEdit);
02595 
02596     KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
02597     if (url.isLocalFile()) {
02598         QString path = url.toLocalFile();
02599 
02600         QFile f( path );
02601         if ( !f.open( QIODevice::ReadOnly ) ) {
02602             return;
02603         }
02604         f.close();
02605 
02606         KDesktopFile config( path );
02607         const KConfigGroup dg = config.desktopGroup();
02608         d->URLStr = dg.readPathEntry( "URL", QString() );
02609 
02610         if (!d->URLStr.isEmpty()) {
02611             d->URLEdit->setUrl( KUrl(d->URLStr) );
02612         }
02613     }
02614 
02615     connect( d->URLEdit, SIGNAL( textChanged( const QString & ) ),
02616              this, SIGNAL( changed() ) );
02617 
02618     layout->addStretch (1);
02619 }
02620 
02621 KUrlPropsPlugin::~KUrlPropsPlugin()
02622 {
02623     delete d;
02624 }
02625 
02626 // QString KUrlPropsPlugin::tabName () const
02627 // {
02628 //   return i18n ("U&RL");
02629 // }
02630 
02631 bool KUrlPropsPlugin::supports( const KFileItemList& _items )
02632 {
02633     if ( _items.count() != 1 )
02634         return false;
02635     const KFileItem item = _items.first();
02636     // check if desktop file
02637     if (!item.isDesktopFile())
02638         return false;
02639 
02640     // open file and check type
02641     bool isLocal;
02642     KUrl url = item.mostLocalUrl(isLocal);
02643     if (!isLocal) {
02644         return false;
02645     }
02646 
02647     KDesktopFile config( url.path() );
02648     return config.hasLinkType();
02649 }
02650 
02651 void KUrlPropsPlugin::applyChanges()
02652 {
02653     KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
02654     if (!url.isLocalFile()) {
02655         //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
02656         return;
02657     }
02658 
02659     QString path = url.path();
02660 
02661     QFile f( path );
02662     if ( !f.open( QIODevice::ReadWrite ) ) {
02663         KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
02664                                     "sufficient access to write to <b>%1</b>.</qt>", path));
02665         return;
02666     }
02667     f.close();
02668 
02669     KDesktopFile config( path );
02670     KConfigGroup dg = config.desktopGroup();
02671     dg.writeEntry( "Type", QString::fromLatin1("Link"));
02672     dg.writePathEntry( "URL", d->URLEdit->url().url() );
02673     // Users can't create a Link .desktop file with a Name field,
02674     // but distributions can. Update the Name field in that case.
02675     if ( dg.hasKey("Name") )
02676     {
02677         QString nameStr = nameFromFileName(properties->kurl().fileName());
02678         dg.writeEntry( "Name", nameStr );
02679         dg.writeEntry( "Name", nameStr, KConfigBase::Persistent|KConfigBase::Localized );
02680 
02681     }
02682 }
02683 
02684 
02685 /* ----------------------------------------------------
02686  *
02687  * KDevicePropsPlugin
02688  *
02689  * -------------------------------------------------- */
02690 
02691 class KDevicePropsPlugin::KDevicePropsPluginPrivate
02692 {
02693 public:
02694     KDevicePropsPluginPrivate()
02695     {
02696     }
02697     ~KDevicePropsPluginPrivate()
02698     {
02699     }
02700 
02701     bool isMounted() const {
02702         const QString dev = device->currentText();
02703         return !dev.isEmpty() && KMountPoint::currentMountPoints().findByDevice(dev);
02704     }
02705 
02706     QFrame *m_frame;
02707     QStringList mountpointlist;
02708     QLabel *m_freeSpaceText;
02709     QLabel *m_freeSpaceLabel;
02710     QProgressBar *m_freeSpaceBar;
02711 
02712     KComboBox* device;
02713     QLabel* mountpoint;
02714     QCheckBox* readonly;
02715 
02716     QStringList m_devicelist;
02717 };
02718 
02719 KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KDevicePropsPluginPrivate)
02720 {
02721     d->m_frame = new QFrame();
02722     properties->addPage(d->m_frame, i18n("De&vice"));
02723 
02724     QStringList devices;
02725     const KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
02726 
02727     for(KMountPoint::List::ConstIterator it = mountPoints.begin();
02728     it != mountPoints.end(); ++it)
02729     {
02730         const KMountPoint::Ptr mp = (*it);
02731         QString mountPoint = mp->mountPoint();
02732         QString device = mp->mountedFrom();
02733         kDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType();
02734 
02735         if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty()
02736             && device != "none")
02737             {
02738             devices.append( device + QString::fromLatin1(" (")
02739                             + mountPoint + QString::fromLatin1(")") );
02740             d->m_devicelist.append(device);
02741             d->mountpointlist.append(mountPoint);
02742         }
02743     }
02744 
02745     QGridLayout *layout = new QGridLayout( d->m_frame );
02746 
02747     layout->setMargin(0);
02748     layout->setColumnStretch(1, 1);
02749 
02750     QLabel* label;
02751     label = new QLabel( d->m_frame );
02752     label->setText( devices.count() == 0 ?
02753                     i18n("Device (/dev/fd0):") : // old style
02754                     i18n("Device:") ); // new style (combobox)
02755     layout->addWidget(label, 0, 0, Qt::AlignRight);
02756 
02757     d->device = new KComboBox( d->m_frame );
02758     d->device->setObjectName( QLatin1String( "ComboBox_device" ) );
02759     d->device->setEditable( true );
02760     d->device->addItems( devices );
02761     layout->addWidget(d->device, 0, 1);
02762     connect( d->device, SIGNAL( activated( int ) ),
02763              this, SLOT( slotActivated( int ) ) );
02764 
02765     d->readonly = new QCheckBox( d->m_frame );
02766     d->readonly->setObjectName( QLatin1String( "CheckBox_readonly" ) );
02767     d->readonly->setText(  i18n("Read only") );
02768     layout->addWidget(d->readonly, 1, 1);
02769 
02770     label = new QLabel( d->m_frame );
02771     label->setText( i18n("File system:") );
02772     layout->addWidget(label, 2, 0, Qt::AlignRight);
02773 
02774     QLabel *fileSystem = new QLabel( d->m_frame );
02775     layout->addWidget(fileSystem, 2, 1);
02776 
02777     label = new QLabel( d->m_frame );
02778     label->setText( devices.count()==0 ?
02779                     i18n("Mount point (/mnt/floppy):") : // old style
02780                     i18n("Mount point:")); // new style (combobox)
02781     layout->addWidget(label, 3, 0, Qt::AlignRight);
02782 
02783     d->mountpoint = new QLabel( d->m_frame );
02784     d->mountpoint->setObjectName( QLatin1String( "LineEdit_mountpoint" ) );
02785 
02786     layout->addWidget(d->mountpoint, 3, 1);
02787 
02788     // show disk free
02789     d->m_freeSpaceText = new QLabel(i18n("Device usage:"), d->m_frame );
02790     layout->addWidget(d->m_freeSpaceText, 4, 0, Qt::AlignRight);
02791 
02792     d->m_freeSpaceLabel = new QLabel( d->m_frame );
02793     layout->addWidget( d->m_freeSpaceLabel, 4, 1 );
02794 
02795     d->m_freeSpaceBar = new QProgressBar( d->m_frame );
02796     d->m_freeSpaceBar->setObjectName( "freeSpaceBar" );
02797     layout->addWidget(d->m_freeSpaceBar, 5, 0, 1, 2);
02798 
02799     // we show it in the slot when we know the values
02800     d->m_freeSpaceText->hide();
02801     d->m_freeSpaceLabel->hide();
02802     d->m_freeSpaceBar->hide();
02803 
02804     KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame);
02805     layout->addWidget(sep, 6, 0, 1, 2);
02806 
02807     layout->setRowStretch(7, 1);
02808 
02809     KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
02810     if (!url.isLocalFile()) {
02811         return;
02812     }
02813     QString path = url.toLocalFile();
02814 
02815     QFile f( path );
02816     if ( !f.open( QIODevice::ReadOnly ) )
02817         return;
02818     f.close();
02819 
02820     const KDesktopFile _config( path );
02821     const KConfigGroup config = _config.desktopGroup();
02822     QString deviceStr = config.readEntry( "Dev" );
02823     QString mountPointStr = config.readEntry( "MountPoint" );
02824     bool ro = config.readEntry( "ReadOnly", false );
02825 
02826     fileSystem->setText(config.readEntry("FSType"));
02827 
02828     d->device->setEditText( deviceStr );
02829     if ( !deviceStr.isEmpty() ) {
02830         // Set default options for this device (first matching entry)
02831         int index = d->m_devicelist.indexOf(deviceStr);
02832         if (index != -1)
02833         {
02834             //kDebug(250) << "found it" << index;
02835             slotActivated( index );
02836         }
02837     }
02838 
02839     if ( !mountPointStr.isEmpty() )
02840     {
02841         d->mountpoint->setText( mountPointStr );
02842         updateInfo();
02843     }
02844 
02845     d->readonly->setChecked( ro );
02846 
02847     connect( d->device, SIGNAL( activated( int ) ),
02848              this, SIGNAL( changed() ) );
02849     connect( d->device, SIGNAL( textChanged( const QString & ) ),
02850              this, SIGNAL( changed() ) );
02851     connect( d->readonly, SIGNAL( toggled( bool ) ),
02852              this, SIGNAL( changed() ) );
02853 
02854     connect( d->device, SIGNAL( textChanged( const QString & ) ),
02855              this, SLOT( slotDeviceChanged() ) );
02856 }
02857 
02858 KDevicePropsPlugin::~KDevicePropsPlugin()
02859 {
02860     delete d;
02861 }
02862 
02863 // QString KDevicePropsPlugin::tabName () const
02864 // {
02865 //   return i18n ("De&vice");
02866 // }
02867 
02868 void KDevicePropsPlugin::updateInfo()
02869 {
02870     // we show it in the slot when we know the values
02871     d->m_freeSpaceText->hide();
02872     d->m_freeSpaceLabel->hide();
02873     d->m_freeSpaceBar->hide();
02874 
02875     if (!d->mountpoint->text().isEmpty() && d->isMounted()) {
02876         KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( d->mountpoint->text() );
02877         slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
02878     }
02879 }
02880 
02881 void KDevicePropsPlugin::slotActivated( int index )
02882 {
02883     // index can be more than the number of known devices, when the user types
02884     // a "custom" device.
02885     if (index < d->m_devicelist.count()) {
02886         // Update mountpoint so that it matches the device that was selected in the combo
02887         d->device->setEditText(d->m_devicelist[index]);
02888         d->mountpoint->setText(d->mountpointlist[index]);
02889     }
02890 
02891     updateInfo();
02892 }
02893 
02894 void KDevicePropsPlugin::slotDeviceChanged()
02895 {
02896     // Update mountpoint so that it matches the typed device
02897     int index = d->m_devicelist.indexOf( d->device->currentText() );
02898     if ( index != -1 )
02899         d->mountpoint->setText( d->mountpointlist[index] );
02900     else
02901         d->mountpoint->setText( QString() );
02902 
02903     updateInfo();
02904 }
02905 
02906 void KDevicePropsPlugin::slotFoundMountPoint( const QString&,
02907                                               quint64 kibSize,
02908                                               quint64 /*kibUsed*/,
02909                                               quint64 kibAvail )
02910 {
02911     d->m_freeSpaceText->show();
02912     d->m_freeSpaceLabel->show();
02913 
02914     const int percUsed = kibSize != 0 ? (100 - (int)(100.0 * kibAvail / kibSize)) : 100;
02915 
02916     d->m_freeSpaceLabel->setText(
02917             i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
02918                   KIO::convertSizeFromKiB(kibAvail),
02919                   KIO::convertSizeFromKiB(kibSize),
02920                   percUsed ));
02921 
02922     d->m_freeSpaceBar->setRange(0, 100);
02923     d->m_freeSpaceBar->setValue(percUsed);
02924     d->m_freeSpaceBar->show();
02925 }
02926 
02927 bool KDevicePropsPlugin::supports( const KFileItemList& _items )
02928 {
02929     if ( _items.count() != 1 )
02930         return false;
02931     const KFileItem item = _items.first();
02932     // check if desktop file
02933     if (!item.isDesktopFile())
02934         return false;
02935 
02936     // open file and check type
02937     bool isLocal;
02938     KUrl url = item.mostLocalUrl(isLocal);
02939     if (!isLocal) {
02940         return false;
02941     }
02942 
02943     KDesktopFile config( url.path() );
02944     return config.hasDeviceType();
02945 }
02946 
02947 void KDevicePropsPlugin::applyChanges()
02948 {
02949     KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
02950     if ( !url.isLocalFile() )
02951         return;
02952     QString path = url.toLocalFile();
02953 
02954     QFile f( path );
02955     if ( !f.open( QIODevice::ReadWrite ) )
02956     {
02957         KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient "
02958                                     "access to write to <b>%1</b>.</qt>", path));
02959         return;
02960     }
02961     f.close();
02962 
02963     KDesktopFile _config( path );
02964     KConfigGroup config = _config.desktopGroup();
02965     config.writeEntry( "Type", QString::fromLatin1("FSDevice") );
02966 
02967     config.writeEntry( "Dev", d->device->currentText() );
02968     config.writeEntry( "MountPoint", d->mountpoint->text() );
02969 
02970     config.writeEntry( "ReadOnly", d->readonly->isChecked() );
02971 
02972     config.sync();
02973 }
02974 
02975 
02976 /* ----------------------------------------------------
02977  *
02978  * KDesktopPropsPlugin
02979  *
02980  * -------------------------------------------------- */
02981 
02982 class KDesktopPropsPlugin::KDesktopPropsPluginPrivate
02983 {
02984 public:
02985     KDesktopPropsPluginPrivate()
02986         : w( new Ui_KPropertiesDesktopBase )
02987         , m_frame( new QFrame() )
02988     {
02989     }
02990     ~KDesktopPropsPluginPrivate()
02991     {
02992         delete w;
02993     }
02994     Ui_KPropertiesDesktopBase* w;
02995     QWidget *m_frame;
02996 
02997     QString m_origCommandStr;
02998     QString m_terminalOptionStr;
02999     QString m_suidUserStr;
03000     QString m_dbusStartupType;
03001     QString m_dbusServiceName;
03002     bool m_terminalBool;
03003     bool m_suidBool;
03004     bool m_startupBool;
03005     bool m_systrayBool;
03006 };
03007 
03008 KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props )
03009     : KPropertiesDialogPlugin( _props ), d( new KDesktopPropsPluginPrivate )
03010 {
03011     d->w->setupUi(d->m_frame);
03012 
03013     properties->addPage(d->m_frame, i18n("&Application"));
03014 
03015     bool bKDesktopMode = properties->kurl().protocol() == QLatin1String("desktop") ||
03016                     properties->currentDir().protocol() == QLatin1String("desktop");
03017 
03018     if (bKDesktopMode)
03019     {
03020         // Hide Name entry
03021         d->w->nameEdit->hide();
03022         d->w->nameLabel->hide();
03023     }
03024 
03025     d->w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
03026     d->w->pathEdit->lineEdit()->setAcceptDrops(false);
03027 
03028     connect( d->w->nameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03029     connect( d->w->genNameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03030     connect( d->w->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03031     connect( d->w->commandEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03032     connect( d->w->pathEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03033 
03034     connect( d->w->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
03035     connect( d->w->addFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotAddFiletype() ) );
03036     connect( d->w->delFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotDelFiletype() ) );
03037     connect( d->w->advancedButton, SIGNAL( clicked() ), this, SLOT( slotAdvanced() ) );
03038 
03039     // now populate the page
03040 
03041     KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
03042     if (!url.isLocalFile()) {
03043         return;
03044     }
03045     QString path = url.toLocalFile();
03046 
03047     QFile f( path );
03048     if ( !f.open( QIODevice::ReadOnly ) )
03049         return;
03050     f.close();
03051 
03052     KDesktopFile  _config( path );
03053     KConfigGroup config = _config.desktopGroup();
03054     QString nameStr = _config.readName();
03055     QString genNameStr = _config.readGenericName();
03056     QString commentStr = _config.readComment();
03057     QString commandStr = config.readEntry( "Exec", QString() );
03058     if (commandStr.startsWith(QLatin1String("ksystraycmd ")))
03059     {
03060         commandStr.remove(0, 12);
03061         d->m_systrayBool = true;
03062     }
03063     else
03064         d->m_systrayBool = false;
03065 
03066     d->m_origCommandStr = commandStr;
03067     QString pathStr = config.readEntry( "Path", QString() ); // not readPathEntry, see kservice.cpp
03068     d->m_terminalBool = config.readEntry( "Terminal", false );
03069     d->m_terminalOptionStr = config.readEntry( "TerminalOptions" );
03070     d->m_suidBool = config.readEntry( "X-KDE-SubstituteUID", false );
03071     d->m_suidUserStr = config.readEntry( "X-KDE-Username" );
03072     if( config.hasKey( "StartupNotify" ))
03073         d->m_startupBool = config.readEntry( "StartupNotify", true );
03074     else
03075         d->m_startupBool = config.readEntry( "X-KDE-StartupNotify", true );
03076     d->m_dbusStartupType = config.readEntry("X-DBUS-StartupType").toLower();
03077     // ### should there be a GUI for this setting?
03078     // At least we're copying it over to the local file, to avoid side effects (#157853)
03079     d->m_dbusServiceName = config.readEntry("X-DBUS-ServiceName");
03080 
03081     const QStringList mimeTypes = config.readXdgListEntry( "MimeType" );
03082 
03083     if ( nameStr.isEmpty() || bKDesktopMode ) {
03084         // We'll use the file name if no name is specified
03085         // because we _need_ a Name for a valid file.
03086         // But let's do it in apply, not here, so that we pick up the right name.
03087         setDirty();
03088     }
03089     if ( !bKDesktopMode )
03090         d->w->nameEdit->setText(nameStr);
03091 
03092     d->w->genNameEdit->setText( genNameStr );
03093     d->w->commentEdit->setText( commentStr );
03094     d->w->commandEdit->setText( commandStr );
03095     d->w->pathEdit->lineEdit()->setText( pathStr );
03096 
03097     // was: d->w->filetypeList->setFullWidth(true);
03098     //  d->w->filetypeList->header()->setStretchEnabled(true, d->w->filetypeList->columns()-1);
03099 
03100     KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
03101     for(QStringList::ConstIterator it = mimeTypes.begin();
03102     it != mimeTypes.end(); )
03103     {
03104         KMimeType::Ptr p = KMimeType::mimeType(*it, KMimeType::ResolveAliases);
03105         ++it;
03106         QString preference;
03107         if (it != mimeTypes.end())
03108         {
03109             bool numeric;
03110             (*it).toInt(&numeric);
03111             if (numeric)
03112             {
03113                 preference = *it;
03114                 ++it;
03115             }
03116         }
03117         if (p)
03118         {
03119             QTreeWidgetItem *item = new QTreeWidgetItem();
03120             item->setText(0, p->name());
03121             item->setText(1, p->comment());
03122             item->setText(2, preference);
03123             d->w->filetypeList->addTopLevelItem(item);
03124         }
03125     }
03126     d->w->filetypeList->resizeColumnToContents(0);
03127 
03128 }
03129 
03130 KDesktopPropsPlugin::~KDesktopPropsPlugin()
03131 {
03132     delete d;
03133 }
03134 
03135 void KDesktopPropsPlugin::slotAddFiletype()
03136 {
03137     KMimeTypeChooserDialog dlg( i18n("Add File Type for %1", properties->kurl().fileName()),
03138                                 i18n("Select one or more file types to add:"),
03139                                 QStringList(), // no preselected mimetypes
03140                                 QString(),
03141                                 QStringList(),
03142                                 KMimeTypeChooser::Comments|KMimeTypeChooser::Patterns,
03143                                 d->m_frame );
03144 
03145     if (dlg.exec() == KDialog::Accepted)
03146     {
03147         foreach(const QString &mimetype, dlg.chooser()->mimeTypes())
03148         {
03149             KMimeType::Ptr p = KMimeType::mimeType(mimetype);
03150             if (!p)
03151                 continue;
03152 
03153             bool found = false;
03154             int count = d->w->filetypeList->topLevelItemCount();
03155             for (int i = 0; !found && i < count; ++i) {
03156                 if (d->w->filetypeList->topLevelItem(i)->text(0) == mimetype) {
03157                     found = true;
03158                 }
03159             }
03160             if (!found) {
03161                 QTreeWidgetItem *item = new QTreeWidgetItem();
03162                 item->setText(0, p->name());
03163                 item->setText(1, p->comment());
03164                 d->w->filetypeList->addTopLevelItem(item);
03165             }
03166             d->w->filetypeList->resizeColumnToContents(0);
03167         }
03168     }
03169     emit changed();
03170 }
03171 
03172 void KDesktopPropsPlugin::slotDelFiletype()
03173 {
03174     QTreeWidgetItem *cur = d->w->filetypeList->currentItem();
03175     if (cur) {
03176         delete cur;
03177         emit changed();
03178     }
03179 }
03180 
03181 void KDesktopPropsPlugin::checkCommandChanged()
03182 {
03183     if (KRun::binaryName(d->w->commandEdit->text(), true) !=
03184         KRun::binaryName(d->m_origCommandStr, true))
03185     {
03186         d->m_origCommandStr = d->w->commandEdit->text();
03187         d->m_dbusStartupType.clear(); // Reset
03188         d->m_dbusServiceName.clear();
03189     }
03190 }
03191 
03192 void KDesktopPropsPlugin::applyChanges()
03193 {
03194     kDebug(250) << "KDesktopPropsPlugin::applyChanges";
03195 
03196     KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
03197     if (!url.isLocalFile()) {
03198         //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
03199         return;
03200     }
03201     QString path = url.toLocalFile();
03202 
03203     QFile f( path );
03204 
03205     if ( !f.open( QIODevice::ReadWrite ) ) {
03206         KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
03207                                     "sufficient access to write to <b>%1</b>.</qt>", path));
03208         return;
03209     }
03210     f.close();
03211 
03212     // If the command is changed we reset certain settings that are strongly
03213     // coupled to the command.
03214     checkCommandChanged();
03215 
03216     KDesktopFile _config( path );
03217     KConfigGroup config = _config.desktopGroup();
03218     config.writeEntry( "Type", QString::fromLatin1("Application"));
03219     config.writeEntry( "Comment", d->w->commentEdit->text() );
03220     config.writeEntry( "Comment", d->w->commentEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
03221     config.writeEntry( "GenericName", d->w->genNameEdit->text() );
03222     config.writeEntry( "GenericName", d->w->genNameEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
03223 
03224     if (d->m_systrayBool)
03225         config.writeEntry( "Exec", d->w->commandEdit->text().prepend("ksystraycmd ") );
03226     else
03227         config.writeEntry( "Exec", d->w->commandEdit->text() );
03228     config.writeEntry( "Path", d->w->pathEdit->lineEdit()->text() ); // not writePathEntry, see kservice.cpp
03229 
03230     // Write mimeTypes
03231     QStringList mimeTypes;
03232     int count = d->w->filetypeList->topLevelItemCount();
03233     for (int i = 0; i < count; ++i) {
03234         QTreeWidgetItem *item = d->w->filetypeList->topLevelItem(i);
03235         QString preference = item->text(2);
03236         mimeTypes.append(item->text(0));
03237         if (!preference.isEmpty())
03238             mimeTypes.append(preference);
03239     }
03240 
03241     kDebug() << mimeTypes;
03242     config.writeXdgListEntry( "MimeType", mimeTypes );
03243 
03244     if ( !d->w->nameEdit->isHidden() ) {
03245         QString nameStr = d->w->nameEdit->text();
03246         config.writeEntry( "Name", nameStr );
03247         config.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized );
03248     }
03249 
03250     config.writeEntry("Terminal", d->m_terminalBool);
03251     config.writeEntry("TerminalOptions", d->m_terminalOptionStr);
03252     config.writeEntry("X-KDE-SubstituteUID", d->m_suidBool);
03253     config.writeEntry("X-KDE-Username", d->m_suidUserStr);
03254     config.writeEntry("StartupNotify", d->m_startupBool);
03255     config.writeEntry("X-DBUS-StartupType", d->m_dbusStartupType);
03256     config.writeEntry("X-DBUS-ServiceName", d->m_dbusServiceName);
03257     config.sync();
03258 
03259     // KSycoca update needed?
03260     QString sycocaPath = KGlobal::dirs()->relativeLocation("apps", path);
03261     bool updateNeeded = !sycocaPath.startsWith('/');
03262     if (!updateNeeded)
03263     {
03264         sycocaPath = KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
03265         updateNeeded = !sycocaPath.startsWith('/');
03266     }
03267     if (updateNeeded)
03268         KBuildSycocaProgressDialog::rebuildKSycoca(d->m_frame);
03269 }
03270 
03271 
03272 void KDesktopPropsPlugin::slotBrowseExec()
03273 {
03274     KUrl f = KFileDialog::getOpenUrl( KUrl(),
03275                                       QString(), d->m_frame );
03276     if ( f.isEmpty() )
03277         return;
03278 
03279     if ( !f.isLocalFile()) {
03280         KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported."));
03281         return;
03282     }
03283 
03284     QString path = f.toLocalFile();
03285     path = KShell::quoteArg( path );
03286     d->w->commandEdit->setText( path );
03287 }
03288 
03289 void KDesktopPropsPlugin::slotAdvanced()
03290 {
03291     KDialog dlg( d->m_frame );
03292     dlg.setObjectName( "KPropertiesDesktopAdv" );
03293     dlg.setModal( true );
03294     dlg.setCaption( i18n("Advanced Options for %1", properties->kurl().fileName()) );
03295     dlg.setButtons( KDialog::Ok | KDialog::Cancel );
03296     dlg.setDefaultButton( KDialog::Ok );
03297     Ui_KPropertiesDesktopAdvBase w;
03298     w.setupUi(dlg.mainWidget());
03299 
03300     // If the command is changed we reset certain settings that are strongly
03301     // coupled to the command.
03302     checkCommandChanged();
03303 
03304     // check to see if we use konsole if not do not add the nocloseonexit
03305     // because we don't know how to do this on other terminal applications
03306     KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
03307     QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
03308                                                         QString::fromLatin1("konsole"));
03309 
03310     bool terminalCloseBool = false;
03311 
03312     if (preferredTerminal == "konsole")
03313     {
03314         terminalCloseBool = (d->m_terminalOptionStr.contains( "--noclose" ) > 0);
03315         w.terminalCloseCheck->setChecked(terminalCloseBool);
03316         d->m_terminalOptionStr.remove( "--noclose");
03317     }
03318     else
03319     {
03320         w.terminalCloseCheck->hide();
03321     }
03322 
03323     w.terminalCheck->setChecked(d->m_terminalBool);
03324     w.terminalEdit->setText(d->m_terminalOptionStr);
03325     w.terminalCloseCheck->setEnabled(d->m_terminalBool);
03326     w.terminalEdit->setEnabled(d->m_terminalBool);
03327     w.terminalEditLabel->setEnabled(d->m_terminalBool);
03328 
03329     w.suidCheck->setChecked(d->m_suidBool);
03330     w.suidEdit->setText(d->m_suidUserStr);
03331     w.suidEdit->setEnabled(d->m_suidBool);
03332     w.suidEditLabel->setEnabled(d->m_suidBool);
03333 
03334     w.startupInfoCheck->setChecked(d->m_startupBool);
03335     w.systrayCheck->setChecked(d->m_systrayBool);
03336 
03337     if (d->m_dbusStartupType == "unique")
03338         w.dbusCombo->setCurrentIndex(2);
03339     else if (d->m_dbusStartupType == "multi")
03340         w.dbusCombo->setCurrentIndex(1);
03341     else if (d->m_dbusStartupType == "wait")
03342         w.dbusCombo->setCurrentIndex(3);
03343     else
03344         w.dbusCombo->setCurrentIndex(0);
03345 
03346     // Provide username completion up to 1000 users.
03347     KCompletion *kcom = new KCompletion;
03348     kcom->setOrder(KCompletion::Sorted);
03349     struct passwd *pw;
03350     int i, maxEntries = 1000;
03351     setpwent();
03352     for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
03353         kcom->addItem(QString::fromLatin1(pw->pw_name));
03354     endpwent();
03355     if (i < maxEntries)
03356     {
03357         w.suidEdit->setCompletionObject(kcom, true);
03358         w.suidEdit->setAutoDeleteCompletionObject( true );
03359         w.suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
03360     }
03361     else
03362     {
03363         delete kcom;
03364     }
03365 
03366     connect( w.terminalEdit, SIGNAL( textChanged( const QString & ) ),
03367              this, SIGNAL( changed() ) );
03368     connect( w.terminalCloseCheck, SIGNAL( toggled( bool ) ),
03369              this, SIGNAL( changed() ) );
03370     connect( w.terminalCheck, SIGNAL( toggled( bool ) ),
03371              this, SIGNAL( changed() ) );
03372     connect( w.suidCheck, SIGNAL( toggled( bool ) ),
03373              this, SIGNAL( changed() ) );
03374     connect( w.suidEdit, SIGNAL( textChanged( const QString & ) ),
03375              this, SIGNAL( changed() ) );
03376     connect( w.startupInfoCheck, SIGNAL( toggled( bool ) ),
03377              this, SIGNAL( changed() ) );
03378     connect( w.systrayCheck, SIGNAL( toggled( bool ) ),
03379              this, SIGNAL( changed() ) );
03380     connect( w.dbusCombo, SIGNAL( activated( int ) ),
03381              this, SIGNAL( changed() ) );
03382 
03383     if ( dlg.exec() == QDialog::Accepted )
03384     {
03385         d->m_terminalOptionStr = w.terminalEdit->text().trimmed();
03386         d->m_terminalBool = w.terminalCheck->isChecked();
03387         d->m_suidBool = w.suidCheck->isChecked();
03388         d->m_suidUserStr = w.suidEdit->text().trimmed();
03389         d->m_startupBool = w.startupInfoCheck->isChecked();
03390         d->m_systrayBool = w.systrayCheck->isChecked();
03391 
03392         if (w.terminalCloseCheck->isChecked())
03393         {
03394             d->m_terminalOptionStr.append(" --noclose");
03395         }
03396 
03397         switch(w.dbusCombo->currentIndex())
03398         {
03399         case 1:  d->m_dbusStartupType = "multi"; break;
03400         case 2:  d->m_dbusStartupType = "unique"; break;
03401         case 3:  d->m_dbusStartupType = "wait"; break;
03402         default: d->m_dbusStartupType = "none"; break;
03403         }
03404     }
03405 }
03406 
03407 bool KDesktopPropsPlugin::supports( const KFileItemList& _items )
03408 {
03409     if ( _items.count() != 1 ) {
03410         return false;
03411     }
03412 
03413     const KFileItem item = _items.first();
03414 
03415     // check if desktop file
03416     if (!item.isDesktopFile()) {
03417         return false;
03418     }
03419 
03420     // open file and check type
03421     bool isLocal;
03422     KUrl url = item.mostLocalUrl( isLocal );
03423     if (!isLocal) {
03424         return false;
03425     }
03426 
03427     KDesktopFile config( url.path() );
03428     return config.hasApplicationType() &&
03429             KAuthorized::authorize("run_desktop_files") &&
03430             KAuthorized::authorize("shell_access");
03431 }
03432 
03433 #include "kpropertiesdialog.moc"
03434 #include "kpropertiesdialog_p.moc"
03435 

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal