KIO
kpropertiesdialog.cpp
Go to the documentation of this file.
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 KDialog by 00039 * Simon Hausmann <hausmann@kde.org> 00040 * KDesktopPropsPlugin by 00041 * Waldo Bastian <bastian@kde.org> 00042 */ 00043 00044 #include "kpropertiesdialog.h" 00045 #include "kpropertiesdialog_p.h" 00046 00047 00048 #include <config.h> 00049 #include <config-acl.h> 00050 extern "C" { 00051 #include <pwd.h> 00052 #include <grp.h> 00053 #include <time.h> 00054 #include <sys/stat.h> 00055 #include <sys/types.h> 00056 } 00057 #include <unistd.h> 00058 #include <errno.h> 00059 #include <assert.h> 00060 #include <algorithm> 00061 #include <functional> 00062 00063 #include <QtCore/QFile> 00064 #include <QtCore/QDir> 00065 #include <QtGui/QLabel> 00066 #include <QtGui/QPushButton> 00067 #include <QtGui/QCheckBox> 00068 #include <QtCore/QMutableStringListIterator> 00069 #include <QtCore/QTextIStream> 00070 #include <QtGui/QPainter> 00071 #include <QtGui/QLayout> 00072 #include <QtGui/QStyle> 00073 #include <QtGui/QProgressBar> 00074 #include <QVector> 00075 #include <QFileInfo> 00076 00077 #ifdef HAVE_POSIX_ACL 00078 extern "C" { 00079 # include <sys/xattr.h> 00080 } 00081 #endif 00082 00083 #include <kauthorized.h> 00084 #include <kdialog.h> 00085 #include <kdirwatch.h> 00086 #include <kdirnotify.h> 00087 #include <kdiskfreespaceinfo.h> 00088 #include <kdebug.h> 00089 #include <kdesktopfile.h> 00090 #include <kicondialog.h> 00091 #include <kurl.h> 00092 #include <kurlrequester.h> 00093 #include <klocale.h> 00094 #include <kglobal.h> 00095 #include <kglobalsettings.h> 00096 #include <kstandarddirs.h> 00097 #include <kjobuidelegate.h> 00098 #include <kio/job.h> 00099 #include <kio/copyjob.h> 00100 #include <kio/chmodjob.h> 00101 #include <kio/directorysizejob.h> 00102 #include <kio/renamedialog.h> 00103 #include <kio/netaccess.h> 00104 #include <kio/jobuidelegate.h> 00105 #include <kfiledialog.h> 00106 #include <kmimetype.h> 00107 #include <kmountpoint.h> 00108 #include <kiconloader.h> 00109 #include <kmessagebox.h> 00110 #include <kservice.h> 00111 #include <kcombobox.h> 00112 #include <kcompletion.h> 00113 #include <klineedit.h> 00114 #include <kseparator.h> 00115 #include <ksqueezedtextlabel.h> 00116 #include <kmimetypetrader.h> 00117 #include <kmetaprops.h> 00118 #include <kpreviewprops.h> 00119 #include <krun.h> 00120 #include <kvbox.h> 00121 #include <kacl.h> 00122 #include <kconfiggroup.h> 00123 #include <kshell.h> 00124 #include <kcapacitybar.h> 00125 #include <kfileitemlistproperties.h> 00126 00127 #ifndef Q_OS_WIN 00128 #include "kfilesharedialog.h" 00129 #endif 00130 00131 #include "ui_kpropertiesdesktopbase.h" 00132 #include "ui_kpropertiesdesktopadvbase.h" 00133 #ifdef HAVE_POSIX_ACL 00134 #include "kacleditwidget.h" 00135 #endif 00136 00137 #include <kbuildsycocaprogressdialog.h> 00138 #include <kmimetypechooser.h> 00139 00140 #ifdef Q_WS_WIN 00141 # include <kkernel_win.h> 00142 #ifdef __GNUC__ 00143 # warning TODO: port completely to win32 00144 #endif 00145 #endif 00146 00147 using namespace KDEPrivate; 00148 00149 static QString nameFromFileName(QString nameStr) 00150 { 00151 if ( nameStr.endsWith(QLatin1String(".desktop")) ) 00152 nameStr.truncate( nameStr.length() - 8 ); 00153 if ( nameStr.endsWith(QLatin1String(".kdelnk")) ) 00154 nameStr.truncate( nameStr.length() - 7 ); 00155 // Make it human-readable (%2F => '/', ...) 00156 nameStr = KIO::decodeFileName( nameStr ); 00157 return nameStr; 00158 } 00159 00160 mode_t KFilePermissionsPropsPlugin::fperm[3][4] = { 00161 {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID}, 00162 {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID}, 00163 {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX} 00164 }; 00165 00166 class KPropertiesDialog::KPropertiesDialogPrivate 00167 { 00168 public: 00169 KPropertiesDialogPrivate(KPropertiesDialog *qq) 00170 { 00171 q = qq; 00172 m_aborted = false; 00173 fileSharePage = 0; 00174 } 00175 ~KPropertiesDialogPrivate() 00176 { 00177 } 00178 00182 void init(); 00186 void insertPages(); 00187 00188 KPropertiesDialog *q; 00189 bool m_aborted:1; 00190 QWidget* fileSharePage; 00194 KUrl m_singleUrl; 00198 KFileItemList m_items; 00202 QString m_defaultName; 00203 KUrl m_currentDir; 00207 QList<KPropertiesDialogPlugin*> m_pageList; 00208 }; 00209 00210 KPropertiesDialog::KPropertiesDialog (const KFileItem& item, 00211 QWidget* parent) 00212 : KPageDialog(parent), d(new KPropertiesDialogPrivate(this)) 00213 { 00214 setCaption( i18n( "Properties for %1" , KIO::decodeFileName(item.url().fileName())) ); 00215 00216 assert( !item.isNull() ); 00217 d->m_items.append(item); 00218 00219 d->m_singleUrl = item.url(); 00220 assert(!d->m_singleUrl.isEmpty()); 00221 00222 d->init(); 00223 } 00224 00225 KPropertiesDialog::KPropertiesDialog (const QString& title, 00226 QWidget* parent) 00227 : KPageDialog(parent), d(new KPropertiesDialogPrivate(this)) 00228 { 00229 setCaption( i18n( "Properties for %1", title ) ); 00230 00231 d->init(); 00232 } 00233 00234 KPropertiesDialog::KPropertiesDialog(const KFileItemList& _items, 00235 QWidget* parent) 00236 : KPageDialog(parent), d(new KPropertiesDialogPrivate(this)) 00237 { 00238 if ( _items.count() > 1 ) 00239 setCaption( i18np( "Properties for 1 item", "Properties for %1 Selected Items", _items.count() ) ); 00240 else 00241 setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_items.first().url().fileName())) ); 00242 00243 assert( !_items.isEmpty() ); 00244 d->m_singleUrl = _items.first().url(); 00245 assert(!d->m_singleUrl.isEmpty()); 00246 00247 d->m_items = _items; 00248 00249 d->init(); 00250 } 00251 00252 KPropertiesDialog::KPropertiesDialog (const KUrl& _url, 00253 QWidget* parent) 00254 : KPageDialog(parent), d(new KPropertiesDialogPrivate(this)) 00255 { 00256 setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_url.fileName())) ); 00257 00258 d->m_singleUrl = _url; 00259 00260 KIO::UDSEntry entry; 00261 KIO::NetAccess::stat(_url, entry, parent); 00262 00263 d->m_items.append(KFileItem(entry, _url)); 00264 d->init(); 00265 } 00266 00267 KPropertiesDialog::KPropertiesDialog (const KUrl& _tempUrl, const KUrl& _currentDir, 00268 const QString& _defaultName, 00269 QWidget* parent) 00270 : KPageDialog(parent), d(new KPropertiesDialogPrivate(this)) 00271 { 00272 setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_tempUrl.fileName())) ); 00273 00274 d->m_singleUrl = _tempUrl; 00275 d->m_defaultName = _defaultName; 00276 d->m_currentDir = _currentDir; 00277 assert(!d->m_singleUrl.isEmpty()); 00278 00279 // Create the KFileItem for the _template_ file, in order to read from it. 00280 d->m_items.append(KFileItem(KFileItem::Unknown, KFileItem::Unknown, d->m_singleUrl)); 00281 d->init(); 00282 } 00283 00284 bool KPropertiesDialog::showDialog(const KFileItem& item, QWidget* parent, 00285 bool modal) 00286 { 00287 // TODO: do we really want to show the win32 property dialog? 00288 // This means we lose metainfo, support for .desktop files, etc. (DF) 00289 #ifdef Q_WS_WIN 00290 QString localPath = item.localPath(); 00291 if (!localPath.isEmpty()) 00292 return showWin32FilePropertyDialog(localPath); 00293 #endif 00294 KPropertiesDialog* dlg = new KPropertiesDialog(item, parent); 00295 if (modal) { 00296 dlg->exec(); 00297 } else { 00298 dlg->show(); 00299 } 00300 00301 return true; 00302 } 00303 00304 bool KPropertiesDialog::showDialog(const KUrl& _url, QWidget* parent, 00305 bool modal) 00306 { 00307 #ifdef Q_WS_WIN 00308 if (_url.isLocalFile()) 00309 return showWin32FilePropertyDialog( _url.toLocalFile() ); 00310 #endif 00311 KPropertiesDialog* dlg = new KPropertiesDialog(_url, parent); 00312 if (modal) { 00313 dlg->exec(); 00314 } else { 00315 dlg->show(); 00316 } 00317 00318 return true; 00319 } 00320 00321 bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent, 00322 bool modal) 00323 { 00324 if (_items.count()==1) { 00325 const KFileItem item = _items.first(); 00326 if (item.entry().count() == 0 && item.localPath().isEmpty()) // this remote item wasn't listed by a slave 00327 // Let's stat to get more info on the file 00328 return KPropertiesDialog::showDialog(item.url(), parent, modal); 00329 else 00330 return KPropertiesDialog::showDialog(_items.first(), parent, modal); 00331 } 00332 KPropertiesDialog* dlg = new KPropertiesDialog(_items, parent); 00333 if (modal) { 00334 dlg->exec(); 00335 } else { 00336 dlg->show(); 00337 } 00338 return true; 00339 } 00340 00341 void KPropertiesDialog::KPropertiesDialogPrivate::init() 00342 { 00343 q->setFaceType(KPageDialog::Tabbed); 00344 q->setButtons(KDialog::Ok | KDialog::Cancel); 00345 q->setDefaultButton(KDialog::Ok); 00346 00347 connect(q, SIGNAL(okClicked()), q, SLOT(slotOk())); 00348 connect(q, SIGNAL(cancelClicked()), q, SLOT(slotCancel())); 00349 00350 insertPages(); 00351 00352 KConfigGroup group(KGlobal::config(), "KPropertiesDialog"); 00353 q->restoreDialogSize(group); 00354 } 00355 00356 void KPropertiesDialog::showFileSharingPage() 00357 { 00358 if (d->fileSharePage) { 00359 // FIXME: this showFileSharingPage thingy looks broken! (tokoe) 00360 // showPage( pageIndex( d->fileSharePage)); 00361 } 00362 } 00363 00364 void KPropertiesDialog::setFileSharingPage(QWidget* page) { 00365 d->fileSharePage = page; 00366 } 00367 00368 00369 void KPropertiesDialog::setFileNameReadOnly( bool ro ) 00370 { 00371 foreach(KPropertiesDialogPlugin *it, d->m_pageList) { 00372 KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it); 00373 if ( plugin ) { 00374 plugin->setFileNameReadOnly( ro ); 00375 break; 00376 } 00377 } 00378 } 00379 00380 KPropertiesDialog::~KPropertiesDialog() 00381 { 00382 qDeleteAll(d->m_pageList); 00383 delete d; 00384 00385 KConfigGroup group(KGlobal::config(), "KPropertiesDialog"); 00386 saveDialogSize(group, KConfigBase::Persistent); 00387 } 00388 00389 void KPropertiesDialog::insertPlugin (KPropertiesDialogPlugin* plugin) 00390 { 00391 connect (plugin, SIGNAL (changed ()), 00392 plugin, SLOT (setDirty ())); 00393 00394 d->m_pageList.append(plugin); 00395 } 00396 00397 KUrl KPropertiesDialog::kurl() const 00398 { 00399 return d->m_singleUrl; 00400 } 00401 00402 KFileItem& KPropertiesDialog::item() 00403 { 00404 return d->m_items.first(); 00405 } 00406 00407 KFileItemList KPropertiesDialog::items() const 00408 { 00409 return d->m_items; 00410 } 00411 00412 KUrl KPropertiesDialog::currentDir() const 00413 { 00414 return d->m_currentDir; 00415 } 00416 00417 QString KPropertiesDialog::defaultName() const 00418 { 00419 return d->m_defaultName; 00420 } 00421 00422 bool KPropertiesDialog::canDisplay( const KFileItemList& _items ) 00423 { 00424 // TODO: cache the result of those calls. Currently we parse .desktop files far too many times 00425 return KFilePropsPlugin::supports( _items ) || 00426 KFilePermissionsPropsPlugin::supports( _items ) || 00427 KDesktopPropsPlugin::supports( _items ) || 00428 KUrlPropsPlugin::supports( _items ) || 00429 KDevicePropsPlugin::supports( _items ) || 00430 KFileMetaPropsPlugin::supports( _items ) || 00431 KPreviewPropsPlugin::supports( _items ); 00432 } 00433 00434 void KPropertiesDialog::slotOk() 00435 { 00436 QList<KPropertiesDialogPlugin*>::const_iterator pageListIt; 00437 d->m_aborted = false; 00438 00439 KFilePropsPlugin * filePropsPlugin = qobject_cast<KFilePropsPlugin*>(d->m_pageList.first()); 00440 00441 // If any page is dirty, then set the main one (KFilePropsPlugin) as 00442 // dirty too. This is what makes it possible to save changes to a global 00443 // desktop file into a local one. In other cases, it doesn't hurt. 00444 for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd(); ++pageListIt) { 00445 if ( (*pageListIt)->isDirty() && filePropsPlugin ) 00446 { 00447 filePropsPlugin->setDirty(); 00448 break; 00449 } 00450 } 00451 00452 // Apply the changes in the _normal_ order of the tabs now 00453 // This is because in case of renaming a file, KFilePropsPlugin will call 00454 // KPropertiesDialog::rename, so other tab will be ok with whatever order 00455 // BUT for file copied from templates, we need to do the renaming first ! 00456 for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd() && !d->m_aborted; ++pageListIt) { 00457 if ( (*pageListIt)->isDirty() ) 00458 { 00459 kDebug( 250 ) << "applying changes for " << (*pageListIt)->metaObject()->className(); 00460 (*pageListIt)->applyChanges(); 00461 // applyChanges may change d->m_aborted. 00462 } 00463 else { 00464 kDebug( 250 ) << "skipping page " << (*pageListIt)->metaObject()->className(); 00465 } 00466 } 00467 00468 if ( !d->m_aborted && filePropsPlugin ) 00469 filePropsPlugin->postApplyChanges(); 00470 00471 if ( !d->m_aborted ) 00472 { 00473 emit applied(); 00474 emit propertiesClosed(); 00475 deleteLater(); // somewhat like Qt::WA_DeleteOnClose would do. 00476 accept(); 00477 } // else, keep dialog open for user to fix the problem. 00478 } 00479 00480 void KPropertiesDialog::slotCancel() 00481 { 00482 emit canceled(); 00483 emit propertiesClosed(); 00484 00485 deleteLater(); 00486 done( Rejected ); 00487 } 00488 00489 void KPropertiesDialog::KPropertiesDialogPrivate::insertPages() 00490 { 00491 if (m_items.isEmpty()) 00492 return; 00493 00494 if ( KFilePropsPlugin::supports( m_items ) ) { 00495 KPropertiesDialogPlugin *p = new KFilePropsPlugin(q); 00496 q->insertPlugin(p); 00497 } 00498 00499 if ( KFilePermissionsPropsPlugin::supports( m_items ) ) { 00500 KPropertiesDialogPlugin *p = new KFilePermissionsPropsPlugin(q); 00501 q->insertPlugin(p); 00502 } 00503 00504 if ( KDesktopPropsPlugin::supports( m_items ) ) { 00505 KPropertiesDialogPlugin *p = new KDesktopPropsPlugin(q); 00506 q->insertPlugin(p); 00507 } 00508 00509 if ( KUrlPropsPlugin::supports( m_items ) ) { 00510 KPropertiesDialogPlugin *p = new KUrlPropsPlugin(q); 00511 q->insertPlugin(p); 00512 } 00513 00514 if ( KDevicePropsPlugin::supports( m_items ) ) { 00515 KPropertiesDialogPlugin *p = new KDevicePropsPlugin(q); 00516 q->insertPlugin(p); 00517 } 00518 00519 if ( KFileMetaPropsPlugin::supports( m_items ) ) { 00520 KPropertiesDialogPlugin *p = new KFileMetaPropsPlugin(q); 00521 q->insertPlugin(p); 00522 } 00523 00524 if ( KPreviewPropsPlugin::supports( m_items ) ) { 00525 KPropertiesDialogPlugin *p = new KPreviewPropsPlugin(q); 00526 q->insertPlugin(p); 00527 } 00528 00529 #ifndef Q_OS_WIN 00530 if ( KAuthorized::authorizeKAction("sharefile") && 00531 KFileSharePropsPlugin::supports( m_items ) ) { 00532 KPropertiesDialogPlugin *p = new KFileSharePropsPlugin(q); 00533 q->insertPlugin(p); 00534 } 00535 #endif 00536 00537 //plugins 00538 00539 if ( m_items.count() != 1 ) 00540 return; 00541 00542 const KFileItem item = m_items.first(); 00543 const QString mimetype = item.mimetype(); 00544 00545 if ( mimetype.isEmpty() ) 00546 return; 00547 00548 QString query = QString::fromLatin1( 00549 "((not exist [X-KDE-Protocol]) or " 00550 " ([X-KDE-Protocol] == '%1' ) )" 00551 ).arg(item.url().protocol()); 00552 00553 kDebug( 250 ) << "trader query: " << query; 00554 const KService::List offers = KMimeTypeTrader::self()->query( mimetype, "KPropertiesDialog/Plugin", query ); 00555 foreach (const KService::Ptr &ptr, offers) { 00556 KPropertiesDialogPlugin *plugin = ptr->createInstance<KPropertiesDialogPlugin>(q); 00557 if (!plugin) 00558 continue; 00559 plugin->setObjectName(ptr->name()); 00560 00561 q->insertPlugin(plugin); 00562 } 00563 } 00564 00565 void KPropertiesDialog::updateUrl( const KUrl& _newUrl ) 00566 { 00567 Q_ASSERT(d->m_items.count() == 1); 00568 kDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url(); 00569 KUrl newUrl = _newUrl; 00570 emit saveAs(d->m_singleUrl, newUrl); 00571 kDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url(); 00572 00573 d->m_singleUrl = newUrl; 00574 d->m_items.first().setUrl(newUrl); 00575 assert(!d->m_singleUrl.isEmpty()); 00576 // If we have an Desktop page, set it dirty, so that a full file is saved locally 00577 // Same for a URL page (because of the Name= hack) 00578 foreach (KPropertiesDialogPlugin *it, d->m_pageList) { 00579 if ( qobject_cast<KUrlPropsPlugin*>(it) || 00580 qobject_cast<KDesktopPropsPlugin*>(it) ) 00581 { 00582 //kDebug(250) << "Setting page dirty"; 00583 it->setDirty(); 00584 break; 00585 } 00586 } 00587 } 00588 00589 void KPropertiesDialog::rename( const QString& _name ) 00590 { 00591 Q_ASSERT(d->m_items.count() == 1); 00592 kDebug(250) << "KPropertiesDialog::rename " << _name; 00593 KUrl newUrl; 00594 // if we're creating from a template : use currentdir 00595 if (!d->m_currentDir.isEmpty()) { 00596 newUrl = d->m_currentDir; 00597 newUrl.addPath(_name); 00598 } else { 00599 QString tmpurl = d->m_singleUrl.url(); 00600 if (!tmpurl.isEmpty() && tmpurl.at(tmpurl.length() - 1) == '/') { 00601 // It's a directory, so strip the trailing slash first 00602 tmpurl.truncate(tmpurl.length() - 1); 00603 } 00604 00605 newUrl = tmpurl; 00606 newUrl.setFileName(_name); 00607 } 00608 updateUrl(newUrl); 00609 } 00610 00611 void KPropertiesDialog::abortApplying() 00612 { 00613 d->m_aborted = true; 00614 } 00615 00616 class KPropertiesDialogPlugin::KPropertiesDialogPluginPrivate 00617 { 00618 public: 00619 KPropertiesDialogPluginPrivate() 00620 { 00621 } 00622 ~KPropertiesDialogPluginPrivate() 00623 { 00624 } 00625 00626 bool m_bDirty; 00627 int fontHeight; 00628 }; 00629 00630 KPropertiesDialogPlugin::KPropertiesDialogPlugin( KPropertiesDialog *_props ) 00631 : QObject( _props ),d(new KPropertiesDialogPluginPrivate) 00632 { 00633 properties = _props; 00634 d->fontHeight = 2*properties->fontMetrics().height(); 00635 d->m_bDirty = false; 00636 } 00637 00638 KPropertiesDialogPlugin::~KPropertiesDialogPlugin() 00639 { 00640 delete d; 00641 } 00642 00643 #ifndef KDE_NO_DEPRECATED 00644 bool KPropertiesDialogPlugin::isDesktopFile( const KFileItem& _item ) 00645 { 00646 return _item.isDesktopFile(); 00647 } 00648 #endif 00649 00650 void KPropertiesDialogPlugin::setDirty( bool b ) 00651 { 00652 d->m_bDirty = b; 00653 } 00654 00655 void KPropertiesDialogPlugin::setDirty() 00656 { 00657 d->m_bDirty = true; 00658 } 00659 00660 bool KPropertiesDialogPlugin::isDirty() const 00661 { 00662 return d->m_bDirty; 00663 } 00664 00665 void KPropertiesDialogPlugin::applyChanges() 00666 { 00667 kWarning(250) << "applyChanges() not implemented in page !"; 00668 } 00669 00670 int KPropertiesDialogPlugin::fontHeight() const 00671 { 00672 return d->fontHeight; 00673 } 00674 00676 00677 class KFilePropsPlugin::KFilePropsPluginPrivate 00678 { 00679 public: 00680 KFilePropsPluginPrivate() 00681 { 00682 dirSizeJob = 0L; 00683 dirSizeUpdateTimer = 0L; 00684 m_lined = 0; 00685 m_capacityBar = 0; 00686 m_linkTargetLineEdit = 0; 00687 } 00688 ~KFilePropsPluginPrivate() 00689 { 00690 if ( dirSizeJob ) 00691 dirSizeJob->kill(); 00692 } 00693 00694 KIO::DirectorySizeJob * dirSizeJob; 00695 QTimer *dirSizeUpdateTimer; 00696 QFrame *m_frame; 00697 bool bMultiple; 00698 bool bIconChanged; 00699 bool bKDesktopMode; 00700 bool bDesktopFile; 00701 KCapacityBar *m_capacityBar; 00702 QString mimeType; 00703 QString oldFileName; 00704 KLineEdit* m_lined; 00705 00706 QWidget *iconArea; 00707 QWidget *nameArea; 00708 00709 QLabel *m_sizeLabel; 00710 QPushButton *m_sizeDetermineButton; 00711 QPushButton *m_sizeStopButton; 00712 KLineEdit* m_linkTargetLineEdit; 00713 00714 QString m_sRelativePath; 00715 bool m_bFromTemplate; 00716 00720 QString oldName; 00721 }; 00722 00723 KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props ) 00724 : KPropertiesDialogPlugin( _props ),d(new KFilePropsPluginPrivate) 00725 { 00726 d->bMultiple = (properties->items().count() > 1); 00727 d->bIconChanged = false; 00728 d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items()); 00729 kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple; 00730 00731 // We set this data from the first item, and we'll 00732 // check that the other items match against it, resetting when not. 00733 bool isLocal; 00734 const KFileItem item = properties->item(); 00735 KUrl url = item.mostLocalUrl( isLocal ); 00736 bool isReallyLocal = item.url().isLocalFile(); 00737 bool bDesktopFile = item.isDesktopFile(); 00738 mode_t mode = item.mode(); 00739 bool hasDirs = item.isDir() && !item.isLink(); 00740 bool hasRoot = url.path() == QLatin1String("/"); 00741 QString iconStr = KMimeType::iconNameForUrl(url, mode); 00742 QString directory = properties->kurl().directory(); 00743 QString protocol = properties->kurl().protocol(); 00744 d->bKDesktopMode = protocol == QLatin1String("desktop") || 00745 properties->currentDir().protocol() == QLatin1String("desktop"); 00746 QString mimeComment = item.mimeComment(); 00747 d->mimeType = item.mimetype(); 00748 KIO::filesize_t totalSize = item.size(); 00749 QString magicMimeComment; 00750 if ( isLocal ) { 00751 KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() ); 00752 if ( magicMimeType->name() != KMimeType::defaultMimeType() ) 00753 magicMimeComment = magicMimeType->comment(); 00754 } 00755 #ifdef Q_WS_WIN 00756 if ( isReallyLocal ) { 00757 directory = QDir::toNativeSeparators( directory.mid( 1 ) ); 00758 } 00759 #endif 00760 00761 // Those things only apply to 'single file' mode 00762 QString filename; 00763 bool isTrash = false; 00764 d->m_bFromTemplate = false; 00765 00766 // And those only to 'multiple' mode 00767 uint iDirCount = hasDirs ? 1 : 0; 00768 uint iFileCount = 1-iDirCount; 00769 00770 d->m_frame = new QFrame(); 00771 properties->addPage(d->m_frame, i18nc("@title:tab File properties", "&General")); 00772 00773 QVBoxLayout *vbl = new QVBoxLayout( d->m_frame ); 00774 vbl->setMargin( 0 ); 00775 vbl->setObjectName( QLatin1String( "vbl" ) ); 00776 QGridLayout *grid = new QGridLayout(); // unknown rows 00777 grid->setColumnStretch(0, 0); 00778 grid->setColumnStretch(1, 0); 00779 grid->setColumnStretch(2, 1); 00780 grid->addItem(new QSpacerItem(KDialog::spacingHint(),0), 0, 1); 00781 vbl->addLayout(grid); 00782 int curRow = 0; 00783 00784 if ( !d->bMultiple ) 00785 { 00786 QString path; 00787 if ( !d->m_bFromTemplate ) { 00788 isTrash = ( properties->kurl().protocol().toLower() == "trash" ); 00789 // Extract the full name, but without file: for local files 00790 if ( isReallyLocal ) 00791 path = properties->kurl().toLocalFile(); 00792 else 00793 path = properties->kurl().prettyUrl(); 00794 } else { 00795 path = properties->currentDir().path(KUrl::AddTrailingSlash) + properties->defaultName(); 00796 directory = properties->currentDir().prettyUrl(); 00797 } 00798 00799 if (d->bDesktopFile) { 00800 determineRelativePath( path ); 00801 } 00802 00803 // Extract the file name only 00804 filename = properties->defaultName(); 00805 if ( filename.isEmpty() ) { // no template 00806 filename = item.name(); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system 00807 } else { 00808 d->m_bFromTemplate = true; 00809 setDirty(); // to enforce that the copy happens 00810 } 00811 d->oldFileName = filename; 00812 00813 // Make it human-readable 00814 filename = nameFromFileName( filename ); 00815 00816 if ( d->bKDesktopMode && d->bDesktopFile ) { 00817 KDesktopFile config( url.path() ); 00818 if ( config.desktopGroup().hasKey( "Name" ) ) { 00819 filename = config.readName(); 00820 } 00821 } 00822 00823 d->oldName = filename; 00824 } 00825 else 00826 { 00827 // Multiple items: see what they have in common 00828 const KFileItemList items = properties->items(); 00829 KFileItemList::const_iterator kit = items.begin(); 00830 const KFileItemList::const_iterator kend = items.end(); 00831 for ( ++kit /*no need to check the first one again*/ ; kit != kend; ++kit ) 00832 { 00833 const KUrl url = (*kit).url(); 00834 kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyUrl(); 00835 // The list of things we check here should match the variables defined 00836 // at the beginning of this method. 00837 if ( url.isLocalFile() != isLocal ) 00838 isLocal = false; // not all local 00839 if ( bDesktopFile && (*kit).isDesktopFile() != bDesktopFile ) 00840 bDesktopFile = false; // not all desktop files 00841 if ( (*kit).mode() != mode ) 00842 mode = (mode_t)0; 00843 if ( KMimeType::iconNameForUrl(url, mode) != iconStr ) 00844 iconStr = "document-multiple"; 00845 if ( url.directory() != directory ) 00846 directory.clear(); 00847 if ( url.protocol() != protocol ) 00848 protocol.clear(); 00849 if ( !mimeComment.isNull() && (*kit).mimeComment() != mimeComment ) 00850 mimeComment.clear(); 00851 if ( isLocal && !magicMimeComment.isNull() ) { 00852 KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() ); 00853 if ( magicMimeType->comment() != magicMimeComment ) 00854 magicMimeComment.clear(); 00855 } 00856 00857 if ( isLocal && url.path() == QLatin1String("/") ) 00858 hasRoot = true; 00859 if ( (*kit).isDir() && !(*kit).isLink() ) 00860 { 00861 iDirCount++; 00862 hasDirs = true; 00863 } 00864 else 00865 { 00866 iFileCount++; 00867 totalSize += (*kit).size(); 00868 } 00869 } 00870 } 00871 00872 if (!isReallyLocal && !protocol.isEmpty()) 00873 { 00874 directory += ' '; 00875 directory += '('; 00876 directory += protocol; 00877 directory += ')'; 00878 } 00879 00880 if (!isTrash && (bDesktopFile || S_ISDIR(mode)) 00881 && !d->bMultiple // not implemented for multiple 00882 && enableIconButton()) // #56857 00883 { 00884 KIconButton *iconButton = new KIconButton( d->m_frame ); 00885 int bsize = 66 + 2 * iconButton->style()->pixelMetric(QStyle::PM_ButtonMargin); 00886 iconButton->setFixedSize(bsize, bsize); 00887 iconButton->setIconSize(48); 00888 iconButton->setStrictIconSize(false); 00889 QString iconStr = KMimeType::findByUrl(url, mode)->iconName(url); 00890 if (bDesktopFile && isLocal) { 00891 KDesktopFile config( url.path() ); 00892 KConfigGroup group = config.desktopGroup(); 00893 iconStr = group.readEntry( "Icon" ); 00894 if ( config.hasDeviceType() ) 00895 iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Device ); 00896 else 00897 iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Application ); 00898 } else { 00899 iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Place ); 00900 } 00901 iconButton->setIcon(iconStr); 00902 d->iconArea = iconButton; 00903 connect(iconButton, SIGNAL(iconChanged(QString)), 00904 this, SLOT(slotIconChanged())); 00905 } else { 00906 QLabel *iconLabel = new QLabel( d->m_frame ); 00907 int bsize = 66 + 2 * iconLabel->style()->pixelMetric(QStyle::PM_ButtonMargin); 00908 iconLabel->setFixedSize(bsize, bsize); 00909 iconLabel->setPixmap( KIconLoader::global()->loadIcon( iconStr, KIconLoader::Desktop, 48) ); 00910 d->iconArea = iconLabel; 00911 } 00912 grid->addWidget(d->iconArea, curRow, 0, Qt::AlignLeft); 00913 00914 if (d->bMultiple || isTrash || hasRoot) 00915 { 00916 QLabel *lab = new QLabel(d->m_frame ); 00917 if ( d->bMultiple ) 00918 lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) ); 00919 else 00920 lab->setText( filename ); 00921 d->nameArea = lab; 00922 } else 00923 { 00924 d->m_lined = new KLineEdit( d->m_frame ); 00925 d->m_lined->setText(filename); 00926 d->nameArea = d->m_lined; 00927 d->m_lined->setFocus(); 00928 00929 //if we don't have permissions to rename, we need to make "m_lined" read only. 00930 KFileItemListProperties itemList(KFileItemList()<< item); 00931 setFileNameReadOnly(!itemList.supportsMoving()); 00932 00933 // Enhanced rename: Don't highlight the file extension. 00934 QString extension = KMimeType::extractKnownExtension( filename ); 00935 if ( !extension.isEmpty() ) 00936 d->m_lined->setSelection( 0, filename.length() - extension.length() - 1 ); 00937 else 00938 { 00939 int lastDot = filename.lastIndexOf('.'); 00940 if (lastDot > 0) 00941 d->m_lined->setSelection(0, lastDot); 00942 } 00943 00944 connect( d->m_lined, SIGNAL( textChanged( const QString & ) ), 00945 this, SLOT( nameFileChanged(const QString & ) ) ); 00946 } 00947 00948 grid->addWidget(d->nameArea, curRow++, 2); 00949 00950 KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame); 00951 grid->addWidget(sep, curRow, 0, 1, 3); 00952 ++curRow; 00953 00954 QLabel *l; 00955 if (!mimeComment.isEmpty() && !isTrash) { 00956 l = new QLabel(i18n("Type:"), d->m_frame ); 00957 00958 grid->addWidget(l, curRow, 0, Qt::AlignRight); 00959 00960 KHBox *box = new KHBox(d->m_frame); 00961 box->setSpacing(20); // ### why 20? 00962 l = new QLabel(mimeComment, box ); 00963 00964 QPushButton *button = new QPushButton(box); 00965 00966 button->setIcon( KIcon(QString::fromLatin1("configure")) ); 00967 const int pixmapSize = button->style()->pixelMetric(QStyle::PM_SmallIconSize); 00968 button->setFixedSize( pixmapSize+8, pixmapSize+8 ); 00969 if ( d->mimeType == KMimeType::defaultMimeType() ) 00970 button->setToolTip(i18n("Create new file type")); 00971 else 00972 button->setToolTip(i18n("Edit file type")); 00973 00974 connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() )); 00975 00976 if (!KAuthorized::authorizeKAction("editfiletype")) 00977 button->hide(); 00978 00979 grid->addWidget(box, curRow++, 2); 00980 } 00981 00982 if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment ) 00983 { 00984 l = new QLabel(i18n("Contents:"), d->m_frame ); 00985 grid->addWidget(l, curRow, 0, Qt::AlignRight); 00986 00987 l = new QLabel(magicMimeComment, d->m_frame ); 00988 grid->addWidget(l, curRow++, 2); 00989 } 00990 00991 if ( !directory.isEmpty() ) 00992 { 00993 l = new QLabel( i18n("Location:"), d->m_frame ); 00994 grid->addWidget(l, curRow, 0, Qt::AlignRight); 00995 00996 l = new KSqueezedTextLabel( directory, d->m_frame ); 00997 // force the layout direction to be always LTR 00998 l->setLayoutDirection(Qt::LeftToRight); 00999 // but if we are in RTL mode, align the text to the right 01000 // otherwise the text is on the wrong side of the dialog 01001 if (properties->layoutDirection() == Qt::RightToLeft) 01002 l->setAlignment( Qt::AlignRight ); 01003 l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard); 01004 grid->addWidget(l, curRow++, 2); 01005 } 01006 01007 l = new QLabel(i18n("Size:"), d->m_frame ); 01008 grid->addWidget(l, curRow, 0, Qt::AlignRight); 01009 01010 d->m_sizeLabel = new QLabel( d->m_frame ); 01011 grid->addWidget( d->m_sizeLabel, curRow++, 2 ); 01012 01013 if ( !hasDirs ) // Only files [and symlinks] 01014 { 01015 d->m_sizeLabel->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize)) 01016 .arg(KGlobal::locale()->formatNumber(totalSize, 0))); 01017 d->m_sizeDetermineButton = 0L; 01018 d->m_sizeStopButton = 0L; 01019 } 01020 else // Directory 01021 { 01022 QHBoxLayout * sizelay = new QHBoxLayout(); 01023 grid->addLayout( sizelay, curRow++, 2 ); 01024 01025 // buttons 01026 d->m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame ); 01027 d->m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame ); 01028 connect( d->m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) ); 01029 connect( d->m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) ); 01030 sizelay->addWidget(d->m_sizeDetermineButton, 0); 01031 sizelay->addWidget(d->m_sizeStopButton, 0); 01032 sizelay->addStretch(10); // so that the buttons don't grow horizontally 01033 01034 // auto-launch for local dirs only, and not for '/' 01035 if ( isLocal && !hasRoot ) 01036 { 01037 d->m_sizeDetermineButton->setText( i18n("Refresh") ); 01038 slotSizeDetermine(); 01039 } 01040 else 01041 d->m_sizeStopButton->setEnabled( false ); 01042 } 01043 01044 if (!d->bMultiple && item.isLink()) { 01045 l = new QLabel(i18n("Points to:"), d->m_frame ); 01046 grid->addWidget(l, curRow, 0, Qt::AlignRight); 01047 01048 d->m_linkTargetLineEdit = new KLineEdit(item.linkDest(), d->m_frame ); 01049 grid->addWidget(d->m_linkTargetLineEdit, curRow++, 2); 01050 connect(d->m_linkTargetLineEdit, SIGNAL(textChanged(QString)), this, SLOT(setDirty())); 01051 } 01052 01053 if (!d->bMultiple) // Dates for multiple don't make much sense... 01054 { 01055 KDateTime dt = item.time(KFileItem::CreationTime); 01056 if ( !dt.isNull() ) 01057 { 01058 l = new QLabel(i18n("Created:"), d->m_frame ); 01059 grid->addWidget(l, curRow, 0, Qt::AlignRight); 01060 01061 l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame ); 01062 grid->addWidget(l, curRow++, 2); 01063 } 01064 01065 dt = item.time(KFileItem::ModificationTime); 01066 if ( !dt.isNull() ) 01067 { 01068 l = new QLabel(i18n("Modified:"), d->m_frame ); 01069 grid->addWidget(l, curRow, 0, Qt::AlignRight); 01070 01071 l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame ); 01072 grid->addWidget(l, curRow++, 2); 01073 } 01074 01075 dt = item.time(KFileItem::AccessTime); 01076 if ( !dt.isNull() ) 01077 { 01078 l = new QLabel(i18n("Accessed:"), d->m_frame ); 01079 grid->addWidget(l, curRow, 0, Qt::AlignRight); 01080 01081 l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame ); 01082 grid->addWidget(l, curRow++, 2); 01083 } 01084 } 01085 01086 if ( isLocal && hasDirs ) // only for directories 01087 { 01088 01089 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() ); 01090 if (mp) { 01091 KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() ); 01092 if(info.size() != 0 ) 01093 { 01094 sep = new KSeparator( Qt::Horizontal, d->m_frame); 01095 grid->addWidget(sep, curRow, 0, 1, 3); 01096 ++curRow; 01097 if (mp->mountPoint() != "/") 01098 { 01099 l = new QLabel(i18n("Mounted on:"), d->m_frame ); 01100 grid->addWidget(l, curRow, 0, Qt::AlignRight); 01101 01102 l = new KSqueezedTextLabel( mp->mountPoint(), d->m_frame ); 01103 l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard); 01104 grid->addWidget( l, curRow++, 2 ); 01105 } 01106 01107 l = new QLabel(i18n("Device usage:"), d->m_frame ); 01108 grid->addWidget(l, curRow, 0, Qt::AlignRight); 01109 01110 d->m_capacityBar = new KCapacityBar( KCapacityBar::DrawTextOutline, d->m_frame ); 01111 grid->addWidget( d->m_capacityBar, curRow++, 2); 01112 01113 slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024); 01114 } 01115 } 01116 } 01117 01118 vbl->addStretch(1); 01119 } 01120 01121 bool KFilePropsPlugin::enableIconButton() const 01122 { 01123 bool iconEnabled = false; 01124 const KFileItem item = properties->item(); 01125 // If the current item is a directory, check if it's writable, 01126 // so we can create/update a .directory 01127 // Current item is a file, same thing: check if it is writable 01128 if (item.isWritable()) { 01129 iconEnabled = true; 01130 } 01131 return iconEnabled; 01132 } 01133 01134 // QString KFilePropsPlugin::tabName () const 01135 // { 01136 // return i18n ("&General"); 01137 // } 01138 01139 void KFilePropsPlugin::setFileNameReadOnly( bool ro ) 01140 { 01141 if ( d->m_lined && !d->m_bFromTemplate ) 01142 { 01143 d->m_lined->setReadOnly( ro ); 01144 if (ro) 01145 { 01146 // Don't put the initial focus on the line edit when it is ro 01147 properties->setButtonFocus(KDialog::Ok); 01148 } 01149 } 01150 } 01151 01152 void KFilePropsPlugin::slotEditFileType() 01153 { 01154 QString mime; 01155 if (d->mimeType == KMimeType::defaultMimeType()) { 01156 const int pos = d->oldFileName.lastIndexOf('.'); 01157 if (pos != -1) 01158 mime = '*' + d->oldFileName.mid(pos); 01159 else 01160 mime = '*'; 01161 } else { 01162 mime = d->mimeType; 01163 } 01164 QString keditfiletype = QString::fromLatin1("keditfiletype"); 01165 KRun::runCommand( keditfiletype 01166 #ifdef Q_WS_X11 01167 + " --parent " + QString::number( (ulong)properties->window()->winId()) 01168 #endif 01169 + ' ' + KShell::quoteArg(mime), 01170 keditfiletype, keditfiletype /*unused*/, properties->window()); 01171 } 01172 01173 void KFilePropsPlugin::slotIconChanged() 01174 { 01175 d->bIconChanged = true; 01176 emit changed(); 01177 } 01178 01179 void KFilePropsPlugin::nameFileChanged(const QString &text ) 01180 { 01181 properties->enableButtonOk(!text.isEmpty()); 01182 emit changed(); 01183 } 01184 01185 void KFilePropsPlugin::determineRelativePath( const QString & path ) 01186 { 01187 // now let's make it relative 01188 d->m_sRelativePath = KGlobal::dirs()->relativeLocation("apps", path); 01189 if (d->m_sRelativePath.startsWith('/')) 01190 { 01191 d->m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path); 01192 if (d->m_sRelativePath.startsWith('/')) 01193 d->m_sRelativePath.clear(); 01194 else 01195 d->m_sRelativePath = path; 01196 } 01197 } 01198 01199 void KFilePropsPlugin::slotFoundMountPoint( const QString&, 01200 quint64 kibSize, 01201 quint64 /*kibUsed*/, 01202 quint64 kibAvail ) 01203 { 01204 d->m_capacityBar->setText( 01205 i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)", 01206 KIO::convertSizeFromKiB(kibAvail), 01207 KIO::convertSizeFromKiB(kibSize), 01208 100 - (int)(100.0 * kibAvail / kibSize) )); 01209 01210 d->m_capacityBar->setValue(100 - (int)(100.0 * kibAvail / kibSize)); 01211 } 01212 01213 void KFilePropsPlugin::slotDirSizeUpdate() 01214 { 01215 KIO::filesize_t totalSize = d->dirSizeJob->totalSize(); 01216 KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles(); 01217 KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs(); 01218 d->m_sizeLabel->setText( 01219 i18n("Calculating... %1 (%2)\n%3, %4", 01220 KIO::convertSize(totalSize), 01221 totalSize, 01222 i18np("1 file", "%1 files", totalFiles), 01223 i18np("1 sub-folder", "%1 sub-folders", totalSubdirs))); 01224 } 01225 01226 void KFilePropsPlugin::slotDirSizeFinished( KJob * job ) 01227 { 01228 if (job->error()) 01229 d->m_sizeLabel->setText( job->errorString() ); 01230 else 01231 { 01232 KIO::filesize_t totalSize = d->dirSizeJob->totalSize(); 01233 KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles(); 01234 KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs(); 01235 d->m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4") 01236 .arg(KIO::convertSize(totalSize)) 01237 .arg(KGlobal::locale()->formatNumber(totalSize, 0)) 01238 .arg(i18np("1 file","%1 files",totalFiles)) 01239 .arg(i18np("1 sub-folder","%1 sub-folders",totalSubdirs))); 01240 } 01241 d->m_sizeStopButton->setEnabled(false); 01242 // just in case you change something and try again :) 01243 d->m_sizeDetermineButton->setText( i18n("Refresh") ); 01244 d->m_sizeDetermineButton->setEnabled(true); 01245 d->dirSizeJob = 0; 01246 delete d->dirSizeUpdateTimer; 01247 d->dirSizeUpdateTimer = 0; 01248 } 01249 01250 void KFilePropsPlugin::slotSizeDetermine() 01251 { 01252 d->m_sizeLabel->setText( i18n("Calculating...") ); 01253 kDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" << properties->item(); 01254 kDebug(250) << " URL=" << properties->item().url().url(); 01255 01256 d->dirSizeJob = KIO::directorySize( properties->items() ); 01257 d->dirSizeUpdateTimer = new QTimer(this); 01258 connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ), 01259 SLOT( slotDirSizeUpdate() ) ); 01260 d->dirSizeUpdateTimer->start(500); 01261 connect( d->dirSizeJob, SIGNAL( result( KJob * ) ), 01262 SLOT( slotDirSizeFinished( KJob * ) ) ); 01263 d->m_sizeStopButton->setEnabled(true); 01264 d->m_sizeDetermineButton->setEnabled(false); 01265 01266 // also update the "Free disk space" display 01267 if ( d->m_capacityBar ) 01268 { 01269 bool isLocal; 01270 const KFileItem item = properties->item(); 01271 KUrl url = item.mostLocalUrl( isLocal ); 01272 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() ); 01273 if (mp) { 01274 KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() ); 01275 slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024); 01276 } 01277 } 01278 } 01279 01280 void KFilePropsPlugin::slotSizeStop() 01281 { 01282 if ( d->dirSizeJob ) 01283 { 01284 KIO::filesize_t totalSize = d->dirSizeJob->totalSize(); 01285 d->m_sizeLabel->setText(i18n("At least %1", 01286 KIO::convertSize(totalSize))); 01287 d->dirSizeJob->kill(); 01288 d->dirSizeJob = 0; 01289 } 01290 if ( d->dirSizeUpdateTimer ) 01291 d->dirSizeUpdateTimer->stop(); 01292 01293 d->m_sizeStopButton->setEnabled(false); 01294 d->m_sizeDetermineButton->setEnabled(true); 01295 } 01296 01297 KFilePropsPlugin::~KFilePropsPlugin() 01298 { 01299 delete d; 01300 } 01301 01302 bool KFilePropsPlugin::supports( const KFileItemList& /*_items*/ ) 01303 { 01304 return true; 01305 } 01306 01307 void KFilePropsPlugin::applyChanges() 01308 { 01309 if ( d->dirSizeJob ) 01310 slotSizeStop(); 01311 01312 kDebug(250) << "KFilePropsPlugin::applyChanges"; 01313 01314 if (qobject_cast<QLineEdit*>(d->nameArea)) 01315 { 01316 QString n = ((QLineEdit *) d->nameArea)->text(); 01317 // Remove trailing spaces (#4345) 01318 while ( ! n.isEmpty() && 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 kDebug(250) << "oldname = " << d->oldName; 01329 kDebug(250) << "newname = " << n; 01330 if ( d->oldName != n || d->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(QLatin1String(".desktop")) && 01336 !newFileName.endsWith(QLatin1String(".kdelnk"))) 01337 newFileName += ".desktop"; 01338 01339 // Tell properties. Warning, this changes the result of properties->kurl() ! 01340 properties->rename( newFileName ); 01341 01342 // Update also relative path (for apps and mimetypes) 01343 if ( !d->m_sRelativePath.isEmpty() ) 01344 determineRelativePath( properties->kurl().toLocalFile() ); 01345 01346 kDebug(250) << "New URL = " << properties->kurl().url(); 01347 kDebug(250) << "old = " << oldurl.url(); 01348 01349 // Don't remove the template !! 01350 if ( !d->m_bFromTemplate ) // (normal renaming) 01351 job = KIO::move( oldurl, properties->kurl() ); 01352 else // Copying a template 01353 job = KIO::copy( oldurl, properties->kurl() ); 01354 01355 connect( job, SIGNAL( result( KJob * ) ), 01356 SLOT( slotCopyFinished( KJob * ) ) ); 01357 connect( job, SIGNAL( renamed( KIO::Job *, const KUrl &, const KUrl & ) ), 01358 SLOT( slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & ) ) ); 01359 // wait for job 01360 QEventLoop eventLoop; 01361 connect(this, SIGNAL(leaveModality()), 01362 &eventLoop, SLOT(quit())); 01363 eventLoop.exec(QEventLoop::ExcludeUserInputEvents); 01364 return; 01365 } 01366 properties->updateUrl(properties->kurl()); 01367 // Update also relative path (for apps and mimetypes) 01368 if ( !d->m_sRelativePath.isEmpty() ) 01369 determineRelativePath( properties->kurl().toLocalFile() ); 01370 } 01371 01372 // No job, keep going 01373 slotCopyFinished( 0L ); 01374 } 01375 01376 void KFilePropsPlugin::slotCopyFinished( KJob * job ) 01377 { 01378 kDebug(250) << "KFilePropsPlugin::slotCopyFinished"; 01379 if (job) 01380 { 01381 // allow apply() to return 01382 emit leaveModality(); 01383 if ( job->error() ) 01384 { 01385 job->uiDelegate()->showErrorMessage(); 01386 // Didn't work. Revert the URL to the old one 01387 properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcUrls().first() ); 01388 properties->abortApplying(); // Don't apply the changes to the wrong file ! 01389 return; 01390 } 01391 } 01392 01393 assert( !properties->item().isNull() ); 01394 assert( !properties->item().url().isEmpty() ); 01395 01396 // Save the file where we can -> usually in ~/.kde/... 01397 if (d->bDesktopFile && !d->m_sRelativePath.isEmpty()) 01398 { 01399 kDebug(250) << "KFilePropsPlugin::slotCopyFinished " << d->m_sRelativePath; 01400 KUrl newURL; 01401 newURL.setPath( KDesktopFile::locateLocal(d->m_sRelativePath) ); 01402 kDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path(); 01403 properties->updateUrl( newURL ); 01404 } 01405 01406 if ( d->bKDesktopMode && d->bDesktopFile ) { 01407 // Renamed? Update Name field 01408 // Note: The desktop ioslave does this as well, but not when 01409 // the file is copied from a template. 01410 if ( d->m_bFromTemplate ) { 01411 KIO::UDSEntry entry; 01412 KIO::NetAccess::stat( properties->kurl(), entry, 0 ); 01413 KFileItem item( entry, properties->kurl() ); 01414 KDesktopFile config( item.localPath() ); 01415 KConfigGroup cg = config.desktopGroup(); 01416 QString nameStr = nameFromFileName(properties->kurl().fileName()); 01417 cg.writeEntry( "Name", nameStr ); 01418 cg.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized); 01419 } 01420 } 01421 01422 if (d->m_linkTargetLineEdit && !d->bMultiple) { 01423 const KFileItem item = properties->item(); 01424 const QString newTarget = d->m_linkTargetLineEdit->text(); 01425 if (newTarget != item.linkDest()) { 01426 kDebug(250) << "Updating target of symlink to" << newTarget; 01427 KIO::Job* job = KIO::symlink(newTarget, item.url(), KIO::Overwrite); 01428 job->ui()->setAutoErrorHandlingEnabled(true); 01429 job->exec(); 01430 } 01431 } 01432 01433 // "Link to Application" templates need to be made executable 01434 // Instead of matching against a filename we check if the destination 01435 // is an Application now. 01436 if ( d->m_bFromTemplate ) { 01437 // destination is not necessarily local, use the src template 01438 KDesktopFile templateResult ( static_cast<KIO::CopyJob*>(job)->srcUrls().first().toLocalFile() ); 01439 if ( templateResult.hasApplicationType() ) { 01440 // We can either stat the file and add the +x bit or use the larger chmod() job 01441 // with a umask designed to only touch u+x. This is only one KIO job, so let's 01442 // do that. 01443 01444 KFileItem appLink ( properties->item() ); 01445 KFileItemList fileItemList; 01446 fileItemList << appLink; 01447 01448 // first 0100 adds u+x, second 0100 only allows chmod to change u+x 01449 KIO::Job* chmodJob = KIO::chmod( fileItemList, 0100, 0100, QString(), QString(), KIO::HideProgressInfo ); 01450 chmodJob->exec(); 01451 } 01452 } 01453 } 01454 01455 void KFilePropsPlugin::applyIconChanges() 01456 { 01457 KIconButton *iconButton = qobject_cast<KIconButton*>(d->iconArea); 01458 if ( !iconButton || !d->bIconChanged ) 01459 return; 01460 // handle icon changes - only local files (or pseudo-local) for now 01461 // TODO: Use KTempFile and KIO::file_copy with overwrite = true 01462 KUrl url = properties->kurl(); 01463 url = KIO::NetAccess::mostLocalUrl( url, properties ); 01464 if ( url.isLocalFile()) { 01465 QString path; 01466 01467 if (S_ISDIR(properties->item().mode())) 01468 { 01469 path = url.toLocalFile(KUrl::AddTrailingSlash) + QString::fromLatin1(".directory"); 01470 // don't call updateUrl because the other tabs (i.e. permissions) 01471 // apply to the directory, not the .directory file. 01472 } 01473 else 01474 path = url.toLocalFile(); 01475 01476 // Get the default image 01477 QString str = KMimeType::findByUrl( url, 01478 properties->item().mode(), 01479 true )->iconName(); 01480 // Is it another one than the default ? 01481 QString sIcon; 01482 if ( str != iconButton->icon() ) 01483 sIcon = iconButton->icon(); 01484 // (otherwise write empty value) 01485 01486 kDebug(250) << "**" << path << "**"; 01487 01488 // If default icon and no .directory file -> don't create one 01489 if ( !sIcon.isEmpty() || QFile::exists(path) ) 01490 { 01491 KDesktopFile cfg(path); 01492 kDebug(250) << "sIcon = " << (sIcon); 01493 kDebug(250) << "str = " << (str); 01494 cfg.desktopGroup().writeEntry( "Icon", sIcon ); 01495 cfg.sync(); 01496 01497 cfg.reparseConfiguration(); 01498 if ( cfg.desktopGroup().readEntry("Icon") != sIcon ) { 01499 KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not " 01500 "have sufficient access to write to <b>%1</b>.</qt>", path)); 01501 } 01502 } 01503 } 01504 } 01505 01506 void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & newUrl ) 01507 { 01508 // This is called in case of an existing local file during the copy/move operation, 01509 // if the user chooses Rename. 01510 properties->updateUrl( newUrl ); 01511 } 01512 01513 void KFilePropsPlugin::postApplyChanges() 01514 { 01515 // Save the icon only after applying the permissions changes (#46192) 01516 applyIconChanges(); 01517 01518 const KFileItemList items = properties->items(); 01519 const KUrl::List lst = items.urlList(); 01520 org::kde::KDirNotify::emitFilesChanged( lst.toStringList() ); 01521 } 01522 01523 class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate 01524 { 01525 public: 01526 KFilePermissionsPropsPluginPrivate() 01527 { 01528 } 01529 ~KFilePermissionsPropsPluginPrivate() 01530 { 01531 } 01532 01533 QFrame *m_frame; 01534 QCheckBox *cbRecursive; 01535 QLabel *explanationLabel; 01536 KComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo; 01537 QCheckBox *extraCheckbox; 01538 mode_t partialPermissions; 01539 KFilePermissionsPropsPlugin::PermissionsMode pmode; 01540 bool canChangePermissions; 01541 bool isIrregular; 01542 bool hasExtendedACL; 01543 KACL extendedACL; 01544 KACL defaultACL; 01545 bool fileSystemSupportsACLs; 01546 01547 KComboBox *grpCombo; 01548 01549 KLineEdit *usrEdit; 01550 KLineEdit *grpEdit; 01551 01552 // Old permissions 01553 mode_t permissions; 01554 // Old group 01555 QString strGroup; 01556 // Old owner 01557 QString strOwner; 01558 }; 01559 01560 #define UniOwner (S_IRUSR|S_IWUSR|S_IXUSR) 01561 #define UniGroup (S_IRGRP|S_IWGRP|S_IXGRP) 01562 #define UniOthers (S_IROTH|S_IWOTH|S_IXOTH) 01563 #define UniRead (S_IRUSR|S_IRGRP|S_IROTH) 01564 #define UniWrite (S_IWUSR|S_IWGRP|S_IWOTH) 01565 #define UniExec (S_IXUSR|S_IXGRP|S_IXOTH) 01566 #define UniSpecial (S_ISUID|S_ISGID|S_ISVTX) 01567 01568 // synced with PermissionsTarget 01569 const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers}; 01570 const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 }; 01571 01572 // synced with PermissionsMode and standardPermissions 01573 const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = { 01574 { I18N_NOOP("Forbidden"), 01575 I18N_NOOP("Can Read"), 01576 I18N_NOOP("Can Read & Write"), 01577 0 }, 01578 { I18N_NOOP("Forbidden"), 01579 I18N_NOOP("Can View Content"), 01580 I18N_NOOP("Can View & Modify Content"), 01581 0 }, 01582 { 0, 0, 0, 0}, // no texts for links 01583 { I18N_NOOP("Forbidden"), 01584 I18N_NOOP("Can View Content & Read"), 01585 I18N_NOOP("Can View/Read & Modify/Write"), 01586 0 } 01587 }; 01588 01589 01590 KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props ) 01591 : KPropertiesDialogPlugin( _props ),d(new KFilePermissionsPropsPluginPrivate) 01592 { 01593 d->cbRecursive = 0L; 01594 d->grpCombo = 0L; d->grpEdit = 0; 01595 d->usrEdit = 0L; 01596 QString path = properties->kurl().path(KUrl::RemoveTrailingSlash); 01597 QString fname = properties->kurl().fileName(); 01598 bool isLocal = properties->kurl().isLocalFile(); 01599 bool isTrash = ( properties->kurl().protocol().toLower() == "trash" ); 01600 bool IamRoot = (geteuid() == 0); 01601 01602 const KFileItem item = properties->item(); 01603 bool isLink = item.isLink(); 01604 bool isDir = item.isDir(); // all dirs 01605 bool hasDir = item.isDir(); // at least one dir 01606 d->permissions = item.permissions(); // common permissions to all files 01607 d->partialPermissions = d->permissions; // permissions that only some files have (at first we take everything) 01608 d->isIrregular = isIrregular(d->permissions, isDir, isLink); 01609 d->strOwner = item.user(); 01610 d->strGroup = item.group(); 01611 d->hasExtendedACL = item.ACL().isExtended() || item.defaultACL().isValid(); 01612 d->extendedACL = item.ACL(); 01613 d->defaultACL = item.defaultACL(); 01614 d->fileSystemSupportsACLs = false; 01615 01616 if ( properties->items().count() > 1 ) 01617 { 01618 // Multiple items: see what they have in common 01619 const KFileItemList items = properties->items(); 01620 KFileItemList::const_iterator it = items.begin(); 01621 const KFileItemList::const_iterator kend = items.end(); 01622 for ( ++it /*no need to check the first one again*/ ; it != kend; ++it ) 01623 { 01624 const KUrl url = (*it).url(); 01625 if (!d->isIrregular) 01626 d->isIrregular |= isIrregular((*it).permissions(), 01627 (*it).isDir() == isDir, 01628 (*it).isLink() == isLink); 01629 d->hasExtendedACL = d->hasExtendedACL || (*it).hasExtendedACL(); 01630 if ( (*it).isLink() != isLink ) 01631 isLink = false; 01632 if ( (*it).isDir() != isDir ) 01633 isDir = false; 01634 hasDir |= (*it).isDir(); 01635 if ( (*it).permissions() != d->permissions ) 01636 { 01637 d->permissions &= (*it).permissions(); 01638 d->partialPermissions |= (*it).permissions(); 01639 } 01640 if ( (*it).user() != d->strOwner ) 01641 d->strOwner.clear(); 01642 if ( (*it).group() != d->strGroup ) 01643 d->strGroup.clear(); 01644 } 01645 } 01646 01647 if (isLink) 01648 d->pmode = PermissionsOnlyLinks; 01649 else if (isDir) 01650 d->pmode = PermissionsOnlyDirs; 01651 else if (hasDir) 01652 d->pmode = PermissionsMixed; 01653 else 01654 d->pmode = PermissionsOnlyFiles; 01655 01656 // keep only what's not in the common permissions 01657 d->partialPermissions = d->partialPermissions & ~d->permissions; 01658 01659 bool isMyFile = false; 01660 01661 if (isLocal && !d->strOwner.isEmpty()) { // local files, and all owned by the same person 01662 struct passwd *myself = getpwuid( geteuid() ); 01663 if ( myself != 0L ) 01664 { 01665 isMyFile = (d->strOwner == QString::fromLocal8Bit(myself->pw_name)); 01666 } else 01667 kWarning() << "I don't exist ?! geteuid=" << geteuid(); 01668 } else { 01669 //We don't know, for remote files, if they are ours or not. 01670 //So we let the user change permissions, and 01671 //KIO::chmod will tell, if he had no right to do it. 01672 isMyFile = true; 01673 } 01674 01675 d->canChangePermissions = (isMyFile || IamRoot) && (!isLink); 01676 01677 01678 // create GUI 01679 01680 d->m_frame = new QFrame(); 01681 properties->addPage( d->m_frame, i18n("&Permissions") ); 01682 01683 QBoxLayout *box = new QVBoxLayout( d->m_frame ); 01684 box->setMargin( 0 ); 01685 01686 QWidget *l; 01687 QLabel *lbl; 01688 QGroupBox *gb; 01689 QGridLayout *gl; 01690 QPushButton* pbAdvancedPerm = 0; 01691 01692 /* Group: Access Permissions */ 01693 gb = new QGroupBox ( i18n("Access Permissions"), d->m_frame ); 01694 box->addWidget (gb); 01695 01696 gl = new QGridLayout (gb); 01697 gl->setColumnStretch(1, 1); 01698 01699 l = d->explanationLabel = new QLabel( "", gb ); 01700 if (isLink) 01701 d->explanationLabel->setText(i18np("This file is a link and does not have permissions.", 01702 "All files are links and do not have permissions.", 01703 properties->items().count())); 01704 else if (!d->canChangePermissions) 01705 d->explanationLabel->setText(i18n("Only the owner can change permissions.")); 01706 gl->addWidget(l, 0, 0, 1, 2); 01707 01708 lbl = new QLabel( i18n("O&wner:"), gb); 01709 gl->addWidget(lbl, 1, 0, Qt::AlignRight); 01710 l = d->ownerPermCombo = new KComboBox(gb); 01711 lbl->setBuddy(l); 01712 gl->addWidget(l, 1, 1); 01713 connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() )); 01714 l->setWhatsThis(i18n("Specifies the actions that the owner is allowed to do.")); 01715 01716 lbl = new QLabel( i18n("Gro&up:"), gb); 01717 gl->addWidget(lbl, 2, 0, Qt::AlignRight); 01718 l = d->groupPermCombo = new KComboBox(gb); 01719 lbl->setBuddy(l); 01720 gl->addWidget(l, 2, 1); 01721 connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() )); 01722 l->setWhatsThis(i18n("Specifies the actions that the members of the group are allowed to do.")); 01723 01724 lbl = new QLabel( i18n("O&thers:"), gb); 01725 gl->addWidget(lbl, 3, 0, Qt::AlignRight); 01726 l = d->othersPermCombo = new KComboBox(gb); 01727 lbl->setBuddy(l); 01728 gl->addWidget(l, 3, 1); 01729 connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() )); 01730 l->setWhatsThis(i18n("Specifies the actions that all users, who are neither " 01731 "owner nor in the group, are allowed to do.")); 01732 01733 if (!isLink) { 01734 l = d->extraCheckbox = new QCheckBox(hasDir ? 01735 i18n("Only own&er can rename and delete folder content") : 01736 i18n("Is &executable"), 01737 gb ); 01738 connect( d->extraCheckbox, SIGNAL( clicked() ), this, SIGNAL( changed() ) ); 01739 gl->addWidget(l, 4, 1); 01740 l->setWhatsThis(hasDir ? i18n("Enable this option to allow only the folder's owner to " 01741 "delete or rename the contained files and folders. Other " 01742 "users can only add new files, which requires the 'Modify " 01743 "Content' permission.") 01744 : i18n("Enable this option to mark the file as executable. This only makes " 01745 "sense for programs and scripts. It is required when you want to " 01746 "execute them.")); 01747 01748 QLayoutItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Expanding); 01749 gl->addItem(spacer, 5, 0, 1, 3); 01750 01751 pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb); 01752 gl->addWidget(pbAdvancedPerm, 6, 0, 1, 2, Qt::AlignRight); 01753 connect(pbAdvancedPerm, SIGNAL( clicked() ), this, SLOT( slotShowAdvancedPermissions() )); 01754 } 01755 else 01756 d->extraCheckbox = 0; 01757 01758 01759 /**** Group: Ownership ****/ 01760 gb = new QGroupBox ( i18n("Ownership"), d->m_frame ); 01761 box->addWidget (gb); 01762 01763 gl = new QGridLayout (gb); 01764 gl->addItem(new QSpacerItem(0, 10), 0, 0); 01765 01766 /*** Set Owner ***/ 01767 l = new QLabel( i18n("User:"), gb ); 01768 gl->addWidget (l, 1, 0, Qt::AlignRight); 01769 01770 /* GJ: Don't autocomplete more than 1000 users. This is a kind of random 01771 * value. Huge sites having 10.000+ user have a fair chance of using NIS, 01772 * (possibly) making this unacceptably slow. 01773 * OTOH, it is nice to offer this functionality for the standard user. 01774 */ 01775 int i, maxEntries = 1000; 01776 struct passwd *user; 01777 01778 /* File owner: For root, offer a KLineEdit with autocompletion. 01779 * For a user, who can never chown() a file, offer a QLabel. 01780 */ 01781 if (IamRoot && isLocal) 01782 { 01783 d->usrEdit = new KLineEdit( gb ); 01784 KCompletion *kcom = d->usrEdit->completionObject(); 01785 kcom->setOrder(KCompletion::Sorted); 01786 setpwent(); 01787 for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); ++i) 01788 kcom->addItem(QString::fromLatin1(user->pw_name)); 01789 endpwent(); 01790 d->usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto : 01791 KGlobalSettings::CompletionNone); 01792 d->usrEdit->setText(d->strOwner); 01793 gl->addWidget(d->usrEdit, 1, 1); 01794 connect( d->usrEdit, SIGNAL( textChanged( const QString & ) ), 01795 this, SIGNAL( changed() ) ); 01796 } 01797 else 01798 { 01799 l = new QLabel(d->strOwner, gb); 01800 gl->addWidget(l, 1, 1); 01801 } 01802 01803 /*** Set Group ***/ 01804 01805 QStringList groupList; 01806 QByteArray strUser; 01807 user = getpwuid(geteuid()); 01808 if (user != 0L) 01809 strUser = user->pw_name; 01810 01811 #ifdef HAVE_GETGROUPLIST 01812 // pick the groups to which the user belongs 01813 int groupCount = 0; 01814 #ifdef Q_OS_MAC 01815 QVarLengthArray<int> groups; 01816 #else 01817 QVarLengthArray<gid_t> groups; 01818 #endif 01819 if (getgrouplist(strUser, user->pw_gid, NULL, &groupCount) < 0) { 01820 groups.resize(groupCount); 01821 if (groups.data()) 01822 getgrouplist(strUser, user->pw_gid, groups.data(), &groupCount); 01823 else 01824 groupCount = 0; 01825 } 01826 01827 for (i = 0; i < groupCount; i++) { 01828 struct group *mygroup = getgrgid(groups[i]); 01829 if (mygroup) 01830 groupList += QString::fromLocal8Bit(mygroup->gr_name); 01831 } 01832 #endif // HAVE_GETGROUPLIST 01833 01834 bool isMyGroup = groupList.contains(d->strGroup); 01835 01836 /* add the group the file currently belongs to .. 01837 * .. if it is not there already 01838 */ 01839 if (!isMyGroup) 01840 groupList += d->strGroup; 01841 01842 l = new QLabel( i18n("Group:"), gb ); 01843 gl->addWidget (l, 2, 0, Qt::AlignRight); 01844 01845 /* Set group: if possible to change: 01846 * - Offer a KLineEdit for root, since he can change to any group. 01847 * - Offer a KComboBox for a normal user, since he can change to a fixed 01848 * (small) set of groups only. 01849 * If not changeable: offer a QLabel. 01850 */ 01851 if (IamRoot && isLocal) 01852 { 01853 d->grpEdit = new KLineEdit(gb); 01854 KCompletion *kcom = new KCompletion; 01855 kcom->setItems(groupList); 01856 d->grpEdit->setCompletionObject(kcom, true); 01857 d->grpEdit->setAutoDeleteCompletionObject( true ); 01858 d->grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto); 01859 d->grpEdit->setText(d->strGroup); 01860 gl->addWidget(d->grpEdit, 2, 1); 01861 connect( d->grpEdit, SIGNAL( textChanged( const QString & ) ), 01862 this, SIGNAL( changed() ) ); 01863 } 01864 else if ((groupList.count() > 1) && isMyFile && isLocal) 01865 { 01866 d->grpCombo = new KComboBox(gb); 01867 d->grpCombo->setObjectName(QLatin1String("combogrouplist")); 01868 d->grpCombo->addItems(groupList); 01869 d->grpCombo->setCurrentIndex(groupList.indexOf(d->strGroup)); 01870 gl->addWidget(d->grpCombo, 2, 1); 01871 connect( d->grpCombo, SIGNAL( activated( int ) ), 01872 this, SIGNAL( changed() ) ); 01873 } 01874 else 01875 { 01876 l = new QLabel(d->strGroup, gb); 01877 gl->addWidget(l, 2, 1); 01878 } 01879 01880 gl->setColumnStretch(2, 10); 01881 01882 // "Apply recursive" checkbox 01883 if ( hasDir && !isLink && !isTrash ) 01884 { 01885 d->cbRecursive = new QCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame ); 01886 connect( d->cbRecursive, SIGNAL( clicked() ), this, SIGNAL( changed() ) ); 01887 box->addWidget( d->cbRecursive ); 01888 } 01889 01890 updateAccessControls(); 01891 01892 01893 if ( isTrash ) 01894 { 01895 //don't allow to change properties for file into trash 01896 enableAccessControls(false); 01897 if ( pbAdvancedPerm) 01898 pbAdvancedPerm->setEnabled(false); 01899 } 01900 01901 box->addStretch (10); 01902 } 01903 01904 #ifdef HAVE_POSIX_ACL 01905 static bool fileSystemSupportsACL( const QByteArray& path ) 01906 { 01907 bool fileSystemSupportsACLs = false; 01908 #ifdef Q_OS_FREEBSD 01909 struct statfs buf; 01910 fileSystemSupportsACLs = ( statfs( path.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS ); 01911 #else 01912 fileSystemSupportsACLs = 01913 getxattr( path.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA; 01914 #endif 01915 return fileSystemSupportsACLs; 01916 } 01917 #endif 01918 01919 01920 void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() { 01921 01922 bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed); 01923 KDialog dlg( properties ); 01924 dlg.setModal( true ); 01925 dlg.setCaption( i18n("Advanced Permissions") ); 01926 dlg.setButtons( KDialog::Ok | KDialog::Cancel ); 01927 01928 QLabel *l, *cl[3]; 01929 QGroupBox *gb; 01930 QGridLayout *gl; 01931 01932 QWidget *mainw = new QWidget( &dlg ); 01933 QVBoxLayout *vbox = new QVBoxLayout(mainw); 01934 // Group: Access Permissions 01935 gb = new QGroupBox ( i18n("Access Permissions"), mainw ); 01936 vbox->addWidget(gb); 01937 01938 gl = new QGridLayout (gb); 01939 gl->addItem(new QSpacerItem(0, 10), 0, 0); 01940 01941 QVector<QWidget*> theNotSpecials; 01942 01943 l = new QLabel(i18n("Class"), gb ); 01944 gl->addWidget(l, 1, 0); 01945 theNotSpecials.append( l ); 01946 01947 if (isDir) 01948 l = new QLabel( i18n("Show\nEntries"), gb ); 01949 else 01950 l = new QLabel( i18n("Read"), gb ); 01951 gl->addWidget (l, 1, 1); 01952 theNotSpecials.append( l ); 01953 QString readWhatsThis; 01954 if (isDir) 01955 readWhatsThis = i18n("This flag allows viewing the content of the folder."); 01956 else 01957 readWhatsThis = i18n("The Read flag allows viewing the content of the file."); 01958 l->setWhatsThis(readWhatsThis); 01959 01960 if (isDir) 01961 l = new QLabel( i18n("Write\nEntries"), gb ); 01962 else 01963 l = new QLabel( i18n("Write"), gb ); 01964 gl->addWidget (l, 1, 2); 01965 theNotSpecials.append( l ); 01966 QString writeWhatsThis; 01967 if (isDir) 01968 writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. " 01969 "Note that deleting and renaming can be limited using the Sticky flag."); 01970 else 01971 writeWhatsThis = i18n("The Write flag allows modifying the content of the file."); 01972 l->setWhatsThis(writeWhatsThis); 01973 01974 QString execWhatsThis; 01975 if (isDir) { 01976 l = new QLabel( i18nc("Enter folder", "Enter"), gb ); 01977 execWhatsThis = i18n("Enable this flag to allow entering the folder."); 01978 } 01979 else { 01980 l = new QLabel( i18n("Exec"), gb ); 01981 execWhatsThis = i18n("Enable this flag to allow executing the file as a program."); 01982 } 01983 l->setWhatsThis(execWhatsThis); 01984 theNotSpecials.append( l ); 01985 // GJ: Add space between normal and special modes 01986 QSize size = l->sizeHint(); 01987 size.setWidth(size.width() + 15); 01988 l->setFixedSize(size); 01989 gl->addWidget (l, 1, 3); 01990 01991 l = new QLabel( i18n("Special"), gb ); 01992 gl->addWidget(l, 1, 4, 1, 2); 01993 QString specialWhatsThis; 01994 if (isDir) 01995 specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact " 01996 "meaning of the flag can be seen in the right hand column."); 01997 else 01998 specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen " 01999 "in the right hand column."); 02000 l->setWhatsThis(specialWhatsThis); 02001 02002 cl[0] = new QLabel( i18n("User"), gb ); 02003 gl->addWidget (cl[0], 2, 0); 02004 theNotSpecials.append( cl[0] ); 02005 02006 cl[1] = new QLabel( i18n("Group"), gb ); 02007 gl->addWidget (cl[1], 3, 0); 02008 theNotSpecials.append( cl[1] ); 02009 02010 cl[2] = new QLabel( i18n("Others"), gb ); 02011 gl->addWidget (cl[2], 4, 0); 02012 theNotSpecials.append( cl[2] ); 02013 02014 l = new QLabel(i18n("Set UID"), gb); 02015 gl->addWidget(l, 2, 5); 02016 QString setUidWhatsThis; 02017 if (isDir) 02018 setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be " 02019 "the owner of all new files."); 02020 else 02021 setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will " 02022 "be executed with the permissions of the owner."); 02023 l->setWhatsThis(setUidWhatsThis); 02024 02025 l = new QLabel(i18n("Set GID"), gb); 02026 gl->addWidget(l, 3, 5); 02027 QString setGidWhatsThis; 02028 if (isDir) 02029 setGidWhatsThis = i18n("If this flag is set, the group of this folder will be " 02030 "set for all new files."); 02031 else 02032 setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will " 02033 "be executed with the permissions of the group."); 02034 l->setWhatsThis(setGidWhatsThis); 02035 02036 l = new QLabel(i18nc("File permission", "Sticky"), gb); 02037 gl->addWidget(l, 4, 5); 02038 QString stickyWhatsThis; 02039 if (isDir) 02040 stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner " 02041 "and root can delete or rename files. Otherwise everybody " 02042 "with write permissions can do this."); 02043 else 02044 stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may " 02045 "be used on some systems"); 02046 l->setWhatsThis(stickyWhatsThis); 02047 02048 mode_t aPermissions, aPartialPermissions; 02049 mode_t dummy1, dummy2; 02050 02051 if (!d->isIrregular) { 02052 switch (d->pmode) { 02053 case PermissionsOnlyFiles: 02054 getPermissionMasks(aPartialPermissions, 02055 dummy1, 02056 aPermissions, 02057 dummy2); 02058 break; 02059 case PermissionsOnlyDirs: 02060 case PermissionsMixed: 02061 getPermissionMasks(dummy1, 02062 aPartialPermissions, 02063 dummy2, 02064 aPermissions); 02065 break; 02066 case PermissionsOnlyLinks: 02067 aPermissions = UniRead | UniWrite | UniExec | UniSpecial; 02068 aPartialPermissions = 0; 02069 break; 02070 } 02071 } 02072 else { 02073 aPermissions = d->permissions; 02074 aPartialPermissions = d->partialPermissions; 02075 } 02076 02077 // Draw Checkboxes 02078 QCheckBox *cba[3][4]; 02079 for (int row = 0; row < 3 ; ++row) { 02080 for (int col = 0; col < 4; ++col) { 02081 QCheckBox *cb = new QCheckBox(gb); 02082 if ( col != 3 ) theNotSpecials.append( cb ); 02083 cba[row][col] = cb; 02084 cb->setChecked(aPermissions & fperm[row][col]); 02085 if ( aPartialPermissions & fperm[row][col] ) 02086 { 02087 cb->setTristate(); 02088 cb->setCheckState(Qt::PartiallyChecked); 02089 } 02090 else if (d->cbRecursive && d->cbRecursive->isChecked()) 02091 cb->setTristate(); 02092 02093 cb->setEnabled( d->canChangePermissions ); 02094 gl->addWidget (cb, row+2, col+1); 02095 switch(col) { 02096 case 0: 02097 cb->setWhatsThis(readWhatsThis); 02098 break; 02099 case 1: 02100 cb->setWhatsThis(writeWhatsThis); 02101 break; 02102 case 2: 02103 cb->setWhatsThis(execWhatsThis); 02104 break; 02105 case 3: 02106 switch(row) { 02107 case 0: 02108 cb->setWhatsThis(setUidWhatsThis); 02109 break; 02110 case 1: 02111 cb->setWhatsThis(setGidWhatsThis); 02112 break; 02113 case 2: 02114 cb->setWhatsThis(stickyWhatsThis); 02115 break; 02116 } 02117 break; 02118 } 02119 } 02120 } 02121 gl->setColumnStretch(6, 10); 02122 02123 #ifdef HAVE_POSIX_ACL 02124 KACLEditWidget *extendedACLs = 0; 02125 02126 // FIXME make it work with partial entries 02127 if ( properties->items().count() == 1 ) { 02128 QByteArray path = QFile::encodeName( properties->item().url().toLocalFile() ); 02129 d->fileSystemSupportsACLs = fileSystemSupportsACL( path ); 02130 } 02131 if ( d->fileSystemSupportsACLs ) { 02132 std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) ); 02133 extendedACLs = new KACLEditWidget( mainw ); 02134 vbox->addWidget(extendedACLs); 02135 if ( d->extendedACL.isValid() && d->extendedACL.isExtended() ) 02136 extendedACLs->setACL( d->extendedACL ); 02137 else 02138 extendedACLs->setACL( KACL( aPermissions ) ); 02139 02140 if ( d->defaultACL.isValid() ) 02141 extendedACLs->setDefaultACL( d->defaultACL ); 02142 02143 if ( properties->items().first().isDir() ) 02144 extendedACLs->setAllowDefaults( true ); 02145 } 02146 #endif 02147 dlg.setMainWidget( mainw ); 02148 if (dlg.exec() != KDialog::Accepted) 02149 return; 02150 02151 mode_t andPermissions = mode_t(~0); 02152 mode_t orPermissions = 0; 02153 for (int row = 0; row < 3; ++row) 02154 for (int col = 0; col < 4; ++col) { 02155 switch (cba[row][col]->checkState()) 02156 { 02157 case Qt::Checked: 02158 orPermissions |= fperm[row][col]; 02159 //fall through 02160 case Qt::Unchecked: 02161 andPermissions &= ~fperm[row][col]; 02162 break; 02163 default: // NoChange 02164 break; 02165 } 02166 } 02167 02168 d->isIrregular = false; 02169 const KFileItemList items = properties->items(); 02170 KFileItemList::const_iterator it = items.begin(); 02171 const KFileItemList::const_iterator kend = items.end(); 02172 for ( ; it != kend; ++it ) { 02173 if (isIrregular(((*it).permissions() & andPermissions) | orPermissions, 02174 (*it).isDir(), (*it).isLink())) { 02175 d->isIrregular = true; 02176 break; 02177 } 02178 } 02179 02180 d->permissions = orPermissions; 02181 d->partialPermissions = andPermissions; 02182 02183 #ifdef HAVE_POSIX_ACL 02184 // override with the acls, if present 02185 if ( extendedACLs ) { 02186 d->extendedACL = extendedACLs->getACL(); 02187 d->defaultACL = extendedACLs->getDefaultACL(); 02188 d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid(); 02189 d->permissions = d->extendedACL.basePermissions(); 02190 d->permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX ); 02191 } 02192 #endif 02193 02194 updateAccessControls(); 02195 emit changed(); 02196 } 02197 02198 // QString KFilePermissionsPropsPlugin::tabName () const 02199 // { 02200 // return i18n ("&Permissions"); 02201 // } 02202 02203 KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin() 02204 { 02205 delete d; 02206 } 02207 02208 bool KFilePermissionsPropsPlugin::supports( const KFileItemList& /*_items*/ ) 02209 { 02210 return true; 02211 } 02212 02213 // sets a combo box in the Access Control frame 02214 void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target, 02215 mode_t permissions, mode_t partial) { 02216 combo->clear(); 02217 if (d->isIrregular) //#176876 02218 return; 02219 02220 if (d->pmode == PermissionsOnlyLinks) { 02221 combo->addItem(i18n("Link")); 02222 combo->setCurrentIndex(0); 02223 return; 02224 } 02225 02226 mode_t tMask = permissionsMasks[target]; 02227 int textIndex; 02228 for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++) { 02229 if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite))) 02230 break; 02231 } 02232 Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar 02233 02234 for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++) 02235 combo->addItem(i18n(permissionsTexts[(int)d->pmode][i])); 02236 02237 if (partial & tMask & ~UniExec) { 02238 combo->addItem(i18n("Varying (No Change)")); 02239 combo->setCurrentIndex(3); 02240 } 02241 else { 02242 combo->setCurrentIndex(textIndex); 02243 } 02244 } 02245 02246 // permissions are irregular if they cant be displayed in a combo box. 02247 bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) { 02248 if (isLink) // links are always ok 02249 return false; 02250 02251 mode_t p = permissions; 02252 if (p & (S_ISUID | S_ISGID)) // setuid/setgid -> irregular 02253 return true; 02254 if (isDir) { 02255 p &= ~S_ISVTX; // ignore sticky on dirs 02256 02257 // check supported flag combinations 02258 mode_t p0 = p & UniOwner; 02259 if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner)) 02260 return true; 02261 p0 = p & UniGroup; 02262 if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup)) 02263 return true; 02264 p0 = p & UniOthers; 02265 if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers)) 02266 return true; 02267 return false; 02268 } 02269 if (p & S_ISVTX) // sticky on file -> irregular 02270 return true; 02271 02272 // check supported flag combinations 02273 mode_t p0 = p & UniOwner; 02274 bool usrXPossible = !p0; // true if this file could be an executable 02275 if (p0 & S_IXUSR) { 02276 if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR))) 02277 return true; 02278 usrXPossible = true; 02279 } 02280 else if (p0 == S_IWUSR) 02281 return true; 02282 02283 p0 = p & UniGroup; 02284 bool grpXPossible = !p0; // true if this file could be an executable 02285 if (p0 & S_IXGRP) { 02286 if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP))) 02287 return true; 02288 grpXPossible = true; 02289 } 02290 else if (p0 == S_IWGRP) 02291 return true; 02292 if (p0 == 0) 02293 grpXPossible = true; 02294 02295 p0 = p & UniOthers; 02296 bool othXPossible = !p0; // true if this file could be an executable 02297 if (p0 & S_IXOTH) { 02298 if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH))) 02299 return true; 02300 othXPossible = true; 02301 } 02302 else if (p0 == S_IWOTH) 02303 return true; 02304 02305 // check that there either all targets are executable-compatible, or none 02306 return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible); 02307 } 02308 02309 // enables/disabled the widgets in the Access Control frame 02310 void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) { 02311 d->ownerPermCombo->setEnabled(enable); 02312 d->groupPermCombo->setEnabled(enable); 02313 d->othersPermCombo->setEnabled(enable); 02314 if (d->extraCheckbox) 02315 d->extraCheckbox->setEnabled(enable); 02316 if ( d->cbRecursive ) 02317 d->cbRecursive->setEnabled(enable); 02318 } 02319 02320 // updates all widgets in the Access Control frame 02321 void KFilePermissionsPropsPlugin::updateAccessControls() { 02322 setComboContent(d->ownerPermCombo, PermissionsOwner, 02323 d->permissions, d->partialPermissions); 02324 setComboContent(d->groupPermCombo, PermissionsGroup, 02325 d->permissions, d->partialPermissions); 02326 setComboContent(d->othersPermCombo, PermissionsOthers, 02327 d->permissions, d->partialPermissions); 02328 02329 switch(d->pmode) { 02330 case PermissionsOnlyLinks: 02331 enableAccessControls(false); 02332 break; 02333 case PermissionsOnlyFiles: 02334 enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL); 02335 if (d->canChangePermissions) 02336 d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ? 02337 i18np("This file uses advanced permissions", 02338 "These files use advanced permissions.", 02339 properties->items().count()) : ""); 02340 if (d->partialPermissions & UniExec) { 02341 d->extraCheckbox->setTristate(); 02342 d->extraCheckbox->setCheckState(Qt::PartiallyChecked); 02343 } 02344 else { 02345 d->extraCheckbox->setTristate(false); 02346 d->extraCheckbox->setChecked(d->permissions & UniExec); 02347 } 02348 break; 02349 case PermissionsOnlyDirs: 02350 enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL); 02351 // if this is a dir, and we can change permissions, don't dis-allow 02352 // recursive, we can do that for ACL setting. 02353 if ( d->cbRecursive ) 02354 d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular ); 02355 02356 if (d->canChangePermissions) 02357 d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ? 02358 i18np("This folder uses advanced permissions.", 02359 "These folders use advanced permissions.", 02360 properties->items().count()) : ""); 02361 if (d->partialPermissions & S_ISVTX) { 02362 d->extraCheckbox->setTristate(); 02363 d->extraCheckbox->setCheckState(Qt::PartiallyChecked); 02364 } 02365 else { 02366 d->extraCheckbox->setTristate(false); 02367 d->extraCheckbox->setChecked(d->permissions & S_ISVTX); 02368 } 02369 break; 02370 case PermissionsMixed: 02371 enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL); 02372 if (d->canChangePermissions) 02373 d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ? 02374 i18n("These files use advanced permissions.") : ""); 02375 break; 02376 if (d->partialPermissions & S_ISVTX) { 02377 d->extraCheckbox->setTristate(); 02378 d->extraCheckbox->setCheckState(Qt::PartiallyChecked); 02379 } 02380 else { 02381 d->extraCheckbox->setTristate(false); 02382 d->extraCheckbox->setChecked(d->permissions & S_ISVTX); 02383 } 02384 break; 02385 } 02386 } 02387 02388 // gets masks for files and dirs from the Access Control frame widgets 02389 void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions, 02390 mode_t &andDirPermissions, 02391 mode_t &orFilePermissions, 02392 mode_t &orDirPermissions) { 02393 andFilePermissions = mode_t(~UniSpecial); 02394 andDirPermissions = mode_t(~(S_ISUID|S_ISGID)); 02395 orFilePermissions = 0; 02396 orDirPermissions = 0; 02397 if (d->isIrregular) 02398 return; 02399 02400 mode_t m = standardPermissions[d->ownerPermCombo->currentIndex()]; 02401 if (m != (mode_t) -1) { 02402 orFilePermissions |= m & UniOwner; 02403 if ((m & UniOwner) && 02404 ((d->pmode == PermissionsMixed) || 02405 ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked)))) 02406 andFilePermissions &= ~(S_IRUSR | S_IWUSR); 02407 else { 02408 andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR); 02409 if ((m & S_IRUSR) && (d->extraCheckbox->checkState() == Qt::Checked)) 02410 orFilePermissions |= S_IXUSR; 02411 } 02412 02413 orDirPermissions |= m & UniOwner; 02414 if (m & S_IRUSR) 02415 orDirPermissions |= S_IXUSR; 02416 andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR); 02417 } 02418 02419 m = standardPermissions[d->groupPermCombo->currentIndex()]; 02420 if (m != (mode_t) -1) { 02421 orFilePermissions |= m & UniGroup; 02422 if ((m & UniGroup) && 02423 ((d->pmode == PermissionsMixed) || 02424 ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked)))) 02425 andFilePermissions &= ~(S_IRGRP | S_IWGRP); 02426 else { 02427 andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP); 02428 if ((m & S_IRGRP) && (d->extraCheckbox->checkState() == Qt::Checked)) 02429 orFilePermissions |= S_IXGRP; 02430 } 02431 02432 orDirPermissions |= m & UniGroup; 02433 if (m & S_IRGRP) 02434 orDirPermissions |= S_IXGRP; 02435 andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP); 02436 } 02437 02438 m = d->othersPermCombo->currentIndex() >= 0 ? standardPermissions[d->othersPermCombo->currentIndex()] : (mode_t)-1; 02439 if (m != (mode_t) -1) { 02440 orFilePermissions |= m & UniOthers; 02441 if ((m & UniOthers) && 02442 ((d->pmode == PermissionsMixed) || 02443 ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked)))) 02444 andFilePermissions &= ~(S_IROTH | S_IWOTH); 02445 else { 02446 andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH); 02447 if ((m & S_IROTH) && (d->extraCheckbox->checkState() == Qt::Checked)) 02448 orFilePermissions |= S_IXOTH; 02449 } 02450 02451 orDirPermissions |= m & UniOthers; 02452 if (m & S_IROTH) 02453 orDirPermissions |= S_IXOTH; 02454 andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH); 02455 } 02456 02457 if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) && 02458 (d->extraCheckbox->checkState() != Qt::PartiallyChecked)) { 02459 andDirPermissions &= ~S_ISVTX; 02460 if (d->extraCheckbox->checkState() == Qt::Checked) 02461 orDirPermissions |= S_ISVTX; 02462 } 02463 } 02464 02465 void KFilePermissionsPropsPlugin::applyChanges() 02466 { 02467 mode_t orFilePermissions; 02468 mode_t orDirPermissions; 02469 mode_t andFilePermissions; 02470 mode_t andDirPermissions; 02471 02472 if (!d->canChangePermissions) 02473 return; 02474 02475 if (!d->isIrregular) 02476 getPermissionMasks(andFilePermissions, 02477 andDirPermissions, 02478 orFilePermissions, 02479 orDirPermissions); 02480 else { 02481 orFilePermissions = d->permissions; 02482 andFilePermissions = d->partialPermissions; 02483 orDirPermissions = d->permissions; 02484 andDirPermissions = d->partialPermissions; 02485 } 02486 02487 QString owner, group; 02488 if (d->usrEdit) 02489 owner = d->usrEdit->text(); 02490 if (d->grpEdit) 02491 group = d->grpEdit->text(); 02492 else if (d->grpCombo) 02493 group = d->grpCombo->currentText(); 02494 02495 if (owner == d->strOwner) 02496 owner.clear(); // no change 02497 02498 if (group == d->strGroup) 02499 group.clear(); 02500 02501 bool recursive = d->cbRecursive && d->cbRecursive->isChecked(); 02502 bool permissionChange = false; 02503 02504 KFileItemList files, dirs; 02505 const KFileItemList items = properties->items(); 02506 KFileItemList::const_iterator it = items.begin(); 02507 const KFileItemList::const_iterator kend = items.end(); 02508 for ( ; it != kend; ++it ) { 02509 if ((*it).isDir()) { 02510 dirs.append(*it); 02511 if ((*it).permissions() != (((*it).permissions() & andDirPermissions) | orDirPermissions)) 02512 permissionChange = true; 02513 } 02514 else if ((*it).isFile()) { 02515 files.append(*it); 02516 if ((*it).permissions() != (((*it).permissions() & andFilePermissions) | orFilePermissions)) 02517 permissionChange = true; 02518 } 02519 } 02520 02521 const bool ACLChange = ( d->extendedACL != properties->item().ACL() ); 02522 const bool defaultACLChange = ( d->defaultACL != properties->item().defaultACL() ); 02523 02524 if (owner.isEmpty() && group.isEmpty() && !recursive 02525 && !permissionChange && !ACLChange && !defaultACLChange) 02526 return; 02527 02528 KIO::Job * job; 02529 if (files.count() > 0) { 02530 job = KIO::chmod( files, orFilePermissions, ~andFilePermissions, 02531 owner, group, false ); 02532 if ( ACLChange && d->fileSystemSupportsACLs ) 02533 job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" ); 02534 if ( defaultACLChange && d->fileSystemSupportsACLs ) 02535 job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" ); 02536 02537 connect( job, SIGNAL( result( KJob * ) ), 02538 SLOT( slotChmodResult( KJob * ) ) ); 02539 QEventLoop eventLoop; 02540 connect(this, SIGNAL(leaveModality()), 02541 &eventLoop, SLOT(quit())); 02542 eventLoop.exec(QEventLoop::ExcludeUserInputEvents); 02543 } 02544 if (dirs.count() > 0) { 02545 job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions, 02546 owner, group, recursive ); 02547 if ( ACLChange && d->fileSystemSupportsACLs ) 02548 job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" ); 02549 if ( defaultACLChange && d->fileSystemSupportsACLs ) 02550 job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" ); 02551 02552 connect( job, SIGNAL( result( KJob * ) ), 02553 SLOT( slotChmodResult( KJob * ) ) ); 02554 QEventLoop eventLoop; 02555 connect(this, SIGNAL(leaveModality()), 02556 &eventLoop, SLOT(quit())); 02557 eventLoop.exec(QEventLoop::ExcludeUserInputEvents); 02558 } 02559 } 02560 02561 void KFilePermissionsPropsPlugin::slotChmodResult( KJob * job ) 02562 { 02563 kDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult"; 02564 if (job->error()) 02565 job->uiDelegate()->showErrorMessage(); 02566 // allow apply() to return 02567 emit leaveModality(); 02568 } 02569 02570 02571 02572 02573 class KUrlPropsPlugin::KUrlPropsPluginPrivate 02574 { 02575 public: 02576 KUrlPropsPluginPrivate() 02577 { 02578 } 02579 ~KUrlPropsPluginPrivate() 02580 { 02581 } 02582 02583 QFrame *m_frame; 02584 KUrlRequester *URLEdit; 02585 QString URLStr; 02586 }; 02587 02588 KUrlPropsPlugin::KUrlPropsPlugin( KPropertiesDialog *_props ) 02589 : KPropertiesDialogPlugin( _props ),d(new KUrlPropsPluginPrivate) 02590 { 02591 d->m_frame = new QFrame(); 02592 properties->addPage(d->m_frame, i18n("U&RL")); 02593 QVBoxLayout *layout = new QVBoxLayout(d->m_frame); 02594 layout->setMargin(0); 02595 02596 QLabel *l; 02597 l = new QLabel( d->m_frame ); 02598 l->setObjectName( QLatin1String( "Label_1" ) ); 02599 l->setText( i18n("URL:") ); 02600 layout->addWidget(l, Qt::AlignRight); 02601 02602 d->URLEdit = new KUrlRequester( d->m_frame ); 02603 layout->addWidget(d->URLEdit); 02604 02605 KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties ); 02606 if (url.isLocalFile()) { 02607 QString path = url.toLocalFile(); 02608 02609 QFile f( path ); 02610 if ( !f.open( QIODevice::ReadOnly ) ) { 02611 return; 02612 } 02613 f.close(); 02614 02615 KDesktopFile config( path ); 02616 const KConfigGroup dg = config.desktopGroup(); 02617 d->URLStr = dg.readPathEntry( "URL", QString() ); 02618 02619 if (!d->URLStr.isEmpty()) { 02620 d->URLEdit->setUrl( KUrl(d->URLStr) ); 02621 } 02622 } 02623 02624 connect( d->URLEdit, SIGNAL( textChanged( const QString & ) ), 02625 this, SIGNAL( changed() ) ); 02626 02627 layout->addStretch (1); 02628 } 02629 02630 KUrlPropsPlugin::~KUrlPropsPlugin() 02631 { 02632 delete d; 02633 } 02634 02635 // QString KUrlPropsPlugin::tabName () const 02636 // { 02637 // return i18n ("U&RL"); 02638 // } 02639 02640 bool KUrlPropsPlugin::supports( const KFileItemList& _items ) 02641 { 02642 if ( _items.count() != 1 ) 02643 return false; 02644 const KFileItem item = _items.first(); 02645 // check if desktop file 02646 if (!item.isDesktopFile()) 02647 return false; 02648 02649 // open file and check type 02650 bool isLocal; 02651 KUrl url = item.mostLocalUrl(isLocal); 02652 if (!isLocal) { 02653 return false; 02654 } 02655 02656 KDesktopFile config( url.path() ); 02657 return config.hasLinkType(); 02658 } 02659 02660 void KUrlPropsPlugin::applyChanges() 02661 { 02662 KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties ); 02663 if (!url.isLocalFile()) { 02664 //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported.")); 02665 return; 02666 } 02667 02668 QString path = url.path(); 02669 02670 QFile f( path ); 02671 if ( !f.open( QIODevice::ReadWrite ) ) { 02672 KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have " 02673 "sufficient access to write to <b>%1</b>.</qt>", path)); 02674 return; 02675 } 02676 f.close(); 02677 02678 KDesktopFile config( path ); 02679 KConfigGroup dg = config.desktopGroup(); 02680 dg.writeEntry( "Type", QString::fromLatin1("Link")); 02681 dg.writePathEntry( "URL", d->URLEdit->url().url() ); 02682 // Users can't create a Link .desktop file with a Name field, 02683 // but distributions can. Update the Name field in that case. 02684 if ( dg.hasKey("Name") ) 02685 { 02686 QString nameStr = nameFromFileName(properties->kurl().fileName()); 02687 dg.writeEntry( "Name", nameStr ); 02688 dg.writeEntry( "Name", nameStr, KConfigBase::Persistent|KConfigBase::Localized ); 02689 02690 } 02691 } 02692 02693 02694 /* ---------------------------------------------------- 02695 * 02696 * KDevicePropsPlugin 02697 * 02698 * -------------------------------------------------- */ 02699 02700 class KDevicePropsPlugin::KDevicePropsPluginPrivate 02701 { 02702 public: 02703 KDevicePropsPluginPrivate() 02704 { 02705 } 02706 ~KDevicePropsPluginPrivate() 02707 { 02708 } 02709 02710 bool isMounted() const { 02711 const QString dev = device->currentText(); 02712 return !dev.isEmpty() && KMountPoint::currentMountPoints().findByDevice(dev); 02713 } 02714 02715 QFrame *m_frame; 02716 QStringList mountpointlist; 02717 QLabel *m_freeSpaceText; 02718 QLabel *m_freeSpaceLabel; 02719 QProgressBar *m_freeSpaceBar; 02720 02721 KComboBox* device; 02722 QLabel* mountpoint; 02723 QCheckBox* readonly; 02724 02725 QStringList m_devicelist; 02726 }; 02727 02728 KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KDevicePropsPluginPrivate) 02729 { 02730 d->m_frame = new QFrame(); 02731 properties->addPage(d->m_frame, i18n("De&vice")); 02732 02733 QStringList devices; 02734 const KMountPoint::List mountPoints = KMountPoint::possibleMountPoints(); 02735 02736 for(KMountPoint::List::ConstIterator it = mountPoints.begin(); 02737 it != mountPoints.end(); ++it) 02738 { 02739 const KMountPoint::Ptr mp = (*it); 02740 QString mountPoint = mp->mountPoint(); 02741 QString device = mp->mountedFrom(); 02742 kDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType(); 02743 02744 if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty() 02745 && device != "none") 02746 { 02747 devices.append( device + QString::fromLatin1(" (") 02748 + mountPoint + QString::fromLatin1(")") ); 02749 d->m_devicelist.append(device); 02750 d->mountpointlist.append(mountPoint); 02751 } 02752 } 02753 02754 QGridLayout *layout = new QGridLayout( d->m_frame ); 02755 02756 layout->setMargin(0); 02757 layout->setColumnStretch(1, 1); 02758 02759 QLabel* label; 02760 label = new QLabel( d->m_frame ); 02761 label->setText( devices.count() == 0 ? 02762 i18n("Device (/dev/fd0):") : // old style 02763 i18n("Device:") ); // new style (combobox) 02764 layout->addWidget(label, 0, 0, Qt::AlignRight); 02765 02766 d->device = new KComboBox( d->m_frame ); 02767 d->device->setObjectName( QLatin1String( "ComboBox_device" ) ); 02768 d->device->setEditable( true ); 02769 d->device->addItems( devices ); 02770 layout->addWidget(d->device, 0, 1); 02771 connect( d->device, SIGNAL( activated( int ) ), 02772 this, SLOT( slotActivated( int ) ) ); 02773 02774 d->readonly = new QCheckBox( d->m_frame ); 02775 d->readonly->setObjectName( QLatin1String( "CheckBox_readonly" ) ); 02776 d->readonly->setText( i18n("Read only") ); 02777 layout->addWidget(d->readonly, 1, 1); 02778 02779 label = new QLabel( d->m_frame ); 02780 label->setText( i18n("File system:") ); 02781 layout->addWidget(label, 2, 0, Qt::AlignRight); 02782 02783 QLabel *fileSystem = new QLabel( d->m_frame ); 02784 layout->addWidget(fileSystem, 2, 1); 02785 02786 label = new QLabel( d->m_frame ); 02787 label->setText( devices.count()==0 ? 02788 i18n("Mount point (/mnt/floppy):") : // old style 02789 i18n("Mount point:")); // new style (combobox) 02790 layout->addWidget(label, 3, 0, Qt::AlignRight); 02791 02792 d->mountpoint = new QLabel( d->m_frame ); 02793 d->mountpoint->setObjectName( QLatin1String( "LineEdit_mountpoint" ) ); 02794 02795 layout->addWidget(d->mountpoint, 3, 1); 02796 02797 // show disk free 02798 d->m_freeSpaceText = new QLabel(i18n("Device usage:"), d->m_frame ); 02799 layout->addWidget(d->m_freeSpaceText, 4, 0, Qt::AlignRight); 02800 02801 d->m_freeSpaceLabel = new QLabel( d->m_frame ); 02802 layout->addWidget( d->m_freeSpaceLabel, 4, 1 ); 02803 02804 d->m_freeSpaceBar = new QProgressBar( d->m_frame ); 02805 d->m_freeSpaceBar->setObjectName( "freeSpaceBar" ); 02806 layout->addWidget(d->m_freeSpaceBar, 5, 0, 1, 2); 02807 02808 // we show it in the slot when we know the values 02809 d->m_freeSpaceText->hide(); 02810 d->m_freeSpaceLabel->hide(); 02811 d->m_freeSpaceBar->hide(); 02812 02813 KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame); 02814 layout->addWidget(sep, 6, 0, 1, 2); 02815 02816 layout->setRowStretch(7, 1); 02817 02818 KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props ); 02819 if (!url.isLocalFile()) { 02820 return; 02821 } 02822 QString path = url.toLocalFile(); 02823 02824 QFile f( path ); 02825 if ( !f.open( QIODevice::ReadOnly ) ) 02826 return; 02827 f.close(); 02828 02829 const KDesktopFile _config( path ); 02830 const KConfigGroup config = _config.desktopGroup(); 02831 QString deviceStr = config.readEntry( "Dev" ); 02832 QString mountPointStr = config.readEntry( "MountPoint" ); 02833 bool ro = config.readEntry( "ReadOnly", false ); 02834 02835 fileSystem->setText(config.readEntry("FSType")); 02836 02837 d->device->setEditText( deviceStr ); 02838 if ( !deviceStr.isEmpty() ) { 02839 // Set default options for this device (first matching entry) 02840 int index = d->m_devicelist.indexOf(deviceStr); 02841 if (index != -1) 02842 { 02843 //kDebug(250) << "found it" << index; 02844 slotActivated( index ); 02845 } 02846 } 02847 02848 if ( !mountPointStr.isEmpty() ) 02849 { 02850 d->mountpoint->setText( mountPointStr ); 02851 updateInfo(); 02852 } 02853 02854 d->readonly->setChecked( ro ); 02855 02856 connect( d->device, SIGNAL( activated( int ) ), 02857 this, SIGNAL( changed() ) ); 02858 connect( d->device, SIGNAL( textChanged( const QString & ) ), 02859 this, SIGNAL( changed() ) ); 02860 connect( d->readonly, SIGNAL( toggled( bool ) ), 02861 this, SIGNAL( changed() ) ); 02862 02863 connect( d->device, SIGNAL( textChanged( const QString & ) ), 02864 this, SLOT( slotDeviceChanged() ) ); 02865 } 02866 02867 KDevicePropsPlugin::~KDevicePropsPlugin() 02868 { 02869 delete d; 02870 } 02871 02872 // QString KDevicePropsPlugin::tabName () const 02873 // { 02874 // return i18n ("De&vice"); 02875 // } 02876 02877 void KDevicePropsPlugin::updateInfo() 02878 { 02879 // we show it in the slot when we know the values 02880 d->m_freeSpaceText->hide(); 02881 d->m_freeSpaceLabel->hide(); 02882 d->m_freeSpaceBar->hide(); 02883 02884 if (!d->mountpoint->text().isEmpty() && d->isMounted()) { 02885 KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( d->mountpoint->text() ); 02886 slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024); 02887 } 02888 } 02889 02890 void KDevicePropsPlugin::slotActivated( int index ) 02891 { 02892 // index can be more than the number of known devices, when the user types 02893 // a "custom" device. 02894 if (index < d->m_devicelist.count()) { 02895 // Update mountpoint so that it matches the device that was selected in the combo 02896 d->device->setEditText(d->m_devicelist[index]); 02897 d->mountpoint->setText(d->mountpointlist[index]); 02898 } 02899 02900 updateInfo(); 02901 } 02902 02903 void KDevicePropsPlugin::slotDeviceChanged() 02904 { 02905 // Update mountpoint so that it matches the typed device 02906 int index = d->m_devicelist.indexOf( d->device->currentText() ); 02907 if ( index != -1 ) 02908 d->mountpoint->setText( d->mountpointlist[index] ); 02909 else 02910 d->mountpoint->setText( QString() ); 02911 02912 updateInfo(); 02913 } 02914 02915 void KDevicePropsPlugin::slotFoundMountPoint( const QString&, 02916 quint64 kibSize, 02917 quint64 /*kibUsed*/, 02918 quint64 kibAvail ) 02919 { 02920 d->m_freeSpaceText->show(); 02921 d->m_freeSpaceLabel->show(); 02922 02923 const int percUsed = kibSize != 0 ? (100 - (int)(100.0 * kibAvail / kibSize)) : 100; 02924 02925 d->m_freeSpaceLabel->setText( 02926 i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)", 02927 KIO::convertSizeFromKiB(kibAvail), 02928 KIO::convertSizeFromKiB(kibSize), 02929 percUsed )); 02930 02931 d->m_freeSpaceBar->setRange(0, 100); 02932 d->m_freeSpaceBar->setValue(percUsed); 02933 d->m_freeSpaceBar->show(); 02934 } 02935 02936 bool KDevicePropsPlugin::supports( const KFileItemList& _items ) 02937 { 02938 if ( _items.count() != 1 ) 02939 return false; 02940 const KFileItem item = _items.first(); 02941 // check if desktop file 02942 if (!item.isDesktopFile()) 02943 return false; 02944 02945 // open file and check type 02946 bool isLocal; 02947 KUrl url = item.mostLocalUrl(isLocal); 02948 if (!isLocal) { 02949 return false; 02950 } 02951 02952 KDesktopFile config( url.path() ); 02953 return config.hasDeviceType(); 02954 } 02955 02956 void KDevicePropsPlugin::applyChanges() 02957 { 02958 KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties ); 02959 if ( !url.isLocalFile() ) 02960 return; 02961 QString path = url.toLocalFile(); 02962 02963 QFile f( path ); 02964 if ( !f.open( QIODevice::ReadWrite ) ) 02965 { 02966 KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient " 02967 "access to write to <b>%1</b>.</qt>", path)); 02968 return; 02969 } 02970 f.close(); 02971 02972 KDesktopFile _config( path ); 02973 KConfigGroup config = _config.desktopGroup(); 02974 config.writeEntry( "Type", QString::fromLatin1("FSDevice") ); 02975 02976 config.writeEntry( "Dev", d->device->currentText() ); 02977 config.writeEntry( "MountPoint", d->mountpoint->text() ); 02978 02979 config.writeEntry( "ReadOnly", d->readonly->isChecked() ); 02980 02981 config.sync(); 02982 } 02983 02984 02985 /* ---------------------------------------------------- 02986 * 02987 * KDesktopPropsPlugin 02988 * 02989 * -------------------------------------------------- */ 02990 02991 class KDesktopPropsPlugin::KDesktopPropsPluginPrivate 02992 { 02993 public: 02994 KDesktopPropsPluginPrivate() 02995 : w( new Ui_KPropertiesDesktopBase ) 02996 , m_frame( new QFrame() ) 02997 { 02998 } 02999 ~KDesktopPropsPluginPrivate() 03000 { 03001 delete w; 03002 } 03003 Ui_KPropertiesDesktopBase* w; 03004 QWidget *m_frame; 03005 03006 QString m_origCommandStr; 03007 QString m_terminalOptionStr; 03008 QString m_suidUserStr; 03009 QString m_dbusStartupType; 03010 QString m_dbusServiceName; 03011 bool m_terminalBool; 03012 bool m_suidBool; 03013 bool m_startupBool; 03014 bool m_systrayBool; 03015 }; 03016 03017 KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props ) 03018 : KPropertiesDialogPlugin( _props ), d( new KDesktopPropsPluginPrivate ) 03019 { 03020 d->w->setupUi(d->m_frame); 03021 03022 properties->addPage(d->m_frame, i18n("&Application")); 03023 03024 bool bKDesktopMode = properties->kurl().protocol() == QLatin1String("desktop") || 03025 properties->currentDir().protocol() == QLatin1String("desktop"); 03026 03027 if (bKDesktopMode) 03028 { 03029 // Hide Name entry 03030 d->w->nameEdit->hide(); 03031 d->w->nameLabel->hide(); 03032 } 03033 03034 d->w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly); 03035 d->w->pathEdit->lineEdit()->setAcceptDrops(false); 03036 03037 connect( d->w->nameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); 03038 connect( d->w->genNameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); 03039 connect( d->w->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); 03040 connect( d->w->commandEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); 03041 connect( d->w->pathEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) ); 03042 03043 connect( d->w->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) ); 03044 connect( d->w->addFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotAddFiletype() ) ); 03045 connect( d->w->delFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotDelFiletype() ) ); 03046 connect( d->w->advancedButton, SIGNAL( clicked() ), this, SLOT( slotAdvanced() ) ); 03047 03048 // now populate the page 03049 03050 KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props ); 03051 if (!url.isLocalFile()) { 03052 return; 03053 } 03054 QString path = url.toLocalFile(); 03055 03056 QFile f( path ); 03057 if ( !f.open( QIODevice::ReadOnly ) ) 03058 return; 03059 f.close(); 03060 03061 KDesktopFile _config( path ); 03062 KConfigGroup config = _config.desktopGroup(); 03063 QString nameStr = _config.readName(); 03064 QString genNameStr = _config.readGenericName(); 03065 QString commentStr = _config.readComment(); 03066 QString commandStr = config.readEntry( "Exec", QString() ); 03067 if (commandStr.startsWith(QLatin1String("ksystraycmd "))) 03068 { 03069 commandStr.remove(0, 12); 03070 d->m_systrayBool = true; 03071 } 03072 else 03073 d->m_systrayBool = false; 03074 03075 d->m_origCommandStr = commandStr; 03076 QString pathStr = config.readEntry( "Path", QString() ); // not readPathEntry, see kservice.cpp 03077 d->m_terminalBool = config.readEntry( "Terminal", false ); 03078 d->m_terminalOptionStr = config.readEntry( "TerminalOptions" ); 03079 d->m_suidBool = config.readEntry( "X-KDE-SubstituteUID", false ); 03080 d->m_suidUserStr = config.readEntry( "X-KDE-Username" ); 03081 if( config.hasKey( "StartupNotify" )) 03082 d->m_startupBool = config.readEntry( "StartupNotify", true ); 03083 else 03084 d->m_startupBool = config.readEntry( "X-KDE-StartupNotify", true ); 03085 d->m_dbusStartupType = config.readEntry("X-DBUS-StartupType").toLower(); 03086 // ### should there be a GUI for this setting? 03087 // At least we're copying it over to the local file, to avoid side effects (#157853) 03088 d->m_dbusServiceName = config.readEntry("X-DBUS-ServiceName"); 03089 03090 const QStringList mimeTypes = config.readXdgListEntry( "MimeType" ); 03091 03092 if ( nameStr.isEmpty() || bKDesktopMode ) { 03093 // We'll use the file name if no name is specified 03094 // because we _need_ a Name for a valid file. 03095 // But let's do it in apply, not here, so that we pick up the right name. 03096 setDirty(); 03097 } 03098 if ( !bKDesktopMode ) 03099 d->w->nameEdit->setText(nameStr); 03100 03101 d->w->genNameEdit->setText( genNameStr ); 03102 d->w->commentEdit->setText( commentStr ); 03103 d->w->commandEdit->setText( commandStr ); 03104 d->w->pathEdit->lineEdit()->setText( pathStr ); 03105 03106 // was: d->w->filetypeList->setFullWidth(true); 03107 // d->w->filetypeList->header()->setStretchEnabled(true, d->w->filetypeList->columns()-1); 03108 03109 KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr(); 03110 for(QStringList::ConstIterator it = mimeTypes.begin(); 03111 it != mimeTypes.end(); ) 03112 { 03113 KMimeType::Ptr p = KMimeType::mimeType(*it, KMimeType::ResolveAliases); 03114 ++it; 03115 QString preference; 03116 if (it != mimeTypes.end()) 03117 { 03118 bool numeric; 03119 (*it).toInt(&numeric); 03120 if (numeric) 03121 { 03122 preference = *it; 03123 ++it; 03124 } 03125 } 03126 if (p) 03127 { 03128 QTreeWidgetItem *item = new QTreeWidgetItem(); 03129 item->setText(0, p->name()); 03130 item->setText(1, p->comment()); 03131 item->setText(2, preference); 03132 d->w->filetypeList->addTopLevelItem(item); 03133 } 03134 } 03135 d->w->filetypeList->resizeColumnToContents(0); 03136 03137 } 03138 03139 KDesktopPropsPlugin::~KDesktopPropsPlugin() 03140 { 03141 delete d; 03142 } 03143 03144 void KDesktopPropsPlugin::slotAddFiletype() 03145 { 03146 KMimeTypeChooserDialog dlg( i18n("Add File Type for %1", properties->kurl().fileName()), 03147 i18n("Select one or more file types to add:"), 03148 QStringList(), // no preselected mimetypes 03149 QString(), 03150 QStringList(), 03151 KMimeTypeChooser::Comments|KMimeTypeChooser::Patterns, 03152 d->m_frame ); 03153 03154 if (dlg.exec() == KDialog::Accepted) 03155 { 03156 foreach(const QString &mimetype, dlg.chooser()->mimeTypes()) 03157 { 03158 KMimeType::Ptr p = KMimeType::mimeType(mimetype); 03159 if (!p) 03160 continue; 03161 03162 bool found = false; 03163 int count = d->w->filetypeList->topLevelItemCount(); 03164 for (int i = 0; !found && i < count; ++i) { 03165 if (d->w->filetypeList->topLevelItem(i)->text(0) == mimetype) { 03166 found = true; 03167 } 03168 } 03169 if (!found) { 03170 QTreeWidgetItem *item = new QTreeWidgetItem(); 03171 item->setText(0, p->name()); 03172 item->setText(1, p->comment()); 03173 d->w->filetypeList->addTopLevelItem(item); 03174 } 03175 d->w->filetypeList->resizeColumnToContents(0); 03176 } 03177 } 03178 emit changed(); 03179 } 03180 03181 void KDesktopPropsPlugin::slotDelFiletype() 03182 { 03183 QTreeWidgetItem *cur = d->w->filetypeList->currentItem(); 03184 if (cur) { 03185 delete cur; 03186 emit changed(); 03187 } 03188 } 03189 03190 void KDesktopPropsPlugin::checkCommandChanged() 03191 { 03192 if (KRun::binaryName(d->w->commandEdit->text(), true) != 03193 KRun::binaryName(d->m_origCommandStr, true)) 03194 { 03195 d->m_origCommandStr = d->w->commandEdit->text(); 03196 d->m_dbusStartupType.clear(); // Reset 03197 d->m_dbusServiceName.clear(); 03198 } 03199 } 03200 03201 void KDesktopPropsPlugin::applyChanges() 03202 { 03203 kDebug(250) << "KDesktopPropsPlugin::applyChanges"; 03204 03205 KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties ); 03206 if (!url.isLocalFile()) { 03207 //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported.")); 03208 return; 03209 } 03210 QString path = url.toLocalFile(); 03211 03212 QFile f( path ); 03213 03214 if ( !f.open( QIODevice::ReadWrite ) ) { 03215 KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have " 03216 "sufficient access to write to <b>%1</b>.</qt>", path)); 03217 return; 03218 } 03219 f.close(); 03220 03221 // If the command is changed we reset certain settings that are strongly 03222 // coupled to the command. 03223 checkCommandChanged(); 03224 03225 KDesktopFile _config( path ); 03226 KConfigGroup config = _config.desktopGroup(); 03227 config.writeEntry( "Type", QString::fromLatin1("Application")); 03228 config.writeEntry( "Comment", d->w->commentEdit->text() ); 03229 config.writeEntry( "Comment", d->w->commentEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat 03230 config.writeEntry( "GenericName", d->w->genNameEdit->text() ); 03231 config.writeEntry( "GenericName", d->w->genNameEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat 03232 03233 if (d->m_systrayBool) 03234 config.writeEntry( "Exec", d->w->commandEdit->text().prepend("ksystraycmd ") ); 03235 else 03236 config.writeEntry( "Exec", d->w->commandEdit->text() ); 03237 config.writeEntry( "Path", d->w->pathEdit->lineEdit()->text() ); // not writePathEntry, see kservice.cpp 03238 03239 // Write mimeTypes 03240 QStringList mimeTypes; 03241 int count = d->w->filetypeList->topLevelItemCount(); 03242 for (int i = 0; i < count; ++i) { 03243 QTreeWidgetItem *item = d->w->filetypeList->topLevelItem(i); 03244 QString preference = item->text(2); 03245 mimeTypes.append(item->text(0)); 03246 if (!preference.isEmpty()) 03247 mimeTypes.append(preference); 03248 } 03249 03250 kDebug() << mimeTypes; 03251 config.writeXdgListEntry( "MimeType", mimeTypes ); 03252 03253 if ( !d->w->nameEdit->isHidden() ) { 03254 QString nameStr = d->w->nameEdit->text(); 03255 config.writeEntry( "Name", nameStr ); 03256 config.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized ); 03257 } 03258 03259 config.writeEntry("Terminal", d->m_terminalBool); 03260 config.writeEntry("TerminalOptions", d->m_terminalOptionStr); 03261 config.writeEntry("X-KDE-SubstituteUID", d->m_suidBool); 03262 config.writeEntry("X-KDE-Username", d->m_suidUserStr); 03263 config.writeEntry("StartupNotify", d->m_startupBool); 03264 config.writeEntry("X-DBUS-StartupType", d->m_dbusStartupType); 03265 config.writeEntry("X-DBUS-ServiceName", d->m_dbusServiceName); 03266 config.sync(); 03267 03268 // KSycoca update needed? 03269 QString sycocaPath = KGlobal::dirs()->relativeLocation("apps", path); 03270 bool updateNeeded = !sycocaPath.startsWith('/'); 03271 if (!updateNeeded) 03272 { 03273 sycocaPath = KGlobal::dirs()->relativeLocation("xdgdata-apps", path); 03274 updateNeeded = !sycocaPath.startsWith('/'); 03275 } 03276 if (updateNeeded) 03277 KBuildSycocaProgressDialog::rebuildKSycoca(d->m_frame); 03278 } 03279 03280 03281 void KDesktopPropsPlugin::slotBrowseExec() 03282 { 03283 KUrl f = KFileDialog::getOpenUrl( KUrl(), 03284 QString(), d->m_frame ); 03285 if ( f.isEmpty() ) 03286 return; 03287 03288 if ( !f.isLocalFile()) { 03289 KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported.")); 03290 return; 03291 } 03292 03293 QString path = f.toLocalFile(); 03294 path = KShell::quoteArg( path ); 03295 d->w->commandEdit->setText( path ); 03296 } 03297 03298 void KDesktopPropsPlugin::slotAdvanced() 03299 { 03300 KDialog dlg( d->m_frame ); 03301 dlg.setObjectName( "KPropertiesDesktopAdv" ); 03302 dlg.setModal( true ); 03303 dlg.setCaption( i18n("Advanced Options for %1", properties->kurl().fileName()) ); 03304 dlg.setButtons( KDialog::Ok | KDialog::Cancel ); 03305 dlg.setDefaultButton( KDialog::Ok ); 03306 Ui_KPropertiesDesktopAdvBase w; 03307 w.setupUi(dlg.mainWidget()); 03308 03309 // If the command is changed we reset certain settings that are strongly 03310 // coupled to the command. 03311 checkCommandChanged(); 03312 03313 // check to see if we use konsole if not do not add the nocloseonexit 03314 // because we don't know how to do this on other terminal applications 03315 KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") ); 03316 QString preferredTerminal = confGroup.readPathEntry("TerminalApplication", 03317 QString::fromLatin1("konsole")); 03318 03319 bool terminalCloseBool = false; 03320 03321 if (preferredTerminal == "konsole") 03322 { 03323 terminalCloseBool = (d->m_terminalOptionStr.contains( "--noclose" ) > 0); 03324 w.terminalCloseCheck->setChecked(terminalCloseBool); 03325 d->m_terminalOptionStr.remove( "--noclose"); 03326 } 03327 else 03328 { 03329 w.terminalCloseCheck->hide(); 03330 } 03331 03332 w.terminalCheck->setChecked(d->m_terminalBool); 03333 w.terminalEdit->setText(d->m_terminalOptionStr); 03334 w.terminalCloseCheck->setEnabled(d->m_terminalBool); 03335 w.terminalEdit->setEnabled(d->m_terminalBool); 03336 w.terminalEditLabel->setEnabled(d->m_terminalBool); 03337 03338 w.suidCheck->setChecked(d->m_suidBool); 03339 w.suidEdit->setText(d->m_suidUserStr); 03340 w.suidEdit->setEnabled(d->m_suidBool); 03341 w.suidEditLabel->setEnabled(d->m_suidBool); 03342 03343 w.startupInfoCheck->setChecked(d->m_startupBool); 03344 w.systrayCheck->setChecked(d->m_systrayBool); 03345 03346 if (d->m_dbusStartupType == "unique") 03347 w.dbusCombo->setCurrentIndex(2); 03348 else if (d->m_dbusStartupType == "multi") 03349 w.dbusCombo->setCurrentIndex(1); 03350 else if (d->m_dbusStartupType == "wait") 03351 w.dbusCombo->setCurrentIndex(3); 03352 else 03353 w.dbusCombo->setCurrentIndex(0); 03354 03355 // Provide username completion up to 1000 users. 03356 KCompletion *kcom = new KCompletion; 03357 kcom->setOrder(KCompletion::Sorted); 03358 struct passwd *pw; 03359 int i, maxEntries = 1000; 03360 setpwent(); 03361 for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++) 03362 kcom->addItem(QString::fromLatin1(pw->pw_name)); 03363 endpwent(); 03364 if (i < maxEntries) 03365 { 03366 w.suidEdit->setCompletionObject(kcom, true); 03367 w.suidEdit->setAutoDeleteCompletionObject( true ); 03368 w.suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto); 03369 } 03370 else 03371 { 03372 delete kcom; 03373 } 03374 03375 connect( w.terminalEdit, SIGNAL( textChanged( const QString & ) ), 03376 this, SIGNAL( changed() ) ); 03377 connect( w.terminalCloseCheck, SIGNAL( toggled( bool ) ), 03378 this, SIGNAL( changed() ) ); 03379 connect( w.terminalCheck, SIGNAL( toggled( bool ) ), 03380 this, SIGNAL( changed() ) ); 03381 connect( w.suidCheck, SIGNAL( toggled( bool ) ), 03382 this, SIGNAL( changed() ) ); 03383 connect( w.suidEdit, SIGNAL( textChanged( const QString & ) ), 03384 this, SIGNAL( changed() ) ); 03385 connect( w.startupInfoCheck, SIGNAL( toggled( bool ) ), 03386 this, SIGNAL( changed() ) ); 03387 connect( w.systrayCheck, SIGNAL( toggled( bool ) ), 03388 this, SIGNAL( changed() ) ); 03389 connect( w.dbusCombo, SIGNAL( activated( int ) ), 03390 this, SIGNAL( changed() ) ); 03391 03392 if ( dlg.exec() == QDialog::Accepted ) 03393 { 03394 d->m_terminalOptionStr = w.terminalEdit->text().trimmed(); 03395 d->m_terminalBool = w.terminalCheck->isChecked(); 03396 d->m_suidBool = w.suidCheck->isChecked(); 03397 d->m_suidUserStr = w.suidEdit->text().trimmed(); 03398 d->m_startupBool = w.startupInfoCheck->isChecked(); 03399 d->m_systrayBool = w.systrayCheck->isChecked(); 03400 03401 if (w.terminalCloseCheck->isChecked()) 03402 { 03403 d->m_terminalOptionStr.append(" --noclose"); 03404 } 03405 03406 switch(w.dbusCombo->currentIndex()) 03407 { 03408 case 1: d->m_dbusStartupType = "multi"; break; 03409 case 2: d->m_dbusStartupType = "unique"; break; 03410 case 3: d->m_dbusStartupType = "wait"; break; 03411 default: d->m_dbusStartupType = "none"; break; 03412 } 03413 } 03414 } 03415 03416 bool KDesktopPropsPlugin::supports( const KFileItemList& _items ) 03417 { 03418 if ( _items.count() != 1 ) { 03419 return false; 03420 } 03421 03422 const KFileItem item = _items.first(); 03423 03424 // check if desktop file 03425 if (!item.isDesktopFile()) { 03426 return false; 03427 } 03428 03429 // open file and check type 03430 bool isLocal; 03431 KUrl url = item.mostLocalUrl( isLocal ); 03432 if (!isLocal) { 03433 return false; 03434 } 03435 03436 KDesktopFile config( url.path() ); 03437 return config.hasApplicationType() && 03438 KAuthorized::authorize("run_desktop_files") && 03439 KAuthorized::authorize("shell_access"); 03440 } 03441 03442 #include "kpropertiesdialog.moc" 03443 #include "kpropertiesdialog_p.moc" 03444
KDE 4.6 API Reference