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    Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
00008 
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Library General Public
00011    License as published by the Free Software Foundation; either
00012    version 2 of the License, or (at your option) any later version.
00013 
00014    This library is distributed in the hope that it will be useful,
00015    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017    Library General Public License for more details.
00018 
00019    You should have received a copy of the GNU Library General Public License
00020    along with this library; see the file COPYING.LIB.  If not, write to
00021    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00022    Boston, MA 02111-1307, USA.
00023 */
00024 
00025 /*
00026  * kpropertiesdialog.cpp
00027  * View/Edit Properties of files, locally or remotely
00028  *
00029  * some FilePermissionsPropsPlugin-changes by
00030  *  Henner Zeller <zeller@think.de>
00031  * some layout management by
00032  *  Bertrand Leconte <B.Leconte@mail.dotcom.fr>
00033  * the rest of the layout management, bug fixes, adaptation to libkio,
00034  * template feature by
00035  *  David Faure <faure@kde.org>
00036  * More layout, cleanups, and fixes by
00037  *  Preston Brown <pbrown@kde.org>
00038  * Plugin capability, cleanups and port to KDialogBase by
00039  *  Simon Hausmann <hausmann@kde.org>
00040  * KDesktopPropsPlugin by
00041  *  Waldo Bastian <bastian@kde.org>
00042  */
00043 
00044 #include <config.h>
00045 extern "C" {
00046 #include <pwd.h>
00047 #include <grp.h>
00048 #include <time.h>
00049 }
00050 #include <unistd.h>
00051 #include <errno.h>
00052 #include <assert.h>
00053 
00054 #include <qfile.h>
00055 #include <qdir.h>
00056 #include <qlabel.h>
00057 #include <qpushbutton.h>
00058 #include <qcheckbox.h>
00059 #include <qstrlist.h>
00060 #include <qstringlist.h>
00061 #include <qtextstream.h>
00062 #include <qpainter.h>
00063 #include <qlayout.h>
00064 #include <qcombobox.h>
00065 #include <qgroupbox.h>
00066 #include <qwhatsthis.h>
00067 #include <qtooltip.h>
00068 #include <qstyle.h>
00069 
00070 #include <kapplication.h>
00071 #include <kdialog.h>
00072 #include <kdirsize.h>
00073 #include <kdirwatch.h>
00074 #include <kdirnotify_stub.h>
00075 #include <kdiskfreesp.h>
00076 #include <kdebug.h>
00077 #include <kdesktopfile.h>
00078 #include <kicondialog.h>
00079 #include <kurl.h>
00080 #include <kurlrequester.h>
00081 #include <klocale.h>
00082 #include <kglobal.h>
00083 #include <kglobalsettings.h>
00084 #include <kstandarddirs.h>
00085 #include <kio/job.h>
00086 #include <kio/chmodjob.h>
00087 #include <kio/renamedlg.h>
00088 #include <kio/netaccess.h>
00089 #include <kfiledialog.h>
00090 #include <kmimetype.h>
00091 #include <kmountpoint.h>
00092 #include <kiconloader.h>
00093 #include <kmessagebox.h>
00094 #include <kservice.h>
00095 #include <kcompletion.h>
00096 #include <klineedit.h>
00097 #include <kseparator.h>
00098 #include <ksqueezedtextlabel.h>
00099 #include <klibloader.h>
00100 #include <ktrader.h>
00101 #include <kparts/componentfactory.h>
00102 #include <kmetaprops.h>
00103 #include <kprocess.h>
00104 #include <krun.h>
00105 #include <klistview.h>
00106 #include "kfilesharedlg.h"
00107 
00108 #include "kpropertiesdesktopbase.h"
00109 #include "kpropertiesdesktopadvbase.h"
00110 #include "kpropertiesmimetypebase.h"
00111 
00112 #include "kpropertiesdialog.h"
00113 
00114 static QString nameFromFileName(QString nameStr)
00115 {
00116    if ( nameStr.endsWith(".desktop") )
00117       nameStr.truncate( nameStr.length() - 8 );
00118    if ( nameStr.endsWith(".kdelnk") )
00119       nameStr.truncate( nameStr.length() - 7 );
00120    // Make it human-readable (%2F => '/', ...)
00121    nameStr = KIO::decodeFileName( nameStr );
00122    return nameStr;
00123 }
00124 
00125 mode_t KFilePermissionsPropsPlugin::fperm[3][4] = {
00126         {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
00127         {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
00128         {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
00129     };
00130 
00131 class KPropertiesDialog::KPropertiesDialogPrivate
00132 {
00133 public:
00134   KPropertiesDialogPrivate()
00135   {
00136     m_aborted = false;
00137   }
00138   ~KPropertiesDialogPrivate()
00139   {
00140   }
00141   bool m_aborted:1;
00142 };
00143 
00144 KPropertiesDialog::KPropertiesDialog (KFileItem* item,
00145                                       QWidget* parent, const char* name,
00146                                       bool modal, bool autoShow)
00147   : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(KIO::decodeFileName(item->url().fileName())),
00148                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00149                  parent, name, modal)
00150 {
00151   d = new KPropertiesDialogPrivate;
00152   assert( item );
00153   m_items.append( new KFileItem(*item) ); // deep copy
00154 
00155   m_singleUrl = item->url();
00156   assert(!m_singleUrl.isEmpty());
00157 
00158   init (modal, autoShow);
00159 }
00160 
00161 KPropertiesDialog::KPropertiesDialog (const QString& title,
00162                                       QWidget* parent, const char* name, bool modal)
00163   : KDialogBase (KDialogBase::Tabbed, i18n ("Properties for %1").arg(title),
00164                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00165                  parent, name, modal)
00166 {
00167   d = new KPropertiesDialogPrivate;
00168 
00169   init (modal, false);
00170 }
00171 
00172 KPropertiesDialog::KPropertiesDialog (KFileItemList _items,
00173                                       QWidget* parent, const char* name,
00174                                       bool modal, bool autoShow)
00175   : KDialogBase (KDialogBase::Tabbed,
00176          i18n( "Properties for %1" ).arg(KIO::decodeFileName(_items.first()->url().fileName())),
00177                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00178                  parent, name, modal)
00179 {
00180   d = new KPropertiesDialogPrivate;
00181 
00182   assert( !_items.isEmpty() );
00183   m_singleUrl = _items.first()->url();
00184   assert(!m_singleUrl.isEmpty());
00185 
00186   KFileItemListIterator it ( _items );
00187   // Deep copy
00188   for ( ; it.current(); ++it )
00189       m_items.append( new KFileItem( **it ) );
00190 
00191   init (modal, autoShow);
00192 }
00193 
00194 #ifndef KDE_NO_COMPAT
00195 KPropertiesDialog::KPropertiesDialog (const KURL& _url, mode_t /* _mode is now unused */,
00196                                       QWidget* parent, const char* name,
00197                                       bool modal, bool autoShow)
00198   : KDialogBase (KDialogBase::Tabbed,
00199          i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())),
00200                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00201                  parent, name, modal),
00202   m_singleUrl( _url )
00203 {
00204   d = new KPropertiesDialogPrivate;
00205 
00206   KIO::UDSEntry entry;
00207 
00208   KIO::NetAccess::stat(_url, entry, parent);
00209 
00210   m_items.append( new KFileItem( entry, _url ) );
00211   init (modal, autoShow);
00212 }
00213 #endif
00214 
00215 KPropertiesDialog::KPropertiesDialog (const KURL& _url,
00216                                       QWidget* parent, const char* name,
00217                                       bool modal, bool autoShow)
00218   : KDialogBase (KDialogBase::Tabbed,
00219          i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())),
00220                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00221                  parent, name, modal),
00222   m_singleUrl( _url )
00223 {
00224   d = new KPropertiesDialogPrivate;
00225 
00226   KIO::UDSEntry entry;
00227 
00228   KIO::NetAccess::stat(_url, entry, parent);
00229 
00230   m_items.append( new KFileItem( entry, _url ) );
00231   init (modal, autoShow);
00232 }
00233 
00234 KPropertiesDialog::KPropertiesDialog (const KURL& _tempUrl, const KURL& _currentDir,
00235                                       const QString& _defaultName,
00236                                       QWidget* parent, const char* name,
00237                                       bool modal, bool autoShow)
00238   : KDialogBase (KDialogBase::Tabbed,
00239          i18n( "Properties for %1" ).arg(KIO::decodeFileName(_tempUrl.fileName())),
00240                  KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
00241                  parent, name, modal),
00242 
00243   m_singleUrl( _tempUrl ),
00244   m_defaultName( _defaultName ),
00245   m_currentDir( _currentDir )
00246 {
00247   d = new KPropertiesDialogPrivate;
00248 
00249   assert(!m_singleUrl.isEmpty());
00250 
00251   // Create the KFileItem for the _template_ file, in order to read from it.
00252   m_items.append( new KFileItem( KFileItem::Unknown, KFileItem::Unknown, m_singleUrl ) );
00253   init (modal, autoShow);
00254 }
00255 
00256 void KPropertiesDialog::init (bool modal, bool autoShow)
00257 {
00258   m_pageList.setAutoDelete( true );
00259   m_items.setAutoDelete( true );
00260 
00261   insertPages();
00262 
00263   if (autoShow)
00264     {
00265       if (!modal)
00266         show();
00267       else
00268         exec();
00269     }
00270 }
00271 
00272 void KPropertiesDialog::showFileSharingPage()
00273 {
00274     KPropsDlgPlugin *it;
00275 
00276     for ( it=m_pageList.first(); it != 0L; it=m_pageList.next() )
00277     {
00278         KFileSharePropsPlugin* plugin = dynamic_cast<KFileSharePropsPlugin*>(it);
00279         if ( plugin )
00280         {
00281             showPage( pageIndex( plugin->page() ) );
00282             break;
00283         }
00284     }
00285 }
00286 
00287 void KPropertiesDialog::setFileNameReadOnly( bool ro )
00288 {
00289     KPropsDlgPlugin *it;
00290 
00291     for ( it=m_pageList.first(); it != 0L; it=m_pageList.next() )
00292     {
00293         KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it);
00294         if ( plugin ) {
00295             plugin->setFileNameReadOnly( ro );
00296             break;
00297         }
00298     }
00299 }
00300 
00301 void KPropertiesDialog::slotStatResult( KIO::Job * )
00302 {
00303 }
00304 
00305 KPropertiesDialog::~KPropertiesDialog()
00306 {
00307   m_pageList.clear();
00308   delete d;
00309 }
00310 
00311 void KPropertiesDialog::insertPlugin (KPropsDlgPlugin* plugin)
00312 {
00313   connect (plugin, SIGNAL (changed ()),
00314            plugin, SLOT (setDirty ()));
00315 
00316   m_pageList.append (plugin);
00317 }
00318 
00319 bool KPropertiesDialog::canDisplay( KFileItemList _items )
00320 {
00321   // TODO: cache the result of those calls. Currently we parse .desktop files far too many times
00322   return KFilePropsPlugin::supports( _items ) ||
00323          KFilePermissionsPropsPlugin::supports( _items ) ||
00324          KDesktopPropsPlugin::supports( _items ) ||
00325          KBindingPropsPlugin::supports( _items ) ||
00326          KURLPropsPlugin::supports( _items ) ||
00327          KDevicePropsPlugin::supports( _items ) ||
00328          KFileMetaPropsPlugin::supports( _items );
00329 }
00330 
00331 void KPropertiesDialog::slotOk()
00332 {
00333   KPropsDlgPlugin *page;
00334   d->m_aborted = false;
00335 
00336   KFilePropsPlugin * filePropsPlugin = 0L;
00337   if ( m_pageList.first()->isA("KFilePropsPlugin") )
00338     filePropsPlugin = static_cast<KFilePropsPlugin *>(m_pageList.first());
00339 
00340   // If any page is dirty, then set the main one (KFilePropsPlugin) as
00341   // dirty too. This is what makes it possible to save changes to a global
00342   // desktop file into a local one. In other cases, it doesn't hurt.
00343   for ( page = m_pageList.first(); page != 0L; page = m_pageList.next() )
00344     if ( page->isDirty() && filePropsPlugin )
00345     {
00346         filePropsPlugin->setDirty();
00347         break;
00348     }
00349 
00350   // Apply the changes in the _normal_ order of the tabs now
00351   // This is because in case of renaming a file, KFilePropsPlugin will call
00352   // KPropertiesDialog::rename, so other tab will be ok with whatever order
00353   // BUT for file copied from templates, we need to do the renaming first !
00354   for ( page = m_pageList.first(); page != 0L && !d->m_aborted; page = m_pageList.next() )
00355     if ( page->isDirty() )
00356     {
00357       kdDebug( 250 ) << "applying changes for " << page->className() << endl;
00358       page->applyChanges();
00359       // applyChanges may change d->m_aborted.
00360     }
00361     else
00362       kdDebug( 250 ) << "skipping page " << page->className() << endl;
00363 
00364   if ( !d->m_aborted && filePropsPlugin )
00365     filePropsPlugin->postApplyChanges();
00366 
00367   if ( !d->m_aborted )
00368   {
00369     emit applied();
00370     emit propertiesClosed();
00371     deleteLater();
00372     accept();
00373   } // else, keep dialog open for user to fix the problem.
00374 }
00375 
00376 void KPropertiesDialog::slotCancel()
00377 {
00378   emit canceled();
00379   emit propertiesClosed();
00380 
00381   deleteLater();
00382   done( Rejected );
00383 }
00384 
00385 void KPropertiesDialog::insertPages()
00386 {
00387   if (m_items.isEmpty())
00388     return;
00389 
00390   if ( KFilePropsPlugin::supports( m_items ) )
00391   {
00392     KPropsDlgPlugin *p = new KFilePropsPlugin( this );
00393     insertPlugin (p);
00394   }
00395 
00396   if ( KFilePermissionsPropsPlugin::supports( m_items ) )
00397   {
00398     KPropsDlgPlugin *p = new KFilePermissionsPropsPlugin( this );
00399     insertPlugin (p);
00400   }
00401 
00402   if ( KDesktopPropsPlugin::supports( m_items ) )
00403   {
00404     KPropsDlgPlugin *p = new KDesktopPropsPlugin( 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 
00433   if ( KFileSharePropsPlugin::supports( m_items ) )
00434   {
00435 
00436       QString path = m_items.first()->url().path(-1);
00437       bool isLocal = m_items.first()->url().isLocalFile();
00438       bool isIntoTrash = isLocal && path.startsWith(KGlobalSettings::trashPath());
00439       if ( !isIntoTrash )
00440       {
00441           KPropsDlgPlugin *p = new KFileSharePropsPlugin( this );
00442           insertPlugin (p);
00443       }
00444   }
00445 
00446   //plugins
00447 
00448   if ( m_items.count() != 1 )
00449     return;
00450 
00451   KFileItem *item = m_items.first();
00452   QString mimetype = item->mimetype();
00453 
00454   if ( mimetype.isEmpty() )
00455     return;
00456 
00457   QString query = QString::fromLatin1(
00458       "('KPropsDlg/Plugin' in ServiceTypes) and "
00459       "((not exist [X-KDE-Protocol]) or "
00460       " ([X-KDE-Protocol] == '%1'  )   )"          ).arg(item->url().protocol());
00461 
00462   kdDebug( 250 ) << "trader query: " << query << endl;
00463   KTrader::OfferList offers = KTrader::self()->query( mimetype, query );
00464   KTrader::OfferList::ConstIterator it = offers.begin();
00465   KTrader::OfferList::ConstIterator end = offers.end();
00466   for (; it != end; ++it )
00467   {
00468     KPropsDlgPlugin *plugin = KParts::ComponentFactory
00469         ::createInstanceFromLibrary<KPropsDlgPlugin>( (*it)->library().local8Bit().data(),
00470                                                       this,
00471                                                       (*it)->name().latin1() );
00472     if ( !plugin )
00473         continue;
00474 
00475     insertPlugin( plugin );
00476   }
00477 }
00478 
00479 void KPropertiesDialog::updateUrl( const KURL& _newUrl )
00480 {
00481   Q_ASSERT( m_items.count() == 1 );
00482   kdDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url() << endl;
00483   KURL newUrl = _newUrl;
00484   emit saveAs(m_singleUrl, newUrl);
00485   kdDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url() << endl;
00486 
00487   m_singleUrl = newUrl;
00488   m_items.first()->setURL( newUrl );
00489   assert(!m_singleUrl.isEmpty());
00490   // If we have an Desktop page, set it dirty, so that a full file is saved locally
00491   // Same for a URL page (because of the Name= hack)
00492   for ( QPtrListIterator<KPropsDlgPlugin> it(m_pageList); it.current(); ++it )
00493    if ( it.current()->isA("KExecPropsPlugin") || // KDE4 remove me
00494         it.current()->isA("KURLPropsPlugin") ||
00495         it.current()->isA("KDesktopPropsPlugin"))
00496    {
00497      //kdDebug(250) << "Setting page dirty" << endl;
00498      it.current()->setDirty();
00499      break;
00500    }
00501 }
00502 
00503 void KPropertiesDialog::rename( const QString& _name )
00504 {
00505   Q_ASSERT( m_items.count() == 1 );
00506   kdDebug(250) << "KPropertiesDialog::rename " << _name << endl;
00507   KURL newUrl;
00508   // if we're creating from a template : use currentdir
00509   if ( !m_currentDir.isEmpty() )
00510   {
00511     newUrl = m_currentDir;
00512     newUrl.addPath( _name );
00513   }
00514   else
00515   {
00516     QString tmpurl = m_singleUrl.url();
00517     if ( tmpurl.at(tmpurl.length() - 1) == '/')
00518       // It's a directory, so strip the trailing slash first
00519       tmpurl.truncate( tmpurl.length() - 1);
00520     newUrl = tmpurl;
00521     newUrl.setFileName( _name );
00522   }
00523   updateUrl( newUrl );
00524 }
00525 
00526 void KPropertiesDialog::abortApplying()
00527 {
00528   d->m_aborted = true;
00529 }
00530 
00531 class KPropsDlgPlugin::KPropsDlgPluginPrivate
00532 {
00533 public:
00534   KPropsDlgPluginPrivate()
00535   {
00536   }
00537   ~KPropsDlgPluginPrivate()
00538   {
00539   }
00540 
00541   bool m_bDirty;
00542 };
00543 
00544 KPropsDlgPlugin::KPropsDlgPlugin( KPropertiesDialog *_props )
00545 : QObject( _props, 0L )
00546 {
00547   d = new KPropsDlgPluginPrivate;
00548   properties = _props;
00549   fontHeight = 2*properties->fontMetrics().height();
00550   d->m_bDirty = false;
00551 }
00552 
00553 KPropsDlgPlugin::~KPropsDlgPlugin()
00554 {
00555   delete d;
00556 }
00557 
00558 bool KPropsDlgPlugin::isDesktopFile( KFileItem * _item )
00559 {
00560   // only local files
00561   if ( !_item->isLocalFile() )
00562     return false;
00563 
00564   // only regular files
00565   if ( !S_ISREG( _item->mode() ) )
00566     return false;
00567 
00568   QString t( _item->url().path() );
00569 
00570   // only if readable
00571   FILE *f = fopen( QFile::encodeName(t), "r" );
00572   if ( f == 0L )
00573     return false;
00574   fclose(f);
00575 
00576   // return true if desktop file
00577   return ( _item->mimetype() == "application/x-desktop" );
00578 }
00579 
00580 void KPropsDlgPlugin::setDirty( bool b )
00581 {
00582   d->m_bDirty = b;
00583 }
00584 
00585 void KPropsDlgPlugin::setDirty()
00586 {
00587   d->m_bDirty = true;
00588 }
00589 
00590 bool KPropsDlgPlugin::isDirty() const
00591 {
00592   return d->m_bDirty;
00593 }
00594 
00595 void KPropsDlgPlugin::applyChanges()
00596 {
00597   kdWarning(250) << "applyChanges() not implemented in page !" << endl;
00598 }
00599 
00601 
00602 class KFilePropsPlugin::KFilePropsPluginPrivate
00603 {
00604 public:
00605   KFilePropsPluginPrivate()
00606   {
00607     dirSizeJob = 0L;
00608     dirSizeUpdateTimer = 0L;
00609     m_lined = 0;
00610   }
00611   ~KFilePropsPluginPrivate()
00612   {
00613     if ( dirSizeJob )
00614       dirSizeJob->kill();
00615   }
00616 
00617   KDirSize * dirSizeJob;
00618   QTimer *dirSizeUpdateTimer;
00619   QFrame *m_frame;
00620   bool bMultiple;
00621   bool bIconChanged;
00622   bool bKDesktopMode;
00623   bool bDesktopFile;
00624   QLabel *m_freeSpaceLabel;
00625   QString mimeType;
00626   QString oldFileName;
00627   KLineEdit* m_lined;
00628 };
00629 
00630 KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props )
00631   : KPropsDlgPlugin( _props )
00632 {
00633   d = new KFilePropsPluginPrivate;
00634   d->bMultiple = (properties->items().count() > 1);
00635   d->bIconChanged = false;
00636   d->bKDesktopMode = (QCString(qApp->name()) == "kdesktop"); // nasty heh?
00637   d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items());
00638   kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple << endl;
00639 
00640   // We set this data from the first item, and we'll
00641   // check that the other items match against it, resetting when not.
00642   bool isLocal = properties->kurl().isLocalFile();
00643   KFileItem * item = properties->item();
00644   bool bDesktopFile = isDesktopFile(item);
00645   mode_t mode = item->mode();
00646   bool hasDirs = item->isDir() && !item->isLink();
00647   bool hasRoot = isLocal && properties->kurl().path() == QString::fromLatin1("/");
00648   QString iconStr = KMimeType::iconForURL(properties->kurl(), mode);
00649   QString directory = properties->kurl().directory();
00650   QString protocol = properties->kurl().protocol();
00651   QString mimeComment = item->mimeComment();
00652   d->mimeType = item->mimetype();
00653   KIO::filesize_t totalSize = item->size();
00654   QString magicMimeComment;
00655   if ( isLocal ) {
00656       KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( properties->kurl().path() );
00657       if ( magicMimeType->name() != KMimeType::defaultMimeType() )
00658           magicMimeComment = magicMimeType->comment();
00659   }
00660 
00661   // Those things only apply to 'single file' mode
00662   QString filename = QString::null;
00663   bool isTrash = false;
00664   bool isIntoTrash = false;
00665   bool isDevice = false;
00666   m_bFromTemplate = false;
00667 
00668   // And those only to 'multiple' mode
00669   uint iDirCount = S_ISDIR(mode) ? 1 : 0;
00670   uint iFileCount = 1-iDirCount;
00671 
00672   d->m_frame = properties->addPage (i18n("&General"));
00673 
00674   QVBoxLayout *vbl = new QVBoxLayout( d->m_frame, 0,
00675                                       KDialog::spacingHint(), "vbl");
00676   QGridLayout *grid = new QGridLayout(0, 3); // unknown rows
00677   grid->setColStretch(0, 0);
00678   grid->setColStretch(1, 0);
00679   grid->setColStretch(2, 1);
00680   grid->addColSpacing(1, KDialog::spacingHint());
00681   vbl->addLayout(grid);
00682   int curRow = 0;
00683 
00684   if ( !d->bMultiple )
00685   {
00686     // Extract the file name only
00687     filename = properties->defaultName();
00688     if ( filename.isEmpty() ) // no template
00689       filename = properties->kurl().fileName();
00690     else
00691     {
00692       m_bFromTemplate = true;
00693       setDirty(); // to enforce that the copy happens
00694     }
00695     d->oldFileName = filename;
00696 
00697     // Make it human-readable
00698     filename = nameFromFileName( filename );
00699 
00700     if ( d->bKDesktopMode && d->bDesktopFile ) {
00701         KDesktopFile config( properties->kurl().path(), true /* readonly */ );
00702         if ( config.hasKey( "Name" ) ) {
00703             filename = config.readName();
00704         }
00705     }
00706 
00707     oldName = filename;
00708 
00709     QString path;
00710 
00711     if ( !m_bFromTemplate ) {
00712       QString tmp = properties->kurl().path( 1 );
00713       // is it the trash bin ?
00714       if ( isLocal )
00715       {
00716           if ( tmp == KGlobalSettings::trashPath())
00717               isTrash = true;
00718           if ( tmp.startsWith(KGlobalSettings::trashPath()))
00719               isIntoTrash = true;
00720       }
00721       if ( properties->kurl().protocol().find("device", 0, false)==0)
00722             isDevice = true;
00723       // Extract the full name, but without file: for local files
00724       if ( isLocal )
00725         path = properties->kurl().path();
00726       else
00727         path = properties->kurl().prettyURL();
00728     } else {
00729       path = properties->currentDir().path(1) + properties->defaultName();
00730       directory = properties->currentDir().prettyURL();
00731     }
00732 
00733     if (KExecPropsPlugin::supports(properties->items()) || // KDE4 remove me
00734         d->bDesktopFile ||
00735         KBindingPropsPlugin::supports(properties->items())) {
00736 
00737       determineRelativePath( path );
00738 
00739     }
00740 
00741   }
00742   else
00743   {
00744     // Multiple items: see what they have in common
00745     KFileItemList items = properties->items();
00746     KFileItemListIterator it( items );
00747     for ( ++it /*no need to check the first one again*/ ; it.current(); ++it )
00748     {
00749       KURL url = (*it)->url();
00750       kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyURL() << endl;
00751       // The list of things we check here should match the variables defined
00752       // at the beginning of this method.
00753       if ( url.isLocalFile() != isLocal )
00754         isLocal = false; // not all local
00755       if ( bDesktopFile && isDesktopFile(*it) != bDesktopFile )
00756         bDesktopFile = false; // not all desktop files
00757       if ( (*it)->mode() != mode )
00758         mode = (mode_t)0;
00759       if ( KMimeType::iconForURL(url, mode) != iconStr )
00760         iconStr = "kmultiple";
00761       if ( url.directory() != directory )
00762         directory = QString::null;
00763       if ( url.protocol() != protocol )
00764         protocol = QString::null;
00765       if ( !mimeComment.isNull() && (*it)->mimeComment() != mimeComment )
00766         mimeComment = QString::null;
00767       if ( isLocal && !magicMimeComment.isNull() ) {
00768           KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
00769           if ( magicMimeType->comment() != magicMimeComment )
00770               magicMimeComment = QString::null;
00771       }
00772 
00773       if ( isLocal && url.path() == QString::fromLatin1("/") )
00774         hasRoot = true;
00775       if ( (*it)->isDir() && !(*it)->isLink() )
00776       {
00777         iDirCount++;
00778         hasDirs = true;
00779       }
00780       else
00781       {
00782         iFileCount++;
00783         totalSize += (*it)->size();
00784       }
00785     }
00786   }
00787 
00788   if (!isLocal && !protocol.isEmpty())
00789   {
00790     directory += ' ';
00791     directory += '(';
00792     directory += protocol;
00793     directory += ')';
00794   }
00795 
00796   if ( !isDevice && !isIntoTrash && (bDesktopFile || S_ISDIR(mode)) && !d->bMultiple /*not implemented for multiple*/ )
00797   {
00798     KIconButton *iconButton = new KIconButton( d->m_frame );
00799     int bsize = 66 + 2 * iconButton->style().pixelMetric(QStyle::PM_ButtonMargin);
00800     iconButton->setFixedSize(bsize, bsize);
00801     iconButton->setStrictIconSize(false);
00802     // This works for everything except Device icons on unmounted devices
00803     // So we have to really open .desktop files
00804     QString iconStr = KMimeType::findByURL( properties->kurl(),
00805                                             mode )->icon( properties->kurl(),
00806                                                           isLocal );
00807     if ( bDesktopFile && isLocal )
00808     {
00809       KDesktopFile config( properties->kurl().path(), true );
00810       config.setDesktopGroup();
00811       iconStr = config.readEntry( "Icon" );
00812       if ( config.hasDeviceType() )
00813     iconButton->setIconType( KIcon::Desktop, KIcon::Device );
00814       else
00815     iconButton->setIconType( KIcon::Desktop, KIcon::Application );
00816     } else
00817       iconButton->setIconType( KIcon::Desktop, KIcon::FileSystem );
00818     iconButton->setIcon(iconStr);
00819     iconArea = iconButton;
00820     connect( iconButton, SIGNAL( iconChanged(QString) ),
00821              this, SLOT( slotIconChanged() ) );
00822   } else {
00823     QLabel *iconLabel = new QLabel( d->m_frame );
00824     int bsize = 66 + 2 * iconLabel->style().pixelMetric(QStyle::PM_ButtonMargin);
00825     iconLabel->setFixedSize(bsize, bsize);
00826     iconLabel->setPixmap( DesktopIcon( iconStr ) );
00827     iconArea = iconLabel;
00828   }
00829   grid->addWidget(iconArea, curRow, 0, AlignLeft);
00830 
00831   if (d->bMultiple || isTrash || isIntoTrash || isDevice || filename == QString::fromLatin1("/"))
00832   {
00833     QLabel *lab = new QLabel(d->m_frame );
00834     if ( d->bMultiple )
00835       lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) );
00836     else
00837       lab->setText( filename );
00838     nameArea = lab;
00839   } else
00840   {
00841     d->m_lined = new KLineEdit( d->m_frame );
00842     d->m_lined->setText(filename);
00843     nameArea = d->m_lined;
00844     d->m_lined->setFocus();
00845     connect( d->m_lined, SIGNAL( textChanged( const QString & ) ),
00846              this, SLOT( nameFileChanged(const QString & ) ) );
00847   }
00848 
00849   grid->addWidget(nameArea, curRow++, 2);
00850 
00851   KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame);
00852   grid->addMultiCellWidget(sep, curRow, curRow, 0, 2);
00853   ++curRow;
00854 
00855   QLabel *l;
00856   if ( !mimeComment.isEmpty() && !isDevice && !isIntoTrash)
00857   {
00858     l = new QLabel(i18n("Type:"), d->m_frame );
00859 
00860     grid->addWidget(l, curRow, 0);
00861 
00862     QHBox *box = new QHBox(d->m_frame);
00863     l = new QLabel(mimeComment, box );
00864 
00865     QPushButton *button = new QPushButton(box);
00866 
00867     QIconSet iconSet = SmallIconSet(QString::fromLatin1("configure"));
00868     QPixmap pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal );
00869     button->setIconSet( iconSet );
00870     button->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
00871     QToolTip::add(button, i18n("Edit file type"));
00872 
00873     connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() ));
00874 
00875 
00876     grid->addWidget(box, curRow++, 2);
00877   }
00878 
00879   if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
00880   {
00881     l = new QLabel(i18n("Contents:"), d->m_frame );
00882     grid->addWidget(l, curRow, 0);
00883 
00884     l = new QLabel(magicMimeComment, d->m_frame );
00885     grid->addWidget(l, curRow++, 2);
00886   }
00887 
00888   if ( !directory.isEmpty() )
00889   {
00890     l = new QLabel( i18n("Location:"), d->m_frame );
00891     grid->addWidget(l, curRow, 0);
00892 
00893     l = new KSqueezedTextLabel( d->m_frame );
00894     l->setText( directory );
00895     grid->addWidget(l, curRow++, 2);
00896   }
00897 
00898   l = new QLabel(i18n("Size:"), d->m_frame );
00899   grid->addWidget(l, curRow, 0);
00900 
00901   m_sizeLabel = new QLabel( d->m_frame );
00902   grid->addWidget( m_sizeLabel, curRow++, 2 );
00903 
00904   if ( !hasDirs ) // Only files [and symlinks]
00905   {
00906     m_sizeLabel->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize))
00907              .arg(KGlobal::locale()->formatNumber(totalSize, 0)));
00908     m_sizeDetermineButton = 0L;
00909     m_sizeStopButton = 0L;
00910   }
00911   else // Directory
00912   {
00913     QHBoxLayout * sizelay = new QHBoxLayout(KDialog::spacingHint());
00914     grid->addLayout( sizelay, curRow++, 2 );
00915 
00916     // buttons
00917     m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
00918     m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
00919     connect( m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) );
00920     connect( m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) );
00921     sizelay->addWidget(m_sizeDetermineButton, 0);
00922     sizelay->addWidget(m_sizeStopButton, 0);
00923     sizelay->addStretch(10); // so that the buttons don't grow horizontally
00924 
00925     // auto-launch for local dirs only, and not for '/'
00926     if ( isLocal && !hasRoot )
00927     {
00928       m_sizeDetermineButton->setText( i18n("Refresh") );
00929       slotSizeDetermine();
00930     }
00931     else
00932       m_sizeStopButton->setEnabled( false );
00933   }
00934 
00935   if ( isLocal )
00936   {
00937       QString mountPoint = KIO::findPathMountPoint( properties->item()->url().path() );
00938 
00939       if (mountPoint != "/")
00940       {
00941           l = new QLabel(i18n("Mounted on:"), d->m_frame );
00942           grid->addWidget(l, curRow, 0);
00943 
00944           l = new KSqueezedTextLabel( mountPoint, d->m_frame );
00945           grid->addWidget( l, curRow++, 2 );
00946       }
00947 
00948       l = new QLabel(i18n("Free disk space:"), d->m_frame );
00949       grid->addWidget(l, curRow, 0);
00950 
00951       d->m_freeSpaceLabel = new QLabel( d->m_frame );
00952       grid->addWidget( d->m_freeSpaceLabel, curRow++, 2 );
00953 
00954       KDiskFreeSp * job = new KDiskFreeSp;
00955       connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&,
00956                          const unsigned long&, const QString& ) ),
00957                this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&,
00958                         const unsigned long&, const QString& ) ) );
00959       job->readDF( mountPoint );
00960   }
00961 
00962   if (!d->bMultiple && item->isLink()) {
00963     l = new QLabel(i18n("Points to:"), d->m_frame );
00964     grid->addWidget(l, curRow, 0);
00965 
00966     l = new QLabel(item->linkDest(), d->m_frame );
00967     grid->addWidget(l, curRow++, 2);
00968   }
00969 
00970   if (!d->bMultiple) // Dates for multiple don't make much sense...
00971   {
00972     sep = new KSeparator( KSeparator::HLine, d->m_frame);
00973     grid->addMultiCellWidget(sep, curRow, curRow, 0, 2);
00974     ++curRow;
00975 
00976     QDateTime dt;
00977     time_t tim = item->time(KIO::UDS_CREATION_TIME);
00978     if ( tim )
00979     {
00980       l = new QLabel(i18n("Created:"), d->m_frame );
00981       grid->addWidget(l, curRow, 0);
00982 
00983       dt.setTime_t( tim );
00984       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
00985       grid->addWidget(l, curRow++, 2);
00986     }
00987 
00988     tim = item->time(KIO::UDS_MODIFICATION_TIME);
00989     if ( tim )
00990     {
00991       l = new QLabel(i18n("Modified:"), d->m_frame );
00992       grid->addWidget(l, curRow, 0);
00993 
00994       dt.setTime_t( tim );
00995       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
00996       grid->addWidget(l, curRow++, 2);
00997     }
00998 
00999     tim = item->time(KIO::UDS_ACCESS_TIME);
01000     if ( tim )
01001     {
01002       l = new QLabel(i18n("Accessed:"), d->m_frame );
01003       grid->addWidget(l, curRow, 0);
01004 
01005       dt.setTime_t( tim );
01006       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
01007       grid->addWidget(l, curRow++, 2);
01008     }
01009   }
01010   vbl->addStretch(1);
01011 }
01012 
01013 // QString KFilePropsPlugin::tabName () const
01014 // {
01015 //   return i18n ("&General");
01016 // }
01017 
01018 void KFilePropsPlugin::setFileNameReadOnly( bool ro )
01019 {
01020   if ( d->m_lined )
01021     d->m_lined->setReadOnly( ro );
01022 }
01023 
01024 void KFilePropsPlugin::slotEditFileType()
01025 {
01026   QString keditfiletype = QString::fromLatin1("keditfiletype");
01027   KRun::runCommand( keditfiletype
01028                     + " --parent " + QString::number( properties->topLevelWidget()->winId())
01029                     + " " + KProcess::quote(d->mimeType),
01030                     keditfiletype, keditfiletype /*unused*/);
01031 }
01032 
01033 void KFilePropsPlugin::slotIconChanged()
01034 {
01035   d->bIconChanged = true;
01036   emit changed();
01037 }
01038 
01039 void KFilePropsPlugin::nameFileChanged(const QString &text )
01040 {
01041   properties->enableButtonOK(!text.isEmpty());
01042   emit changed();
01043 }
01044 
01045 void KFilePropsPlugin::determineRelativePath( const QString & path )
01046 {
01047     // now let's make it relative
01048     QStringList dirs;
01049     if (KBindingPropsPlugin::supports(properties->items()))
01050     {
01051        m_sRelativePath =KGlobal::dirs()->relativeLocation("mime", path);
01052        if (m_sRelativePath.startsWith("/"))
01053           m_sRelativePath = QString::null;
01054     }
01055     else
01056     {
01057        m_sRelativePath =KGlobal::dirs()->relativeLocation("apps", path);
01058        if (m_sRelativePath.startsWith("/"))
01059        {
01060           m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
01061           if (m_sRelativePath.startsWith("/"))
01062              m_sRelativePath = QString::null;
01063           else
01064              m_sRelativePath = path;
01065        }
01066     }
01067     if ( m_sRelativePath.isEmpty() )
01068     {
01069       if (KBindingPropsPlugin::supports(properties->items()))
01070         kdWarning(250) << "Warning : editing a mimetype file out of the mimetype dirs!" << endl;
01071     }
01072 }
01073 
01074 void KFilePropsPlugin::slotFoundMountPoint( const QString&,
01075                         unsigned long kBSize,
01076                         unsigned long /*kBUsed*/,
01077                         unsigned long kBAvail )
01078 {
01079     d->m_freeSpaceLabel->setText(
01080     i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
01081     .arg(KIO::convertSizeFromKB(kBAvail))
01082     .arg(KIO::convertSizeFromKB(kBSize))
01083     .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
01084 }
01085 
01086 // attention: copy&paste below, due to compiler bug
01087 // it doesn't like those unsigned long parameters -- unsigned long& are ok :-/
01088 void KFilePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize,
01089                         const unsigned long& /*kBUsed*/,
01090                         const unsigned long& kBAvail,
01091                         const QString& )
01092 {
01093     d->m_freeSpaceLabel->setText(
01094     i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
01095     .arg(KIO::convertSizeFromKB(kBAvail))
01096     .arg(KIO::convertSizeFromKB(kBSize))
01097     .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
01098 }
01099 
01100 void KFilePropsPlugin::slotDirSizeUpdate()
01101 {
01102     KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
01103     m_sizeLabel->setText( i18n("Calculating... %1 (%2)")
01104               .arg(KIO::convertSize(totalSize))
01105               .arg(KGlobal::locale()->formatNumber(totalSize, 0)) );
01106 }
01107 
01108 void KFilePropsPlugin::slotDirSizeFinished( KIO::Job * job )
01109 {
01110   if (job->error())
01111     m_sizeLabel->setText( job->errorString() );
01112   else
01113   {
01114     KIO::filesize_t totalSize = static_cast<KDirSize*>(job)->totalSize();
01115     m_sizeLabel->setText( QString::fromLatin1("%1 (%2)")
01116               .arg(KIO::convertSize(totalSize))
01117               .arg(KGlobal::locale()->formatNumber(totalSize, 0)) );
01118   }
01119   m_sizeStopButton->setEnabled(false);
01120   // just in case you change something and try again :)
01121   m_sizeDetermineButton->setText( i18n("Refresh") );
01122   m_sizeDetermineButton->setEnabled(true);
01123   d->dirSizeJob = 0L;
01124   delete d->dirSizeUpdateTimer;
01125   d->dirSizeUpdateTimer = 0L;
01126 }
01127 
01128 void KFilePropsPlugin::slotSizeDetermine()
01129 {
01130   m_sizeLabel->setText( i18n("Calculating...") );
01131   kdDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" <<  properties->item() << endl;
01132   kdDebug(250) << " URL=" << properties->item()->url().url() << endl;
01133   d->dirSizeJob = KDirSize::dirSizeJob( properties->items() );
01134   d->dirSizeUpdateTimer = new QTimer(this);
01135   connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ),
01136            SLOT( slotDirSizeUpdate() ) );
01137   d->dirSizeUpdateTimer->start(500);
01138   connect( d->dirSizeJob, SIGNAL( result( KIO::Job * ) ),
01139            SLOT( slotDirSizeFinished( KIO::Job * ) ) );
01140   m_sizeStopButton->setEnabled(true);
01141   m_sizeDetermineButton->setEnabled(false);
01142 }
01143 
01144 void KFilePropsPlugin::slotSizeStop()
01145 {
01146   if ( d->dirSizeJob )
01147   {
01148     m_sizeLabel->setText( i18n("Stopped") );
01149     d->dirSizeJob->kill();
01150     d->dirSizeJob = 0;
01151   }
01152   if ( d->dirSizeUpdateTimer )
01153     d->dirSizeUpdateTimer->stop();
01154 
01155   m_sizeStopButton->setEnabled(false);
01156   m_sizeDetermineButton->setEnabled(true);
01157 }
01158 
01159 KFilePropsPlugin::~KFilePropsPlugin()
01160 {
01161   delete d;
01162 }
01163 
01164 bool KFilePropsPlugin::supports( KFileItemList /*_items*/ )
01165 {
01166   return true;
01167 }
01168 
01169 // Don't do this at home
01170 void qt_enter_modal( QWidget *widget );
01171 void qt_leave_modal( QWidget *widget );
01172 
01173 void KFilePropsPlugin::applyChanges()
01174 {
01175   if ( d->dirSizeJob )
01176     slotSizeStop();
01177 
01178   kdDebug(250) << "KFilePropsPlugin::applyChanges" << endl;
01179 
01180   if (nameArea->inherits("QLineEdit"))
01181   {
01182     QString n = ((QLineEdit *) nameArea)->text();
01183     // Remove trailing spaces (#4345)
01184     while ( n[n.length()-1].isSpace() )
01185       n.truncate( n.length() - 1 );
01186     if ( n.isEmpty() )
01187     {
01188       KMessageBox::sorry( properties, i18n("The new file name is empty!"));
01189       properties->abortApplying();
01190       return;
01191     }
01192 
01193     // Do we need to rename the file ?
01194     kdDebug(250) << "oldname = " << oldName << endl;
01195     kdDebug(250) << "newname = " << n << endl;
01196     if ( oldName != n || m_bFromTemplate ) { // true for any from-template file
01197       KIO::Job * job = 0L;
01198       KURL oldurl = properties->kurl();
01199       
01200       QString newFileName = KIO::encodeFileName(n);
01201       if (d->bDesktopFile && !newFileName.endsWith(".desktop") && !newFileName.endsWith(".kdelnk"))
01202          newFileName += ".desktop";
01203          
01204       // Tell properties. Warning, this changes the result of properties->kurl() !
01205       properties->rename( newFileName );
01206 
01207       // Update also relative path (for apps and mimetypes)
01208       if ( !m_sRelativePath.isEmpty() )
01209         determineRelativePath( properties->kurl().path() );
01210 
01211       kdDebug(250) << "New URL = " << properties->kurl().url() << endl;
01212       kdDebug(250) << "old = " << oldurl.url() << endl;
01213 
01214       // Don't remove the template !!
01215       if ( !m_bFromTemplate ) // (normal renaming)
01216         job = KIO::move( oldurl, properties->kurl() );
01217       else // Copying a template
01218         job = KIO::copy( oldurl, properties->kurl() );
01219 
01220       connect( job, SIGNAL( result( KIO::Job * ) ),
01221                SLOT( slotCopyFinished( KIO::Job * ) ) );
01222       connect( job, SIGNAL( renamed( KIO::Job *, const KURL &, const KURL & ) ),
01223                SLOT( slotFileRenamed( KIO::Job *, const KURL &, const KURL & ) ) );
01224       // wait for job
01225       QWidget dummy(0,0,WType_Dialog|WShowModal);
01226       qt_enter_modal(&dummy);
01227       qApp->enter_loop();
01228       qt_leave_modal(&dummy);
01229       return;
01230     }
01231   }
01232 
01233   // No job, keep going
01234   slotCopyFinished( 0L );
01235 }
01236 
01237 void KFilePropsPlugin::slotCopyFinished( KIO::Job * job )
01238 {
01239   kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl;
01240   if (job)
01241   {
01242     // allow apply() to return
01243     qApp->exit_loop();
01244     if ( job->error() )
01245     {
01246         job->showErrorDialog( d->m_frame );
01247         // Didn't work. Revert the URL to the old one
01248         properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcURLs().first() );
01249         properties->abortApplying(); // Don't apply the changes to the wrong file !
01250         return;
01251     }
01252   }
01253 
01254   assert( properties->item() );
01255   assert( !properties->item()->url().isEmpty() );
01256 
01257   // Save the file where we can -> usually in ~/.kde/...
01258   if (KBindingPropsPlugin::supports(properties->items()) && !m_sRelativePath.isEmpty())
01259   {
01260     KURL newURL;
01261     newURL.setPath( locateLocal("mime", m_sRelativePath) );
01262     properties->updateUrl( newURL );
01263   }
01264   else if (d->bDesktopFile && !m_sRelativePath.isEmpty())
01265   {
01266     kdDebug(250) << "KFilePropsPlugin::slotCopyFinished " << m_sRelativePath << endl;
01267     KURL newURL;
01268     newURL.setPath( KDesktopFile::locateLocal(m_sRelativePath) );
01269     kdDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path() << endl;
01270     properties->updateUrl( newURL );
01271   }
01272 
01273   if ( d->bKDesktopMode && d->bDesktopFile ) {
01274       // Renamed? Update Name field
01275       if ( d->oldFileName != properties->kurl().fileName() || m_bFromTemplate ) {
01276           KDesktopFile config( properties->kurl().path() );
01277           QString nameStr = nameFromFileName(properties->kurl().fileName());
01278           config.writeEntry( "Name", nameStr );
01279           config.writeEntry( "Name", nameStr, true, false, true );
01280       }
01281   }
01282 }
01283 
01284 void KFilePropsPlugin::applyIconChanges()
01285 {
01286   // handle icon changes - only local files for now
01287   // TODO: Use KTempFile and KIO::file_copy with overwrite = true
01288   if (!iconArea->isA(