• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • 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 KDialogBase by
00039  *  Simon Hausmann <hausmann@kde.org>
00040  * KDesktopPropsPlugin by
00041  *  Waldo Bastian <bastian@kde.org>
00042  */
00043 
00044 #include <config.h>
00045 extern "C" {
00046 #include <pwd.h>
00047 #include <grp.h>
00048 #include <time.h>
00049 #include <sys/types.h>
00050 }
00051 #include <unistd.h>
00052 #include <errno.h>
00053 #include <assert.h>
00054 #include <algorithm>
00055 #include <functional>
00056 
00057 #include <qfile.h>
00058 #include <qdir.h>
00059 #include <qlabel.h>
00060 #include <qpushbutton.h>
00061 #include <qcheckbox.h>
00062 #include <qstrlist.h>
00063 #include <qstringlist.h>
00064 #include <qtextstream.h>
00065 #include <qpainter.h>
00066 #include <qlayout.h>
00067 #include <qcombobox.h>
00068 #include <qgroupbox.h>
00069 #include <qwhatsthis.h>
00070 #include <qtooltip.h>
00071 #include <qstyle.h>
00072 #include <qprogressbar.h>
00073 #include <qvbox.h>
00074 #include <qvaluevector.h>
00075 
00076 #ifdef USE_POSIX_ACL
00077 extern "C" {
00078 #include <sys/param.h>
00079 #ifdef HAVE_SYS_MOUNT_H
00080 #include <sys/mount.h>
00081 #endif
00082 #ifdef HAVE_SYS_XATTR_H
00083 #include <sys/xattr.h>
00084 #endif
00085 }
00086 #endif
00087 
00088 #include <kapplication.h>
00089 #include <kdialog.h>
00090 #include <kdirsize.h>
00091 #include <kdirwatch.h>
00092 #include <kdirnotify_stub.h>
00093 #include <kdiskfreesp.h>
00094 #include <kdebug.h>
00095 #include <kdesktopfile.h>
00096 #include <kicondialog.h>
00097 #include <kurl.h>
00098 #include <kurlrequester.h>
00099 #include <klocale.h>
00100 #include <kglobal.h>
00101 #include <kglobalsettings.h>
00102 #include <kstandarddirs.h>
00103 #include <kio/job.h>
00104 #include <kio/chmodjob.h>
00105 #include <kio/renamedlg.h>
00106 #include <kio/netaccess.h>
00107 #include <kio/kservicetypefactory.h>
00108 #include <kfiledialog.h>
00109 #include <kmimetype.h>
00110 #include <kmountpoint.h>
00111 #include <kiconloader.h>
00112 #include <kmessagebox.h>
00113 #include <kservice.h>
00114 #include <kcompletion.h>
00115 #include <klineedit.h>
00116 #include <kseparator.h>
00117 #include <ksqueezedtextlabel.h>
00118 #include <klibloader.h>
00119 #include <ktrader.h>
00120 #include <kparts/componentfactory.h>
00121 #include <kmetaprops.h>
00122 #include <kpreviewprops.h>
00123 #include <kprocess.h>
00124 #include <krun.h>
00125 #include <klistview.h>
00126 #include <kacl.h>
00127 #include "kfilesharedlg.h"
00128 
00129 #include "kpropertiesdesktopbase.h"
00130 #include "kpropertiesdesktopadvbase.h"
00131 #include "kpropertiesmimetypebase.h"
00132 #ifdef USE_POSIX_ACL
00133 #include "kacleditwidget.h"
00134 #endif
00135 
00136 #include "kpropertiesdialog.h"
00137 
00138 #ifdef Q_WS_WIN
00139 # include <win32_utils.h>
00140 #endif
00141 
00142 static QString nameFromFileName(QString nameStr)
00143 {
00144    if ( nameStr.endsWith(".desktop") )
00145       nameStr.truncate( nameStr.length() - 8 );
00146    if ( nameStr.endsWith(".kdelnk") )
00147       nameStr.truncate( nameStr.length() - 7 );
00148    // Make it human-readable (%2F => '/', ...)
00149    nameStr = KIO::decodeFileName( nameStr );
00150    return nameStr;
00151 }
00152 
00153 mode_t KFilePermissionsPropsPlugin::fperm[3][4] = {
00154         {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
00155         {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
00156         {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
00157     };
00158 
00159 class KPropertiesDialog::KPropertiesDialogPrivate
00160 {
00161 public:
00162   KPropertiesDialogPrivate()
00163   {
00164     m_aborted = false;
00165     fileSharePage = 0;
00166   }
00167   ~KPropertiesDialogPrivate()
00168   {
00169   }
00170   bool m_aborted:1;
00171   QWidget* fileSharePage;
00172 };
00173 
00174 KPropertiesDialog::KPropertiesDialog (KFileItem* item,
00175                                       QWidget* parent, const char* name,
00176                                       bool modal, bool autoShow)
00177   : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(KIO::decodeFileName(item->url().fileName())),
00178                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00179                  parent, name, modal)
00180 {
00181   d = new KPropertiesDialogPrivate;
00182   assert( item );
00183   m_items.append( new KFileItem(*item) ); // deep copy
00184 
00185   m_singleUrl = item->url();
00186   assert(!m_singleUrl.isEmpty());
00187 
00188   init (modal, autoShow);
00189 }
00190 
00191 KPropertiesDialog::KPropertiesDialog (const QString& title,
00192                                       QWidget* parent, const char* name, bool modal)
00193   : KDialogBase (KDialogBase::Tabbed, i18n ("Properties for %1").arg(title),
00194                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00195                  parent, name, modal)
00196 {
00197   d = new KPropertiesDialogPrivate;
00198 
00199   init (modal, false);
00200 }
00201 
00202 KPropertiesDialog::KPropertiesDialog (KFileItemList _items,
00203                                       QWidget* parent, const char* name,
00204                                       bool modal, bool autoShow)
00205   : KDialogBase (KDialogBase::Tabbed,
00206                  // TODO: replace <never used> with "Properties for 1 item". It's very confusing how it has to be translated otherwise
00207                  // (empty translation before the "\n" is not allowed by msgfmt...)
00208          _items.count()>1 ? i18n( "<never used>","Properties for %n Selected Items",_items.count()) :
00209          i18n( "Properties for %1" ).arg(KIO::decodeFileName(_items.first()->url().fileName())),
00210                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00211                  parent, name, modal)
00212 {
00213   d = new KPropertiesDialogPrivate;
00214 
00215   assert( !_items.isEmpty() );
00216   m_singleUrl = _items.first()->url();
00217   assert(!m_singleUrl.isEmpty());
00218 
00219   KFileItemListIterator it ( _items );
00220   // Deep copy
00221   for ( ; it.current(); ++it )
00222       m_items.append( new KFileItem( **it ) );
00223 
00224   init (modal, autoShow);
00225 }
00226 
00227 #ifndef KDE_NO_COMPAT
00228 KPropertiesDialog::KPropertiesDialog (const KURL& _url, mode_t /* _mode is now unused */,
00229                                       QWidget* parent, const char* name,
00230                                       bool modal, bool autoShow)
00231   : KDialogBase (KDialogBase::Tabbed,
00232          i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())),
00233                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00234                  parent, name, modal),
00235   m_singleUrl( _url )
00236 {
00237   d = new KPropertiesDialogPrivate;
00238 
00239   KIO::UDSEntry entry;
00240 
00241   KIO::NetAccess::stat(_url, entry, parent);
00242 
00243   m_items.append( new KFileItem( entry, _url ) );
00244   init (modal, autoShow);
00245 }
00246 #endif
00247 
00248 KPropertiesDialog::KPropertiesDialog (const KURL& _url,
00249                                       QWidget* parent, const char* name,
00250                                       bool modal, bool autoShow)
00251   : KDialogBase (KDialogBase::Tabbed,
00252          i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())),
00253                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00254                  parent, name, modal),
00255   m_singleUrl( _url )
00256 {
00257   d = new KPropertiesDialogPrivate;
00258 
00259   KIO::UDSEntry entry;
00260 
00261   KIO::NetAccess::stat(_url, entry, parent);
00262 
00263   m_items.append( new KFileItem( entry, _url ) );
00264   init (modal, autoShow);
00265 }
00266 
00267 KPropertiesDialog::KPropertiesDialog (const KURL& _tempUrl, const KURL& _currentDir,
00268                                       const QString& _defaultName,
00269                                       QWidget* parent, const char* name,
00270                                       bool modal, bool autoShow)
00271   : KDialogBase (KDialogBase::Tabbed,
00272          i18n( "Properties for %1" ).arg(KIO::decodeFileName(_tempUrl.fileName())),
00273                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00274                  parent, name, modal),
00275 
00276   m_singleUrl( _tempUrl ),
00277   m_defaultName( _defaultName ),
00278   m_currentDir( _currentDir )
00279 {
00280   d = new KPropertiesDialogPrivate;
00281 
00282   assert(!m_singleUrl.isEmpty());
00283 
00284   // Create the KFileItem for the _template_ file, in order to read from it.
00285   m_items.append( new KFileItem( KFileItem::Unknown, KFileItem::Unknown, m_singleUrl ) );
00286   init (modal, autoShow);
00287 }
00288 
00289 bool KPropertiesDialog::showDialog(KFileItem* item, QWidget* parent,
00290                                    const char* name, bool modal)
00291 {
00292 #ifdef Q_WS_WIN
00293   QString localPath = item->localPath();
00294   if (!localPath.isEmpty())
00295     return showWin32FilePropertyDialog(localPath);
00296 #endif
00297   new KPropertiesDialog(item, parent, name, modal);
00298   return true;
00299 }
00300 
00301 bool KPropertiesDialog::showDialog(const KURL& _url, QWidget* parent,
00302                                    const char* name, bool modal)
00303 {
00304 #ifdef Q_WS_WIN
00305   if (_url.isLocalFile())
00306     return showWin32FilePropertyDialog( _url.path() );
00307 #endif
00308   new KPropertiesDialog(_url, parent, name, modal);
00309   return true;
00310 }
00311 
00312 bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent,
00313                                    const char* name, bool modal)
00314 {
00315   if (_items.count()==1)
00316     return KPropertiesDialog::showDialog(_items.getFirst(), parent, name, modal);
00317   new KPropertiesDialog(_items, parent, name, modal);
00318   return true;
00319 }
00320 
00321 void KPropertiesDialog::init (bool modal, bool autoShow)
00322 {
00323   m_pageList.setAutoDelete( true );
00324   m_items.setAutoDelete( true );
00325 
00326   insertPages();
00327 
00328   if (autoShow)
00329     {
00330       if (!modal)
00331         show();
00332       else
00333         exec();
00334     }
00335 }
00336 
00337 void KPropertiesDialog::showFileSharingPage()
00338 {
00339   if (d->fileSharePage) {
00340      showPage( pageIndex( d->fileSharePage));
00341   }
00342 }
00343 
00344 void KPropertiesDialog::setFileSharingPage(QWidget* page) {
00345   d->fileSharePage = page;
00346 }
00347 
00348 
00349 void KPropertiesDialog::setFileNameReadOnly( bool ro )
00350 {
00351     KPropsDlgPlugin *it;
00352 
00353     for ( it=m_pageList.first(); it != 0L; it=m_pageList.next() )
00354     {
00355         KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it);
00356         if ( plugin ) {
00357             plugin->setFileNameReadOnly( ro );
00358             break;
00359         }
00360     }
00361 }
00362 
00363 void KPropertiesDialog::slotStatResult( KIO::Job * )
00364 {
00365 }
00366 
00367 KPropertiesDialog::~KPropertiesDialog()
00368 {
00369   m_pageList.clear();
00370   delete d;
00371 }
00372 
00373 void KPropertiesDialog::insertPlugin (KPropsDlgPlugin* plugin)
00374 {
00375   connect (plugin, SIGNAL (changed ()),
00376            plugin, SLOT (setDirty ()));
00377 
00378   m_pageList.append (plugin);
00379 }
00380 
00381 bool KPropertiesDialog::canDisplay( KFileItemList _items )
00382 {
00383   // TODO: cache the result of those calls. Currently we parse .desktop files far too many times
00384   return KFilePropsPlugin::supports( _items ) ||
00385          KFilePermissionsPropsPlugin::supports( _items ) ||
00386          KDesktopPropsPlugin::supports( _items ) ||
00387          KBindingPropsPlugin::supports( _items ) ||
00388          KURLPropsPlugin::supports( _items ) ||
00389          KDevicePropsPlugin::supports( _items ) ||
00390          KFileMetaPropsPlugin::supports( _items ) ||
00391          KPreviewPropsPlugin::supports( _items );
00392 }
00393 
00394 void KPropertiesDialog::slotOk()
00395 {
00396   KPropsDlgPlugin *page;
00397   d->m_aborted = false;
00398 
00399   KFilePropsPlugin * filePropsPlugin = 0L;
00400   if ( m_pageList.first()->isA("KFilePropsPlugin") )
00401     filePropsPlugin = static_cast<KFilePropsPlugin *>(m_pageList.first());
00402 
00403   // If any page is dirty, then set the main one (KFilePropsPlugin) as
00404   // dirty too. This is what makes it possible to save changes to a global
00405   // desktop file into a local one. In other cases, it doesn't hurt.
00406   for ( page = m_pageList.first(); page != 0L; page = m_pageList.next() )
00407     if ( page->isDirty() && filePropsPlugin )
00408     {
00409         filePropsPlugin->setDirty();
00410         break;
00411     }
00412 
00413   // Apply the changes in the _normal_ order of the tabs now
00414   // This is because in case of renaming a file, KFilePropsPlugin will call
00415   // KPropertiesDialog::rename, so other tab will be ok with whatever order
00416   // BUT for file copied from templates, we need to do the renaming first !
00417   for ( page = m_pageList.first(); page != 0L && !d->m_aborted; page = m_pageList.next() )
00418     if ( page->isDirty() )
00419     {
00420       kdDebug( 250 ) << "applying changes for " << page->className() << endl;
00421       page->applyChanges();
00422       // applyChanges may change d->m_aborted.
00423     }
00424     else
00425       kdDebug( 250 ) << "skipping page " << page->className() << endl;
00426 
00427   if ( !d->m_aborted && filePropsPlugin )
00428     filePropsPlugin->postApplyChanges();
00429 
00430   if ( !d->m_aborted )
00431   {
00432     emit applied();
00433     emit propertiesClosed();
00434     deleteLater();
00435     accept();
00436   } // else, keep dialog open for user to fix the problem.
00437 }
00438 
00439 void KPropertiesDialog::slotCancel()
00440 {
00441   emit canceled();
00442   emit propertiesClosed();
00443 
00444   deleteLater();
00445   done( Rejected );
00446 }
00447 
00448 void KPropertiesDialog::insertPages()
00449 {
00450   if (m_items.isEmpty())
00451     return;
00452 
00453   if ( KFilePropsPlugin::supports( m_items ) )
00454   {
00455     KPropsDlgPlugin *p = new KFilePropsPlugin( this );
00456     insertPlugin (p);
00457   }
00458 
00459   if ( KFilePermissionsPropsPlugin::supports( m_items ) )
00460   {
00461     KPropsDlgPlugin *p = new KFilePermissionsPropsPlugin( this );
00462     insertPlugin (p);
00463   }
00464 
00465   if ( KDesktopPropsPlugin::supports( m_items ) )
00466   {
00467     KPropsDlgPlugin *p = new KDesktopPropsPlugin( this );
00468     insertPlugin (p);
00469   }
00470 
00471   if ( KBindingPropsPlugin::supports( m_items ) )
00472   {
00473     KPropsDlgPlugin *p = new KBindingPropsPlugin( this );
00474     insertPlugin (p);
00475   }
00476 
00477   if ( KURLPropsPlugin::supports( m_items ) )
00478   {
00479     KPropsDlgPlugin *p = new KURLPropsPlugin( this );
00480     insertPlugin (p);
00481   }
00482 
00483   if ( KDevicePropsPlugin::supports( m_items ) )
00484   {
00485     KPropsDlgPlugin *p = new KDevicePropsPlugin( this );
00486     insertPlugin (p);
00487   }
00488 
00489   if ( KFileMetaPropsPlugin::supports( m_items ) )
00490   {
00491     KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this );
00492     insertPlugin (p);
00493   }
00494 
00495   if ( KPreviewPropsPlugin::supports( m_items ) )
00496   {
00497     KPropsDlgPlugin *p = new KPreviewPropsPlugin( this );
00498     insertPlugin (p);
00499   }
00500 
00501   if ( kapp->authorizeKAction("sharefile") &&
00502        KFileSharePropsPlugin::supports( m_items ) )
00503   {
00504     KPropsDlgPlugin *p = new KFileSharePropsPlugin( this );
00505     insertPlugin (p);
00506   }
00507 
00508   //plugins
00509 
00510   if ( m_items.count() != 1 )
00511     return;
00512 
00513   KFileItem *item = m_items.first();
00514   QString mimetype = item->mimetype();
00515 
00516   if ( mimetype.isEmpty() )
00517     return;
00518 
00519   QString query = QString::fromLatin1(
00520       "('KPropsDlg/Plugin' in ServiceTypes) and "
00521       "((not exist [X-KDE-Protocol]) or "
00522       " ([X-KDE-Protocol] == '%1'  )   )"          ).arg(item->url().protocol());
00523 
00524   kdDebug( 250 ) << "trader query: " << query << endl;
00525   KTrader::OfferList offers = KTrader::self()->query( mimetype, query );
00526   KTrader::OfferList::ConstIterator it = offers.begin();
00527   KTrader::OfferList::ConstIterator end = offers.end();
00528   for (; it != end; ++it )
00529   {
00530     KPropsDlgPlugin *plugin = KParts::ComponentFactory
00531         ::createInstanceFromLibrary<KPropsDlgPlugin>( (*it)->library().local8Bit().data(),
00532                                                       this,
00533                                                       (*it)->name().latin1() );
00534     if ( !plugin )
00535         continue;
00536 
00537     insertPlugin( plugin );
00538   }
00539 }
00540 
00541 void KPropertiesDialog::updateUrl( const KURL& _newUrl )
00542 {
00543   Q_ASSERT( m_items.count() == 1 );
00544   kdDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url() << endl;
00545   KURL newUrl = _newUrl;
00546   emit saveAs(m_singleUrl, newUrl);
00547   kdDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url() << endl;
00548 
00549   m_singleUrl = newUrl;
00550   m_items.first()->setURL( newUrl );
00551   assert(!m_singleUrl.isEmpty());
00552   // If we have an Desktop page, set it dirty, so that a full file is saved locally
00553   // Same for a URL page (because of the Name= hack)
00554   for ( QPtrListIterator<KPropsDlgPlugin> it(m_pageList); it.current(); ++it )
00555    if ( it.current()->isA("KExecPropsPlugin") || // KDE4 remove me
00556         it.current()->isA("KURLPropsPlugin") ||
00557         it.current()->isA("KDesktopPropsPlugin"))
00558    {
00559      //kdDebug(250) << "Setting page dirty" << endl;
00560      it.current()->setDirty();
00561      break;
00562    }
00563 }
00564 
00565 void KPropertiesDialog::rename( const QString& _name )
00566 {
00567   Q_ASSERT( m_items.count() == 1 );
00568   kdDebug(250) << "KPropertiesDialog::rename " << _name << endl;
00569   KURL newUrl;
00570   // if we're creating from a template : use currentdir
00571   if ( !m_currentDir.isEmpty() )
00572   {
00573     newUrl = m_currentDir;
00574     newUrl.addPath( _name );
00575   }
00576   else
00577   {
00578     QString tmpurl = m_singleUrl.url();
00579     if ( tmpurl.at(tmpurl.length() - 1) == '/')
00580       // It's a directory, so strip the trailing slash first
00581       tmpurl.truncate( tmpurl.length() - 1);
00582     newUrl = tmpurl;
00583     newUrl.setFileName( _name );
00584   }
00585   updateUrl( newUrl );
00586 }
00587 
00588 void KPropertiesDialog::abortApplying()
00589 {
00590   d->m_aborted = true;
00591 }
00592 
00593 class KPropsDlgPlugin::KPropsDlgPluginPrivate
00594 {
00595 public:
00596   KPropsDlgPluginPrivate()
00597   {
00598   }
00599   ~KPropsDlgPluginPrivate()
00600   {
00601   }
00602 
00603   bool m_bDirty;
00604 };
00605 
00606 KPropsDlgPlugin::KPropsDlgPlugin( KPropertiesDialog *_props )
00607 : QObject( _props, 0L )
00608 {
00609   d = new KPropsDlgPluginPrivate;
00610   properties = _props;
00611   fontHeight = 2*properties->fontMetrics().height();
00612   d->m_bDirty = false;
00613 }
00614 
00615 KPropsDlgPlugin::~KPropsDlgPlugin()
00616 {
00617   delete d;
00618 }
00619 
00620 bool KPropsDlgPlugin::isDesktopFile( KFileItem * _item )
00621 {
00622   // only local files
00623   bool isLocal;
00624   KURL url = _item->mostLocalURL( isLocal );
00625   if ( !isLocal )
00626     return false;
00627 
00628   // only regular files
00629   if ( !S_ISREG( _item->mode() ) )
00630     return false;
00631 
00632   QString t( url.path() );
00633 
00634   // only if readable
00635   FILE *f = fopen( QFile::encodeName(t), "r" );
00636   if ( f == 0L )
00637     return false;
00638   fclose(f);
00639 
00640   // return true if desktop file
00641   return ( _item->mimetype() == "application/x-desktop" );
00642 }
00643 
00644 void KPropsDlgPlugin::setDirty( bool b )
00645 {
00646   d->m_bDirty = b;
00647 }
00648 
00649 void KPropsDlgPlugin::setDirty()
00650 {
00651   d->m_bDirty = true;
00652 }
00653 
00654 bool KPropsDlgPlugin::isDirty() const
00655 {
00656   return d->m_bDirty;
00657 }
00658 
00659 void KPropsDlgPlugin::applyChanges()
00660 {
00661   kdWarning(250) << "applyChanges() not implemented in page !" << endl;
00662 }
00663 
00665 
00666 class KFilePropsPlugin::KFilePropsPluginPrivate
00667 {
00668 public:
00669   KFilePropsPluginPrivate()
00670   {
00671     dirSizeJob = 0L;
00672     dirSizeUpdateTimer = 0L;
00673     m_lined = 0;
00674     m_freeSpaceLabel = 0;
00675   }
00676   ~KFilePropsPluginPrivate()
00677   {
00678     if ( dirSizeJob )
00679       dirSizeJob->kill();
00680   }
00681 
00682   KDirSize * dirSizeJob;
00683   QTimer *dirSizeUpdateTimer;
00684   QFrame *m_frame;
00685   bool bMultiple;
00686   bool bIconChanged;
00687   bool bKDesktopMode;
00688   bool bDesktopFile;
00689   QLabel *m_freeSpaceLabel;
00690   QString mimeType;
00691   QString oldFileName;
00692   KLineEdit* m_lined;
00693 };
00694 
00695 KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props )
00696   : KPropsDlgPlugin( _props )
00697 {
00698   d = new KFilePropsPluginPrivate;
00699   d->bMultiple = (properties->items().count() > 1);
00700   d->bIconChanged = false;
00701   d->bKDesktopMode = (QCString(qApp->name()) == "kdesktop"); // nasty heh?
00702   d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items());
00703   kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple << endl;
00704 
00705   // We set this data from the first item, and we'll
00706   // check that the other items match against it, resetting when not.
00707   bool isLocal;
00708   KFileItem * item = properties->item();
00709   KURL url = item->mostLocalURL( isLocal );
00710   bool isReallyLocal = item->url().isLocalFile();
00711   bool bDesktopFile = isDesktopFile(item);
00712   kdDebug() << "url=" << url << " bDesktopFile=" << bDesktopFile << " isLocal=" << isLocal << " isReallyLocal=" << isReallyLocal << endl;
00713   mode_t mode = item->mode();
00714   bool hasDirs = item->isDir() && !item->isLink();
00715   bool hasRoot = url.path() == QString::fromLatin1("/");
00716   QString iconStr = KMimeType::iconForURL(url, mode);
00717   QString directory = properties->kurl().directory();
00718   QString protocol = properties->kurl().protocol();
00719   QString mimeComment = item->mimeComment();
00720   d->mimeType = item->mimetype();
00721   bool hasTotalSize;
00722   KIO::filesize_t totalSize = item->size(hasTotalSize);
00723   QString magicMimeComment;
00724   if ( isLocal ) {
00725       KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
00726       if ( magicMimeType->name() != KMimeType::defaultMimeType() )
00727           magicMimeComment = magicMimeType->comment();
00728   }
00729 
00730   // Those things only apply to 'single file' mode
00731   QString filename = QString::null;
00732   bool isTrash = false;
00733   bool isDevice = false;
00734   m_bFromTemplate = false;
00735 
00736   // And those only to 'multiple' mode
00737   uint iDirCount = hasDirs ? 1 : 0;
00738   uint iFileCount = 1-iDirCount;
00739 
00740   d->m_frame = properties->addPage (i18n("&General"));
00741 
00742   QVBoxLayout *vbl = new QVBoxLayout( d->m_frame, 0,
00743                                       KDialog::spacingHint(), "vbl");
00744   QGridLayout *grid = new QGridLayout(0, 3); // unknown rows
00745   grid->setColStretch(0, 0);
00746   grid->setColStretch(1, 0);
00747   grid->setColStretch(2, 1);
00748   grid->addColSpacing(1, KDialog::spacingHint());
00749   vbl->addLayout(grid);
00750   int curRow = 0;
00751 
00752   if ( !d->bMultiple )
00753   {
00754     QString path;
00755     if ( !m_bFromTemplate ) {
00756       isTrash = ( properties->kurl().protocol().find( "trash", 0, false)==0 );
00757       if ( properties->kurl().protocol().find("device", 0, false)==0)
00758             isDevice = true;
00759       // Extract the full name, but without file: for local files
00760       if ( isReallyLocal )
00761         path = properties->kurl().path();
00762       else
00763         path = properties->kurl().prettyURL();
00764     } else {
00765       path = properties->currentDir().path(1) + properties->defaultName();
00766       directory = properties->currentDir().prettyURL();
00767     }
00768 
00769     if (KExecPropsPlugin::supports(properties->items()) || // KDE4 remove me
00770         d->bDesktopFile ||
00771         KBindingPropsPlugin::supports(properties->items())) {
00772       determineRelativePath( path );
00773     }
00774 
00775     // Extract the file name only
00776     filename = properties->defaultName();
00777     if ( filename.isEmpty() ) { // no template
00778       filename = item->name(); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system
00779     } else {
00780       m_bFromTemplate = true;
00781       setDirty(); // to enforce that the copy happens
00782     }
00783     d->oldFileName = filename;
00784 
00785     // Make it human-readable
00786     filename = nameFromFileName( filename );
00787 
00788     if ( d->bKDesktopMode && d->bDesktopFile ) {
00789         KDesktopFile config( url.path(), true /* readonly */ );
00790         if ( config.hasKey( "Name" ) ) {
00791             filename = config.readName();
00792         }
00793     }
00794 
00795     oldName = filename;
00796   }
00797   else
00798   {
00799     // Multiple items: see what they have in common
00800     KFileItemList items = properties->items();
00801     KFileItemListIterator it( items );
00802     for ( ++it /*no need to check the first one again*/ ; it.current(); ++it )
00803     {
00804       KURL url = (*it)->url();
00805       kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyURL() << endl;
00806       // The list of things we check here should match the variables defined
00807       // at the beginning of this method.
00808       if ( url.isLocalFile() != isLocal )
00809         isLocal = false; // not all local
00810       if ( bDesktopFile && isDesktopFile(*it) != bDesktopFile )
00811         bDesktopFile = false; // not all desktop files
00812       if ( (*it)->mode() != mode )
00813         mode = (mode_t)0;
00814       if ( KMimeType::iconForURL(url, mode) != iconStr )
00815         iconStr = "kmultiple";
00816       if ( url.directory() != directory )
00817         directory = QString::null;
00818       if ( url.protocol() != protocol )
00819         protocol = QString::null;
00820       if ( !mimeComment.isNull() && (*it)->mimeComment() != mimeComment )
00821         mimeComment = QString::null;
00822       if ( isLocal && !magicMimeComment.isNull() ) {
00823           KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
00824           if ( magicMimeType->comment() != magicMimeComment )
00825               magicMimeComment = QString::null;
00826       }
00827 
00828       if ( url.path() == QString::fromLatin1("/") )
00829         hasRoot = true;
00830       if ( (*it)->isDir() && !(*it)->isLink() )
00831       {
00832         iDirCount++;
00833         hasDirs = true;
00834       }
00835       else
00836       {
00837         iFileCount++;
00838     bool hasSize;
00839         totalSize += (*it)->size(hasSize);
00840     hasTotalSize = hasTotalSize || hasSize;
00841       }
00842     }
00843   }
00844 
00845   if (!isReallyLocal && !protocol.isEmpty())
00846   {
00847     directory += ' ';
00848     directory += '(';
00849     directory += protocol;
00850     directory += ')';
00851   }
00852 
00853   if ( !isDevice && !isTrash && (bDesktopFile || S_ISDIR(mode)) && !d->bMultiple /*not implemented for multiple*/ )
00854   {
00855     KIconButton *iconButton = new KIconButton( d->m_frame );
00856     int bsize = 66 + 2 * iconButton->style().pixelMetric(QStyle::PM_ButtonMargin);
00857     iconButton->setFixedSize(bsize, bsize);
00858     iconButton->setIconSize(48);
00859     iconButton->setStrictIconSize(false);
00860     // This works for everything except Device icons on unmounted devices
00861     // So we have to really open .desktop files
00862     QString iconStr = KMimeType::findByURL( url, mode )->icon( url, isLocal );
00863     if ( bDesktopFile && isLocal )
00864     {
00865       KDesktopFile config( url.path(), true );
00866       config.setDesktopGroup();
00867       iconStr = config.readEntry( "Icon" );
00868       if ( config.hasDeviceType() )
00869     iconButton->setIconType( KIcon::Desktop, KIcon::Device );
00870       else
00871     iconButton->setIconType( KIcon::Desktop, KIcon::Application );
00872     } else
00873       iconButton->setIconType( KIcon::Desktop, KIcon::FileSystem );
00874     iconButton->setIcon(iconStr);
00875     iconArea = iconButton;
00876     connect( iconButton, SIGNAL( iconChanged(QString) ),
00877              this, SLOT( slotIconChanged() ) );
00878   } else {
00879     QLabel *iconLabel = new QLabel( d->m_frame );
00880     int bsize = 66 + 2 * iconLabel->style().pixelMetric(QStyle::PM_ButtonMargin);
00881     iconLabel->setFixedSize(bsize, bsize);
00882     iconLabel->setPixmap( KGlobal::iconLoader()->loadIcon( iconStr, KIcon::Desktop, 48) );
00883     iconArea = iconLabel;
00884   }
00885   grid->addWidget(iconArea, curRow, 0, AlignLeft);
00886 
00887   if (d->bMultiple || isTrash || isDevice || hasRoot)
00888   {
00889     QLabel *lab = new QLabel(d->m_frame );
00890     if ( d->bMultiple )
00891       lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) );
00892     else
00893       lab->setText( filename );
00894     nameArea = lab;
00895   } else
00896   {
00897     d->m_lined = new KLineEdit( d->m_frame );
00898     d->m_lined->setText(filename);
00899     nameArea = d->m_lined;
00900     d->m_lined->setFocus();
00901 
00902     // Enhanced rename: Don't highlight the file extension.
00903     QString pattern;
00904     KServiceTypeFactory::self()->findFromPattern( filename, &pattern );
00905     if (!pattern.isEmpty() && pattern.at(0)=='*' && pattern.find('*',1)==-1)
00906       d->m_lined->setSelection(0, filename.length()-pattern.stripWhiteSpace().length()+1);
00907     else
00908     {
00909       int lastDot = filename.findRev('.');
00910       if (lastDot > 0)
00911         d->m_lined->setSelection(0, lastDot);
00912     }
00913 
00914     connect( d->m_lined, SIGNAL( textChanged( const QString & ) ),
00915              this, SLOT( nameFileChanged(const QString & ) ) );
00916   }
00917 
00918   grid->addWidget(nameArea, curRow++, 2);
00919 
00920   KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame);
00921   grid->addMultiCellWidget(sep, curRow, curRow, 0, 2);
00922   ++curRow;
00923 
00924   QLabel *l;
00925   if ( !mimeComment.isEmpty() && !isDevice && !isTrash)
00926   {
00927     l = new QLabel(i18n("Type:"), d->m_frame );
00928 
00929     grid->addWidget(l, curRow, 0);
00930 
00931     QHBox *box = new QHBox(d->m_frame);
00932     box->setSpacing(20);
00933     l = new QLabel(mimeComment, box );
00934 
00935 #ifdef Q_WS_X11
00936     //TODO: wrap for win32 or mac?
00937     QPushButton *button = new QPushButton(box);
00938 
00939     QIconSet iconSet = SmallIconSet(QString::fromLatin1("configure"));
00940     QPixmap pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal );
00941     button->setIconSet( iconSet );
00942     button->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
00943     if ( d->mimeType == KMimeType::defaultMimeType() )
00944        QToolTip::add(button, i18n("Create new file type"));
00945     else
00946        QToolTip::add(button, i18n("Edit file type"));
00947 
00948     connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() ));
00949 
00950     if (!kapp->authorizeKAction("editfiletype"))
00951        button->hide();
00952 #endif
00953 
00954     grid->addWidget(box, curRow++, 2);
00955   }
00956 
00957   if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
00958   {
00959     l = new QLabel(i18n("Contents:"), d->m_frame );
00960     grid->addWidget(l, curRow, 0);
00961 
00962     l = new QLabel(magicMimeComment, d->m_frame );
00963     grid->addWidget(l, curRow++, 2);
00964   }
00965 
00966   if ( !directory.isEmpty() )
00967   {
00968     l = new QLabel( i18n("Location:"), d->m_frame );
00969     grid->addWidget(l, curRow, 0);
00970 
00971     l = new KSqueezedTextLabel( d->m_frame );
00972     l->setText( directory );
00973     grid->addWidget(l, curRow++, 2);
00974   }
00975 
00976   if( hasDirs || hasTotalSize ) {
00977     l = new QLabel(i18n("Size:"), d->m_frame );
00978     grid->addWidget(l, curRow, 0);
00979 
00980     m_sizeLabel = new QLabel( d->m_frame );
00981     grid->addWidget( m_sizeLabel, curRow++, 2 );
00982   } else {
00983     m_sizeLabel = 0;
00984   }
00985 
00986   if ( !hasDirs ) // Only files [and symlinks]
00987   {
00988     if(hasTotalSize) {
00989       m_sizeLabel->setText(KIO::convertSizeWithBytes(totalSize));
00990     }
00991 
00992     m_sizeDetermineButton = 0L;
00993     m_sizeStopButton = 0L;
00994   }
00995   else // Directory
00996   {
00997     QHBoxLayout * sizelay = new QHBoxLayout(KDialog::spacingHint());
00998     grid->addLayout( sizelay, curRow++, 2 );
00999 
01000     // buttons
01001     m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
01002     m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
01003     connect( m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) );
01004     connect( m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) );
01005     sizelay->addWidget(m_sizeDetermineButton, 0);
01006     sizelay->addWidget(m_sizeStopButton, 0);
01007     sizelay->addStretch(10); // so that the buttons don't grow horizontally
01008 
01009     // auto-launch for local dirs only, and not for '/'
01010     if ( isLocal && !hasRoot )
01011     {
01012       m_sizeDetermineButton->setText( i18n("Refresh") );
01013       slotSizeDetermine();
01014     }
01015     else
01016       m_sizeStopButton->setEnabled( false );
01017   }
01018 
01019   if (!d->bMultiple && item->isLink()) {
01020     l = new QLabel(i18n("Points to:"), d->m_frame );
01021     grid->addWidget(l, curRow, 0);
01022 
01023     l = new KSqueezedTextLabel(item->linkDest(), d->m_frame );
01024     grid->addWidget(l, curRow++, 2);
01025   }
01026 
01027   if (!d->bMultiple) // Dates for multiple don't make much sense...
01028   {
01029     QDateTime dt;
01030     bool hasTime;
01031     time_t tim = item->time(KIO::UDS_CREATION_TIME, hasTime);
01032     if ( hasTime )
01033     {
01034       l = new QLabel(i18n("Created:"), d->m_frame );
01035       grid->addWidget(l, curRow, 0);
01036 
01037       dt.setTime_t( tim );
01038       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
01039       grid->addWidget(l, curRow++, 2);
01040     }
01041 
01042     tim = item->time(KIO::UDS_MODIFICATION_TIME, hasTime);
01043     if ( hasTime )
01044     {
01045       l = new QLabel(i18n("Modified:"), d->m_frame );
01046       grid->addWidget(l, curRow, 0);
01047 
01048       dt.setTime_t( tim );
01049       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
01050       grid->addWidget(l, curRow++, 2);
01051     }
01052 
01053     tim = item->time(KIO::UDS_ACCESS_TIME, hasTime);
01054     if ( hasTime )
01055     {
01056       l = new QLabel(i18n("Accessed:"), d->m_frame );
01057       grid->addWidget(l, curRow, 0);
01058 
01059       dt.setTime_t( tim );
01060       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
01061       grid->addWidget(l, curRow++, 2);
01062     }
01063   }
01064 
01065   if ( isLocal && hasDirs )  // only for directories
01066   {
01067     sep = new KSeparator( KSeparator::HLine, d->m_frame);
01068     grid->addMultiCellWidget(sep, curRow, curRow, 0, 2);
01069     ++curRow;
01070 
01071     QString mountPoint = KIO::findPathMountPoint( url.path() );
01072 
01073     if (mountPoint != "/")
01074     {
01075         l = new QLabel(i18n("Mounted on:"), d->m_frame );
01076         grid->addWidget(l, curRow, 0);
01077 
01078         l = new KSqueezedTextLabel( mountPoint, d->m_frame );
01079         grid->addWidget( l, curRow++, 2 );
01080     }
01081 
01082     l = new QLabel(i18n("Free disk space:"), d->m_frame );
01083     grid->addWidget(l, curRow, 0);
01084 
01085     d->m_freeSpaceLabel = new QLabel( d->m_frame );
01086     grid->addWidget( d->m_freeSpaceLabel, curRow++, 2 );
01087 
01088     KDiskFreeSp * job = new KDiskFreeSp;
01089     connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&,
01090              const unsigned long&, const QString& ) ),
01091              this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&,
01092           const unsigned long&, const QString& ) ) );
01093     job->readDF( mountPoint );
01094   }
01095 
01096   vbl->addStretch(1);
01097 }
01098 
01099 // QString KFilePropsPlugin::tabName () const
01100 // {
01101 //   return i18n ("&General");
01102 // }
01103 
01104 void KFilePropsPlugin::setFileNameReadOnly( bool ro )
01105 {
01106   if ( d->m_lined )
01107   {
01108     d->m_lined->setReadOnly( ro );
01109     if (ro)
01110     {
01111        // Don't put the initial focus on the line edit when it is ro
01112        QPushButton *button = properties->actionButton(KDialogBase::Ok);
01113        if (button)
01114           button->setFocus();
01115     }
01116   }
01117 }
01118 
01119 void KFilePropsPlugin::slotEditFileType()
01120 {
01121 #ifdef Q_WS_X11
01122   QString mime;
01123   if ( d->mimeType == KMimeType::defaultMimeType() ) {
01124     int pos = d->oldFileName.findRev( '.' );
01125     if ( pos != -1 )
01126     mime = "*" + d->oldFileName.mid(pos);
01127     else
01128     mime = "*";
01129   }
01130   else
01131     mime = d->mimeType;
01132     //TODO: wrap for win32 or mac?
01133   QString keditfiletype = QString::fromLatin1("keditfiletype");
01134   KRun::runCommand( keditfiletype
01135                     + " --parent " + QString::number( (ulong)properties->topLevelWidget()->winId())
01136                     + " " + KProcess::quote(mime),
01137                     keditfiletype, keditfiletype /*unused*/);
01138 #endif
01139 }
01140 
01141 void KFilePropsPlugin::slotIconChanged()
01142 {
01143   d->bIconChanged = true;
01144   emit changed();
01145 }
01146 
01147 void KFilePropsPlugin::nameFileChanged(const QString &text )
01148 {
01149   properties->enableButtonOK(!text.isEmpty());
01150   emit changed();
01151 }
01152 
01153 void KFilePropsPlugin::determineRelativePath( const QString & path )
01154 {
01155     // now let's make it relative
01156     QStringList dirs;
01157     if (KBindingPropsPlugin::supports(properties->items()))
01158     {
01159        m_sRelativePath =KGlobal::dirs()->relativeLocation("mime", path);
01160        if (m_sRelativePath.startsWith("/"))
01161           m_sRelativePath = QString::null;
01162     }
01163     else
01164     {
01165        m_sRelativePath =KGlobal::dirs()->relativeLocation("apps", path);
01166        if (m_sRelativePath.startsWith("/"))
01167        {
01168           m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
01169           if (m_sRelativePath.startsWith("/"))
01170              m_sRelativePath = QString::null;
01171           else
01172              m_sRelativePath = path;
01173        }
01174     }
01175     if ( m_sRelativePath.isEmpty() )
01176     {
01177       if (KBindingPropsPlugin::supports(properties->items()))
01178         kdWarning(250) << "Warning : editing a mimetype file out of the mimetype dirs!" << endl;
01179     }
01180 }
01181 
01182 void KFilePropsPlugin::slotFoundMountPoint( const QString&,
01183                         unsigned long kBSize,
01184                         unsigned long /*kBUsed*/,
01185                         unsigned long kBAvail )
01186 {
01187     d->m_freeSpaceLabel->setText(
01188     // xgettext:no-c-format  --  Don't warn about translating the %1 out of %2 part.
01189     i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
01190     .arg(KIO::convertSizeFromKB(kBAvail))
01191     .arg(KIO::convertSizeFromKB(kBSize))
01192     .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
01193 }
01194 
01195 // attention: copy&paste below, due to compiler bug
01196 // it doesn't like those unsigned long parameters -- unsigned long& are ok :-/
01197 void KFilePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize,
01198                         const unsigned long& /*kBUsed*/,
01199                         const unsigned long& kBAvail,
01200                         const QString& )
01201 {
01202     d->m_freeSpaceLabel->setText(
01203     // xgettext:no-c-format  --  Don't warn about translating the %1 out of %2 part.
01204     i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
01205     .arg(KIO::convertSizeFromKB(kBAvail))
01206     .arg(KIO::convertSizeFromKB(kBSize))
01207     .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
01208 }
01209 
01210 void KFilePropsPlugin::slotDirSizeUpdate()
01211 {
01212     KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
01213     KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
01214          KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
01215     m_sizeLabel->setText( i18n("Calculating... %1 (%2)\n%3, %4")
01216               .arg(KIO::convertSize(totalSize))
01217                          .arg(KGlobal::locale()->formatNumber(totalSize, 0))
01218         .arg(i18n("1 file","%n files",totalFiles))
01219         .arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs)));
01220 }
01221 
01222 void KFilePropsPlugin::slotDirSizeFinished( KIO::Job * job )
01223 {
01224   if (job->error())
01225     m_sizeLabel->setText( job->errorString() );
01226   else
01227   {
01228     KIO::filesize_t totalSize = static_cast<KDirSize*>(job)->totalSize();
01229     KIO::filesize_t totalFiles = static_cast<KDirSize*>(job)->totalFiles();
01230     KIO::filesize_t totalSubdirs = static_cast<KDirSize*>(job)->totalSubdirs();
01231     m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4")
01232               .arg(KIO::convertSize(totalSize))
01233               .arg(KGlobal::locale()->formatNumber(totalSize, 0))
01234         .arg(i18n("1 file","%n files",totalFiles))
01235         .arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs)));
01236   }
01237   m_sizeStopButton->setEnabled(false);
01238   // just in case you change something and try again :)
01239   m_sizeDetermineButton->setText( i18n("Refresh") );
01240   m_sizeDetermineButton->setEnabled(true);
01241   d->dirSizeJob = 0L;
01242   delete d->dirSizeUpdateTimer;
01243   d->dirSizeUpdateTimer = 0L;
01244 }
01245 
01246 void KFilePropsPlugin::slotSizeDetermine()
01247 {
01248   m_sizeLabel->setText( i18n("Calculating...") );
01249   kdDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" <<  properties->item() << endl;
01250   kdDebug(250) << " URL=" << properties->item()->url().url() << endl;
01251   d->dirSizeJob = KDirSize::dirSizeJob( properties->items() );
01252   d->dirSizeUpdateTimer = new QTimer(this);
01253   connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ),
01254            SLOT( slotDirSizeUpdate() ) );
01255   d->dirSizeUpdateTimer->start(500);
01256   connect( d->dirSizeJob, SIGNAL( result( KIO::Job * ) ),
01257            SLOT( slotDirSizeFinished( KIO::Job * ) ) );
01258   m_sizeStopButton->setEnabled(true);
01259   m_sizeDetermineButton->setEnabled(false);
01260 
01261   // also update the "Free disk space" display
01262   if ( d->m_freeSpaceLabel )
01263   {
01264     bool isLocal;
01265     KFileItem * item = properties->item();
01266     KURL url = item->mostLocalURL( isLocal );
01267     QString mountPoint = KIO::findPathMountPoint( url.path() );
01268 
01269     KDiskFreeSp * job = new KDiskFreeSp;
01270     connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&,
01271              const unsigned long&, const QString& ) ),
01272              this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&,
01273           const unsigned long&, const QString& ) ) );
01274     job->readDF( mountPoint );
01275   }
01276 }
01277 
01278 void KFilePropsPlugin::slotSizeStop()
01279 {
01280   if ( d->dirSizeJob )
01281   {
01282     m_sizeLabel->setText( i18n("Stopped") );
01283     d->dirSizeJob->kill();
01284     d->dirSizeJob = 0;
01285   }
01286   if ( d->dirSizeUpdateTimer )
01287     d->dirSizeUpdateTimer->stop();
01288 
01289   m_sizeStopButton->setEnabled(false);
01290   m_sizeDetermineButton->setEnabled(true);
01291 }
01292 
01293 KFilePropsPlugin::~KFilePropsPlugin()
01294 {
01295   delete d;
01296 }
01297 
01298 bool KFilePropsPlugin::supports( KFileItemList /*_items*/ )
01299 {
01300   return true;
01301 }
01302 
01303 // Don't do this at home
01304 void qt_enter_modal( QWidget *widget );
01305 void qt_leave_modal( QWidget *widget );
01306 
01307 void KFilePropsPlugin::applyChanges()
01308 {
01309   if ( d->dirSizeJob )
01310     slotSizeStop();
01311 
01312   kdDebug(250) << "KFilePropsPlugin::applyChanges" << endl;
01313 
01314   if (nameArea->inherits("QLineEdit"))
01315   {
01316     QString n = ((QLineEdit *) nameArea)->text();
01317     // Remove trailing spaces (#4345)
01318     while ( n[n.length()-1].isSpace() )
01319       n.truncate( n.length() - 1 );
01320     if ( n.isEmpty() )
01321     {
01322       KMessageBox::sorry( properties, i18n("The new file name is empty."));
01323       properties->abortApplying();
01324       return;
01325     }
01326 
01327     // Do we need to rename the file ?
01328     kdDebug(250) << "oldname = " << oldName << endl;
01329     kdDebug(250) << "newname = " << n << endl;
01330     if ( oldName != n || m_bFromTemplate ) { // true for any from-template file
01331       KIO::Job * job = 0L;
01332       KURL oldurl = properties->kurl();
01333 
01334       QString newFileName = KIO::encodeFileName(n);
01335       if (d->bDesktopFile && !newFileName.endsWith(".desktop") && !newFileName.endsWith(".kdelnk"))
01336          newFileName += ".desktop";
01337 
01338       // Tell properties. Warning, this changes the result of properties->kurl() !
01339       properties->rename( newFileName );
01340 
01341       // Update also relative path (for apps and mimetypes)
01342       if ( !m_sRelativePath.isEmpty() )
01343         determineRelativePath( properties->kurl().path() );
01344 
01345       kdDebug(250) << "New URL = " << properties->kurl().url() << endl;
01346       kdDebug(250) << "old = " << oldurl.url() << endl;
01347 
01348       // Don't remove the template !!
01349       if ( !m_bFromTemplate ) // (normal renaming)
01350         job = KIO::move( oldurl, properties->kurl() );
01351       else // Copying a template
01352         job = KIO::copy( oldurl, properties->kurl() );
01353 
01354       connect( job, SIGNAL( result( KIO::Job * ) ),
01355                SLOT( slotCopyFinished( KIO::Job * ) ) );
01356       connect( job, SIGNAL( renamed( KIO::Job *, const KURL &, const KURL & ) ),
01357                SLOT( slotFileRenamed( KIO::Job *, const KURL &, const KURL & ) ) );
01358       // wait for job
01359       QWidget dummy(0,0,WType_Dialog|WShowModal);
01360       qt_enter_modal(&dummy);
01361       qApp->enter_loop();
01362       qt_leave_modal(&dummy);
01363       return;
01364     }
01365     properties->updateUrl(properties->kurl());
01366     // Update also relative path (for apps and mimetypes)
01367     if ( !m_sRelativePath.isEmpty() )
01368       determineRelativePath( properties->kurl().path() );
01369   }
01370 
01371   // No job, keep going
01372   slotCopyFinished( 0L );
01373 }
01374 
01375 void KFilePropsPlugin::slotCopyFinished( KIO::Job * job )
01376 {
01377   kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl;
01378   if (job)
01379   {
01380     // allow apply() to return
01381     qApp->exit_loop();
01382     if ( job->error() )
01383     {
01384         job->showErrorDialog( d->m_frame );
01385         // Didn't work. Revert the URL to the old one
01386         properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcURLs().first() );
01387         properties->abortApplying(); // Don't apply the changes to the wrong file !
01388         return;
01389     }
01390   }
01391 
01392   assert( properties->item() );
01393   assert( !properties->item()->url().isEmpty() );
01394 
01395   // Save the file where we can -> usually in ~/.kde/...
01396   if (KBindingPropsPlugin::supports(properties->items()) && !m_sRelativePath.isEmpty())
01397   {
01398     KURL newURL;
01399     newURL.setPath( locateLocal("mime", m_sRelativePath) );
01400     properties->updateUrl( newURL );
01401   }
01402   else if (d->bDesktopFile && !m_sRelativePath.isEmpty())
01403   {
01404     kdDebug(250) << "KFilePropsPlugin::slotCopyFinished " << m_sRelativePath << endl;
01405     KURL newURL;
01406     newURL.setPath( KDesktopFile::locateLocal(m_sRelativePath) );
01407     kdDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path() << endl;
01408     properties->updateUrl( newURL );
01409   }
01410 
01411   if ( d->bKDesktopMode && d->bDesktopFile ) {
01412       // Renamed? Update Name field
01413       if ( d->oldFileName != properties->kurl().fileName() || m_bFromTemplate ) {
01414           KDesktopFile config( properties->kurl().path() );
01415           QString nameStr = nameFromFileName(properties->kurl().fileName());
01416           config.writeEntry( "Name", nameStr );
01417           config.writeEntry( "Name", nameStr, true, false, true );
01418       }
01419   }
01420 }
01421 
01422 void KFilePropsPlugin::applyIconChanges()
01423 {
01424   KIconButton *iconButton = ::qt_cast<KIconButton *>( iconArea );
01425   if ( !iconButton || !d->bIconChanged )
01426     return;
01427   // handle icon changes - only local files (or pseudo-local) for now
01428   // TODO: Use KTempFile and KIO::file_copy with overwrite = true
01429   KURL url = properties->kurl();
01430   url = KIO::NetAccess::mostLocalURL( url, properties );
01431   if (url.isLocalFile()) {
01432     QString path;
01433 
01434     if (S_ISDIR(properties->item()->mode()))
01435     {
01436       path = url.path(1) + QString::fromLatin1(".directory");
01437       // don't call updateUrl because the other tabs (i.e. permissions)
01438       // apply to the directory, not the .directory file.
01439     }
01440     else
01441       path = url.path();
01442 
01443     // Get the default image
01444     QString str = KMimeType::findByURL( url,
01445                                         properties->item()->mode(),
01446                                         true )->KServiceType::icon();
01447     // Is it another one than the default ?
01448     QString sIcon;
01449     if ( str != iconButton->icon() )
01450       sIcon = iconButton->icon();
01451     // (otherwise write empty value)
01452 
01453     kdDebug(250) << "**" << path << "**" << endl;
01454     QFile f( path );
01455 
01456     // If default icon and no .directory file -> don't create one
01457     if ( !sIcon.isEmpty() || f.exists() )
01458     {
01459         if ( !f.open( IO_ReadWrite ) ) {
01460           KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
01461                       "have sufficient access to write to <b>%1</b>.</qt>").arg(path));
01462           return;
01463         }
01464         f.close();
01465 
01466         KDesktopFile cfg(path);
01467         kdDebug(250) << "sIcon = " << (sIcon) << endl;
01468         kdDebug(250) << "str = " << (str) << endl;
01469         cfg.writeEntry( "Icon", sIcon );
01470         cfg.sync();
01471     }
01472   }
01473 }
01474 
01475 void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KURL &, const KURL & newUrl )
01476 {
01477   // This is called in case of an existing local file during the copy/move operation,
01478   // if the user chooses Rename.
01479   properties->updateUrl( newUrl );
01480 }
01481 
01482 void KFilePropsPlugin::postApplyChanges()
01483 {
01484   // Save the icon only after applying the permissions changes (#46192)
01485   applyIconChanges();
01486 
01487   KURL::List lst;
01488   KFileItemList items = properties->items();
01489   for ( KFileItemListIterator it( items ); it.current(); ++it )
01490     lst.append((*it)->url());
01491   KDirNotify_stub allDirNotify("*", "KDirNotify*");
01492   allDirNotify.FilesChanged( lst );
01493 }
01494 
01495 class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
01496 {
01497 public:
01498   KFilePermissionsPropsPluginPrivate()
01499   {
01500   }
01501   ~KFilePermissionsPropsPluginPrivate()
01502   {
01503   }
01504 
01505   QFrame *m_frame;
01506   QCheckBox *cbRecursive;
01507   QLabel *explanationLabel;
01508   QComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo;
01509   QCheckBox *extraCheckbox;
01510   mode_t partialPermissions;
01511   KFilePermissionsPropsPlugin::PermissionsMode pmode;
01512   bool canChangePermissions;
01513   bool isIrregular;
01514   bool hasExtendedACL;
01515   KACL extendedACL;
01516   KACL defaultACL;
01517   bool fileSystemSupportsACLs;
01518 };
01519 
01520 #define UniOwner    (S_IRUSR|S_IWUSR|S_IXUSR)
01521 #define UniGroup    (S_IRGRP|S_IWGRP|S_IXGRP)
01522 #define UniOthers   (S_IROTH|S_IWOTH|S_IXOTH)
01523 #define UniRead     (S_IRUSR|S_IRGRP|S_IROTH)
01524 #define UniWrite    (S_IWUSR|S_IWGRP|S_IWOTH)
01525 #define UniExec     (S_IXUSR|S_IXGRP|S_IXOTH)
01526 #define UniSpecial  (S_ISUID|S_ISGID|S_ISVTX)
01527 
01528 // synced with PermissionsTarget
01529 const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers};
01530 const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 };
01531 
01532 // synced with PermissionsMode and standardPermissions
01533 const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = {
01534   { I18N_NOOP("Forbidden"),
01535     I18N_NOOP("Can Read"),
01536     I18N_NOOP("Can Read & Write"),
01537     0 },
01538   { I18N_NOOP("Forbidden"),
01539     I18N_NOOP("Can View Content"),
01540     I18N_NOOP("Can View & Modify Content"),
01541     0 },
01542   { 0, 0, 0, 0}, // no texts for links
01543   { I18N_NOOP("Forbidden"),
01544     I18N_NOOP("Can View Content & Read"),
01545     I18N_NOOP("Can View/Read & Modify/Write"),
01546     0 }
01547 };
01548 
01549 
01550 KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props )
01551   : KPropsDlgPlugin( _props )
01552 {
01553   d = new KFilePermissionsPropsPluginPrivate;
01554   d->cbRecursive = 0L;
01555   grpCombo = 0L; grpEdit = 0;
01556   usrEdit = 0L;
01557   QString path = properties->kurl().path(-1);
01558   QString fname = properties->kurl().fileName();
01559   bool isLocal = properties->kurl().isLocalFile();
01560   bool isTrash = ( properties->kurl().protocol().find("trash", 0, false)==0 );
01561   bool IamRoot = (geteuid() == 0);
01562 
01563   KFileItem * item = properties->item();
01564   bool isLink = item->isLink();
01565   bool isDir = item->isDir(); // all dirs
01566   bool hasDir = item->isDir(); // at least one dir
01567   permissions = item->permissions(); // common permissions to all files
01568   d->partialPermissions = permissions; // permissions that only some files have (at first we take everything)
01569   d->isIrregular = isIrregular(permissions, isDir, isLink);
01570   strOwner = item->user();
01571   strGroup = item->group();
01572   d->hasExtendedACL = item->ACL().isExtended() || item->defaultACL().isValid();
01573   d->extendedACL = item->ACL();
01574   d->defaultACL = item->defaultACL();
01575   d->fileSystemSupportsACLs = false;
01576 
01577   if ( properties->items().count() > 1 )
01578   {
01579     // Multiple items: see what they have in common
01580     KFileItemList items = properties->items();
01581     KFileItemListIterator it( items );
01582     for ( ++it /*no need to check the first one again*/ ; it.current(); ++it )
01583     {
01584       if (!d->isIrregular)
01585         d->isIrregular |= isIrregular((*it)->permissions(),
01586                                       (*it)->isDir() == isDir,
01587                                       (*it)->isLink() == isLink);
01588       d->hasExtendedACL = d->hasExtendedACL || (*it)->hasExtendedACL();
01589       if ( (*it)->isLink() != isLink )
01590         isLink = false;
01591       if ( (*it)->isDir() != isDir )
01592         isDir = false;
01593       hasDir |= (*it)->isDir();
01594       if ( (*it)->permissions() != permissions )
01595       {
01596         permissions &= (*it)->permissions();
01597         d->partialPermissions |= (*it)->permissions();
01598       }
01599       if ( (*it)->user() != strOwner )
01600         strOwner = QString::null;
01601       if ( (*it)->group() != strGroup )
01602         strGroup = QString::null;
01603     }
01604   }
01605 
01606   if (isLink)
01607     d->pmode = PermissionsOnlyLinks;
01608   else if (isDir)
01609     d->pmode = PermissionsOnlyDirs;
01610   else if (hasDir)
01611     d->pmode = PermissionsMixed;
01612   else
01613     d->pmode = PermissionsOnlyFiles;
01614 
01615   // keep only what's not in the common permissions
01616   d->partialPermissions = d->partialPermissions & ~permissions;
01617 
01618   bool isMyFile = false;
01619 
01620   if (isLocal && !strOwner.isEmpty()) { // local files, and all owned by the same person
01621     struct passwd *myself = getpwuid( geteuid() );
01622     if ( myself != 0L )
01623     {
01624       isMyFile = (strOwner == QString::fromLocal8Bit(myself->pw_name));
01625     } else
01626       kdWarning() << "I don't exist ?! geteuid=" << geteuid() << endl;
01627   } else {
01628     //We don't know, for remote files, if they are ours or not.
01629     //So we let the user change permissions, and
01630     //KIO::chmod will tell, if he had no right to do it.
01631     isMyFile = true;
01632   }
01633 
01634   d->canChangePermissions = (isMyFile || IamRoot) && (!isLink);
01635 
01636 
01637   // create GUI
01638 
01639   d->m_frame = properties->addPage(i18n("&Permissions"));
01640 
01641   QBoxLayout *box = new QVBoxLayout( d->m_frame, 0, KDialog::spacingHint() );
01642 
01643   QWidget *l;
01644   QLabel *lbl;
01645   QGroupBox *gb;
01646   QGridLayout *gl;
01647   QPushButton* pbAdvancedPerm = 0;
01648 
01649   /* Group: Access Permissions */
01650   gb = new QGroupBox ( 0, Qt::Vertical, i18n("Access Permissions"), d->m_frame );
01651   gb->layout()->setSpacing(KDialog::spacingHint());
01652   gb->layout()->setMargin(KDialog::marginHint());
01653   box->addWidget (gb);
01654 
01655   gl = new QGridLayout (gb->layout(), 7, 2);
01656   gl->setColStretch(1, 1);
01657 
01658   l = d->explanationLabel = new QLabel( "", gb );
01659   if (isLink)
01660     d->explanationLabel->setText(i18n("This file is a link and does not have permissions.",
01661                       "All files are links and do not have permissions.",
01662                       properties->items().count()));
01663   else if (!d->canChangePermissions)
01664     d->explanationLabel->setText(i18n("Only the owner can change permissions."));
01665   gl->addMultiCellWidget(l, 0, 0, 0, 1);
01666 
01667   lbl = new QLabel( i18n("O&wner:"), gb);
01668   gl->addWidget(lbl, 1, 0);
01669   l = d->ownerPermCombo = new QComboBox(gb);
01670   lbl->setBuddy(l);
01671   gl->addWidget(l, 1, 1);
01672   connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() ));
01673   QWhatsThis::add(l, i18n("Specifies the actions that the owner is allowed to do."));
01674 
01675   lbl = new QLabel( i18n("Gro&up:"), gb);
01676   gl->addWidget(lbl, 2, 0);
01677   l = d->groupPermCombo = new QComboBox(gb);
01678   lbl->setBuddy(l);
01679   gl->addWidget(l, 2, 1);
01680   connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() ));
01681   QWhatsThis::add(l, i18n("Specifies the actions that the members of the group are allowed to do."));
01682 
01683   lbl = new QLabel( i18n("O&thers:"), gb);
01684   gl->addWidget(lbl, 3, 0);
01685   l = d->othersPermCombo = new QComboBox(gb);
01686   lbl->setBuddy(l);
01687   gl->addWidget(l, 3, 1);
01688   connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() ));
01689   QWhatsThis::add(l, i18n("Specifies the actions that all users, who are neither "
01690               "owner nor in the group, are allowed to do."));
01691 
01692   if (!isLink) {
01693     l = d->extraCheckbox = new QCheckBox(hasDir ?
01694                      i18n("Only own&er can rename and delete folder content") :
01695                      i18n("Is &executable"),
01696                      gb );
01697     connect( d->extraCheckbox, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
01698     gl->addWidget(l, 4, 1);
01699     QWhatsThis::add(l, hasDir ? i18n("Enable this option to allow only the folder's owner to "
01700                      "delete or rename the contained files and folders. Other "
01701                      "users can only add new files, which requires the 'Modify "
01702                      "Content' permission.")
01703             : i18n("Enable this option to mark the file as executable. This only makes "
01704                "sense for programs and scripts. It is required when you want to "
01705                "execute them."));
01706 
01707     QLayoutItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
01708     gl->addMultiCell(spacer, 5, 5, 0, 1);
01709 
01710     pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb);
01711     gl->addMultiCellWidget(pbAdvancedPerm, 6, 6, 0, 1, AlignRight);
01712     connect(pbAdvancedPerm, SIGNAL( clicked() ), this, SLOT( slotShowAdvancedPermissions() ));
01713   }
01714   else
01715     d->extraCheckbox = 0;
01716 
01717 
01718   /**** Group: Ownership ****/
01719   gb = new QGroupBox ( 0, Qt::Vertical, i18n("Ownership"), d->m_frame );
01720   gb->layout()->setSpacing(KDialog::spacingHint());
01721   gb->layout()->setMargin(KDialog::marginHint());
01722   box->addWidget (gb);
01723 
01724   gl = new QGridLayout (gb->layout(), 4, 3);
01725   gl->addRowSpacing(0, 10);
01726 
01727   /*** Set Owner ***/
01728   l = new QLabel( i18n("User:"), gb );
01729   gl->addWidget (l, 1, 0);
01730 
01731   /* GJ: Don't autocomplete more than 1000 users. This is a kind of random
01732    * value. Huge sites having 10.000+ user have a fair chance of using NIS,
01733    * (possibly) making this unacceptably slow.
01734    * OTOH, it is nice to offer this functionality for the standard user.
01735    */
01736   int i, maxEntries = 1000;
01737   struct passwd *user;
01738   struct group *ge;
01739 
01740   /* File owner: For root, offer a KLineEdit with autocompletion.
01741    * For a user, who can never chown() a file, offer a QLabel.
01742    */
01743   if (IamRoot && isLocal)
01744   {
01745     usrEdit = new KLineEdit( gb );
01746     KCompletion *kcom = usrEdit->completionObject();
01747     kcom->setOrder(KCompletion::Sorted);
01748     setpwent();
01749     for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); i++)
01750       kcom->addItem(QString::fromLatin1(user->pw_name));
01751     endpwent();
01752     usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto :
01753                                KGlobalSettings::CompletionNone);
01754     usrEdit->setText(strOwner);
01755     gl->addWidget(usrEdit, 1, 1);
01756     connect( usrEdit, SIGNAL( textChanged( const QString & ) ),
01757              this, SIGNAL( changed() ) );
01758   }
01759   else
01760   {
01761     l = new QLabel(strOwner, gb);
01762     gl->addWidget(l, 1, 1);
01763   }
01764 
01765   /*** Set Group ***/
01766 
01767   QStringList groupList;
01768   QCString strUser;
01769   user = getpwuid(geteuid());
01770   if (user != 0L)
01771     strUser = user->pw_name;
01772 
01773 #ifdef Q_OS_UNIX
01774   setgrent();
01775   for (i=0; ((ge = getgrent()) != 0L) && (i < maxEntries); i++)
01776   {
01777     if (IamRoot)
01778       groupList += QString::fromLatin1(ge->gr_name);
01779     else
01780     {
01781       /* pick the groups to which the user belongs */
01782       char ** members = ge->gr_mem;
01783       char * member;
01784       while ((member = *members) != 0L) {
01785         if (strUser == member) {
01786           groupList += QString::fromLocal8Bit(ge->gr_name);
01787           break;
01788         }
01789         ++members;
01790       }
01791     }
01792   }
01793   endgrent();
01794 #endif //Q_OS_UNIX
01795 
01796   /* add the effective Group to the list .. */
01797   ge = getgrgid (getegid());
01798   if (ge) {
01799     QString name = QString::fromLatin1(ge->gr_name);
01800     if (name.isEmpty())
01801       name.setNum(ge->gr_gid);
01802     if (groupList.find(name) == groupList.end())
01803       groupList += name;
01804   }
01805 
01806   bool isMyGroup = groupList.contains(strGroup);
01807 
01808   /* add the group the file currently belongs to ..
01809    * .. if its not there already
01810    */
01811   if (!isMyGroup)
01812     groupList += strGroup;
01813 
01814   l = new QLabel( i18n("Group:"), gb );
01815   gl->addWidget (l, 2, 0);
01816 
01817   /* Set group: if possible to change:
01818    * - Offer a KLineEdit for root, since he can change to any group.
01819    * - Offer a QComboBox for a normal user, since he can change to a fixed
01820    *   (small) set of groups only.
01821    * If not changeable: offer a QLabel.
01822    */
01823   if (IamRoot && isLocal)
01824   {
01825     grpEdit = new KLineEdit(gb);
01826     KCompletion *kcom = new KCompletion;
01827     kcom->setItems(groupList);
01828     grpEdit->setCompletionObject(kcom, true);
01829     grpEdit->setAutoDeleteCompletionObject( true );
01830     grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
01831     grpEdit->setText(strGroup);
01832     gl->addWidget(grpEdit, 2, 1);
01833     connect( grpEdit, SIGNAL( textChanged( const QString & ) ),
01834              this, SIGNAL( changed() ) );
01835   }
01836   else if ((groupList.count() > 1) && isMyFile && isLocal)
01837   {
01838     grpCombo = new QComboBox(gb, "combogrouplist");
01839     grpCombo->insertStringList(groupList);
01840     grpCombo->setCurrentItem(groupList.findIndex(strGroup));
01841     gl->addWidget(grpCombo, 2, 1);
01842     connect( grpCombo, SIGNAL( activated( int ) ),
01843              this, SIGNAL( changed() ) );
01844   }
01845   else
01846   {
01847     l = new QLabel(strGroup, gb);
01848     gl->addWidget(l, 2, 1);
01849   }
01850 
01851   gl->setColStretch(2, 10);
01852 
01853   // "Apply recursive" checkbox
01854   if ( hasDir && !isLink && !isTrash  )
01855   {
01856       d->cbRecursive = new QCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame );
01857       connect( d->cbRecursive, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
01858       box->addWidget( d->cbRecursive );
01859   }
01860 
01861   updateAccessControls();
01862 
01863 
01864   if ( isTrash || !d->canChangePermissions )
01865   {
01866       //don't allow to change properties for file into trash
01867       enableAccessControls(false);
01868       if ( pbAdvancedPerm  && !d->hasExtendedACL )
01869           pbAdvancedPerm->setEnabled(false);
01870   }
01871 
01872   box->addStretch (10);
01873 }
01874 
01875 #ifdef USE_POSIX_ACL
01876 static bool fileSystemSupportsACL( const QCString& pathCString )
01877 {
01878     bool fileSystemSupportsACLs = false;
01879 #ifdef Q_OS_FREEBSD
01880     struct statfs buf;
01881     fileSystemSupportsACLs = ( statfs( pathCString.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS );
01882 #else
01883     fileSystemSupportsACLs =
01884       getxattr( pathCString.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA;
01885 #endif
01886     return fileSystemSupportsACLs;
01887 }
01888 #endif
01889 
01890 
01891 void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() {
01892 
01893   bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed);
01894   KDialogBase dlg(properties, 0, true, i18n("Advanced Permissions"),
01895           KDialogBase::Ok|KDialogBase::Cancel);
01896 
01897   QLabel *l, *cl[3];
01898   QGroupBox *gb;
01899   QGridLayout *gl;
01900 
01901   QVBox *mainVBox = dlg.makeVBoxMainWidget();
01902 
01903   // Group: Access Permissions
01904   gb = new QGroupBox ( 0, Qt::Vertical, i18n("Access Permissions"), mainVBox );
01905   gb->layout()->setSpacing(KDialog::spacingHint());
01906   gb->layout()->setMargin(KDialog::marginHint());
01907 
01908   gl = new QGridLayout (gb->layout(), 6, 6);
01909   gl->addRowSpacing(0, 10);
01910 
01911   QValueVector<QWidget*> theNotSpecials;
01912 
01913   l = new QLabel(i18n("Class"), gb );
01914   gl->addWidget(l, 1, 0);
01915   theNotSpecials.append( l );
01916 
01917   if (isDir)
01918     l = new QLabel( i18n("Show\nEntries"), gb );
01919   else
01920     l = new QLabel( i18n("Read"), gb );
01921   gl->addWidget (l, 1, 1);
01922   theNotSpecials.append( l );
01923   QString readWhatsThis;
01924   if (isDir)
01925     readWhatsThis = i18n("This flag allows viewing the content of the folder.");
01926   else
01927     readWhatsThis = i18n("The Read flag allows viewing the content of the file.");
01928   QWhatsThis::add(l, readWhatsThis);
01929 
01930   if (isDir)
01931     l = new QLabel( i18n("Write\nEntries"), gb );
01932   else
01933     l = new QLabel( i18n("Write"), gb );
01934   gl->addWidget (l, 1, 2);
01935   theNotSpecials.append( l );
01936   QString writeWhatsThis;
01937   if (isDir)
01938     writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. "
01939               "Note that deleting and renaming can be limited using the Sticky flag.");
01940   else
01941     writeWhatsThis = i18n("The Write flag allows modifying the content of the file.");
01942   QWhatsThis::add(l, writeWhatsThis);
01943 
01944   QString execWhatsThis;
01945   if (isDir) {
01946     l = new QLabel( i18n("Enter folder", "Enter"), gb );
01947     execWhatsThis = i18n("Enable this flag to allow entering the folder.");
01948   }
01949   else {
01950     l = new QLabel( i18n("Exec"), gb );
01951     execWhatsThis = i18n("Enable this flag to allow executing the file as a program.");
01952   }
01953   QWhatsThis::add(l, execWhatsThis);
01954   theNotSpecials.append( l );
01955   // GJ: Add space between normal and special modes
01956   QSize size = l->sizeHint();
01957   size.setWidth(size.width() + 15);
01958   l->setFixedSize(size);
01959   gl->addWidget (l, 1, 3);
01960 
01961   l = new QLabel( i18n("Special"), gb );
01962   gl->addMultiCellWidget(l, 1, 1, 4, 5);
01963   QString specialWhatsThis;
01964   if (isDir)
01965     specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact "
01966                 "meaning of the flag can be seen in the right hand column.");
01967   else
01968     specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen "
01969                 "in the right hand column.");
01970   QWhatsThis::add(l, specialWhatsThis);
01971 
01972   cl[0] = new QLabel( i18n("User"), gb );
01973   gl->addWidget (cl[0], 2, 0);
01974   theNotSpecials.append( cl[0] );
01975 
01976   cl[1] = new QLabel( i18n("Group"), gb );
01977   gl->addWidget (cl[1], 3, 0);
01978   theNotSpecials.append( cl[1] );
01979 
01980   cl[2] = new QLabel( i18n("Others"), gb );
01981   gl->addWidget (cl[2], 4, 0);
01982   theNotSpecials.append( cl[2] );
01983 
01984   l = new QLabel(i18n("Set UID"), gb);
01985   gl->addWidget(l, 2, 5);
01986   QString setUidWhatsThis;
01987   if (isDir)
01988     setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be "
01989                "the owner of all new files.");
01990   else
01991     setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
01992                "be executed with the permissions of the owner.");
01993   QWhatsThis::add(l, setUidWhatsThis);
01994 
01995   l = new QLabel(i18n("Set GID"), gb);
01996   gl->addWidget(l, 3, 5);
01997   QString setGidWhatsThis;
01998   if (isDir)
01999     setGidWhatsThis = i18n("If this flag is set, the group of this folder will be "
02000                "set for all new files.");
02001   else
02002     setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
02003                "be executed with the permissions of the group.");
02004   QWhatsThis::add(l, setGidWhatsThis);
02005 
02006   l = new QLabel(i18n("File permission", "Sticky"), gb);
02007   gl->addWidget(l, 4, 5);
02008   QString stickyWhatsThis;
02009   if (isDir)
02010     stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner "
02011                "and root can delete or rename files. Otherwise everybody "
02012                "with write permissions can do this.");
02013   else
02014     stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may "
02015                "be used on some systems");
02016   QWhatsThis::add(l, stickyWhatsThis);
02017 
02018   mode_t aPermissions, aPartialPermissions;
02019   mode_t dummy1, dummy2;
02020 
02021   if (!d->isIrregular) {
02022     switch (d->pmode) {
02023     case PermissionsOnlyFiles:
02024       getPermissionMasks(aPartialPermissions,
02025              dummy1,
02026              aPermissions,
02027              dummy2);
02028       break;
02029     case PermissionsOnlyDirs:
02030     case PermissionsMixed:
02031       getPermissionMasks(dummy1,
02032              aPartialPermissions,
02033              dummy2,
02034              aPermissions);
02035       break;
02036     case PermissionsOnlyLinks:
02037       aPermissions = UniRead | UniWrite | UniExec | UniSpecial;
02038       aPartialPermissions = 0;
02039       break;
02040     }
02041   }
02042   else {
02043     aPermissions = permissions;
02044     aPartialPermissions = d->partialPermissions;
02045   }
02046 
02047   // Draw Checkboxes
02048   QCheckBox *cba[3][4];
02049   for (int row = 0; row < 3 ; ++row) {
02050     for (int col = 0; col < 4; ++col) {
02051       QCheckBox *cb = new QCheckBox( gb );
02052       if ( col != 3 ) theNotSpecials.append( cb );
02053       cba[row][col] = cb;
02054       cb->setChecked(aPermissions & fperm[row][col]);
02055       if ( aPartialPermissions & fperm[row][col] )
02056       {
02057         cb->setTristate();
02058         cb->setNoChange();
02059       }
02060       else if (d->cbRecursive && d->cbRecursive->isChecked())
02061     cb->setTristate();
02062 
02063       cb->setEnabled( d->canChangePermissions );
02064       gl->addWidget (cb, row+2, col+1);
02065       switch(col) {
02066       case 0:
02067     QWhatsThis::add(cb, readWhatsThis);
02068     break;
02069       case 1:
02070     QWhatsThis::add(cb, writeWhatsThis);
02071     break;
02072       case 2:
02073     QWhatsThis::add(cb, execWhatsThis);
02074     break;
02075       case 3:
02076     switch(row) {
02077     case 0:
02078       QWhatsThis::add(cb, setUidWhatsThis);
02079       break;
02080     case 1:
02081       QWhatsThis::add(cb, setGidWhatsThis);
02082       break;
02083     case 2:
02084       QWhatsThis::add(cb, stickyWhatsThis);
02085       break;
02086     }
02087     break;
02088       }
02089     }
02090   }
02091   gl->setColStretch(6, 10);
02092 
02093 #ifdef USE_POSIX_ACL
02094   KACLEditWidget *extendedACLs = 0;
02095 
02096   // FIXME make it work with partial entries
02097   if ( properties->items().count() == 1 ) {
02098       QCString pathCString = QFile::encodeName( properties->item()->url().path() );
02099       d->fileSystemSupportsACLs = fileSystemSupportsACL( pathCString );
02100   }
02101   if ( d->fileSystemSupportsACLs  ) {
02102     std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) );
02103     extendedACLs = new KACLEditWidget( mainVBox );
02104     if ( d->extendedACL.isValid() && d->extendedACL.isExtended() )
02105       extendedACLs->setACL( d->extendedACL );
02106     else
02107       extendedACLs->setACL( KACL( aPermissions ) );
02108 
02109     if ( d->defaultACL.isValid() )
02110       extendedACLs->setDefaultACL( d->defaultACL );
02111 
02112     if ( properties->items().first()->isDir() )
02113       extendedACLs->setAllowDefaults( true );
02114     if ( !d->canChangePermissions )
02115       extendedACLs->setReadOnly( true );
02116 
02117   }
02118 #endif
02119   if (dlg.exec() != KDialogBase::Accepted)
02120     return;
02121 
02122   mode_t andPermissions = mode_t(~0);
02123   mode_t orPermissions = 0;
02124   for (int row = 0; row < 3; ++row)
02125     for (int col = 0; col < 4; ++col) {
02126       switch (cba[row][col]->state())
02127       {
02128       case QCheckBox::On:
02129     orPermissions |= fperm[row][col];
02130     //fall through
02131       case QCheckBox::Off:
02132     andPermissions &= ~fperm[row][col];
02133     break;
02134       default: // NoChange
02135     break;
02136       }
02137     }
02138 
02139   d->isIrregular = false;
02140   KFileItemList items = properties->items();
02141   for (KFileItemListIterator it(items); it.current(); ++it) {
02142     if (isIrregular(((*it)->permissions() & andPermissions) | orPermissions,
02143             (*it)->isDir(), (*it)->isLink())) {
02144       d->isIrregular = true;
02145       break;
02146     }
02147   }
02148 
02149   permissions = orPermissions;
02150   d->partialPermissions = andPermissions;
02151 
02152 #ifdef USE_POSIX_ACL
02153   // override with the acls, if present
02154   if ( extendedACLs ) {
02155     d->extendedACL = extendedACLs->getACL();
02156     d->defaultACL = extendedACLs->getDefaultACL();
02157     d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid();
02158     permissions = d->extendedACL.basePermissions();
02159     permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX );
02160   }
02161 #endif
02162 
02163   updateAccessControls();
02164   emit changed();
02165 }
02166 
02167 // QString KFilePermissionsPropsPlugin::tabName () const
02168 // {
02169 //   return i18n ("&Permissions");
02170 // }
02171 
02172 KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin()
02173 {
02174   delete d;
02175 }
02176 
02177 bool KFilePermissionsPropsPlugin::supports( KFileItemList _items )
02178 {
02179   KFileItemList::const_iterator it = _items.constBegin();
02180   for ( ; it != _items.constEnd(); ++it ) {
02181     KFileItem *item = *it;
02182     if( !item->user().isEmpty() || !item->group().isEmpty() )
02183       return true;
02184   }
02185   return false;
02186 }
02187 
02188 // sets a combo box in the Access Control frame
02189 void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target,
02190                           mode_t permissions, mode_t partial) {
02191   combo->clear();
02192   if (d->pmode == PermissionsOnlyLinks) {
02193     combo->insertItem(i18n("Link"));
02194     combo->setCurrentItem(0);
02195     return;
02196   }
02197 
02198   mode_t tMask = permissionsMasks[target];
02199   int textIndex;
02200   for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++)
02201     if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite)))
02202       break;
02203   Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar
02204 
02205   for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++)
02206     combo->insertItem(i18n(permissionsTexts[(int)d->pmode][i]));
02207 
02208   if (partial & tMask & ~UniExec) {
02209     combo->insertItem(i18n("Varying (No Change)"));
02210     combo->setCurrentItem(3);
02211   }
02212   else
02213     combo->setCurrentItem(textIndex);
02214 }
02215 
02216 // permissions are irregular if they cant be displayed in a combo box.
02217 bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) {
02218   if (isLink)                             // links are always ok
02219     return false;
02220 
02221   mode_t p = permissions;
02222   if (p & (S_ISUID | S_ISGID))  // setuid/setgid -> irregular
02223     return true;
02224   if (isDir) {
02225     p &= ~S_ISVTX;          // ignore sticky on dirs
02226 
02227     // check supported flag combinations
02228     mode_t p0 = p & UniOwner;
02229     if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner))
02230       return true;
02231     p0 = p & UniGroup;
02232     if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup))
02233       return true;
02234     p0 = p & UniOthers;
02235     if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers))
02236       return true;
02237     return false;
02238   }
02239   if (p & S_ISVTX) // sticky on file -> irregular
02240     return true;
02241 
02242   // check supported flag combinations
02243   mode_t p0 = p & UniOwner;
02244   bool usrXPossible = !p0; // true if this file could be an executable
02245   if (p0 & S_IXUSR) {
02246     if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR)))
02247       return true;
02248     usrXPossible = true;
02249   }
02250   else if (p0 == S_IWUSR)
02251     return true;
02252 
02253   p0 = p & UniGroup;
02254   bool grpXPossible = !p0; // true if this file could be an executable
02255   if (p0 & S_IXGRP) {
02256     if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP)))
02257       return true;
02258     grpXPossible = true;
02259   }
02260   else if (p0 == S_IWGRP)
02261     return true;
02262   if (p0 == 0)
02263     grpXPossible = true;
02264 
02265   p0 = p & UniOthers;
02266   bool othXPossible = !p0; // true if this file could be an executable
02267   if (p0 & S_IXOTH) {
02268     if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH)))
02269       return true;
02270     othXPossible = true;
02271   }
02272   else if (p0 == S_IWOTH)
02273     return true;
02274 
02275   // check that there either all targets are executable-compatible, or none
02276   return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible);
02277 }
02278 
02279 // enables/disabled the widgets in the Access Control frame
02280 void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) {
02281     d->ownerPermCombo->setEnabled(enable);
02282     d->groupPermCombo->setEnabled(enable);
02283     d->othersPermCombo->setEnabled(enable);
02284     if (d->extraCheckbox)
02285       d->extraCheckbox->setEnabled(enable);
02286         if ( d->cbRecursive )
02287             d->cbRecursive->setEnabled(enable);
02288 }
02289 
02290 // updates all widgets in the Access Control frame
02291 void KFilePermissionsPropsPlugin::updateAccessControls() {
02292   setComboContent(d->ownerPermCombo, PermissionsOwner,
02293           permissions, d->partialPermissions);
02294   setComboContent(d->groupPermCombo, PermissionsGroup,
02295           permissions, d->partialPermissions);
02296   setComboContent(d->othersPermCombo, PermissionsOthers,
02297           permissions, d->partialPermissions);
02298 
02299   switch(d->pmode) {
02300   case PermissionsOnlyLinks:
02301     enableAccessControls(false);
02302     break;
02303   case PermissionsOnlyFiles:
02304     enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
02305     if (d->canChangePermissions)
02306       d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
02307                    i18n("This file uses advanced permissions",
02308                       "These files use advanced permissions.",
02309                       properties->items().count()) : "");
02310     if (d->partialPermissions & UniExec) {
02311       d->extraCheckbox->setTristate();
02312       d->extraCheckbox->setNoChange();
02313     }
02314     else {
02315       d->extraCheckbox->setTristate(false);
02316       d->extraCheckbox->setChecked(permissions & UniExec);
02317     }
02318     break;
02319   case PermissionsOnlyDirs:
02320     enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
02321     // if this is a dir, and we can change permissions, don't dis-allow
02322     // recursive, we can do that for ACL setting.
02323     if ( d->cbRecursive )
02324        d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular );
02325 
02326     if (d->canChangePermissions)
02327       d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
02328                    i18n("This folder uses advanced permissions.",
02329                       "These folders use advanced permissions.",
02330                       properties->items().count()) : "");
02331     if (d->partialPermissions & S_ISVTX) {
02332       d->extraCheckbox->setTristate();
02333       d->extraCheckbox->setNoChange();
02334     }
02335     else {
02336       d->extraCheckbox->setTristate(false);
02337       d->extraCheckbox->setChecked(permissions & S_ISVTX);
02338     }
02339     break;
02340   case PermissionsMixed:
02341     enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
02342     if (d->canChangePermissions)
02343       d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
02344                    i18n("These files use advanced permissions.") : "");
02345     break;
02346     if (d->partialPermissions & S_ISVTX) {
02347       d->extraCheckbox->setTristate();
02348       d->extraCheckbox->setNoChange();
02349     }
02350     else {
02351       d->extraCheckbox->setTristate(false);
02352       d->extraCheckbox->setChecked(permissions & S_ISVTX);
02353     }
02354     break;
02355   }
02356 }
02357 
02358 // gets masks for files and dirs from the Access Control frame widgets
02359 void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions,
02360                              mode_t &andDirPermissions,
02361                              mode_t &orFilePermissions,
02362                              mode_t &orDirPermissions) {
02363   andFilePermissions = mode_t(~UniSpecial);
02364   andDirPermissions = mode_t(~(S_ISUID|S_ISGID));
02365   orFilePermissions = 0;
02366   orDirPermissions = 0;
02367   if (d->isIrregular)
02368     return;
02369 
02370   mode_t m = standardPermissions[d->ownerPermCombo->currentItem()];
02371   if (m != (mode_t) -1) {
02372     orFilePermissions |= m & UniOwner;
02373     if ((m & UniOwner) &&
02374     ((d->pmode == PermissionsMixed) ||
02375      ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange))))
02376       andFilePermissions &= ~(S_IRUSR | S_IWUSR);
02377     else {
02378       andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
02379       if ((m & S_IRUSR) && (d->extraCheckbox->state() == QButton::On))
02380     orFilePermissions |= S_IXUSR;
02381     }
02382 
02383     orDirPermissions |= m & UniOwner;
02384     if (m & S_IRUSR)
02385     orDirPermissions |= S_IXUSR;
02386     andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
02387   }
02388 
02389   m = standardPermissions[d->groupPermCombo->currentItem()];
02390   if (m != (mode_t) -1) {
02391     orFilePermissions |= m & UniGroup;
02392     if ((m & UniGroup) &&
02393     ((d->pmode == PermissionsMixed) ||
02394      ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange))))
02395       andFilePermissions &= ~(S_IRGRP | S_IWGRP);
02396     else {
02397       andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
02398       if ((m & S_IRGRP) && (d->extraCheckbox->state() == QButton::On))
02399     orFilePermissions |= S_IXGRP;
02400     }
02401 
02402     orDirPermissions |= m & UniGroup;
02403     if (m & S_IRGRP)
02404     orDirPermissions |= S_IXGRP;
02405     andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
02406   }
02407 
02408   m = standardPermissions[d->othersPermCombo->currentItem()];
02409   if (m != (mode_t) -1) {
02410     orFilePermissions |= m & UniOthers;
02411     if ((m & UniOthers) &&
02412     ((d->pmode == PermissionsMixed) ||
02413      ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange))))
02414       andFilePermissions &= ~(S_IROTH | S_IWOTH);
02415     else {
02416       andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
02417       if ((m & S_IROTH) && (d->extraCheckbox->state() == QButton::On))
02418     orFilePermissions |= S_IXOTH;
02419     }
02420 
02421     orDirPermissions |= m & UniOthers;
02422     if (m & S_IROTH)
02423     orDirPermissions |= S_IXOTH;
02424     andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
02425   }
02426 
02427   if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) &&
02428       (d->extraCheckbox->state() != QButton::NoChange)) {
02429     andDirPermissions &= ~S_ISVTX;
02430     if (d->extraCheckbox->state() == QButton::On)
02431       orDirPermissions |= S_ISVTX;
02432   }
02433 }
02434 
02435 void KFilePermissionsPropsPlugin::applyChanges()
02436 {
02437   mode_t orFilePermissions;
02438   mode_t orDirPermissions;
02439   mode_t andFilePermissions;
02440   mode_t andDirPermissions;
02441 
02442   if (!d->canChangePermissions)
02443     return;
02444 
02445   if (!d->isIrregular)
02446     getPermissionMasks(andFilePermissions,
02447                andDirPermissions,
02448                orFilePermissions,
02449                orDirPermissions);
02450   else {
02451     orFilePermissions = permissions;
02452     andFilePermissions = d->partialPermissions;
02453     orDirPermissions = permissions;
02454     andDirPermissions = d->partialPermissions;
02455   }
02456 
02457   QString owner, group;
02458   if (usrEdit)
02459     owner = usrEdit->text();
02460   if (grpEdit)
02461     group = grpEdit->text();
02462   else if (grpCombo)
02463     group = grpCombo->currentText();
02464 
02465   if (owner == strOwner)
02466       owner = QString::null; // no change
02467 
02468   if (group == strGroup)
02469       group = QString::null;
02470 
02471   bool recursive = d->cbRecursive && d->cbRecursive->isChecked();
02472   bool permissionChange = false;
02473 
02474   KFileItemList files, dirs;
02475   KFileItemList items = properties->items();
02476   for (KFileItemListIterator it(items); it.current(); ++it) {
02477     if ((*it)->isDir()) {
02478       dirs.append(*it);
02479       if ((*it)->permissions() != (((*it)->permissions() & andDirPermissions) | orDirPermissions))
02480     permissionChange = true;
02481     }
02482     else if ((*it)->isFile()) {
02483       files.append(*it);
02484       if ((*it)->permissions() != (((*it)->permissions() & andFilePermissions) | orFilePermissions))
02485     permissionChange = true;
02486     }
02487   }
02488 
02489   const bool ACLChange = ( d->extendedACL !=  properties->item()->ACL() );
02490   const bool defaultACLChange = ( d->defaultACL != properties->item()->defaultACL() );
02491 
02492   if ( owner.isEmpty() && group.isEmpty() && !recursive
02493       && !permissionChange && !ACLChange && !defaultACLChange )
02494     return;
02495 
02496   KIO::Job * job;
02497   if (files.count() > 0) {
02498     job = KIO::chmod( files, orFilePermissions, ~andFilePermissions,
02499         owner, group, false );
02500     if ( ACLChange && d->fileSystemSupportsACLs )
02501       job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
02502     if ( defaultACLChange && d->fileSystemSupportsACLs )
02503       job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
02504 
02505     connect( job, SIGNAL( result( KIO::Job * ) ),
02506         SLOT( slotChmodResult( KIO::Job * ) ) );
02507     // Wait for job
02508     QWidget dummy(0,0,WType_Dialog|WShowModal);
02509     qt_enter_modal(&dummy);
02510     qApp->enter_loop();
02511     qt_leave_modal(&dummy);
02512   }
02513   if (dirs.count() > 0) {
02514     job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
02515         owner, group, recursive );
02516     if ( ACLChange && d->fileSystemSupportsACLs )
02517       job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
02518     if ( defaultACLChange && d->fileSystemSupportsACLs )
02519       job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
02520 
02521     connect( job, SIGNAL( result( KIO::Job * ) ),
02522         SLOT( slotChmodResult( KIO::Job * ) ) );
02523     // Wait for job
02524     QWidget dummy(0,0,WType_Dialog|WShowModal);
02525     qt_enter_modal(&dummy);
02526     qApp->enter_loop();
02527     qt_leave_modal(&dummy);
02528   }
02529 }
02530 
02531 void KFilePermissionsPropsPlugin::slotChmodResult( KIO::Job * job )
02532 {
02533   kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl;
02534   if (job->error())
02535     job->showErrorDialog( d->m_frame );
02536   // allow apply() to return
02537   qApp->exit_loop();
02538 }
02539 
02540 
02541 
02542 
02543 class KURLPropsPlugin::KURLPropsPluginPrivate
02544 {
02545 public:
02546   KURLPropsPluginPrivate()
02547   {
02548   }
02549   ~KURLPropsPluginPrivate()
02550   {
02551   }
02552 
02553   QFrame *m_frame;
02554 };
02555 
02556 KURLPropsPlugin::KURLPropsPlugin( KPropertiesDialog *_props )
02557   : KPropsDlgPlugin( _props )
02558 {
02559   d = new KURLPropsPluginPrivate;
02560   d->m_frame = properties->addPage(i18n("U&RL"));
02561   QVBoxLayout *layout = new QVBoxLayout(d->m_frame, 0, KDialog::spacingHint());
02562 
02563   QLabel *l;
02564   l = new QLabel( d->m_frame, "Label_1" );
02565   l->setText( i18n("URL:") );
02566   layout->addWidget(l);
02567 
02568   URLEdit = new KURLRequester( d->m_frame, "URL Requester" );
02569   layout->addWidget(URLEdit);
02570 
02571   QString path = properties->kurl().path();
02572 
02573   QFile f( path );
02574   if ( !f.open( IO_ReadOnly ) )
02575     return;
02576   f.close();
02577 
02578   KSimpleConfig config( path );
02579   config.setDesktopGroup();
02580   URLStr = config.readPathEntry( "URL" );
02581 
02582   if ( !URLStr.isNull() )
02583     URLEdit->setURL( URLStr );
02584 
02585   connect( URLEdit, SIGNAL( textChanged( const QString & ) ),
02586            this, SIGNAL( changed() ) );
02587 
02588   layout->addStretch (1);
02589 }
02590 
02591 KURLPropsPlugin::~KURLPropsPlugin()
02592 {
02593   delete d;
02594 }
02595 
02596 // QString KURLPropsPlugin::tabName () const
02597 // {
02598 //   return i18n ("U&RL");
02599 // }
02600 
02601 bool KURLPropsPlugin::supports( KFileItemList _items )
02602 {
02603   if ( _items.count() != 1 )
02604     return false;
02605   KFileItem * item = _items.first();
02606   // check if desktop file
02607   if ( !KPropsDlgPlugin::isDesktopFile( item ) )
02608     return false;
02609 
02610   // open file and check type
02611   KDesktopFile config( item->url().path(), true /* readonly */ );
02612   return config.hasLinkType();
02613 }
02614 
02615 void KURLPropsPlugin::applyChanges()
02616 {
02617   QString path = properties->kurl().path();
02618 
02619   QFile f( path );
02620   if ( !f.open( IO_ReadWrite ) ) {
02621     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
02622                 "sufficient access to write to <b>%1</b>.</qt>").arg(path));
02623     return;
02624   }
02625   f.close();
02626 
02627   KSimpleConfig config( path );
02628   config.setDesktopGroup();
02629   config.writeEntry( "Type", QString::fromLatin1("Link"));
02630   config.writePathEntry( "URL", URLEdit->url() );
02631   // Users can't create a Link .desktop file with a Name field,
02632   // but distributions can. Update the Name field in that case.
02633   if ( config.hasKey("Name") )
02634   {
02635     QString nameStr = nameFromFileName(properties->kurl().fileName());
02636     config.writeEntry( "Name", nameStr );
02637     config.writeEntry( "Name", nameStr, true, false, true );
02638 
02639   }
02640 }
02641 
02642 
02643 /* ----------------------------------------------------
02644  *
02645  * KBindingPropsPlugin
02646  *
02647  * -------------------------------------------------- */
02648 
02649 class KBindingPropsPlugin::KBindingPropsPluginPrivate
02650 {
02651 public:
02652   KBindingPropsPluginPrivate()
02653   {
02654   }
02655   ~KBindingPropsPluginPrivate()
02656   {
02657   }
02658 
02659   QFrame *m_frame;
02660 };
02661 
02662 KBindingPropsPlugin::KBindingPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props )
02663 {
02664   d = new KBindingPropsPluginPrivate;
02665   d->m_frame = properties->addPage(i18n("A&ssociation"));
02666   patternEdit = new KLineEdit( d->m_frame, "LineEdit_1" );
02667   commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" );
02668   mimeEdit = new KLineEdit( d->m_frame, "LineEdit_3" );
02669 
02670   QBoxLayout *mainlayout = new QVBoxLayout(d->m_frame, 0, KDialog::spacingHint());
02671   QLabel* tmpQLabel;
02672 
02673   tmpQLabel = new QLabel( d->m_frame, "Label_1" );
02674   tmpQLabel->setText(  i18n("Pattern ( example: *.html;*.htm )") );
02675   tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
02676   mainlayout->addWidget(tmpQLabel, 1);
02677 
02678   //patternEdit->setGeometry( 10, 40, 210, 30 );
02679   //patternEdit->setText( "" );
02680   patternEdit->setMaxLength( 512 );
02681   patternEdit->setMinimumSize( patternEdit->sizeHint() );
02682   patternEdit->setFixedHeight( fontHeight );
02683   mainlayout->addWidget(patternEdit, 1);
02684 
02685   tmpQLabel = new QLabel( d->m_frame, "Label_2" );
02686   tmpQLabel->setText(  i18n("Mime Type") );
02687   tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
02688   mainlayout->addWidget(tmpQLabel, 1);
02689 
02690   //mimeEdit->setGeometry( 10, 160, 210, 30 );
02691   mimeEdit->setMaxLength( 256 );
02692   mimeEdit->setMinimumSize( mimeEdit->sizeHint() );
02693   mimeEdit->setFixedHeight( fontHeight );
02694   mainlayout->addWidget(mimeEdit, 1);
02695 
02696   tmpQLabel = new QLabel( d->m_frame, "Label_3" );
02697   tmpQLabel->setText(  i18n("Comment") );
02698   tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
02699   mainlayout->addWidget(tmpQLabel, 1);
02700 
02701   //commentEdit->setGeometry( 10, 100, 210, 30 );
02702   commentEdit->setMaxLength( 256 );
02703   commentEdit->setMinimumSize( commentEdit->sizeHint() );
02704   commentEdit->setFixedHeight( fontHeight );
02705   mainlayout->addWidget(commentEdit, 1);
02706 
02707   cbAutoEmbed = new QCheckBox( i18n("Left click previews"), d->m_frame, "cbAutoEmbed" );
02708   mainlayout->addWidget(cbAutoEmbed, 1);
02709 
02710   mainlayout->addStretch (10);
02711   mainlayout->activate();
02712 
02713   QFile f( _props->kurl().path() );
02714   if ( !f.open( IO_ReadOnly ) )
02715     return;
02716   f.close();
02717 
02718   KSimpleConfig config( _props->kurl().path() );
02719   config.setDesktopGroup();
02720   QString patternStr = config.readEntry( "Patterns" );
02721   QString iconStr = config.readEntry( "Icon" );
02722   QString commentStr = config.readEntry( "Comment" );
02723   m_sMimeStr = config.readEntry( "MimeType" );
02724 
02725   if ( !patternStr.isEmpty() )
02726     patternEdit->setText( patternStr );
02727   if ( !commentStr.isEmpty() )
02728     commentEdit->setText( commentStr );
02729   if ( !m_sMimeStr.isEmpty() )
02730     mimeEdit->setText( m_sMimeStr );
02731   cbAutoEmbed->setTristate();
02732   if ( config.hasKey( "X-KDE-AutoEmbed" ) )
02733       cbAutoEmbed->setChecked( config.readBoolEntry( "X-KDE-AutoEmbed" ) );
02734   else
02735       cbAutoEmbed->setNoChange();
02736 
02737   connect( patternEdit, SIGNAL( textChanged( const QString & ) ),
02738            this, SIGNAL( changed() ) );
02739   connect( commentEdit, SIGNAL( textChanged( const QString & ) ),
02740            this, SIGNAL( changed() ) );
02741   connect( mimeEdit, SIGNAL( textChanged( const QString & ) ),
02742            this, SIGNAL( changed() ) );
02743   connect( cbAutoEmbed, SIGNAL( toggled( bool ) ),
02744            this, SIGNAL( changed() ) );
02745 }
02746 
02747 KBindingPropsPlugin::~KBindingPropsPlugin()
02748 {
02749   delete d;
02750 }
02751 
02752 // QString KBindingPropsPlugin::tabName () const
02753 // {
02754 //   return i18n ("A&ssociation");
02755 // }
02756 
02757 bool KBindingPropsPlugin::supports( KFileItemList _items )
02758 {
02759   if ( _items.count() != 1 )
02760     return false;
02761   KFileItem * item = _items.first();
02762   // check if desktop file
02763   if ( !KPropsDlgPlugin::isDesktopFile( item ) )
02764     return false;
02765 
02766   // open file and check type
02767   KDesktopFile config( item->url().path(), true /* readonly */ );
02768   return config.hasMimeTypeType();
02769 }
02770 
02771 void KBindingPropsPlugin::applyChanges()
02772 {
02773   QString path = properties->kurl().path();
02774   QFile f( path );
02775 
02776   if ( !f.open( IO_ReadWrite ) )
02777   {
02778     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
02779                 "sufficient access to write to <b>%1</b>.</qt>").arg(path));
02780     return;
02781   }
02782   f.close();
02783 
02784   KSimpleConfig config( path );
02785   config.setDesktopGroup();
02786   config.writeEntry( "Type", QString::fromLatin1("MimeType") );
02787 
02788   config.writeEntry( "Patterns",  patternEdit->text() );
02789   config.writeEntry( "Comment", commentEdit->text() );
02790   config.writeEntry( "Comment",
02791              commentEdit->text(), true, false, true ); // for compat
02792   config.writeEntry( "MimeType", mimeEdit->text() );
02793   if ( cbAutoEmbed->state() == QButton::NoChange )
02794       config.deleteEntry( "X-KDE-AutoEmbed", false );
02795   else
02796       config.writeEntry( "X-KDE-AutoEmbed", cbAutoEmbed->isChecked() );
02797   config.sync();
02798 }
02799 
02800 /* ----------------------------------------------------
02801  *
02802  * KDevicePropsPlugin
02803  *
02804  * -------------------------------------------------- */
02805 
02806 class KDevicePropsPlugin::KDevicePropsPluginPrivate
02807 {
02808 public:
02809   KDevicePropsPluginPrivate()
02810   {
02811   }
02812   ~KDevicePropsPluginPrivate()
02813   {
02814   }
02815 
02816   QFrame *m_frame;
02817   QStringList mountpointlist;
02818   QLabel *m_freeSpaceText;
02819   QLabel *m_freeSpaceLabel;
02820   QProgressBar *m_freeSpaceBar;
02821 };
02822 
02823 KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props )
02824 {
02825   d = new KDevicePropsPluginPrivate;
02826   d->m_frame = properties->addPage(i18n("De&vice"));
02827 
02828   QStringList devices;
02829   KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
02830 
02831   for(KMountPoint::List::ConstIterator it = mountPoints.begin();
02832       it != mountPoints.end(); ++it)
02833   {
02834      KMountPoint *mp = *it;
02835      QString mountPoint = mp->mountPoint();
02836      QString device = mp->mountedFrom();
02837      kdDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType()<<endl;
02838 
02839      if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty()
02840           && device != "none")
02841      {
02842         devices.append( device + QString::fromLatin1(" (")
02843                         + mountPoint + QString::fromLatin1(")") );
02844         m_devicelist.append(device);
02845         d->mountpointlist.append(mountPoint);
02846      }
02847   }
02848 
02849   QGridLayout *layout = new QGridLayout( d->m_frame, 0, 2, 0,
02850                                         KDialog::spacingHint());
02851   layout->setColStretch(1, 1);
02852 
02853   QLabel* label;
02854   label = new QLabel( d->m_frame );
02855   label->setText( devices.count() == 0 ?
02856                       i18n("Device (/dev/fd0):") : // old style
02857                       i18n("Device:") ); // new style (combobox)
02858   layout->addWidget(label, 0, 0);
02859 
02860   device = new QComboBox( true, d->m_frame, "ComboBox_device" );
02861   device->insertStringList( devices );
02862   layout->addWidget(device, 0, 1);
02863   connect( device, SIGNAL( activated( int ) ),
02864            this, SLOT( slotActivated( int ) ) );
02865 
02866   readonly = new QCheckBox( d->m_frame, "CheckBox_readonly" );
02867   readonly->setText(  i18n("Read only") );
02868   layout->addWidget(readonly, 1, 1);
02869 
02870   label = new QLabel( d->m_frame );
02871   label->setText( i18n("File system:") );
02872   layout->addWidget(label, 2, 0);
02873 
02874   QLabel *fileSystem = new QLabel( d->m_frame );
02875   layout->addWidget(fileSystem, 2, 1);
02876 
02877   label = new QLabel( d->m_frame );
02878   label->setText( devices.count()==0 ?
02879                       i18n("Mount point (/mnt/floppy):") : // old style
02880                       i18n("Mount point:")); // new style (combobox)
02881   layout->addWidget(label, 3, 0);
02882 
02883   mountpoint = new QLabel( d->m_frame, "LineEdit_mountpoint" );
02884 
02885   layout->addWidget(mountpoint, 3, 1);
02886 
02887   // show disk free
02888   d->m_freeSpaceText = new QLabel(i18n("Free disk space:"), d->m_frame );
02889   layout->addWidget(d->m_freeSpaceText, 4, 0);
02890 
02891   d->m_freeSpaceLabel = new QLabel( d->m_frame );
02892   layout->addWidget( d->m_freeSpaceLabel, 4, 1 );
02893 
02894   d->m_freeSpaceBar = new QProgressBar( d->m_frame, "freeSpaceBar" );
02895   layout->addMultiCellWidget(d->m_freeSpaceBar, 5, 5, 0, 1);
02896 
02897   // we show it in the slot when we know the values
02898   d->m_freeSpaceText->hide();
02899   d->m_freeSpaceLabel->hide();
02900   d->m_freeSpaceBar->hide();
02901 
02902   KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame);
02903   layout->addMultiCellWidget(sep, 6, 6, 0, 1);
02904 
02905   unmounted = new KIconButton( d->m_frame );
02906   int bsize = 66 + 2 * unmounted->style().pixelMetric(QStyle::PM_ButtonMargin);
02907   unmounted->setFixedSize(bsize, bsize);
02908   unmounted->setIconType(KIcon::Desktop, KIcon::Device);
02909   layout->addWidget(unmounted, 7, 0);
02910 
02911   label = new QLabel( i18n("Unmounted Icon"),  d->m_frame );
02912   layout->addWidget(label, 7, 1);
02913 
02914   layout->setRowStretch(8, 1);
02915 
02916   QString path( _props->kurl().path() );
02917 
02918   QFile f( path );
02919   if ( !f.open( IO_ReadOnly ) )
02920     return;
02921   f.close();
02922 
02923   KSimpleConfig config( path );
02924   config.setDesktopGroup();
02925   QString deviceStr = config.readEntry( "Dev" );
02926   QString mountPointStr = config.readEntry( "MountPoint" );
02927   bool ro = config.readBoolEntry( "ReadOnly", false );
02928   QString unmountedStr = config.readEntry( "UnmountIcon" );
02929 
02930   fileSystem->setText( i18n(config.readEntry("FSType").local8Bit()) );
02931 
02932   device->setEditText( deviceStr );
02933   if ( !deviceStr.isEmpty() ) {
02934     // Set default options for this device (first matching entry)
02935     int index = m_devicelist.findIndex(deviceStr);
02936     if (index != -1)
02937     {
02938       //kdDebug(250) << "found it " << index << endl;
02939       slotActivated( index );
02940     }
02941   }
02942 
02943   if ( !mountPointStr.isEmpty() )
02944   {
02945     mountpoint->setText( mountPointStr );
02946     updateInfo();
02947   }
02948 
02949   readonly->setChecked( ro );
02950 
02951   if ( unmountedStr.isEmpty() )
02952     unmountedStr = KMimeType::defaultMimeTypePtr()->KServiceType::icon(); // default icon
02953 
02954   unmounted->setIcon( unmountedStr );
02955 
02956   connect( device, SIGNAL( activated( int ) ),
02957            this, SIGNAL( changed() ) );
02958   connect( device, SIGNAL( textChanged( const QString & ) ),
02959            this, SIGNAL( changed() ) );
02960   connect( readonly, SIGNAL( toggled( bool ) ),
02961            this, SIGNAL( changed() ) );
02962   connect( unmounted, SIGNAL( iconChanged( QString ) ),
02963            this, SIGNAL( changed() ) );
02964 
02965   connect( device, SIGNAL( textChanged( const QString & ) ),
02966            this, SLOT( slotDeviceChanged() ) );
02967 }
02968 
02969 KDevicePropsPlugin::~KDevicePropsPlugin()
02970 {
02971   delete d;
02972 }
02973 
02974 // QString KDevicePropsPlugin::tabName () const
02975 // {
02976 //   return i18n ("De&vice");
02977 // }
02978 
02979 void KDevicePropsPlugin::updateInfo()
02980 {
02981   // we show it in the slot when we know the values
02982   d->m_freeSpaceText->hide();
02983   d->m_freeSpaceLabel->hide();
02984   d->m_freeSpaceBar->hide();
02985 
02986   if ( !mountpoint->text().isEmpty() )
02987   {
02988     KDiskFreeSp * job = new KDiskFreeSp;
02989     connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&,
02990                                            const unsigned long&, const QString& ) ),
02991              this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&,
02992                                               const unsigned long&, const QString& ) ) );
02993 
02994     job->readDF( mountpoint->text() );
02995   }
02996 }
02997 
02998 void KDevicePropsPlugin::slotActivated( int index )
02999 {
03000   // Update mountpoint so that it matches the device that was selected in the combo
03001   device->setEditText( m_devicelist[index] );
03002   mountpoint->setText( d->mountpointlist[index] );
03003 
03004   updateInfo();
03005 }
03006 
03007 void KDevicePropsPlugin::slotDeviceChanged()
03008 {
03009   // Update mountpoint so that it matches the typed device
03010   int index = m_devicelist.findIndex( device->currentText() );
03011   if ( index != -1 )
03012     mountpoint->setText( d->mountpointlist[index] );
03013   else
03014     mountpoint->setText( QString::null );
03015 
03016   updateInfo();
03017 }
03018 
03019 void KDevicePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize,
03020                                               const unsigned long& /*kBUsed*/,
03021                                               const unsigned long& kBAvail,
03022                                               const QString& )
03023 {
03024   d->m_freeSpaceText->show();
03025   d->m_freeSpaceLabel->show();
03026 
03027   int percUsed = 100 - (int)(100.0 * kBAvail / kBSize);
03028 
03029   d->m_freeSpaceLabel->setText(
03030       // xgettext:no-c-format  --  Don't warn about translating the %1 out of %2 part.
03031       i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
03032       .arg(KIO::convertSizeFromKB(kBAvail))
03033       .arg(KIO::convertSizeFromKB(kBSize))
03034       .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
03035 
03036   d->m_freeSpaceBar->setProgress(percUsed, 100);
03037   d->m_freeSpaceBar->show();
03038 }
03039 
03040 bool KDevicePropsPlugin::supports( KFileItemList _items )
03041 {
03042   if ( _items.count() != 1 )
03043     return false;
03044   KFileItem * item = _items.first();
03045   // check if desktop file
03046   if ( !KPropsDlgPlugin::isDesktopFile( item ) )
03047     return false;
03048   // open file and check type
03049   KDesktopFile config( item->url().path(), true /* readonly */ );
03050   return config.hasDeviceType();
03051 }
03052 
03053 void KDevicePropsPlugin::applyChanges()
03054 {
03055   QString path = properties->kurl().path();
03056   QFile f( path );
03057   if ( !f.open( IO_ReadWrite ) )
03058   {
03059     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient "
03060                 "access to write to <b>%1</b>.</qt>").arg(path));
03061     return;
03062   }
03063   f.close();
03064 
03065   KSimpleConfig config( path );
03066   config.setDesktopGroup();
03067   config.writeEntry( "Type", QString::fromLatin1("FSDevice") );
03068 
03069   config.writeEntry( "Dev", device->currentText() );
03070   config.writeEntry( "MountPoint", mountpoint->text() );
03071 
03072   config.writeEntry( "UnmountIcon", unmounted->icon() );
03073   kdDebug(250) << "unmounted->icon() = " << unmounted->icon() << endl;
03074 
03075   config.writeEntry( "ReadOnly", readonly->isChecked() );
03076 
03077   config.sync();
03078 }
03079 
03080 
03081 /* ----------------------------------------------------
03082  *
03083  * KDesktopPropsPlugin
03084  *
03085  * -------------------------------------------------- */
03086 
03087 
03088 KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props )
03089   : KPropsDlgPlugin( _props )
03090 {
03091   QFrame *frame = properties->addPage(i18n("&Application"));
03092   QVBoxLayout *mainlayout = new QVBoxLayout( frame, 0, KDialog::spacingHint() );
03093 
03094   w = new KPropertiesDesktopBase(frame);
03095   mainlayout->addWidget(w);
03096 
03097   bool bKDesktopMode = (QCString(qApp->name()) == "kdesktop"); // nasty heh?
03098 
03099   if (bKDesktopMode)
03100   {
03101     // Hide Name entry
03102     w->nameEdit->hide();
03103     w->nameLabel->hide();
03104   }
03105 
03106   w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
03107   w->pathEdit->lineEdit()->setAcceptDrops(false);
03108 
03109   connect( w->nameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03110   connect( w->genNameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03111   connect( w->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03112   connect( w->commandEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03113   connect( w->pathEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03114 
03115   connect( w->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
03116   connect( w->addFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotAddFiletype() ) );
03117   connect( w->delFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotDelFiletype() ) );
03118   connect( w->advancedButton, SIGNAL( clicked() ), this, SLOT( slotAdvanced() ) );
03119 
03120   // now populate the page
03121   QString path = _props->kurl().path();
03122   QFile f( path );
03123   if ( !f.open( IO_ReadOnly ) )
03124     return;
03125   f.close();
03126 
03127   KDesktopFile  config( path );
03128   QString nameStr = config.readName();
03129   QString genNameStr = config.readGenericName();
03130   QString commentStr = config.readComment();
03131   QString commandStr = config.readPathEntry( "Exec" );
03132   if (commandStr.left(12) == "ksystraycmd ")
03133   {
03134     commandStr.remove(0, 12);
03135     m_systrayBool = true;
03136   }
03137   else
03138     m_systrayBool = false;
03139 
03140   m_origCommandStr = commandStr;
03141   QString pathStr = config.readPathEntry( "Path" );
03142   m_terminalBool = config.readBoolEntry( "Terminal" );
03143   m_terminalOptionStr = config.readEntry( "TerminalOptions" );
03144   m_suidBool = config.readBoolEntry( "X-KDE-SubstituteUID" );
03145   m_suidUserStr = config.readEntry( "X-KDE-Username" );
03146   if( config.hasKey( "StartupNotify" ))
03147     m_startupBool = config.readBoolEntry( "StartupNotify", true );
03148   else
03149     m_startupBool = config.readBoolEntry( "X-KDE-StartupNotify", true );
03150   m_dcopServiceType = config.readEntry("X-DCOP-ServiceType").lower();
03151 
03152   QStringList mimeTypes = config.readListEntry( "MimeType", ';' );
03153 
03154   if ( nameStr.isEmpty() || bKDesktopMode ) {
03155     // We'll use the file name if no name is specified
03156     // because we _need_ a Name for a valid file.
03157     // But let's do it in apply, not here, so that we pick up the right name.
03158     setDirty();
03159   }
03160   if ( !bKDesktopMode )
03161     w->nameEdit->setText(nameStr);
03162 
03163   w->genNameEdit->setText( genNameStr );
03164   w->commentEdit->setText( commentStr );
03165   w->commandEdit->setText( commandStr );
03166   w->pathEdit->lineEdit()->setText( pathStr );
03167   w->filetypeList->setAllColumnsShowFocus(true);
03168 
03169   KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
03170   for(QStringList::ConstIterator it = mimeTypes.begin();
03171       it != mimeTypes.end(); )
03172   {
03173     KMimeType::Ptr p = KMimeType::mimeType(*it);
03174     ++it;
03175     QString preference;
03176     if (it != mimeTypes.end())
03177     {
03178        bool numeric;
03179        (*it).toInt(&numeric);
03180        if (numeric)
03181        {
03182          preference = *it;
03183          ++it;
03184        }
03185     }
03186     if (p && (p != defaultMimetype))
03187     {
03188        new QListViewItem(w->filetypeList, p->name(), p->comment(), preference);
03189     }
03190   }
03191 
03192 }
03193 
03194 KDesktopPropsPlugin::~KDesktopPropsPlugin()
03195 {
03196 }
03197 
03198 void KDesktopPropsPlugin::slotSelectMimetype()
03199 {
03200   QListView *w = (QListView*)sender();
03201   QListViewItem *item = w->firstChild();
03202   while(item)
03203   {
03204      if (item->isSelected())
03205         w->setSelected(item, false);
03206      item = item->nextSibling();
03207   }
03208 }
03209 
03210 void KDesktopPropsPlugin::slotAddFiletype()
03211 {
03212   KDialogBase dlg(w, "KPropertiesMimetypes", true,
03213                   i18n("Add File Type for %1").arg(properties->kurl().fileName()),
03214                   KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok);
03215 
03216   KGuiItem okItem(i18n("&Add"), QString::null /* no icon */,
03217                   i18n("Add the selected file types to\nthe list of supported file types."),
03218                   i18n("Add the selected file types to\nthe list of supported file types."));
03219   dlg.setButtonOK(okItem);
03220 
03221   KPropertiesMimetypeBase *mw = new KPropertiesMimetypeBase(&dlg);
03222 
03223   dlg.setMainWidget(mw);
03224 
03225   {
03226      mw->listView->setRootIsDecorated(true);
03227      mw->listView->setSelectionMode(QListView::Extended);
03228      mw->listView->setAllColumnsShowFocus(true);
03229      mw->listView->setFullWidth(true);
03230      mw->listView->setMinimumSize(500,400);
03231 
03232      connect(mw->listView, SIGNAL(selectionChanged()),
03233              this, SLOT(slotSelectMimetype()));
03234      connect(mw->listView, SIGNAL(doubleClicked( QListViewItem *, const QPoint &, int )),
03235              &dlg, SLOT( slotOk()));
03236 
03237      QMap<QString,QListViewItem*> majorMap;
03238      QListViewItem *majorGroup;
03239      KMimeType::List mimetypes = KMimeType::allMimeTypes();
03240      QValueListIterator<KMimeType::Ptr> it(mimetypes.begin());
03241      for (; it != mimetypes.end(); ++it) {
03242         QString mimetype = (*it)->name();
03243         if (mimetype == KMimeType::defaultMimeType())
03244            continue;
03245         int index = mimetype.find("/");
03246         QString maj = mimetype.left(index);
03247         QString min = mimetype.mid(index+1);
03248 
03249         QMapIterator<QString,QListViewItem*> mit = majorMap.find( maj );
03250         if ( mit == majorMap.end() ) {
03251            majorGroup = new QListViewItem( mw->listView, maj );
03252            majorGroup->setExpandable(true);
03253            mw->listView->setOpen(majorGroup, true);
03254            majorMap.insert( maj, majorGroup );
03255         }
03256         else
03257         {
03258            majorGroup = mit.data();
03259         }
03260 
03261         QListViewItem *item = new QListViewItem(majorGroup, min, (*it)->comment());
03262         item->setPixmap(0, (*it)->pixmap(KIcon::Small, IconSize(KIcon::Small)));
03263      }
03264      QMapIterator<QString,QListViewItem*> mit = majorMap.find( "all" );
03265      if ( mit != majorMap.end())
03266      {
03267         mw->listView->setCurrentItem(mit.data());
03268         mw->listView->ensureItemVisible(mit.data());
03269      }
03270   }
03271 
03272   if (dlg.exec() == KDialogBase::Accepted)
03273   {
03274      KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
03275      QListViewItem *majorItem = mw->listView->firstChild();
03276      while(majorItem)
03277      {
03278         QString major = majorItem->text(0);
03279 
03280         QListViewItem *minorItem = majorItem->firstChild();
03281         while(minorItem)
03282         {
03283            if (minorItem->isSelected())
03284            {
03285               QString mimetype = major + "/" + minorItem->text(0);
03286               KMimeType::Ptr p = KMimeType::mimeType(mimetype);
03287               if (p && (p != defaultMimetype))
03288               {
03289                  mimetype = p->name();
03290                  bool found = false;
03291                  QListViewItem *item = w->filetypeList->firstChild();
03292                  while (item)
03293                  {
03294                     if (mimetype == item->text(0))
03295                     {
03296                        found = true;
03297                        break;
03298                     }
03299                     item = item->nextSibling();
03300                  }
03301                  if (!found) {
03302                     new QListViewItem(w->filetypeList, p->name(), p->comment());
03303                     emit changed();
03304                  }
03305               }
03306            }
03307            minorItem = minorItem->nextSibling();
03308         }
03309 
03310         majorItem = majorItem->nextSibling();
03311      }
03312 
03313   }
03314 }
03315 
03316 void KDesktopPropsPlugin::slotDelFiletype()
03317 {
03318   delete w->filetypeList->currentItem();
03319   emit changed();
03320 }
03321 
03322 void KDesktopPropsPlugin::checkCommandChanged()
03323 {
03324   if (KRun::binaryName(w->commandEdit->text(), true) !=
03325       KRun::binaryName(m_origCommandStr, true))
03326   {
03327     QString m_origCommandStr = w->commandEdit->text();
03328     m_dcopServiceType= QString::null; // Reset
03329   }
03330 }
03331 
03332 void KDesktopPropsPlugin::applyChanges()
03333 {
03334   kdDebug(250) << "KDesktopPropsPlugin::applyChanges" << endl;
03335   QString path = properties->kurl().path();
03336 
03337   QFile f( path );
03338 
03339   if ( !f.open( IO_ReadWrite ) ) {
03340     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
03341                 "sufficient access to write to <b>%1</b>.</qt>").arg(path));
03342     return;
03343   }
03344   f.close();
03345 
03346   // If the command is changed we reset certain settings that are strongly
03347   // coupled to the command.
03348   checkCommandChanged();
03349 
03350   KSimpleConfig config( path );
03351   config.setDesktopGroup();
03352   config.writeEntry( "Type", QString::fromLatin1("Application"));
03353   config.writeEntry( "Comment", w->commentEdit->text() );
03354   config.writeEntry( "Comment", w->commentEdit->text(), true, false, true ); // for compat
03355   config.writeEntry( "GenericName", w->genNameEdit->text() );
03356   config.writeEntry( "GenericName", w->genNameEdit->text(), true, false, true ); // for compat
03357 
03358   if (m_systrayBool)
03359     config.writePathEntry( "Exec", w->commandEdit->text().prepend("ksystraycmd ") );
03360   else
03361     config.writePathEntry( "Exec", w->commandEdit->text() );
03362   config.writePathEntry( "Path", w->pathEdit->lineEdit()->text() );
03363 
03364   // Write mimeTypes
03365   QStringList mimeTypes;
03366   for( QListViewItem *item = w->filetypeList->firstChild();
03367        item; item = item->nextSibling() )
03368   {
03369     QString preference = item->text(2);
03370     mimeTypes.append(item->text(0));
03371     if (!preference.isEmpty())
03372        mimeTypes.append(preference);
03373   }
03374 
03375   config.writeEntry( "MimeType", mimeTypes, ';' );
03376 
03377   if ( !w->nameEdit->isHidden() ) {
03378       QString nameStr = w->nameEdit->text();
03379       config.writeEntry( "Name", nameStr );
03380       config.writeEntry( "Name", nameStr, true, false, true );
03381   }
03382 
03383   config.writeEntry("Terminal", m_terminalBool);
03384   config.writeEntry("TerminalOptions", m_terminalOptionStr);
03385   config.writeEntry("X-KDE-SubstituteUID", m_suidBool);
03386   config.writeEntry("X-KDE-Username", m_suidUserStr);
03387   config.writeEntry("StartupNotify", m_startupBool);
03388   config.writeEntry("X-DCOP-ServiceType", m_dcopServiceType);
03389   config.sync();
03390 
03391   // KSycoca update needed?
03392   QString sycocaPath = KGlobal::dirs()->relativeLocation("apps", path);
03393   bool updateNeeded = !sycocaPath.startsWith("/");
03394   if (!updateNeeded)
03395   {
03396      sycocaPath = KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
03397      updateNeeded = !sycocaPath.startsWith("/");
03398   }
03399   if (updateNeeded)
03400      KService::rebuildKSycoca(w);
03401 }
03402 
03403 
03404 void KDesktopPropsPlugin::slotBrowseExec()
03405 {
03406   KURL f = KFileDialog::getOpenURL( QString::null,
03407                                       QString::null, w );
03408   if ( f.isEmpty() )
03409     return;
03410 
03411   if ( !f.isLocalFile()) {
03412     KMessageBox::sorry(w, i18n("Only executables on local file systems are supported."));
03413     return;
03414   }
03415 
03416   QString path = f.path();
03417   KRun::shellQuote( path );
03418   w->commandEdit->setText( path );
03419 }
03420 
03421 void KDesktopPropsPlugin::slotAdvanced()
03422 {
03423   KDialogBase dlg(w, "KPropertiesDesktopAdv", true,
03424       i18n("Advanced Options for %1").arg(properties->kurl().fileName()),
03425       KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok);
03426   KPropertiesDesktopAdvBase *w = new KPropertiesDesktopAdvBase(&dlg);
03427 
03428   dlg.setMainWidget(w);
03429 
03430   // If the command is changed we reset certain settings that are strongly
03431   // coupled to the command.
03432   checkCommandChanged();
03433 
03434   // check to see if we use konsole if not do not add the nocloseonexit
03435   // because we don't know how to do this on other terminal applications
03436   KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
03437   QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
03438                           QString::fromLatin1("konsole"));
03439 
03440   bool terminalCloseBool = false;
03441 
03442   if (preferredTerminal == "konsole")
03443   {
03444      terminalCloseBool = (m_terminalOptionStr.contains( "--noclose" ) > 0);
03445      w->terminalCloseCheck->setChecked(terminalCloseBool);
03446      m_terminalOptionStr.replace( "--noclose", "");
03447   }
03448   else
03449   {
03450      w->terminalCloseCheck->hide();
03451   }
03452 
03453   w->terminalCheck->setChecked(m_terminalBool);
03454   w->terminalEdit->setText(m_terminalOptionStr);
03455   w->terminalCloseCheck->setEnabled(m_terminalBool);
03456   w->terminalEdit->setEnabled(m_terminalBool);
03457   w->terminalEditLabel->setEnabled(m_terminalBool);
03458 
03459   w->suidCheck->setChecked(m_suidBool);
03460   w->suidEdit->setText(m_suidUserStr);
03461   w->suidEdit->setEnabled(m_suidBool);
03462   w->suidEditLabel->setEnabled(m_suidBool);
03463 
03464   w->startupInfoCheck->setChecked(m_startupBool);
03465   w->systrayCheck->setChecked(m_systrayBool);
03466 
03467   if (m_dcopServiceType == "unique")
03468     w->dcopCombo->setCurrentItem(2);
03469   else if (m_dcopServiceType == "multi")
03470     w->dcopCombo->setCurrentItem(1);
03471   else if (m_dcopServiceType == "wait")
03472     w->dcopCombo->setCurrentItem(3);
03473   else
03474     w->dcopCombo->setCurrentItem(0);
03475 
03476   // Provide username completion up to 1000 users.
03477   KCompletion *kcom = new KCompletion;
03478   kcom->setOrder(KCompletion::Sorted);
03479   struct passwd *pw;
03480   int i, maxEntries = 1000;
03481   setpwent();
03482   for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
03483     kcom->addItem(QString::fromLatin1(pw->pw_name));
03484   endpwent();
03485   if (i < maxEntries)
03486   {
03487     w->suidEdit->setCompletionObject(kcom, true);
03488     w->suidEdit->setAutoDeleteCompletionObject( true );
03489     w->suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
03490   }
03491   else
03492   {
03493     delete kcom;
03494   }
03495 
03496   connect( w->terminalEdit, SIGNAL( textChanged( const QString & ) ),
03497            this, SIGNAL( changed() ) );
03498   connect( w->terminalCloseCheck, SIGNAL( toggled( bool ) ),
03499            this, SIGNAL( changed() ) );
03500   connect( w->terminalCheck, SIGNAL( toggled( bool ) ),
03501            this, SIGNAL( changed() ) );
03502   connect( w->suidCheck, SIGNAL( toggled( bool ) ),
03503            this, SIGNAL( changed() ) );
03504   connect( w->suidEdit, SIGNAL( textChanged( const QString & ) ),
03505            this, SIGNAL( changed() ) );
03506   connect( w->startupInfoCheck, SIGNAL( toggled( bool ) ),
03507            this, SIGNAL( changed() ) );
03508   connect( w->systrayCheck, SIGNAL( toggled( bool ) ),
03509            this, SIGNAL( changed() ) );
03510   connect( w->dcopCombo, SIGNAL( highlighted( int ) ),
03511            this, SIGNAL( changed() ) );
03512 
03513   if ( dlg.exec() == QDialog::Accepted )
03514   {
03515     m_terminalOptionStr = w->terminalEdit->text().stripWhiteSpace();
03516     m_terminalBool = w->terminalCheck->isChecked();
03517     m_suidBool = w->suidCheck->isChecked();
03518     m_suidUserStr = w->suidEdit->text().stripWhiteSpace();
03519     m_startupBool = w->startupInfoCheck->isChecked();
03520     m_systrayBool = w->systrayCheck->isChecked();
03521 
03522     if (w->terminalCloseCheck->isChecked())
03523     {
03524       m_terminalOptionStr.append(" --noclose");
03525     }
03526 
03527     switch(w->dcopCombo->currentItem())
03528     {
03529       case 1:  m_dcopServiceType = "multi"; break;
03530       case 2:  m_dcopServiceType = "unique"; break;
03531       case 3:  m_dcopServiceType = "wait"; break;
03532       default: m_dcopServiceType = "none"; break;
03533     }
03534   }
03535 }
03536 
03537 bool KDesktopPropsPlugin::supports( KFileItemList _items )
03538 {
03539   if ( _items.count() != 1 )
03540     return false;
03541   KFileItem * item = _items.first();
03542   // check if desktop file
03543   if ( !KPropsDlgPlugin::isDesktopFile( item ) )
03544     return false;
03545   // open file and check type
03546   KDesktopFile config( item->url().path(), true /* readonly */ );
03547   return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access");
03548 }
03549 
03550 void KPropertiesDialog::virtual_hook( int id, void* data )
03551 { KDialogBase::virtual_hook( id, data ); }
03552 
03553 void KPropsDlgPlugin::virtual_hook( int, void* )
03554 { /*BASE::virtual_hook( id, data );*/ }
03555 
03556 
03557 
03558 
03559 
03565 class KExecPropsPlugin::KExecPropsPluginPrivate
03566 {
03567 public:
03568   KExecPropsPluginPrivate()
03569   {
03570   }
03571   ~KExecPropsPluginPrivate()
03572   {
03573   }
03574 
03575   QFrame *m_frame;
03576   QCheckBox *nocloseonexitCheck;
03577 };
03578 
03579 KExecPropsPlugin::KExecPropsPlugin( KPropertiesDialog *_props )
03580   : KPropsDlgPlugin( _props )
03581 {
03582   d = new KExecPropsPluginPrivate;
03583   d->m_frame = properties->addPage(i18n("E&xecute"));
03584   QVBoxLayout * mainlayout = new QVBoxLayout( d->m_frame, 0,
03585       KDialog::spacingHint());
03586 
03587   // Now the widgets in the top layout
03588 
03589   QLabel* l;
03590   l = new QLabel( i18n( "Comman&d:" ), d->m_frame );
03591   mainlayout->addWidget(l);
03592 
03593   QHBoxLayout * hlayout;
03594   hlayout = new QHBoxLayout(KDialog::spacingHint());
03595   mainlayout->addLayout(hlayout);
03596 
03597   execEdit = new KLineEdit( d->m_frame );
03598   QWhatsThis::add(execEdit,i18n(
03599     "Following the command, you can have several place holders which will be replaced "
03600     "with the actual values when the actual program is run:\n"
03601     "%f - a single file name\n"
03602     "%F - a list of files; use for applications that can open several local files at once\n"
03603     "%u - a single URL\n"
03604     "%U - a list of URLs\n"
03605     "%d - the folder of the file to open\n"
03606     "%D - a list of folders\n"
03607     "%i - the icon\n"
03608     "%m - the mini-icon\n"
03609     "%c - the caption"));
03610   hlayout->addWidget(execEdit, 1);
03611 
03612   l->setBuddy( execEdit );
03613 
03614   execBrowse = new QPushButton( d->m_frame );
03615   execBrowse->setText( i18n("&Browse...") );
03616   hlayout->addWidget(execBrowse);
03617 
03618   // The groupbox about swallowing
03619   QGroupBox* tmpQGroupBox;
03620   tmpQGroupBox = new QGroupBox( i18n("Panel Embedding"), d->m_frame );
03621   tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
03622 
03623   mainlayout->addWidget(tmpQGroupBox);
03624 
03625   QGridLayout *grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2);
03626   grid->setSpacing( KDialog::spacingHint() );
03627   grid->setColStretch(1, 1);
03628 
03629   l = new QLabel( i18n( "&Execute on click:" ), tmpQGroupBox );
03630   grid->addWidget(l, 0, 0);
03631 
03632   swallowExecEdit = new KLineEdit( tmpQGroupBox );
03633   grid->addWidget(swallowExecEdit, 0, 1);
03634 
03635   l->setBuddy( swallowExecEdit );
03636 
03637   l = new QLabel( i18n( "&Window title:" ), tmpQGroupBox );
03638   grid->addWidget(l, 1, 0);
03639 
03640   swallowTitleEdit = new KLineEdit( tmpQGroupBox );
03641   grid->addWidget(swallowTitleEdit, 1, 1);
03642 
03643   l->setBuddy( swallowTitleEdit );
03644 
03645   // The groupbox about run in terminal
03646 
03647   tmpQGroupBox = new QGroupBox( d->m_frame );
03648   tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
03649 
03650   mainlayout->addWidget(tmpQGroupBox);
03651 
03652   grid = new QGridLayout(tmpQGroupBox->layout(), 3, 2);
03653   grid->setSpacing( KDialog::spacingHint() );
03654   grid->setColStretch(1, 1);
03655 
03656   terminalCheck = new QCheckBox( tmpQGroupBox );
03657   terminalCheck->setText( i18n("&Run in terminal") );
03658   grid->addMultiCellWidget(terminalCheck, 0, 0, 0, 1);
03659 
03660   // check to see if we use konsole if not do not add the nocloseonexit
03661   // because we don't know how to do this on other terminal applications
03662   KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
03663   QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
03664                           QString::fromLatin1("konsole"));
03665 
03666   int posOptions = 1;
03667   d->nocloseonexitCheck = 0L;
03668   if (preferredTerminal == "konsole")
03669   {
03670     posOptions = 2;
03671     d->nocloseonexitCheck = new QCheckBox( tmpQGroupBox );
03672     d->nocloseonexitCheck->setText( i18n("Do not &close when command exits") );
03673     grid->addMultiCellWidget(d->nocloseonexitCheck, 1, 1, 0, 1);
03674   }
03675 
03676   terminalLabel = new QLabel( i18n( "&Terminal options:" ), tmpQGroupBox );
03677   grid->addWidget(terminalLabel, posOptions, 0);
03678 
03679   terminalEdit = new KLineEdit( tmpQGroupBox );
03680   grid->addWidget(terminalEdit, posOptions, 1);
03681 
03682   terminalLabel->setBuddy( terminalEdit );
03683 
03684   // The groupbox about run with substituted uid.
03685 
03686   tmpQGroupBox = new QGroupBox( d->m_frame );
03687   tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
03688 
03689   mainlayout->addWidget(tmpQGroupBox);
03690 
03691   grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2);
03692   grid->setSpacing(KDialog::spacingHint());
03693   grid->setColStretch(1, 1);
03694 
03695   suidCheck = new QCheckBox(tmpQGroupBox);
03696   suidCheck->setText(i18n("Ru&n as a different user"));
03697   grid->addMultiCellWidget(suidCheck, 0, 0, 0, 1);
03698 
03699   suidLabel = new QLabel(i18n( "&Username:" ), tmpQGroupBox);
03700   grid->addWidget(suidLabel, 1, 0);
03701 
03702   suidEdit = new KLineEdit(tmpQGroupBox);
03703   grid->addWidget(suidEdit, 1, 1);
03704 
03705   suidLabel->setBuddy( suidEdit );
03706 
03707   mainlayout->addStretch(1);
03708 
03709   // now populate the page
03710   QString path = _props->kurl().path();
03711   QFile f( path );
03712   if ( !f.open( IO_ReadOnly ) )
03713     return;
03714   f.close();
03715 
03716   KSimpleConfig config( path );
03717   config.setDollarExpansion( false );
03718   config.setDesktopGroup();
03719   execStr = config.readPathEntry( "Exec" );
03720   swallowExecStr = config.readPathEntry( "SwallowExec" );
03721   swallowTitleStr = config.readEntry( "SwallowTitle" );
03722   termBool = config.readBoolEntry( "Terminal" );
03723   termOptionsStr = config.readEntry( "TerminalOptions" );
03724   suidBool = config.readBoolEntry( "X-KDE-SubstituteUID" );
03725   suidUserStr = config.readEntry( "X-KDE-Username" );
03726 
03727   if ( !swallowExecStr.isNull() )
03728     swallowExecEdit->setText( swallowExecStr );
03729   if ( !swallowTitleStr.isNull() )
03730     swallowTitleEdit->setText( swallowTitleStr );
03731 
03732   if ( !execStr.isNull() )
03733     execEdit->setText( execStr );
03734 
03735   if ( d->nocloseonexitCheck )
03736   {
03737     d->nocloseonexitCheck->setChecked( (termOptionsStr.contains( "--noclose" ) > 0) );
03738     termOptionsStr.replace( "--noclose", "");
03739   }
03740   if ( !termOptionsStr.isNull() )
03741     terminalEdit->setText( termOptionsStr );
03742 
03743   terminalCheck->setChecked( termBool );
03744   enableCheckedEdit();
03745 
03746   suidCheck->setChecked( suidBool );
03747   suidEdit->setText( suidUserStr );
03748   enableSuidEdit();
03749 
03750   // Provide username completion up to 1000 users.
03751   KCompletion *kcom = new KCompletion;
03752   kcom->setOrder(KCompletion::Sorted);
03753   struct passwd *pw;
03754   int i, maxEntries = 1000;
03755   setpwent();
03756   for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
03757     kcom->addItem(QString::fromLatin1(pw->pw_name));
03758   endpwent();
03759   if (i < maxEntries)
03760   {
03761     suidEdit->setCompletionObject(kcom, true);
03762     suidEdit->setAutoDeleteCompletionObject( true );
03763     suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
03764   }
03765   else
03766   {
03767     delete kcom;
03768   }
03769 
03770   connect( swallowExecEdit, SIGNAL( textChanged( const QString & ) ),
03771            this, SIGNAL( changed() ) );
03772   connect( swallowTitleEdit, SIGNAL( textChanged( const QString & ) ),
03773            this, SIGNAL( changed() ) );
03774   connect( execEdit, SIGNAL( textChanged( const QString & ) ),
03775            this, SIGNAL( changed() ) );
03776   connect( terminalEdit, SIGNAL( textChanged( const QString & ) ),
03777            this, SIGNAL( changed() ) );
03778   if (d->nocloseonexitCheck)
03779     connect( d->nocloseonexitCheck, SIGNAL( toggled( bool ) ),
03780            this, SIGNAL( changed() ) );
03781   connect( terminalCheck, SIGNAL( toggled( bool ) ),
03782            this, SIGNAL( changed() ) );
03783   connect( suidCheck, SIGNAL( toggled( bool ) ),
03784            this, SIGNAL( changed() ) );
03785   connect( suidEdit, SIGNAL( textChanged( const QString & ) ),
03786            this, SIGNAL( changed() ) );
03787 
03788   connect( execBrowse, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
03789   connect( terminalCheck, SIGNAL( clicked() ), this,  SLOT( enableCheckedEdit() ) );
03790   connect( suidCheck, SIGNAL( clicked() ), this,  SLOT( enableSuidEdit() ) );
03791 
03792 }
03793 
03794 KExecPropsPlugin::~KExecPropsPlugin()
03795 {
03796   delete d;
03797 }
03798 
03799 void KExecPropsPlugin::enableCheckedEdit()
03800 {
03801   bool checked = terminalCheck->isChecked();
03802   terminalLabel->setEnabled( checked );
03803   if (d->nocloseonexitCheck)
03804     d->nocloseonexitCheck->setEnabled( checked );
03805   terminalEdit->setEnabled( checked );
03806 }
03807 
03808 void KExecPropsPlugin::enableSuidEdit()
03809 {
03810   bool checked = suidCheck->isChecked();
03811   suidLabel->setEnabled( checked );
03812   suidEdit->setEnabled( checked );
03813 }
03814 
03815 bool KExecPropsPlugin::supports( KFileItemList _items )
03816 {
03817   if ( _items.count() != 1 )
03818     return false;
03819   KFileItem * item = _items.first();
03820   // check if desktop file
03821   if ( !KPropsDlgPlugin::isDesktopFile( item ) )
03822     return false;
03823   // open file and check type
03824   KDesktopFile config( item->url().path(), true /* readonly */ );
03825   return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access");
03826 }
03827 
03828 void KExecPropsPlugin::applyChanges()
03829 {
03830   kdDebug(250) << "KExecPropsPlugin::applyChanges" << endl;
03831   QString path = properties->kurl().path();
03832 
03833   QFile f( path );
03834 
03835   if ( !f.open( IO_ReadWrite ) ) {
03836     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
03837                 "sufficient access to write to <b>%1</b>.</qt>").arg(path));
03838     return;
03839   }
03840   f.close();
03841 
03842   KSimpleConfig config( path );
03843   config.setDesktopGroup();
03844   config.writeEntry( "Type", QString::fromLatin1("Application"));
03845   config.writePathEntry( "Exec", execEdit->text() );
03846   config.writePathEntry( "SwallowExec", swallowExecEdit->text() );
03847   config.writeEntry( "SwallowTitle", swallowTitleEdit->text() );
03848   config.writeEntry( "Terminal", terminalCheck->isChecked() );
03849   QString temp = terminalEdit->text();
03850   if (d->nocloseonexitCheck )
03851     if ( d->nocloseonexitCheck->isChecked() )
03852       temp += QString::fromLatin1("--noclose ");
03853   temp = temp.stripWhiteSpace();
03854   config.writeEntry( "TerminalOptions", temp );
03855   config.writeEntry( "X-KDE-SubstituteUID", suidCheck->isChecked() );
03856   config.writeEntry( "X-KDE-Username", suidEdit->text() );
03857 }
03858 
03859 
03860 void KExecPropsPlugin::slotBrowseExec()
03861 {
03862     KURL f = KFileDialog::getOpenURL( QString::null,
03863                                       QString::null, d->m_frame );
03864     if ( f.isEmpty() )
03865         return;
03866 
03867     if ( !f.isLocalFile()) {
03868         KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported."));
03869         return;
03870     }
03871 
03872     QString path = f.path();
03873     KRun::shellQuote( path );
03874     execEdit->setText( path );
03875 }
03876 
03877 class KApplicationPropsPlugin::KApplicationPropsPluginPrivate
03878 {
03879 public:
03880   KApplicationPropsPluginPrivate()
03881   {
03882       m_kdesktopMode = QCString(qApp->name()) == "kdesktop"; // nasty heh?
03883   }
03884   ~KApplicationPropsPluginPrivate()
03885   {
03886   }
03887 
03888   QFrame *m_frame;
03889   bool m_kdesktopMode;
03890 };
03891 
03892 KApplicationPropsPlugin::KApplicationPropsPlugin( KPropertiesDialog *_props )
03893   : KPropsDlgPlugin( _props )
03894 {
03895   d = new KApplicationPropsPluginPrivate;
03896   d->m_frame = properties->addPage(i18n("&Application"));
03897   QVBoxLayout *toplayout = new QVBoxLayout( d->m_frame, 0, KDialog::spacingHint());
03898 
03899   QIconSet iconSet;
03900   QPixmap pixMap;
03901 
03902   addExtensionButton = new QPushButton( QString::null, d->m_frame );
03903   iconSet = SmallIconSet( "back" );
03904   addExtensionButton->setIconSet( iconSet );
03905   pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal );
03906   addExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
03907   connect( addExtensionButton, SIGNAL( clicked() ),
03908             SLOT( slotAddExtension() ) );
03909 
03910   delExtensionButton = new QPushButton( QString::null, d->m_frame );
03911   iconSet = SmallIconSet( "forward" );
03912   delExtensionButton->setIconSet( iconSet );
03913   delExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
03914   connect( delExtensionButton, SIGNAL( clicked() ),
03915             SLOT( slotDelExtension() ) );
03916 
03917   QLabel *l;
03918 
03919   QGridLayout *grid = new QGridLayout(2, 2);
03920   grid->setColStretch(1, 1);
03921   toplayout->addLayout(grid);
03922 
03923   if ( d->m_kdesktopMode )
03924   {
03925       // in kdesktop the name field comes from the first tab
03926       nameEdit = 0L;
03927   }
03928   else
03929   {
03930       l = new QLabel(i18n("Name:"), d->m_frame, "Label_4" );
03931       grid->addWidget(l, 0, 0);
03932 
03933       nameEdit = new KLineEdit( d->m_frame, "LineEdit_3" );
03934       grid->addWidget(nameEdit, 0, 1);
03935   }
03936 
03937   l = new QLabel(i18n("Description:"),  d->m_frame, "Label_5" );
03938   grid->addWidget(l, 1, 0);
03939 
03940   genNameEdit = new KLineEdit( d->m_frame, "LineEdit_4" );
03941   grid->addWidget(genNameEdit, 1, 1);
03942 
03943   l = new QLabel(i18n("Comment:"),  d->m_frame, "Label_3" );
03944   grid->addWidget(l, 2, 0);
03945 
03946   commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" );
03947   grid->addWidget(commentEdit, 2, 1);
03948 
03949   l = new QLabel(i18n("File types:"), d->m_frame);
03950   toplayout->addWidget(l, 0, AlignLeft);
03951 
03952   grid = new QGridLayout(4, 3);
03953   grid->setColStretch(0, 1);
03954   grid->setColStretch(2, 1);
03955   grid->setRowStretch( 0, 1 );
03956   grid->setRowStretch( 3, 1 );
03957   toplayout->addLayout(grid, 2);
03958 
03959   extensionsList = new QListBox( d->m_frame );
03960   extensionsList->setSelectionMode( QListBox::Extended );
03961   grid->addMultiCellWidget(extensionsList, 0, 3, 0, 0);
03962 
03963   grid->addWidget(addExtensionButton, 1, 1);
03964   grid->addWidget(delExtensionButton, 2, 1);
03965 
03966   availableExtensionsList = new QListBox( d->m_frame );
03967   availableExtensionsList->setSelectionMode( QListBox::Extended );
03968   grid->addMultiCellWidget(availableExtensionsList, 0, 3, 2, 2);
03969 
03970   QString path = properties->kurl().path() ;
03971   QFile f( path );
03972   if ( !f.open( IO_ReadOnly ) )
03973     return;
03974   f.close();
03975 
03976   KDesktopFile config( path );
03977   QString commentStr = config.readComment();
03978   QString genNameStr = config.readGenericName();
03979 
03980   QStringList selectedTypes = config.readListEntry( "ServiceTypes" );
03981   // For compatibility with KDE 1.x
03982   selectedTypes += config.readListEntry( "MimeType", ';' );
03983 
03984   QString nameStr = config.readName();
03985   if ( nameStr.isEmpty() || d->m_kdesktopMode ) {
03986     // We'll use the file name if no name is specified
03987     // because we _need_ a Name for a valid file.
03988     // But let's do it in apply, not here, so that we pick up the right name.
03989     setDirty();
03990   }
03991 
03992   commentEdit->setText( commentStr );
03993   genNameEdit->setText( genNameStr );
03994   if ( nameEdit )
03995       nameEdit->setText( nameStr );
03996 
03997   selectedTypes.sort();
03998   QStringList::Iterator sit = selectedTypes.begin();
03999   for( ; sit != selectedTypes.end(); ++sit ) {
04000     if ( !((*sit).isEmpty()) )
04001       extensionsList->insertItem( *sit );
04002   }
04003 
04004   KMimeType::List mimeTypes = KMimeType::allMimeTypes();
04005   QValueListIterator<KMimeType::Ptr> it2 = mimeTypes.begin();
04006   for ( ; it2 != mimeTypes.end(); ++it2 )
04007     addMimeType ( (*it2)->name() );
04008 
04009   updateButton();
04010 
04011   connect( extensionsList, SIGNAL( highlighted( int ) ),
04012            this, SLOT( updateButton() ) );
04013   connect( availableExtensionsList, SIGNAL( highlighted( int ) ),
04014            this, SLOT( updateButton() ) );
04015 
04016   connect( addExtensionButton, SIGNAL( clicked() ),
04017            this, SIGNAL( changed() ) );
04018   connect( delExtensionButton, SIGNAL( clicked() ),
04019            this, SIGNAL( changed() ) );
04020   if ( nameEdit )
04021       connect( nameEdit, SIGNAL( textChanged( const QString & ) ),
04022                this, SIGNAL( changed() ) );
04023   connect( commentEdit, SIGNAL( textChanged( const QString & ) ),
04024            this, SIGNAL( changed() ) );
04025   connect( genNameEdit, SIGNAL( textChanged( const QString & ) ),
04026            this, SIGNAL( changed() ) );
04027   connect( availableExtensionsList, SIGNAL( selected( int ) ),
04028            this, SIGNAL( changed() ) );
04029   connect( extensionsList, SIGNAL( selected( int ) ),
04030            this, SIGNAL( changed() ) );
04031 }
04032 
04033 KApplicationPropsPlugin::~KApplicationPropsPlugin()
04034 {
04035   delete d;
04036 }
04037 
04038 // QString KApplicationPropsPlugin::tabName () const
04039 // {
04040 //   return i18n ("&Application");
04041 // }
04042 
04043 void KApplicationPropsPlugin::updateButton()
04044 {
04045     addExtensionButton->setEnabled(availableExtensionsList->currentItem()>-1);
04046     delExtensionButton->setEnabled(extensionsList->currentItem()>-1);
04047 }
04048 
04049 void KApplicationPropsPlugin::addMimeType( const QString & name )
04050 {
04051   // Add a mimetype to the list of available mime types if not in the extensionsList
04052 
04053   bool insert = true;
04054 
04055   for ( uint i = 0; i < extensionsList->count(); i++ )
04056     if ( extensionsList->text( i ) == name )
04057       insert = false;
04058 
04059   if ( insert )
04060   {
04061     availableExtensionsList->insertItem( name );
04062     availableExtensionsList->sort();
04063   }
04064 }
04065 
04066 bool KApplicationPropsPlugin::supports( KFileItemList _items )
04067 {
04068   // same constraints as KExecPropsPlugin : desktop file with Type = Application
04069   return KExecPropsPlugin::supports( _items );
04070 }
04071 
04072 void KApplicationPropsPlugin::applyChanges()
04073 {
04074   QString path = properties->kurl().path();
04075 
04076   QFile f( path );
04077 
04078   if ( !f.open( IO_ReadWrite ) ) {
04079     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
04080                 "have sufficient access to write to <b>%1</b>.</qt>").arg(path));
04081     return;
04082   }
04083   f.close();
04084 
04085   KSimpleConfig config( path );
04086   config.setDesktopGroup();
04087   config.writeEntry( "Type", QString::fromLatin1("Application"));
04088   config.writeEntry( "Comment", commentEdit->text() );
04089   config.writeEntry( "Comment", commentEdit->text(), true, false, true ); // for compat
04090   config.writeEntry( "GenericName", genNameEdit->text() );
04091   config.writeEntry( "GenericName", genNameEdit->text(), true, false, true ); // for compat
04092 
04093   QStringList selectedTypes;
04094   for ( uint i = 0; i < extensionsList->count(); i++ )
04095     selectedTypes.append( extensionsList->text( i ) );
04096 
04097   config.writeEntry( "MimeType", selectedTypes, ';' );
04098   config.writeEntry( "ServiceTypes", "" );
04099   // hmm, actually it should probably be the contrary (but see also typeslistitem.cpp)
04100 
04101   QString nameStr = nameEdit ? nameEdit->text() : QString::null;
04102   if ( nameStr.isEmpty() ) // nothing entered, or widget not existing at all (kdesktop mode)
04103     nameStr = nameFromFileName(properties->kurl().fileName());
04104 
04105   config.writeEntry( "Name", nameStr );
04106   config.writeEntry( "Name", nameStr, true, false, true );
04107 
04108   config.sync();
04109 }
04110 
04111 void KApplicationPropsPlugin::slotAddExtension()
04112 {
04113   QListBoxItem *item = availableExtensionsList->firstItem();
04114   QListBoxItem *nextItem;
04115 
04116   while ( item )
04117   {
04118     nextItem = item->next();
04119 
04120     if ( item->isSelected() )
04121     {
04122       extensionsList->insertItem( item->text() );
04123       availableExtensionsList->removeItem( availableExtensionsList->index( item ) );
04124     }
04125 
04126     item = nextItem;
04127   }
04128 
04129   extensionsList->sort();
04130   updateButton();
04131 }
04132 
04133 void KApplicationPropsPlugin::slotDelExtension()
04134 {
04135   QListBoxItem *item = extensionsList->firstItem();
04136   QListBoxItem *nextItem;
04137 
04138   while ( item )
04139   {
04140     nextItem = item->next();
04141 
04142     if ( item->isSelected() )
04143     {
04144       availableExtensionsList->insertItem( item->text() );
04145       extensionsList->removeItem( extensionsList->index( item ) );
04146     }
04147 
04148     item = nextItem;
04149   }
04150 
04151   availableExtensionsList->sort();
04152   updateButton();
04153 }
04154 
04155 
04156 
04157 #include "kpropertiesdialog.moc"

kio

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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