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