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