kio Library API Documentation

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 
00008    This library is free software; you can redistribute it and/or
00009    modify it under the terms of the GNU Library General Public
00010    License as published by the Free Software Foundation; either
00011    version 2 of the License, or (at your option) any later version.
00012 
00013    This library is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016    Library General Public License for more details.
00017 
00018    You should have received a copy of the GNU Library General Public License
00019    along with this library; see the file COPYING.LIB.  If not, write to
00020    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00021    Boston, MA 02111-1307, USA.
00022 */
00023 
00024 /*
00025  * kpropertiesdialog.cpp
00026  * View/Edit Properties of files, locally or remotely
00027  *
00028  * some FilePermissionsPropsPlugin-changes by
00029  *  Henner Zeller <zeller@think.de>
00030  * some layout management by
00031  *  Bertrand Leconte <B.Leconte@mail.dotcom.fr>
00032  * the rest of the layout management, bug fixes, adaptation to libkio,
00033  * template feature by
00034  *  David Faure <faure@kde.org>
00035  * More layout, cleanups, and fixes by
00036  *  Preston Brown <pbrown@kde.org>
00037  * Plugin capability, cleanups and port to KDialogBase by
00038  *  Simon Hausmann <hausmann@kde.org>
00039  */
00040 
00041 #include <config.h>
00042 extern "C" {
00043 #include <pwd.h>
00044 #include <grp.h>
00045 #include <time.h>
00046 }
00047 #include <unistd.h>
00048 #include <errno.h>
00049 #include <assert.h>
00050 
00051 #include <qfile.h>
00052 #include <qdir.h>
00053 #include <qlabel.h>
00054 #include <qpushbutton.h>
00055 #include <qcheckbox.h>
00056 #include <qstrlist.h>
00057 #include <qstringlist.h>
00058 #include <qtextstream.h>
00059 #include <qpainter.h>
00060 #include <qlayout.h>
00061 #include <qcombobox.h>
00062 #include <qgroupbox.h>
00063 
00064 #include <kapplication.h>
00065 #include <kdialog.h>
00066 #include <kdirsize.h>
00067 #include <kdirwatch.h>
00068 #include <kdirnotify_stub.h>
00069 #include <kdiskfreesp.h>
00070 #include <kdebug.h>
00071 #include <kdesktopfile.h>
00072 #include <kicondialog.h>
00073 #include <kurl.h>
00074 #include <kurlrequester.h>
00075 #include <klocale.h>
00076 #include <kglobal.h>
00077 #include <kglobalsettings.h>
00078 #include <kstandarddirs.h>
00079 #include <kio/job.h>
00080 #include <kio/chmodjob.h>
00081 #include <kio/renamedlg.h>
00082 #include <kfiledialog.h>
00083 #include <kmimetype.h>
00084 #include <kiconloader.h>
00085 #include <kmessagebox.h>
00086 #include <kservice.h>
00087 #include <kcompletion.h>
00088 #include <klineedit.h>
00089 #include <kseparator.h>
00090 #include <ksqueezedtextlabel.h>
00091 #include <klibloader.h>
00092 #include <ktrader.h>
00093 #include <kparts/componentfactory.h>
00094 #include <kmetaprops.h>
00095 #include <krun.h>
00096 #include "kfilesharedlg.h"
00097 
00098 #include "kpropertiesdialog.h"
00099 
00100 #ifdef Q_WS_X11
00101 extern "C" {
00102 #include <X11/Xlib.h> // for XSetTransientForHint
00103 }
00104 #endif
00105 
00106 mode_t KFilePermissionsPropsPlugin::fperm[3][4] = {
00107         {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
00108         {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
00109         {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
00110     };
00111 
00112 class KPropertiesDialog::KPropertiesDialogPrivate
00113 {
00114 public:
00115   KPropertiesDialogPrivate()
00116   {
00117     m_aborted = false;
00118   }
00119   ~KPropertiesDialogPrivate()
00120   {
00121   }
00122   bool m_aborted:1;
00123   bool modal:1;
00124   bool autoShow:1;
00125 };
00126 
00127 KPropertiesDialog::KPropertiesDialog (KFileItem* item,
00128                                       QWidget* parent, const char* name,
00129                                       bool modal, bool autoShow)
00130   : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(KIO::decodeFileName(item->url().fileName())),
00131                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00132                  parent, name, modal)
00133 {
00134   d = new KPropertiesDialogPrivate;
00135   assert( item );
00136   m_items.append( new KFileItem(*item) ); // deep copy
00137 
00138   m_singleUrl = item->url();
00139   assert(!m_singleUrl.isEmpty());
00140 
00141   init (modal, autoShow);
00142 }
00143 
00144 KPropertiesDialog::KPropertiesDialog (const QString& title,
00145                                       QWidget* parent, const char* name, bool modal)
00146   : KDialogBase (KDialogBase::Tabbed, i18n ("Properties for %1").arg(title),
00147                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00148                  parent, name, modal)
00149 {
00150   d = new KPropertiesDialogPrivate;
00151 
00152   init (modal, false);
00153 }
00154 
00155 KPropertiesDialog::KPropertiesDialog (KFileItemList _items,
00156                                       QWidget* parent, const char* name,
00157                                       bool modal, bool autoShow)
00158   : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(KIO::decodeFileName(_items.first()->url().fileName())),
00159                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00160                  parent, name, modal)
00161 {
00162   d = new KPropertiesDialogPrivate;
00163 
00164   assert( !_items.isEmpty() );
00165   m_singleUrl = _items.first()->url();
00166   assert(!m_singleUrl.isEmpty());
00167 
00168   KFileItemListIterator it ( _items );
00169   // Deep copy
00170   for ( ; it.current(); ++it )
00171       m_items.append( new KFileItem( **it ) );
00172 
00173   init (modal, autoShow);
00174 }
00175 
00176 #ifndef KDE_NO_COMPAT
00177 KPropertiesDialog::KPropertiesDialog (const KURL& _url, mode_t /* _mode is now unused */,
00178                                       QWidget* parent, const char* name,
00179                                       bool modal, bool autoShow)
00180   : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())),
00181                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00182                  parent, name, modal),
00183   m_singleUrl( _url )
00184 {
00185   d = new KPropertiesDialogPrivate;
00186   d->modal = modal;
00187   d->autoShow = autoShow;
00188 
00189   assert(!_url.isEmpty());
00190 
00191   KIO::StatJob * job = KIO::stat( _url );
00192   connect( job, SIGNAL( result( KIO::Job * ) ),
00193            SLOT( slotStatResult( KIO::Job * ) ) );
00194 }
00195 #endif
00196 
00197 KPropertiesDialog::KPropertiesDialog (const KURL& _url,
00198                                       QWidget* parent, const char* name,
00199                                       bool modal, bool autoShow)
00200   : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())),
00201                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00202                  parent, name, modal),
00203   m_singleUrl( _url )
00204 {
00205   d = new KPropertiesDialogPrivate;
00206   d->modal = modal;
00207   d->autoShow = autoShow;
00208 
00209   assert(!_url.isEmpty());
00210 
00211   KIO::StatJob * job = KIO::stat( _url );
00212   connect( job, SIGNAL( result( KIO::Job * ) ),
00213            SLOT( slotStatResult( KIO::Job * ) ) );
00214 }
00215 
00216 KPropertiesDialog::KPropertiesDialog (const KURL& _tempUrl, const KURL& _currentDir,
00217                                       const QString& _defaultName,
00218                                       QWidget* parent, const char* name,
00219                                       bool modal, bool autoShow)
00220   : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(KIO::decodeFileName(_tempUrl.fileName())),
00221                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00222                  parent, name, modal),
00223 
00224   m_singleUrl( _tempUrl ),
00225   m_defaultName( _defaultName ),
00226   m_currentDir( _currentDir )
00227 {
00228   d = new KPropertiesDialogPrivate;
00229 
00230   assert(!m_singleUrl.isEmpty());
00231 
00232   // Create the KFileItem for the _template_ file, in order to read from it.
00233   m_items.append( new KFileItem( KFileItem::Unknown, KFileItem::Unknown, m_singleUrl ) );
00234   init (modal, autoShow);
00235 }
00236 
00237 void KPropertiesDialog::init (bool modal, bool autoShow)
00238 {
00239   m_pageList.setAutoDelete( true );
00240   m_items.setAutoDelete( true );
00241 
00242 #ifdef Q_WS_X11 // FIXME(E): Can we do something similar for Qt Embedded?
00243   // Matthias: let the dialog look like a modal dialog
00244   if (!modal)
00245     XSetTransientForHint(qt_xdisplay(), winId(), winId());
00246 #endif
00247 
00248   //  resize( 400, 400 ); // not sure what that's for
00249 
00250   insertPages();
00251 
00252   //kdDebug(250) << "KPropertiesDialog sizeHint " << sizeHint().width() << "x" << sizeHint().height() << endl;
00253   // This HACK forces KDialogBase to recompute the layout
00254   // It is necessary for the case where init is not called from the constructor,
00255   // but from slotStatResult. And I'm way too lazy to look into KDialogBase...
00256   enableLinkedHelp( true );
00257   enableLinkedHelp( false );
00258   resize(sizeHint());
00259 
00260   if (autoShow)
00261     {
00262       if (!modal)
00263         show();
00264       else
00265         exec();
00266     }
00267 }
00268 
00269 void KPropertiesDialog::showFileSharingPage()
00270 {
00271     KPropsDlgPlugin *it;
00272 
00273     for ( it=m_pageList.first(); it != 0L; it=m_pageList.next() )
00274     {
00275         KFileSharePropsPlugin* plugin = dynamic_cast<KFileSharePropsPlugin*>(it);
00276         if ( plugin )
00277         {
00278             showPage( pageIndex( plugin->page() ) );
00279             break;
00280         }
00281     }
00282 }
00283 
00284 void KPropertiesDialog::slotStatResult( KIO::Job * job )
00285 {
00286     if (job->error())
00287     {
00288         job->showErrorDialog( this );
00289         delete this;
00290     }
00291     else
00292     {
00293         KIO::StatJob * statJob = static_cast<KIO::StatJob*>(job);
00294         m_items.append( new KFileItem( statJob->statResult(), statJob->url() ) );
00295         init (d->modal, d->autoShow);
00296     }
00297 }
00298 
00299 KPropertiesDialog::~KPropertiesDialog()
00300 {
00301   m_pageList.clear();
00302   delete d;
00303 }
00304 
00305 void KPropertiesDialog::insertPlugin (KPropsDlgPlugin* plugin)
00306 {
00307   connect (plugin, SIGNAL (changed ()),
00308            plugin, SLOT (setDirty ()));
00309 
00310   m_pageList.append (plugin);
00311 }
00312 
00313 bool KPropertiesDialog::canDisplay( KFileItemList _items )
00314 {
00315   return KFilePropsPlugin::supports( _items ) ||
00316          KFilePermissionsPropsPlugin::supports( _items ) ||
00317          KExecPropsPlugin::supports( _items ) ||
00318          KApplicationPropsPlugin::supports( _items ) ||
00319          KBindingPropsPlugin::supports( _items ) ||
00320          KURLPropsPlugin::supports( _items ) ||
00321          KDevicePropsPlugin::supports( _items ) ||
00322          KFileMetaPropsPlugin::supports( _items );
00323 }
00324 
00325 void KPropertiesDialog::slotOk()
00326 {
00327   KPropsDlgPlugin *page;
00328   d->m_aborted = false;
00329 
00330   KFilePropsPlugin * filePropsPlugin = 0L;
00331   if ( m_pageList.first()->isA("KFilePropsPlugin") )
00332     filePropsPlugin = static_cast<KFilePropsPlugin *>(m_pageList.first());
00333 
00334   // If any page is dirty, then set the main one (KFilePropsPlugin) as
00335   // dirty too. This is what makes it possible to save changes to a global
00336   // desktop file into a local one. In other cases, it doesn't hurt.
00337   for ( page = m_pageList.first(); page != 0L; page = m_pageList.next() )
00338     if ( page->isDirty() && filePropsPlugin )
00339     {
00340         filePropsPlugin->setDirty();
00341         break;
00342     }
00343 
00344   // Apply the changes in the _normal_ order of the tabs now
00345   // This is because in case of renaming a file, KFilePropsPlugin will call
00346   // KPropertiesDialog::rename, so other tab will be ok with whatever order
00347   // BUT for file copied from templates, we need to do the renaming first !
00348   for ( page = m_pageList.first(); page != 0L && !d->m_aborted; page = m_pageList.next() )
00349     if ( page->isDirty() )
00350     {
00351       kdDebug( 250 ) << "applying changes for " << page->className() << endl;
00352       page->applyChanges();
00353       // applyChanges may change d->m_aborted.
00354     }
00355     else
00356       kdDebug( 250 ) << "skipping page " << page->className() << endl;
00357 
00358   if ( !d->m_aborted && filePropsPlugin )
00359     filePropsPlugin->postApplyChanges();
00360 
00361   if ( !d->m_aborted )
00362   {
00363     emit applied();
00364     emit propertiesClosed();
00365     deleteLater();
00366     accept();
00367   } // else, keep dialog open for user to fix the problem.
00368 }
00369 
00370 void KPropertiesDialog::slotCancel()
00371 {
00372   emit canceled();
00373   emit propertiesClosed();
00374 
00375   deleteLater();
00376   done( Rejected );
00377 }
00378 
00379 void KPropertiesDialog::insertPages()
00380 {
00381   if (m_items.isEmpty())
00382     return;
00383 
00384   if ( KFilePropsPlugin::supports( m_items ) )
00385   {
00386     KPropsDlgPlugin *p = new KFilePropsPlugin( this );
00387     insertPlugin (p);
00388   }
00389 
00390   if ( KFilePermissionsPropsPlugin::supports( m_items ) )
00391   {
00392     KPropsDlgPlugin *p = new KFilePermissionsPropsPlugin( this );
00393     insertPlugin (p);
00394   }
00395 
00396   if ( KExecPropsPlugin::supports( m_items ) )
00397   {
00398     KPropsDlgPlugin *p = new KExecPropsPlugin( this );
00399     insertPlugin (p);
00400   }
00401 
00402   if ( KApplicationPropsPlugin::supports( m_items ) )
00403   {
00404     KPropsDlgPlugin *p = new KApplicationPropsPlugin( this );
00405     insertPlugin (p);
00406   }
00407 
00408   if ( KBindingPropsPlugin::supports( m_items ) )
00409   {
00410     KPropsDlgPlugin *p = new KBindingPropsPlugin( this );
00411     insertPlugin (p);
00412   }
00413 
00414   if ( KURLPropsPlugin::supports( m_items ) )
00415   {
00416     KPropsDlgPlugin *p = new KURLPropsPlugin( this );
00417     insertPlugin (p);
00418   }
00419 
00420   if ( KDevicePropsPlugin::supports( m_items ) )
00421   {
00422     KPropsDlgPlugin *p = new KDevicePropsPlugin( this );
00423     insertPlugin (p);
00424   }
00425 
00426   if ( KFileMetaPropsPlugin::supports( m_items ) )
00427   {
00428     KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this );
00429     insertPlugin (p);
00430   }
00431 
00432   if ( KFileSharePropsPlugin::supports( m_items ) )
00433   {
00434     KPropsDlgPlugin *p = new KFileSharePropsPlugin( this );
00435     insertPlugin (p);
00436   }
00437 
00438   //plugins
00439 
00440   if ( m_items.count() != 1 )
00441     return;
00442 
00443   KFileItem *item = m_items.first();
00444   QString mimetype = item->mimetype();
00445 
00446   if ( mimetype.isEmpty() )
00447     return;
00448 
00449   QString query = QString::fromLatin1(
00450       "('KPropsDlg/Plugin' in ServiceTypes) and "
00451       "((not exist [X-KDE-Protocol]) or "
00452       " ([X-KDE-Protocol] == '%1'  )   )"          ).arg(item->url().protocol());
00453 
00454   kdDebug( 250 ) << "trader query: " << query << endl;
00455   KTrader::OfferList offers = KTrader::self()->query( mimetype, query );
00456   KTrader::OfferList::ConstIterator it = offers.begin();
00457   KTrader::OfferList::ConstIterator end = offers.end();
00458   for (; it != end; ++it )
00459   {
00460     KPropsDlgPlugin *plugin = KParts::ComponentFactory
00461         ::createInstanceFromLibrary<KPropsDlgPlugin>( (*it)->library().local8Bit().data(),
00462                                                       this,
00463                                                       (*it)->name().latin1() );
00464     if ( !plugin )
00465         continue;
00466 
00467     insertPlugin( plugin );
00468   }
00469 }
00470 
00471 void KPropertiesDialog::updateUrl( const KURL& _newUrl )
00472 {
00473   Q_ASSERT( m_items.count() == 1 );
00474   kdDebug(250) << "KPropertiesDialog::updateUrl " << _newUrl.url() << endl;
00475   m_singleUrl = _newUrl;
00476   m_items.first()->setURL( _newUrl );
00477   assert(!m_singleUrl.isEmpty());
00478   // If we have an Exec page, set it dirty, so that a full file is saved locally
00479   // Same for a URL page (because of the Name= hack)
00480   for ( QPtrListIterator<KPropsDlgPlugin> it(m_pageList); it.current(); ++it )
00481    if ( it.current()->isA("KExecPropsPlugin") || it.current()->isA("KURLPropsPlugin") )
00482    {
00483      //kdDebug(250) << "Setting page dirty" << endl;
00484      it.current()->setDirty();
00485      break;
00486    }
00487 }
00488 
00489 void KPropertiesDialog::rename( const QString& _name )
00490 {
00491   Q_ASSERT( m_items.count() == 1 );
00492   kdDebug(250) << "KPropertiesDialog::rename " << _name << endl;
00493   KURL newUrl;
00494   // if we're creating from a template : use currentdir
00495   if ( !m_currentDir.isEmpty() )
00496   {
00497     newUrl = m_currentDir;
00498     newUrl.addPath( _name );
00499   }
00500   else
00501   {
00502     QString tmpurl = m_singleUrl.url();
00503     if ( tmpurl.at(tmpurl.length() - 1) == '/')
00504       // It's a directory, so strip the trailing slash first
00505       tmpurl.truncate( tmpurl.length() - 1);
00506     newUrl = tmpurl;
00507     newUrl.setFileName( _name );
00508   }
00509   updateUrl( newUrl );
00510 }
00511 
00512 void KPropertiesDialog::abortApplying()
00513 {
00514   d->m_aborted = true;
00515 }
00516 
00517 class KPropsDlgPlugin::KPropsDlgPluginPrivate
00518 {
00519 public:
00520   KPropsDlgPluginPrivate()
00521   {
00522   }
00523   ~KPropsDlgPluginPrivate()
00524   {
00525   }
00526 
00527   bool m_bDirty;
00528 };
00529 
00530 KPropsDlgPlugin::KPropsDlgPlugin( KPropertiesDialog *_props )
00531 : QObject( _props, 0L )
00532 {
00533   d = new KPropsDlgPluginPrivate;
00534   properties = _props;
00535   fontHeight = 2*properties->dialog()->fontMetrics().height();
00536   d->m_bDirty = false;
00537 }
00538 
00539 KPropsDlgPlugin::~KPropsDlgPlugin()
00540 {
00541   delete d;
00542 }
00543 
00544 bool KPropsDlgPlugin::isDesktopFile( KFileItem * _item )
00545 {
00546   // only local files
00547   if ( !_item->isLocalFile() )
00548     return false;
00549 
00550   // only regular files
00551   if ( !S_ISREG( _item->mode() ) )
00552     return false;
00553 
00554   QString t( _item->url().path() );
00555 
00556   // only if readable
00557   FILE *f = fopen( QFile::encodeName(t), "r" );
00558   if ( f == 0L )
00559     return false;
00560   fclose(f);
00561 
00562   // return true if desktop file
00563   return ( _item->mimetype() == QString::fromLatin1("application/x-desktop") );
00564 }
00565 
00566 void KPropsDlgPlugin::setDirty( bool b )
00567 {
00568   d->m_bDirty = b;
00569 }
00570 
00571 void KPropsDlgPlugin::setDirty()
00572 {
00573   d->m_bDirty = true;
00574 }
00575 
00576 bool KPropsDlgPlugin::isDirty() const
00577 {
00578   return d->m_bDirty;
00579 }
00580 
00581 void KPropsDlgPlugin::applyChanges()
00582 {
00583   kdWarning(250) << "applyChanges() not implemented in page !" << endl;
00584 }
00585 
00587 
00588 class KFilePropsPlugin::KFilePropsPluginPrivate
00589 {
00590 public:
00591   KFilePropsPluginPrivate()
00592   {
00593     dirSizeJob = 0L;
00594   }
00595   ~KFilePropsPluginPrivate()
00596   {
00597     if ( dirSizeJob )
00598       dirSizeJob->kill();
00599   }
00600 
00601   KDirSize * dirSizeJob;
00602   QFrame *m_frame;
00603   bool bMultiple;
00604   QLabel *m_freeSpaceLabel;
00605 };
00606 
00607 KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props )
00608   : KPropsDlgPlugin( _props )
00609 {
00610   d = new KFilePropsPluginPrivate;
00611   d->bMultiple = (properties->items().count() > 1);
00612   kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple << endl;
00613 
00614   // We set this data from the first item, and we'll
00615   // check that the other items match against it, resetting when not.
00616   bool isLocal = properties->kurl().isLocalFile();
00617   KFileItem * item = properties->item();
00618   bool bDesktopFile = isDesktopFile(item);
00619   mode_t mode = item->mode();
00620   bool hasDirs = item->isDir() && !item->isLink();
00621   bool hasRoot = isLocal && properties->kurl().path() == QString::fromLatin1("/");
00622   QString iconStr = KMimeType::iconForURL(properties->kurl(), mode);
00623   QString directory = properties->kurl().directory();
00624   QString protocol = properties->kurl().protocol();
00625   QString mimeComment = item->mimeComment();
00626   KIO::filesize_t totalSize = item->size();
00627   QString magicMimeComment;
00628   if ( isLocal ) {
00629       KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( properties->kurl().path() );
00630       if ( magicMimeType->name() != KMimeType::defaultMimeType() )
00631           magicMimeComment = magicMimeType->comment();
00632   }
00633 
00634   // Those things only apply to 'single file' mode
00635   QString filename = QString::null;
00636   bool isTrash = false;
00637   m_bFromTemplate = false;
00638 
00639   // And those only to 'multiple' mode
00640   uint iDirCount = S_ISDIR(mode) ? 1 : 0;
00641   uint iFileCount = 1-iDirCount;
00642 
00643   d->m_frame = properties->dialog()->addPage (i18n("&General"));
00644 
00645   QVBoxLayout *vbl = new QVBoxLayout( d->m_frame, KDialog::marginHint(),
00646                                       KDialog::spacingHint(), "vbl");
00647   QGridLayout *grid = new QGridLayout(0, 3); // unknown rows
00648   grid->setColStretch(2, 1);
00649   grid->addColSpacing(1, KDialog::spacingHint());
00650   vbl->addLayout(grid);
00651   int curRow = 0;
00652 
00653   if ( !d->bMultiple )
00654   {
00655     // Extract the file name only
00656     filename = properties->defaultName();
00657     if ( filename.isEmpty() ) // no template
00658       filename = properties->kurl().fileName();
00659     else
00660     {
00661       m_bFromTemplate = true;
00662       setDirty(); // to enforce that the copy happens
00663     }
00664     oldName = filename;
00665 
00666     // Make it human-readable (%2F => '/', ...)
00667     filename = KIO::decodeFileName( filename );
00668 
00669     QString path;
00670 
00671     if ( !m_bFromTemplate ) {
00672       QString tmp = properties->kurl().path( 1 );
00673       // is it the trash bin ?
00674       if ( isLocal && tmp == KGlobalSettings::trashPath())
00675         isTrash = true;
00676 
00677       // Extract the full name, but without file: for local files
00678       if ( isLocal )
00679         path = properties->kurl().path();
00680       else
00681         path = properties->kurl().prettyURL();
00682     } else {
00683       path = properties->currentDir().path(1) + properties->defaultName();
00684       directory = properties->currentDir().prettyURL();
00685     }
00686 
00687     if (KExecPropsPlugin::supports(properties->items()) ||
00688         KBindingPropsPlugin::supports(properties->items())) {
00689 
00690       determineRelativePath( path );
00691 
00692     }
00693 
00694   }
00695   else
00696   {
00697     // Multiple items: see what they have in common
00698     KFileItemList items = properties->items();
00699     KFileItemListIterator it( items );
00700     for ( ++it /*no need to check the first one again*/ ; it.current(); ++it )
00701     {
00702       KURL url = (*it)->url();
00703       kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyURL() << endl;
00704       // The list of things we check here should match the variables defined
00705       // at the beginning of this method.
00706       if ( url.isLocalFile() != isLocal )
00707         isLocal = false; // not all local
00708       if ( bDesktopFile && isDesktopFile(*it) != bDesktopFile )
00709         bDesktopFile = false; // not all desktop files
00710       if ( (*it)->mode() != mode )
00711         mode = (mode_t)0;
00712       if ( KMimeType::iconForURL(url, mode) != iconStr )
00713         iconStr = "kmultiple";
00714       if ( url.directory() != directory )
00715         directory = QString::null;
00716       if ( url.protocol() != protocol )
00717         protocol = QString::null;
00718       if ( !mimeComment.isNull() && (*it)->mimeComment() != mimeComment )
00719         mimeComment = QString::null;
00720       if ( isLocal && !magicMimeComment.isNull() ) {
00721           KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
00722           if ( magicMimeType->comment() != magicMimeComment )
00723               magicMimeComment = QString::null;
00724       }
00725 
00726       if ( isLocal && url.path() == QString::fromLatin1("/") )
00727         hasRoot = true;
00728       if ( (*it)->isDir() && !(*it)->isLink() )
00729       {
00730         iDirCount++;
00731         hasDirs = true;
00732       }
00733       else
00734       {
00735         iFileCount++;
00736         totalSize += (*it)->size();
00737       }
00738     }
00739   }
00740 
00741   if (!isLocal && !protocol.isEmpty())
00742   {
00743     directory += ' ';
00744     directory += '(';
00745     directory += protocol;
00746     directory += ')';
00747   }
00748 
00749   if ( (bDesktopFile || S_ISDIR(mode)) && !d->bMultiple /*not implemented for multiple*/ )
00750   {
00751     KIconButton *iconButton = new KIconButton( d->m_frame );
00752     iconButton->setFixedSize(70, 70);
00753     iconButton->setStrictIconSize(false);
00754     iconButton->setIconType(KIcon::Desktop, KIcon::Device);
00755     // This works for everything except Device icons on unmounted devices
00756     // So we have to really open .desktop files
00757     QString iconStr = KMimeType::findByURL( properties->kurl(),
00758                                             mode )->icon( properties->kurl(),
00759                                                           isLocal );
00760     if ( bDesktopFile && isLocal )
00761     {
00762       KSimpleConfig config( properties->kurl().path() );
00763       config.setDesktopGroup();
00764       iconStr = config.readEntry( QString::fromLatin1("Icon") );
00765     }
00766     iconButton->setIcon(iconStr);
00767     iconArea = iconButton;
00768     connect( iconButton, SIGNAL( iconChanged(QString) ),
00769              this, SIGNAL( changed() ) );
00770   } else {
00771     QLabel *iconLabel = new QLabel( d->m_frame );
00772     iconLabel->setFixedSize(70, 70);
00773     iconLabel->setPixmap( DesktopIcon( iconStr ) );
00774     iconArea = iconLabel;
00775   }
00776   grid->addWidget(iconArea, curRow, 0, AlignLeft);
00777 
00778   if (d->bMultiple || isTrash || filename == QString::fromLatin1("/"))
00779   {
00780     QLabel *lab = new QLabel(d->m_frame );
00781     if ( d->bMultiple )
00782       lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) );
00783     else
00784       lab->setText( filename );
00785     nameArea = lab;
00786   } else
00787   {
00788     KLineEdit *lined = new KLineEdit( d->m_frame );
00789     lined->setText(filename);
00790     nameArea = lined;
00791     lined->setFocus();
00792     connect( lined, SIGNAL( textChanged( const QString & ) ),
00793              this, SLOT( nameFileChanged(const QString & ) ) );
00794   }
00795 
00796   grid->addWidget(nameArea, curRow++, 2);
00797 
00798   KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame);
00799   grid->addMultiCellWidget(sep, curRow, curRow, 0, 2);
00800   ++curRow;
00801 
00802   QLabel *l;
00803   if ( !mimeComment.isEmpty() )
00804   {
00805     l = new QLabel(i18n("Type:"), d->m_frame );
00806     grid->addWidget(l, curRow, 0);
00807 
00808     l = new QLabel(mimeComment, d->m_frame );
00809     grid->addWidget(l, curRow++, 2);
00810   }
00811 
00812   if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
00813   {
00814     l = new QLabel(i18n("Contents:"), d->m_frame );
00815     grid->addWidget(l, curRow, 0);
00816 
00817     l = new QLabel(magicMimeComment, d->m_frame );
00818     grid->addWidget(l, curRow++, 2);
00819   }
00820 
00821   if ( !directory.isEmpty() )
00822   {
00823     l = new QLabel( i18n("Location:"), d->m_frame );
00824     grid->addWidget(l, curRow, 0);
00825 
00826     l = new KSqueezedTextLabel( d->m_frame );
00827     l->setText( directory );
00828     grid->addWidget(l, curRow++, 2);
00829   }
00830 
00831   l = new QLabel(i18n("Size:"), d->m_frame );
00832   grid->addWidget(l, curRow, 0);
00833 
00834   m_sizeLabel = new QLabel( d->m_frame );
00835   grid->addWidget( m_sizeLabel, curRow++, 2 );
00836 
00837   if ( !hasDirs ) // Only files [and symlinks]
00838   {
00839     m_sizeLabel->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize)).arg(KGlobal::locale()->formatNumber(totalSize, 0)));
00840     m_sizeDetermineButton = 0L;
00841     m_sizeStopButton = 0L;
00842   }
00843   else // Directory
00844   {
00845     QHBoxLayout * sizelay = new QHBoxLayout(KDialog::spacingHint());
00846     grid->addLayout( sizelay, curRow++, 2 );
00847 
00848     // buttons
00849     m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
00850     m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
00851     connect( m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) );
00852     connect( m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) );
00853     sizelay->addWidget(m_sizeDetermineButton, 0);
00854     sizelay->addWidget(m_sizeStopButton, 0);
00855     sizelay->addStretch(10); // so that the buttons don't grow horizontally
00856 
00857     // auto-launch for local dirs only, and not for '/'
00858     if ( isLocal && !hasRoot )
00859     {
00860       m_sizeDetermineButton->setText( i18n("Refresh") );
00861       slotSizeDetermine();
00862     }
00863     else
00864       m_sizeStopButton->setEnabled( false );
00865   }
00866 
00867   if ( isLocal )
00868   {
00869       QString mountPoint = KIO::findPathMountPoint( properties->item()->url().path() );
00870 
00871       l = new QLabel(i18n("Free space on %1:").arg(mountPoint), d->m_frame );
00872       grid->addWidget(l, curRow, 0);
00873 
00874       d->m_freeSpaceLabel = new QLabel( d->m_frame );
00875       grid->addWidget( d->m_freeSpaceLabel, curRow++, 2 );
00876 
00877       KDiskFreeSp * job = new KDiskFreeSp;
00878       connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&, const unsigned long&, const QString& ) ),
00879                this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&, const unsigned long&, const QString& ) ) );
00880       job->readDF( mountPoint );
00881   }
00882 
00883   if (!d->bMultiple && item->isLink()) {
00884     l = new QLabel(i18n("Points to:"), d->m_frame );
00885     grid->addWidget(l, curRow, 0);
00886 
00887     l = new QLabel(item->linkDest(), d->m_frame );
00888     grid->addWidget(l, curRow++, 2);
00889   }
00890 
00891   if (!d->bMultiple) // Dates for multiple don't make much sense...
00892   {
00893     sep = new KSeparator( KSeparator::HLine, d->m_frame);
00894     grid->addMultiCellWidget(sep, curRow, curRow, 0, 2);
00895     ++curRow;
00896 
00897     grid = new QGridLayout(0, 3); // unknown # of rows
00898     grid->setColStretch(2, 1);
00899     grid->addColSpacing(1, KDialog::spacingHint());
00900     vbl->addLayout(grid);
00901     curRow = 0;
00902 
00903     QDateTime dt;
00904     time_t tim = item->time(KIO::UDS_CREATION_TIME);
00905     if ( tim )
00906     {
00907       l = new QLabel(i18n("Created:"), d->m_frame );
00908       grid->addWidget(l, curRow, 0);
00909 
00910       dt.setTime_t( tim );
00911       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
00912       grid->addWidget(l, curRow++, 2);
00913     }
00914 
00915     tim = item->time(KIO::UDS_MODIFICATION_TIME);
00916     if ( tim )
00917     {
00918       l = new QLabel(i18n("Modified:"), d->m_frame );
00919       grid->addWidget(l, curRow, 0);
00920 
00921       dt.setTime_t( tim );
00922       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
00923       grid->addWidget(l, curRow++, 2);
00924     }
00925 
00926     tim = item->time(KIO::UDS_ACCESS_TIME);
00927     if ( tim )
00928     {
00929       l = new QLabel(i18n("Accessed:"), d->m_frame );
00930       grid->addWidget(l, curRow, 0);
00931 
00932       dt.setTime_t( tim );
00933       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
00934       grid->addWidget(l, curRow++, 2);
00935     }
00936   }
00937 
00938   vbl->addStretch(1);
00939 }
00940 
00941 // QString KFilePropsPlugin::tabName () const
00942 // {
00943 //   return i18n ("&General");
00944 // }
00945 
00946 
00947 void KFilePropsPlugin::nameFileChanged(const QString &text )
00948 {
00949   properties->enableButtonOK(!text.isEmpty());
00950   emit changed();
00951 }
00952 void KFilePropsPlugin::determineRelativePath( const QString & path )
00953 {
00954     m_sRelativePath = "";
00955     // now let's make it relative
00956     QStringList dirs;
00957     if (KBindingPropsPlugin::supports(properties->items()))
00958       dirs = KGlobal::dirs()->resourceDirs("mime");
00959     else
00960       dirs = KGlobal::dirs()->resourceDirs("apps");
00961 
00962     QStringList::ConstIterator it = dirs.begin();
00963     for ( ; it != dirs.end() && m_sRelativePath.isEmpty(); ++it ) {
00964       // might need canonicalPath() ...
00965       if ( path.find( *it ) == 0 ) // path is dirs + relativePath
00966         m_sRelativePath = path.mid( (*it).length() ); // skip appsdirs
00967     }
00968     if ( m_sRelativePath.isEmpty() )
00969     {
00970       if (KBindingPropsPlugin::supports(properties->items()))
00971         kdWarning(250) << "Warning : editing a mimetype file out of the mimetype dirs!" << endl;
00972       // for Application desktop files, no problem : we can editing a .desktop file anywhere...
00973     } else
00974         while ( m_sRelativePath.at(0) == '/' ) m_sRelativePath.remove( 0, 1 );
00975 }
00976 
00977 void KFilePropsPlugin::slotFoundMountPoint( const QString&, unsigned long kBSize, unsigned long /*kBUsed*/, unsigned long kBAvail )
00978 {
00979     d->m_freeSpaceLabel->setText( i18n("Available space out of total partition size (percent used)", "%1/%2 (%3% used)")
00980                                .arg(KIO::convertSizeFromKB(kBAvail))
00981                                .arg(KIO::convertSizeFromKB(kBSize))
00982                                .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
00983 }
00984 
00985 // attention: copy&paste below, due to compiler bug
00986 // it doesn't like those unsigned long parameters -- unsigned long& are ok :-/
00987 void KFilePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize, const unsigned long& /*kBUsed*/, const unsigned long& kBAvail, const QString& )
00988 {
00989     d->m_freeSpaceLabel->setText( i18n("Available space out of total partition size (percent used)", "%1/%2 (%3% used)")
00990                                .arg(KIO::convertSizeFromKB(kBAvail))
00991                                .arg(KIO::convertSizeFromKB(kBSize))
00992                                .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
00993 }
00994 
00995 void KFilePropsPlugin::slotDirSizeFinished( KIO::Job * job )
00996 {
00997   if (job->error())
00998     m_sizeLabel->setText( job->errorString() );
00999   else
01000   {
01001     KIO::filesize_t totalSize = static_cast<KDirSize*>(job)->totalSize();
01002     m_sizeLabel->setText( QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize)).arg(KGlobal::locale()->formatNumber(totalSize, 0)) );
01003   }
01004   m_sizeStopButton->setEnabled(false);
01005   // just in case you change something and try again :)
01006   m_sizeDetermineButton->setText( i18n("Refresh") );
01007   m_sizeDetermineButton->setEnabled(true);
01008   d->dirSizeJob = 0L;
01009 }
01010 
01011 void KFilePropsPlugin::slotSizeDetermine()
01012 {
01013   m_sizeLabel->setText( i18n("Calculating...") );
01014   kdDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" <<  properties->item() << endl;
01015   kdDebug(250) << " URL=" << properties->item()->url().url() << endl;
01016   d->dirSizeJob = KDirSize::dirSizeJob( properties->items() );
01017   connect( d->dirSizeJob, SIGNAL( result( KIO::Job * ) ),
01018            SLOT( slotDirSizeFinished( KIO::Job * ) ) );
01019   m_sizeStopButton->setEnabled(true);
01020   m_sizeDetermineButton->setEnabled(false);
01021 }
01022 
01023 void KFilePropsPlugin::slotSizeStop()
01024 {
01025   if ( d->dirSizeJob )
01026   {
01027     m_sizeLabel->setText( i18n("Stopped") );
01028     d->dirSizeJob->kill();
01029     d->dirSizeJob = 0;
01030   }
01031   m_sizeStopButton->setEnabled(false);
01032   m_sizeDetermineButton->setEnabled(true);
01033 }
01034 
01035 KFilePropsPlugin::~KFilePropsPlugin()
01036 {
01037   delete d;
01038 }
01039 
01040 bool KFilePropsPlugin::supports( KFileItemList /*_items*/ )
01041 {
01042   return true;
01043 }
01044 
01045 // Don't do this at home
01046 void qt_enter_modal( QWidget *widget );
01047 void qt_leave_modal( QWidget *widget );
01048 
01049 void KFilePropsPlugin::applyChanges()
01050 {
01051   if ( d->dirSizeJob )
01052     slotSizeStop();
01053 
01054   kdDebug(250) << "KFilePropsPlugin::applyChanges" << endl;
01055 
01056   if (nameArea->inherits("QLineEdit"))
01057   {
01058     QString n = KIO::encodeFileName(((QLineEdit *) nameArea)->text());
01059     // Remove trailing spaces (#4345)
01060     while ( n[n.length()-1].isSpace() )
01061       n.truncate( n.length() - 1 );
01062     if ( n.isEmpty() )
01063     {
01064       KMessageBox::sorry( properties, i18n("The new file name is empty!"));
01065       properties->abortApplying();
01066       return;
01067     }
01068 
01069     // Do we need to rename the file ?
01070     kdDebug(250) << "oldname = " << oldName << endl;
01071     kdDebug(250) << "newname = " << n << endl;
01072     if ( oldName != n || m_bFromTemplate ) { // true for any from-template file
01073       KIO::Job * job = 0L;
01074       KURL oldurl = properties->kurl();
01075       // Tell properties. Warning, this changes the result of properties->kurl() !
01076       properties->rename( n );
01077 
01078       // Update also relative path (for apps and mimetypes)
01079       if ( !m_sRelativePath.isEmpty() )
01080         determineRelativePath( properties->kurl().path() );
01081 
01082       kdDebug(250) << "New URL = " << properties->kurl().url() << endl;
01083       kdDebug(250) << "old = " << oldurl.url() << endl;
01084 
01085       // Don't remove the template !!
01086       if ( !m_bFromTemplate ) // (normal renaming)
01087         job = KIO::move( oldurl, properties->kurl() );
01088       else // Copying a template
01089         job = KIO::copy( oldurl, properties->kurl() );
01090 
01091       connect( job, SIGNAL( result( KIO::Job * ) ),
01092                SLOT( slotCopyFinished( KIO::Job * ) ) );
01093       connect( job, SIGNAL( renamed( KIO::Job *, const KURL &, const KURL & ) ),
01094                SLOT( slotFileRenamed( KIO::Job *, const KURL &, const KURL & ) ) );
01095       // wait for job
01096       QWidget dummy(0,0,WType_Dialog|WShowModal);
01097       qt_enter_modal(&dummy);
01098       qApp->enter_loop();
01099       qt_leave_modal(&dummy);
01100       return;
01101     }
01102   }
01103 
01104   // No job, keep going
01105   slotCopyFinished( 0L );
01106 }
01107 
01108 void KFilePropsPlugin::slotCopyFinished( KIO::Job * job )
01109 {
01110   kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl;
01111   if (job)
01112   {
01113     // allow apply() to return
01114     qApp->exit_loop();
01115     if ( job->error() )
01116     {
01117         job->showErrorDialog( d->m_frame );
01118         // Didn't work. Revert the URL to the old one
01119         properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcURLs().first() );
01120         properties->abortApplying(); // Don't apply the changes to the wrong file !
01121         return;
01122     }
01123   }
01124 
01125   assert( properties->item() );
01126   assert( !properties->item()->url().isEmpty() );
01127 
01128   // Save the file where we can -> usually in ~/.kde/...
01129   if (KBindingPropsPlugin::supports(properties->items()) && !m_sRelativePath.isEmpty())
01130   {
01131     KURL newURL;
01132     newURL.setPath( locateLocal("mime", m_sRelativePath) );
01133     properties->updateUrl( newURL );
01134   }
01135   else if (KExecPropsPlugin::supports(properties->items()) && !m_sRelativePath.isEmpty())
01136   {
01137     kdDebug(250) << "KFilePropsPlugin::slotCopyFinished " << m_sRelativePath << endl;
01138     KURL newURL;
01139     newURL.setPath( locateLocal("apps", m_sRelativePath) );
01140     kdDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path() << endl;
01141     properties->updateUrl( newURL );
01142   }
01143 
01144   // handle icon changes - only local files for now
01145   // TODO: Use KTempFile and KIO::file_copy with resume = true
01146   if (!iconArea->isA("QLabel") && properties->kurl().isLocalFile()) {
01147     KIconButton *iconButton = (KIconButton *) iconArea;
01148     QString path;
01149 
01150     if (S_ISDIR(properties->item()->mode()))
01151     {
01152       path = properties->kurl().path(1) + QString::fromLatin1(".directory");
01153       // don't call updateUrl because the other tabs (i.e. permissions)
01154       // apply to the directory, not the .directory file.
01155     }
01156     else
01157       path = properties->kurl().path();
01158 
01159     // Get the default image
01160     QString str = KMimeType::findByURL( properties->kurl(),
01161                                         properties->item()->mode(),
01162                                         true )->KServiceType::icon();
01163     // Is it another one than the default ?
01164     QString sIcon;
01165     if ( str != iconButton->icon() )
01166       sIcon = iconButton->icon();
01167     // (otherwise write empty value)
01168 
01169     kdDebug(250) << "**" << path << "**" << endl;
01170     QFile f( path );
01171 
01172     // If default icon and no .directory file -> don't create one
01173     if ( !sIcon.isEmpty() || f.exists() )
01174     {
01175         if ( !f.open( IO_ReadWrite ) ) {
01176           KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient access to write to <b>%1</b>.</qt>").arg(path));
01177           return;
01178         }
01179         f.close();
01180 
01181         KDesktopFile cfg(path);
01182         kdDebug(250) << "sIcon = " << (sIcon) << endl;
01183         kdDebug(250) << "str = " << (str) << endl;
01184         cfg.writeEntry( QString::fromLatin1("Icon"), sIcon );
01185         cfg.sync();
01186     }
01187   }
01188 }
01189 
01190 void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KURL &, const KURL & newUrl )
01191 {
01192   // This is called in case of an existing local file during the copy/move operation,
01193   // if the user chooses Rename.
01194   properties->updateUrl( newUrl );
01195 }
01196 
01197 void KFilePropsPlugin::postApplyChanges()
01198 {
01199   KURL::List lst;
01200   KFileItemList items = properties->items();
01201   for ( KFileItemListIterator it( items ); it.current(); ++it )
01202     lst.append((*it)->url());
01203   KDirNotify_stub allDirNotify("*", "KDirNotify*");
01204   allDirNotify.FilesChanged( lst );
01205 }
01206 
01207 class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
01208 {
01209 public:
01210   KFilePermissionsPropsPluginPrivate()
01211   {
01212   }
01213   ~KFilePermissionsPropsPluginPrivate()
01214   {
01215   }
01216 
01217   QFrame *m_frame;
01218   QCheckBox *cbRecursive;
01219   mode_t partialPermissions;
01220 };
01221 
01222 KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props )
01223   : KPropsDlgPlugin( _props )
01224 {
01225   d = new KFilePermissionsPropsPluginPrivate;
01226   d->cbRecursive = 0L;
01227   grpCombo = 0L; grpEdit = 0;
01228   usrEdit = 0L;
01229   QString path = properties->kurl().path(-1);
01230   QString fname = properties->kurl().fileName();
01231   bool isLocal = properties->kurl().isLocalFile();
01232 
01233   bool IamRoot = (geteuid() == 0);
01234 
01235   KFileItem * item = properties->item();
01236   bool isLink = item->isLink();
01237   bool isDir = item->isDir(); // all dirs
01238   bool hasDir = item->isDir(); // at least one dir
01239   permissions = item->permissions(); // common permissions to all files
01240   d->partialPermissions = permissions; // permissions that only some files have (at first we take everything)
01241   strOwner = item->user();
01242   strGroup = item->group();
01243 
01244   if ( properties->items().count() > 1 )
01245   {
01246     // Multiple items: see what they have in common
01247     KFileItemList items = properties->items();
01248     KFileItemListIterator it( items );
01249     for ( ++it /*no need to check the first one again*/ ; it.current(); ++it )
01250     {
01251       if ( (*it)->isLink() != isLink )
01252         isLink = false;
01253       if ( (*it)->isDir() != isDir )
01254         isDir = false;
01255       hasDir |= (*it)->isDir();
01256       if ( (*it)->permissions() != permissions )
01257       {
01258         permissions &= (*it)->permissions();
01259         d->partialPermissions |= (*it)->permissions();
01260       }
01261       if ( (*it)->user() != strOwner )
01262         strOwner = QString::null;
01263       if ( (*it)->group() != strGroup )
01264         strGroup = QString::null;
01265     }
01266   }
01267 
01268   // keep only what's not in the common permissions
01269   d->partialPermissions = d->partialPermissions & ~permissions;
01270 
01271   bool isMyFile = false;
01272 
01273   if (isLocal && !strOwner.isEmpty()) { // local files, and all owned by the same person
01274     struct passwd *myself = getpwuid( geteuid() );
01275     if ( myself != 0L )
01276     {
01277       isMyFile = (strOwner == QString::fromLocal8Bit(myself->pw_name));
01278     } else
01279       kdWarning() << "I don't exist ?! geteuid=" << geteuid() << endl;
01280   } else {
01281     //We don't know, for remote files, if they are ours or not.
01282     //So we let the user change permissions, and
01283     //KIO::chmod will tell, if he had no right to do it.
01284     isMyFile = true;
01285   }
01286 
01287   d->m_frame = properties->dialog()->addPage(i18n("&Permissions"));
01288 
01289   QBoxLayout *box = new QVBoxLayout( d->m_frame, KDialog::spacingHint() );
01290 
01291   QLabel *l, *cl[3];
01292   QGroupBox *gb;
01293   QGridLayout *gl;
01294 
01295   /* Group: Access Permissions */
01296   gb = new QGroupBox ( i18n("Access Permissions"), d->m_frame );
01297   box->addWidget (gb);
01298 
01299   gl = new QGridLayout (gb, 6, 6, 15);
01300   gl->addRowSpacing(0, 10);
01301 
01302   l = new QLabel(i18n("Class"), gb);
01303   gl->addWidget(l, 1, 0);
01304 
01305   if (isDir)
01306     l = new QLabel( i18n("Show\nEntries"), gb );
01307   else
01308     l = new QLabel( i18n("Read"), gb );
01309   gl->addWidget (l, 1, 1);
01310 
01311   if (isDir)
01312     l = new QLabel( i18n("Write\nEntries"), gb );
01313   else
01314     l = new QLabel( i18n("Write"), gb );
01315   gl->addWidget (l, 1, 2);
01316 
01317   if (isDir)
01318     l = new QLabel( i18n("Enter directory", "Enter"), gb );
01319   else
01320     l = new QLabel( i18n("Exec"), gb );
01321   // GJ: Add space between normal and special modes
01322   QSize size = l->sizeHint();
01323   size.setWidth(size.width() + 15);
01324   l->setFixedSize(size);
01325   gl->addWidget (l, 1, 3);
01326 
01327   l = new QLabel( i18n("Special"), gb );
01328   gl->addMultiCellWidget(l, 1, 1, 4, 5);
01329 
01330   cl[0] = new QLabel( i18n("User"), gb );
01331   gl->addWidget (cl[0], 2, 0);
01332 
01333   cl[1] = new QLabel( i18n("Group"), gb );
01334   gl->addWidget (cl[1], 3, 0);
01335 
01336   cl[2] = new QLabel( i18n("Others"), gb );
01337   gl->addWidget (cl[2], 4, 0);
01338 
01339   l = new QLabel(i18n("Set UID"), gb);
01340   gl->addWidget(l, 2, 5);
01341 
01342   l = new QLabel(i18n("Set GID"), gb);
01343   gl->addWidget(l, 3, 5);
01344 
01345   l = new QLabel(i18n("File permission, sets user or group ID on execution", "Sticky"), gb);
01346   gl->addWidget(l, 4, 5);
01347 
01348   bool enablePage = (isMyFile || IamRoot) && (!isLink);
01349   /* Draw Checkboxes */
01350   for (int row = 0; row < 3 ; ++row) {
01351     for (int col = 0; col < 4; ++col) {
01352       QCheckBox *cb = new QCheckBox(gb);
01353       cb->setChecked(permissions & fperm[row][col]);
01354       if ( d->partialPermissions & fperm[row][col] )
01355       {
01356         cb->setTristate( true );
01357         cb->setNoChange();
01358       }
01359       cb->setEnabled( enablePage );
01360       permBox[row][col] = cb;
01361       gl->addWidget (permBox[row][col], row+2, col+1);
01362       connect( cb, SIGNAL( clicked() ),
01363                this, SIGNAL( changed() ) );
01364     }
01365   }
01366   gl->setColStretch(6, 10);
01367   gb->setEnabled( enablePage );
01368 
01369   /**** Group: Ownership ****/
01370   gb = new QGroupBox ( i18n("Ownership"), d->m_frame );
01371   box->addWidget (gb);
01372 
01373   gl = new QGridLayout (gb, 4, 3, 15);
01374   gl->addRowSpacing(0, 10);
01375 
01376   /*** Set Owner ***/
01377   l = new QLabel( i18n("User:"), gb );
01378   gl->addWidget (l, 1, 0);
01379 
01380   /* GJ: Don't autocomplete more than 1000 users. This is a kind of random
01381    * value. Huge sites having 10.000+ user have a fair chance of using NIS,
01382    * (possibly) making this unacceptably slow.
01383    * OTOH, it is nice to offer this functionality for the standard user.
01384    */
01385   int i, maxEntries = 1000;
01386   struct passwd *user;
01387   struct group *ge;
01388 
01389   /* File owner: For root, offer a KLineEdit with autocompletion.
01390    * For a user, who can never chown() a file, offer a QLabel.
01391    */
01392   if (IamRoot && isLocal)
01393   {
01394     usrEdit = new KLineEdit( gb );
01395     KCompletion *kcom = usrEdit->completionObject();
01396     kcom->setOrder(KCompletion::Sorted);
01397     setpwent();
01398     for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); i++)
01399       kcom->addItem(QString::fromLatin1(user->pw_name));
01400     endpwent();
01401     usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto :
01402                                KGlobalSettings::CompletionNone);
01403     usrEdit->setText(strOwner);
01404     gl->addWidget(usrEdit, 1, 1);
01405     connect( usrEdit, SIGNAL( textChanged( const QString & ) ),
01406              this, SIGNAL( changed() ) );
01407   }
01408   else
01409   {
01410     l = new QLabel(strOwner, gb);
01411     gl->addWidget(l, 1, 1);
01412   }
01413 
01414   /*** Set Group ***/
01415 
01416   QStringList groupList;
01417   QCString strUser;
01418   user = getpwuid(geteuid());
01419   if (user != 0L)
01420     strUser = user->pw_name;
01421 
01422   setgrent();
01423   for (i=0; ((ge = getgrent()) != 0L) && (i < maxEntries); i++)
01424   {
01425     if (IamRoot)
01426       groupList += QString::fromLatin1(ge->gr_name);
01427     else
01428     {
01429       /* pick the groups to which the user belongs */
01430       char ** members = ge->gr_mem;
01431       char * member;
01432       while ((member = *members) != 0L) {
01433         if (strUser == member) {
01434           groupList += QString::fromLocal8Bit(ge->gr_name);
01435           break;
01436         }
01437         ++members;
01438       }
01439     }
01440   }
01441   endgrent();
01442 
01443   /* add the effective Group to the list .. */
01444   ge = getgrgid (getegid());
01445   if (ge) {
01446     QString name = QString::fromLatin1(ge->gr_name);
01447     if (name.isEmpty())
01448       name.setNum(ge->gr_gid);
01449     if (groupList.find(name) == groupList.end())
01450       groupList += name;
01451   }
01452 
01453   bool isMyGroup = groupList.contains(strGroup);
01454 
01455   /* add the group the file currently belongs to ..
01456    * .. if its not there already
01457    */
01458   if (!isMyGroup)
01459     groupList += strGroup;
01460 
01461   l = new QLabel( i18n("Group:"), gb );
01462   gl->addWidget (l, 2, 0);
01463 
01464   /* Set group: if possible to change:
01465    * - Offer a KLineEdit for root, since he can change to any group.
01466    * - Offer a QComboBox for a normal user, since he can change to a fixed
01467    *   (small) set of groups only.
01468    * If not changeable: offer a QLabel.
01469    */
01470   if (IamRoot && isLocal)
01471   {
01472     grpEdit = new KLineEdit(gb);
01473     KCompletion *kcom = new KCompletion;
01474     kcom->setItems(groupList);
01475     grpEdit->setCompletionObject(kcom, true);
01476     grpEdit->setAutoDeleteCompletionObject( true );
01477     grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
01478     grpEdit->setText(strGroup);
01479     gl->addWidget(grpEdit, 2, 1);
01480     connect( grpEdit, SIGNAL( textChanged( const QString & ) ),
01481              this, SIGNAL( changed() ) );
01482   }
01483   else if ((groupList.count() > 1) && isMyFile && isLocal)
01484   {
01485     grpCombo = new QComboBox(gb, "combogrouplist");
01486     grpCombo->insertStringList(groupList);
01487     grpCombo->setCurrentItem(groupList.findIndex(strGroup));
01488     gl->addWidget(grpCombo, 2, 1);
01489     connect( grpCombo, SIGNAL( activated( int ) ),
01490              this, SIGNAL( changed() ) );
01491   }
01492   else
01493   {
01494     l = new QLabel(strGroup, gb);
01495     gl->addWidget(l, 2, 1);
01496   }
01497 
01498   gl->setColStretch(2, 10);
01499 
01500   // "Apply recursive" checkbox
01501   if ( hasDir )
01502   {
01503       d->cbRecursive = new QCheckBox( i18n("Apply changes to all subdirectories and their contents"), d->m_frame );
01504       box->addWidget( d->cbRecursive );
01505       connect( d->cbRecursive, SIGNAL( clicked() ),
01506                this, SLOT( slotRecursiveClicked() ) );
01507   }
01508 
01509   box->addStretch (10);
01510 
01511   if (isMyFile)
01512     cl[0]->setText(i18n("<b>User</b>"));
01513   else if (isMyGroup)
01514     cl[1]->setText(i18n("<b>Group</b>"));
01515   else
01516     cl[2]->setText(i18n("<b>Others</b>"));
01517 }
01518 
01519 // QString KFilePermissionsPropsPlugin::tabName () const
01520 // {
01521 //   return i18n ("&Permissions");
01522 // }
01523 
01524 KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin()
01525 {
01526   delete d;
01527 }
01528 
01529 bool KFilePermissionsPropsPlugin::supports( KFileItemList /*_items*/ )
01530 {
01531   return true;
01532 }
01533 
01534 void KFilePermissionsPropsPlugin::slotRecursiveClicked()
01535 {
01536   // If we want to apply permissions recursively, then we didn't
01537   // show up the right permissions to start with. Files in subdirs might
01538   // have other flags.... At least, let the user the possibility
01539   // to set any flag to "unchanged", so that he isn't forced to set +x
01540   // on all files !
01541   for (int row = 0;row < 3; ++row)
01542     for (int col = 0; col < 4; ++col)
01543       permBox[row][col]->setTristate();
01544 }
01545 
01546 void KFilePermissionsPropsPlugin::applyChanges()
01547 {
01548   mode_t newPermission = 0;
01549   mode_t newPartialPermission = 0;
01550   mode_t permissionMask = 0;
01551   for (int row = 0;row < 3; ++row)
01552     for (int col = 0; col < 4; ++col)
01553     {
01554       switch (permBox[row][col]->state())
01555       {
01556           case QCheckBox::On:
01557             newPermission |= fperm[row][col];
01558             //fall through
01559           case QCheckBox::Off:
01560             permissionMask |= fperm[row][col];
01561             break;
01562           default: // NoChange
01563         newPartialPermission |= fperm[ row ][ col ];
01564             break;
01565       }
01566     }
01567 
01568   QString owner, group;
01569   if (usrEdit)
01570     owner = usrEdit->text();
01571   if (grpEdit)
01572     group = grpEdit->text();
01573   else if (grpCombo)
01574     group = grpCombo->currentText();
01575 
01576   if (owner == strOwner)
01577       owner = QString::null; // no change
01578 
01579   if (group == strGroup)
01580       group = QString::null;
01581 
01582   kdDebug(250) << "old permissions : " << QString::number(permissions,8) << endl;
01583   kdDebug(250) << "new permissions : " << QString::number(newPermission,8) << endl;
01584   kdDebug(250) << "permissions mask : " << QString::number(permissionMask,8) << endl;
01585   kdDebug(250) << "url=" << properties->items().first()->url().url() << endl;
01586 
01587   if ( permissions != newPermission || d->partialPermissions != newPartialPermission
01588           || !owner.isEmpty() || !group.isEmpty() )
01589   {
01590     KIO::Job * job = KIO::chmod( properties->items(), newPermission, permissionMask,
01591                                  owner, group,
01592                                  d->cbRecursive && d->cbRecursive->isChecked() );
01593     connect( job, SIGNAL( result( KIO::Job * ) ),
01594              SLOT( slotChmodResult( KIO::Job * ) ) );
01595     // Wait for job
01596     QWidget dummy(0,0,WType_Dialog|WShowModal);
01597     qt_enter_modal(&dummy);
01598     qApp->enter_loop();
01599     qt_leave_modal(&dummy);
01600   }
01601 }
01602 
01603 void KFilePermissionsPropsPlugin::slotChmodResult( KIO::Job * job )
01604 {
01605   kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl;
01606   if (job->error())
01607     job->showErrorDialog( d->m_frame );
01608   // allow apply() to return
01609   qApp->exit_loop();
01610 }
01611 
01612 class KExecPropsPlugin::KExecPropsPluginPrivate
01613 {
01614 public:
01615   KExecPropsPluginPrivate()
01616   {
01617   }
01618   ~KExecPropsPluginPrivate()
01619   {
01620   }
01621 
01622   QFrame *m_frame;
01623 };
01624 
01625 KExecPropsPlugin::KExecPropsPlugin( KPropertiesDialog *_props )
01626   : KPropsDlgPlugin( _props )
01627 {
01628   d = new KExecPropsPluginPrivate;
01629   d->m_frame = properties->dialog()->addPage(i18n("E&xecute"));
01630   QVBoxLayout * mainlayout = new QVBoxLayout( d->m_frame );
01631   mainlayout->setSpacing( KDialog::spacingHint() );
01632 
01633   // Now the widgets in the top layout
01634 
01635   QLabel* l;
01636   l = new QLabel( i18n( "Comman&d:" ), d->m_frame );
01637   mainlayout->addWidget(l);
01638 
01639   QHBoxLayout * hlayout;
01640   hlayout = new QHBoxLayout(KDialog::spacingHint());
01641   mainlayout->addLayout(hlayout);
01642 
01643   execEdit = new KLineEdit( d->m_frame );
01644   hlayout->addWidget(execEdit, 1);
01645 
01646   l->setBuddy( execEdit );
01647 
01648   execBrowse = new QPushButton( d->m_frame );
01649   execBrowse->setText( i18n("&Browse...") );
01650   hlayout->addWidget(execBrowse);
01651 
01652   // The groupbox about swallowing
01653   QGroupBox* tmpQGroupBox;
01654   tmpQGroupBox = new QGroupBox( i18n("Panel Embedding"), d->m_frame );
01655   tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
01656 
01657   mainlayout->addWidget(tmpQGroupBox);
01658 
01659   QGridLayout *grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2);
01660   grid->setSpacing( KDialog::spacingHint() );
01661   grid->setColStretch(1, 1);
01662 
01663   l = new QLabel( i18n( "&Execute on click:" ), tmpQGroupBox );
01664   grid->addWidget(l, 0, 0);
01665 
01666   swallowExecEdit = new KLineEdit( tmpQGroupBox );
01667   grid->addWidget(swallowExecEdit, 0, 1);
01668 
01669   l->setBuddy( swallowExecEdit );
01670 
01671   l = new QLabel( i18n( "&Window title:" ), tmpQGroupBox );
01672   grid->addWidget(l, 1, 0);
01673 
01674   swallowTitleEdit = new KLineEdit( tmpQGroupBox );
01675   grid->addWidget(swallowTitleEdit, 1, 1);
01676 
01677   l->setBuddy( swallowTitleEdit );
01678 
01679   // The groupbox about run in terminal
01680 
01681   tmpQGroupBox = new QGroupBox( d->m_frame );
01682   tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
01683 
01684   mainlayout->addWidget(tmpQGroupBox);
01685 
01686   grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2);
01687   grid->setSpacing( KDialog::spacingHint() );
01688   grid->setColStretch(1, 1);
01689 
01690   terminalCheck = new QCheckBox( tmpQGroupBox );
01691   terminalCheck->setText( i18n("&Run in terminal") );
01692   grid->addMultiCellWidget(terminalCheck, 0, 0, 0, 1);
01693 
01694   terminalLabel = new QLabel( i18n( "&Terminal options:" ), tmpQGroupBox );
01695   grid->addWidget(terminalLabel, 1, 0);
01696 
01697   terminalEdit = new KLineEdit( tmpQGroupBox );
01698   grid->addWidget(terminalEdit, 1, 1);
01699 
01700   terminalLabel->setBuddy( terminalEdit );
01701 
01702   // The groupbox about run with substituted uid.
01703 
01704   tmpQGroupBox = new QGroupBox( d->m_frame );
01705   tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
01706 
01707   mainlayout->addWidget(tmpQGroupBox);
01708 
01709   grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2);
01710   grid->setSpacing(KDialog::spacingHint());
01711   grid->setColStretch(1, 1);
01712 
01713   suidCheck = new QCheckBox(tmpQGroupBox);
01714   suidCheck->setText(i18n("Ru&n as a different user"));
01715   grid->addMultiCellWidget(suidCheck, 0, 0, 0, 1);
01716 
01717   suidLabel = new QLabel(i18n( "&Username:" ), tmpQGroupBox);
01718   grid->addWidget(suidLabel, 1, 0);
01719 
01720   suidEdit = new KLineEdit(tmpQGroupBox);
01721   grid->addWidget(suidEdit, 1, 1);
01722 
01723   suidLabel->setBuddy( suidEdit );
01724 
01725   mainlayout->addStretch(1);
01726 
01727   // now populate the page
01728   QString path = _props->kurl().path();
01729   QFile f( path );
01730   if ( !f.open( IO_ReadOnly ) )
01731     return;
01732   f.close();
01733 
01734   KSimpleConfig config( path );
01735   config.setDollarExpansion( false );
01736   config.setDesktopGroup();
01737   execStr = config.readEntry( QString::fromLatin1("Exec") );
01738   swallowExecStr = config.readEntry( QString::fromLatin1("SwallowExec") );
01739   swallowTitleStr = config.readEntry( QString::fromLatin1("SwallowTitle") );
01740   termBool = config.readBoolEntry( QString::fromLatin1("Terminal") );
01741   termOptionsStr = config.readEntry( QString::fromLatin1("TerminalOptions") );
01742   suidBool = config.readBoolEntry( QString::fromLatin1("X-KDE-SubstituteUID") );
01743   suidUserStr = config.readEntry( QString::fromLatin1("X-KDE-Username") );
01744 
01745   if ( !swallowExecStr.isNull() )
01746     swallowExecEdit->setText( swallowExecStr );
01747   if ( !swallowTitleStr.isNull() )
01748     swallowTitleEdit->setText( swallowTitleStr );
01749 
01750   if ( !execStr.isNull() )
01751     execEdit->setText( execStr );
01752   if ( !termOptionsStr.isNull() )
01753     terminalEdit->setText( termOptionsStr );
01754 
01755   terminalCheck->setChecked( termBool );
01756   enableCheckedEdit();
01757 
01758   suidCheck->setChecked( suidBool );
01759   suidEdit->setText( suidUserStr );
01760   enableSuidEdit();
01761 
01762   // Provide username completion up to 1000 users.
01763   KCompletion *kcom = new KCompletion;
01764   kcom->setOrder(KCompletion::Sorted);
01765   struct passwd *pw;
01766   int i, maxEntries = 1000;
01767   setpwent();
01768   for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
01769     kcom->addItem(QString::fromLatin1(pw->pw_name));
01770   endpwent();
01771   if (i < maxEntries)
01772   {
01773     suidEdit->setCompletionObject(kcom, true);
01774     suidEdit->setAutoDeleteCompletionObject( true );
01775     suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
01776   }
01777   else
01778   {
01779     delete kcom;
01780   }
01781 
01782   connect( swallowExecEdit, SIGNAL( textChanged( const QString & ) ),
01783            this, SIGNAL( changed() ) );
01784   connect( swallowTitleEdit, SIGNAL( textChanged( const QString & ) ),
01785            this, SIGNAL( changed() ) );
01786   connect( execEdit, SIGNAL( textChanged( const QString & ) ),
01787            this, SIGNAL( changed() ) );
01788   connect( terminalEdit, SIGNAL( textChanged( const QString & ) ),
01789            this, SIGNAL( changed() ) );
01790   connect( terminalCheck, SIGNAL( toggled( bool ) ),
01791            this, SIGNAL( changed() ) );
01792   connect( suidCheck, SIGNAL( toggled( bool ) ),
01793            this, SIGNAL( changed() ) );
01794   connect( suidEdit, SIGNAL( textChanged( const QString & ) ),
01795            this, SIGNAL( changed() ) );
01796 
01797   connect( execBrowse, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
01798   connect( terminalCheck, SIGNAL( clicked() ), this,  SLOT( enableCheckedEdit() ) );
01799   connect( suidCheck, SIGNAL( clicked() ), this,  SLOT( enableSuidEdit() ) );
01800 
01801 }
01802 
01803 KExecPropsPlugin::~KExecPropsPlugin()
01804 {
01805   delete d;
01806 }
01807 
01808 // QString KExecPropsPlugin::tabName () const
01809 // {
01810 //   return i18n ("E&xecute");
01811 // }
01812 
01813 void KExecPropsPlugin::enableCheckedEdit()
01814 {
01815   bool checked = terminalCheck->isChecked();
01816   terminalLabel->setEnabled( checked );
01817   terminalEdit->setEnabled( checked );
01818 }
01819 
01820 void KExecPropsPlugin::enableSuidEdit()
01821 {
01822   bool checked = suidCheck->isChecked();
01823   suidLabel->setEnabled( checked );
01824   suidEdit->setEnabled( checked );
01825 }
01826 
01827 bool KExecPropsPlugin::supports( KFileItemList _items )
01828 {
01829   if ( _items.count() != 1 )
01830     return false;
01831   KFileItem * item = _items.first();
01832   // check if desktop file
01833   if ( !KPropsDlgPlugin::isDesktopFile( item ) )
01834     return false;
01835   // open file and check type
01836   KDesktopFile config( item->url().path(), true /* readonly */ );
01837   return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access");
01838 }
01839 
01840 void KExecPropsPlugin::applyChanges()
01841 {
01842   kdDebug(250) << "KExecPropsPlugin::applyChanges" << endl;
01843   QString path = properties->kurl().path();
01844 
01845   QFile f( path );
01846 
01847   if ( !f.open( IO_ReadWrite ) ) {
01848     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient access to write to <b>%1</b>.</qt>").arg(path));
01849     return;
01850   }
01851   f.close();
01852 
01853   KSimpleConfig config( path );
01854   config.setDesktopGroup();
01855   config.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("Application"));
01856   config.writeEntry( QString::fromLatin1("Exec"), execEdit->text() );
01857   config.writeEntry( QString::fromLatin1("SwallowExec"), swallowExecEdit->text() );
01858   config.writeEntry( QString::fromLatin1("SwallowTitle"), swallowTitleEdit->text() );
01859   config.writeEntry( QString::fromLatin1("Terminal"), terminalCheck->isChecked() );
01860   config.writeEntry( QString::fromLatin1("TerminalOptions"), terminalEdit->text() );
01861   config.writeEntry( QString::fromLatin1("X-KDE-SubstituteUID"), suidCheck->isChecked() );
01862   config.writeEntry( QString::fromLatin1("X-KDE-Username"), suidEdit->text() );
01863 }
01864 
01865 
01866 void KExecPropsPlugin::slotBrowseExec()
01867 {
01868     KURL f = KFileDialog::getOpenURL( QString::null,
01869                                       QString::null, d->m_frame );
01870     if ( f.isEmpty() )
01871         return;
01872 
01873     if ( !f.isLocalFile()) {
01874         KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported."));
01875         return;
01876     }
01877 
01878     QString path = f.path();
01879     KRun::shellQuote( path );
01880     execEdit->setText( path );
01881 }
01882 
01883 class KURLPropsPlugin::KURLPropsPluginPrivate
01884 {
01885 public:
01886   KURLPropsPluginPrivate()
01887   {
01888   }
01889   ~KURLPropsPluginPrivate()
01890   {
01891   }
01892 
01893   QFrame *m_frame;
01894 };
01895 
01896 KURLPropsPlugin::KURLPropsPlugin( KPropertiesDialog *_props )
01897   : KPropsDlgPlugin( _props )
01898 {
01899   d = new KURLPropsPluginPrivate;
01900   d->m_frame = properties->dialog()->addPage(i18n("U&RL"));
01901   QVBoxLayout * layout = new QVBoxLayout(d->m_frame, KDialog::spacingHint());
01902 
01903   QLabel *l;
01904   l = new QLabel( d->m_frame, "Label_1" );
01905   l->setText( i18n("URL:") );
01906   layout->addWidget(l);
01907 
01908   URLEdit = new KURLRequester( d->m_frame, "URL Requester" );
01909   layout->addWidget(URLEdit);
01910 
01911   QString path = properties->kurl().path();
01912 
01913   QFile f( path );
01914   if ( !f.open( IO_ReadOnly ) )
01915     return;
01916   f.close();
01917 
01918   KSimpleConfig config( path );
01919   config.setDesktopGroup();
01920   URLStr = config.readEntry( QString::fromLatin1("URL") );
01921 
01922   if ( !URLStr.isNull() )
01923     URLEdit->setURL( URLStr );
01924 
01925   connect( URLEdit, SIGNAL( textChanged( const QString & ) ),
01926            this, SIGNAL( changed() ) );
01927 
01928   layout->addStretch (1);
01929 }
01930 
01931 KURLPropsPlugin::~KURLPropsPlugin()
01932 {
01933   delete d;
01934 }
01935 
01936 // QString KURLPropsPlugin::tabName () const
01937 // {
01938 //   return i18n ("U&RL");
01939 // }
01940 
01941 bool KURLPropsPlugin::supports( KFileItemList _items )
01942 {
01943   if ( _items.count() != 1 )
01944     return false;
01945   KFileItem * item = _items.first();
01946   // check if desktop file
01947   if ( !KPropsDlgPlugin::isDesktopFile( item ) )
01948     return false;
01949 
01950   // open file and check type
01951   KDesktopFile config( item->url().path(), true /* readonly */ );
01952   return config.hasLinkType();
01953 }
01954 
01955 void KURLPropsPlugin::applyChanges()
01956 {
01957   QString path = properties->kurl().path();
01958 
01959   QFile f( path );
01960   if ( !f.open( IO_ReadWrite ) ) {
01961     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient access to write to <b>%1</b>.</qt>").arg(path));
01962     return;
01963   }
01964   f.close();
01965 
01966   KSimpleConfig config( path );
01967   config.setDesktopGroup();
01968   config.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("Link"));
01969   config.writeEntry( QString::fromLatin1("URL"), URLEdit->url() );
01970   // Users can't create a Link .desktop file with a Name field,
01971   // but distributions can. Update the Name field in that case.
01972   if ( config.hasKey("Name") )
01973   {
01974     // ### duplicated from KApplicationPropsPlugin
01975     QString nameStr = properties->kurl().fileName();
01976     if ( nameStr.right(8) == QString::fromLatin1(".desktop") )
01977       nameStr.truncate( nameStr.length() - 8 );
01978     if ( nameStr.right(7) == QString::fromLatin1(".kdelnk") )
01979       nameStr.truncate( nameStr.length() - 7 );
01980     config.writeEntry( QString::fromLatin1("Name"), nameStr );
01981   }
01982 }
01983 
01984 /* ----------------------------------------------------
01985  *
01986  * KApplicationPropsPlugin
01987  *
01988  * -------------------------------------------------- */
01989 
01990 class KApplicationPropsPlugin::KApplicationPropsPluginPrivate
01991 {
01992 public:
01993   KApplicationPropsPluginPrivate()
01994   {
01995       m_kdesktopMode = QCString(qApp->name()) == "kdesktop"; // nasty heh?
01996   }
01997   ~KApplicationPropsPluginPrivate()
01998   {
01999   }
02000 
02001   QFrame *m_frame;
02002   bool m_kdesktopMode;
02003 };
02004 
02005 KApplicationPropsPlugin::KApplicationPropsPlugin( KPropertiesDialog *_props )
02006   : KPropsDlgPlugin( _props )
02007 {
02008   d = new KApplicationPropsPluginPrivate;
02009   d->m_frame = properties->dialog()->addPage(i18n("&Application"));
02010   QVBoxLayout *toplayout = new QVBoxLayout( d->m_frame, KDialog::spacingHint());
02011 
02012   QIconSet iconSet;
02013   QPixmap pixMap;
02014 
02015   addExtensionButton = new QPushButton( QString::null, d->m_frame );
02016   iconSet = SmallIconSet( "back" );
02017   addExtensionButton->setIconSet( iconSet );
02018   pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal );
02019   addExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
02020   connect( addExtensionButton, SIGNAL( clicked() ),
02021             SLOT( slotAddExtension() ) );
02022 
02023   delExtensionButton = new QPushButton( QString::null, d->m_frame );
02024   iconSet = SmallIconSet( "forward" );
02025   delExtensionButton->setIconSet( iconSet );
02026   delExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
02027   connect( delExtensionButton, SIGNAL( clicked() ),
02028             SLOT( slotDelExtension() ) );
02029 
02030   QLabel *l;
02031 
02032   QGridLayout *grid = new QGridLayout(2, 2);
02033   grid->setColStretch(1, 1);
02034   toplayout->addLayout(grid);
02035 
02036   if ( d->m_kdesktopMode )
02037   {
02038       // in kdesktop the name field comes from the first tab
02039       nameEdit = 0L;
02040   }
02041   else
02042   {
02043       l = new QLabel(i18n("Name:"), d->m_frame, "Label_4" );
02044       grid->addWidget(l, 0, 0);
02045 
02046       nameEdit = new KLineEdit( d->m_frame, "LineEdit_3" );
02047       grid->addWidget(nameEdit, 0, 1);
02048   }
02049 
02050   l = new QLabel(i18n("Description:"),  d->m_frame, "Label_5" );
02051   grid->addWidget(l, 1, 0);
02052 
02053   genNameEdit = new KLineEdit( d->m_frame, "LineEdit_4" );
02054   grid->addWidget(genNameEdit, 1, 1);
02055 
02056   l = new QLabel(i18n("Comment:"),  d->m_frame, "Label_3" );
02057   grid->addWidget(l, 2, 0);
02058 
02059   commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" );
02060   grid->addWidget(commentEdit, 2, 1);
02061 
02062   l = new QLabel(i18n("File types:"), d->m_frame);
02063   toplayout->addWidget(l, 0, AlignLeft);
02064 
02065   grid = new QGridLayout(4, 3);
02066   grid->setColStretch(0, 1);
02067   grid->setColStretch(2, 1);
02068   grid->setRowStretch( 0, 1 );
02069   grid->setRowStretch( 3, 1 );
02070   toplayout->addLayout(grid, 2);
02071 
02072   extensionsList = new QListBox( d->m_frame );
02073   extensionsList->setSelectionMode( QListBox::Extended );
02074   grid->addMultiCellWidget(extensionsList, 0, 3, 0, 0);
02075 
02076   grid->addWidget(addExtensionButton, 1, 1);
02077   grid->addWidget(delExtensionButton, 2, 1);
02078 
02079   availableExtensionsList = new QListBox( d->m_frame );
02080   availableExtensionsList->setSelectionMode( QListBox::Extended );
02081   grid->addMultiCellWidget(availableExtensionsList, 0, 3, 2, 2);
02082 
02083   QString path = properties->kurl().path() ;
02084   QFile f( path );
02085   if ( !f.open( IO_ReadOnly ) )
02086     return;
02087   f.close();
02088 
02089   KSimpleConfig config( path );
02090   config.setDesktopGroup();
02091   QString commentStr = config.readEntry( QString::fromLatin1("Comment") );
02092   QString genNameStr = config.readEntry( QString::fromLatin1("GenericName") );
02093 
02094   QStringList selectedTypes = config.readListEntry( "ServiceTypes" );
02095   // For compatibility with KDE 1.x
02096   selectedTypes += config.readListEntry( "MimeType", ';' );
02097 
02098   QString nameStr = config.readEntry( QString::fromLatin1("Name") );
02099   if ( nameStr.isEmpty() || d->m_kdesktopMode ) {
02100     // We'll use the file name if no name is specified
02101     // because we _need_ a Name for a valid file.
02102     // But let's do it in apply, not here, so that we pick up the right name.
02103     setDirty();
02104   }
02105 
02106   commentEdit->setText( commentStr );
02107   genNameEdit->setText( genNameStr );
02108   if ( nameEdit )
02109       nameEdit->setText( nameStr );
02110 
02111   selectedTypes.sort();
02112   QStringList::Iterator sit = selectedTypes.begin();
02113   for( ; sit != selectedTypes.end(); ++sit ) {
02114     if ( !((*sit).isEmpty()) )
02115       extensionsList->insertItem( *sit );
02116   }
02117 
02118   KMimeType::List mimeTypes = KMimeType::allMimeTypes();
02119   QValueListIterator<KMimeType::Ptr> it2 = mimeTypes.begin();
02120   for ( ; it2 != mimeTypes.end(); ++it2 )
02121     addMimeType ( (*it2)->name() );
02122 
02123   updateButton();
02124 
02125   connect( extensionsList, SIGNAL( highlighted( int ) ),
02126            this, SLOT( updateButton() ) );
02127   connect( availableExtensionsList, SIGNAL( highlighted( int ) ),
02128            this, SLOT( updateButton() ) );
02129 
02130   connect( addExtensionButton, SIGNAL( clicked() ),
02131            this, SIGNAL( changed() ) );
02132   connect( delExtensionButton, SIGNAL( clicked() ),
02133            this, SIGNAL( changed() ) );
02134   if ( nameEdit )
02135       connect( nameEdit, SIGNAL( textChanged( const QString & ) ),
02136                this, SIGNAL( changed() ) );
02137   connect( commentEdit, SIGNAL( textChanged( const QString & ) ),
02138            this, SIGNAL( changed() ) );
02139   connect( genNameEdit, SIGNAL( textChanged( const QString & ) ),
02140            this, SIGNAL( changed() ) );
02141   connect( availableExtensionsList, SIGNAL( selected( int ) ),
02142            this, SIGNAL( changed() ) );
02143   connect( extensionsList, SIGNAL( selected( int ) ),
02144            this, SIGNAL( changed() ) );
02145 }
02146 
02147 KApplicationPropsPlugin::~KApplicationPropsPlugin()
02148 {
02149   delete d;
02150 }
02151 
02152 // QString KApplicationPropsPlugin::tabName () const
02153 // {
02154 //   return i18n ("&Application");
02155 // }
02156 
02157 void KApplicationPropsPlugin::updateButton()
02158 {
02159     addExtensionButton->setEnabled(availableExtensionsList->currentItem()>-1);
02160     delExtensionButton->setEnabled(extensionsList->currentItem()>-1);
02161 }
02162 
02163 void KApplicationPropsPlugin::addMimeType( const QString & name )
02164 {
02165   // Add a mimetype to the list of available mime types if not in the extensionsList
02166 
02167   bool insert = true;
02168 
02169   for ( uint i = 0; i < extensionsList->count(); i++ )
02170     if ( extensionsList->text( i ) == name )
02171       insert = false;
02172 
02173   if ( insert )
02174   {
02175     availableExtensionsList->insertItem( name );
02176     availableExtensionsList->sort();
02177   }
02178 }
02179 
02180 bool KApplicationPropsPlugin::supports( KFileItemList _items )
02181 {
02182   // same constraints as KExecPropsPlugin : desktop file with Type = Application
02183   return KExecPropsPlugin::supports( _items );
02184 }
02185 
02186 void KApplicationPropsPlugin::applyChanges()
02187 {
02188   QString path = properties->kurl().path();
02189 
02190   QFile f( path );
02191 
02192   if ( !f.open( IO_ReadWrite ) ) {
02193     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient access to write to <b>%1</b>.</qt>").arg(path));
02194     return;
02195   }
02196   f.close();
02197 
02198   KSimpleConfig config( path );
02199   config.setDesktopGroup();
02200   config.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("Application"));
02201   config.writeEntry( QString::fromLatin1("Comment"), commentEdit->text() );
02202   config.writeEntry( QString::fromLatin1("Comment"), commentEdit->text(), true, false, true ); // for compat
02203   config.writeEntry( QString::fromLatin1("GenericName"), genNameEdit->text() );
02204   config.writeEntry( QString::fromLatin1("GenericName"), genNameEdit->text(), true, false, true ); // for compat
02205 
02206   QStringList selectedTypes;
02207   for ( uint i = 0; i < extensionsList->count(); i++ )
02208     selectedTypes.append( extensionsList->text( i ) );
02209 
02210   config.writeEntry( QString::fromLatin1("MimeType"), selectedTypes, ';' );
02211   config.writeEntry( QString::fromLatin1("ServiceTypes"), "" );
02212   // hmm, actually it should probably be the contrary (but see also typeslistitem.cpp)
02213 
02214   QString nameStr = nameEdit ? nameEdit->text() : QString::null;
02215   if ( nameStr.isEmpty() ) // nothing entered, or widget not existing at all (kdesktop mode)
02216   {
02217     nameStr = properties->kurl().fileName();
02218     if ( nameStr.right(8) == QString::fromLatin1(".desktop") )
02219       nameStr.truncate( nameStr.length() - 8 );
02220     if ( nameStr.right(7) == QString::fromLatin1(".kdelnk") )
02221       nameStr.truncate( nameStr.length() - 7 );
02222   }
02223   config.writeEntry( QString::fromLatin1("Name"), nameStr );
02224   config.writeEntry( QString::fromLatin1("Name"), nameStr, true, false, true );
02225 
02226   config.sync();
02227   f.close();
02228 }
02229 
02230 void KApplicationPropsPlugin::slotAddExtension()
02231 {
02232   QListBoxItem *item = availableExtensionsList->firstItem();
02233   QListBoxItem *nextItem;
02234 
02235   while ( item )
02236   {
02237     nextItem = item->next();
02238 
02239     if ( item->isSelected() )
02240     {
02241       extensionsList->insertItem( item->text() );
02242       availableExtensionsList->removeItem( availableExtensionsList->index( item ) );
02243     }
02244 
02245     item = nextItem;
02246   }
02247 
02248   extensionsList->sort();
02249   updateButton();
02250 }
02251 
02252 void KApplicationPropsPlugin::slotDelExtension()
02253 {
02254   QListBoxItem *item = extensionsList->firstItem();
02255   QListBoxItem *nextItem;
02256 
02257   while ( item )
02258   {
02259     nextItem = item->next();
02260 
02261     if ( item->isSelected() )
02262     {
02263       availableExtensionsList->insertItem( item->text() );
02264       extensionsList->removeItem( extensionsList->index( item ) );
02265     }
02266 
02267     item = nextItem;
02268   }
02269 
02270   availableExtensionsList->sort();
02271   updateButton();
02272 }
02273 
02274 /* ----------------------------------------------------
02275  *
02276  * KBindingPropsPlugin
02277  *
02278  * -------------------------------------------------- */
02279 
02280 class KBindingPropsPlugin::KBindingPropsPluginPrivate
02281 {
02282 public:
02283   KBindingPropsPluginPrivate()
02284   {
02285   }
02286   ~KBindingPropsPluginPrivate()
02287   {
02288   }
02289 
02290   QFrame *m_frame;
02291 };
02292 
02293 KBindingPropsPlugin::KBindingPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props )
02294 {
02295   d = new KBindingPropsPluginPrivate;
02296   d->m_frame = properties->dialog()->addPage(i18n("A&ssociation"));
02297   patternEdit = new KLineEdit( d->m_frame, "LineEdit_1" );
02298   commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" );
02299   mimeEdit = new KLineEdit( d->m_frame, "LineEdit_3" );
02300 
02301   QBoxLayout * mainlayout = new QVBoxLayout(d->m_frame, KDialog::spacingHint());
02302   QLabel* tmpQLabel;
02303 
02304   tmpQLabel = new QLabel( d->m_frame, "Label_1" );
02305   tmpQLabel->setText(  i18n("Pattern ( example: *.html;*.htm )") );
02306   tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
02307   mainlayout->addWidget(tmpQLabel, 1);
02308 
02309   //patternEdit->setGeometry( 10, 40, 210, 30 );
02310   //patternEdit->setText( "" );
02311   patternEdit->setMaxLength( 512 );
02312   patternEdit->setMinimumSize( patternEdit->sizeHint() );
02313   patternEdit->setFixedHeight( fontHeight );
02314   mainlayout->addWidget(patternEdit, 1);
02315 
02316   tmpQLabel = new QLabel( d->m_frame, "Label_2" );
02317   tmpQLabel->setText(  i18n("Mime Type") );
02318   tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
02319   mainlayout->addWidget(tmpQLabel, 1);
02320 
02321   //mimeEdit->setGeometry( 10, 160, 210, 30 );
02322   mimeEdit->setMaxLength( 256 );
02323   mimeEdit->setMinimumSize( mimeEdit->sizeHint() );
02324   mimeEdit->setFixedHeight( fontHeight );
02325   mainlayout->addWidget(mimeEdit, 1);
02326 
02327   tmpQLabel = new QLabel( d->m_frame, "Label_3" );
02328   tmpQLabel->setText(  i18n("Comment") );
02329   tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
02330   mainlayout->addWidget(tmpQLabel, 1);
02331 
02332   //commentEdit->setGeometry( 10, 100, 210, 30 );
02333   commentEdit->setMaxLength( 256 );
02334   commentEdit->setMinimumSize( commentEdit->sizeHint() );
02335   commentEdit->setFixedHeight( fontHeight );
02336   mainlayout->addWidget(commentEdit, 1);
02337 
02338   cbAutoEmbed = new QCheckBox( i18n("Left click previews"), d->m_frame, "cbAutoEmbed" );
02339   mainlayout->addWidget(cbAutoEmbed, 1);
02340 
02341   mainlayout->addStretch (10);
02342   mainlayout->activate();
02343 
02344   QFile f( _props->kurl().path() );
02345   if ( !f.open( IO_ReadOnly ) )
02346     return;
02347   f.close();
02348 
02349   KSimpleConfig config( _props->kurl().path() );
02350   config.setDesktopGroup();
02351   QString patternStr = config.readEntry( QString::fromLatin1("Patterns") );
02352   QString iconStr = config.readEntry( QString::fromLatin1("Icon") );
02353   QString commentStr = config.readEntry( QString::fromLatin1("Comment") );
02354   m_sMimeStr = config.readEntry( QString::fromLatin1("MimeType") );
02355 
02356   if ( !patternStr.isEmpty() )
02357     patternEdit->setText( patternStr );
02358   if ( !commentStr.isEmpty() )
02359     commentEdit->setText( commentStr );
02360   if ( !m_sMimeStr.isEmpty() )
02361     mimeEdit->setText( m_sMimeStr );
02362   cbAutoEmbed->setTristate();
02363   if ( config.hasKey( QString::fromLatin1("X-KDE-AutoEmbed") ) )
02364       cbAutoEmbed->setChecked( config.readBoolEntry( QString::fromLatin1("X-KDE-AutoEmbed") ) );
02365   else
02366       cbAutoEmbed->setNoChange();
02367 
02368   connect( patternEdit, SIGNAL( textChanged( const QString & ) ),
02369            this, SIGNAL( changed() ) );
02370   connect( commentEdit, SIGNAL( textChanged( const QString & ) ),
02371            this, SIGNAL( changed() ) );
02372   connect( mimeEdit, SIGNAL( textChanged( const QString & ) ),
02373            this, SIGNAL( changed() ) );
02374   connect( cbAutoEmbed, SIGNAL( toggled( bool ) ),
02375            this, SIGNAL( changed() ) );
02376 }
02377 
02378 KBindingPropsPlugin::~KBindingPropsPlugin()
02379 {
02380   delete d;
02381 }
02382 
02383 // QString KBindingPropsPlugin::tabName () const
02384 // {
02385 //   return i18n ("A&ssociation");
02386 // }
02387 
02388 bool KBindingPropsPlugin::supports( KFileItemList _items )
02389 {
02390   if ( _items.count() != 1 )
02391     return false;
02392   KFileItem * item = _items.first();
02393   // check if desktop file
02394   if ( !KPropsDlgPlugin::isDesktopFile( item ) )
02395     return false;
02396 
02397   // open file and check type
02398   KDesktopFile config( item->url().path(), true /* readonly */ );
02399   return config.hasMimeTypeType();
02400 }
02401 
02402 void KBindingPropsPlugin::applyChanges()
02403 {
02404   QString path = properties->kurl().path();
02405   QFile f( path );
02406 
02407   if ( !f.open( IO_ReadWrite ) )
02408   {
02409     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient access to write to <b>%1</b>.</qt>").arg(path));
02410     return;
02411   }
02412   f.close();
02413 
02414   KSimpleConfig config( path );
02415   config.setDesktopGroup();
02416   config.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("MimeType") );
02417 
02418   config.writeEntry( QString::fromLatin1("Patterns"),  patternEdit->text() );
02419   config.writeEntry( QString::fromLatin1("Comment"), commentEdit->text() );
02420   config.writeEntry( QString::fromLatin1("Comment"), commentEdit->text(), true, false, true ); // for compat
02421   config.writeEntry( QString::fromLatin1("MimeType"), mimeEdit->text() );
02422   if ( cbAutoEmbed->state() == QButton::NoChange )
02423       config.deleteEntry( QString::fromLatin1("X-KDE-AutoEmbed"), false );
02424   else
02425       config.writeEntry( QString::fromLatin1("X-KDE-AutoEmbed"), cbAutoEmbed->isChecked() );
02426   config.sync();
02427 }
02428 
02429 /* ----------------------------------------------------
02430  *
02431  * KDevicePropsPlugin
02432  *
02433  * -------------------------------------------------- */
02434 
02435 class KDevicePropsPlugin::KDevicePropsPluginPrivate
02436 {
02437 public:
02438   KDevicePropsPluginPrivate()
02439   {
02440   }
02441   ~KDevicePropsPluginPrivate()
02442   {
02443   }
02444 
02445   QFrame *m_frame;
02446 };
02447 
02448 KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props )
02449 {
02450   d = new KDevicePropsPluginPrivate;
02451   d->m_frame = properties->dialog()->addPage(i18n("De&vice"));
02452 
02453   QStringList devices;
02454   QCString fstabFile;
02455   indexDevice = 0;  // device on first column
02456   indexMountPoint = 1; // mount point on second column
02457   if ( QFile::exists(QString::fromLatin1("/etc/fstab")) ) // Linux, ...
02458   {
02459     fstabFile = "/etc/fstab";
02460   }
02461   else if ( QFile::exists(QString::fromLatin1("/etc/vfstab")) ) // Solaris
02462   {
02463     fstabFile = "/etc/vfstab";
02464     indexMountPoint++;
02465   }
02466 
02467   // insert your favorite location for fstab here
02468   if ( !fstabFile.isEmpty() )
02469   {
02470     QFile f( fstabFile );
02471     if ( f.open( IO_ReadOnly ) )
02472     {
02473       QTextStream stream( &f );
02474       while ( !stream.eof() )
02475       {
02476         QString line = stream.readLine();
02477         line = line.simplifyWhiteSpace();
02478         if (!line.isEmpty() && line[0] == '/') // skip comments but also
02479         {
02480           QStringList lst = QStringList::split( ' ', line );
02481           if ( lst.count() > 2 && lst[indexDevice] != QString::fromLatin1("/proc")
02482               && lst[indexMountPoint] != QString::fromLatin1("none")
02483               && lst[indexMountPoint] != QString::fromLatin1("-") )
02484           {
02485             devices.append( lst[indexDevice]+QString::fromLatin1(" (")
02486                              +lst[indexMountPoint]+QString::fromLatin1(")") );
02487             m_devicelist.append( line );
02488           }
02489         }
02490       }
02491       f.close();
02492     }
02493   }
02494 
02495 
02496   QGridLayout *layout = new QGridLayout( d->m_frame, 0, 3, KDialog::marginHint(),
02497                                         KDialog::spacingHint());
02498   layout->setColStretch(1, 1);
02499 
02500   QLabel* label;
02501   label = new QLabel( d->m_frame );
02502   label->setText( devices.count() == 0 ?
02503                       i18n("Device (/dev/fd0):") : // old style
02504                       i18n("Device:") ); // new style (combobox)
02505   layout->addWidget(label, 0, 0);
02506 
02507   device = new QComboBox( true, d->m_frame, "ComboBox_device" );
02508   device->insertStringList( devices );
02509   layout->addWidget(device, 0, 1);
02510   connect( device, SIGNAL( activated( int ) ),
02511            this, SLOT( slotActivated( int ) ) );
02512 
02513   readonly = new QCheckBox( d->m_frame, "CheckBox_readonly" );
02514   readonly->setText(  i18n("Read only") );
02515   layout->addWidget(readonly, 1, 1);
02516 
02517   label = new QLabel( d->m_frame );
02518   label->setText( devices.count()==0 ?
02519                       i18n("Mount point (/mnt/floppy):") : // old style
02520                       i18n("Mount point:")); // new style (combobox)
02521   layout->addWidget(label, 2, 0);
02522 
02523   mountpoint = new QLabel( d->m_frame, "LineEdit_mountpoint" );
02524 
02525   layout->addWidget(mountpoint, 2, 1);
02526 
02527   KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame);
02528   layout->addMultiCellWidget(sep, 4, 4, 0, 2);
02529 
02530   unmounted = new KIconButton( d->m_frame );
02531   unmounted->setFixedSize(70, 70);
02532   unmounted->setIconType(KIcon::Desktop, KIcon::Device);
02533   layout->addWidget(unmounted, 5, 0);
02534 
02535   label = new QLabel( i18n("Unmounted Icon"),  d->m_frame );
02536   layout->addWidget(label, 5, 1);
02537 
02538   layout->setRowStretch(6, 1);
02539 
02540   QString path( _props->kurl().path() );
02541 
02542   QFile f( path );
02543   if ( !f.open( IO_ReadOnly ) )
02544     return;
02545   f.close();
02546 
02547   KSimpleConfig config( path );
02548   config.setDesktopGroup();
02549   QString deviceStr = config.readEntry( QString::fromLatin1("Dev") );
02550   QString mountPointStr = config.readEntry( QString::fromLatin1("MountPoint") );
02551   bool ro = config.readBoolEntry( QString::fromLatin1("ReadOnly"), false );
02552   QString unmountedStr = config.readEntry( QString::fromLatin1("UnmountIcon") );
02553 
02554   device->setEditText( deviceStr );
02555   if ( !deviceStr.isEmpty() ) {
02556     // Set default options for this device (first matching entry)
02557     int index = 0;
02558     for ( QStringList::Iterator it = m_devicelist.begin();
02559           it != m_devicelist.end(); ++it, ++index ) {
02560       // WARNING : this works only if indexDevice == 0
02561       if ( (*it).left( deviceStr.length() ) == deviceStr ) {
02562         //kdDebug(250) << "found it " << index << endl;
02563         slotActivated( index );
02564         break;
02565       }
02566     }
02567   }
02568 
02569   if ( !mountPointStr.isEmpty() )
02570     mountpoint->setText( mountPointStr );
02571 
02572   readonly->setChecked( ro );
02573 
02574   if ( unmountedStr.isEmpty() )
02575     unmountedStr = KMimeType::mimeType(QString::fromLatin1("application/octet-stream"))->KServiceType::icon(); // default icon
02576 
02577   unmounted->setIcon( unmountedStr );
02578 
02579   connect( device, SIGNAL( activated( int ) ),
02580            this, SIGNAL( changed() ) );
02581   connect( device, SIGNAL( textChanged( const QString & ) ),
02582            this, SIGNAL( changed() ) );
02583   connect( readonly, SIGNAL( toggled( bool ) ),
02584            this, SIGNAL( changed() ) );
02585   connect( unmounted, SIGNAL( iconChanged( QString ) ),
02586            this, SIGNAL( changed() ) );
02587 }
02588 
02589 KDevicePropsPlugin::~KDevicePropsPlugin()
02590 {
02591   delete d;
02592 }
02593 
02594 // QString KDevicePropsPlugin::tabName () const
02595 // {
02596 //   return i18n ("De&vice");
02597 // }
02598 
02599 void KDevicePropsPlugin::slotActivated( int index )
02600 {
02601   QStringList lst = QStringList::split( ' ', m_devicelist[index] );
02602   device->setEditText( lst[indexDevice] );
02603   mountpoint->setText( lst[indexMountPoint] );
02604 }
02605 
02606 bool KDevicePropsPlugin::supports( KFileItemList _items )
02607 {
02608   if ( _items.count() != 1 )
02609     return false;
02610   KFileItem * item = _items.first();
02611   // check if desktop file
02612   if ( !KPropsDlgPlugin::isDesktopFile( item ) )
02613     return false;
02614   // open file and check type
02615   KDesktopFile config( item->url().path(), true /* readonly */ );
02616   return config.hasDeviceType();
02617 }
02618 
02619 void KDevicePropsPlugin::applyChanges()
02620 {
02621   QString path = properties->kurl().path();
02622   QFile f( path );
02623   if ( !f.open( IO_ReadWrite ) )
02624   {
02625     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient access to write to <b>%1</b>.</qt>").arg(path));
02626     return;
02627   }
02628   f.close();
02629 
02630   KSimpleConfig config( path );
02631   config.setDesktopGroup();
02632   config.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("FSDevice") );
02633 
02634   config.writeEntry( QString::fromLatin1("Dev"), device->currentText() );
02635   config.writeEntry( QString::fromLatin1("MountPoint"), mountpoint->text() );
02636 
02637   config.writeEntry( QString::fromLatin1("UnmountIcon"), unmounted->icon() );
02638   kdDebug(250) << "unmounted->icon() = " << unmounted->icon() << endl;
02639 
02640   config.writeEntry( QString::fromLatin1("ReadOnly"), readonly->isChecked() );
02641 
02642   config.sync();
02643 }
02644 
02645 void KPropertiesDialog::virtual_hook( int id, void* data )
02646 { KDialogBase::virtual_hook( id, data ); }
02647 
02648 void KPropsDlgPlugin::virtual_hook( int, void* )
02649 { /*BASE::virtual_hook( id, data );*/ }
02650 
02651 #include "kpropertiesdialog.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Oct 8 12:21:31 2003 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2001