fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kio / kfile / kpropertiesdialog.cpp
blobc642256f47278f8cf9dafc24d8450c52ac8e692a
1 /* This file is part of the KDE project
3 Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
4 Copyright (c) 1999, 2000 Preston Brown <pbrown@kde.org>
5 Copyright (c) 2000 Simon Hausmann <hausmann@kde.org>
6 Copyright (c) 2000 David Faure <faure@kde.org>
7 Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public License
20 along with this library; see the file COPYING.LIB. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA.
26 * kpropertiesdialog.cpp
27 * View/Edit Properties of files, locally or remotely
29 * some FilePermissionsPropsPlugin-changes by
30 * Henner Zeller <zeller@think.de>
31 * some layout management by
32 * Bertrand Leconte <B.Leconte@mail.dotcom.fr>
33 * the rest of the layout management, bug fixes, adaptation to libkio,
34 * template feature by
35 * David Faure <faure@kde.org>
36 * More layout, cleanups, and fixes by
37 * Preston Brown <pbrown@kde.org>
38 * Plugin capability, cleanups and port to KDialog by
39 * Simon Hausmann <hausmann@kde.org>
40 * KDesktopPropsPlugin by
41 * Waldo Bastian <bastian@kde.org>
44 #include "kpropertiesdialog.h"
45 #include "kpropertiesdialog_p.h"
48 #include <config.h>
49 #include <config-acl.h>
50 extern "C" {
51 #include <pwd.h>
52 #include <grp.h>
53 #include <time.h>
54 #include <sys/stat.h>
55 #include <sys/types.h>
57 #include <unistd.h>
58 #include <errno.h>
59 #include <assert.h>
60 #include <algorithm>
61 #include <functional>
63 #include <QtCore/QFile>
64 #include <QtCore/QDir>
65 #include <QtGui/QLabel>
66 #include <QtGui/QPushButton>
67 #include <QtGui/QCheckBox>
68 #include <QtCore/QMutableStringListIterator>
69 #include <QtCore/QTextIStream>
70 #include <QtGui/QPainter>
71 #include <QtGui/QLayout>
72 #include <QtGui/QStyle>
73 #include <QtGui/QProgressBar>
74 #include <QVector>
76 #ifdef HAVE_POSIX_ACL
77 extern "C" {
78 # include <sys/xattr.h>
80 #endif
82 #include <kauthorized.h>
83 #include <kdialog.h>
84 #include <kdirwatch.h>
85 #include <kdirnotify.h>
86 #include <kdiskfreespaceinfo.h>
87 #include <kdebug.h>
88 #include <kdesktopfile.h>
89 #include <kicondialog.h>
90 #include <kurl.h>
91 #include <kurlrequester.h>
92 #include <klocale.h>
93 #include <kglobal.h>
94 #include <kglobalsettings.h>
95 #include <kstandarddirs.h>
96 #include <kjobuidelegate.h>
97 #include <kio/job.h>
98 #include <kio/copyjob.h>
99 #include <kio/chmodjob.h>
100 #include <kio/directorysizejob.h>
101 #include <kio/renamedialog.h>
102 #include <kio/netaccess.h>
103 #include <kio/jobuidelegate.h>
104 #include <kfiledialog.h>
105 #include <kmimetype.h>
106 #include <kmountpoint.h>
107 #include <kiconloader.h>
108 #include <kmessagebox.h>
109 #include <kservice.h>
110 #include <kcombobox.h>
111 #include <kcompletion.h>
112 #include <klineedit.h>
113 #include <kseparator.h>
114 #include <ksqueezedtextlabel.h>
115 #include <klibloader.h>
116 #include <kmimetypetrader.h>
117 #include <kmetaprops.h>
118 #include <kpreviewprops.h>
119 #include <krun.h>
120 #include <kvbox.h>
121 #include <kacl.h>
122 #include <kconfiggroup.h>
123 #include <kshell.h>
124 #include <kcapacitybar.h>
125 #ifndef Q_OS_WIN
126 #include "kfilesharedialog.h"
127 #endif
129 #include "ui_kpropertiesdesktopbase.h"
130 #include "ui_kpropertiesdesktopadvbase.h"
131 #ifdef HAVE_POSIX_ACL
132 #include "kacleditwidget.h"
133 #endif
135 #include <kbuildsycocaprogressdialog.h>
136 #include <kmimetypechooser.h>
138 #ifdef Q_WS_WIN
139 # include <kkernel_win.h>
140 #ifdef __GNUC__
141 # warning TODO: port completely to win32
142 #endif
143 #endif
145 using namespace KDEPrivate;
147 static QString nameFromFileName(QString nameStr)
149 if ( nameStr.endsWith(".desktop") )
150 nameStr.truncate( nameStr.length() - 8 );
151 if ( nameStr.endsWith(".kdelnk") )
152 nameStr.truncate( nameStr.length() - 7 );
153 // Make it human-readable (%2F => '/', ...)
154 nameStr = KIO::decodeFileName( nameStr );
155 return nameStr;
158 mode_t KFilePermissionsPropsPlugin::fperm[3][4] = {
159 {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
160 {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
161 {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
164 class KPropertiesDialog::KPropertiesDialogPrivate
166 public:
167 KPropertiesDialogPrivate(KPropertiesDialog *qq)
169 q = qq;
170 m_aborted = false;
171 fileSharePage = 0;
173 ~KPropertiesDialogPrivate()
178 * Common initialization for all constructors
180 void init();
182 * Inserts all pages in the dialog.
184 void insertPages();
186 KPropertiesDialog *q;
187 bool m_aborted:1;
188 QWidget* fileSharePage;
190 * The URL of the props dialog (when shown for only one file)
192 KUrl m_singleUrl;
194 * List of items this props dialog is shown for
196 KFileItemList m_items;
198 * For templates
200 QString m_defaultName;
201 KUrl m_currentDir;
203 * List of all plugins inserted ( first one first )
205 QList<KPropertiesDialogPlugin*> m_pageList;
208 KPropertiesDialog::KPropertiesDialog (const KFileItem& item,
209 QWidget* parent)
210 : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
212 setCaption( i18n( "Properties for %1" , KIO::decodeFileName(item.url().fileName())) );
214 assert( !item.isNull() );
215 d->m_items.append(item);
217 d->m_singleUrl = item.url();
218 assert(!d->m_singleUrl.isEmpty());
220 d->init();
223 KPropertiesDialog::KPropertiesDialog (const QString& title,
224 QWidget* parent)
225 : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
227 setCaption( i18n( "Properties for %1", title ) );
229 d->init();
232 KPropertiesDialog::KPropertiesDialog(const KFileItemList& _items,
233 QWidget* parent)
234 : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
236 if ( _items.count() > 1 )
237 setCaption( i18np( "Properties for 1 item", "Properties for %1 Selected Items", _items.count() ) );
238 else
239 setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_items.first().url().fileName())) );
241 assert( !_items.isEmpty() );
242 d->m_singleUrl = _items.first().url();
243 assert(!d->m_singleUrl.isEmpty());
245 d->m_items = _items;
247 d->init();
250 KPropertiesDialog::KPropertiesDialog (const KUrl& _url,
251 QWidget* parent)
252 : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
254 setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_url.fileName())) );
256 d->m_singleUrl = _url;
258 KIO::UDSEntry entry;
259 KIO::NetAccess::stat(_url, entry, parent);
261 d->m_items.append(KFileItem(entry, _url));
262 d->init();
265 KPropertiesDialog::KPropertiesDialog (const KUrl& _tempUrl, const KUrl& _currentDir,
266 const QString& _defaultName,
267 QWidget* parent)
268 : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
270 setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_tempUrl.fileName())) );
272 d->m_singleUrl = _tempUrl;
273 d->m_defaultName = _defaultName;
274 d->m_currentDir = _currentDir;
275 assert(!d->m_singleUrl.isEmpty());
277 // Create the KFileItem for the _template_ file, in order to read from it.
278 d->m_items.append(KFileItem(KFileItem::Unknown, KFileItem::Unknown, d->m_singleUrl));
279 d->init();
282 bool KPropertiesDialog::showDialog(const KFileItem& item, QWidget* parent,
283 bool modal)
285 // TODO: do we really want to show the win32 property dialog?
286 // This means we lose metainfo, support for .desktop files, etc. (DF)
287 #ifdef Q_WS_WIN
288 QString localPath = item.localPath();
289 if (!localPath.isEmpty())
290 return showWin32FilePropertyDialog(localPath);
291 #endif
292 KPropertiesDialog* dlg = new KPropertiesDialog(item, parent);
293 if (modal) {
294 dlg->exec();
295 } else {
296 dlg->show();
299 return true;
302 bool KPropertiesDialog::showDialog(const KUrl& _url, QWidget* parent,
303 bool modal)
305 #ifdef Q_WS_WIN
306 if (_url.isLocalFile())
307 return showWin32FilePropertyDialog( _url.toLocalFile() );
308 #endif
309 KPropertiesDialog* dlg = new KPropertiesDialog(_url, parent);
310 if (modal) {
311 dlg->exec();
312 } else {
313 dlg->show();
316 return true;
319 bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent,
320 bool modal)
322 if (_items.count()==1) {
323 const KFileItem item = _items.first();
324 if (item.entry().count() == 0 && item.localPath().isEmpty()) // this remote item wasn't listed by a slave
325 // Let's stat to get more info on the file
326 return KPropertiesDialog::showDialog(item.url(), parent, modal);
327 else
328 return KPropertiesDialog::showDialog(_items.first(), parent, modal);
330 KPropertiesDialog* dlg = new KPropertiesDialog(_items, parent);
331 if (modal) {
332 dlg->exec();
333 } else {
334 dlg->show();
336 return true;
339 void KPropertiesDialog::KPropertiesDialogPrivate::init()
341 q->setFaceType(KPageDialog::Tabbed);
342 q->setButtons(KDialog::Ok | KDialog::Cancel);
343 q->setDefaultButton(KDialog::Ok);
345 connect(q, SIGNAL(okClicked()), q, SLOT(slotOk()));
346 connect(q, SIGNAL(cancelClicked()), q, SLOT(slotCancel()));
348 insertPages();
350 KConfigGroup group(KGlobal::config(), "KPropertiesDialog");
351 q->restoreDialogSize(group);
354 void KPropertiesDialog::showFileSharingPage()
356 if (d->fileSharePage) {
357 // FIXME: this showFileSharingPage thingy looks broken! (tokoe)
358 // showPage( pageIndex( d->fileSharePage));
362 void KPropertiesDialog::setFileSharingPage(QWidget* page) {
363 d->fileSharePage = page;
367 void KPropertiesDialog::setFileNameReadOnly( bool ro )
369 foreach(KPropertiesDialogPlugin *it, d->m_pageList) {
370 KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it);
371 if ( plugin ) {
372 plugin->setFileNameReadOnly( ro );
373 break;
378 KPropertiesDialog::~KPropertiesDialog()
380 qDeleteAll(d->m_pageList);
381 delete d;
383 KConfigGroup group(KGlobal::config(), "KPropertiesDialog");
384 saveDialogSize(group, KConfigBase::Persistent);
387 void KPropertiesDialog::insertPlugin (KPropertiesDialogPlugin* plugin)
389 connect (plugin, SIGNAL (changed ()),
390 plugin, SLOT (setDirty ()));
392 d->m_pageList.append(plugin);
395 KUrl KPropertiesDialog::kurl() const
397 return d->m_singleUrl;
400 KFileItem& KPropertiesDialog::item()
402 return d->m_items.first();
405 KFileItemList KPropertiesDialog::items() const
407 return d->m_items;
410 KUrl KPropertiesDialog::currentDir() const
412 return d->m_currentDir;
415 QString KPropertiesDialog::defaultName() const
417 return d->m_defaultName;
420 bool KPropertiesDialog::canDisplay( const KFileItemList& _items )
422 // TODO: cache the result of those calls. Currently we parse .desktop files far too many times
423 return KFilePropsPlugin::supports( _items ) ||
424 KFilePermissionsPropsPlugin::supports( _items ) ||
425 KDesktopPropsPlugin::supports( _items ) ||
426 KUrlPropsPlugin::supports( _items ) ||
427 KDevicePropsPlugin::supports( _items ) ||
428 KFileMetaPropsPlugin::supports( _items ) ||
429 KPreviewPropsPlugin::supports( _items );
432 void KPropertiesDialog::slotOk()
434 QList<KPropertiesDialogPlugin*>::const_iterator pageListIt;
435 d->m_aborted = false;
437 KFilePropsPlugin * filePropsPlugin = 0L;
438 if (qobject_cast<KFilePropsPlugin*>(d->m_pageList.first()))
439 filePropsPlugin = static_cast<KFilePropsPlugin *>(d->m_pageList.first());
441 // If any page is dirty, then set the main one (KFilePropsPlugin) as
442 // dirty too. This is what makes it possible to save changes to a global
443 // desktop file into a local one. In other cases, it doesn't hurt.
444 for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd(); ++pageListIt) {
445 if ( (*pageListIt)->isDirty() && filePropsPlugin )
447 filePropsPlugin->setDirty();
448 break;
452 // Apply the changes in the _normal_ order of the tabs now
453 // This is because in case of renaming a file, KFilePropsPlugin will call
454 // KPropertiesDialog::rename, so other tab will be ok with whatever order
455 // BUT for file copied from templates, we need to do the renaming first !
456 for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd() && !d->m_aborted; ++pageListIt) {
457 if ( (*pageListIt)->isDirty() )
459 kDebug( 250 ) << "applying changes for " << (*pageListIt)->metaObject()->className();
460 (*pageListIt)->applyChanges();
461 // applyChanges may change d->m_aborted.
463 else {
464 kDebug( 250 ) << "skipping page " << (*pageListIt)->metaObject()->className();
468 if ( !d->m_aborted && filePropsPlugin )
469 filePropsPlugin->postApplyChanges();
471 if ( !d->m_aborted )
473 emit applied();
474 emit propertiesClosed();
475 deleteLater(); // somewhat like Qt::WA_DeleteOnClose would do.
476 accept();
477 } // else, keep dialog open for user to fix the problem.
480 void KPropertiesDialog::slotCancel()
482 emit canceled();
483 emit propertiesClosed();
485 deleteLater();
486 done( Rejected );
489 void KPropertiesDialog::KPropertiesDialogPrivate::insertPages()
491 if (m_items.isEmpty())
492 return;
494 if ( KFilePropsPlugin::supports( m_items ) )
496 KPropertiesDialogPlugin *p = new KFilePropsPlugin(q);
497 q->insertPlugin(p);
500 if ( KFilePermissionsPropsPlugin::supports( m_items ) )
502 KPropertiesDialogPlugin *p = new KFilePermissionsPropsPlugin(q);
503 q->insertPlugin(p);
506 if ( KDesktopPropsPlugin::supports( m_items ) )
508 KPropertiesDialogPlugin *p = new KDesktopPropsPlugin(q);
509 q->insertPlugin(p);
512 if ( KUrlPropsPlugin::supports( m_items ) )
514 KPropertiesDialogPlugin *p = new KUrlPropsPlugin(q);
515 q->insertPlugin(p);
518 if ( KDevicePropsPlugin::supports( m_items ) )
520 KPropertiesDialogPlugin *p = new KDevicePropsPlugin(q);
521 q->insertPlugin(p);
524 if ( KFileMetaPropsPlugin::supports( m_items ) )
526 KPropertiesDialogPlugin *p = new KFileMetaPropsPlugin(q);
527 q->insertPlugin(p);
530 if ( KPreviewPropsPlugin::supports( m_items ) )
532 KPropertiesDialogPlugin *p = new KPreviewPropsPlugin(q);
533 q->insertPlugin(p);
536 #ifndef Q_OS_WIN
537 if ( KAuthorized::authorizeKAction("sharefile") &&
538 KFileSharePropsPlugin::supports( m_items ) )
540 KPropertiesDialogPlugin *p = new KFileSharePropsPlugin(q);
541 q->insertPlugin(p);
543 #endif
545 //plugins
547 if ( m_items.count() != 1 )
548 return;
550 const KFileItem item = m_items.first();
551 const QString mimetype = item.mimetype();
553 if ( mimetype.isEmpty() )
554 return;
556 QString query = QString::fromLatin1(
557 "((not exist [X-KDE-Protocol]) or "
558 " ([X-KDE-Protocol] == '%1' ) )"
559 ).arg(item.url().protocol());
561 kDebug( 250 ) << "trader query: " << query;
562 KService::List offers = KMimeTypeTrader::self()->query( mimetype, "KPropertiesDialog/Plugin", query );
563 foreach (const KService::Ptr &ptr, offers) {
564 KPropertiesDialogPlugin *plugin = ptr->createInstance<KPropertiesDialogPlugin>(q);
565 if (!plugin)
566 continue;
567 plugin->setObjectName(ptr->name());
569 q->insertPlugin(plugin);
573 void KPropertiesDialog::updateUrl( const KUrl& _newUrl )
575 Q_ASSERT(d->m_items.count() == 1);
576 kDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url();
577 KUrl newUrl = _newUrl;
578 emit saveAs(d->m_singleUrl, newUrl);
579 kDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url();
581 d->m_singleUrl = newUrl;
582 d->m_items.first().setUrl(newUrl);
583 assert(!d->m_singleUrl.isEmpty());
584 // If we have an Desktop page, set it dirty, so that a full file is saved locally
585 // Same for a URL page (because of the Name= hack)
586 foreach (KPropertiesDialogPlugin *it, d->m_pageList) {
587 if ( qobject_cast<KUrlPropsPlugin*>(it) ||
588 qobject_cast<KDesktopPropsPlugin*>(it) )
590 //kDebug(250) << "Setting page dirty";
591 it->setDirty();
592 break;
597 void KPropertiesDialog::rename( const QString& _name )
599 Q_ASSERT(d->m_items.count() == 1);
600 kDebug(250) << "KPropertiesDialog::rename " << _name;
601 KUrl newUrl;
602 // if we're creating from a template : use currentdir
603 if (!d->m_currentDir.isEmpty()) {
604 newUrl = d->m_currentDir;
605 newUrl.addPath(_name);
606 } else {
607 QString tmpurl = d->m_singleUrl.url();
608 if (!tmpurl.isEmpty() && tmpurl.at(tmpurl.length() - 1) == '/') {
609 // It's a directory, so strip the trailing slash first
610 tmpurl.truncate(tmpurl.length() - 1);
613 newUrl = tmpurl;
614 newUrl.setFileName(_name);
616 updateUrl(newUrl);
619 void KPropertiesDialog::abortApplying()
621 d->m_aborted = true;
624 class KPropertiesDialogPlugin::KPropertiesDialogPluginPrivate
626 public:
627 KPropertiesDialogPluginPrivate()
630 ~KPropertiesDialogPluginPrivate()
634 bool m_bDirty;
635 int fontHeight;
638 KPropertiesDialogPlugin::KPropertiesDialogPlugin( KPropertiesDialog *_props )
639 : QObject( _props ),d(new KPropertiesDialogPluginPrivate)
641 properties = _props;
642 d->fontHeight = 2*properties->fontMetrics().height();
643 d->m_bDirty = false;
646 KPropertiesDialogPlugin::~KPropertiesDialogPlugin()
648 delete d;
651 bool KPropertiesDialogPlugin::isDesktopFile( const KFileItem& _item )
653 return _item.isDesktopFile();
656 void KPropertiesDialogPlugin::setDirty( bool b )
658 d->m_bDirty = b;
661 void KPropertiesDialogPlugin::setDirty()
663 d->m_bDirty = true;
666 bool KPropertiesDialogPlugin::isDirty() const
668 return d->m_bDirty;
671 void KPropertiesDialogPlugin::applyChanges()
673 kWarning(250) << "applyChanges() not implemented in page !";
676 int KPropertiesDialogPlugin::fontHeight() const
678 return d->fontHeight;
681 ///////////////////////////////////////////////////////////////////////////////
683 class KFilePropsPlugin::KFilePropsPluginPrivate
685 public:
686 KFilePropsPluginPrivate()
688 dirSizeJob = 0L;
689 dirSizeUpdateTimer = 0L;
690 m_lined = 0;
691 m_capacityBar = 0;
692 m_linkTargetLineEdit = 0;
694 ~KFilePropsPluginPrivate()
696 if ( dirSizeJob )
697 dirSizeJob->kill();
700 KIO::DirectorySizeJob * dirSizeJob;
701 QTimer *dirSizeUpdateTimer;
702 QFrame *m_frame;
703 bool bMultiple;
704 bool bIconChanged;
705 bool bKDesktopMode;
706 bool bDesktopFile;
707 KCapacityBar *m_capacityBar;
708 QString mimeType;
709 QString oldFileName;
710 KLineEdit* m_lined;
712 QWidget *iconArea;
713 QWidget *nameArea;
715 QLabel *m_sizeLabel;
716 QPushButton *m_sizeDetermineButton;
717 QPushButton *m_sizeStopButton;
718 KLineEdit* m_linkTargetLineEdit;
720 QString m_sRelativePath;
721 bool m_bFromTemplate;
724 * The initial filename
726 QString oldName;
729 KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props )
730 : KPropertiesDialogPlugin( _props ),d(new KFilePropsPluginPrivate)
732 d->bMultiple = (properties->items().count() > 1);
733 d->bIconChanged = false;
734 d->bKDesktopMode = (qApp->objectName() == "kdesktop");
735 d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items());
736 kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple;
738 // We set this data from the first item, and we'll
739 // check that the other items match against it, resetting when not.
740 bool isLocal;
741 const KFileItem item = properties->item();
742 KUrl url = item.mostLocalUrl( isLocal );
743 bool isReallyLocal = item.url().isLocalFile();
744 bool bDesktopFile = item.isDesktopFile();
745 mode_t mode = item.mode();
746 bool hasDirs = item.isDir() && !item.isLink();
747 bool hasRoot = url.path() == QLatin1String("/");
748 QString iconStr = KMimeType::iconNameForUrl(url, mode);
749 QString directory = properties->kurl().directory();
750 QString protocol = properties->kurl().protocol();
751 QString mimeComment = item.mimeComment();
752 d->mimeType = item.mimetype();
753 KIO::filesize_t totalSize = item.size();
754 QString magicMimeComment;
755 if ( isLocal ) {
756 KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
757 if ( magicMimeType->name() != KMimeType::defaultMimeType() )
758 magicMimeComment = magicMimeType->comment();
760 #ifdef Q_WS_WIN
761 if ( isReallyLocal ) {
762 directory = QDir::toNativeSeparators( directory.mid( 1 ) );
764 #endif
766 // Those things only apply to 'single file' mode
767 QString filename;
768 bool isTrash = false;
769 bool isDevice = false;
770 d->m_bFromTemplate = false;
772 // And those only to 'multiple' mode
773 uint iDirCount = hasDirs ? 1 : 0;
774 uint iFileCount = 1-iDirCount;
776 d->m_frame = new QFrame();
777 properties->addPage(d->m_frame, i18nc("@title:tab File properties", "&General"));
779 QVBoxLayout *vbl = new QVBoxLayout( d->m_frame );
780 vbl->setMargin( 0 );
781 vbl->setSpacing( KDialog::spacingHint() );
782 vbl->setObjectName( QLatin1String( "vbl" ) );
783 QGridLayout *grid = new QGridLayout(); // unknown rows
784 grid->setColumnStretch(0, 0);
785 grid->setColumnStretch(1, 0);
786 grid->setColumnStretch(2, 1);
787 grid->addItem(new QSpacerItem(KDialog::spacingHint(),0), 0, 1);
788 vbl->addLayout(grid);
789 int curRow = 0;
791 if ( !d->bMultiple )
793 QString path;
794 if ( !d->m_bFromTemplate ) {
795 isTrash = ( properties->kurl().protocol().toLower() == "trash" );
796 isDevice = ( properties->kurl().protocol().toLower() == "device" );
797 // Extract the full name, but without file: for local files
798 if ( isReallyLocal )
799 path = properties->kurl().toLocalFile();
800 else
801 path = properties->kurl().prettyUrl();
802 } else {
803 path = properties->currentDir().path(KUrl::AddTrailingSlash) + properties->defaultName();
804 directory = properties->currentDir().prettyUrl();
807 if (d->bDesktopFile) {
808 determineRelativePath( path );
811 // Extract the file name only
812 filename = properties->defaultName();
813 if ( filename.isEmpty() ) { // no template
814 filename = item.name(); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system
815 } else {
816 d->m_bFromTemplate = true;
817 setDirty(); // to enforce that the copy happens
819 d->oldFileName = filename;
821 // Make it human-readable
822 filename = nameFromFileName( filename );
824 if ( d->bKDesktopMode && d->bDesktopFile ) {
825 KDesktopFile config( url.path() );
826 if ( config.desktopGroup().hasKey( "Name" ) ) {
827 filename = config.readName();
831 d->oldName = filename;
833 else
835 // Multiple items: see what they have in common
836 const KFileItemList items = properties->items();
837 KFileItemList::const_iterator kit = items.begin();
838 const KFileItemList::const_iterator kend = items.end();
839 for ( ++kit /*no need to check the first one again*/ ; kit != kend; ++kit )
841 const KUrl url = (*kit).url();
842 kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyUrl();
843 // The list of things we check here should match the variables defined
844 // at the beginning of this method.
845 if ( url.isLocalFile() != isLocal )
846 isLocal = false; // not all local
847 if ( bDesktopFile && (*kit).isDesktopFile() != bDesktopFile )
848 bDesktopFile = false; // not all desktop files
849 if ( (*kit).mode() != mode )
850 mode = (mode_t)0;
851 if ( KMimeType::iconNameForUrl(url, mode) != iconStr )
852 iconStr = "document-multiple";
853 if ( url.directory() != directory )
854 directory.clear();
855 if ( url.protocol() != protocol )
856 protocol.clear();
857 if ( !mimeComment.isNull() && (*kit).mimeComment() != mimeComment )
858 mimeComment.clear();
859 if ( isLocal && !magicMimeComment.isNull() ) {
860 KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
861 if ( magicMimeType->comment() != magicMimeComment )
862 magicMimeComment.clear();
865 if ( isLocal && url.path() == QLatin1String("/") )
866 hasRoot = true;
867 if ( (*kit).isDir() && !(*kit).isLink() )
869 iDirCount++;
870 hasDirs = true;
872 else
874 iFileCount++;
875 totalSize += (*kit).size();
880 if (!isReallyLocal && !protocol.isEmpty())
882 directory += ' ';
883 directory += '(';
884 directory += protocol;
885 directory += ')';
888 if ( !isDevice && !isTrash && (bDesktopFile || S_ISDIR(mode)) && !d->bMultiple /*not implemented for multiple*/ )
890 KIconButton *iconButton = new KIconButton( d->m_frame );
891 int bsize = 66 + 2 * iconButton->style()->pixelMetric(QStyle::PM_ButtonMargin);
892 iconButton->setFixedSize(bsize, bsize);
893 iconButton->setIconSize(48);
894 iconButton->setStrictIconSize(false);
895 // This works for everything except Device icons on unmounted devices
896 // So we have to really open .desktop files
897 QString iconStr = KMimeType::findByUrl( url, mode )->iconName( url );
898 if ( bDesktopFile && isLocal )
900 KDesktopFile config( url.path() );
901 KConfigGroup group = config.desktopGroup();
902 iconStr = group.readEntry( "Icon" );
903 if ( config.hasDeviceType() )
904 iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Device );
905 else
906 iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Application );
907 } else
908 iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Place );
909 iconButton->setIcon(iconStr);
910 d->iconArea = iconButton;
911 connect( iconButton, SIGNAL( iconChanged(const QString&) ),
912 this, SLOT( slotIconChanged() ) );
913 } else {
914 QLabel *iconLabel = new QLabel( d->m_frame );
915 int bsize = 66 + 2 * iconLabel->style()->pixelMetric(QStyle::PM_ButtonMargin);
916 iconLabel->setFixedSize(bsize, bsize);
917 iconLabel->setPixmap( KIconLoader::global()->loadIcon( iconStr, KIconLoader::Desktop, 48) );
918 d->iconArea = iconLabel;
920 grid->addWidget(d->iconArea, curRow, 0, Qt::AlignLeft);
922 if (d->bMultiple || isTrash || isDevice || hasRoot)
924 QLabel *lab = new QLabel(d->m_frame );
925 if ( d->bMultiple )
926 lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) );
927 else
928 lab->setText( filename );
929 d->nameArea = lab;
930 } else
932 d->m_lined = new KLineEdit( d->m_frame );
933 d->m_lined->setText(filename);
934 d->nameArea = d->m_lined;
935 d->m_lined->setFocus();
937 // Enhanced rename: Don't highlight the file extension.
938 QString extension = KMimeType::extractKnownExtension( filename );
939 if ( !extension.isEmpty() )
940 d->m_lined->setSelection( 0, filename.length() - extension.length() - 1 );
941 else
943 int lastDot = filename.lastIndexOf('.');
944 if (lastDot > 0)
945 d->m_lined->setSelection(0, lastDot);
948 connect( d->m_lined, SIGNAL( textChanged( const QString & ) ),
949 this, SLOT( nameFileChanged(const QString & ) ) );
952 grid->addWidget(d->nameArea, curRow++, 2);
954 KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame);
955 grid->addWidget(sep, curRow, 0, 1, 3);
956 ++curRow;
958 QLabel *l;
959 if ( !mimeComment.isEmpty() && !isDevice && !isTrash)
961 l = new QLabel(i18n("Type:"), d->m_frame );
963 grid->addWidget(l, curRow, 0, Qt::AlignRight);
965 KHBox *box = new KHBox(d->m_frame);
966 box->setSpacing(20);
967 l = new QLabel(mimeComment, box );
969 #ifdef Q_WS_X11
970 //TODO: wrap for win32 or mac?
971 QPushButton *button = new QPushButton(box);
973 button->setIcon( KIcon(QString::fromLatin1("configure")) );
974 const int pixmapSize = button->style()->pixelMetric(QStyle::PM_SmallIconSize);
975 button->setFixedSize( pixmapSize+8, pixmapSize+8 );
976 if ( d->mimeType == KMimeType::defaultMimeType() )
977 button->setToolTip(i18n("Create new file type"));
978 else
979 button->setToolTip(i18n("Edit file type"));
981 connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() ));
983 if (!KAuthorized::authorizeKAction("editfiletype"))
984 button->hide();
985 #endif
987 grid->addWidget(box, curRow++, 2);
990 if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
992 l = new QLabel(i18n("Contents:"), d->m_frame );
993 grid->addWidget(l, curRow, 0, Qt::AlignRight);
995 l = new QLabel(magicMimeComment, d->m_frame );
996 grid->addWidget(l, curRow++, 2);
999 if ( !directory.isEmpty() )
1001 l = new QLabel( i18n("Location:"), d->m_frame );
1002 grid->addWidget(l, curRow, 0, Qt::AlignRight);
1004 l = new KSqueezedTextLabel( directory, d->m_frame );
1005 // force the layout direction to be always LTR
1006 l->setLayoutDirection(Qt::LeftToRight);
1007 // but if we are in RTL mode, align the text to the right
1008 // otherwise the text is on the wrong side of the dialog
1009 if (properties->layoutDirection() == Qt::RightToLeft)
1010 l->setAlignment( Qt::AlignRight );
1011 l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
1012 grid->addWidget(l, curRow++, 2);
1015 l = new QLabel(i18n("Size:"), d->m_frame );
1016 grid->addWidget(l, curRow, 0, Qt::AlignRight);
1018 d->m_sizeLabel = new QLabel( d->m_frame );
1019 grid->addWidget( d->m_sizeLabel, curRow++, 2 );
1021 if ( !hasDirs ) // Only files [and symlinks]
1023 d->m_sizeLabel->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize))
1024 .arg(KGlobal::locale()->formatNumber(totalSize, 0)));
1025 d->m_sizeDetermineButton = 0L;
1026 d->m_sizeStopButton = 0L;
1028 else // Directory
1030 QHBoxLayout * sizelay = new QHBoxLayout();
1031 sizelay->setSpacing(KDialog::spacingHint());
1032 grid->addLayout( sizelay, curRow++, 2 );
1034 // buttons
1035 d->m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
1036 d->m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
1037 connect( d->m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) );
1038 connect( d->m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) );
1039 sizelay->addWidget(d->m_sizeDetermineButton, 0);
1040 sizelay->addWidget(d->m_sizeStopButton, 0);
1041 sizelay->addStretch(10); // so that the buttons don't grow horizontally
1043 // auto-launch for local dirs only, and not for '/'
1044 if ( isLocal && !hasRoot )
1046 d->m_sizeDetermineButton->setText( i18n("Refresh") );
1047 slotSizeDetermine();
1049 else
1050 d->m_sizeStopButton->setEnabled( false );
1053 if (!d->bMultiple && item.isLink()) {
1054 l = new QLabel(i18n("Points to:"), d->m_frame );
1055 grid->addWidget(l, curRow, 0, Qt::AlignRight);
1057 d->m_linkTargetLineEdit = new KLineEdit(item.linkDest(), d->m_frame );
1058 grid->addWidget(d->m_linkTargetLineEdit, curRow++, 2);
1061 if (!d->bMultiple) // Dates for multiple don't make much sense...
1063 KDateTime dt = item.time(KFileItem::CreationTime);
1064 if ( !dt.isNull() )
1066 l = new QLabel(i18n("Created:"), d->m_frame );
1067 grid->addWidget(l, curRow, 0, Qt::AlignRight);
1069 l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1070 grid->addWidget(l, curRow++, 2);
1073 dt = item.time(KFileItem::ModificationTime);
1074 if ( !dt.isNull() )
1076 l = new QLabel(i18n("Modified:"), d->m_frame );
1077 grid->addWidget(l, curRow, 0, Qt::AlignRight);
1079 l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1080 grid->addWidget(l, curRow++, 2);
1083 dt = item.time(KFileItem::AccessTime);
1084 if ( !dt.isNull() )
1086 l = new QLabel(i18n("Accessed:"), d->m_frame );
1087 grid->addWidget(l, curRow, 0, Qt::AlignRight);
1089 l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1090 grid->addWidget(l, curRow++, 2);
1094 if ( isLocal && hasDirs ) // only for directories
1097 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() );
1098 if (mp) {
1099 KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() );
1100 if(info.size() != 0 )
1102 sep = new KSeparator( Qt::Horizontal, d->m_frame);
1103 grid->addWidget(sep, curRow, 0, 1, 3);
1104 ++curRow;
1105 if (mp->mountPoint() != "/")
1107 l = new QLabel(i18n("Mounted on:"), d->m_frame );
1108 grid->addWidget(l, curRow, 0, Qt::AlignRight);
1110 l = new KSqueezedTextLabel( mp->mountPoint(), d->m_frame );
1111 l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
1112 grid->addWidget( l, curRow++, 2 );
1115 l = new QLabel(i18n("Device usage:"), d->m_frame );
1116 grid->addWidget(l, curRow, 0, Qt::AlignRight);
1118 d->m_capacityBar = new KCapacityBar( KCapacityBar::DrawTextOutline, d->m_frame );
1119 grid->addWidget( d->m_capacityBar, curRow++, 2);
1121 slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
1126 vbl->addStretch(1);
1129 // QString KFilePropsPlugin::tabName () const
1130 // {
1131 // return i18n ("&General");
1132 // }
1134 void KFilePropsPlugin::setFileNameReadOnly( bool ro )
1136 if ( d->m_lined )
1138 d->m_lined->setReadOnly( ro );
1139 if (ro)
1141 // Don't put the initial focus on the line edit when it is ro
1142 properties->setButtonFocus(KDialog::Ok);
1147 void KFilePropsPlugin::slotEditFileType()
1149 #ifdef Q_WS_X11
1150 QString mime;
1151 if ( d->mimeType == KMimeType::defaultMimeType() ) {
1152 int pos = d->oldFileName.lastIndexOf( '.' );
1153 if ( pos != -1 )
1154 mime = '*' + d->oldFileName.mid(pos);
1155 else
1156 mime = '*';
1158 else
1159 mime = d->mimeType;
1160 //TODO: wrap for win32 or mac?
1161 QString keditfiletype = QString::fromLatin1("keditfiletype");
1162 KRun::runCommand( keditfiletype
1163 + " --parent " + QString::number( (ulong)properties->topLevelWidget()->winId())
1164 + ' ' + KShell::quoteArg(mime),
1165 keditfiletype, keditfiletype /*unused*/, properties->topLevelWidget());
1166 #endif
1169 void KFilePropsPlugin::slotIconChanged()
1171 d->bIconChanged = true;
1172 emit changed();
1175 void KFilePropsPlugin::nameFileChanged(const QString &text )
1177 properties->enableButtonOk(!text.isEmpty());
1178 emit changed();
1181 void KFilePropsPlugin::determineRelativePath( const QString & path )
1183 // now let's make it relative
1184 d->m_sRelativePath = KGlobal::dirs()->relativeLocation("apps", path);
1185 if (d->m_sRelativePath.startsWith('/'))
1187 d->m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
1188 if (d->m_sRelativePath.startsWith('/'))
1189 d->m_sRelativePath.clear();
1190 else
1191 d->m_sRelativePath = path;
1195 void KFilePropsPlugin::slotFoundMountPoint( const QString&,
1196 quint64 kibSize,
1197 quint64 /*kibUsed*/,
1198 quint64 kibAvail )
1200 d->m_capacityBar->setText(
1201 i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
1202 KIO::convertSizeFromKiB(kibAvail),
1203 KIO::convertSizeFromKiB(kibSize),
1204 100 - (int)(100.0 * kibAvail / kibSize) ));
1206 d->m_capacityBar->setValue(100 - (int)(100.0 * kibAvail / kibSize));
1209 void KFilePropsPlugin::slotDirSizeUpdate()
1211 KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1212 KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
1213 KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
1214 d->m_sizeLabel->setText(
1215 i18n("Calculating... %1 (%2)\n%3, %4",
1216 KIO::convertSize(totalSize),
1217 totalSize,
1218 i18np("1 file", "%1 files", totalFiles),
1219 i18np("1 sub-folder", "%1 sub-folders", totalSubdirs)));
1222 void KFilePropsPlugin::slotDirSizeFinished( KJob * job )
1224 if (job->error())
1225 d->m_sizeLabel->setText( job->errorString() );
1226 else
1228 KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1229 KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
1230 KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
1231 d->m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4")
1232 .arg(KIO::convertSize(totalSize))
1233 .arg(KGlobal::locale()->formatNumber(totalSize, 0))
1234 .arg(i18np("1 file","%1 files",totalFiles))
1235 .arg(i18np("1 sub-folder","%1 sub-folders",totalSubdirs)));
1237 d->m_sizeStopButton->setEnabled(false);
1238 // just in case you change something and try again :)
1239 d->m_sizeDetermineButton->setText( i18n("Refresh") );
1240 d->m_sizeDetermineButton->setEnabled(true);
1241 d->dirSizeJob = 0;
1242 delete d->dirSizeUpdateTimer;
1243 d->dirSizeUpdateTimer = 0;
1246 void KFilePropsPlugin::slotSizeDetermine()
1248 d->m_sizeLabel->setText( i18n("Calculating...") );
1249 kDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" << properties->item();
1250 kDebug(250) << " URL=" << properties->item().url().url();
1252 d->dirSizeJob = KIO::directorySize( properties->items() );
1253 d->dirSizeUpdateTimer = new QTimer(this);
1254 connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ),
1255 SLOT( slotDirSizeUpdate() ) );
1256 d->dirSizeUpdateTimer->start(500);
1257 connect( d->dirSizeJob, SIGNAL( result( KJob * ) ),
1258 SLOT( slotDirSizeFinished( KJob * ) ) );
1259 d->m_sizeStopButton->setEnabled(true);
1260 d->m_sizeDetermineButton->setEnabled(false);
1262 // also update the "Free disk space" display
1263 if ( d->m_capacityBar )
1265 bool isLocal;
1266 const KFileItem item = properties->item();
1267 KUrl url = item.mostLocalUrl( isLocal );
1268 KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() );
1269 if (mp) {
1270 KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() );
1271 slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
1276 void KFilePropsPlugin::slotSizeStop()
1278 if ( d->dirSizeJob )
1280 KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1281 d->m_sizeLabel->setText(i18n("At least %1",
1282 KIO::convertSize(totalSize)));
1283 d->dirSizeJob->kill();
1284 d->dirSizeJob = 0;
1286 if ( d->dirSizeUpdateTimer )
1287 d->dirSizeUpdateTimer->stop();
1289 d->m_sizeStopButton->setEnabled(false);
1290 d->m_sizeDetermineButton->setEnabled(true);
1293 KFilePropsPlugin::~KFilePropsPlugin()
1295 delete d;
1298 bool KFilePropsPlugin::supports( const KFileItemList& /*_items*/ )
1300 return true;
1303 void KFilePropsPlugin::applyChanges()
1305 if ( d->dirSizeJob )
1306 slotSizeStop();
1308 kDebug(250) << "KFilePropsPlugin::applyChanges";
1310 if (qobject_cast<QLineEdit*>(d->nameArea))
1312 QString n = ((QLineEdit *) d->nameArea)->text();
1313 // Remove trailing spaces (#4345)
1314 while ( ! n.isEmpty() && n[n.length()-1].isSpace() )
1315 n.truncate( n.length() - 1 );
1316 if ( n.isEmpty() )
1318 KMessageBox::sorry( properties, i18n("The new file name is empty."));
1319 properties->abortApplying();
1320 return;
1323 // Do we need to rename the file ?
1324 kDebug(250) << "oldname = " << d->oldName;
1325 kDebug(250) << "newname = " << n;
1326 if ( d->oldName != n || d->m_bFromTemplate ) { // true for any from-template file
1327 KIO::Job * job = 0L;
1328 KUrl oldurl = properties->kurl();
1330 QString newFileName = KIO::encodeFileName(n);
1331 if (d->bDesktopFile && !newFileName.endsWith(".desktop") && !newFileName.endsWith(".kdelnk"))
1332 newFileName += ".desktop";
1334 // Tell properties. Warning, this changes the result of properties->kurl() !
1335 properties->rename( newFileName );
1337 // Update also relative path (for apps and mimetypes)
1338 if ( !d->m_sRelativePath.isEmpty() )
1339 determineRelativePath( properties->kurl().toLocalFile() );
1341 kDebug(250) << "New URL = " << properties->kurl().url();
1342 kDebug(250) << "old = " << oldurl.url();
1344 // Don't remove the template !!
1345 if ( !d->m_bFromTemplate ) // (normal renaming)
1346 job = KIO::move( oldurl, properties->kurl() );
1347 else // Copying a template
1348 job = KIO::copy( oldurl, properties->kurl() );
1350 connect( job, SIGNAL( result( KJob * ) ),
1351 SLOT( slotCopyFinished( KJob * ) ) );
1352 connect( job, SIGNAL( renamed( KIO::Job *, const KUrl &, const KUrl & ) ),
1353 SLOT( slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & ) ) );
1354 // wait for job
1355 QEventLoop eventLoop;
1356 connect(this, SIGNAL(leaveModality()),
1357 &eventLoop, SLOT(quit()));
1358 eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1359 return;
1361 properties->updateUrl(properties->kurl());
1362 // Update also relative path (for apps and mimetypes)
1363 if ( !d->m_sRelativePath.isEmpty() )
1364 determineRelativePath( properties->kurl().toLocalFile() );
1367 // No job, keep going
1368 slotCopyFinished( 0L );
1371 void KFilePropsPlugin::slotCopyFinished( KJob * job )
1373 kDebug(250) << "KFilePropsPlugin::slotCopyFinished";
1374 if (job)
1376 // allow apply() to return
1377 emit leaveModality();
1378 if ( job->error() )
1380 job->uiDelegate()->showErrorMessage();
1381 // Didn't work. Revert the URL to the old one
1382 properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcUrls().first() );
1383 properties->abortApplying(); // Don't apply the changes to the wrong file !
1384 return;
1388 assert( !properties->item().isNull() );
1389 assert( !properties->item().url().isEmpty() );
1391 // Save the file where we can -> usually in ~/.kde/...
1392 if (d->bDesktopFile && !d->m_sRelativePath.isEmpty())
1394 kDebug(250) << "KFilePropsPlugin::slotCopyFinished " << d->m_sRelativePath;
1395 KUrl newURL;
1396 newURL.setPath( KDesktopFile::locateLocal(d->m_sRelativePath) );
1397 kDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path();
1398 properties->updateUrl( newURL );
1401 if ( d->bKDesktopMode && d->bDesktopFile ) {
1402 // Renamed? Update Name field
1403 if ( d->oldFileName != properties->kurl().fileName() || d->m_bFromTemplate ) {
1404 KDesktopFile config( properties->kurl().toLocalFile() );
1405 KConfigGroup cg = config.desktopGroup();
1406 QString nameStr = nameFromFileName(properties->kurl().fileName());
1407 cg.writeEntry( "Name", nameStr );
1408 cg.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized);
1412 if (d->m_linkTargetLineEdit && !d->bMultiple) {
1413 const KFileItem item = properties->item();
1414 const QString newTarget = d->m_linkTargetLineEdit->text();
1415 if (newTarget != item.linkDest()) {
1416 kDebug(250) << "Updating target of symlink to" << newTarget;
1417 KIO::Job* job = KIO::symlink(newTarget, item.url(), KIO::Overwrite);
1418 job->ui()->setAutoErrorHandlingEnabled(true);
1419 job->exec();
1423 // "Link to Application" templates need to be made executable
1424 // Instead of matching against a filename we check if the destination
1425 // is an Application now.
1426 if ( d->m_bFromTemplate ) {
1427 // destination is not necessarily local, use the src template
1428 KDesktopFile templateResult ( static_cast<KIO::CopyJob*>(job)->srcUrls().first().toLocalFile() );
1429 if ( templateResult.hasApplicationType() ) {
1430 // We can either stat the file and add the +x bit or use the larger chmod() job
1431 // with a umask designed to only touch u+x. This is only one KIO job, so let's
1432 // do that.
1434 KFileItem appLink ( properties->item() );
1435 KFileItemList fileItemList;
1436 fileItemList << appLink;
1438 // first 0100 adds u+x, second 0100 only allows chmod to change u+x
1439 KIO::Job* chmodJob = KIO::chmod( fileItemList, 0100, 0100, QString(), QString(), KIO::HideProgressInfo );
1440 chmodJob->exec();
1445 void KFilePropsPlugin::applyIconChanges()
1447 KIconButton *iconButton = qobject_cast<KIconButton*>(d->iconArea);
1448 if ( !iconButton || !d->bIconChanged )
1449 return;
1450 // handle icon changes - only local files (or pseudo-local) for now
1451 // TODO: Use KTempFile and KIO::file_copy with overwrite = true
1452 KUrl url = properties->kurl();
1453 url = KIO::NetAccess::mostLocalUrl( url, properties );
1454 if ( url.isLocalFile()) {
1455 QString path;
1457 if (S_ISDIR(properties->item().mode()))
1459 path = url.toLocalFile(KUrl::AddTrailingSlash) + QString::fromLatin1(".directory");
1460 // don't call updateUrl because the other tabs (i.e. permissions)
1461 // apply to the directory, not the .directory file.
1463 else
1464 path = url.toLocalFile();
1466 // Get the default image
1467 QString str = KMimeType::findByUrl( url,
1468 properties->item().mode(),
1469 true )->iconName();
1470 // Is it another one than the default ?
1471 QString sIcon;
1472 if ( str != iconButton->icon() )
1473 sIcon = iconButton->icon();
1474 // (otherwise write empty value)
1476 kDebug(250) << "**" << path << "**";
1477 QFile f( path );
1479 // If default icon and no .directory file -> don't create one
1480 if ( !sIcon.isEmpty() || f.exists() )
1482 if ( !f.open( QIODevice::ReadWrite ) ) {
1483 KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
1484 "have sufficient access to write to <b>%1</b>.</qt>", path));
1485 return;
1487 f.close();
1489 KDesktopFile cfg(path);
1490 kDebug(250) << "sIcon = " << (sIcon);
1491 kDebug(250) << "str = " << (str);
1492 cfg.desktopGroup().writeEntry( "Icon", sIcon );
1493 cfg.sync();
1498 void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & newUrl )
1500 // This is called in case of an existing local file during the copy/move operation,
1501 // if the user chooses Rename.
1502 properties->updateUrl( newUrl );
1505 void KFilePropsPlugin::postApplyChanges()
1507 // Save the icon only after applying the permissions changes (#46192)
1508 applyIconChanges();
1510 const KFileItemList items = properties->items();
1511 const KUrl::List lst = items.urlList();
1512 org::kde::KDirNotify::emitFilesChanged( lst.toStringList() );
1515 class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
1517 public:
1518 KFilePermissionsPropsPluginPrivate()
1521 ~KFilePermissionsPropsPluginPrivate()
1525 QFrame *m_frame;
1526 QCheckBox *cbRecursive;
1527 QLabel *explanationLabel;
1528 KComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo;
1529 QCheckBox *extraCheckbox;
1530 mode_t partialPermissions;
1531 KFilePermissionsPropsPlugin::PermissionsMode pmode;
1532 bool canChangePermissions;
1533 bool isIrregular;
1534 bool hasExtendedACL;
1535 KACL extendedACL;
1536 KACL defaultACL;
1537 bool fileSystemSupportsACLs;
1539 KComboBox *grpCombo;
1541 KLineEdit *usrEdit;
1542 KLineEdit *grpEdit;
1544 // Old permissions
1545 mode_t permissions;
1546 // Old group
1547 QString strGroup;
1548 // Old owner
1549 QString strOwner;
1552 #define UniOwner (S_IRUSR|S_IWUSR|S_IXUSR)
1553 #define UniGroup (S_IRGRP|S_IWGRP|S_IXGRP)
1554 #define UniOthers (S_IROTH|S_IWOTH|S_IXOTH)
1555 #define UniRead (S_IRUSR|S_IRGRP|S_IROTH)
1556 #define UniWrite (S_IWUSR|S_IWGRP|S_IWOTH)
1557 #define UniExec (S_IXUSR|S_IXGRP|S_IXOTH)
1558 #define UniSpecial (S_ISUID|S_ISGID|S_ISVTX)
1560 // synced with PermissionsTarget
1561 const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers};
1562 const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 };
1564 // synced with PermissionsMode and standardPermissions
1565 const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = {
1566 { I18N_NOOP("Forbidden"),
1567 I18N_NOOP("Can Read"),
1568 I18N_NOOP("Can Read & Write"),
1569 0 },
1570 { I18N_NOOP("Forbidden"),
1571 I18N_NOOP("Can View Content"),
1572 I18N_NOOP("Can View & Modify Content"),
1573 0 },
1574 { 0, 0, 0, 0}, // no texts for links
1575 { I18N_NOOP("Forbidden"),
1576 I18N_NOOP("Can View Content & Read"),
1577 I18N_NOOP("Can View/Read & Modify/Write"),
1582 KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props )
1583 : KPropertiesDialogPlugin( _props ),d(new KFilePermissionsPropsPluginPrivate)
1585 d->cbRecursive = 0L;
1586 d->grpCombo = 0L; d->grpEdit = 0;
1587 d->usrEdit = 0L;
1588 QString path = properties->kurl().path(KUrl::RemoveTrailingSlash);
1589 QString fname = properties->kurl().fileName();
1590 bool isLocal = properties->kurl().isLocalFile();
1591 bool isTrash = ( properties->kurl().protocol().toLower() == "trash" );
1592 bool IamRoot = (geteuid() == 0);
1594 const KFileItem item = properties->item();
1595 bool isLink = item.isLink();
1596 bool isDir = item.isDir(); // all dirs
1597 bool hasDir = item.isDir(); // at least one dir
1598 d->permissions = item.permissions(); // common permissions to all files
1599 d->partialPermissions = d->permissions; // permissions that only some files have (at first we take everything)
1600 d->isIrregular = isIrregular(d->permissions, isDir, isLink);
1601 d->strOwner = item.user();
1602 d->strGroup = item.group();
1603 d->hasExtendedACL = item.ACL().isExtended() || item.defaultACL().isValid();
1604 d->extendedACL = item.ACL();
1605 d->defaultACL = item.defaultACL();
1606 d->fileSystemSupportsACLs = false;
1608 if ( properties->items().count() > 1 )
1610 // Multiple items: see what they have in common
1611 const KFileItemList items = properties->items();
1612 KFileItemList::const_iterator it = items.begin();
1613 const KFileItemList::const_iterator kend = items.end();
1614 for ( ++it /*no need to check the first one again*/ ; it != kend; ++it )
1616 const KUrl url = (*it).url();
1617 if (!d->isIrregular)
1618 d->isIrregular |= isIrregular((*it).permissions(),
1619 (*it).isDir() == isDir,
1620 (*it).isLink() == isLink);
1621 d->hasExtendedACL = d->hasExtendedACL || (*it).hasExtendedACL();
1622 if ( (*it).isLink() != isLink )
1623 isLink = false;
1624 if ( (*it).isDir() != isDir )
1625 isDir = false;
1626 hasDir |= (*it).isDir();
1627 if ( (*it).permissions() != d->permissions )
1629 d->permissions &= (*it).permissions();
1630 d->partialPermissions |= (*it).permissions();
1632 if ( (*it).user() != d->strOwner )
1633 d->strOwner.clear();
1634 if ( (*it).group() != d->strGroup )
1635 d->strGroup.clear();
1639 if (isLink)
1640 d->pmode = PermissionsOnlyLinks;
1641 else if (isDir)
1642 d->pmode = PermissionsOnlyDirs;
1643 else if (hasDir)
1644 d->pmode = PermissionsMixed;
1645 else
1646 d->pmode = PermissionsOnlyFiles;
1648 // keep only what's not in the common permissions
1649 d->partialPermissions = d->partialPermissions & ~d->permissions;
1651 bool isMyFile = false;
1653 if (isLocal && !d->strOwner.isEmpty()) { // local files, and all owned by the same person
1654 struct passwd *myself = getpwuid( geteuid() );
1655 if ( myself != 0L )
1657 isMyFile = (d->strOwner == QString::fromLocal8Bit(myself->pw_name));
1658 } else
1659 kWarning() << "I don't exist ?! geteuid=" << geteuid();
1660 } else {
1661 //We don't know, for remote files, if they are ours or not.
1662 //So we let the user change permissions, and
1663 //KIO::chmod will tell, if he had no right to do it.
1664 isMyFile = true;
1667 d->canChangePermissions = (isMyFile || IamRoot) && (!isLink);
1670 // create GUI
1672 d->m_frame = new QFrame();
1673 properties->addPage( d->m_frame, i18n("&Permissions") );
1675 QBoxLayout *box = new QVBoxLayout( d->m_frame );
1676 box->setMargin( 0 );
1677 box->setSpacing( KDialog::spacingHint() );
1679 QWidget *l;
1680 QLabel *lbl;
1681 QGroupBox *gb;
1682 QGridLayout *gl;
1683 QPushButton* pbAdvancedPerm = 0;
1685 /* Group: Access Permissions */
1686 gb = new QGroupBox ( i18n("Access Permissions"), d->m_frame );
1687 box->addWidget (gb);
1689 gl = new QGridLayout (gb);
1690 gl->setSpacing(KDialog::spacingHint());
1691 gl->setMargin(KDialog::marginHint());
1692 gl->setColumnStretch(1, 1);
1694 l = d->explanationLabel = new QLabel( "", gb );
1695 if (isLink)
1696 d->explanationLabel->setText(i18np("This file is a link and does not have permissions.",
1697 "All files are links and do not have permissions.",
1698 properties->items().count()));
1699 else if (!d->canChangePermissions)
1700 d->explanationLabel->setText(i18n("Only the owner can change permissions."));
1701 gl->addWidget(l, 0, 0, 1, 2);
1703 lbl = new QLabel( i18n("O&wner:"), gb);
1704 gl->addWidget(lbl, 1, 0, Qt::AlignRight);
1705 l = d->ownerPermCombo = new KComboBox(gb);
1706 lbl->setBuddy(l);
1707 gl->addWidget(l, 1, 1);
1708 connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
1709 l->setWhatsThis(i18n("Specifies the actions that the owner is allowed to do."));
1711 lbl = new QLabel( i18n("Gro&up:"), gb);
1712 gl->addWidget(lbl, 2, 0, Qt::AlignRight);
1713 l = d->groupPermCombo = new KComboBox(gb);
1714 lbl->setBuddy(l);
1715 gl->addWidget(l, 2, 1);
1716 connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
1717 l->setWhatsThis(i18n("Specifies the actions that the members of the group are allowed to do."));
1719 lbl = new QLabel( i18n("O&thers:"), gb);
1720 gl->addWidget(lbl, 3, 0, Qt::AlignRight);
1721 l = d->othersPermCombo = new KComboBox(gb);
1722 lbl->setBuddy(l);
1723 gl->addWidget(l, 3, 1);
1724 connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
1725 l->setWhatsThis(i18n("Specifies the actions that all users, who are neither "
1726 "owner nor in the group, are allowed to do."));
1728 if (!isLink) {
1729 l = d->extraCheckbox = new QCheckBox(hasDir ?
1730 i18n("Only own&er can rename and delete folder content") :
1731 i18n("Is &executable"),
1732 gb );
1733 connect( d->extraCheckbox, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
1734 gl->addWidget(l, 4, 1);
1735 l->setWhatsThis(hasDir ? i18n("Enable this option to allow only the folder's owner to "
1736 "delete or rename the contained files and folders. Other "
1737 "users can only add new files, which requires the 'Modify "
1738 "Content' permission.")
1739 : i18n("Enable this option to mark the file as executable. This only makes "
1740 "sense for programs and scripts. It is required when you want to "
1741 "execute them."));
1743 QLayoutItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
1744 gl->addItem(spacer, 5, 0, 1, 3);
1746 pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb);
1747 gl->addWidget(pbAdvancedPerm, 6, 0, 1, 2, Qt::AlignRight);
1748 connect(pbAdvancedPerm, SIGNAL( clicked() ), this, SLOT( slotShowAdvancedPermissions() ));
1750 else
1751 d->extraCheckbox = 0;
1754 /**** Group: Ownership ****/
1755 gb = new QGroupBox ( i18n("Ownership"), d->m_frame );
1756 box->addWidget (gb);
1758 gl = new QGridLayout (gb);
1759 gl->setSpacing(KDialog::spacingHint());
1760 gl->setMargin(KDialog::marginHint());
1761 gl->addItem(new QSpacerItem(0, 10), 0, 0);
1763 /*** Set Owner ***/
1764 l = new QLabel( i18n("User:"), gb );
1765 gl->addWidget (l, 1, 0, Qt::AlignRight);
1767 /* GJ: Don't autocomplete more than 1000 users. This is a kind of random
1768 * value. Huge sites having 10.000+ user have a fair chance of using NIS,
1769 * (possibly) making this unacceptably slow.
1770 * OTOH, it is nice to offer this functionality for the standard user.
1772 int i, maxEntries = 1000;
1773 struct passwd *user;
1775 /* File owner: For root, offer a KLineEdit with autocompletion.
1776 * For a user, who can never chown() a file, offer a QLabel.
1778 if (IamRoot && isLocal)
1780 d->usrEdit = new KLineEdit( gb );
1781 KCompletion *kcom = d->usrEdit->completionObject();
1782 kcom->setOrder(KCompletion::Sorted);
1783 setpwent();
1784 for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); ++i)
1785 kcom->addItem(QString::fromLatin1(user->pw_name));
1786 endpwent();
1787 d->usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto :
1788 KGlobalSettings::CompletionNone);
1789 d->usrEdit->setText(d->strOwner);
1790 gl->addWidget(d->usrEdit, 1, 1);
1791 connect( d->usrEdit, SIGNAL( textChanged( const QString & ) ),
1792 this, SIGNAL( changed() ) );
1794 else
1796 l = new QLabel(d->strOwner, gb);
1797 gl->addWidget(l, 1, 1);
1800 /*** Set Group ***/
1802 QStringList groupList;
1803 QByteArray strUser;
1804 user = getpwuid(geteuid());
1805 if (user != 0L)
1806 strUser = user->pw_name;
1808 #ifdef Q_OS_UNIX
1809 // pick the groups to which the user belongs
1810 int groupCount = 0;
1811 #ifdef Q_OS_MAC
1812 QVarLengthArray<int> groups;
1813 #else
1814 QVarLengthArray<gid_t> groups;
1815 #endif
1816 if (getgrouplist(strUser, user->pw_gid, NULL, &groupCount) < 0) {
1817 groups.resize(groupCount);
1818 if (groups.data())
1819 getgrouplist(strUser, user->pw_gid, groups.data(), &groupCount);
1820 else
1821 groupCount = 0;
1824 for (i = 0; i < groupCount; i++) {
1825 struct group *mygroup = getgrgid(groups[i]);
1826 if (mygroup)
1827 groupList += QString::fromLocal8Bit(mygroup->gr_name);
1829 #endif //Q_OS_UNIX
1831 bool isMyGroup = groupList.contains(d->strGroup);
1833 /* add the group the file currently belongs to ..
1834 * .. if it is not there already
1836 if (!isMyGroup)
1837 groupList += d->strGroup;
1839 l = new QLabel( i18n("Group:"), gb );
1840 gl->addWidget (l, 2, 0, Qt::AlignRight);
1842 /* Set group: if possible to change:
1843 * - Offer a KLineEdit for root, since he can change to any group.
1844 * - Offer a KComboBox for a normal user, since he can change to a fixed
1845 * (small) set of groups only.
1846 * If not changeable: offer a QLabel.
1848 if (IamRoot && isLocal)
1850 d->grpEdit = new KLineEdit(gb);
1851 KCompletion *kcom = new KCompletion;
1852 kcom->setItems(groupList);
1853 d->grpEdit->setCompletionObject(kcom, true);
1854 d->grpEdit->setAutoDeleteCompletionObject( true );
1855 d->grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
1856 d->grpEdit->setText(d->strGroup);
1857 gl->addWidget(d->grpEdit, 2, 1);
1858 connect( d->grpEdit, SIGNAL( textChanged( const QString & ) ),
1859 this, SIGNAL( changed() ) );
1861 else if ((groupList.count() > 1) && isMyFile && isLocal)
1863 d->grpCombo = new KComboBox(gb);
1864 d->grpCombo->setObjectName(QLatin1String("combogrouplist"));
1865 d->grpCombo->addItems(groupList);
1866 d->grpCombo->setCurrentIndex(groupList.indexOf(d->strGroup));
1867 gl->addWidget(d->grpCombo, 2, 1);
1868 connect( d->grpCombo, SIGNAL( activated( int ) ),
1869 this, SIGNAL( changed() ) );
1871 else
1873 l = new QLabel(d->strGroup, gb);
1874 gl->addWidget(l, 2, 1);
1877 gl->setColumnStretch(2, 10);
1879 // "Apply recursive" checkbox
1880 if ( hasDir && !isLink && !isTrash )
1882 d->cbRecursive = new QCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame );
1883 connect( d->cbRecursive, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
1884 box->addWidget( d->cbRecursive );
1887 updateAccessControls();
1890 if ( isTrash )
1892 //don't allow to change properties for file into trash
1893 enableAccessControls(false);
1894 if ( pbAdvancedPerm)
1895 pbAdvancedPerm->setEnabled(false);
1898 box->addStretch (10);
1901 #ifdef HAVE_POSIX_ACL
1902 static bool fileSystemSupportsACL( const QByteArray& path )
1904 bool fileSystemSupportsACLs = false;
1905 #ifdef Q_OS_FREEBSD
1906 struct statfs buf;
1907 fileSystemSupportsACLs = ( statfs( path.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS );
1908 #else
1909 fileSystemSupportsACLs =
1910 getxattr( path.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA;
1911 #endif
1912 return fileSystemSupportsACLs;
1914 #endif
1917 void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() {
1919 bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed);
1920 KDialog dlg( properties );
1921 dlg.setModal( true );
1922 dlg.setCaption( i18n("Advanced Permissions") );
1923 dlg.setButtons( KDialog::Ok | KDialog::Cancel );
1925 QLabel *l, *cl[3];
1926 QGroupBox *gb;
1927 QGridLayout *gl;
1929 QWidget *mainw = new QWidget( &dlg );
1930 QVBoxLayout *vbox = new QVBoxLayout(mainw);
1931 // Group: Access Permissions
1932 gb = new QGroupBox ( i18n("Access Permissions"), mainw );
1933 vbox->addWidget(gb);
1935 gl = new QGridLayout (gb);
1936 gl->setSpacing(KDialog::spacingHint());
1937 gl->setMargin(KDialog::marginHint());
1938 gl->addItem(new QSpacerItem(0, 10), 0, 0);
1940 QVector<QWidget*> theNotSpecials;
1942 l = new QLabel(i18n("Class"), gb );
1943 gl->addWidget(l, 1, 0);
1944 theNotSpecials.append( l );
1946 if (isDir)
1947 l = new QLabel( i18n("Show\nEntries"), gb );
1948 else
1949 l = new QLabel( i18n("Read"), gb );
1950 gl->addWidget (l, 1, 1);
1951 theNotSpecials.append( l );
1952 QString readWhatsThis;
1953 if (isDir)
1954 readWhatsThis = i18n("This flag allows viewing the content of the folder.");
1955 else
1956 readWhatsThis = i18n("The Read flag allows viewing the content of the file.");
1957 l->setWhatsThis(readWhatsThis);
1959 if (isDir)
1960 l = new QLabel( i18n("Write\nEntries"), gb );
1961 else
1962 l = new QLabel( i18n("Write"), gb );
1963 gl->addWidget (l, 1, 2);
1964 theNotSpecials.append( l );
1965 QString writeWhatsThis;
1966 if (isDir)
1967 writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. "
1968 "Note that deleting and renaming can be limited using the Sticky flag.");
1969 else
1970 writeWhatsThis = i18n("The Write flag allows modifying the content of the file.");
1971 l->setWhatsThis(writeWhatsThis);
1973 QString execWhatsThis;
1974 if (isDir) {
1975 l = new QLabel( i18nc("Enter folder", "Enter"), gb );
1976 execWhatsThis = i18n("Enable this flag to allow entering the folder.");
1978 else {
1979 l = new QLabel( i18n("Exec"), gb );
1980 execWhatsThis = i18n("Enable this flag to allow executing the file as a program.");
1982 l->setWhatsThis(execWhatsThis);
1983 theNotSpecials.append( l );
1984 // GJ: Add space between normal and special modes
1985 QSize size = l->sizeHint();
1986 size.setWidth(size.width() + 15);
1987 l->setFixedSize(size);
1988 gl->addWidget (l, 1, 3);
1990 l = new QLabel( i18n("Special"), gb );
1991 gl->addWidget(l, 1, 4, 1, 2);
1992 QString specialWhatsThis;
1993 if (isDir)
1994 specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact "
1995 "meaning of the flag can be seen in the right hand column.");
1996 else
1997 specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen "
1998 "in the right hand column.");
1999 l->setWhatsThis(specialWhatsThis);
2001 cl[0] = new QLabel( i18n("User"), gb );
2002 gl->addWidget (cl[0], 2, 0);
2003 theNotSpecials.append( cl[0] );
2005 cl[1] = new QLabel( i18n("Group"), gb );
2006 gl->addWidget (cl[1], 3, 0);
2007 theNotSpecials.append( cl[1] );
2009 cl[2] = new QLabel( i18n("Others"), gb );
2010 gl->addWidget (cl[2], 4, 0);
2011 theNotSpecials.append( cl[2] );
2013 l = new QLabel(i18n("Set UID"), gb);
2014 gl->addWidget(l, 2, 5);
2015 QString setUidWhatsThis;
2016 if (isDir)
2017 setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be "
2018 "the owner of all new files.");
2019 else
2020 setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
2021 "be executed with the permissions of the owner.");
2022 l->setWhatsThis(setUidWhatsThis);
2024 l = new QLabel(i18n("Set GID"), gb);
2025 gl->addWidget(l, 3, 5);
2026 QString setGidWhatsThis;
2027 if (isDir)
2028 setGidWhatsThis = i18n("If this flag is set, the group of this folder will be "
2029 "set for all new files.");
2030 else
2031 setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
2032 "be executed with the permissions of the group.");
2033 l->setWhatsThis(setGidWhatsThis);
2035 l = new QLabel(i18nc("File permission", "Sticky"), gb);
2036 gl->addWidget(l, 4, 5);
2037 QString stickyWhatsThis;
2038 if (isDir)
2039 stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner "
2040 "and root can delete or rename files. Otherwise everybody "
2041 "with write permissions can do this.");
2042 else
2043 stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may "
2044 "be used on some systems");
2045 l->setWhatsThis(stickyWhatsThis);
2047 mode_t aPermissions, aPartialPermissions;
2048 mode_t dummy1, dummy2;
2050 if (!d->isIrregular) {
2051 switch (d->pmode) {
2052 case PermissionsOnlyFiles:
2053 getPermissionMasks(aPartialPermissions,
2054 dummy1,
2055 aPermissions,
2056 dummy2);
2057 break;
2058 case PermissionsOnlyDirs:
2059 case PermissionsMixed:
2060 getPermissionMasks(dummy1,
2061 aPartialPermissions,
2062 dummy2,
2063 aPermissions);
2064 break;
2065 case PermissionsOnlyLinks:
2066 aPermissions = UniRead | UniWrite | UniExec | UniSpecial;
2067 aPartialPermissions = 0;
2068 break;
2071 else {
2072 aPermissions = d->permissions;
2073 aPartialPermissions = d->partialPermissions;
2076 // Draw Checkboxes
2077 QCheckBox *cba[3][4];
2078 for (int row = 0; row < 3 ; ++row) {
2079 for (int col = 0; col < 4; ++col) {
2080 QCheckBox *cb = new QCheckBox(gb);
2081 if ( col != 3 ) theNotSpecials.append( cb );
2082 cba[row][col] = cb;
2083 cb->setChecked(aPermissions & fperm[row][col]);
2084 if ( aPartialPermissions & fperm[row][col] )
2086 cb->setTristate();
2087 cb->setCheckState(Qt::PartiallyChecked);
2089 else if (d->cbRecursive && d->cbRecursive->isChecked())
2090 cb->setTristate();
2092 cb->setEnabled( d->canChangePermissions );
2093 gl->addWidget (cb, row+2, col+1);
2094 switch(col) {
2095 case 0:
2096 cb->setWhatsThis(readWhatsThis);
2097 break;
2098 case 1:
2099 cb->setWhatsThis(writeWhatsThis);
2100 break;
2101 case 2:
2102 cb->setWhatsThis(execWhatsThis);
2103 break;
2104 case 3:
2105 switch(row) {
2106 case 0:
2107 cb->setWhatsThis(setUidWhatsThis);
2108 break;
2109 case 1:
2110 cb->setWhatsThis(setGidWhatsThis);
2111 break;
2112 case 2:
2113 cb->setWhatsThis(stickyWhatsThis);
2114 break;
2116 break;
2120 gl->setColumnStretch(6, 10);
2122 #ifdef HAVE_POSIX_ACL
2123 KACLEditWidget *extendedACLs = 0;
2125 // FIXME make it work with partial entries
2126 if ( properties->items().count() == 1 ) {
2127 QByteArray path = QFile::encodeName( properties->item().url().toLocalFile() );
2128 d->fileSystemSupportsACLs = fileSystemSupportsACL( path );
2130 if ( d->fileSystemSupportsACLs ) {
2131 std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) );
2132 extendedACLs = new KACLEditWidget( mainw );
2133 vbox->addWidget(extendedACLs);
2134 if ( d->extendedACL.isValid() && d->extendedACL.isExtended() )
2135 extendedACLs->setACL( d->extendedACL );
2136 else
2137 extendedACLs->setACL( KACL( aPermissions ) );
2139 if ( d->defaultACL.isValid() )
2140 extendedACLs->setDefaultACL( d->defaultACL );
2142 if ( properties->items().first().isDir() )
2143 extendedACLs->setAllowDefaults( true );
2145 #endif
2146 dlg.setMainWidget( mainw );
2147 if (dlg.exec() != KDialog::Accepted)
2148 return;
2150 mode_t andPermissions = mode_t(~0);
2151 mode_t orPermissions = 0;
2152 for (int row = 0; row < 3; ++row)
2153 for (int col = 0; col < 4; ++col) {
2154 switch (cba[row][col]->checkState())
2156 case Qt::Checked:
2157 orPermissions |= fperm[row][col];
2158 //fall through
2159 case Qt::Unchecked:
2160 andPermissions &= ~fperm[row][col];
2161 break;
2162 default: // NoChange
2163 break;
2167 d->isIrregular = false;
2168 const KFileItemList items = properties->items();
2169 KFileItemList::const_iterator it = items.begin();
2170 const KFileItemList::const_iterator kend = items.end();
2171 for ( ; it != kend; ++it ) {
2172 if (isIrregular(((*it).permissions() & andPermissions) | orPermissions,
2173 (*it).isDir(), (*it).isLink())) {
2174 d->isIrregular = true;
2175 break;
2179 d->permissions = orPermissions;
2180 d->partialPermissions = andPermissions;
2182 #ifdef HAVE_POSIX_ACL
2183 // override with the acls, if present
2184 if ( extendedACLs ) {
2185 d->extendedACL = extendedACLs->getACL();
2186 d->defaultACL = extendedACLs->getDefaultACL();
2187 d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid();
2188 d->permissions = d->extendedACL.basePermissions();
2189 d->permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX );
2191 #endif
2193 updateAccessControls();
2194 emit changed();
2197 // QString KFilePermissionsPropsPlugin::tabName () const
2198 // {
2199 // return i18n ("&Permissions");
2200 // }
2202 KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin()
2204 delete d;
2207 bool KFilePermissionsPropsPlugin::supports( const KFileItemList& /*_items*/ )
2209 return true;
2212 // sets a combo box in the Access Control frame
2213 void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target,
2214 mode_t permissions, mode_t partial) {
2215 combo->clear();
2216 if (d->isIrregular) //#176876
2217 return;
2219 if (d->pmode == PermissionsOnlyLinks) {
2220 combo->addItem(i18n("Link"));
2221 combo->setCurrentIndex(0);
2222 return;
2225 mode_t tMask = permissionsMasks[target];
2226 int textIndex;
2227 for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++) {
2228 if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite)))
2229 break;
2231 Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar
2233 for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++)
2234 combo->addItem(i18n(permissionsTexts[(int)d->pmode][i]));
2236 if (partial & tMask & ~UniExec) {
2237 combo->addItem(i18n("Varying (No Change)"));
2238 combo->setCurrentIndex(3);
2240 else {
2241 combo->setCurrentIndex(textIndex);
2245 // permissions are irregular if they cant be displayed in a combo box.
2246 bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) {
2247 if (isLink) // links are always ok
2248 return false;
2250 mode_t p = permissions;
2251 if (p & (S_ISUID | S_ISGID)) // setuid/setgid -> irregular
2252 return true;
2253 if (isDir) {
2254 p &= ~S_ISVTX; // ignore sticky on dirs
2256 // check supported flag combinations
2257 mode_t p0 = p & UniOwner;
2258 if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner))
2259 return true;
2260 p0 = p & UniGroup;
2261 if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup))
2262 return true;
2263 p0 = p & UniOthers;
2264 if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers))
2265 return true;
2266 return false;
2268 if (p & S_ISVTX) // sticky on file -> irregular
2269 return true;
2271 // check supported flag combinations
2272 mode_t p0 = p & UniOwner;
2273 bool usrXPossible = !p0; // true if this file could be an executable
2274 if (p0 & S_IXUSR) {
2275 if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR)))
2276 return true;
2277 usrXPossible = true;
2279 else if (p0 == S_IWUSR)
2280 return true;
2282 p0 = p & UniGroup;
2283 bool grpXPossible = !p0; // true if this file could be an executable
2284 if (p0 & S_IXGRP) {
2285 if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP)))
2286 return true;
2287 grpXPossible = true;
2289 else if (p0 == S_IWGRP)
2290 return true;
2291 if (p0 == 0)
2292 grpXPossible = true;
2294 p0 = p & UniOthers;
2295 bool othXPossible = !p0; // true if this file could be an executable
2296 if (p0 & S_IXOTH) {
2297 if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH)))
2298 return true;
2299 othXPossible = true;
2301 else if (p0 == S_IWOTH)
2302 return true;
2304 // check that there either all targets are executable-compatible, or none
2305 return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible);
2308 // enables/disabled the widgets in the Access Control frame
2309 void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) {
2310 d->ownerPermCombo->setEnabled(enable);
2311 d->groupPermCombo->setEnabled(enable);
2312 d->othersPermCombo->setEnabled(enable);
2313 if (d->extraCheckbox)
2314 d->extraCheckbox->setEnabled(enable);
2315 if ( d->cbRecursive )
2316 d->cbRecursive->setEnabled(enable);
2319 // updates all widgets in the Access Control frame
2320 void KFilePermissionsPropsPlugin::updateAccessControls() {
2321 setComboContent(d->ownerPermCombo, PermissionsOwner,
2322 d->permissions, d->partialPermissions);
2323 setComboContent(d->groupPermCombo, PermissionsGroup,
2324 d->permissions, d->partialPermissions);
2325 setComboContent(d->othersPermCombo, PermissionsOthers,
2326 d->permissions, d->partialPermissions);
2328 switch(d->pmode) {
2329 case PermissionsOnlyLinks:
2330 enableAccessControls(false);
2331 break;
2332 case PermissionsOnlyFiles:
2333 enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2334 if (d->canChangePermissions)
2335 d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2336 i18np("This file uses advanced permissions",
2337 "These files use advanced permissions.",
2338 properties->items().count()) : "");
2339 if (d->partialPermissions & UniExec) {
2340 d->extraCheckbox->setTristate();
2341 d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2343 else {
2344 d->extraCheckbox->setTristate(false);
2345 d->extraCheckbox->setChecked(d->permissions & UniExec);
2347 break;
2348 case PermissionsOnlyDirs:
2349 enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2350 // if this is a dir, and we can change permissions, don't dis-allow
2351 // recursive, we can do that for ACL setting.
2352 if ( d->cbRecursive )
2353 d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular );
2355 if (d->canChangePermissions)
2356 d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2357 i18np("This folder uses advanced permissions.",
2358 "These folders use advanced permissions.",
2359 properties->items().count()) : "");
2360 if (d->partialPermissions & S_ISVTX) {
2361 d->extraCheckbox->setTristate();
2362 d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2364 else {
2365 d->extraCheckbox->setTristate(false);
2366 d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
2368 break;
2369 case PermissionsMixed:
2370 enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2371 if (d->canChangePermissions)
2372 d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2373 i18n("These files use advanced permissions.") : "");
2374 break;
2375 if (d->partialPermissions & S_ISVTX) {
2376 d->extraCheckbox->setTristate();
2377 d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2379 else {
2380 d->extraCheckbox->setTristate(false);
2381 d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
2383 break;
2387 // gets masks for files and dirs from the Access Control frame widgets
2388 void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions,
2389 mode_t &andDirPermissions,
2390 mode_t &orFilePermissions,
2391 mode_t &orDirPermissions) {
2392 andFilePermissions = mode_t(~UniSpecial);
2393 andDirPermissions = mode_t(~(S_ISUID|S_ISGID));
2394 orFilePermissions = 0;
2395 orDirPermissions = 0;
2396 if (d->isIrregular)
2397 return;
2399 mode_t m = standardPermissions[d->ownerPermCombo->currentIndex()];
2400 if (m != (mode_t) -1) {
2401 orFilePermissions |= m & UniOwner;
2402 if ((m & UniOwner) &&
2403 ((d->pmode == PermissionsMixed) ||
2404 ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
2405 andFilePermissions &= ~(S_IRUSR | S_IWUSR);
2406 else {
2407 andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
2408 if ((m & S_IRUSR) && (d->extraCheckbox->checkState() == Qt::Checked))
2409 orFilePermissions |= S_IXUSR;
2412 orDirPermissions |= m & UniOwner;
2413 if (m & S_IRUSR)
2414 orDirPermissions |= S_IXUSR;
2415 andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
2418 m = standardPermissions[d->groupPermCombo->currentIndex()];
2419 if (m != (mode_t) -1) {
2420 orFilePermissions |= m & UniGroup;
2421 if ((m & UniGroup) &&
2422 ((d->pmode == PermissionsMixed) ||
2423 ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
2424 andFilePermissions &= ~(S_IRGRP | S_IWGRP);
2425 else {
2426 andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
2427 if ((m & S_IRGRP) && (d->extraCheckbox->checkState() == Qt::Checked))
2428 orFilePermissions |= S_IXGRP;
2431 orDirPermissions |= m & UniGroup;
2432 if (m & S_IRGRP)
2433 orDirPermissions |= S_IXGRP;
2434 andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
2437 m = d->othersPermCombo->currentIndex() >= 0 ? standardPermissions[d->othersPermCombo->currentIndex()] : (mode_t)-1;
2438 if (m != (mode_t) -1) {
2439 orFilePermissions |= m & UniOthers;
2440 if ((m & UniOthers) &&
2441 ((d->pmode == PermissionsMixed) ||
2442 ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
2443 andFilePermissions &= ~(S_IROTH | S_IWOTH);
2444 else {
2445 andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
2446 if ((m & S_IROTH) && (d->extraCheckbox->checkState() == Qt::Checked))
2447 orFilePermissions |= S_IXOTH;
2450 orDirPermissions |= m & UniOthers;
2451 if (m & S_IROTH)
2452 orDirPermissions |= S_IXOTH;
2453 andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
2456 if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) &&
2457 (d->extraCheckbox->checkState() != Qt::PartiallyChecked)) {
2458 andDirPermissions &= ~S_ISVTX;
2459 if (d->extraCheckbox->checkState() == Qt::Checked)
2460 orDirPermissions |= S_ISVTX;
2464 void KFilePermissionsPropsPlugin::applyChanges()
2466 mode_t orFilePermissions;
2467 mode_t orDirPermissions;
2468 mode_t andFilePermissions;
2469 mode_t andDirPermissions;
2471 if (!d->canChangePermissions)
2472 return;
2474 if (!d->isIrregular)
2475 getPermissionMasks(andFilePermissions,
2476 andDirPermissions,
2477 orFilePermissions,
2478 orDirPermissions);
2479 else {
2480 orFilePermissions = d->permissions;
2481 andFilePermissions = d->partialPermissions;
2482 orDirPermissions = d->permissions;
2483 andDirPermissions = d->partialPermissions;
2486 QString owner, group;
2487 if (d->usrEdit)
2488 owner = d->usrEdit->text();
2489 if (d->grpEdit)
2490 group = d->grpEdit->text();
2491 else if (d->grpCombo)
2492 group = d->grpCombo->currentText();
2494 if (owner == d->strOwner)
2495 owner.clear(); // no change
2497 if (group == d->strGroup)
2498 group.clear();
2500 bool recursive = d->cbRecursive && d->cbRecursive->isChecked();
2501 bool permissionChange = false;
2503 KFileItemList files, dirs;
2504 const KFileItemList items = properties->items();
2505 KFileItemList::const_iterator it = items.begin();
2506 const KFileItemList::const_iterator kend = items.end();
2507 for ( ; it != kend; ++it ) {
2508 if ((*it).isDir()) {
2509 dirs.append(*it);
2510 if ((*it).permissions() != (((*it).permissions() & andDirPermissions) | orDirPermissions))
2511 permissionChange = true;
2513 else if ((*it).isFile()) {
2514 files.append(*it);
2515 if ((*it).permissions() != (((*it).permissions() & andFilePermissions) | orFilePermissions))
2516 permissionChange = true;
2520 const bool ACLChange = ( d->extendedACL != properties->item().ACL() );
2521 const bool defaultACLChange = ( d->defaultACL != properties->item().defaultACL() );
2523 if (owner.isEmpty() && group.isEmpty() && !recursive
2524 && !permissionChange && !ACLChange && !defaultACLChange)
2525 return;
2527 KIO::Job * job;
2528 if (files.count() > 0) {
2529 job = KIO::chmod( files, orFilePermissions, ~andFilePermissions,
2530 owner, group, false );
2531 if ( ACLChange && d->fileSystemSupportsACLs )
2532 job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
2533 if ( defaultACLChange && d->fileSystemSupportsACLs )
2534 job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
2536 connect( job, SIGNAL( result( KJob * ) ),
2537 SLOT( slotChmodResult( KJob * ) ) );
2538 QEventLoop eventLoop;
2539 connect(this, SIGNAL(leaveModality()),
2540 &eventLoop, SLOT(quit()));
2541 eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
2543 if (dirs.count() > 0) {
2544 job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
2545 owner, group, recursive );
2546 if ( ACLChange && d->fileSystemSupportsACLs )
2547 job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
2548 if ( defaultACLChange && d->fileSystemSupportsACLs )
2549 job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
2551 connect( job, SIGNAL( result( KJob * ) ),
2552 SLOT( slotChmodResult( KJob * ) ) );
2553 QEventLoop eventLoop;
2554 connect(this, SIGNAL(leaveModality()),
2555 &eventLoop, SLOT(quit()));
2556 eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
2560 void KFilePermissionsPropsPlugin::slotChmodResult( KJob * job )
2562 kDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult";
2563 if (job->error())
2564 job->uiDelegate()->showErrorMessage();
2565 // allow apply() to return
2566 emit leaveModality();
2572 class KUrlPropsPlugin::KUrlPropsPluginPrivate
2574 public:
2575 KUrlPropsPluginPrivate()
2578 ~KUrlPropsPluginPrivate()
2582 QFrame *m_frame;
2583 KUrlRequester *URLEdit;
2584 QString URLStr;
2587 KUrlPropsPlugin::KUrlPropsPlugin( KPropertiesDialog *_props )
2588 : KPropertiesDialogPlugin( _props ),d(new KUrlPropsPluginPrivate)
2590 d->m_frame = new QFrame();
2591 properties->addPage(d->m_frame, i18n("U&RL"));
2592 QVBoxLayout *layout = new QVBoxLayout(d->m_frame);
2593 layout->setMargin(0);
2594 layout->setSpacing(KDialog::spacingHint());
2596 QLabel *l;
2597 l = new QLabel( d->m_frame );
2598 l->setObjectName( QLatin1String( "Label_1" ) );
2599 l->setText( i18n("URL:") );
2600 layout->addWidget(l, Qt::AlignRight);
2602 d->URLEdit = new KUrlRequester( d->m_frame );
2603 layout->addWidget(d->URLEdit);
2605 KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
2606 if (url.isLocalFile()) {
2607 QString path = url.toLocalFile();
2609 QFile f( path );
2610 if ( !f.open( QIODevice::ReadOnly ) ) {
2611 return;
2613 f.close();
2615 KDesktopFile config( path );
2616 const KConfigGroup dg = config.desktopGroup();
2617 d->URLStr = dg.readPathEntry( "URL", QString() );
2619 if (!d->URLStr.isEmpty()) {
2620 d->URLEdit->setUrl( KUrl(d->URLStr) );
2624 connect( d->URLEdit, SIGNAL( textChanged( const QString & ) ),
2625 this, SIGNAL( changed() ) );
2627 layout->addStretch (1);
2630 KUrlPropsPlugin::~KUrlPropsPlugin()
2632 delete d;
2635 // QString KUrlPropsPlugin::tabName () const
2636 // {
2637 // return i18n ("U&RL");
2638 // }
2640 bool KUrlPropsPlugin::supports( const KFileItemList& _items )
2642 if ( _items.count() != 1 )
2643 return false;
2644 const KFileItem item = _items.first();
2645 // check if desktop file
2646 if (!item.isDesktopFile())
2647 return false;
2649 // open file and check type
2650 bool isLocal;
2651 KUrl url = item.mostLocalUrl(isLocal);
2652 if (!isLocal) {
2653 return false;
2656 KDesktopFile config( url.path() );
2657 return config.hasLinkType();
2660 void KUrlPropsPlugin::applyChanges()
2662 KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
2663 if (!url.isLocalFile()) {
2664 //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
2665 return;
2668 QString path = url.path();
2670 QFile f( path );
2671 if ( !f.open( QIODevice::ReadWrite ) ) {
2672 KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
2673 "sufficient access to write to <b>%1</b>.</qt>", path));
2674 return;
2676 f.close();
2678 KDesktopFile config( path );
2679 KConfigGroup dg = config.desktopGroup();
2680 dg.writeEntry( "Type", QString::fromLatin1("Link"));
2681 dg.writePathEntry( "URL", d->URLEdit->url().url() );
2682 // Users can't create a Link .desktop file with a Name field,
2683 // but distributions can. Update the Name field in that case.
2684 if ( dg.hasKey("Name") )
2686 QString nameStr = nameFromFileName(properties->kurl().fileName());
2687 dg.writeEntry( "Name", nameStr );
2688 dg.writeEntry( "Name", nameStr, KConfigBase::Persistent|KConfigBase::Localized );
2694 /* ----------------------------------------------------
2696 * KDevicePropsPlugin
2698 * -------------------------------------------------- */
2700 class KDevicePropsPlugin::KDevicePropsPluginPrivate
2702 public:
2703 KDevicePropsPluginPrivate()
2706 ~KDevicePropsPluginPrivate()
2710 QFrame *m_frame;
2711 QStringList mountpointlist;
2712 QLabel *m_freeSpaceText;
2713 QLabel *m_freeSpaceLabel;
2714 QProgressBar *m_freeSpaceBar;
2716 KComboBox* device;
2717 QLabel* mountpoint;
2718 QCheckBox* readonly;
2719 KIconButton* unmounted;
2721 QStringList m_devicelist;
2724 KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KDevicePropsPluginPrivate)
2726 d->m_frame = new QFrame();
2727 properties->addPage(d->m_frame, i18n("De&vice"));
2729 QStringList devices;
2730 const KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
2732 for(KMountPoint::List::ConstIterator it = mountPoints.begin();
2733 it != mountPoints.end(); ++it)
2735 const KMountPoint::Ptr mp = (*it);
2736 QString mountPoint = mp->mountPoint();
2737 QString device = mp->mountedFrom();
2738 kDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType();
2740 if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty()
2741 && device != "none")
2743 devices.append( device + QString::fromLatin1(" (")
2744 + mountPoint + QString::fromLatin1(")") );
2745 d->m_devicelist.append(device);
2746 d->mountpointlist.append(mountPoint);
2750 QGridLayout *layout = new QGridLayout( d->m_frame );
2752 layout->setMargin(0);
2753 layout->setSpacing(KDialog::spacingHint());
2754 layout->setColumnStretch(1, 1);
2756 QLabel* label;
2757 label = new QLabel( d->m_frame );
2758 label->setText( devices.count() == 0 ?
2759 i18n("Device (/dev/fd0):") : // old style
2760 i18n("Device:") ); // new style (combobox)
2761 layout->addWidget(label, 0, 0, Qt::AlignRight);
2763 d->device = new KComboBox( d->m_frame );
2764 d->device->setObjectName( QLatin1String( "ComboBox_device" ) );
2765 d->device->setEditable( true );
2766 d->device->addItems( devices );
2767 layout->addWidget(d->device, 0, 1);
2768 connect( d->device, SIGNAL( activated( int ) ),
2769 this, SLOT( slotActivated( int ) ) );
2771 d->readonly = new QCheckBox( d->m_frame );
2772 d->readonly->setObjectName( QLatin1String( "CheckBox_readonly" ) );
2773 d->readonly->setText( i18n("Read only") );
2774 layout->addWidget(d->readonly, 1, 1);
2776 label = new QLabel( d->m_frame );
2777 label->setText( i18n("File system:") );
2778 layout->addWidget(label, 2, 0, Qt::AlignRight);
2780 QLabel *fileSystem = new QLabel( d->m_frame );
2781 layout->addWidget(fileSystem, 2, 1);
2783 label = new QLabel( d->m_frame );
2784 label->setText( devices.count()==0 ?
2785 i18n("Mount point (/mnt/floppy):") : // old style
2786 i18n("Mount point:")); // new style (combobox)
2787 layout->addWidget(label, 3, 0, Qt::AlignRight);
2789 d->mountpoint = new QLabel( d->m_frame );
2790 d->mountpoint->setObjectName( QLatin1String( "LineEdit_mountpoint" ) );
2792 layout->addWidget(d->mountpoint, 3, 1);
2794 // show disk free
2795 d->m_freeSpaceText = new QLabel(i18n("Device usage:"), d->m_frame );
2796 layout->addWidget(d->m_freeSpaceText, 4, 0, Qt::AlignRight);
2798 d->m_freeSpaceLabel = new QLabel( d->m_frame );
2799 layout->addWidget( d->m_freeSpaceLabel, 4, 1 );
2801 d->m_freeSpaceBar = new QProgressBar( d->m_frame );
2802 d->m_freeSpaceBar->setObjectName( "freeSpaceBar" );
2803 layout->addWidget(d->m_freeSpaceBar, 5, 0, 1, 2);
2805 // we show it in the slot when we know the values
2806 d->m_freeSpaceText->hide();
2807 d->m_freeSpaceLabel->hide();
2808 d->m_freeSpaceBar->hide();
2810 KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame);
2811 layout->addWidget(sep, 6, 0, 1, 2);
2813 d->unmounted = new KIconButton( d->m_frame );
2814 int bsize = 66 + 2 * d->unmounted->style()->pixelMetric(QStyle::PM_ButtonMargin);
2815 d->unmounted->setFixedSize(bsize, bsize);
2816 d->unmounted->setIconType(KIconLoader::Desktop, KIconLoader::Device);
2817 layout->addWidget(d->unmounted, 7, 0);
2819 label = new QLabel( i18n("Unmounted Icon"), d->m_frame );
2820 layout->addWidget(label, 7, 1);
2822 layout->setRowStretch(8, 1);
2824 KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
2825 if (!url.isLocalFile()) {
2826 return;
2828 QString path = url.toLocalFile();
2830 QFile f( path );
2831 if ( !f.open( QIODevice::ReadOnly ) )
2832 return;
2833 f.close();
2835 const KDesktopFile _config( path );
2836 const KConfigGroup config = _config.desktopGroup();
2837 QString deviceStr = config.readEntry( "Dev" );
2838 QString mountPointStr = config.readEntry( "MountPoint" );
2839 bool ro = config.readEntry( "ReadOnly", false );
2840 QString unmountedStr = config.readEntry( "UnmountIcon" );
2842 fileSystem->setText(config.readEntry("FSType"));
2844 d->device->setEditText( deviceStr );
2845 if ( !deviceStr.isEmpty() ) {
2846 // Set default options for this device (first matching entry)
2847 int index = d->m_devicelist.indexOf(deviceStr);
2848 if (index != -1)
2850 //kDebug(250) << "found it " << index;
2851 slotActivated( index );
2855 if ( !mountPointStr.isEmpty() )
2857 d->mountpoint->setText( mountPointStr );
2858 updateInfo();
2861 d->readonly->setChecked( ro );
2863 if ( unmountedStr.isEmpty() )
2864 unmountedStr = KMimeType::defaultMimeTypePtr()->iconName(); // default icon
2866 d->unmounted->setIcon( unmountedStr );
2868 connect( d->device, SIGNAL( activated( int ) ),
2869 this, SIGNAL( changed() ) );
2870 connect( d->device, SIGNAL( textChanged( const QString & ) ),
2871 this, SIGNAL( changed() ) );
2872 connect( d->readonly, SIGNAL( toggled( bool ) ),
2873 this, SIGNAL( changed() ) );
2874 connect( d->unmounted, SIGNAL( iconChanged( const QString& ) ),
2875 this, SIGNAL( changed() ) );
2877 connect( d->device, SIGNAL( textChanged( const QString & ) ),
2878 this, SLOT( slotDeviceChanged() ) );
2881 KDevicePropsPlugin::~KDevicePropsPlugin()
2883 delete d;
2886 // QString KDevicePropsPlugin::tabName () const
2887 // {
2888 // return i18n ("De&vice");
2889 // }
2891 void KDevicePropsPlugin::updateInfo()
2893 // we show it in the slot when we know the values
2894 d->m_freeSpaceText->hide();
2895 d->m_freeSpaceLabel->hide();
2896 d->m_freeSpaceBar->hide();
2898 if ( !d->mountpoint->text().isEmpty() )
2900 KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( d->mountpoint->text() );
2901 slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
2905 void KDevicePropsPlugin::slotActivated( int index )
2907 // Update mountpoint so that it matches the device that was selected in the combo
2908 d->device->setEditText( d->m_devicelist[index] );
2909 d->mountpoint->setText( d->mountpointlist[index] );
2911 updateInfo();
2914 void KDevicePropsPlugin::slotDeviceChanged()
2916 // Update mountpoint so that it matches the typed device
2917 int index = d->m_devicelist.indexOf( d->device->currentText() );
2918 if ( index != -1 )
2919 d->mountpoint->setText( d->mountpointlist[index] );
2920 else
2921 d->mountpoint->setText( QString() );
2923 updateInfo();
2926 void KDevicePropsPlugin::slotFoundMountPoint( const QString&,
2927 quint64 kibSize,
2928 quint64 /*kibUsed*/,
2929 quint64 kibAvail )
2931 d->m_freeSpaceText->show();
2932 d->m_freeSpaceLabel->show();
2934 int percUsed = 100 - (int)(100.0 * kibAvail / kibSize);
2936 d->m_freeSpaceLabel->setText(
2937 i18nc("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)",
2938 KIO::convertSizeFromKiB(kibAvail),
2939 KIO::convertSizeFromKiB(kibSize),
2940 100 - (int)(100.0 * kibAvail / kibSize) ));
2942 d->m_freeSpaceBar->setRange(0, 100);
2943 d->m_freeSpaceBar->setValue(percUsed);
2944 d->m_freeSpaceBar->show();
2947 bool KDevicePropsPlugin::supports( const KFileItemList& _items )
2949 if ( _items.count() != 1 )
2950 return false;
2951 const KFileItem item = _items.first();
2952 // check if desktop file
2953 if (!item.isDesktopFile())
2954 return false;
2956 // open file and check type
2957 bool isLocal;
2958 KUrl url = item.mostLocalUrl(isLocal);
2959 if (!isLocal) {
2960 return false;
2963 KDesktopFile config( url.path() );
2964 return config.hasDeviceType();
2967 void KDevicePropsPlugin::applyChanges()
2969 KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
2970 if ( !url.isLocalFile() )
2971 return;
2972 QString path = url.toLocalFile();
2974 QFile f( path );
2975 if ( !f.open( QIODevice::ReadWrite ) )
2977 KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient "
2978 "access to write to <b>%1</b>.</qt>", path));
2979 return;
2981 f.close();
2983 KDesktopFile _config( path );
2984 KConfigGroup config = _config.desktopGroup();
2985 config.writeEntry( "Type", QString::fromLatin1("FSDevice") );
2987 config.writeEntry( "Dev", d->device->currentText() );
2988 config.writeEntry( "MountPoint", d->mountpoint->text() );
2990 config.writeEntry( "UnmountIcon", d->unmounted->icon() );
2991 kDebug(250) << "d->unmounted->icon() = " << d->unmounted->icon();
2993 config.writeEntry( "ReadOnly", d->readonly->isChecked() );
2995 config.sync();
2999 /* ----------------------------------------------------
3001 * KDesktopPropsPlugin
3003 * -------------------------------------------------- */
3005 class KDesktopPropsPlugin::KDesktopPropsPluginPrivate
3007 public:
3008 KDesktopPropsPluginPrivate()
3009 : w( new Ui_KPropertiesDesktopBase )
3010 , m_frame( new QFrame() )
3013 ~KDesktopPropsPluginPrivate()
3015 delete w;
3017 Ui_KPropertiesDesktopBase* w;
3018 QWidget *m_frame;
3020 QString m_origCommandStr;
3021 QString m_terminalOptionStr;
3022 QString m_suidUserStr;
3023 QString m_dbusStartupType;
3024 QString m_dbusServiceName;
3025 bool m_terminalBool;
3026 bool m_suidBool;
3027 bool m_startupBool;
3028 bool m_systrayBool;
3031 KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props )
3032 : KPropertiesDialogPlugin( _props ), d( new KDesktopPropsPluginPrivate )
3034 properties->addPage(d->m_frame, i18n("&Application"));
3036 d->w->setupUi(d->m_frame);
3038 bool bKDesktopMode = (qApp->objectName() == "kdesktop");
3040 if (bKDesktopMode)
3042 // Hide Name entry
3043 d->w->nameEdit->hide();
3044 d->w->nameLabel->hide();
3047 d->w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
3048 d->w->pathEdit->lineEdit()->setAcceptDrops(false);
3050 connect( d->w->nameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
3051 connect( d->w->genNameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
3052 connect( d->w->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
3053 connect( d->w->commandEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
3054 connect( d->w->pathEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
3056 connect( d->w->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
3057 connect( d->w->addFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotAddFiletype() ) );
3058 connect( d->w->delFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotDelFiletype() ) );
3059 connect( d->w->advancedButton, SIGNAL( clicked() ), this, SLOT( slotAdvanced() ) );
3061 // now populate the page
3063 KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
3064 if (!url.isLocalFile()) {
3065 return;
3067 QString path = url.toLocalFile();
3069 QFile f( path );
3070 if ( !f.open( QIODevice::ReadOnly ) )
3071 return;
3072 f.close();
3074 KDesktopFile _config( path );
3075 KConfigGroup config = _config.desktopGroup();
3076 QString nameStr = _config.readName();
3077 QString genNameStr = _config.readGenericName();
3078 QString commentStr = _config.readComment();
3079 QString commandStr = config.readEntry( "Exec", QString() );
3080 if (commandStr.startsWith(QLatin1String("ksystraycmd ")))
3082 commandStr.remove(0, 12);
3083 d->m_systrayBool = true;
3085 else
3086 d->m_systrayBool = false;
3088 d->m_origCommandStr = commandStr;
3089 QString pathStr = config.readEntry( "Path", QString() ); // not readPathEntry, see kservice.cpp
3090 d->m_terminalBool = config.readEntry( "Terminal", false );
3091 d->m_terminalOptionStr = config.readEntry( "TerminalOptions" );
3092 d->m_suidBool = config.readEntry( "X-KDE-SubstituteUID", false );
3093 d->m_suidUserStr = config.readEntry( "X-KDE-Username" );
3094 if( config.hasKey( "StartupNotify" ))
3095 d->m_startupBool = config.readEntry( "StartupNotify", true );
3096 else
3097 d->m_startupBool = config.readEntry( "X-KDE-StartupNotify", true );
3098 d->m_dbusStartupType = config.readEntry("X-DBUS-StartupType").toLower();
3099 //Compatibility
3100 if( d->m_dbusStartupType.isEmpty() && config.hasKey("X-DCOP-ServiceType"))
3101 d->m_dbusStartupType = config.readEntry("X-DCOP-ServiceType").toLower();
3102 // ### should there be a GUI for this setting?
3103 // At least we're copying it over to the local file, to avoid side effects (#157853)
3104 d->m_dbusServiceName = config.readEntry("X-DBUS-ServiceName");
3106 const QStringList mimeTypes = config.readXdgListEntry( "MimeType" );
3108 if ( nameStr.isEmpty() || bKDesktopMode ) {
3109 // We'll use the file name if no name is specified
3110 // because we _need_ a Name for a valid file.
3111 // But let's do it in apply, not here, so that we pick up the right name.
3112 setDirty();
3114 if ( !bKDesktopMode )
3115 d->w->nameEdit->setText(nameStr);
3117 d->w->genNameEdit->setText( genNameStr );
3118 d->w->commentEdit->setText( commentStr );
3119 d->w->commandEdit->setText( commandStr );
3120 d->w->pathEdit->lineEdit()->setText( pathStr );
3122 // was: d->w->filetypeList->setFullWidth(true);
3123 // d->w->filetypeList->header()->setStretchEnabled(true, d->w->filetypeList->columns()-1);
3125 KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
3126 for(QStringList::ConstIterator it = mimeTypes.begin();
3127 it != mimeTypes.end(); )
3129 KMimeType::Ptr p = KMimeType::mimeType(*it, KMimeType::ResolveAliases);
3130 ++it;
3131 QString preference;
3132 if (it != mimeTypes.end())
3134 bool numeric;
3135 (*it).toInt(&numeric);
3136 if (numeric)
3138 preference = *it;
3139 ++it;
3142 if (p)
3144 QTreeWidgetItem *item = new QTreeWidgetItem();
3145 item->setText(0, p->name());
3146 item->setText(1, p->comment());
3147 item->setText(2, preference);
3148 d->w->filetypeList->addTopLevelItem(item);
3151 d->w->filetypeList->resizeColumnToContents(0);
3155 KDesktopPropsPlugin::~KDesktopPropsPlugin()
3157 delete d;
3160 void KDesktopPropsPlugin::slotAddFiletype()
3162 KMimeTypeChooserDialog dlg( i18n("Add File Type for %1", properties->kurl().fileName()),
3163 i18n("Select one or more file types to add:"),
3164 QStringList(), // no preselected mimetypes
3165 QString(),
3166 QStringList(),
3167 KMimeTypeChooser::Comments|KMimeTypeChooser::Patterns,
3168 d->m_frame );
3170 if (dlg.exec() == KDialog::Accepted)
3172 foreach(const QString &mimetype, dlg.chooser()->mimeTypes())
3174 KMimeType::Ptr p = KMimeType::mimeType(mimetype);
3175 if (!p)
3176 continue;
3178 bool found = false;
3179 int count = d->w->filetypeList->topLevelItemCount();
3180 for (int i = 0; !found && i < count; ++i) {
3181 if (d->w->filetypeList->topLevelItem(i)->text(0) == mimetype) {
3182 found = true;
3185 if (!found) {
3186 QTreeWidgetItem *item = new QTreeWidgetItem();
3187 item->setText(0, p->name());
3188 item->setText(1, p->comment());
3189 d->w->filetypeList->addTopLevelItem(item);
3191 d->w->filetypeList->resizeColumnToContents(0);
3194 emit changed();
3197 void KDesktopPropsPlugin::slotDelFiletype()
3199 QTreeWidgetItem *cur = d->w->filetypeList->currentItem();
3200 if (cur) {
3201 delete cur;
3202 emit changed();
3206 void KDesktopPropsPlugin::checkCommandChanged()
3208 if (KRun::binaryName(d->w->commandEdit->text(), true) !=
3209 KRun::binaryName(d->m_origCommandStr, true))
3211 d->m_origCommandStr = d->w->commandEdit->text();
3212 d->m_dbusStartupType.clear(); // Reset
3213 d->m_dbusServiceName.clear();
3217 void KDesktopPropsPlugin::applyChanges()
3219 kDebug(250) << "KDesktopPropsPlugin::applyChanges";
3221 KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
3222 if (!url.isLocalFile()) {
3223 //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
3224 return;
3226 QString path = url.toLocalFile();
3228 QFile f( path );
3230 if ( !f.open( QIODevice::ReadWrite ) ) {
3231 KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
3232 "sufficient access to write to <b>%1</b>.</qt>", path));
3233 return;
3235 f.close();
3237 // If the command is changed we reset certain settings that are strongly
3238 // coupled to the command.
3239 checkCommandChanged();
3241 KDesktopFile _config( path );
3242 KConfigGroup config = _config.desktopGroup();
3243 config.writeEntry( "Type", QString::fromLatin1("Application"));
3244 config.writeEntry( "Comment", d->w->commentEdit->text() );
3245 config.writeEntry( "Comment", d->w->commentEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
3246 config.writeEntry( "GenericName", d->w->genNameEdit->text() );
3247 config.writeEntry( "GenericName", d->w->genNameEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
3249 if (d->m_systrayBool)
3250 config.writeEntry( "Exec", d->w->commandEdit->text().prepend("ksystraycmd ") );
3251 else
3252 config.writeEntry( "Exec", d->w->commandEdit->text() );
3253 config.writeEntry( "Path", d->w->pathEdit->lineEdit()->text() ); // not writePathEntry, see kservice.cpp
3255 // Write mimeTypes
3256 QStringList mimeTypes;
3257 int count = d->w->filetypeList->topLevelItemCount();
3258 for (int i = 0; i < count; ++i) {
3259 QTreeWidgetItem *item = d->w->filetypeList->topLevelItem(i);
3260 QString preference = item->text(2);
3261 mimeTypes.append(item->text(0));
3262 if (!preference.isEmpty())
3263 mimeTypes.append(preference);
3266 kDebug() << mimeTypes;
3267 config.writeXdgListEntry( "MimeType", mimeTypes );
3269 if ( !d->w->nameEdit->isHidden() ) {
3270 QString nameStr = d->w->nameEdit->text();
3271 config.writeEntry( "Name", nameStr );
3272 config.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized );
3275 config.writeEntry("Terminal", d->m_terminalBool);
3276 config.writeEntry("TerminalOptions", d->m_terminalOptionStr);
3277 config.writeEntry("X-KDE-SubstituteUID", d->m_suidBool);
3278 config.writeEntry("X-KDE-Username", d->m_suidUserStr);
3279 config.writeEntry("StartupNotify", d->m_startupBool);
3280 config.writeEntry("X-DBUS-StartupType", d->m_dbusStartupType);
3281 config.writeEntry("X-DBUS-ServiceName", d->m_dbusServiceName);
3282 config.sync();
3284 // KSycoca update needed?
3285 QString sycocaPath = KGlobal::dirs()->relativeLocation("apps", path);
3286 bool updateNeeded = !sycocaPath.startsWith('/');
3287 if (!updateNeeded)
3289 sycocaPath = KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
3290 updateNeeded = !sycocaPath.startsWith('/');
3292 if (updateNeeded)
3293 KBuildSycocaProgressDialog::rebuildKSycoca(d->m_frame);
3297 void KDesktopPropsPlugin::slotBrowseExec()
3299 KUrl f = KFileDialog::getOpenUrl( KUrl(),
3300 QString(), d->m_frame );
3301 if ( f.isEmpty() )
3302 return;
3304 if ( !f.isLocalFile()) {
3305 KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported."));
3306 return;
3309 QString path = f.toLocalFile();
3310 path = KShell::quoteArg( path );
3311 d->w->commandEdit->setText( path );
3314 void KDesktopPropsPlugin::slotAdvanced()
3316 KDialog dlg( d->m_frame );
3317 dlg.setObjectName( "KPropertiesDesktopAdv" );
3318 dlg.setModal( true );
3319 dlg.setCaption( i18n("Advanced Options for %1", properties->kurl().fileName()) );
3320 dlg.setButtons( KDialog::Ok | KDialog::Cancel );
3321 dlg.setDefaultButton( KDialog::Ok );
3322 Ui_KPropertiesDesktopAdvBase w;
3323 w.setupUi(dlg.mainWidget());
3325 // If the command is changed we reset certain settings that are strongly
3326 // coupled to the command.
3327 checkCommandChanged();
3329 // check to see if we use konsole if not do not add the nocloseonexit
3330 // because we don't know how to do this on other terminal applications
3331 KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
3332 QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
3333 QString::fromLatin1("konsole"));
3335 bool terminalCloseBool = false;
3337 if (preferredTerminal == "konsole")
3339 terminalCloseBool = (d->m_terminalOptionStr.contains( "--noclose" ) > 0);
3340 w.terminalCloseCheck->setChecked(terminalCloseBool);
3341 d->m_terminalOptionStr.remove( "--noclose");
3343 else
3345 w.terminalCloseCheck->hide();
3348 w.terminalCheck->setChecked(d->m_terminalBool);
3349 w.terminalEdit->setText(d->m_terminalOptionStr);
3350 w.terminalCloseCheck->setEnabled(d->m_terminalBool);
3351 w.terminalEdit->setEnabled(d->m_terminalBool);
3352 w.terminalEditLabel->setEnabled(d->m_terminalBool);
3354 w.suidCheck->setChecked(d->m_suidBool);
3355 w.suidEdit->setText(d->m_suidUserStr);
3356 w.suidEdit->setEnabled(d->m_suidBool);
3357 w.suidEditLabel->setEnabled(d->m_suidBool);
3359 w.startupInfoCheck->setChecked(d->m_startupBool);
3360 w.systrayCheck->setChecked(d->m_systrayBool);
3362 if (d->m_dbusStartupType == "unique")
3363 w.dbusCombo->setCurrentIndex(2);
3364 else if (d->m_dbusStartupType == "multi")
3365 w.dbusCombo->setCurrentIndex(1);
3366 else if (d->m_dbusStartupType == "wait")
3367 w.dbusCombo->setCurrentIndex(3);
3368 else
3369 w.dbusCombo->setCurrentIndex(0);
3371 // Provide username completion up to 1000 users.
3372 KCompletion *kcom = new KCompletion;
3373 kcom->setOrder(KCompletion::Sorted);
3374 struct passwd *pw;
3375 int i, maxEntries = 1000;
3376 setpwent();
3377 for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
3378 kcom->addItem(QString::fromLatin1(pw->pw_name));
3379 endpwent();
3380 if (i < maxEntries)
3382 w.suidEdit->setCompletionObject(kcom, true);
3383 w.suidEdit->setAutoDeleteCompletionObject( true );
3384 w.suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
3386 else
3388 delete kcom;
3391 connect( w.terminalEdit, SIGNAL( textChanged( const QString & ) ),
3392 this, SIGNAL( changed() ) );
3393 connect( w.terminalCloseCheck, SIGNAL( toggled( bool ) ),
3394 this, SIGNAL( changed() ) );
3395 connect( w.terminalCheck, SIGNAL( toggled( bool ) ),
3396 this, SIGNAL( changed() ) );
3397 connect( w.suidCheck, SIGNAL( toggled( bool ) ),
3398 this, SIGNAL( changed() ) );
3399 connect( w.suidEdit, SIGNAL( textChanged( const QString & ) ),
3400 this, SIGNAL( changed() ) );
3401 connect( w.startupInfoCheck, SIGNAL( toggled( bool ) ),
3402 this, SIGNAL( changed() ) );
3403 connect( w.systrayCheck, SIGNAL( toggled( bool ) ),
3404 this, SIGNAL( changed() ) );
3405 connect( w.dbusCombo, SIGNAL( activated( int ) ),
3406 this, SIGNAL( changed() ) );
3408 if ( dlg.exec() == QDialog::Accepted )
3410 d->m_terminalOptionStr = w.terminalEdit->text().trimmed();
3411 d->m_terminalBool = w.terminalCheck->isChecked();
3412 d->m_suidBool = w.suidCheck->isChecked();
3413 d->m_suidUserStr = w.suidEdit->text().trimmed();
3414 d->m_startupBool = w.startupInfoCheck->isChecked();
3415 d->m_systrayBool = w.systrayCheck->isChecked();
3417 if (w.terminalCloseCheck->isChecked())
3419 d->m_terminalOptionStr.append(" --noclose");
3422 switch(w.dbusCombo->currentIndex())
3424 case 1: d->m_dbusStartupType = "multi"; break;
3425 case 2: d->m_dbusStartupType = "unique"; break;
3426 case 3: d->m_dbusStartupType = "wait"; break;
3427 default: d->m_dbusStartupType = "none"; break;
3432 bool KDesktopPropsPlugin::supports( const KFileItemList& _items )
3434 if ( _items.count() != 1 ) {
3435 return false;
3438 const KFileItem item = _items.first();
3440 // check if desktop file
3441 if (!item.isDesktopFile()) {
3442 return false;
3445 // open file and check type
3446 bool isLocal;
3447 KUrl url = item.mostLocalUrl( isLocal );
3448 if (!isLocal) {
3449 return false;
3452 KDesktopFile config( url.path() );
3453 return config.hasApplicationType() &&
3454 KAuthorized::authorize("run_desktop_files") &&
3455 KAuthorized::authorize("shell_access");
3458 #include "kpropertiesdialog.moc"
3459 #include "kpropertiesdialog_p.moc"