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