kio Library: kpropertiesdialog.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

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

This file is part of the documentation for kio Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:24:03 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003