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,
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 KDialogBase by
39
* Simon Hausmann <hausmann@kde.org>
40
* KDesktopPropsPlugin by
41
* Waldo Bastian <bastian@kde.org>
49
#include <sys/types.h>
60
#include <qpushbutton.h>
61
#include <qcheckbox.h>
63
#include <qstringlist.h>
64
#include <qtextstream.h>
67
#include <qcombobox.h>
68
#include <qgroupbox.h>
69
#include <qwhatsthis.h>
72
#include <qprogressbar.h>
74
#include <qvaluevector.h>
78
#include <sys/param.h>
79
#ifdef HAVE_SYS_MOUNT_H
80
#include <sys/mount.h>
82
#ifdef HAVE_SYS_XATTR_H
83
#include <sys/xattr.h>
88
#include <kapplication.h>
91
#include <kdirwatch.h>
92
#include <kdirnotify_stub.h>
93
#include <kdiskfreesp.h>
95
#include <kdesktopfile.h>
96
#include <kicondialog.h>
98
#include <kurlrequester.h>
101
#include <kglobalsettings.h>
102
#include <kstandarddirs.h>
104
#include <kio/chmodjob.h>
105
#include <kio/renamedlg.h>
106
#include <kio/netaccess.h>
107
#include <kio/kservicetypefactory.h>
108
#include <kfiledialog.h>
109
#include <kmimetype.h>
110
#include <kmountpoint.h>
111
#include <kiconloader.h>
112
#include <kmessagebox.h>
113
#include <kservice.h>
114
#include <kcompletion.h>
115
#include <klineedit.h>
116
#include <kseparator.h>
117
#include <ksqueezedtextlabel.h>
118
#include <klibloader.h>
120
#include <kparts/componentfactory.h>
121
#include <kmetaprops.h>
122
#include <kpreviewprops.h>
123
#include <kprocess.h>
125
#include <klistview.h>
127
#include "kfilesharedlg.h"
129
#include "kpropertiesdesktopbase.h"
130
#include "kpropertiesdesktopadvbase.h"
131
#include "kpropertiesmimetypebase.h"
133
#include "kacleditwidget.h"
136
#include "kpropertiesdialog.h"
139
# include <win32_utils.h>
142
static QString nameFromFileName(QString nameStr)
144
if ( nameStr.endsWith(".desktop") )
145
nameStr.truncate( nameStr.length() - 8 );
146
if ( nameStr.endsWith(".kdelnk") )
147
nameStr.truncate( nameStr.length() - 7 );
148
// Make it human-readable (%2F => '/', ...)
149
nameStr = KIO::decodeFileName( nameStr );
153
mode_t KFilePermissionsPropsPlugin::fperm[3][4] = {
154
{S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
155
{S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
156
{S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
159
class KPropertiesDialog::KPropertiesDialogPrivate
162
KPropertiesDialogPrivate()
167
~KPropertiesDialogPrivate()
171
QWidget* fileSharePage;
174
KPropertiesDialog::KPropertiesDialog (KFileItem* item,
175
QWidget* parent, const char* name,
176
bool modal, bool autoShow)
177
: KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(KIO::decodeFileName(item->url().fileName())),
178
KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
181
d = new KPropertiesDialogPrivate;
183
m_items.append( new KFileItem(*item) ); // deep copy
185
m_singleUrl = item->url();
186
assert(!m_singleUrl.isEmpty());
188
init (modal, autoShow);
191
KPropertiesDialog::KPropertiesDialog (const QString& title,
192
QWidget* parent, const char* name, bool modal)
193
: KDialogBase (KDialogBase::Tabbed, i18n ("Properties for %1").arg(title),
194
KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
197
d = new KPropertiesDialogPrivate;
202
KPropertiesDialog::KPropertiesDialog (KFileItemList _items,
203
QWidget* parent, const char* name,
204
bool modal, bool autoShow)
205
: KDialogBase (KDialogBase::Tabbed,
206
// TODO: replace <never used> with "Properties for 1 item". It's very confusing how it has to be translated otherwise
207
// (empty translation before the "\n" is not allowed by msgfmt...)
208
_items.count()>1 ? i18n( "<never used>","Properties for %n Selected Items",_items.count()) :
209
i18n( "Properties for %1" ).arg(KIO::decodeFileName(_items.first()->url().fileName())),
210
KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
213
d = new KPropertiesDialogPrivate;
215
assert( !_items.isEmpty() );
216
m_singleUrl = _items.first()->url();
217
assert(!m_singleUrl.isEmpty());
219
KFileItemListIterator it ( _items );
221
for ( ; it.current(); ++it )
222
m_items.append( new KFileItem( **it ) );
224
init (modal, autoShow);
227
#ifndef KDE_NO_COMPAT
228
KPropertiesDialog::KPropertiesDialog (const KURL& _url, mode_t /* _mode is now unused */,
229
QWidget* parent, const char* name,
230
bool modal, bool autoShow)
231
: KDialogBase (KDialogBase::Tabbed,
232
i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())),
233
KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
234
parent, name, modal),
237
d = new KPropertiesDialogPrivate;
241
KIO::NetAccess::stat(_url, entry, parent);
243
m_items.append( new KFileItem( entry, _url ) );
244
init (modal, autoShow);
248
KPropertiesDialog::KPropertiesDialog (const KURL& _url,
249
QWidget* parent, const char* name,
250
bool modal, bool autoShow)
251
: KDialogBase (KDialogBase::Tabbed,
252
i18n( "Properties for %1" ).arg(KIO::decodeFileName(_url.fileName())),
253
KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
254
parent, name, modal),
257
d = new KPropertiesDialogPrivate;
261
KIO::NetAccess::stat(_url, entry, parent);
263
m_items.append( new KFileItem( entry, _url ) );
264
init (modal, autoShow);
267
KPropertiesDialog::KPropertiesDialog (const KURL& _tempUrl, const KURL& _currentDir,
268
const QString& _defaultName,
269
QWidget* parent, const char* name,
270
bool modal, bool autoShow)
271
: KDialogBase (KDialogBase::Tabbed,
272
i18n( "Properties for %1" ).arg(KIO::decodeFileName(_tempUrl.fileName())),
273
KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok,
274
parent, name, modal),
276
m_singleUrl( _tempUrl ),
277
m_defaultName( _defaultName ),
278
m_currentDir( _currentDir )
280
d = new KPropertiesDialogPrivate;
282
assert(!m_singleUrl.isEmpty());
284
// Create the KFileItem for the _template_ file, in order to read from it.
285
m_items.append( new KFileItem( KFileItem::Unknown, KFileItem::Unknown, m_singleUrl ) );
286
init (modal, autoShow);
289
bool KPropertiesDialog::showDialog(KFileItem* item, QWidget* parent,
290
const char* name, bool modal)
293
QString localPath = item->localPath();
294
if (!localPath.isEmpty())
295
return showWin32FilePropertyDialog(localPath);
297
new KPropertiesDialog(item, parent, name, modal);
301
bool KPropertiesDialog::showDialog(const KURL& _url, QWidget* parent,
302
const char* name, bool modal)
305
if (_url.isLocalFile())
306
return showWin32FilePropertyDialog( _url.path() );
308
new KPropertiesDialog(_url, parent, name, modal);
312
bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent,
313
const char* name, bool modal)
315
if (_items.count()==1)
316
return KPropertiesDialog::showDialog(_items.getFirst(), parent, name, modal);
317
new KPropertiesDialog(_items, parent, name, modal);
321
void KPropertiesDialog::init (bool modal, bool autoShow)
323
m_pageList.setAutoDelete( true );
324
m_items.setAutoDelete( true );
337
void KPropertiesDialog::showFileSharingPage()
339
if (d->fileSharePage) {
340
showPage( pageIndex( d->fileSharePage));
344
void KPropertiesDialog::setFileSharingPage(QWidget* page) {
345
d->fileSharePage = page;
349
void KPropertiesDialog::setFileNameReadOnly( bool ro )
353
for ( it=m_pageList.first(); it != 0L; it=m_pageList.next() )
355
KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it);
357
plugin->setFileNameReadOnly( ro );
363
void KPropertiesDialog::slotStatResult( KIO::Job * )
367
KPropertiesDialog::~KPropertiesDialog()
373
void KPropertiesDialog::insertPlugin (KPropsDlgPlugin* plugin)
375
connect (plugin, SIGNAL (changed ()),
376
plugin, SLOT (setDirty ()));
378
m_pageList.append (plugin);
381
bool KPropertiesDialog::canDisplay( KFileItemList _items )
383
// TODO: cache the result of those calls. Currently we parse .desktop files far too many times
384
return KFilePropsPlugin::supports( _items ) ||
385
KFilePermissionsPropsPlugin::supports( _items ) ||
386
KDesktopPropsPlugin::supports( _items ) ||
387
KBindingPropsPlugin::supports( _items ) ||
388
KURLPropsPlugin::supports( _items ) ||
389
KDevicePropsPlugin::supports( _items ) ||
390
KFileMetaPropsPlugin::supports( _items ) ||
391
KPreviewPropsPlugin::supports( _items );
394
void KPropertiesDialog::slotOk()
396
KPropsDlgPlugin *page;
397
d->m_aborted = false;
399
KFilePropsPlugin * filePropsPlugin = 0L;
400
if ( m_pageList.first()->isA("KFilePropsPlugin") )
401
filePropsPlugin = static_cast<KFilePropsPlugin *>(m_pageList.first());
403
// If any page is dirty, then set the main one (KFilePropsPlugin) as
404
// dirty too. This is what makes it possible to save changes to a global
405
// desktop file into a local one. In other cases, it doesn't hurt.
406
for ( page = m_pageList.first(); page != 0L; page = m_pageList.next() )
407
if ( page->isDirty() && filePropsPlugin )
409
filePropsPlugin->setDirty();
413
// Apply the changes in the _normal_ order of the tabs now
414
// This is because in case of renaming a file, KFilePropsPlugin will call
415
// KPropertiesDialog::rename, so other tab will be ok with whatever order
416
// BUT for file copied from templates, we need to do the renaming first !
417
for ( page = m_pageList.first(); page != 0L && !d->m_aborted; page = m_pageList.next() )
418
if ( page->isDirty() )
420
kdDebug( 250 ) << "applying changes for " << page->className() << endl;
421
page->applyChanges();
422
// applyChanges may change d->m_aborted.
425
kdDebug( 250 ) << "skipping page " << page->className() << endl;
427
if ( !d->m_aborted && filePropsPlugin )
428
filePropsPlugin->postApplyChanges();
433
emit propertiesClosed();
436
} // else, keep dialog open for user to fix the problem.
439
void KPropertiesDialog::slotCancel()
442
emit propertiesClosed();
448
void KPropertiesDialog::insertPages()
450
if (m_items.isEmpty())
453
if ( KFilePropsPlugin::supports( m_items ) )
455
KPropsDlgPlugin *p = new KFilePropsPlugin( this );
459
if ( KFilePermissionsPropsPlugin::supports( m_items ) )
461
KPropsDlgPlugin *p = new KFilePermissionsPropsPlugin( this );
465
if ( KDesktopPropsPlugin::supports( m_items ) )
467
KPropsDlgPlugin *p = new KDesktopPropsPlugin( this );
471
if ( KBindingPropsPlugin::supports( m_items ) )
473
KPropsDlgPlugin *p = new KBindingPropsPlugin( this );
477
if ( KURLPropsPlugin::supports( m_items ) )
479
KPropsDlgPlugin *p = new KURLPropsPlugin( this );
483
if ( KDevicePropsPlugin::supports( m_items ) )
485
KPropsDlgPlugin *p = new KDevicePropsPlugin( this );
489
if ( KFileMetaPropsPlugin::supports( m_items ) )
491
KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this );
495
if ( KPreviewPropsPlugin::supports( m_items ) )
497
KPropsDlgPlugin *p = new KPreviewPropsPlugin( this );
501
if ( kapp->authorizeKAction("sharefile") &&
502
KFileSharePropsPlugin::supports( m_items ) )
504
KPropsDlgPlugin *p = new KFileSharePropsPlugin( this );
510
if ( m_items.count() != 1 )
513
KFileItem *item = m_items.first();
514
QString mimetype = item->mimetype();
516
if ( mimetype.isEmpty() )
519
QString query = QString::fromLatin1(
520
"('KPropsDlg/Plugin' in ServiceTypes) and "
521
"((not exist [X-KDE-Protocol]) or "
522
" ([X-KDE-Protocol] == '%1' ) )" ).arg(item->url().protocol());
524
kdDebug( 250 ) << "trader query: " << query << endl;
525
KTrader::OfferList offers = KTrader::self()->query( mimetype, query );
526
KTrader::OfferList::ConstIterator it = offers.begin();
527
KTrader::OfferList::ConstIterator end = offers.end();
528
for (; it != end; ++it )
530
KPropsDlgPlugin *plugin = KParts::ComponentFactory
531
::createInstanceFromLibrary<KPropsDlgPlugin>( (*it)->library().local8Bit().data(),
533
(*it)->name().latin1() );
537
insertPlugin( plugin );
541
void KPropertiesDialog::updateUrl( const KURL& _newUrl )
543
Q_ASSERT( m_items.count() == 1 );
544
kdDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url() << endl;
545
KURL newUrl = _newUrl;
546
emit saveAs(m_singleUrl, newUrl);
547
kdDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url() << endl;
549
m_singleUrl = newUrl;
550
m_items.first()->setURL( newUrl );
551
assert(!m_singleUrl.isEmpty());
552
// If we have an Desktop page, set it dirty, so that a full file is saved locally
553
// Same for a URL page (because of the Name= hack)
554
for ( QPtrListIterator<KPropsDlgPlugin> it(m_pageList); it.current(); ++it )
555
if ( it.current()->isA("KExecPropsPlugin") || // KDE4 remove me
556
it.current()->isA("KURLPropsPlugin") ||
557
it.current()->isA("KDesktopPropsPlugin"))
559
//kdDebug(250) << "Setting page dirty" << endl;
560
it.current()->setDirty();
565
void KPropertiesDialog::rename( const QString& _name )
567
Q_ASSERT( m_items.count() == 1 );
568
kdDebug(250) << "KPropertiesDialog::rename " << _name << endl;
570
// if we're creating from a template : use currentdir
571
if ( !m_currentDir.isEmpty() )
573
newUrl = m_currentDir;
574
newUrl.addPath( _name );
578
QString tmpurl = m_singleUrl.url();
579
if ( tmpurl.at(tmpurl.length() - 1) == '/')
580
// It's a directory, so strip the trailing slash first
581
tmpurl.truncate( tmpurl.length() - 1);
583
newUrl.setFileName( _name );
588
void KPropertiesDialog::abortApplying()
593
class KPropsDlgPlugin::KPropsDlgPluginPrivate
596
KPropsDlgPluginPrivate()
599
~KPropsDlgPluginPrivate()
606
KPropsDlgPlugin::KPropsDlgPlugin( KPropertiesDialog *_props )
607
: QObject( _props, 0L )
609
d = new KPropsDlgPluginPrivate;
611
fontHeight = 2*properties->fontMetrics().height();
615
KPropsDlgPlugin::~KPropsDlgPlugin()
620
bool KPropsDlgPlugin::isDesktopFile( KFileItem * _item )
624
KURL url = _item->mostLocalURL( isLocal );
628
// only regular files
629
if ( !S_ISREG( _item->mode() ) )
632
QString t( url.path() );
635
FILE *f = fopen( QFile::encodeName(t), "r" );
640
// return true if desktop file
641
return ( _item->mimetype() == "application/x-desktop" );
644
void KPropsDlgPlugin::setDirty( bool b )
649
void KPropsDlgPlugin::setDirty()
654
bool KPropsDlgPlugin::isDirty() const
659
void KPropsDlgPlugin::applyChanges()
661
kdWarning(250) << "applyChanges() not implemented in page !" << endl;
664
///////////////////////////////////////////////////////////////////////////////
666
class KFilePropsPlugin::KFilePropsPluginPrivate
669
KFilePropsPluginPrivate()
672
dirSizeUpdateTimer = 0L;
674
m_freeSpaceLabel = 0;
676
~KFilePropsPluginPrivate()
682
KDirSize * dirSizeJob;
683
QTimer *dirSizeUpdateTimer;
689
QLabel *m_freeSpaceLabel;
695
KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props )
696
: KPropsDlgPlugin( _props )
698
d = new KFilePropsPluginPrivate;
699
d->bMultiple = (properties->items().count() > 1);
700
d->bIconChanged = false;
701
d->bKDesktopMode = (QCString(qApp->name()) == "kdesktop"); // nasty heh?
702
d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items());
703
kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple << endl;
705
// We set this data from the first item, and we'll
706
// check that the other items match against it, resetting when not.
708
KFileItem * item = properties->item();
709
KURL url = item->mostLocalURL( isLocal );
710
bool isReallyLocal = item->url().isLocalFile();
711
bool bDesktopFile = isDesktopFile(item);
712
kdDebug() << "url=" << url << " bDesktopFile=" << bDesktopFile << " isLocal=" << isLocal << " isReallyLocal=" << isReallyLocal << endl;
713
mode_t mode = item->mode();
714
bool hasDirs = item->isDir() && !item->isLink();
715
bool hasRoot = url.path() == QString::fromLatin1("/");
716
QString iconStr = KMimeType::iconForURL(url, mode);
717
QString directory = properties->kurl().directory();
718
QString protocol = properties->kurl().protocol();
719
QString mimeComment = item->mimeComment();
720
d->mimeType = item->mimetype();
722
KIO::filesize_t totalSize = item->size(hasTotalSize);
723
QString magicMimeComment;
725
KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
726
if ( magicMimeType->name() != KMimeType::defaultMimeType() )
727
magicMimeComment = magicMimeType->comment();
730
// Those things only apply to 'single file' mode
731
QString filename = QString::null;
732
bool isTrash = false;
733
bool isDevice = false;
734
m_bFromTemplate = false;
736
// And those only to 'multiple' mode
737
uint iDirCount = hasDirs ? 1 : 0;
738
uint iFileCount = 1-iDirCount;
740
d->m_frame = properties->addPage (i18n("&General"));
742
QVBoxLayout *vbl = new QVBoxLayout( d->m_frame, 0,
743
KDialog::spacingHint(), "vbl");
744
QGridLayout *grid = new QGridLayout(0, 3); // unknown rows
745
grid->setColStretch(0, 0);
746
grid->setColStretch(1, 0);
747
grid->setColStretch(2, 1);
748
grid->addColSpacing(1, KDialog::spacingHint());
749
vbl->addLayout(grid);
755
if ( !m_bFromTemplate ) {
756
isTrash = ( properties->kurl().protocol().find( "trash", 0, false)==0 );
757
if ( properties->kurl().protocol().find("device", 0, false)==0)
759
// Extract the full name, but without file: for local files
761
path = properties->kurl().path();
763
path = properties->kurl().prettyURL();
765
path = properties->currentDir().path(1) + properties->defaultName();
766
directory = properties->currentDir().prettyURL();
769
if (KExecPropsPlugin::supports(properties->items()) || // KDE4 remove me
771
KBindingPropsPlugin::supports(properties->items())) {
772
determineRelativePath( path );
775
// Extract the file name only
776
filename = properties->defaultName();
777
if ( filename.isEmpty() ) { // no template
778
filename = item->name(); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system
780
m_bFromTemplate = true;
781
setDirty(); // to enforce that the copy happens
783
d->oldFileName = filename;
785
// Make it human-readable
786
filename = nameFromFileName( filename );
788
if ( d->bKDesktopMode && d->bDesktopFile ) {
789
KDesktopFile config( url.path(), true /* readonly */ );
790
if ( config.hasKey( "Name" ) ) {
791
filename = config.readName();
799
// Multiple items: see what they have in common
800
KFileItemList items = properties->items();
801
KFileItemListIterator it( items );
802
for ( ++it /*no need to check the first one again*/ ; it.current(); ++it )
804
KURL url = (*it)->url();
805
kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyURL() << endl;
806
// The list of things we check here should match the variables defined
807
// at the beginning of this method.
808
if ( url.isLocalFile() != isLocal )
809
isLocal = false; // not all local
810
if ( bDesktopFile && isDesktopFile(*it) != bDesktopFile )
811
bDesktopFile = false; // not all desktop files
812
if ( (*it)->mode() != mode )
814
if ( KMimeType::iconForURL(url, mode) != iconStr )
815
iconStr = "kmultiple";
816
if ( url.directory() != directory )
817
directory = QString::null;
818
if ( url.protocol() != protocol )
819
protocol = QString::null;
820
if ( !mimeComment.isNull() && (*it)->mimeComment() != mimeComment )
821
mimeComment = QString::null;
822
if ( isLocal && !magicMimeComment.isNull() ) {
823
KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
824
if ( magicMimeType->comment() != magicMimeComment )
825
magicMimeComment = QString::null;
828
if ( url.path() == QString::fromLatin1("/") )
830
if ( (*it)->isDir() && !(*it)->isLink() )
839
totalSize += (*it)->size(hasSize);
840
hasTotalSize = hasTotalSize || hasSize;
845
if (!isReallyLocal && !protocol.isEmpty())
849
directory += protocol;
853
if ( !isDevice && !isTrash && (bDesktopFile || S_ISDIR(mode)) && !d->bMultiple /*not implemented for multiple*/ )
855
KIconButton *iconButton = new KIconButton( d->m_frame );
856
int bsize = 66 + 2 * iconButton->style().pixelMetric(QStyle::PM_ButtonMargin);
857
iconButton->setFixedSize(bsize, bsize);
858
iconButton->setIconSize(48);
859
iconButton->setStrictIconSize(false);
860
// This works for everything except Device icons on unmounted devices
861
// So we have to really open .desktop files
862
QString iconStr = KMimeType::findByURL( url, mode )->icon( url, isLocal );
863
if ( bDesktopFile && isLocal )
865
KDesktopFile config( url.path(), true );
866
config.setDesktopGroup();
867
iconStr = config.readEntry( "Icon" );
868
if ( config.hasDeviceType() )
869
iconButton->setIconType( KIcon::Desktop, KIcon::Device );
871
iconButton->setIconType( KIcon::Desktop, KIcon::Application );
873
iconButton->setIconType( KIcon::Desktop, KIcon::FileSystem );
874
iconButton->setIcon(iconStr);
875
iconArea = iconButton;
876
connect( iconButton, SIGNAL( iconChanged(QString) ),
877
this, SLOT( slotIconChanged() ) );
879
QLabel *iconLabel = new QLabel( d->m_frame );
880
int bsize = 66 + 2 * iconLabel->style().pixelMetric(QStyle::PM_ButtonMargin);
881
iconLabel->setFixedSize(bsize, bsize);
882
iconLabel->setPixmap( KGlobal::iconLoader()->loadIcon( iconStr, KIcon::Desktop, 48) );
883
iconArea = iconLabel;
885
grid->addWidget(iconArea, curRow, 0, AlignLeft);
887
if (d->bMultiple || isTrash || isDevice || hasRoot)
889
QLabel *lab = new QLabel(d->m_frame );
891
lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) );
893
lab->setText( filename );
897
d->m_lined = new KLineEdit( d->m_frame );
898
d->m_lined->setText(filename);
899
nameArea = d->m_lined;
900
d->m_lined->setFocus();
902
// Enhanced rename: Don't highlight the file extension.
904
KServiceTypeFactory::self()->findFromPattern( filename, &pattern );
905
if (!pattern.isEmpty() && pattern.at(0)=='*' && pattern.find('*',1)==-1)
906
d->m_lined->setSelection(0, filename.length()-pattern.stripWhiteSpace().length()+1);
909
int lastDot = filename.findRev('.');
911
d->m_lined->setSelection(0, lastDot);
914
connect( d->m_lined, SIGNAL( textChanged( const QString & ) ),
915
this, SLOT( nameFileChanged(const QString & ) ) );
918
grid->addWidget(nameArea, curRow++, 2);
920
KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame);
921
grid->addMultiCellWidget(sep, curRow, curRow, 0, 2);
925
if ( !mimeComment.isEmpty() && !isDevice && !isTrash)
927
l = new QLabel(i18n("Type:"), d->m_frame );
929
grid->addWidget(l, curRow, 0);
931
QHBox *box = new QHBox(d->m_frame);
933
l = new QLabel(mimeComment, box );
936
//TODO: wrap for win32 or mac?
937
QPushButton *button = new QPushButton(box);
939
QIconSet iconSet = SmallIconSet(QString::fromLatin1("configure"));
940
QPixmap pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal );
941
button->setIconSet( iconSet );
942
button->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
943
if ( d->mimeType == KMimeType::defaultMimeType() )
944
QToolTip::add(button, i18n("Create new file type"));
946
QToolTip::add(button, i18n("Edit file type"));
948
connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() ));
950
if (!kapp->authorizeKAction("editfiletype"))
954
grid->addWidget(box, curRow++, 2);
957
if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
959
l = new QLabel(i18n("Contents:"), d->m_frame );
960
grid->addWidget(l, curRow, 0);
962
l = new QLabel(magicMimeComment, d->m_frame );
963
grid->addWidget(l, curRow++, 2);
966
if ( !directory.isEmpty() )
968
l = new QLabel( i18n("Location:"), d->m_frame );
969
grid->addWidget(l, curRow, 0);
971
l = new KSqueezedTextLabel( d->m_frame );
972
l->setText( directory );
973
grid->addWidget(l, curRow++, 2);
976
if( hasDirs || hasTotalSize ) {
977
l = new QLabel(i18n("Size:"), d->m_frame );
978
grid->addWidget(l, curRow, 0);
980
m_sizeLabel = new QLabel( d->m_frame );
981
grid->addWidget( m_sizeLabel, curRow++, 2 );
986
if ( !hasDirs ) // Only files [and symlinks]
989
m_sizeLabel->setText(KIO::convertSizeWithBytes(totalSize));
992
m_sizeDetermineButton = 0L;
993
m_sizeStopButton = 0L;
997
QHBoxLayout * sizelay = new QHBoxLayout(KDialog::spacingHint());
998
grid->addLayout( sizelay, curRow++, 2 );
1001
m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
1002
m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
1003
connect( m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) );
1004
connect( m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) );
1005
sizelay->addWidget(m_sizeDetermineButton, 0);
1006
sizelay->addWidget(m_sizeStopButton, 0);
1007
sizelay->addStretch(10); // so that the buttons don't grow horizontally
1009
// auto-launch for local dirs only, and not for '/'
1010
if ( isLocal && !hasRoot )
1012
m_sizeDetermineButton->setText( i18n("Refresh") );
1013
slotSizeDetermine();
1016
m_sizeStopButton->setEnabled( false );
1019
if (!d->bMultiple && item->isLink()) {
1020
l = new QLabel(i18n("Points to:"), d->m_frame );
1021
grid->addWidget(l, curRow, 0);
1023
l = new KSqueezedTextLabel(item->linkDest(), d->m_frame );
1024
grid->addWidget(l, curRow++, 2);
1027
if (!d->bMultiple) // Dates for multiple don't make much sense...
1031
time_t tim = item->time(KIO::UDS_CREATION_TIME, hasTime);
1034
l = new QLabel(i18n("Created:"), d->m_frame );
1035
grid->addWidget(l, curRow, 0);
1037
dt.setTime_t( tim );
1038
l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1039
grid->addWidget(l, curRow++, 2);
1042
tim = item->time(KIO::UDS_MODIFICATION_TIME, hasTime);
1045
l = new QLabel(i18n("Modified:"), d->m_frame );
1046
grid->addWidget(l, curRow, 0);
1048
dt.setTime_t( tim );
1049
l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1050
grid->addWidget(l, curRow++, 2);
1053
tim = item->time(KIO::UDS_ACCESS_TIME, hasTime);
1056
l = new QLabel(i18n("Accessed:"), d->m_frame );
1057
grid->addWidget(l, curRow, 0);
1059
dt.setTime_t( tim );
1060
l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1061
grid->addWidget(l, curRow++, 2);
1065
if ( isLocal && hasDirs ) // only for directories
1067
sep = new KSeparator( KSeparator::HLine, d->m_frame);
1068
grid->addMultiCellWidget(sep, curRow, curRow, 0, 2);
1071
QString mountPoint = KIO::findPathMountPoint( url.path() );
1073
if (mountPoint != "/")
1075
l = new QLabel(i18n("Mounted on:"), d->m_frame );
1076
grid->addWidget(l, curRow, 0);
1078
l = new KSqueezedTextLabel( mountPoint, d->m_frame );
1079
grid->addWidget( l, curRow++, 2 );
1082
l = new QLabel(i18n("Free disk space:"), d->m_frame );
1083
grid->addWidget(l, curRow, 0);
1085
d->m_freeSpaceLabel = new QLabel( d->m_frame );
1086
grid->addWidget( d->m_freeSpaceLabel, curRow++, 2 );
1088
KDiskFreeSp * job = new KDiskFreeSp;
1089
connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&,
1090
const unsigned long&, const QString& ) ),
1091
this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&,
1092
const unsigned long&, const QString& ) ) );
1093
job->readDF( mountPoint );
1099
// QString KFilePropsPlugin::tabName () const
1101
// return i18n ("&General");
1104
void KFilePropsPlugin::setFileNameReadOnly( bool ro )
1108
d->m_lined->setReadOnly( ro );
1111
// Don't put the initial focus on the line edit when it is ro
1112
QPushButton *button = properties->actionButton(KDialogBase::Ok);
1119
void KFilePropsPlugin::slotEditFileType()
1123
if ( d->mimeType == KMimeType::defaultMimeType() ) {
1124
int pos = d->oldFileName.findRev( '.' );
1126
mime = "*" + d->oldFileName.mid(pos);
1132
//TODO: wrap for win32 or mac?
1133
QString keditfiletype = QString::fromLatin1("keditfiletype");
1134
KRun::runCommand( keditfiletype
1135
+ " --parent " + QString::number( (ulong)properties->topLevelWidget()->winId())
1136
+ " " + KProcess::quote(mime),
1137
keditfiletype, keditfiletype /*unused*/);
1141
void KFilePropsPlugin::slotIconChanged()
1143
d->bIconChanged = true;
1147
void KFilePropsPlugin::nameFileChanged(const QString &text )
1149
properties->enableButtonOK(!text.isEmpty());
1153
void KFilePropsPlugin::determineRelativePath( const QString & path )
1155
// now let's make it relative
1157
if (KBindingPropsPlugin::supports(properties->items()))
1159
m_sRelativePath =KGlobal::dirs()->relativeLocation("mime", path);
1160
if (m_sRelativePath.startsWith("/"))
1161
m_sRelativePath = QString::null;
1165
m_sRelativePath =KGlobal::dirs()->relativeLocation("apps", path);
1166
if (m_sRelativePath.startsWith("/"))
1168
m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
1169
if (m_sRelativePath.startsWith("/"))
1170
m_sRelativePath = QString::null;
1172
m_sRelativePath = path;
1175
if ( m_sRelativePath.isEmpty() )
1177
if (KBindingPropsPlugin::supports(properties->items()))
1178
kdWarning(250) << "Warning : editing a mimetype file out of the mimetype dirs!" << endl;
1182
void KFilePropsPlugin::slotFoundMountPoint( const QString&,
1183
unsigned long kBSize,
1184
unsigned long /*kBUsed*/,
1185
unsigned long kBAvail )
1187
d->m_freeSpaceLabel->setText(
1188
// xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part.
1189
i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
1190
.arg(KIO::convertSizeFromKB(kBAvail))
1191
.arg(KIO::convertSizeFromKB(kBSize))
1192
.arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
1195
// attention: copy&paste below, due to compiler bug
1196
// it doesn't like those unsigned long parameters -- unsigned long& are ok :-/
1197
void KFilePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize,
1198
const unsigned long& /*kBUsed*/,
1199
const unsigned long& kBAvail,
1202
d->m_freeSpaceLabel->setText(
1203
// xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part.
1204
i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
1205
.arg(KIO::convertSizeFromKB(kBAvail))
1206
.arg(KIO::convertSizeFromKB(kBSize))
1207
.arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
1210
void KFilePropsPlugin::slotDirSizeUpdate()
1212
KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1213
KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
1214
KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
1215
m_sizeLabel->setText( i18n("Calculating... %1 (%2)\n%3, %4")
1216
.arg(KIO::convertSize(totalSize))
1217
.arg(KGlobal::locale()->formatNumber(totalSize, 0))
1218
.arg(i18n("1 file","%n files",totalFiles))
1219
.arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs)));
1222
void KFilePropsPlugin::slotDirSizeFinished( KIO::Job * job )
1225
m_sizeLabel->setText( job->errorString() );
1228
KIO::filesize_t totalSize = static_cast<KDirSize*>(job)->totalSize();
1229
KIO::filesize_t totalFiles = static_cast<KDirSize*>(job)->totalFiles();
1230
KIO::filesize_t totalSubdirs = static_cast<KDirSize*>(job)->totalSubdirs();
1231
m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4")
1232
.arg(KIO::convertSize(totalSize))
1233
.arg(KGlobal::locale()->formatNumber(totalSize, 0))
1234
.arg(i18n("1 file","%n files",totalFiles))
1235
.arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs)));
1237
m_sizeStopButton->setEnabled(false);
1238
// just in case you change something and try again :)
1239
m_sizeDetermineButton->setText( i18n("Refresh") );
1240
m_sizeDetermineButton->setEnabled(true);
1242
delete d->dirSizeUpdateTimer;
1243
d->dirSizeUpdateTimer = 0L;
1246
void KFilePropsPlugin::slotSizeDetermine()
1248
m_sizeLabel->setText( i18n("Calculating...") );
1249
kdDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" << properties->item() << endl;
1250
kdDebug(250) << " URL=" << properties->item()->url().url() << endl;
1251
d->dirSizeJob = KDirSize::dirSizeJob( properties->items() );
1252
d->dirSizeUpdateTimer = new QTimer(this);
1253
connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ),
1254
SLOT( slotDirSizeUpdate() ) );
1255
d->dirSizeUpdateTimer->start(500);
1256
connect( d->dirSizeJob, SIGNAL( result( KIO::Job * ) ),
1257
SLOT( slotDirSizeFinished( KIO::Job * ) ) );
1258
m_sizeStopButton->setEnabled(true);
1259
m_sizeDetermineButton->setEnabled(false);
1261
// also update the "Free disk space" display
1262
if ( d->m_freeSpaceLabel )
1265
KFileItem * item = properties->item();
1266
KURL url = item->mostLocalURL( isLocal );
1267
QString mountPoint = KIO::findPathMountPoint( url.path() );
1269
KDiskFreeSp * job = new KDiskFreeSp;
1270
connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&,
1271
const unsigned long&, const QString& ) ),
1272
this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&,
1273
const unsigned long&, const QString& ) ) );
1274
job->readDF( mountPoint );
1278
void KFilePropsPlugin::slotSizeStop()
1280
if ( d->dirSizeJob )
1282
m_sizeLabel->setText( i18n("Stopped") );
1283
d->dirSizeJob->kill();
1286
if ( d->dirSizeUpdateTimer )
1287
d->dirSizeUpdateTimer->stop();
1289
m_sizeStopButton->setEnabled(false);
1290
m_sizeDetermineButton->setEnabled(true);
1293
KFilePropsPlugin::~KFilePropsPlugin()
1298
bool KFilePropsPlugin::supports( KFileItemList /*_items*/ )
1303
// Don't do this at home
1304
void qt_enter_modal( QWidget *widget );
1305
void qt_leave_modal( QWidget *widget );
1307
void KFilePropsPlugin::applyChanges()
1309
if ( d->dirSizeJob )
1312
kdDebug(250) << "KFilePropsPlugin::applyChanges" << endl;
1314
if (nameArea->inherits("QLineEdit"))
1316
QString n = ((QLineEdit *) nameArea)->text();
1317
// Remove trailing spaces (#4345)
1318
while ( n[n.length()-1].isSpace() )
1319
n.truncate( n.length() - 1 );
1322
KMessageBox::sorry( properties, i18n("The new file name is empty."));
1323
properties->abortApplying();
1327
// Do we need to rename the file ?
1328
kdDebug(250) << "oldname = " << oldName << endl;
1329
kdDebug(250) << "newname = " << n << endl;
1330
if ( oldName != n || m_bFromTemplate ) { // true for any from-template file
1331
KIO::Job * job = 0L;
1332
KURL oldurl = properties->kurl();
1334
QString newFileName = KIO::encodeFileName(n);
1335
if (d->bDesktopFile && !newFileName.endsWith(".desktop") && !newFileName.endsWith(".kdelnk"))
1336
newFileName += ".desktop";
1338
// Tell properties. Warning, this changes the result of properties->kurl() !
1339
properties->rename( newFileName );
1341
// Update also relative path (for apps and mimetypes)
1342
if ( !m_sRelativePath.isEmpty() )
1343
determineRelativePath( properties->kurl().path() );
1345
kdDebug(250) << "New URL = " << properties->kurl().url() << endl;
1346
kdDebug(250) << "old = " << oldurl.url() << endl;
1348
// Don't remove the template !!
1349
if ( !m_bFromTemplate ) // (normal renaming)
1350
job = KIO::move( oldurl, properties->kurl() );
1351
else // Copying a template
1352
job = KIO::copy( oldurl, properties->kurl() );
1354
connect( job, SIGNAL( result( KIO::Job * ) ),
1355
SLOT( slotCopyFinished( KIO::Job * ) ) );
1356
connect( job, SIGNAL( renamed( KIO::Job *, const KURL &, const KURL & ) ),
1357
SLOT( slotFileRenamed( KIO::Job *, const KURL &, const KURL & ) ) );
1359
QWidget dummy(0,0,WType_Dialog|WShowModal);
1360
qt_enter_modal(&dummy);
1362
qt_leave_modal(&dummy);
1365
properties->updateUrl(properties->kurl());
1366
// Update also relative path (for apps and mimetypes)
1367
if ( !m_sRelativePath.isEmpty() )
1368
determineRelativePath( properties->kurl().path() );
1371
// No job, keep going
1372
slotCopyFinished( 0L );
1375
void KFilePropsPlugin::slotCopyFinished( KIO::Job * job )
1377
kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl;
1380
// allow apply() to return
1384
job->showErrorDialog( d->m_frame );
1385
// Didn't work. Revert the URL to the old one
1386
properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcURLs().first() );
1387
properties->abortApplying(); // Don't apply the changes to the wrong file !
1392
assert( properties->item() );
1393
assert( !properties->item()->url().isEmpty() );
1395
// Save the file where we can -> usually in ~/.kde/...
1396
if (KBindingPropsPlugin::supports(properties->items()) && !m_sRelativePath.isEmpty())
1399
newURL.setPath( locateLocal("mime", m_sRelativePath) );
1400
properties->updateUrl( newURL );
1402
else if (d->bDesktopFile && !m_sRelativePath.isEmpty())
1404
kdDebug(250) << "KFilePropsPlugin::slotCopyFinished " << m_sRelativePath << endl;
1406
newURL.setPath( KDesktopFile::locateLocal(m_sRelativePath) );
1407
kdDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path() << endl;
1408
properties->updateUrl( newURL );
1411
if ( d->bKDesktopMode && d->bDesktopFile ) {
1412
// Renamed? Update Name field
1413
if ( d->oldFileName != properties->kurl().fileName() || m_bFromTemplate ) {
1414
KDesktopFile config( properties->kurl().path() );
1415
QString nameStr = nameFromFileName(properties->kurl().fileName());
1416
config.writeEntry( "Name", nameStr );
1417
config.writeEntry( "Name", nameStr, true, false, true );
1422
void KFilePropsPlugin::applyIconChanges()
1424
KIconButton *iconButton = ::qt_cast<KIconButton *>( iconArea );
1425
if ( !iconButton || !d->bIconChanged )
1427
// handle icon changes - only local files (or pseudo-local) for now
1428
// TODO: Use KTempFile and KIO::file_copy with overwrite = true
1429
KURL url = properties->kurl();
1430
url = KIO::NetAccess::mostLocalURL( url, properties );
1431
if (url.isLocalFile()) {
1434
if (S_ISDIR(properties->item()->mode()))
1436
path = url.path(1) + QString::fromLatin1(".directory");
1437
// don't call updateUrl because the other tabs (i.e. permissions)
1438
// apply to the directory, not the .directory file.
1443
// Get the default image
1444
QString str = KMimeType::findByURL( url,
1445
properties->item()->mode(),
1446
true )->KServiceType::icon();
1447
// Is it another one than the default ?
1449
if ( str != iconButton->icon() )
1450
sIcon = iconButton->icon();
1451
// (otherwise write empty value)
1453
kdDebug(250) << "**" << path << "**" << endl;
1456
// If default icon and no .directory file -> don't create one
1457
if ( !sIcon.isEmpty() || f.exists() )
1459
if ( !f.open( IO_ReadWrite ) ) {
1460
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
1461
"have sufficient access to write to <b>%1</b>.</qt>").arg(path));
1466
KDesktopFile cfg(path);
1467
kdDebug(250) << "sIcon = " << (sIcon) << endl;
1468
kdDebug(250) << "str = " << (str) << endl;
1469
cfg.writeEntry( "Icon", sIcon );
1475
void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KURL &, const KURL & newUrl )
1477
// This is called in case of an existing local file during the copy/move operation,
1478
// if the user chooses Rename.
1479
properties->updateUrl( newUrl );
1482
void KFilePropsPlugin::postApplyChanges()
1484
// Save the icon only after applying the permissions changes (#46192)
1488
KFileItemList items = properties->items();
1489
for ( KFileItemListIterator it( items ); it.current(); ++it )
1490
lst.append((*it)->url());
1491
KDirNotify_stub allDirNotify("*", "KDirNotify*");
1492
allDirNotify.FilesChanged( lst );
1495
class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
1498
KFilePermissionsPropsPluginPrivate()
1501
~KFilePermissionsPropsPluginPrivate()
1506
QCheckBox *cbRecursive;
1507
QLabel *explanationLabel;
1508
QComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo;
1509
QCheckBox *extraCheckbox;
1510
mode_t partialPermissions;
1511
KFilePermissionsPropsPlugin::PermissionsMode pmode;
1512
bool canChangePermissions;
1514
bool hasExtendedACL;
1517
bool fileSystemSupportsACLs;
1520
#define UniOwner (S_IRUSR|S_IWUSR|S_IXUSR)
1521
#define UniGroup (S_IRGRP|S_IWGRP|S_IXGRP)
1522
#define UniOthers (S_IROTH|S_IWOTH|S_IXOTH)
1523
#define UniRead (S_IRUSR|S_IRGRP|S_IROTH)
1524
#define UniWrite (S_IWUSR|S_IWGRP|S_IWOTH)
1525
#define UniExec (S_IXUSR|S_IXGRP|S_IXOTH)
1526
#define UniSpecial (S_ISUID|S_ISGID|S_ISVTX)
1528
// synced with PermissionsTarget
1529
const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers};
1530
const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 };
1532
// synced with PermissionsMode and standardPermissions
1533
const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = {
1534
{ I18N_NOOP("Forbidden"),
1535
I18N_NOOP("Can Read"),
1536
I18N_NOOP("Can Read & Write"),
1538
{ I18N_NOOP("Forbidden"),
1539
I18N_NOOP("Can View Content"),
1540
I18N_NOOP("Can View & Modify Content"),
1542
{ 0, 0, 0, 0}, // no texts for links
1543
{ I18N_NOOP("Forbidden"),
1544
I18N_NOOP("Can View Content & Read"),
1545
I18N_NOOP("Can View/Read & Modify/Write"),
1550
KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props )
1551
: KPropsDlgPlugin( _props )
1553
d = new KFilePermissionsPropsPluginPrivate;
1554
d->cbRecursive = 0L;
1555
grpCombo = 0L; grpEdit = 0;
1557
QString path = properties->kurl().path(-1);
1558
QString fname = properties->kurl().fileName();
1559
bool isLocal = properties->kurl().isLocalFile();
1560
bool isTrash = ( properties->kurl().protocol().find("trash", 0, false)==0 );
1561
bool IamRoot = (geteuid() == 0);
1563
KFileItem * item = properties->item();
1564
bool isLink = item->isLink();
1565
bool isDir = item->isDir(); // all dirs
1566
bool hasDir = item->isDir(); // at least one dir
1567
permissions = item->permissions(); // common permissions to all files
1568
d->partialPermissions = permissions; // permissions that only some files have (at first we take everything)
1569
d->isIrregular = isIrregular(permissions, isDir, isLink);
1570
strOwner = item->user();
1571
strGroup = item->group();
1572
d->hasExtendedACL = item->ACL().isExtended() || item->defaultACL().isValid();
1573
d->extendedACL = item->ACL();
1574
d->defaultACL = item->defaultACL();
1575
d->fileSystemSupportsACLs = false;
1577
if ( properties->items().count() > 1 )
1579
// Multiple items: see what they have in common
1580
KFileItemList items = properties->items();
1581
KFileItemListIterator it( items );
1582
for ( ++it /*no need to check the first one again*/ ; it.current(); ++it )
1584
if (!d->isIrregular)
1585
d->isIrregular |= isIrregular((*it)->permissions(),
1586
(*it)->isDir() == isDir,
1587
(*it)->isLink() == isLink);
1588
d->hasExtendedACL = d->hasExtendedACL || (*it)->hasExtendedACL();
1589
if ( (*it)->isLink() != isLink )
1591
if ( (*it)->isDir() != isDir )
1593
hasDir |= (*it)->isDir();
1594
if ( (*it)->permissions() != permissions )
1596
permissions &= (*it)->permissions();
1597
d->partialPermissions |= (*it)->permissions();
1599
if ( (*it)->user() != strOwner )
1600
strOwner = QString::null;
1601
if ( (*it)->group() != strGroup )
1602
strGroup = QString::null;
1607
d->pmode = PermissionsOnlyLinks;
1609
d->pmode = PermissionsOnlyDirs;
1611
d->pmode = PermissionsMixed;
1613
d->pmode = PermissionsOnlyFiles;
1615
// keep only what's not in the common permissions
1616
d->partialPermissions = d->partialPermissions & ~permissions;
1618
bool isMyFile = false;
1620
if (isLocal && !strOwner.isEmpty()) { // local files, and all owned by the same person
1621
struct passwd *myself = getpwuid( geteuid() );
1624
isMyFile = (strOwner == QString::fromLocal8Bit(myself->pw_name));
1626
kdWarning() << "I don't exist ?! geteuid=" << geteuid() << endl;
1628
//We don't know, for remote files, if they are ours or not.
1629
//So we let the user change permissions, and
1630
//KIO::chmod will tell, if he had no right to do it.
1634
d->canChangePermissions = (isMyFile || IamRoot) && (!isLink);
1639
d->m_frame = properties->addPage(i18n("&Permissions"));
1641
QBoxLayout *box = new QVBoxLayout( d->m_frame, 0, KDialog::spacingHint() );
1647
QPushButton* pbAdvancedPerm = 0;
1649
/* Group: Access Permissions */
1650
gb = new QGroupBox ( 0, Qt::Vertical, i18n("Access Permissions"), d->m_frame );
1651
gb->layout()->setSpacing(KDialog::spacingHint());
1652
gb->layout()->setMargin(KDialog::marginHint());
1653
box->addWidget (gb);
1655
gl = new QGridLayout (gb->layout(), 7, 2);
1656
gl->setColStretch(1, 1);
1658
l = d->explanationLabel = new QLabel( "", gb );
1660
d->explanationLabel->setText(i18n("This file is a link and does not have permissions.",
1661
"All files are links and do not have permissions.",
1662
properties->items().count()));
1663
else if (!d->canChangePermissions)
1664
d->explanationLabel->setText(i18n("Only the owner can change permissions."));
1665
gl->addMultiCellWidget(l, 0, 0, 0, 1);
1667
lbl = new QLabel( i18n("O&wner:"), gb);
1668
gl->addWidget(lbl, 1, 0);
1669
l = d->ownerPermCombo = new QComboBox(gb);
1671
gl->addWidget(l, 1, 1);
1672
connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() ));
1673
QWhatsThis::add(l, i18n("Specifies the actions that the owner is allowed to do."));
1675
lbl = new QLabel( i18n("Gro&up:"), gb);
1676
gl->addWidget(lbl, 2, 0);
1677
l = d->groupPermCombo = new QComboBox(gb);
1679
gl->addWidget(l, 2, 1);
1680
connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() ));
1681
QWhatsThis::add(l, i18n("Specifies the actions that the members of the group are allowed to do."));
1683
lbl = new QLabel( i18n("O&thers:"), gb);
1684
gl->addWidget(lbl, 3, 0);
1685
l = d->othersPermCombo = new QComboBox(gb);
1687
gl->addWidget(l, 3, 1);
1688
connect(l, SIGNAL( highlighted(int) ), this, SIGNAL( changed() ));
1689
QWhatsThis::add(l, i18n("Specifies the actions that all users, who are neither "
1690
"owner nor in the group, are allowed to do."));
1693
l = d->extraCheckbox = new QCheckBox(hasDir ?
1694
i18n("Only own&er can rename and delete folder content") :
1695
i18n("Is &executable"),
1697
connect( d->extraCheckbox, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
1698
gl->addWidget(l, 4, 1);
1699
QWhatsThis::add(l, hasDir ? i18n("Enable this option to allow only the folder's owner to "
1700
"delete or rename the contained files and folders. Other "
1701
"users can only add new files, which requires the 'Modify "
1702
"Content' permission.")
1703
: i18n("Enable this option to mark the file as executable. This only makes "
1704
"sense for programs and scripts. It is required when you want to "
1707
QLayoutItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
1708
gl->addMultiCell(spacer, 5, 5, 0, 1);
1710
pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb);
1711
gl->addMultiCellWidget(pbAdvancedPerm, 6, 6, 0, 1, AlignRight);
1712
connect(pbAdvancedPerm, SIGNAL( clicked() ), this, SLOT( slotShowAdvancedPermissions() ));
1715
d->extraCheckbox = 0;
1718
/**** Group: Ownership ****/
1719
gb = new QGroupBox ( 0, Qt::Vertical, i18n("Ownership"), d->m_frame );
1720
gb->layout()->setSpacing(KDialog::spacingHint());
1721
gb->layout()->setMargin(KDialog::marginHint());
1722
box->addWidget (gb);
1724
gl = new QGridLayout (gb->layout(), 4, 3);
1725
gl->addRowSpacing(0, 10);
1728
l = new QLabel( i18n("User:"), gb );
1729
gl->addWidget (l, 1, 0);
1731
/* GJ: Don't autocomplete more than 1000 users. This is a kind of random
1732
* value. Huge sites having 10.000+ user have a fair chance of using NIS,
1733
* (possibly) making this unacceptably slow.
1734
* OTOH, it is nice to offer this functionality for the standard user.
1736
int i, maxEntries = 1000;
1737
struct passwd *user;
1740
/* File owner: For root, offer a KLineEdit with autocompletion.
1741
* For a user, who can never chown() a file, offer a QLabel.
1743
if (IamRoot && isLocal)
1745
usrEdit = new KLineEdit( gb );
1746
KCompletion *kcom = usrEdit->completionObject();
1747
kcom->setOrder(KCompletion::Sorted);
1749
for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); i++)
1750
kcom->addItem(QString::fromLatin1(user->pw_name));
1752
usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto :
1753
KGlobalSettings::CompletionNone);
1754
usrEdit->setText(strOwner);
1755
gl->addWidget(usrEdit, 1, 1);
1756
connect( usrEdit, SIGNAL( textChanged( const QString & ) ),
1757
this, SIGNAL( changed() ) );
1761
l = new QLabel(strOwner, gb);
1762
gl->addWidget(l, 1, 1);
1767
QStringList groupList;
1769
user = getpwuid(geteuid());
1771
strUser = user->pw_name;
1775
for (i=0; ((ge = getgrent()) != 0L) && (i < maxEntries); i++)
1778
groupList += QString::fromLatin1(ge->gr_name);
1781
/* pick the groups to which the user belongs */
1782
char ** members = ge->gr_mem;
1784
while ((member = *members) != 0L) {
1785
if (strUser == member) {
1786
groupList += QString::fromLocal8Bit(ge->gr_name);
1796
/* add the effective Group to the list .. */
1797
ge = getgrgid (getegid());
1799
QString name = QString::fromLatin1(ge->gr_name);
1801
name.setNum(ge->gr_gid);
1802
if (groupList.find(name) == groupList.end())
1806
bool isMyGroup = groupList.contains(strGroup);
1808
/* add the group the file currently belongs to ..
1809
* .. if its not there already
1812
groupList += strGroup;
1814
l = new QLabel( i18n("Group:"), gb );
1815
gl->addWidget (l, 2, 0);
1817
/* Set group: if possible to change:
1818
* - Offer a KLineEdit for root, since he can change to any group.
1819
* - Offer a QComboBox for a normal user, since he can change to a fixed
1820
* (small) set of groups only.
1821
* If not changeable: offer a QLabel.
1823
if (IamRoot && isLocal)
1825
grpEdit = new KLineEdit(gb);
1826
KCompletion *kcom = new KCompletion;
1827
kcom->setItems(groupList);
1828
grpEdit->setCompletionObject(kcom, true);
1829
grpEdit->setAutoDeleteCompletionObject( true );
1830
grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
1831
grpEdit->setText(strGroup);
1832
gl->addWidget(grpEdit, 2, 1);
1833
connect( grpEdit, SIGNAL( textChanged( const QString & ) ),
1834
this, SIGNAL( changed() ) );
1836
else if ((groupList.count() > 1) && isMyFile && isLocal)
1838
grpCombo = new QComboBox(gb, "combogrouplist");
1839
grpCombo->insertStringList(groupList);
1840
grpCombo->setCurrentItem(groupList.findIndex(strGroup));
1841
gl->addWidget(grpCombo, 2, 1);
1842
connect( grpCombo, SIGNAL( activated( int ) ),
1843
this, SIGNAL( changed() ) );
1847
l = new QLabel(strGroup, gb);
1848
gl->addWidget(l, 2, 1);
1851
gl->setColStretch(2, 10);
1853
// "Apply recursive" checkbox
1854
if ( hasDir && !isLink && !isTrash )
1856
d->cbRecursive = new QCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame );
1857
connect( d->cbRecursive, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
1858
box->addWidget( d->cbRecursive );
1861
updateAccessControls();
1864
if ( isTrash || !d->canChangePermissions )
1866
//don't allow to change properties for file into trash
1867
enableAccessControls(false);
1868
if ( pbAdvancedPerm && !d->hasExtendedACL )
1869
pbAdvancedPerm->setEnabled(false);
1872
box->addStretch (10);
1875
#ifdef USE_POSIX_ACL
1876
static bool fileSystemSupportsACL( const QCString& pathCString )
1878
bool fileSystemSupportsACLs = false;
1881
fileSystemSupportsACLs = ( statfs( pathCString.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS );
1883
fileSystemSupportsACLs =
1884
getxattr( pathCString.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA;
1886
return fileSystemSupportsACLs;
1891
void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() {
1893
bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed);
1894
KDialogBase dlg(properties, 0, true, i18n("Advanced Permissions"),
1895
KDialogBase::Ok|KDialogBase::Cancel);
1901
QVBox *mainVBox = dlg.makeVBoxMainWidget();
1903
// Group: Access Permissions
1904
gb = new QGroupBox ( 0, Qt::Vertical, i18n("Access Permissions"), mainVBox );
1905
gb->layout()->setSpacing(KDialog::spacingHint());
1906
gb->layout()->setMargin(KDialog::marginHint());
1908
gl = new QGridLayout (gb->layout(), 6, 6);
1909
gl->addRowSpacing(0, 10);
1911
QValueVector<QWidget*> theNotSpecials;
1913
l = new QLabel(i18n("Class"), gb );
1914
gl->addWidget(l, 1, 0);
1915
theNotSpecials.append( l );
1918
l = new QLabel( i18n("Show\nEntries"), gb );
1920
l = new QLabel( i18n("Read"), gb );
1921
gl->addWidget (l, 1, 1);
1922
theNotSpecials.append( l );
1923
QString readWhatsThis;
1925
readWhatsThis = i18n("This flag allows viewing the content of the folder.");
1927
readWhatsThis = i18n("The Read flag allows viewing the content of the file.");
1928
QWhatsThis::add(l, readWhatsThis);
1931
l = new QLabel( i18n("Write\nEntries"), gb );
1933
l = new QLabel( i18n("Write"), gb );
1934
gl->addWidget (l, 1, 2);
1935
theNotSpecials.append( l );
1936
QString writeWhatsThis;
1938
writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. "
1939
"Note that deleting and renaming can be limited using the Sticky flag.");
1941
writeWhatsThis = i18n("The Write flag allows modifying the content of the file.");
1942
QWhatsThis::add(l, writeWhatsThis);
1944
QString execWhatsThis;
1946
l = new QLabel( i18n("Enter folder", "Enter"), gb );
1947
execWhatsThis = i18n("Enable this flag to allow entering the folder.");
1950
l = new QLabel( i18n("Exec"), gb );
1951
execWhatsThis = i18n("Enable this flag to allow executing the file as a program.");
1953
QWhatsThis::add(l, execWhatsThis);
1954
theNotSpecials.append( l );
1955
// GJ: Add space between normal and special modes
1956
QSize size = l->sizeHint();
1957
size.setWidth(size.width() + 15);
1958
l->setFixedSize(size);
1959
gl->addWidget (l, 1, 3);
1961
l = new QLabel( i18n("Special"), gb );
1962
gl->addMultiCellWidget(l, 1, 1, 4, 5);
1963
QString specialWhatsThis;
1965
specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact "
1966
"meaning of the flag can be seen in the right hand column.");
1968
specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen "
1969
"in the right hand column.");
1970
QWhatsThis::add(l, specialWhatsThis);
1972
cl[0] = new QLabel( i18n("User"), gb );
1973
gl->addWidget (cl[0], 2, 0);
1974
theNotSpecials.append( cl[0] );
1976
cl[1] = new QLabel( i18n("Group"), gb );
1977
gl->addWidget (cl[1], 3, 0);
1978
theNotSpecials.append( cl[1] );
1980
cl[2] = new QLabel( i18n("Others"), gb );
1981
gl->addWidget (cl[2], 4, 0);
1982
theNotSpecials.append( cl[2] );
1984
l = new QLabel(i18n("Set UID"), gb);
1985
gl->addWidget(l, 2, 5);
1986
QString setUidWhatsThis;
1988
setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be "
1989
"the owner of all new files.");
1991
setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
1992
"be executed with the permissions of the owner.");
1993
QWhatsThis::add(l, setUidWhatsThis);
1995
l = new QLabel(i18n("Set GID"), gb);
1996
gl->addWidget(l, 3, 5);
1997
QString setGidWhatsThis;
1999
setGidWhatsThis = i18n("If this flag is set, the group of this folder will be "
2000
"set for all new files.");
2002
setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
2003
"be executed with the permissions of the group.");
2004
QWhatsThis::add(l, setGidWhatsThis);
2006
l = new QLabel(i18n("File permission", "Sticky"), gb);
2007
gl->addWidget(l, 4, 5);
2008
QString stickyWhatsThis;
2010
stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner "
2011
"and root can delete or rename files. Otherwise everybody "
2012
"with write permissions can do this.");
2014
stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may "
2015
"be used on some systems");
2016
QWhatsThis::add(l, stickyWhatsThis);
2018
mode_t aPermissions, aPartialPermissions;
2019
mode_t dummy1, dummy2;
2021
if (!d->isIrregular) {
2023
case PermissionsOnlyFiles:
2024
getPermissionMasks(aPartialPermissions,
2029
case PermissionsOnlyDirs:
2030
case PermissionsMixed:
2031
getPermissionMasks(dummy1,
2032
aPartialPermissions,
2036
case PermissionsOnlyLinks:
2037
aPermissions = UniRead | UniWrite | UniExec | UniSpecial;
2038
aPartialPermissions = 0;
2043
aPermissions = permissions;
2044
aPartialPermissions = d->partialPermissions;
2048
QCheckBox *cba[3][4];
2049
for (int row = 0; row < 3 ; ++row) {
2050
for (int col = 0; col < 4; ++col) {
2051
QCheckBox *cb = new QCheckBox( gb );
2052
if ( col != 3 ) theNotSpecials.append( cb );
2054
cb->setChecked(aPermissions & fperm[row][col]);
2055
if ( aPartialPermissions & fperm[row][col] )
2060
else if (d->cbRecursive && d->cbRecursive->isChecked())
2063
cb->setEnabled( d->canChangePermissions );
2064
gl->addWidget (cb, row+2, col+1);
2067
QWhatsThis::add(cb, readWhatsThis);
2070
QWhatsThis::add(cb, writeWhatsThis);
2073
QWhatsThis::add(cb, execWhatsThis);
2078
QWhatsThis::add(cb, setUidWhatsThis);
2081
QWhatsThis::add(cb, setGidWhatsThis);
2084
QWhatsThis::add(cb, stickyWhatsThis);
2091
gl->setColStretch(6, 10);
2093
#ifdef USE_POSIX_ACL
2094
KACLEditWidget *extendedACLs = 0;
2096
// FIXME make it work with partial entries
2097
if ( properties->items().count() == 1 ) {
2098
QCString pathCString = QFile::encodeName( properties->item()->url().path() );
2099
d->fileSystemSupportsACLs = fileSystemSupportsACL( pathCString );
2101
if ( d->fileSystemSupportsACLs ) {
2102
std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) );
2103
extendedACLs = new KACLEditWidget( mainVBox );
2104
if ( d->extendedACL.isValid() && d->extendedACL.isExtended() )
2105
extendedACLs->setACL( d->extendedACL );
2107
extendedACLs->setACL( KACL( aPermissions ) );
2109
if ( d->defaultACL.isValid() )
2110
extendedACLs->setDefaultACL( d->defaultACL );
2112
if ( properties->items().first()->isDir() )
2113
extendedACLs->setAllowDefaults( true );
2114
if ( !d->canChangePermissions )
2115
extendedACLs->setReadOnly( true );
2119
if (dlg.exec() != KDialogBase::Accepted)
2122
mode_t andPermissions = mode_t(~0);
2123
mode_t orPermissions = 0;
2124
for (int row = 0; row < 3; ++row)
2125
for (int col = 0; col < 4; ++col) {
2126
switch (cba[row][col]->state())
2129
orPermissions |= fperm[row][col];
2131
case QCheckBox::Off:
2132
andPermissions &= ~fperm[row][col];
2134
default: // NoChange
2139
d->isIrregular = false;
2140
KFileItemList items = properties->items();
2141
for (KFileItemListIterator it(items); it.current(); ++it) {
2142
if (isIrregular(((*it)->permissions() & andPermissions) | orPermissions,
2143
(*it)->isDir(), (*it)->isLink())) {
2144
d->isIrregular = true;
2149
permissions = orPermissions;
2150
d->partialPermissions = andPermissions;
2152
#ifdef USE_POSIX_ACL
2153
// override with the acls, if present
2154
if ( extendedACLs ) {
2155
d->extendedACL = extendedACLs->getACL();
2156
d->defaultACL = extendedACLs->getDefaultACL();
2157
d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid();
2158
permissions = d->extendedACL.basePermissions();
2159
permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX );
2163
updateAccessControls();
2167
// QString KFilePermissionsPropsPlugin::tabName () const
2169
// return i18n ("&Permissions");
2172
KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin()
2177
bool KFilePermissionsPropsPlugin::supports( KFileItemList _items )
2179
KFileItemList::const_iterator it = _items.constBegin();
2180
for ( ; it != _items.constEnd(); ++it ) {
2181
KFileItem *item = *it;
2182
if( !item->user().isEmpty() || !item->group().isEmpty() )
2188
// sets a combo box in the Access Control frame
2189
void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target,
2190
mode_t permissions, mode_t partial) {
2192
if (d->pmode == PermissionsOnlyLinks) {
2193
combo->insertItem(i18n("Link"));
2194
combo->setCurrentItem(0);
2198
mode_t tMask = permissionsMasks[target];
2200
for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++)
2201
if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite)))
2203
Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar
2205
for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++)
2206
combo->insertItem(i18n(permissionsTexts[(int)d->pmode][i]));
2208
if (partial & tMask & ~UniExec) {
2209
combo->insertItem(i18n("Varying (No Change)"));
2210
combo->setCurrentItem(3);
2213
combo->setCurrentItem(textIndex);
2216
// permissions are irregular if they cant be displayed in a combo box.
2217
bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) {
2218
if (isLink) // links are always ok
2221
mode_t p = permissions;
2222
if (p & (S_ISUID | S_ISGID)) // setuid/setgid -> irregular
2225
p &= ~S_ISVTX; // ignore sticky on dirs
2227
// check supported flag combinations
2228
mode_t p0 = p & UniOwner;
2229
if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner))
2232
if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup))
2235
if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers))
2239
if (p & S_ISVTX) // sticky on file -> irregular
2242
// check supported flag combinations
2243
mode_t p0 = p & UniOwner;
2244
bool usrXPossible = !p0; // true if this file could be an executable
2246
if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR)))
2248
usrXPossible = true;
2250
else if (p0 == S_IWUSR)
2254
bool grpXPossible = !p0; // true if this file could be an executable
2256
if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP)))
2258
grpXPossible = true;
2260
else if (p0 == S_IWGRP)
2263
grpXPossible = true;
2266
bool othXPossible = !p0; // true if this file could be an executable
2268
if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH)))
2270
othXPossible = true;
2272
else if (p0 == S_IWOTH)
2275
// check that there either all targets are executable-compatible, or none
2276
return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible);
2279
// enables/disabled the widgets in the Access Control frame
2280
void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) {
2281
d->ownerPermCombo->setEnabled(enable);
2282
d->groupPermCombo->setEnabled(enable);
2283
d->othersPermCombo->setEnabled(enable);
2284
if (d->extraCheckbox)
2285
d->extraCheckbox->setEnabled(enable);
2286
if ( d->cbRecursive )
2287
d->cbRecursive->setEnabled(enable);
2290
// updates all widgets in the Access Control frame
2291
void KFilePermissionsPropsPlugin::updateAccessControls() {
2292
setComboContent(d->ownerPermCombo, PermissionsOwner,
2293
permissions, d->partialPermissions);
2294
setComboContent(d->groupPermCombo, PermissionsGroup,
2295
permissions, d->partialPermissions);
2296
setComboContent(d->othersPermCombo, PermissionsOthers,
2297
permissions, d->partialPermissions);
2300
case PermissionsOnlyLinks:
2301
enableAccessControls(false);
2303
case PermissionsOnlyFiles:
2304
enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2305
if (d->canChangePermissions)
2306
d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2307
i18n("This file uses advanced permissions",
2308
"These files use advanced permissions.",
2309
properties->items().count()) : "");
2310
if (d->partialPermissions & UniExec) {
2311
d->extraCheckbox->setTristate();
2312
d->extraCheckbox->setNoChange();
2315
d->extraCheckbox->setTristate(false);
2316
d->extraCheckbox->setChecked(permissions & UniExec);
2319
case PermissionsOnlyDirs:
2320
enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2321
// if this is a dir, and we can change permissions, don't dis-allow
2322
// recursive, we can do that for ACL setting.
2323
if ( d->cbRecursive )
2324
d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular );
2326
if (d->canChangePermissions)
2327
d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2328
i18n("This folder uses advanced permissions.",
2329
"These folders use advanced permissions.",
2330
properties->items().count()) : "");
2331
if (d->partialPermissions & S_ISVTX) {
2332
d->extraCheckbox->setTristate();
2333
d->extraCheckbox->setNoChange();
2336
d->extraCheckbox->setTristate(false);
2337
d->extraCheckbox->setChecked(permissions & S_ISVTX);
2340
case PermissionsMixed:
2341
enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2342
if (d->canChangePermissions)
2343
d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2344
i18n("These files use advanced permissions.") : "");
2346
if (d->partialPermissions & S_ISVTX) {
2347
d->extraCheckbox->setTristate();
2348
d->extraCheckbox->setNoChange();
2351
d->extraCheckbox->setTristate(false);
2352
d->extraCheckbox->setChecked(permissions & S_ISVTX);
2358
// gets masks for files and dirs from the Access Control frame widgets
2359
void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions,
2360
mode_t &andDirPermissions,
2361
mode_t &orFilePermissions,
2362
mode_t &orDirPermissions) {
2363
andFilePermissions = mode_t(~UniSpecial);
2364
andDirPermissions = mode_t(~(S_ISUID|S_ISGID));
2365
orFilePermissions = 0;
2366
orDirPermissions = 0;
2370
mode_t m = standardPermissions[d->ownerPermCombo->currentItem()];
2371
if (m != (mode_t) -1) {
2372
orFilePermissions |= m & UniOwner;
2373
if ((m & UniOwner) &&
2374
((d->pmode == PermissionsMixed) ||
2375
((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange))))
2376
andFilePermissions &= ~(S_IRUSR | S_IWUSR);
2378
andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
2379
if ((m & S_IRUSR) && (d->extraCheckbox->state() == QButton::On))
2380
orFilePermissions |= S_IXUSR;
2383
orDirPermissions |= m & UniOwner;
2385
orDirPermissions |= S_IXUSR;
2386
andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
2389
m = standardPermissions[d->groupPermCombo->currentItem()];
2390
if (m != (mode_t) -1) {
2391
orFilePermissions |= m & UniGroup;
2392
if ((m & UniGroup) &&
2393
((d->pmode == PermissionsMixed) ||
2394
((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange))))
2395
andFilePermissions &= ~(S_IRGRP | S_IWGRP);
2397
andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
2398
if ((m & S_IRGRP) && (d->extraCheckbox->state() == QButton::On))
2399
orFilePermissions |= S_IXGRP;
2402
orDirPermissions |= m & UniGroup;
2404
orDirPermissions |= S_IXGRP;
2405
andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
2408
m = standardPermissions[d->othersPermCombo->currentItem()];
2409
if (m != (mode_t) -1) {
2410
orFilePermissions |= m & UniOthers;
2411
if ((m & UniOthers) &&
2412
((d->pmode == PermissionsMixed) ||
2413
((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == QButton::NoChange))))
2414
andFilePermissions &= ~(S_IROTH | S_IWOTH);
2416
andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
2417
if ((m & S_IROTH) && (d->extraCheckbox->state() == QButton::On))
2418
orFilePermissions |= S_IXOTH;
2421
orDirPermissions |= m & UniOthers;
2423
orDirPermissions |= S_IXOTH;
2424
andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
2427
if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) &&
2428
(d->extraCheckbox->state() != QButton::NoChange)) {
2429
andDirPermissions &= ~S_ISVTX;
2430
if (d->extraCheckbox->state() == QButton::On)
2431
orDirPermissions |= S_ISVTX;
2435
void KFilePermissionsPropsPlugin::applyChanges()
2437
mode_t orFilePermissions;
2438
mode_t orDirPermissions;
2439
mode_t andFilePermissions;
2440
mode_t andDirPermissions;
2442
if (!d->canChangePermissions)
2445
if (!d->isIrregular)
2446
getPermissionMasks(andFilePermissions,
2451
orFilePermissions = permissions;
2452
andFilePermissions = d->partialPermissions;
2453
orDirPermissions = permissions;
2454
andDirPermissions = d->partialPermissions;
2457
QString owner, group;
2459
owner = usrEdit->text();
2461
group = grpEdit->text();
2463
group = grpCombo->currentText();
2465
if (owner == strOwner)
2466
owner = QString::null; // no change
2468
if (group == strGroup)
2469
group = QString::null;
2471
bool recursive = d->cbRecursive && d->cbRecursive->isChecked();
2472
bool permissionChange = false;
2474
KFileItemList files, dirs;
2475
KFileItemList items = properties->items();
2476
for (KFileItemListIterator it(items); it.current(); ++it) {
2477
if ((*it)->isDir()) {
2479
if ((*it)->permissions() != (((*it)->permissions() & andDirPermissions) | orDirPermissions))
2480
permissionChange = true;
2482
else if ((*it)->isFile()) {
2484
if ((*it)->permissions() != (((*it)->permissions() & andFilePermissions) | orFilePermissions))
2485
permissionChange = true;
2489
const bool ACLChange = ( d->extendedACL != properties->item()->ACL() );
2490
const bool defaultACLChange = ( d->defaultACL != properties->item()->defaultACL() );
2492
if ( owner.isEmpty() && group.isEmpty() && !recursive
2493
&& !permissionChange && !ACLChange && !defaultACLChange )
2497
if (files.count() > 0) {
2498
job = KIO::chmod( files, orFilePermissions, ~andFilePermissions,
2499
owner, group, false );
2500
if ( ACLChange && d->fileSystemSupportsACLs )
2501
job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
2502
if ( defaultACLChange && d->fileSystemSupportsACLs )
2503
job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
2505
connect( job, SIGNAL( result( KIO::Job * ) ),
2506
SLOT( slotChmodResult( KIO::Job * ) ) );
2508
QWidget dummy(0,0,WType_Dialog|WShowModal);
2509
qt_enter_modal(&dummy);
2511
qt_leave_modal(&dummy);
2513
if (dirs.count() > 0) {
2514
job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
2515
owner, group, recursive );
2516
if ( ACLChange && d->fileSystemSupportsACLs )
2517
job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
2518
if ( defaultACLChange && d->fileSystemSupportsACLs )
2519
job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
2521
connect( job, SIGNAL( result( KIO::Job * ) ),
2522
SLOT( slotChmodResult( KIO::Job * ) ) );
2524
QWidget dummy(0,0,WType_Dialog|WShowModal);
2525
qt_enter_modal(&dummy);
2527
qt_leave_modal(&dummy);
2531
void KFilePermissionsPropsPlugin::slotChmodResult( KIO::Job * job )
2533
kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl;
2535
job->showErrorDialog( d->m_frame );
2536
// allow apply() to return
2543
class KURLPropsPlugin::KURLPropsPluginPrivate
2546
KURLPropsPluginPrivate()
2549
~KURLPropsPluginPrivate()
2556
KURLPropsPlugin::KURLPropsPlugin( KPropertiesDialog *_props )
2557
: KPropsDlgPlugin( _props )
2559
d = new KURLPropsPluginPrivate;
2560
d->m_frame = properties->addPage(i18n("U&RL"));
2561
QVBoxLayout *layout = new QVBoxLayout(d->m_frame, 0, KDialog::spacingHint());
2564
l = new QLabel( d->m_frame, "Label_1" );
2565
l->setText( i18n("URL:") );
2566
layout->addWidget(l);
2568
URLEdit = new KURLRequester( d->m_frame, "URL Requester" );
2569
layout->addWidget(URLEdit);
2571
QString path = properties->kurl().path();
2574
if ( !f.open( IO_ReadOnly ) )
2578
KSimpleConfig config( path );
2579
config.setDesktopGroup();
2580
URLStr = config.readPathEntry( "URL" );
2582
if ( !URLStr.isNull() )
2583
URLEdit->setURL( URLStr );
2585
connect( URLEdit, SIGNAL( textChanged( const QString & ) ),
2586
this, SIGNAL( changed() ) );
2588
layout->addStretch (1);
2591
KURLPropsPlugin::~KURLPropsPlugin()
2596
// QString KURLPropsPlugin::tabName () const
2598
// return i18n ("U&RL");
2601
bool KURLPropsPlugin::supports( KFileItemList _items )
2603
if ( _items.count() != 1 )
2605
KFileItem * item = _items.first();
2606
// check if desktop file
2607
if ( !KPropsDlgPlugin::isDesktopFile( item ) )
2610
// open file and check type
2611
KDesktopFile config( item->url().path(), true /* readonly */ );
2612
return config.hasLinkType();
2615
void KURLPropsPlugin::applyChanges()
2617
QString path = properties->kurl().path();
2620
if ( !f.open( IO_ReadWrite ) ) {
2621
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
2622
"sufficient access to write to <b>%1</b>.</qt>").arg(path));
2627
KSimpleConfig config( path );
2628
config.setDesktopGroup();
2629
config.writeEntry( "Type", QString::fromLatin1("Link"));
2630
config.writePathEntry( "URL", URLEdit->url() );
2631
// Users can't create a Link .desktop file with a Name field,
2632
// but distributions can. Update the Name field in that case.
2633
if ( config.hasKey("Name") )
2635
QString nameStr = nameFromFileName(properties->kurl().fileName());
2636
config.writeEntry( "Name", nameStr );
2637
config.writeEntry( "Name", nameStr, true, false, true );
2643
/* ----------------------------------------------------
2645
* KBindingPropsPlugin
2647
* -------------------------------------------------- */
2649
class KBindingPropsPlugin::KBindingPropsPluginPrivate
2652
KBindingPropsPluginPrivate()
2655
~KBindingPropsPluginPrivate()
2662
KBindingPropsPlugin::KBindingPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props )
2664
d = new KBindingPropsPluginPrivate;
2665
d->m_frame = properties->addPage(i18n("A&ssociation"));
2666
patternEdit = new KLineEdit( d->m_frame, "LineEdit_1" );
2667
commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" );
2668
mimeEdit = new KLineEdit( d->m_frame, "LineEdit_3" );
2670
QBoxLayout *mainlayout = new QVBoxLayout(d->m_frame, 0, KDialog::spacingHint());
2673
tmpQLabel = new QLabel( d->m_frame, "Label_1" );
2674
tmpQLabel->setText( i18n("Pattern ( example: *.html;*.htm )") );
2675
tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
2676
mainlayout->addWidget(tmpQLabel, 1);
2678
//patternEdit->setGeometry( 10, 40, 210, 30 );
2679
//patternEdit->setText( "" );
2680
patternEdit->setMaxLength( 512 );
2681
patternEdit->setMinimumSize( patternEdit->sizeHint() );
2682
patternEdit->setFixedHeight( fontHeight );
2683
mainlayout->addWidget(patternEdit, 1);
2685
tmpQLabel = new QLabel( d->m_frame, "Label_2" );
2686
tmpQLabel->setText( i18n("Mime Type") );
2687
tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
2688
mainlayout->addWidget(tmpQLabel, 1);
2690
//mimeEdit->setGeometry( 10, 160, 210, 30 );
2691
mimeEdit->setMaxLength( 256 );
2692
mimeEdit->setMinimumSize( mimeEdit->sizeHint() );
2693
mimeEdit->setFixedHeight( fontHeight );
2694
mainlayout->addWidget(mimeEdit, 1);
2696
tmpQLabel = new QLabel( d->m_frame, "Label_3" );
2697
tmpQLabel->setText( i18n("Comment") );
2698
tmpQLabel->setMinimumSize(tmpQLabel->sizeHint());
2699
mainlayout->addWidget(tmpQLabel, 1);
2701
//commentEdit->setGeometry( 10, 100, 210, 30 );
2702
commentEdit->setMaxLength( 256 );
2703
commentEdit->setMinimumSize( commentEdit->sizeHint() );
2704
commentEdit->setFixedHeight( fontHeight );
2705
mainlayout->addWidget(commentEdit, 1);
2707
cbAutoEmbed = new QCheckBox( i18n("Left click previews"), d->m_frame, "cbAutoEmbed" );
2708
mainlayout->addWidget(cbAutoEmbed, 1);
2710
mainlayout->addStretch (10);
2711
mainlayout->activate();
2713
QFile f( _props->kurl().path() );
2714
if ( !f.open( IO_ReadOnly ) )
2718
KSimpleConfig config( _props->kurl().path() );
2719
config.setDesktopGroup();
2720
QString patternStr = config.readEntry( "Patterns" );
2721
QString iconStr = config.readEntry( "Icon" );
2722
QString commentStr = config.readEntry( "Comment" );
2723
m_sMimeStr = config.readEntry( "MimeType" );
2725
if ( !patternStr.isEmpty() )
2726
patternEdit->setText( patternStr );
2727
if ( !commentStr.isEmpty() )
2728
commentEdit->setText( commentStr );
2729
if ( !m_sMimeStr.isEmpty() )
2730
mimeEdit->setText( m_sMimeStr );
2731
cbAutoEmbed->setTristate();
2732
if ( config.hasKey( "X-KDE-AutoEmbed" ) )
2733
cbAutoEmbed->setChecked( config.readBoolEntry( "X-KDE-AutoEmbed" ) );
2735
cbAutoEmbed->setNoChange();
2737
connect( patternEdit, SIGNAL( textChanged( const QString & ) ),
2738
this, SIGNAL( changed() ) );
2739
connect( commentEdit, SIGNAL( textChanged( const QString & ) ),
2740
this, SIGNAL( changed() ) );
2741
connect( mimeEdit, SIGNAL( textChanged( const QString & ) ),
2742
this, SIGNAL( changed() ) );
2743
connect( cbAutoEmbed, SIGNAL( toggled( bool ) ),
2744
this, SIGNAL( changed() ) );
2747
KBindingPropsPlugin::~KBindingPropsPlugin()
2752
// QString KBindingPropsPlugin::tabName () const
2754
// return i18n ("A&ssociation");
2757
bool KBindingPropsPlugin::supports( KFileItemList _items )
2759
if ( _items.count() != 1 )
2761
KFileItem * item = _items.first();
2762
// check if desktop file
2763
if ( !KPropsDlgPlugin::isDesktopFile( item ) )
2766
// open file and check type
2767
KDesktopFile config( item->url().path(), true /* readonly */ );
2768
return config.hasMimeTypeType();
2771
void KBindingPropsPlugin::applyChanges()
2773
QString path = properties->kurl().path();
2776
if ( !f.open( IO_ReadWrite ) )
2778
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
2779
"sufficient access to write to <b>%1</b>.</qt>").arg(path));
2784
KSimpleConfig config( path );
2785
config.setDesktopGroup();
2786
config.writeEntry( "Type", QString::fromLatin1("MimeType") );
2788
config.writeEntry( "Patterns", patternEdit->text() );
2789
config.writeEntry( "Comment", commentEdit->text() );
2790
config.writeEntry( "Comment",
2791
commentEdit->text(), true, false, true ); // for compat
2792
config.writeEntry( "MimeType", mimeEdit->text() );
2793
if ( cbAutoEmbed->state() == QButton::NoChange )
2794
config.deleteEntry( "X-KDE-AutoEmbed", false );
2796
config.writeEntry( "X-KDE-AutoEmbed", cbAutoEmbed->isChecked() );
2800
/* ----------------------------------------------------
2802
* KDevicePropsPlugin
2804
* -------------------------------------------------- */
2806
class KDevicePropsPlugin::KDevicePropsPluginPrivate
2809
KDevicePropsPluginPrivate()
2812
~KDevicePropsPluginPrivate()
2817
QStringList mountpointlist;
2818
QLabel *m_freeSpaceText;
2819
QLabel *m_freeSpaceLabel;
2820
QProgressBar *m_freeSpaceBar;
2823
KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props )
2825
d = new KDevicePropsPluginPrivate;
2826
d->m_frame = properties->addPage(i18n("De&vice"));
2828
QStringList devices;
2829
KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
2831
for(KMountPoint::List::ConstIterator it = mountPoints.begin();
2832
it != mountPoints.end(); ++it)
2834
KMountPoint *mp = *it;
2835
QString mountPoint = mp->mountPoint();
2836
QString device = mp->mountedFrom();
2837
kdDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType()<<endl;
2839
if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty()
2840
&& device != "none")
2842
devices.append( device + QString::fromLatin1(" (")
2843
+ mountPoint + QString::fromLatin1(")") );
2844
m_devicelist.append(device);
2845
d->mountpointlist.append(mountPoint);
2849
QGridLayout *layout = new QGridLayout( d->m_frame, 0, 2, 0,
2850
KDialog::spacingHint());
2851
layout->setColStretch(1, 1);
2854
label = new QLabel( d->m_frame );
2855
label->setText( devices.count() == 0 ?
2856
i18n("Device (/dev/fd0):") : // old style
2857
i18n("Device:") ); // new style (combobox)
2858
layout->addWidget(label, 0, 0);
2860
device = new QComboBox( true, d->m_frame, "ComboBox_device" );
2861
device->insertStringList( devices );
2862
layout->addWidget(device, 0, 1);
2863
connect( device, SIGNAL( activated( int ) ),
2864
this, SLOT( slotActivated( int ) ) );
2866
readonly = new QCheckBox( d->m_frame, "CheckBox_readonly" );
2867
readonly->setText( i18n("Read only") );
2868
layout->addWidget(readonly, 1, 1);
2870
label = new QLabel( d->m_frame );
2871
label->setText( i18n("File system:") );
2872
layout->addWidget(label, 2, 0);
2874
QLabel *fileSystem = new QLabel( d->m_frame );
2875
layout->addWidget(fileSystem, 2, 1);
2877
label = new QLabel( d->m_frame );
2878
label->setText( devices.count()==0 ?
2879
i18n("Mount point (/mnt/floppy):") : // old style
2880
i18n("Mount point:")); // new style (combobox)
2881
layout->addWidget(label, 3, 0);
2883
mountpoint = new QLabel( d->m_frame, "LineEdit_mountpoint" );
2885
layout->addWidget(mountpoint, 3, 1);
2888
d->m_freeSpaceText = new QLabel(i18n("Free disk space:"), d->m_frame );
2889
layout->addWidget(d->m_freeSpaceText, 4, 0);
2891
d->m_freeSpaceLabel = new QLabel( d->m_frame );
2892
layout->addWidget( d->m_freeSpaceLabel, 4, 1 );
2894
d->m_freeSpaceBar = new QProgressBar( d->m_frame, "freeSpaceBar" );
2895
layout->addMultiCellWidget(d->m_freeSpaceBar, 5, 5, 0, 1);
2897
// we show it in the slot when we know the values
2898
d->m_freeSpaceText->hide();
2899
d->m_freeSpaceLabel->hide();
2900
d->m_freeSpaceBar->hide();
2902
KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame);
2903
layout->addMultiCellWidget(sep, 6, 6, 0, 1);
2905
unmounted = new KIconButton( d->m_frame );
2906
int bsize = 66 + 2 * unmounted->style().pixelMetric(QStyle::PM_ButtonMargin);
2907
unmounted->setFixedSize(bsize, bsize);
2908
unmounted->setIconType(KIcon::Desktop, KIcon::Device);
2909
layout->addWidget(unmounted, 7, 0);
2911
label = new QLabel( i18n("Unmounted Icon"), d->m_frame );
2912
layout->addWidget(label, 7, 1);
2914
layout->setRowStretch(8, 1);
2916
QString path( _props->kurl().path() );
2919
if ( !f.open( IO_ReadOnly ) )
2923
KSimpleConfig config( path );
2924
config.setDesktopGroup();
2925
QString deviceStr = config.readEntry( "Dev" );
2926
QString mountPointStr = config.readEntry( "MountPoint" );
2927
bool ro = config.readBoolEntry( "ReadOnly", false );
2928
QString unmountedStr = config.readEntry( "UnmountIcon" );
2930
fileSystem->setText( i18n(config.readEntry("FSType").local8Bit()) );
2932
device->setEditText( deviceStr );
2933
if ( !deviceStr.isEmpty() ) {
2934
// Set default options for this device (first matching entry)
2935
int index = m_devicelist.findIndex(deviceStr);
2938
//kdDebug(250) << "found it " << index << endl;
2939
slotActivated( index );
2943
if ( !mountPointStr.isEmpty() )
2945
mountpoint->setText( mountPointStr );
2949
readonly->setChecked( ro );
2951
if ( unmountedStr.isEmpty() )
2952
unmountedStr = KMimeType::defaultMimeTypePtr()->KServiceType::icon(); // default icon
2954
unmounted->setIcon( unmountedStr );
2956
connect( device, SIGNAL( activated( int ) ),
2957
this, SIGNAL( changed() ) );
2958
connect( device, SIGNAL( textChanged( const QString & ) ),
2959
this, SIGNAL( changed() ) );
2960
connect( readonly, SIGNAL( toggled( bool ) ),
2961
this, SIGNAL( changed() ) );
2962
connect( unmounted, SIGNAL( iconChanged( QString ) ),
2963
this, SIGNAL( changed() ) );
2965
connect( device, SIGNAL( textChanged( const QString & ) ),
2966
this, SLOT( slotDeviceChanged() ) );
2969
KDevicePropsPlugin::~KDevicePropsPlugin()
2974
// QString KDevicePropsPlugin::tabName () const
2976
// return i18n ("De&vice");
2979
void KDevicePropsPlugin::updateInfo()
2981
// we show it in the slot when we know the values
2982
d->m_freeSpaceText->hide();
2983
d->m_freeSpaceLabel->hide();
2984
d->m_freeSpaceBar->hide();
2986
if ( !mountpoint->text().isEmpty() )
2988
KDiskFreeSp * job = new KDiskFreeSp;
2989
connect( job, SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&,
2990
const unsigned long&, const QString& ) ),
2991
this, SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&,
2992
const unsigned long&, const QString& ) ) );
2994
job->readDF( mountpoint->text() );
2998
void KDevicePropsPlugin::slotActivated( int index )
3000
// Update mountpoint so that it matches the device that was selected in the combo
3001
device->setEditText( m_devicelist[index] );
3002
mountpoint->setText( d->mountpointlist[index] );
3007
void KDevicePropsPlugin::slotDeviceChanged()
3009
// Update mountpoint so that it matches the typed device
3010
int index = m_devicelist.findIndex( device->currentText() );
3012
mountpoint->setText( d->mountpointlist[index] );
3014
mountpoint->setText( QString::null );
3019
void KDevicePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize,
3020
const unsigned long& /*kBUsed*/,
3021
const unsigned long& kBAvail,
3024
d->m_freeSpaceText->show();
3025
d->m_freeSpaceLabel->show();
3027
int percUsed = 100 - (int)(100.0 * kBAvail / kBSize);
3029
d->m_freeSpaceLabel->setText(
3030
// xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part.
3031
i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)")
3032
.arg(KIO::convertSizeFromKB(kBAvail))
3033
.arg(KIO::convertSizeFromKB(kBSize))
3034
.arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
3036
d->m_freeSpaceBar->setProgress(percUsed, 100);
3037
d->m_freeSpaceBar->show();
3040
bool KDevicePropsPlugin::supports( KFileItemList _items )
3042
if ( _items.count() != 1 )
3044
KFileItem * item = _items.first();
3045
// check if desktop file
3046
if ( !KPropsDlgPlugin::isDesktopFile( item ) )
3048
// open file and check type
3049
KDesktopFile config( item->url().path(), true /* readonly */ );
3050
return config.hasDeviceType();
3053
void KDevicePropsPlugin::applyChanges()
3055
QString path = properties->kurl().path();
3057
if ( !f.open( IO_ReadWrite ) )
3059
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient "
3060
"access to write to <b>%1</b>.</qt>").arg(path));
3065
KSimpleConfig config( path );
3066
config.setDesktopGroup();
3067
config.writeEntry( "Type", QString::fromLatin1("FSDevice") );
3069
config.writeEntry( "Dev", device->currentText() );
3070
config.writeEntry( "MountPoint", mountpoint->text() );
3072
config.writeEntry( "UnmountIcon", unmounted->icon() );
3073
kdDebug(250) << "unmounted->icon() = " << unmounted->icon() << endl;
3075
config.writeEntry( "ReadOnly", readonly->isChecked() );
3081
/* ----------------------------------------------------
3083
* KDesktopPropsPlugin
3085
* -------------------------------------------------- */
3088
KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props )
3089
: KPropsDlgPlugin( _props )
3091
QFrame *frame = properties->addPage(i18n("&Application"));
3092
QVBoxLayout *mainlayout = new QVBoxLayout( frame, 0, KDialog::spacingHint() );
3094
w = new KPropertiesDesktopBase(frame);
3095
mainlayout->addWidget(w);
3097
bool bKDesktopMode = (QCString(qApp->name()) == "kdesktop"); // nasty heh?
3102
w->nameEdit->hide();
3103
w->nameLabel->hide();
3106
w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
3107
w->pathEdit->lineEdit()->setAcceptDrops(false);
3109
connect( w->nameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
3110
connect( w->genNameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
3111
connect( w->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
3112
connect( w->commandEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
3113
connect( w->pathEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
3115
connect( w->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
3116
connect( w->addFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotAddFiletype() ) );
3117
connect( w->delFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotDelFiletype() ) );
3118
connect( w->advancedButton, SIGNAL( clicked() ), this, SLOT( slotAdvanced() ) );
3120
// now populate the page
3121
QString path = _props->kurl().path();
3123
if ( !f.open( IO_ReadOnly ) )
3127
KDesktopFile config( path );
3128
QString nameStr = config.readName();
3129
QString genNameStr = config.readGenericName();
3130
QString commentStr = config.readComment();
3131
QString commandStr = config.readPathEntry( "Exec" );
3132
if (commandStr.left(12) == "ksystraycmd ")
3134
commandStr.remove(0, 12);
3135
m_systrayBool = true;
3138
m_systrayBool = false;
3140
m_origCommandStr = commandStr;
3141
QString pathStr = config.readPathEntry( "Path" );
3142
m_terminalBool = config.readBoolEntry( "Terminal" );
3143
m_terminalOptionStr = config.readEntry( "TerminalOptions" );
3144
m_suidBool = config.readBoolEntry( "X-KDE-SubstituteUID" );
3145
m_suidUserStr = config.readEntry( "X-KDE-Username" );
3146
if( config.hasKey( "StartupNotify" ))
3147
m_startupBool = config.readBoolEntry( "StartupNotify", true );
3149
m_startupBool = config.readBoolEntry( "X-KDE-StartupNotify", true );
3150
m_dcopServiceType = config.readEntry("X-DCOP-ServiceType").lower();
3152
QStringList mimeTypes = config.readListEntry( "MimeType", ';' );
3154
if ( nameStr.isEmpty() || bKDesktopMode ) {
3155
// We'll use the file name if no name is specified
3156
// because we _need_ a Name for a valid file.
3157
// But let's do it in apply, not here, so that we pick up the right name.
3160
if ( !bKDesktopMode )
3161
w->nameEdit->setText(nameStr);
3163
w->genNameEdit->setText( genNameStr );
3164
w->commentEdit->setText( commentStr );
3165
w->commandEdit->setText( commandStr );
3166
w->pathEdit->lineEdit()->setText( pathStr );
3167
w->filetypeList->setAllColumnsShowFocus(true);
3169
KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
3170
for(QStringList::ConstIterator it = mimeTypes.begin();
3171
it != mimeTypes.end(); )
3173
KMimeType::Ptr p = KMimeType::mimeType(*it);
3176
if (it != mimeTypes.end())
3179
(*it).toInt(&numeric);
3186
if (p && (p != defaultMimetype))
3188
new QListViewItem(w->filetypeList, p->name(), p->comment(), preference);
3194
KDesktopPropsPlugin::~KDesktopPropsPlugin()
3198
void KDesktopPropsPlugin::slotSelectMimetype()
3200
QListView *w = (QListView*)sender();
3201
QListViewItem *item = w->firstChild();
3204
if (item->isSelected())
3205
w->setSelected(item, false);
3206
item = item->nextSibling();
3210
void KDesktopPropsPlugin::slotAddFiletype()
3212
KDialogBase dlg(w, "KPropertiesMimetypes", true,
3213
i18n("Add File Type for %1").arg(properties->kurl().fileName()),
3214
KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok);
3216
KGuiItem okItem(i18n("&Add"), QString::null /* no icon */,
3217
i18n("Add the selected file types to\nthe list of supported file types."),
3218
i18n("Add the selected file types to\nthe list of supported file types."));
3219
dlg.setButtonOK(okItem);
3221
KPropertiesMimetypeBase *mw = new KPropertiesMimetypeBase(&dlg);
3223
dlg.setMainWidget(mw);
3226
mw->listView->setRootIsDecorated(true);
3227
mw->listView->setSelectionMode(QListView::Extended);
3228
mw->listView->setAllColumnsShowFocus(true);
3229
mw->listView->setFullWidth(true);
3230
mw->listView->setMinimumSize(500,400);
3232
connect(mw->listView, SIGNAL(selectionChanged()),
3233
this, SLOT(slotSelectMimetype()));
3234
connect(mw->listView, SIGNAL(doubleClicked( QListViewItem *, const QPoint &, int )),
3235
&dlg, SLOT( slotOk()));
3237
QMap<QString,QListViewItem*> majorMap;
3238
QListViewItem *majorGroup;
3239
KMimeType::List mimetypes = KMimeType::allMimeTypes();
3240
QValueListIterator<KMimeType::Ptr> it(mimetypes.begin());
3241
for (; it != mimetypes.end(); ++it) {
3242
QString mimetype = (*it)->name();
3243
if (mimetype == KMimeType::defaultMimeType())
3245
int index = mimetype.find("/");
3246
QString maj = mimetype.left(index);
3247
QString min = mimetype.mid(index+1);
3249
QMapIterator<QString,QListViewItem*> mit = majorMap.find( maj );
3250
if ( mit == majorMap.end() ) {
3251
majorGroup = new QListViewItem( mw->listView, maj );
3252
majorGroup->setExpandable(true);
3253
mw->listView->setOpen(majorGroup, true);
3254
majorMap.insert( maj, majorGroup );
3258
majorGroup = mit.data();
3261
QListViewItem *item = new QListViewItem(majorGroup, min, (*it)->comment());
3262
item->setPixmap(0, (*it)->pixmap(KIcon::Small, IconSize(KIcon::Small)));
3264
QMapIterator<QString,QListViewItem*> mit = majorMap.find( "all" );
3265
if ( mit != majorMap.end())
3267
mw->listView->setCurrentItem(mit.data());
3268
mw->listView->ensureItemVisible(mit.data());
3272
if (dlg.exec() == KDialogBase::Accepted)
3274
KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
3275
QListViewItem *majorItem = mw->listView->firstChild();
3278
QString major = majorItem->text(0);
3280
QListViewItem *minorItem = majorItem->firstChild();
3283
if (minorItem->isSelected())
3285
QString mimetype = major + "/" + minorItem->text(0);
3286
KMimeType::Ptr p = KMimeType::mimeType(mimetype);
3287
if (p && (p != defaultMimetype))
3289
mimetype = p->name();
3291
QListViewItem *item = w->filetypeList->firstChild();
3294
if (mimetype == item->text(0))
3299
item = item->nextSibling();
3302
new QListViewItem(w->filetypeList, p->name(), p->comment());
3307
minorItem = minorItem->nextSibling();
3310
majorItem = majorItem->nextSibling();
3316
void KDesktopPropsPlugin::slotDelFiletype()
3318
delete w->filetypeList->currentItem();
3322
void KDesktopPropsPlugin::checkCommandChanged()
3324
if (KRun::binaryName(w->commandEdit->text(), true) !=
3325
KRun::binaryName(m_origCommandStr, true))
3327
QString m_origCommandStr = w->commandEdit->text();
3328
m_dcopServiceType= QString::null; // Reset
3332
void KDesktopPropsPlugin::applyChanges()
3334
kdDebug(250) << "KDesktopPropsPlugin::applyChanges" << endl;
3335
QString path = properties->kurl().path();
3339
if ( !f.open( IO_ReadWrite ) ) {
3340
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
3341
"sufficient access to write to <b>%1</b>.</qt>").arg(path));
3346
// If the command is changed we reset certain settings that are strongly
3347
// coupled to the command.
3348
checkCommandChanged();
3350
KSimpleConfig config( path );
3351
config.setDesktopGroup();
3352
config.writeEntry( "Type", QString::fromLatin1("Application"));
3353
config.writeEntry( "Comment", w->commentEdit->text() );
3354
config.writeEntry( "Comment", w->commentEdit->text(), true, false, true ); // for compat
3355
config.writeEntry( "GenericName", w->genNameEdit->text() );
3356
config.writeEntry( "GenericName", w->genNameEdit->text(), true, false, true ); // for compat
3359
config.writePathEntry( "Exec", w->commandEdit->text().prepend("ksystraycmd ") );
3361
config.writePathEntry( "Exec", w->commandEdit->text() );
3362
config.writePathEntry( "Path", w->pathEdit->lineEdit()->text() );
3365
QStringList mimeTypes;
3366
for( QListViewItem *item = w->filetypeList->firstChild();
3367
item; item = item->nextSibling() )
3369
QString preference = item->text(2);
3370
mimeTypes.append(item->text(0));
3371
if (!preference.isEmpty())
3372
mimeTypes.append(preference);
3375
config.writeEntry( "MimeType", mimeTypes, ';' );
3377
if ( !w->nameEdit->isHidden() ) {
3378
QString nameStr = w->nameEdit->text();
3379
config.writeEntry( "Name", nameStr );
3380
config.writeEntry( "Name", nameStr, true, false, true );
3383
config.writeEntry("Terminal", m_terminalBool);
3384
config.writeEntry("TerminalOptions", m_terminalOptionStr);
3385
config.writeEntry("X-KDE-SubstituteUID", m_suidBool);
3386
config.writeEntry("X-KDE-Username", m_suidUserStr);
3387
config.writeEntry("StartupNotify", m_startupBool);
3388
config.writeEntry("X-DCOP-ServiceType", m_dcopServiceType);
3391
// KSycoca update needed?
3392
QString sycocaPath = KGlobal::dirs()->relativeLocation("apps", path);
3393
bool updateNeeded = !sycocaPath.startsWith("/");
3396
sycocaPath = KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
3397
updateNeeded = !sycocaPath.startsWith("/");
3400
KService::rebuildKSycoca(w);
3404
void KDesktopPropsPlugin::slotBrowseExec()
3406
KURL f = KFileDialog::getOpenURL( QString::null,
3411
if ( !f.isLocalFile()) {
3412
KMessageBox::sorry(w, i18n("Only executables on local file systems are supported."));
3416
QString path = f.path();
3417
KRun::shellQuote( path );
3418
w->commandEdit->setText( path );
3421
void KDesktopPropsPlugin::slotAdvanced()
3423
KDialogBase dlg(w, "KPropertiesDesktopAdv", true,
3424
i18n("Advanced Options for %1").arg(properties->kurl().fileName()),
3425
KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok);
3426
KPropertiesDesktopAdvBase *w = new KPropertiesDesktopAdvBase(&dlg);
3428
dlg.setMainWidget(w);
3430
// If the command is changed we reset certain settings that are strongly
3431
// coupled to the command.
3432
checkCommandChanged();
3434
// check to see if we use konsole if not do not add the nocloseonexit
3435
// because we don't know how to do this on other terminal applications
3436
KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
3437
QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
3438
QString::fromLatin1("konsole"));
3440
bool terminalCloseBool = false;
3442
if (preferredTerminal == "konsole")
3444
terminalCloseBool = (m_terminalOptionStr.contains( "--noclose" ) > 0);
3445
w->terminalCloseCheck->setChecked(terminalCloseBool);
3446
m_terminalOptionStr.replace( "--noclose", "");
3450
w->terminalCloseCheck->hide();
3453
w->terminalCheck->setChecked(m_terminalBool);
3454
w->terminalEdit->setText(m_terminalOptionStr);
3455
w->terminalCloseCheck->setEnabled(m_terminalBool);
3456
w->terminalEdit->setEnabled(m_terminalBool);
3457
w->terminalEditLabel->setEnabled(m_terminalBool);
3459
w->suidCheck->setChecked(m_suidBool);
3460
w->suidEdit->setText(m_suidUserStr);
3461
w->suidEdit->setEnabled(m_suidBool);
3462
w->suidEditLabel->setEnabled(m_suidBool);
3464
w->startupInfoCheck->setChecked(m_startupBool);
3465
w->systrayCheck->setChecked(m_systrayBool);
3467
if (m_dcopServiceType == "unique")
3468
w->dcopCombo->setCurrentItem(2);
3469
else if (m_dcopServiceType == "multi")
3470
w->dcopCombo->setCurrentItem(1);
3471
else if (m_dcopServiceType == "wait")
3472
w->dcopCombo->setCurrentItem(3);
3474
w->dcopCombo->setCurrentItem(0);
3476
// Provide username completion up to 1000 users.
3477
KCompletion *kcom = new KCompletion;
3478
kcom->setOrder(KCompletion::Sorted);
3480
int i, maxEntries = 1000;
3482
for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
3483
kcom->addItem(QString::fromLatin1(pw->pw_name));
3487
w->suidEdit->setCompletionObject(kcom, true);
3488
w->suidEdit->setAutoDeleteCompletionObject( true );
3489
w->suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
3496
connect( w->terminalEdit, SIGNAL( textChanged( const QString & ) ),
3497
this, SIGNAL( changed() ) );
3498
connect( w->terminalCloseCheck, SIGNAL( toggled( bool ) ),
3499
this, SIGNAL( changed() ) );
3500
connect( w->terminalCheck, SIGNAL( toggled( bool ) ),
3501
this, SIGNAL( changed() ) );
3502
connect( w->suidCheck, SIGNAL( toggled( bool ) ),
3503
this, SIGNAL( changed() ) );
3504
connect( w->suidEdit, SIGNAL( textChanged( const QString & ) ),
3505
this, SIGNAL( changed() ) );
3506
connect( w->startupInfoCheck, SIGNAL( toggled( bool ) ),
3507
this, SIGNAL( changed() ) );
3508
connect( w->systrayCheck, SIGNAL( toggled( bool ) ),
3509
this, SIGNAL( changed() ) );
3510
connect( w->dcopCombo, SIGNAL( highlighted( int ) ),
3511
this, SIGNAL( changed() ) );
3513
if ( dlg.exec() == QDialog::Accepted )
3515
m_terminalOptionStr = w->terminalEdit->text().stripWhiteSpace();
3516
m_terminalBool = w->terminalCheck->isChecked();
3517
m_suidBool = w->suidCheck->isChecked();
3518
m_suidUserStr = w->suidEdit->text().stripWhiteSpace();
3519
m_startupBool = w->startupInfoCheck->isChecked();
3520
m_systrayBool = w->systrayCheck->isChecked();
3522
if (w->terminalCloseCheck->isChecked())
3524
m_terminalOptionStr.append(" --noclose");
3527
switch(w->dcopCombo->currentItem())
3529
case 1: m_dcopServiceType = "multi"; break;
3530
case 2: m_dcopServiceType = "unique"; break;
3531
case 3: m_dcopServiceType = "wait"; break;
3532
default: m_dcopServiceType = "none"; break;
3537
bool KDesktopPropsPlugin::supports( KFileItemList _items )
3539
if ( _items.count() != 1 )
3541
KFileItem * item = _items.first();
3542
// check if desktop file
3543
if ( !KPropsDlgPlugin::isDesktopFile( item ) )
3545
// open file and check type
3546
KDesktopFile config( item->url().path(), true /* readonly */ );
3547
return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access");
3550
void KPropertiesDialog::virtual_hook( int id, void* data )
3551
{ KDialogBase::virtual_hook( id, data ); }
3553
void KPropsDlgPlugin::virtual_hook( int, void* )
3554
{ /*BASE::virtual_hook( id, data );*/ }
3561
* The following code is obsolete and only kept for binary compatibility
3562
* To be removed in KDE 4
3565
class KExecPropsPlugin::KExecPropsPluginPrivate
3568
KExecPropsPluginPrivate()
3571
~KExecPropsPluginPrivate()
3576
QCheckBox *nocloseonexitCheck;
3579
KExecPropsPlugin::KExecPropsPlugin( KPropertiesDialog *_props )
3580
: KPropsDlgPlugin( _props )
3582
d = new KExecPropsPluginPrivate;
3583
d->m_frame = properties->addPage(i18n("E&xecute"));
3584
QVBoxLayout * mainlayout = new QVBoxLayout( d->m_frame, 0,
3585
KDialog::spacingHint());
3587
// Now the widgets in the top layout
3590
l = new QLabel( i18n( "Comman&d:" ), d->m_frame );
3591
mainlayout->addWidget(l);
3593
QHBoxLayout * hlayout;
3594
hlayout = new QHBoxLayout(KDialog::spacingHint());
3595
mainlayout->addLayout(hlayout);
3597
execEdit = new KLineEdit( d->m_frame );
3598
QWhatsThis::add(execEdit,i18n(
3599
"Following the command, you can have several place holders which will be replaced "
3600
"with the actual values when the actual program is run:\n"
3601
"%f - a single file name\n"
3602
"%F - a list of files; use for applications that can open several local files at once\n"
3603
"%u - a single URL\n"
3604
"%U - a list of URLs\n"
3605
"%d - the folder of the file to open\n"
3606
"%D - a list of folders\n"
3608
"%m - the mini-icon\n"
3609
"%c - the caption"));
3610
hlayout->addWidget(execEdit, 1);
3612
l->setBuddy( execEdit );
3614
execBrowse = new QPushButton( d->m_frame );
3615
execBrowse->setText( i18n("&Browse...") );
3616
hlayout->addWidget(execBrowse);
3618
// The groupbox about swallowing
3619
QGroupBox* tmpQGroupBox;
3620
tmpQGroupBox = new QGroupBox( i18n("Panel Embedding"), d->m_frame );
3621
tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
3623
mainlayout->addWidget(tmpQGroupBox);
3625
QGridLayout *grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2);
3626
grid->setSpacing( KDialog::spacingHint() );
3627
grid->setColStretch(1, 1);
3629
l = new QLabel( i18n( "&Execute on click:" ), tmpQGroupBox );
3630
grid->addWidget(l, 0, 0);
3632
swallowExecEdit = new KLineEdit( tmpQGroupBox );
3633
grid->addWidget(swallowExecEdit, 0, 1);
3635
l->setBuddy( swallowExecEdit );
3637
l = new QLabel( i18n( "&Window title:" ), tmpQGroupBox );
3638
grid->addWidget(l, 1, 0);
3640
swallowTitleEdit = new KLineEdit( tmpQGroupBox );
3641
grid->addWidget(swallowTitleEdit, 1, 1);
3643
l->setBuddy( swallowTitleEdit );
3645
// The groupbox about run in terminal
3647
tmpQGroupBox = new QGroupBox( d->m_frame );
3648
tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
3650
mainlayout->addWidget(tmpQGroupBox);
3652
grid = new QGridLayout(tmpQGroupBox->layout(), 3, 2);
3653
grid->setSpacing( KDialog::spacingHint() );
3654
grid->setColStretch(1, 1);
3656
terminalCheck = new QCheckBox( tmpQGroupBox );
3657
terminalCheck->setText( i18n("&Run in terminal") );
3658
grid->addMultiCellWidget(terminalCheck, 0, 0, 0, 1);
3660
// check to see if we use konsole if not do not add the nocloseonexit
3661
// because we don't know how to do this on other terminal applications
3662
KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
3663
QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
3664
QString::fromLatin1("konsole"));
3667
d->nocloseonexitCheck = 0L;
3668
if (preferredTerminal == "konsole")
3671
d->nocloseonexitCheck = new QCheckBox( tmpQGroupBox );
3672
d->nocloseonexitCheck->setText( i18n("Do not &close when command exits") );
3673
grid->addMultiCellWidget(d->nocloseonexitCheck, 1, 1, 0, 1);
3676
terminalLabel = new QLabel( i18n( "&Terminal options:" ), tmpQGroupBox );
3677
grid->addWidget(terminalLabel, posOptions, 0);
3679
terminalEdit = new KLineEdit( tmpQGroupBox );
3680
grid->addWidget(terminalEdit, posOptions, 1);
3682
terminalLabel->setBuddy( terminalEdit );
3684
// The groupbox about run with substituted uid.
3686
tmpQGroupBox = new QGroupBox( d->m_frame );
3687
tmpQGroupBox->setColumnLayout( 0, Qt::Horizontal );
3689
mainlayout->addWidget(tmpQGroupBox);
3691
grid = new QGridLayout(tmpQGroupBox->layout(), 2, 2);
3692
grid->setSpacing(KDialog::spacingHint());
3693
grid->setColStretch(1, 1);
3695
suidCheck = new QCheckBox(tmpQGroupBox);
3696
suidCheck->setText(i18n("Ru&n as a different user"));
3697
grid->addMultiCellWidget(suidCheck, 0, 0, 0, 1);
3699
suidLabel = new QLabel(i18n( "&Username:" ), tmpQGroupBox);
3700
grid->addWidget(suidLabel, 1, 0);
3702
suidEdit = new KLineEdit(tmpQGroupBox);
3703
grid->addWidget(suidEdit, 1, 1);
3705
suidLabel->setBuddy( suidEdit );
3707
mainlayout->addStretch(1);
3709
// now populate the page
3710
QString path = _props->kurl().path();
3712
if ( !f.open( IO_ReadOnly ) )
3716
KSimpleConfig config( path );
3717
config.setDollarExpansion( false );
3718
config.setDesktopGroup();
3719
execStr = config.readPathEntry( "Exec" );
3720
swallowExecStr = config.readPathEntry( "SwallowExec" );
3721
swallowTitleStr = config.readEntry( "SwallowTitle" );
3722
termBool = config.readBoolEntry( "Terminal" );
3723
termOptionsStr = config.readEntry( "TerminalOptions" );
3724
suidBool = config.readBoolEntry( "X-KDE-SubstituteUID" );
3725
suidUserStr = config.readEntry( "X-KDE-Username" );
3727
if ( !swallowExecStr.isNull() )
3728
swallowExecEdit->setText( swallowExecStr );
3729
if ( !swallowTitleStr.isNull() )
3730
swallowTitleEdit->setText( swallowTitleStr );
3732
if ( !execStr.isNull() )
3733
execEdit->setText( execStr );
3735
if ( d->nocloseonexitCheck )
3737
d->nocloseonexitCheck->setChecked( (termOptionsStr.contains( "--noclose" ) > 0) );
3738
termOptionsStr.replace( "--noclose", "");
3740
if ( !termOptionsStr.isNull() )
3741
terminalEdit->setText( termOptionsStr );
3743
terminalCheck->setChecked( termBool );
3744
enableCheckedEdit();
3746
suidCheck->setChecked( suidBool );
3747
suidEdit->setText( suidUserStr );
3750
// Provide username completion up to 1000 users.
3751
KCompletion *kcom = new KCompletion;
3752
kcom->setOrder(KCompletion::Sorted);
3754
int i, maxEntries = 1000;
3756
for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
3757
kcom->addItem(QString::fromLatin1(pw->pw_name));
3761
suidEdit->setCompletionObject(kcom, true);
3762
suidEdit->setAutoDeleteCompletionObject( true );
3763
suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
3770
connect( swallowExecEdit, SIGNAL( textChanged( const QString & ) ),
3771
this, SIGNAL( changed() ) );
3772
connect( swallowTitleEdit, SIGNAL( textChanged( const QString & ) ),
3773
this, SIGNAL( changed() ) );
3774
connect( execEdit, SIGNAL( textChanged( const QString & ) ),
3775
this, SIGNAL( changed() ) );
3776
connect( terminalEdit, SIGNAL( textChanged( const QString & ) ),
3777
this, SIGNAL( changed() ) );
3778
if (d->nocloseonexitCheck)
3779
connect( d->nocloseonexitCheck, SIGNAL( toggled( bool ) ),
3780
this, SIGNAL( changed() ) );
3781
connect( terminalCheck, SIGNAL( toggled( bool ) ),
3782
this, SIGNAL( changed() ) );
3783
connect( suidCheck, SIGNAL( toggled( bool ) ),
3784
this, SIGNAL( changed() ) );
3785
connect( suidEdit, SIGNAL( textChanged( const QString & ) ),
3786
this, SIGNAL( changed() ) );
3788
connect( execBrowse, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
3789
connect( terminalCheck, SIGNAL( clicked() ), this, SLOT( enableCheckedEdit() ) );
3790
connect( suidCheck, SIGNAL( clicked() ), this, SLOT( enableSuidEdit() ) );
3794
KExecPropsPlugin::~KExecPropsPlugin()
3799
void KExecPropsPlugin::enableCheckedEdit()
3801
bool checked = terminalCheck->isChecked();
3802
terminalLabel->setEnabled( checked );
3803
if (d->nocloseonexitCheck)
3804
d->nocloseonexitCheck->setEnabled( checked );
3805
terminalEdit->setEnabled( checked );
3808
void KExecPropsPlugin::enableSuidEdit()
3810
bool checked = suidCheck->isChecked();
3811
suidLabel->setEnabled( checked );
3812
suidEdit->setEnabled( checked );
3815
bool KExecPropsPlugin::supports( KFileItemList _items )
3817
if ( _items.count() != 1 )
3819
KFileItem * item = _items.first();
3820
// check if desktop file
3821
if ( !KPropsDlgPlugin::isDesktopFile( item ) )
3823
// open file and check type
3824
KDesktopFile config( item->url().path(), true /* readonly */ );
3825
return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access");
3828
void KExecPropsPlugin::applyChanges()
3830
kdDebug(250) << "KExecPropsPlugin::applyChanges" << endl;
3831
QString path = properties->kurl().path();
3835
if ( !f.open( IO_ReadWrite ) ) {
3836
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
3837
"sufficient access to write to <b>%1</b>.</qt>").arg(path));
3842
KSimpleConfig config( path );
3843
config.setDesktopGroup();
3844
config.writeEntry( "Type", QString::fromLatin1("Application"));
3845
config.writePathEntry( "Exec", execEdit->text() );
3846
config.writePathEntry( "SwallowExec", swallowExecEdit->text() );
3847
config.writeEntry( "SwallowTitle", swallowTitleEdit->text() );
3848
config.writeEntry( "Terminal", terminalCheck->isChecked() );
3849
QString temp = terminalEdit->text();
3850
if (d->nocloseonexitCheck )
3851
if ( d->nocloseonexitCheck->isChecked() )
3852
temp += QString::fromLatin1("--noclose ");
3853
temp = temp.stripWhiteSpace();
3854
config.writeEntry( "TerminalOptions", temp );
3855
config.writeEntry( "X-KDE-SubstituteUID", suidCheck->isChecked() );
3856
config.writeEntry( "X-KDE-Username", suidEdit->text() );
3860
void KExecPropsPlugin::slotBrowseExec()
3862
KURL f = KFileDialog::getOpenURL( QString::null,
3863
QString::null, d->m_frame );
3867
if ( !f.isLocalFile()) {
3868
KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported."));
3872
QString path = f.path();
3873
KRun::shellQuote( path );
3874
execEdit->setText( path );
3877
class KApplicationPropsPlugin::KApplicationPropsPluginPrivate
3880
KApplicationPropsPluginPrivate()
3882
m_kdesktopMode = QCString(qApp->name()) == "kdesktop"; // nasty heh?
3884
~KApplicationPropsPluginPrivate()
3889
bool m_kdesktopMode;
3892
KApplicationPropsPlugin::KApplicationPropsPlugin( KPropertiesDialog *_props )
3893
: KPropsDlgPlugin( _props )
3895
d = new KApplicationPropsPluginPrivate;
3896
d->m_frame = properties->addPage(i18n("&Application"));
3897
QVBoxLayout *toplayout = new QVBoxLayout( d->m_frame, 0, KDialog::spacingHint());
3902
addExtensionButton = new QPushButton( QString::null, d->m_frame );
3903
iconSet = SmallIconSet( "back" );
3904
addExtensionButton->setIconSet( iconSet );
3905
pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal );
3906
addExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
3907
connect( addExtensionButton, SIGNAL( clicked() ),
3908
SLOT( slotAddExtension() ) );
3910
delExtensionButton = new QPushButton( QString::null, d->m_frame );
3911
iconSet = SmallIconSet( "forward" );
3912
delExtensionButton->setIconSet( iconSet );
3913
delExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
3914
connect( delExtensionButton, SIGNAL( clicked() ),
3915
SLOT( slotDelExtension() ) );
3919
QGridLayout *grid = new QGridLayout(2, 2);
3920
grid->setColStretch(1, 1);
3921
toplayout->addLayout(grid);
3923
if ( d->m_kdesktopMode )
3925
// in kdesktop the name field comes from the first tab
3930
l = new QLabel(i18n("Name:"), d->m_frame, "Label_4" );
3931
grid->addWidget(l, 0, 0);
3933
nameEdit = new KLineEdit( d->m_frame, "LineEdit_3" );
3934
grid->addWidget(nameEdit, 0, 1);
3937
l = new QLabel(i18n("Description:"), d->m_frame, "Label_5" );
3938
grid->addWidget(l, 1, 0);
3940
genNameEdit = new KLineEdit( d->m_frame, "LineEdit_4" );
3941
grid->addWidget(genNameEdit, 1, 1);
3943
l = new QLabel(i18n("Comment:"), d->m_frame, "Label_3" );
3944
grid->addWidget(l, 2, 0);
3946
commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" );
3947
grid->addWidget(commentEdit, 2, 1);
3949
l = new QLabel(i18n("File types:"), d->m_frame);
3950
toplayout->addWidget(l, 0, AlignLeft);
3952
grid = new QGridLayout(4, 3);
3953
grid->setColStretch(0, 1);
3954
grid->setColStretch(2, 1);
3955
grid->setRowStretch( 0, 1 );
3956
grid->setRowStretch( 3, 1 );
3957
toplayout->addLayout(grid, 2);
3959
extensionsList = new QListBox( d->m_frame );
3960
extensionsList->setSelectionMode( QListBox::Extended );
3961
grid->addMultiCellWidget(extensionsList, 0, 3, 0, 0);
3963
grid->addWidget(addExtensionButton, 1, 1);
3964
grid->addWidget(delExtensionButton, 2, 1);
3966
availableExtensionsList = new QListBox( d->m_frame );
3967
availableExtensionsList->setSelectionMode( QListBox::Extended );
3968
grid->addMultiCellWidget(availableExtensionsList, 0, 3, 2, 2);
3970
QString path = properties->kurl().path() ;
3972
if ( !f.open( IO_ReadOnly ) )
3976
KDesktopFile config( path );
3977
QString commentStr = config.readComment();
3978
QString genNameStr = config.readGenericName();
3980
QStringList selectedTypes = config.readListEntry( "ServiceTypes" );
3981
// For compatibility with KDE 1.x
3982
selectedTypes += config.readListEntry( "MimeType", ';' );
3984
QString nameStr = config.readName();
3985
if ( nameStr.isEmpty() || d->m_kdesktopMode ) {
3986
// We'll use the file name if no name is specified
3987
// because we _need_ a Name for a valid file.
3988
// But let's do it in apply, not here, so that we pick up the right name.
3992
commentEdit->setText( commentStr );
3993
genNameEdit->setText( genNameStr );
3995
nameEdit->setText( nameStr );
3997
selectedTypes.sort();
3998
QStringList::Iterator sit = selectedTypes.begin();
3999
for( ; sit != selectedTypes.end(); ++sit ) {
4000
if ( !((*sit).isEmpty()) )
4001
extensionsList->insertItem( *sit );
4004
KMimeType::List mimeTypes = KMimeType::allMimeTypes();
4005
QValueListIterator<KMimeType::Ptr> it2 = mimeTypes.begin();
4006
for ( ; it2 != mimeTypes.end(); ++it2 )
4007
addMimeType ( (*it2)->name() );
4011
connect( extensionsList, SIGNAL( highlighted( int ) ),
4012
this, SLOT( updateButton() ) );
4013
connect( availableExtensionsList, SIGNAL( highlighted( int ) ),
4014
this, SLOT( updateButton() ) );
4016
connect( addExtensionButton, SIGNAL( clicked() ),
4017
this, SIGNAL( changed() ) );
4018
connect( delExtensionButton, SIGNAL( clicked() ),
4019
this, SIGNAL( changed() ) );
4021
connect( nameEdit, SIGNAL( textChanged( const QString & ) ),
4022
this, SIGNAL( changed() ) );
4023
connect( commentEdit, SIGNAL( textChanged( const QString & ) ),
4024
this, SIGNAL( changed() ) );
4025
connect( genNameEdit, SIGNAL( textChanged( const QString & ) ),
4026
this, SIGNAL( changed() ) );
4027
connect( availableExtensionsList, SIGNAL( selected( int ) ),
4028
this, SIGNAL( changed() ) );
4029
connect( extensionsList, SIGNAL( selected( int ) ),
4030
this, SIGNAL( changed() ) );
4033
KApplicationPropsPlugin::~KApplicationPropsPlugin()
4038
// QString KApplicationPropsPlugin::tabName () const
4040
// return i18n ("&Application");
4043
void KApplicationPropsPlugin::updateButton()
4045
addExtensionButton->setEnabled(availableExtensionsList->currentItem()>-1);
4046
delExtensionButton->setEnabled(extensionsList->currentItem()>-1);
4049
void KApplicationPropsPlugin::addMimeType( const QString & name )
4051
// Add a mimetype to the list of available mime types if not in the extensionsList
4055
for ( uint i = 0; i < extensionsList->count(); i++ )
4056
if ( extensionsList->text( i ) == name )
4061
availableExtensionsList->insertItem( name );
4062
availableExtensionsList->sort();
4066
bool KApplicationPropsPlugin::supports( KFileItemList _items )
4068
// same constraints as KExecPropsPlugin : desktop file with Type = Application
4069
return KExecPropsPlugin::supports( _items );
4072
void KApplicationPropsPlugin::applyChanges()
4074
QString path = properties->kurl().path();
4078
if ( !f.open( IO_ReadWrite ) ) {
4079
KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
4080
"have sufficient access to write to <b>%1</b>.</qt>").arg(path));
4085
KSimpleConfig config( path );
4086
config.setDesktopGroup();
4087
config.writeEntry( "Type", QString::fromLatin1("Application"));
4088
config.writeEntry( "Comment", commentEdit->text() );
4089
config.writeEntry( "Comment", commentEdit->text(), true, false, true ); // for compat
4090
config.writeEntry( "GenericName", genNameEdit->text() );
4091
config.writeEntry( "GenericName", genNameEdit->text(), true, false, true ); // for compat
4093
QStringList selectedTypes;
4094
for ( uint i = 0; i < extensionsList->count(); i++ )
4095
selectedTypes.append( extensionsList->text( i ) );
4097
config.writeEntry( "MimeType", selectedTypes, ';' );
4098
config.writeEntry( "ServiceTypes", "" );
4099
// hmm, actually it should probably be the contrary (but see also typeslistitem.cpp)
4101
QString nameStr = nameEdit ? nameEdit->text() : QString::null;
4102
if ( nameStr.isEmpty() ) // nothing entered, or widget not existing at all (kdesktop mode)
4103
nameStr = nameFromFileName(properties->kurl().fileName());
4105
config.writeEntry( "Name", nameStr );
4106
config.writeEntry( "Name", nameStr, true, false, true );
4111
void KApplicationPropsPlugin::slotAddExtension()
4113
QListBoxItem *item = availableExtensionsList->firstItem();
4114
QListBoxItem *nextItem;
4118
nextItem = item->next();
4120
if ( item->isSelected() )
4122
extensionsList->insertItem( item->text() );
4123
availableExtensionsList->removeItem( availableExtensionsList->index( item ) );
4129
extensionsList->sort();
4133
void KApplicationPropsPlugin::slotDelExtension()
4135
QListBoxItem *item = extensionsList->firstItem();
4136
QListBoxItem *nextItem;
4140
nextItem = item->next();
4142
if ( item->isSelected() )
4144
availableExtensionsList->insertItem( item->text() );
4145
extensionsList->removeItem( extensionsList->index( item ) );
4151
availableExtensionsList->sort();
4157
#include "kpropertiesdialog.moc"