kpropertiesdialog.cpp

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