/* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> Copyright (c) 1999, 2000 Preston Brown <pbrown@kde.org> Copyright (c) 2000 Simon Hausmann <hausmann@kde.org> Copyright (c) 2000 David Faure <faure@kde.org> Copyright (c) 2003 Waldo Bastian <bastian@kde.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * kpropertiesdialog.cpp * View/Edit Properties of files, locally or remotely * * some FilePermissionsPropsPlugin-changes by * Henner Zeller <zeller@think.de> * some layout management by * Bertrand Leconte <B.Leconte@mail.dotcom.fr> * the rest of the layout management, bug fixes, adaptation to libtdeio, * template feature by * David Faure <faure@kde.org> * More layout, cleanups, and fixes by * Preston Brown <pbrown@kde.org> * Plugin capability, cleanups and port to KDialogBase by * Simon Hausmann <hausmann@kde.org> * KDesktopPropsPlugin by * Waldo Bastian <bastian@kde.org> */ #include <config.h> extern "C" { #include <pwd.h> #include <grp.h> #include <time.h> #include <sys/types.h> } #include <unistd.h> #include <errno.h> #include <assert.h> #include <algorithm> #include <functional> #include <tqfile.h> #include <tqdir.h> #include <tqlabel.h> #include <tqpushbutton.h> #include <tqcheckbox.h> #include <tqstrlist.h> #include <tqstringlist.h> #include <tqtextstream.h> #include <tqpainter.h> #include <tqlayout.h> #include <tqcombobox.h> #include <tqgroupbox.h> #include <tqwhatsthis.h> #include <tqtooltip.h> #include <tqstyle.h> #include <tqprogressbar.h> #include <tqvbox.h> #include <tqvaluevector.h> #ifdef USE_POSIX_ACL extern "C" { #include <sys/param.h> #ifdef HAVE_SYS_MOUNT_H #include <sys/mount.h> #endif #ifdef HAVE_SYS_XATTR_H #include <sys/xattr.h> #endif } #endif #include <tdeapplication.h> #include <kdialog.h> #include <kdirsize.h> #include <kdirwatch.h> #include <kdirnotify_stub.h> #include <kdiskfreesp.h> #include <kdebug.h> #include <kdesktopfile.h> #include <kicondialog.h> #include <kurl.h> #include <kurlrequester.h> #include <tdelocale.h> #include <tdeglobal.h> #include <tdeglobalsettings.h> #include <kstandarddirs.h> #include <tdeio/job.h> #include <tdeio/chmodjob.h> #include <tdeio/renamedlg.h> #include <tdeio/netaccess.h> #include <tdeio/kservicetypefactory.h> #include <tdefiledialog.h> #include <kmimetype.h> #include <kmountpoint.h> #include <kiconloader.h> #include <tdemessagebox.h> #include <kservice.h> #include <kcompletion.h> #include <klineedit.h> #include <kseparator.h> #include <ksqueezedtextlabel.h> #include <klibloader.h> #include <ktrader.h> #include <tdeparts/componentfactory.h> #include <kmetaprops.h> #include <kpreviewprops.h> #include <kprocess.h> #include <krun.h> #include <tdelistview.h> #include <kacl.h> #include "tdefilesharedlg.h" #include "kpropertiesdesktopbase.h" #include "kpropertiesdesktopadvbase.h" #include "kpropertiesmimetypebase.h" #ifdef USE_POSIX_ACL #include "kacleditwidget.h" #endif #include "kpropertiesdialog.h" #ifdef Q_WS_WIN # include <win32_utils.h> #endif static TQString nameFromFileName(TQString nameStr) { if ( nameStr.endsWith(".desktop") ) nameStr.truncate( nameStr.length() - 8 ); if ( nameStr.endsWith(".kdelnk") ) nameStr.truncate( nameStr.length() - 7 ); // Make it human-readable (%2F => '/', ...) nameStr = TDEIO::decodeFileName( nameStr ); return nameStr; } mode_t KFilePermissionsPropsPlugin::fperm[3][4] = { {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID}, {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID}, {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX} }; class KPropertiesDialog::KPropertiesDialogPrivate { public: KPropertiesDialogPrivate() { m_aborted = false; fileSharePage = 0; } ~KPropertiesDialogPrivate() { } bool m_aborted:1; TQWidget* fileSharePage; }; KPropertiesDialog::KPropertiesDialog (KFileItem* item, TQWidget* parent, const char* name, bool modal, bool autoShow) : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(TDEIO::decodeFileName(item->url().fileName())), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, parent, name, modal) { d = new KPropertiesDialogPrivate; assert( item ); m_items.append( new KFileItem(*item) ); // deep copy m_singleUrl = item->url(); assert(!m_singleUrl.isEmpty()); init (modal, autoShow); } KPropertiesDialog::KPropertiesDialog (const TQString& title, TQWidget* parent, const char* name, bool modal) : KDialogBase (KDialogBase::Tabbed, i18n ("Properties for %1").arg(title), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, parent, name, modal) { d = new KPropertiesDialogPrivate; init (modal, false); } KPropertiesDialog::KPropertiesDialog (KFileItemList _items, TQWidget* parent, const char* name, bool modal, bool autoShow) : KDialogBase (KDialogBase::Tabbed, // TODO: replace <never used> with "Properties for 1 item". It's very confusing how it has to be translated otherwise // (empty translation before the "\n" is not allowed by msgfmt...) _items.count()>1 ? i18n( "<never used>","Properties for %n Selected Items",_items.count()) : i18n( "Properties for %1" ).arg(TDEIO::decodeFileName(_items.first()->url().fileName())), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, parent, name, modal) { d = new KPropertiesDialogPrivate; assert( !_items.isEmpty() ); m_singleUrl = _items.first()->url(); assert(!m_singleUrl.isEmpty()); KFileItemListIterator it ( _items ); // Deep copy for ( ; it.current(); ++it ) m_items.append( new KFileItem( **it ) ); init (modal, autoShow); } #ifndef KDE_NO_COMPAT KPropertiesDialog::KPropertiesDialog (const KURL& _url, mode_t /* _mode is now unused */, TQWidget* parent, const char* name, bool modal, bool autoShow) : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(TDEIO::decodeFileName(_url.fileName())), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, parent, name, modal), m_singleUrl( _url ) { d = new KPropertiesDialogPrivate; TDEIO::UDSEntry entry; TDEIO::NetAccess::stat(_url, entry, parent); m_items.append( new KFileItem( entry, _url ) ); init (modal, autoShow); } #endif KPropertiesDialog::KPropertiesDialog (const KURL& _url, TQWidget* parent, const char* name, bool modal, bool autoShow) : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(TDEIO::decodeFileName(_url.fileName())), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, parent, name, modal), m_singleUrl( _url ) { d = new KPropertiesDialogPrivate; TDEIO::UDSEntry entry; TDEIO::NetAccess::stat(_url, entry, parent); m_items.append( new KFileItem( entry, _url ) ); init (modal, autoShow); } KPropertiesDialog::KPropertiesDialog (const KURL& _tempUrl, const KURL& _currentDir, const TQString& _defaultName, TQWidget* parent, const char* name, bool modal, bool autoShow) : KDialogBase (KDialogBase::Tabbed, i18n( "Properties for %1" ).arg(TDEIO::decodeFileName(_tempUrl.fileName())), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, parent, name, modal), m_singleUrl( _tempUrl ), m_defaultName( _defaultName ), m_currentDir( _currentDir ) { d = new KPropertiesDialogPrivate; assert(!m_singleUrl.isEmpty()); // Create the KFileItem for the _template_ file, in order to read from it. m_items.append( new KFileItem( KFileItem::Unknown, KFileItem::Unknown, m_singleUrl ) ); init (modal, autoShow); } bool KPropertiesDialog::showDialog(KFileItem* item, TQWidget* parent, const char* name, bool modal) { #ifdef Q_WS_WIN TQString localPath = item->localPath(); if (!localPath.isEmpty()) return showWin32FilePropertyDialog(localPath); #endif new KPropertiesDialog(item, parent, name, modal); return true; } bool KPropertiesDialog::showDialog(const KURL& _url, TQWidget* parent, const char* name, bool modal) { #ifdef Q_WS_WIN if (_url.isLocalFile()) return showWin32FilePropertyDialog( _url.path() ); #endif new KPropertiesDialog(_url, parent, name, modal); return true; } bool KPropertiesDialog::showDialog(const KFileItemList& _items, TQWidget* parent, const char* name, bool modal) { if (_items.count()==1) return KPropertiesDialog::showDialog(_items.getFirst(), parent, name, modal); new KPropertiesDialog(_items, parent, name, modal); return true; } void KPropertiesDialog::init (bool modal, bool autoShow) { m_pageList.setAutoDelete( true ); m_items.setAutoDelete( true ); insertPages(); if (autoShow) { if (!modal) show(); else exec(); } } void KPropertiesDialog::showFileSharingPage() { if (d->fileSharePage) { showPage( pageIndex( d->fileSharePage)); } } void KPropertiesDialog::setFileSharingPage(TQWidget* page) { d->fileSharePage = page; } void KPropertiesDialog::setFileNameReadOnly( bool ro ) { KPropsDlgPlugin *it; for ( it=m_pageList.first(); it != 0L; it=m_pageList.next() ) { KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it); if ( plugin ) { plugin->setFileNameReadOnly( ro ); break; } } } void KPropertiesDialog::slotStatResult( TDEIO::Job * ) { } KPropertiesDialog::~KPropertiesDialog() { m_pageList.clear(); delete d; } void KPropertiesDialog::insertPlugin (KPropsDlgPlugin* plugin) { connect (plugin, TQT_SIGNAL (changed ()), plugin, TQT_SLOT (setDirty ())); m_pageList.append (plugin); } bool KPropertiesDialog::canDisplay( KFileItemList _items ) { // TODO: cache the result of those calls. Currently we parse .desktop files far too many times return KFilePropsPlugin::supports( _items ) || KFilePermissionsPropsPlugin::supports( _items ) || KDesktopPropsPlugin::supports( _items ) || KBindingPropsPlugin::supports( _items ) || KURLPropsPlugin::supports( _items ) || KDevicePropsPlugin::supports( _items ) || KFileMetaPropsPlugin::supports( _items ) || KPreviewPropsPlugin::supports( _items ); } void KPropertiesDialog::slotOk() { KPropsDlgPlugin *page; d->m_aborted = false; KFilePropsPlugin * filePropsPlugin = 0L; if ( m_pageList.first()->isA("KFilePropsPlugin") ) filePropsPlugin = static_cast<KFilePropsPlugin *>(m_pageList.first()); // If any page is dirty, then set the main one (KFilePropsPlugin) as // dirty too. This is what makes it possible to save changes to a global // desktop file into a local one. In other cases, it doesn't hurt. for ( page = m_pageList.first(); page != 0L; page = m_pageList.next() ) if ( page->isDirty() && filePropsPlugin ) { filePropsPlugin->setDirty(); break; } // Apply the changes in the _normal_ order of the tabs now // This is because in case of renaming a file, KFilePropsPlugin will call // KPropertiesDialog::rename, so other tab will be ok with whatever order // BUT for file copied from templates, we need to do the renaming first ! for ( page = m_pageList.first(); page != 0L && !d->m_aborted; page = m_pageList.next() ) if ( page->isDirty() ) { kdDebug( 250 ) << "applying changes for " << page->className() << endl; page->applyChanges(); // applyChanges may change d->m_aborted. } else kdDebug( 250 ) << "skipping page " << page->className() << endl; if ( !d->m_aborted && filePropsPlugin ) filePropsPlugin->postApplyChanges(); if ( !d->m_aborted ) { emit applied(); emit propertiesClosed(); deleteLater(); accept(); } // else, keep dialog open for user to fix the problem. } void KPropertiesDialog::slotCancel() { emit canceled(); emit propertiesClosed(); deleteLater(); done( Rejected ); } void KPropertiesDialog::insertPages() { if (m_items.isEmpty()) return; if ( KFilePropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KFilePropsPlugin( this ); insertPlugin (p); } if ( KFilePermissionsPropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KFilePermissionsPropsPlugin( this ); insertPlugin (p); } if ( KDesktopPropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KDesktopPropsPlugin( this ); insertPlugin (p); } if ( KBindingPropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KBindingPropsPlugin( this ); insertPlugin (p); } if ( KURLPropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KURLPropsPlugin( this ); insertPlugin (p); } if ( KDevicePropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KDevicePropsPlugin( this ); insertPlugin (p); } if ( KFileMetaPropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KFileMetaPropsPlugin( this ); insertPlugin (p); } if ( KPreviewPropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KPreviewPropsPlugin( this ); insertPlugin (p); } if ( kapp->authorizeTDEAction("sharefile") && KFileSharePropsPlugin::supports( m_items ) ) { KPropsDlgPlugin *p = new KFileSharePropsPlugin( this ); insertPlugin (p); } //plugins if ( m_items.count() != 1 ) return; KFileItem *item = m_items.first(); TQString mimetype = item->mimetype(); if ( mimetype.isEmpty() ) return; TQString query = TQString::fromLatin1( "('KPropsDlg/Plugin' in ServiceTypes) and " "((not exist [X-TDE-Protocol]) or " " ([X-TDE-Protocol] == '%1' ) )" ).arg(item->url().protocol()); kdDebug( 250 ) << "trader query: " << query << endl; TDETrader::OfferList offers = TDETrader::self()->query( mimetype, query ); TDETrader::OfferList::ConstIterator it = offers.begin(); TDETrader::OfferList::ConstIterator end = offers.end(); for (; it != end; ++it ) { KPropsDlgPlugin *plugin = KParts::ComponentFactory ::createInstanceFromLibrary<KPropsDlgPlugin>( (*it)->library().local8Bit().data(), TQT_TQOBJECT(this), (*it)->name().latin1() ); if ( !plugin ) continue; insertPlugin( plugin ); } } void KPropertiesDialog::updateUrl( const KURL& _newUrl ) { Q_ASSERT( m_items.count() == 1 ); kdDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url() << endl; KURL newUrl = _newUrl; emit saveAs(m_singleUrl, newUrl); kdDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url() << endl; m_singleUrl = newUrl; m_items.first()->setURL( newUrl ); assert(!m_singleUrl.isEmpty()); // If we have an Desktop page, set it dirty, so that a full file is saved locally // Same for a URL page (because of the Name= hack) for ( TQPtrListIterator<KPropsDlgPlugin> it(m_pageList); it.current(); ++it ) if ( it.current()->isA("KExecPropsPlugin") || // KDE4 remove me it.current()->isA("KURLPropsPlugin") || it.current()->isA("KDesktopPropsPlugin")) { //kdDebug(250) << "Setting page dirty" << endl; it.current()->setDirty(); break; } } void KPropertiesDialog::rename( const TQString& _name ) { Q_ASSERT( m_items.count() == 1 ); kdDebug(250) << "KPropertiesDialog::rename " << _name << endl; KURL newUrl; // if we're creating from a template : use currentdir if ( !m_currentDir.isEmpty() ) { newUrl = m_currentDir; newUrl.addPath( _name ); } else { TQString tmpurl = m_singleUrl.url(); if ( tmpurl.at(tmpurl.length() - 1) == '/') // It's a directory, so strip the trailing slash first tmpurl.truncate( tmpurl.length() - 1); newUrl = tmpurl; newUrl.setFileName( _name ); } updateUrl( newUrl ); } void KPropertiesDialog::abortApplying() { d->m_aborted = true; } class KPropsDlgPlugin::KPropsDlgPluginPrivate { public: KPropsDlgPluginPrivate() { } ~KPropsDlgPluginPrivate() { } bool m_bDirty; }; KPropsDlgPlugin::KPropsDlgPlugin( KPropertiesDialog *_props ) : TQObject( _props, 0L ) { d = new KPropsDlgPluginPrivate; properties = _props; fontHeight = 2*properties->fontMetrics().height(); d->m_bDirty = false; } KPropsDlgPlugin::~KPropsDlgPlugin() { delete d; } bool KPropsDlgPlugin::isDesktopFile( KFileItem * _item ) { // only local files bool isLocal; KURL url = _item->mostLocalURL( isLocal ); if ( !isLocal ) return false; // only regular files if ( !S_ISREG( _item->mode() ) ) return false; TQString t( url.path() ); // only if readable FILE *f = fopen( TQFile::encodeName(t), "r" ); if ( f == 0L ) return false; fclose(f); // return true if desktop file return ( (_item->mimetype() == "application/x-desktop") || (_item->mimetype() == "media/builtin-mydocuments") || (_item->mimetype() == "media/builtin-mycomputer") || (_item->mimetype() == "media/builtin-mynetworkplaces") || (_item->mimetype() == "media/builtin-printers") || (_item->mimetype() == "media/builtin-trash") || (_item->mimetype() == "media/builtin-webbrowser") ); } void KPropsDlgPlugin::setDirty( bool b ) { d->m_bDirty = b; } void KPropsDlgPlugin::setDirty() { d->m_bDirty = true; } bool KPropsDlgPlugin::isDirty() const { return d->m_bDirty; } void KPropsDlgPlugin::applyChanges() { kdWarning(250) << "applyChanges() not implemented in page !" << endl; } /////////////////////////////////////////////////////////////////////////////// class KFilePropsPlugin::KFilePropsPluginPrivate { public: KFilePropsPluginPrivate() { dirSizeJob = 0L; dirSizeUpdateTimer = 0L; m_lined = 0; m_freeSpaceLabel = 0; } ~KFilePropsPluginPrivate() { if ( dirSizeJob ) dirSizeJob->kill(); } KDirSize * dirSizeJob; TQTimer *dirSizeUpdateTimer; TQFrame *m_frame; bool bMultiple; bool bIconChanged; bool bKDesktopMode; bool bDesktopFile; TQLabel *m_freeSpaceLabel; TQString mimeType; TQString oldFileName; KLineEdit* m_lined; }; KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) { d = new KFilePropsPluginPrivate; d->bMultiple = (properties->items().count() > 1); d->bIconChanged = false; d->bKDesktopMode = (TQCString(tqApp->name()) == "kdesktop"); // nasty heh? d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items()); kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple << endl; // We set this data from the first item, and we'll // check that the other items match against it, resetting when not. bool isLocal; KFileItem * item = properties->item(); KURL url = item->mostLocalURL( isLocal ); bool isReallyLocal = item->url().isLocalFile(); bool bDesktopFile = isDesktopFile(item); kdDebug() << "url=" << url << " bDesktopFile=" << bDesktopFile << " isLocal=" << isLocal << " isReallyLocal=" << isReallyLocal << endl; mode_t mode = item->mode(); bool hasDirs = item->isDir() && !item->isLink(); bool hasRoot = url.path() == TQString::fromLatin1("/"); TQString iconStr = KMimeType::iconForURL(url, mode); TQString directory = properties->kurl().directory(); TQString protocol = properties->kurl().protocol(); TQString mimeComment = item->mimeComment(); d->mimeType = item->mimetype(); bool hasTotalSize; TDEIO::filesize_t totalSize = item->size(hasTotalSize); TQString magicMimeComment; if ( isLocal ) { KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() ); if ( magicMimeType->name() != KMimeType::defaultMimeType() ) { magicMimeComment = magicMimeType->comment(); } } // Those things only apply to 'single file' mode TQString filename = TQString::null; bool isTrash = false; bool isDevice = false; bool isMediaNode = false; m_bFromTemplate = false; // And those only to 'multiple' mode uint iDirCount = hasDirs ? 1 : 0; uint iFileCount = 1-iDirCount; d->m_frame = properties->addPage (i18n("&General")); TQVBoxLayout *vbl = new TQVBoxLayout( d->m_frame, 0, KDialog::spacingHint(), "vbl"); TQGridLayout *grid = new TQGridLayout(0, 3); // unknown rows grid->setColStretch(0, 0); grid->setColStretch(1, 0); grid->setColStretch(2, 1); grid->addColSpacing(1, KDialog::spacingHint()); vbl->addLayout(TQT_TQLAYOUT(grid)); int curRow = 0; if ( !d->bMultiple ) { TQString path; if ( !m_bFromTemplate ) { isTrash = ( properties->kurl().protocol().find( "trash", 0, false)==0 ); if ( properties->kurl().protocol().find("device", 0, false)==0) { isDevice = true; } if (d->mimeType.startsWith("media/")) { isMediaNode = true; } // Extract the full name, but without file: for local files if ( isReallyLocal ) { path = properties->kurl().path(); } else { path = properties->kurl().prettyURL(); } } else { path = properties->currentDir().path(1) + properties->defaultName(); directory = properties->currentDir().prettyURL(); } if (KExecPropsPlugin::supports(properties->items()) || // KDE4 remove me d->bDesktopFile || KBindingPropsPlugin::supports(properties->items())) { determineRelativePath( path ); } // Extract the file name only filename = properties->defaultName(); if ( filename.isEmpty() ) { // no template filename = item->name(); // this gives support for UDS_NAME, e.g. for tdeio_trash or tdeio_system } else { m_bFromTemplate = true; setDirty(); // to enforce that the copy happens } d->oldFileName = filename; // Make it human-readable filename = nameFromFileName( filename ); if ( d->bKDesktopMode && d->bDesktopFile ) { KDesktopFile config( url.path(), true /* readonly */ ); if ( config.hasKey( "Name" ) ) { filename = config.readName(); } } oldName = filename; } else { // Multiple items: see what they have in common KFileItemList items = properties->items(); KFileItemListIterator it( items ); for ( ++it /*no need to check the first one again*/ ; it.current(); ++it ) { KURL url = (*it)->url(); kdDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyURL() << endl; // The list of things we check here should match the variables defined // at the beginning of this method. if ( url.isLocalFile() != isLocal ) isLocal = false; // not all local if ( bDesktopFile && isDesktopFile(*it) != bDesktopFile ) bDesktopFile = false; // not all desktop files if ( (*it)->mode() != mode ) mode = (mode_t)0; if ( KMimeType::iconForURL(url, mode) != iconStr ) iconStr = "application-vnd.tde.tdemultiple"; if ( url.directory() != directory ) directory = TQString::null; if ( url.protocol() != protocol ) protocol = TQString::null; if ( !mimeComment.isNull() && (*it)->mimeComment() != mimeComment ) mimeComment = TQString::null; if ( isLocal && !magicMimeComment.isNull() ) { KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() ); if ( magicMimeType->comment() != magicMimeComment ) magicMimeComment = TQString::null; } if ( url.path() == TQString::fromLatin1("/") ) hasRoot = true; if ( (*it)->isDir() && !(*it)->isLink() ) { iDirCount++; hasDirs = true; } else { iFileCount++; bool hasSize; totalSize += (*it)->size(hasSize); hasTotalSize = hasTotalSize || hasSize; } } } if (!isReallyLocal && !protocol.isEmpty()) { directory += ' '; directory += '('; directory += protocol; directory += ')'; } if ( !isDevice && !isMediaNode && !isTrash && (bDesktopFile || S_ISDIR(mode)) && !d->bMultiple /*not implemented for multiple*/ ) { TDEIconButton *iconButton = new TDEIconButton( d->m_frame ); int bsize = 66 + 2 * iconButton->style().pixelMetric(TQStyle::PM_ButtonMargin); iconButton->setFixedSize(bsize, bsize); iconButton->setIconSize(48); iconButton->setStrictIconSize(false); // This works for everything except Device icons on unmounted devices // So we have to really open .desktop files TQString iconStr = KMimeType::findByURL( url, mode )->icon( url, isLocal ); if ( bDesktopFile && isLocal ) { KDesktopFile config( url.path(), true ); config.setDesktopGroup(); iconStr = config.readEntry( "Icon" ); if ( config.hasDeviceType() ) { iconButton->setIconType( TDEIcon::Desktop, TDEIcon::Device ); } else { iconButton->setIconType( TDEIcon::Desktop, TDEIcon::Application ); } } else { iconButton->setIconType( TDEIcon::Desktop, TDEIcon::Place ); } iconButton->setIcon(iconStr); iconArea = iconButton; connect( iconButton, TQT_SIGNAL( iconChanged(TQString) ), this, TQT_SLOT( slotIconChanged() ) ); } else { TQLabel *iconLabel = new TQLabel( d->m_frame ); int bsize = 66 + 2 * iconLabel->style().pixelMetric(TQStyle::PM_ButtonMargin); iconLabel->setFixedSize(bsize, bsize); if (isMediaNode) { // Display the correct device icon iconLabel->setPixmap( TDEGlobal::iconLoader()->loadIcon( item->iconName(), TDEIcon::Desktop, 48) ); } else { // Display the generic folder icon iconLabel->setPixmap( TDEGlobal::iconLoader()->loadIcon( iconStr, TDEIcon::Desktop, 48) ); } iconArea = iconLabel; } grid->addWidget(iconArea, curRow, 0, TQt::AlignLeft); if (d->bMultiple || isTrash || isDevice || isMediaNode || hasRoot) { TQLabel *lab = new TQLabel(d->m_frame ); if ( d->bMultiple ) lab->setText( TDEIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) ); else lab->setText( filename ); nameArea = lab; } else { d->m_lined = new KLineEdit( d->m_frame ); d->m_lined->setText(filename); nameArea = d->m_lined; d->m_lined->setFocus(); // Enhanced rename: Don't highlight the file extension. TQString pattern; KServiceTypeFactory::self()->findFromPattern( filename, &pattern ); if (!pattern.isEmpty() && pattern.at(0)=='*' && pattern.find('*',1)==-1) d->m_lined->setSelection(0, filename.length()-pattern.stripWhiteSpace().length()+1); else { int lastDot = filename.findRev('.'); if (lastDot > 0) d->m_lined->setSelection(0, lastDot); } connect( d->m_lined, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SLOT( nameFileChanged(const TQString & ) ) ); } grid->addWidget(nameArea, curRow++, 2); KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame); grid->addMultiCellWidget(sep, curRow, curRow, 0, 2); ++curRow; TQLabel *l; if ( !mimeComment.isEmpty() && !isDevice && !isMediaNode && !isTrash) { l = new TQLabel(i18n("Type:"), d->m_frame ); grid->addWidget(l, curRow, 0); TQHBox *box = new TQHBox(d->m_frame); box->setSpacing(20); l = new TQLabel(mimeComment, box ); #ifdef Q_WS_X11 //TODO: wrap for win32 or mac? TQPushButton *button = new TQPushButton(box); TQIconSet iconSet = SmallIconSet(TQString::fromLatin1("configure")); TQPixmap pixMap = iconSet.pixmap( TQIconSet::Small, TQIconSet::Normal ); button->setIconSet( iconSet ); button->setFixedSize( pixMap.width()+8, pixMap.height()+8 ); if ( d->mimeType == KMimeType::defaultMimeType() ) TQToolTip::add(button, i18n("Create new file type")); else TQToolTip::add(button, i18n("Edit file type")); connect( button, TQT_SIGNAL( clicked() ), TQT_SLOT( slotEditFileType() )); if (!kapp->authorizeTDEAction("editfiletype")) button->hide(); #endif grid->addWidget(box, curRow++, 2); } if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment ) { l = new TQLabel(i18n("Contents:"), d->m_frame ); grid->addWidget(l, curRow, 0); l = new TQLabel(magicMimeComment, d->m_frame ); grid->addWidget(l, curRow++, 2); } if ( !directory.isEmpty() ) { l = new TQLabel( i18n("Location:"), d->m_frame ); grid->addWidget(l, curRow, 0); l = new KSqueezedTextLabel( d->m_frame ); l->setText( directory ); grid->addWidget(l, curRow++, 2); } if( hasDirs || hasTotalSize ) { l = new TQLabel(i18n("Size:"), d->m_frame ); grid->addWidget(l, curRow, 0); m_sizeLabel = new TQLabel( d->m_frame ); grid->addWidget( m_sizeLabel, curRow++, 2 ); } else { m_sizeLabel = 0; } if ( !hasDirs ) // Only files [and symlinks] { if(hasTotalSize) { m_sizeLabel->setText(TDEIO::convertSizeWithBytes(totalSize)); } m_sizeDetermineButton = 0L; m_sizeStopButton = 0L; } else // Directory { TQHBoxLayout * sizelay = new TQHBoxLayout(KDialog::spacingHint()); grid->addLayout( sizelay, curRow++, 2 ); // buttons m_sizeDetermineButton = new TQPushButton( i18n("Calculate"), d->m_frame ); m_sizeStopButton = new TQPushButton( i18n("Stop"), d->m_frame ); connect( m_sizeDetermineButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotSizeDetermine() ) ); connect( m_sizeStopButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotSizeStop() ) ); sizelay->addWidget(m_sizeDetermineButton, 0); sizelay->addWidget(m_sizeStopButton, 0); sizelay->addStretch(10); // so that the buttons don't grow horizontally // auto-launch for local dirs only, but not for '/' or medias if ( isReallyLocal && !hasRoot ) { m_sizeDetermineButton->setText( i18n("Refresh") ); slotSizeDetermine(); } else m_sizeStopButton->setEnabled( false ); } if (!d->bMultiple && item->isLink()) { l = new TQLabel(i18n("Points to:"), d->m_frame ); grid->addWidget(l, curRow, 0); l = new KSqueezedTextLabel(item->linkDest(), d->m_frame ); grid->addWidget(l, curRow++, 2); } if (!d->bMultiple) // Dates for multiple don't make much sense... { TQDateTime dt; bool hasTime; time_t tim = item->time(TDEIO::UDS_CREATION_TIME, hasTime); if ( hasTime ) { l = new TQLabel(i18n("Created:"), d->m_frame ); grid->addWidget(l, curRow, 0); dt.setTime_t( tim ); l = new TQLabel(TDEGlobal::locale()->formatDateTime(dt), d->m_frame ); grid->addWidget(l, curRow++, 2); } tim = item->time(TDEIO::UDS_MODIFICATION_TIME, hasTime); if ( hasTime ) { l = new TQLabel(i18n("Modified:"), d->m_frame ); grid->addWidget(l, curRow, 0); dt.setTime_t( tim ); l = new TQLabel(TDEGlobal::locale()->formatDateTime(dt), d->m_frame ); grid->addWidget(l, curRow++, 2); } tim = item->time(TDEIO::UDS_ACCESS_TIME, hasTime); if ( hasTime ) { l = new TQLabel(i18n("Accessed:"), d->m_frame ); grid->addWidget(l, curRow, 0); dt.setTime_t( tim ); l = new TQLabel(TDEGlobal::locale()->formatDateTime(dt), d->m_frame ); grid->addWidget(l, curRow++, 2); } } if ( isLocal && hasDirs ) // only for directories { sep = new KSeparator( KSeparator::HLine, d->m_frame); grid->addMultiCellWidget(sep, curRow, curRow, 0, 2); ++curRow; TQString mountPoint = TDEIO::findPathMountPoint( url.path() ); if (mountPoint != "/") { l = new TQLabel(i18n("Mounted on:"), d->m_frame ); grid->addWidget(l, curRow, 0); l = new KSqueezedTextLabel( mountPoint, d->m_frame ); grid->addWidget( l, curRow++, 2 ); } l = new TQLabel(i18n("Free disk space:"), d->m_frame ); grid->addWidget(l, curRow, 0); d->m_freeSpaceLabel = new TQLabel( d->m_frame ); grid->addWidget( d->m_freeSpaceLabel, curRow++, 2 ); KDiskFreeSp * job = new KDiskFreeSp; connect( job, TQT_SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&, const unsigned long&, const TQString& ) ), this, TQT_SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&, const unsigned long&, const TQString& ) ) ); job->readDF( mountPoint ); } vbl->addStretch(1); } // TQString KFilePropsPlugin::tabName () const // { // return i18n ("&General"); // } void KFilePropsPlugin::setFileNameReadOnly( bool ro ) { if ( d->m_lined ) { d->m_lined->setReadOnly( ro ); if (ro) { // Don't put the initial focus on the line edit when it is ro TQPushButton *button = properties->actionButton(KDialogBase::Ok); if (button) button->setFocus(); } } } void KFilePropsPlugin::slotEditFileType() { #ifdef Q_WS_X11 TQString mime; if ( d->mimeType == KMimeType::defaultMimeType() ) { int pos = d->oldFileName.findRev( '.' ); if ( pos != -1 ) mime = "*" + d->oldFileName.mid(pos); else mime = "*"; } else mime = d->mimeType; //TODO: wrap for win32 or mac? TQString keditfiletype = TQString::fromLatin1("keditfiletype"); KRun::runCommand( keditfiletype + " --parent " + TQString::number( (ulong)properties->topLevelWidget()->winId()) + " " + TDEProcess::quote(mime), keditfiletype, keditfiletype /*unused*/); #endif } void KFilePropsPlugin::slotIconChanged() { d->bIconChanged = true; emit changed(); } void KFilePropsPlugin::nameFileChanged(const TQString &text ) { properties->enableButtonOK(!text.isEmpty()); emit changed(); } void KFilePropsPlugin::determineRelativePath( const TQString & path ) { // now let's make it relative TQStringList dirs; if (KBindingPropsPlugin::supports(properties->items())) { m_sRelativePath =TDEGlobal::dirs()->relativeLocation("mime", path); if (m_sRelativePath.startsWith("/")) m_sRelativePath = TQString::null; } else { m_sRelativePath =TDEGlobal::dirs()->relativeLocation("apps", path); if (m_sRelativePath.startsWith("/")) { m_sRelativePath =TDEGlobal::dirs()->relativeLocation("xdgdata-apps", path); if (m_sRelativePath.startsWith("/")) m_sRelativePath = TQString::null; else m_sRelativePath = path; } } if ( m_sRelativePath.isEmpty() ) { if (KBindingPropsPlugin::supports(properties->items())) kdWarning(250) << "Warning : editing a mimetype file out of the mimetype dirs!" << endl; } } void KFilePropsPlugin::slotFoundMountPoint( const TQString&, unsigned long kBSize, unsigned long /*kBUsed*/, unsigned long kBAvail ) { d->m_freeSpaceLabel->setText( // xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part. i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)") .arg(TDEIO::convertSizeFromKB(kBAvail)) .arg(TDEIO::convertSizeFromKB(kBSize)) .arg( 100 - (int)(100.0 * kBAvail / kBSize) )); } // attention: copy&paste below, due to compiler bug // it doesn't like those unsigned long parameters -- unsigned long& are ok :-/ void KFilePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize, const unsigned long& /*kBUsed*/, const unsigned long& kBAvail, const TQString& ) { d->m_freeSpaceLabel->setText( // xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part. i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)") .arg(TDEIO::convertSizeFromKB(kBAvail)) .arg(TDEIO::convertSizeFromKB(kBSize)) .arg( 100 - (int)(100.0 * kBAvail / kBSize) )); } void KFilePropsPlugin::slotDirSizeUpdate() { TDEIO::filesize_t totalSize = d->dirSizeJob->totalSize(); TDEIO::filesize_t totalFiles = d->dirSizeJob->totalFiles(); TDEIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs(); m_sizeLabel->setText( i18n("Calculating... %1 (%2)\n%3, %4") .arg(TDEIO::convertSize(totalSize)) .arg(TDEGlobal::locale()->formatNumber(totalSize, 0)) .arg(i18n("1 file","%n files",totalFiles)) .arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs))); } void KFilePropsPlugin::slotDirSizeFinished( TDEIO::Job * job ) { if (job->error()) m_sizeLabel->setText( job->errorString() ); else { TDEIO::filesize_t totalSize = static_cast<KDirSize*>(job)->totalSize(); TDEIO::filesize_t totalFiles = static_cast<KDirSize*>(job)->totalFiles(); TDEIO::filesize_t totalSubdirs = static_cast<KDirSize*>(job)->totalSubdirs(); m_sizeLabel->setText( TQString::fromLatin1("%1 (%2)\n%3, %4") .arg(TDEIO::convertSize(totalSize)) .arg(TDEGlobal::locale()->formatNumber(totalSize, 0)) .arg(i18n("1 file","%n files",totalFiles)) .arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs))); } m_sizeStopButton->setEnabled(false); // just in case you change something and try again :) m_sizeDetermineButton->setText( i18n("Refresh") ); m_sizeDetermineButton->setEnabled(true); d->dirSizeJob = 0L; delete d->dirSizeUpdateTimer; d->dirSizeUpdateTimer = 0L; } void KFilePropsPlugin::slotSizeDetermine() { m_sizeLabel->setText( i18n("Calculating...") ); kdDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" << properties->item() << endl; kdDebug(250) << " URL=" << properties->item()->url().url() << endl; d->dirSizeJob = KDirSize::dirSizeJob( properties->items() ); d->dirSizeUpdateTimer = new TQTimer(this); connect( d->dirSizeUpdateTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotDirSizeUpdate() ) ); d->dirSizeUpdateTimer->start(500); connect( d->dirSizeJob, TQT_SIGNAL( result( TDEIO::Job * ) ), TQT_SLOT( slotDirSizeFinished( TDEIO::Job * ) ) ); m_sizeStopButton->setEnabled(true); m_sizeDetermineButton->setEnabled(false); // also update the "Free disk space" display if ( d->m_freeSpaceLabel ) { bool isLocal; KFileItem * item = properties->item(); KURL url = item->mostLocalURL( isLocal ); TQString mountPoint = TDEIO::findPathMountPoint( url.path() ); KDiskFreeSp * job = new KDiskFreeSp; connect( job, TQT_SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&, const unsigned long&, const TQString& ) ), this, TQT_SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&, const unsigned long&, const TQString& ) ) ); job->readDF( mountPoint ); } } void KFilePropsPlugin::slotSizeStop() { if ( d->dirSizeJob ) { m_sizeLabel->setText( i18n("Stopped") ); d->dirSizeJob->kill(); d->dirSizeJob = 0; } if ( d->dirSizeUpdateTimer ) d->dirSizeUpdateTimer->stop(); m_sizeStopButton->setEnabled(false); m_sizeDetermineButton->setEnabled(true); } KFilePropsPlugin::~KFilePropsPlugin() { delete d; } bool KFilePropsPlugin::supports( KFileItemList /*_items*/ ) { return true; } // Don't do this at home void tqt_enter_modal( TQWidget *widget ); void tqt_leave_modal( TQWidget *widget ); void KFilePropsPlugin::applyChanges() { if ( d->dirSizeJob ) { slotSizeStop(); } kdDebug(250) << "KFilePropsPlugin::applyChanges" << endl; if (nameArea->inherits("TQLineEdit")) { TQString n = ((TQLineEdit *) nameArea)->text(); // Remove trailing spaces (#4345) while ( n[n.length()-1].isSpace() ) n.truncate( n.length() - 1 ); if ( n.isEmpty() ) { KMessageBox::sorry( properties, i18n("The new file name is empty.")); properties->abortApplying(); return; } // Do we need to rename the file ? kdDebug(250) << "oldname = " << oldName << endl; kdDebug(250) << "newname = " << n << endl; if ( oldName != n || m_bFromTemplate ) { // true for any from-template file TDEIO::Job * job = 0L; KURL oldurl = properties->kurl(); TQString newFileName = TDEIO::encodeFileName(n); if (d->bDesktopFile && !newFileName.endsWith(".desktop") && !newFileName.endsWith(".kdelnk")) newFileName += ".desktop"; // Tell properties. Warning, this changes the result of properties->kurl() ! properties->rename( newFileName ); // Update also relative path (for apps and mimetypes) if ( !m_sRelativePath.isEmpty() ) { determineRelativePath( properties->kurl().path() ); } kdDebug(250) << "New URL = " << properties->kurl().url() << endl; kdDebug(250) << "old = " << oldurl.url() << endl; // Don't remove the template !! if ( !m_bFromTemplate ) { // (normal renaming) job = TDEIO::move( oldurl, properties->kurl() ); } else { // Copying a template job = TDEIO::copy( oldurl, properties->kurl() ); } connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), TQT_SLOT( slotCopyFinished( TDEIO::Job * ) ) ); connect( job, TQT_SIGNAL( renamed( TDEIO::Job *, const KURL &, const KURL & ) ), TQT_SLOT( slotFileRenamed( TDEIO::Job *, const KURL &, const KURL & ) ) ); // wait for job TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); tqt_enter_modal(&dummy); tqApp->enter_loop(); tqt_leave_modal(&dummy); return; } properties->updateUrl(properties->kurl()); // Update also relative path (for apps and mimetypes) if ( !m_sRelativePath.isEmpty() ) { determineRelativePath( properties->kurl().path() ); } } // No job, keep going slotCopyFinished( 0L ); } void KFilePropsPlugin::slotCopyFinished( TDEIO::Job * job ) { kdDebug(250) << "KFilePropsPlugin::slotCopyFinished" << endl; if (job) { // allow apply() to return tqApp->exit_loop(); if ( job->error() ) { job->showErrorDialog( d->m_frame ); // Didn't work. Revert the URL to the old one properties->updateUrl( static_cast<TDEIO::CopyJob*>(job)->srcURLs().first() ); properties->abortApplying(); // Don't apply the changes to the wrong file ! return; } } assert( properties->item() ); assert( !properties->item()->url().isEmpty() ); // Save the file where we can -> usually in ~/.trinity/... if (KBindingPropsPlugin::supports(properties->items()) && !m_sRelativePath.isEmpty()) { KURL newURL; newURL.setPath( locateLocal("mime", m_sRelativePath) ); properties->updateUrl( newURL ); } else if (d->bDesktopFile && !m_sRelativePath.isEmpty()) { kdDebug(250) << "KFilePropsPlugin::slotCopyFinished " << m_sRelativePath << endl; KURL newURL; newURL.setPath( KDesktopFile::locateLocal(m_sRelativePath) ); kdDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path() << endl; properties->updateUrl( newURL ); } if ( d->bKDesktopMode && d->bDesktopFile ) { // Renamed? Update Name field if ( d->oldFileName != properties->kurl().fileName() || m_bFromTemplate ) { KDesktopFile config( properties->kurl().path() ); TQString nameStr = nameFromFileName(properties->kurl().fileName()); config.writeEntry( "Name", nameStr ); config.writeEntry( "Name", nameStr, true, false, true ); } } } void KFilePropsPlugin::applyIconChanges() { TDEIconButton *iconButton = ::tqt_cast<TDEIconButton *>( iconArea ); if ( !iconButton || !d->bIconChanged ) return; // handle icon changes - only local files (or pseudo-local) for now // TODO: Use KTempFile and TDEIO::file_copy with overwrite = true KURL url = properties->kurl(); url = TDEIO::NetAccess::mostLocalURL( url, properties ); if (url.isLocalFile()) { TQString path; if (S_ISDIR(properties->item()->mode())) { path = url.path(1) + TQString::fromLatin1(".directory"); // don't call updateUrl because the other tabs (i.e. permissions) // apply to the directory, not the .directory file. } else path = url.path(); // Get the default image TQString str = KMimeType::findByURL( url, properties->item()->mode(), true )->KServiceType::icon(); // Is it another one than the default ? TQString sIcon; if ( str != iconButton->icon() ) sIcon = iconButton->icon(); // (otherwise write empty value) kdDebug(250) << "**" << path << "**" << endl; TQFile f( path ); // If default icon and no .directory file -> don't create one if ( !sIcon.isEmpty() || f.exists() ) { if ( !f.open( IO_ReadWrite ) ) { KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not " "have sufficient access to write to <b>%1</b>.</qt>").arg(path)); return; } f.close(); KDesktopFile cfg(path); kdDebug(250) << "sIcon = " << (sIcon) << endl; kdDebug(250) << "str = " << (str) << endl; cfg.writeEntry( "Icon", sIcon ); cfg.sync(); } } } void KFilePropsPlugin::slotFileRenamed( TDEIO::Job *, const KURL &, const KURL & newUrl ) { // This is called in case of an existing local file during the copy/move operation, // if the user chooses Rename. properties->updateUrl( newUrl ); } void KFilePropsPlugin::postApplyChanges() { // Save the icon only after applying the permissions changes (#46192) applyIconChanges(); KURL::List lst; KFileItemList items = properties->items(); for ( KFileItemListIterator it( items ); it.current(); ++it ) lst.append((*it)->url()); KDirNotify_stub allDirNotify("*", "KDirNotify*"); allDirNotify.FilesChanged( lst ); } class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate { public: KFilePermissionsPropsPluginPrivate() { } ~KFilePermissionsPropsPluginPrivate() { } TQFrame *m_frame; TQCheckBox *cbRecursive; TQLabel *explanationLabel; TQComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo; TQCheckBox *extraCheckbox; mode_t partialPermissions; KFilePermissionsPropsPlugin::PermissionsMode pmode; bool canChangePermissions; bool isIrregular; bool hasExtendedACL; KACL extendedACL; KACL defaultACL; bool fileSystemSupportsACLs; }; #define UniOwner (S_IRUSR|S_IWUSR|S_IXUSR) #define UniGroup (S_IRGRP|S_IWGRP|S_IXGRP) #define UniOthers (S_IROTH|S_IWOTH|S_IXOTH) #define UniRead (S_IRUSR|S_IRGRP|S_IROTH) #define UniWrite (S_IWUSR|S_IWGRP|S_IWOTH) #define UniExec (S_IXUSR|S_IXGRP|S_IXOTH) #define UniSpecial (S_ISUID|S_ISGID|S_ISVTX) // synced with PermissionsTarget const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers}; const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 }; // synced with PermissionsMode and standardPermissions const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = { { I18N_NOOP("Forbidden"), I18N_NOOP("Can Read"), I18N_NOOP("Can Read & Write"), 0 }, { I18N_NOOP("Forbidden"), I18N_NOOP("Can View Content"), I18N_NOOP("Can View & Modify Content"), 0 }, { 0, 0, 0, 0}, // no texts for links { I18N_NOOP("Forbidden"), I18N_NOOP("Can View Content & Read"), I18N_NOOP("Can View/Read & Modify/Write"), 0 } }; KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) { d = new KFilePermissionsPropsPluginPrivate; d->cbRecursive = 0L; grpCombo = 0L; grpEdit = 0; usrEdit = 0L; TQString path = properties->kurl().path(-1); TQString fname = properties->kurl().fileName(); bool isLocal = properties->kurl().isLocalFile(); bool isTrash = ( properties->kurl().protocol().find("trash", 0, false)==0 ); bool IamRoot = (geteuid() == 0); KFileItem * item = properties->item(); bool isLink = item->isLink(); bool isDir = item->isDir(); // all dirs bool hasDir = item->isDir(); // at least one dir permissions = item->permissions(); // common permissions to all files d->partialPermissions = permissions; // permissions that only some files have (at first we take everything) d->isIrregular = isIrregular(permissions, isDir, isLink); strOwner = item->user(); strGroup = item->group(); d->hasExtendedACL = item->ACL().isExtended() || item->defaultACL().isValid(); d->extendedACL = item->ACL(); d->defaultACL = item->defaultACL(); d->fileSystemSupportsACLs = false; if ( properties->items().count() > 1 ) { // Multiple items: see what they have in common KFileItemList items = properties->items(); KFileItemListIterator it( items ); for ( ++it /*no need to check the first one again*/ ; it.current(); ++it ) { if (!d->isIrregular) d->isIrregular |= isIrregular((*it)->permissions(), (*it)->isDir() == isDir, (*it)->isLink() == isLink); d->hasExtendedACL = d->hasExtendedACL || (*it)->hasExtendedACL(); if ( (*it)->isLink() != isLink ) isLink = false; if ( (*it)->isDir() != isDir ) isDir = false; hasDir |= (*it)->isDir(); if ( (*it)->permissions() != permissions ) { permissions &= (*it)->permissions(); d->partialPermissions |= (*it)->permissions(); } if ( (*it)->user() != strOwner ) strOwner = TQString::null; if ( (*it)->group() != strGroup ) strGroup = TQString::null; } } if (isLink) d->pmode = PermissionsOnlyLinks; else if (isDir) d->pmode = PermissionsOnlyDirs; else if (hasDir) d->pmode = PermissionsMixed; else d->pmode = PermissionsOnlyFiles; // keep only what's not in the common permissions d->partialPermissions = d->partialPermissions & ~permissions; bool isMyFile = false; if (isLocal && !strOwner.isEmpty()) { // local files, and all owned by the same person struct passwd *myself = getpwuid( geteuid() ); if ( myself != 0L ) { isMyFile = (strOwner == TQString::fromLocal8Bit(myself->pw_name)); } else kdWarning() << "I don't exist ?! geteuid=" << geteuid() << endl; } else { //We don't know, for remote files, if they are ours or not. //So we let the user change permissions, and //TDEIO::chmod will tell, if he had no right to do it. isMyFile = true; } d->canChangePermissions = (isMyFile || IamRoot) && (!isLink); // create GUI d->m_frame = properties->addPage(i18n("&Permissions")); TQBoxLayout *box = new TQVBoxLayout( d->m_frame, 0, KDialog::spacingHint() ); TQWidget *l; TQLabel *lbl; TQGroupBox *gb; TQGridLayout *gl; TQPushButton* pbAdvancedPerm = 0; /* Group: Access Permissions */ gb = new TQGroupBox ( 0, TQt::Vertical, i18n("Access Permissions"), d->m_frame ); gb->layout()->setSpacing(KDialog::spacingHint()); gb->layout()->setMargin(KDialog::marginHint()); box->addWidget (gb); gl = new TQGridLayout (gb->layout(), 7, 2); gl->setColStretch(1, 1); l = d->explanationLabel = new TQLabel( "", gb ); if (isLink) d->explanationLabel->setText(i18n("This file is a link and does not have permissions.", "All files are links and do not have permissions.", properties->items().count())); else if (!d->canChangePermissions) d->explanationLabel->setText(i18n("Only the owner can change permissions.")); gl->addMultiCellWidget(l, 0, 0, 0, 1); lbl = new TQLabel( i18n("O&wner:"), gb); gl->addWidget(lbl, 1, 0); l = d->ownerPermCombo = new TQComboBox(gb); lbl->setBuddy(l); gl->addWidget(l, 1, 1); connect(l, TQT_SIGNAL( highlighted(int) ), this, TQT_SIGNAL( changed() )); TQWhatsThis::add(l, i18n("Specifies the actions that the owner is allowed to do.")); lbl = new TQLabel( i18n("Gro&up:"), gb); gl->addWidget(lbl, 2, 0); l = d->groupPermCombo = new TQComboBox(gb); lbl->setBuddy(l); gl->addWidget(l, 2, 1); connect(l, TQT_SIGNAL( highlighted(int) ), this, TQT_SIGNAL( changed() )); TQWhatsThis::add(l, i18n("Specifies the actions that the members of the group are allowed to do.")); lbl = new TQLabel( i18n("O&thers:"), gb); gl->addWidget(lbl, 3, 0); l = d->othersPermCombo = new TQComboBox(gb); lbl->setBuddy(l); gl->addWidget(l, 3, 1); connect(l, TQT_SIGNAL( highlighted(int) ), this, TQT_SIGNAL( changed() )); TQWhatsThis::add(l, i18n("Specifies the actions that all users, who are neither " "owner nor in the group, are allowed to do.")); if (!isLink) { l = d->extraCheckbox = new TQCheckBox(hasDir ? i18n("Only own&er can rename and delete folder content") : i18n("Is &executable"), gb ); connect( d->extraCheckbox, TQT_SIGNAL( clicked() ), this, TQT_SIGNAL( changed() ) ); gl->addWidget(l, 4, 1); TQWhatsThis::add(l, hasDir ? i18n("Enable this option to allow only the folder's owner to " "delete or rename the contained files and folders. Other " "users can only add new files, which requires the 'Modify " "Content' permission.") : i18n("Enable this option to mark the file as executable. This only makes " "sense for programs and scripts. It is required when you want to " "execute them.")); TQLayoutItem *spacer = TQT_TQLAYOUTITEM(new TQSpacerItem(0, 20, TQSizePolicy::Minimum, TQSizePolicy::Expanding)); gl->addMultiCell(spacer, 5, 5, 0, 1); pbAdvancedPerm = new TQPushButton(i18n("A&dvanced Permissions"), gb); gl->addMultiCellWidget(pbAdvancedPerm, 6, 6, 0, 1, TQt::AlignRight); connect(pbAdvancedPerm, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotShowAdvancedPermissions() )); } else d->extraCheckbox = 0; /**** Group: Ownership ****/ gb = new TQGroupBox ( 0, TQt::Vertical, i18n("Ownership"), d->m_frame ); gb->layout()->setSpacing(KDialog::spacingHint()); gb->layout()->setMargin(KDialog::marginHint()); box->addWidget (gb); gl = new TQGridLayout (gb->layout(), 4, 3); gl->addRowSpacing(0, 10); /*** Set Owner ***/ l = new TQLabel( i18n("User:"), gb ); gl->addWidget (l, 1, 0); /* GJ: Don't autocomplete more than 1000 users. This is a kind of random * value. Huge sites having 10.000+ user have a fair chance of using NIS, * (possibly) making this unacceptably slow. * OTOH, it is nice to offer this functionality for the standard user. */ int i, maxEntries = 1000; struct passwd *user; /* File owner: For root, offer a KLineEdit with autocompletion. * For a user, who can never chown() a file, offer a TQLabel. */ if (IamRoot && isLocal) { usrEdit = new KLineEdit( gb ); TDECompletion *kcom = usrEdit->completionObject(); kcom->setOrder(TDECompletion::Sorted); setpwent(); for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); i++) kcom->addItem(TQString::fromLatin1(user->pw_name)); endpwent(); usrEdit->setCompletionMode((i < maxEntries) ? TDEGlobalSettings::CompletionAuto : TDEGlobalSettings::CompletionNone); usrEdit->setText(strOwner); gl->addWidget(usrEdit, 1, 1); connect( usrEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); } else { l = new TQLabel(strOwner, gb); gl->addWidget(l, 1, 1); } /*** Set Group ***/ TQStringList groupList; TQCString strUser; user = getpwuid(geteuid()); if (user != 0L) strUser = user->pw_name; #ifdef Q_OS_UNIX gid_t *groups = NULL; int ng = 1; struct group *mygroup; gid_t *newgroups = NULL; groups = (gid_t *) malloc(ng * sizeof(gid_t)); if (getgrouplist(strUser, user->pw_gid, groups, &ng) == -1) { newgroups = (gid_t *) malloc(ng * sizeof(gid_t)); if (newgroups != NULL) { free(groups); groups = newgroups; getgrouplist(strUser, user->pw_gid, groups, &ng); } else ng = 1; } for (i = 0; i < ng; i++) { mygroup = getgrgid(groups[i]); if (mygroup != NULL) groupList += TQString::fromLocal8Bit(mygroup->gr_name); } free(groups); #else //Q_OS_UNIX struct group *ge; /* add the effective Group to the list .. */ ge = getgrgid (getegid()); if (ge) { TQString name = TQString::fromLatin1(ge->gr_name); if (name.isEmpty()) name.setNum(ge->gr_gid); if (groupList.find(name) == groupList.end()) groupList += name; } #endif //Q_OS_UNIX bool isMyGroup = groupList.contains(strGroup); /* add the group the file currently belongs to .. * .. if its not there already */ if (!isMyGroup) groupList += strGroup; l = new TQLabel( i18n("Group:"), gb ); gl->addWidget (l, 2, 0); /* Set group: if possible to change: * - Offer a KLineEdit for root, since he can change to any group. * - Offer a TQComboBox for a normal user, since he can change to a fixed * (small) set of groups only. * If not changeable: offer a TQLabel. */ if (IamRoot && isLocal) { grpEdit = new KLineEdit(gb); TDECompletion *kcom = new TDECompletion; kcom->setItems(groupList); grpEdit->setCompletionObject(kcom, true); grpEdit->setAutoDeleteCompletionObject( true ); grpEdit->setCompletionMode(TDEGlobalSettings::CompletionAuto); grpEdit->setText(strGroup); gl->addWidget(grpEdit, 2, 1); connect( grpEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); } else if ((groupList.count() > 1) && isMyFile && isLocal) { grpCombo = new TQComboBox(gb, "combogrouplist"); grpCombo->insertStringList(groupList); grpCombo->setCurrentItem(groupList.findIndex(strGroup)); gl->addWidget(grpCombo, 2, 1); connect( grpCombo, TQT_SIGNAL( activated( int ) ), this, TQT_SIGNAL( changed() ) ); } else { l = new TQLabel(strGroup, gb); gl->addWidget(l, 2, 1); } gl->setColStretch(2, 10); // "Apply recursive" checkbox if ( hasDir && !isLink && !isTrash ) { d->cbRecursive = new TQCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame ); connect( d->cbRecursive, TQT_SIGNAL( clicked() ), this, TQT_SIGNAL( changed() ) ); box->addWidget( d->cbRecursive ); } updateAccessControls(); if ( isTrash || !d->canChangePermissions ) { //don't allow to change properties for file into trash enableAccessControls(false); if ( pbAdvancedPerm && !d->hasExtendedACL ) pbAdvancedPerm->setEnabled(false); } box->addStretch (10); } #ifdef USE_POSIX_ACL static bool fileSystemSupportsACL( const TQCString& pathCString ) { bool fileSystemSupportsACLs = false; #ifdef Q_OS_FREEBSD struct statfs buf; fileSystemSupportsACLs = ( statfs( pathCString.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS ); #else fileSystemSupportsACLs = getxattr( pathCString.data(), "system.posix_acl_access", NULL, 0 ) >= 0 #ifdef ENODATA || (errno == ENODATA) #endif #ifdef ENOATTR || (errno == ENOATTR) #endif ; #endif return fileSystemSupportsACLs; } #endif void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() { bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed); KDialogBase dlg(properties, 0, true, i18n("Advanced Permissions"), KDialogBase::Ok|KDialogBase::Cancel); TQLabel *l, *cl[3]; TQGroupBox *gb; TQGridLayout *gl; TQVBox *mainVBox = dlg.makeVBoxMainWidget(); // Group: Access Permissions gb = new TQGroupBox ( 0, TQt::Vertical, i18n("Access Permissions"), mainVBox ); gb->layout()->setSpacing(KDialog::spacingHint()); gb->layout()->setMargin(KDialog::marginHint()); gl = new TQGridLayout (gb->layout(), 6, 6); gl->addRowSpacing(0, 10); TQValueVector<TQWidget*> theNotSpecials; l = new TQLabel(i18n("Class"), gb ); gl->addWidget(l, 1, 0); theNotSpecials.append( l ); if (isDir) l = new TQLabel( i18n("Show\nEntries"), gb ); else l = new TQLabel( i18n("Read"), gb ); gl->addWidget (l, 1, 1); theNotSpecials.append( l ); TQString readWhatsThis; if (isDir) readWhatsThis = i18n("This flag allows viewing the content of the folder."); else readWhatsThis = i18n("The Read flag allows viewing the content of the file."); TQWhatsThis::add(l, readWhatsThis); if (isDir) l = new TQLabel( i18n("Write\nEntries"), gb ); else l = new TQLabel( i18n("Write"), gb ); gl->addWidget (l, 1, 2); theNotSpecials.append( l ); TQString writeWhatsThis; if (isDir) writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. " "Note that deleting and renaming can be limited using the Sticky flag."); else writeWhatsThis = i18n("The Write flag allows modifying the content of the file."); TQWhatsThis::add(l, writeWhatsThis); TQString execWhatsThis; if (isDir) { l = new TQLabel( i18n("Enter folder", "Enter"), gb ); execWhatsThis = i18n("Enable this flag to allow entering the folder."); } else { l = new TQLabel( i18n("Exec"), gb ); execWhatsThis = i18n("Enable this flag to allow executing the file as a program."); } TQWhatsThis::add(l, execWhatsThis); theNotSpecials.append( l ); // GJ: Add space between normal and special modes TQSize size = l->sizeHint(); size.setWidth(size.width() + 15); l->setFixedSize(size); gl->addWidget (l, 1, 3); l = new TQLabel( i18n("Special"), gb ); gl->addMultiCellWidget(l, 1, 1, 4, 5); TQString specialWhatsThis; if (isDir) specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact " "meaning of the flag can be seen in the right hand column."); else specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen " "in the right hand column."); TQWhatsThis::add(l, specialWhatsThis); cl[0] = new TQLabel( i18n("User"), gb ); gl->addWidget (cl[0], 2, 0); theNotSpecials.append( cl[0] ); cl[1] = new TQLabel( i18n("Group"), gb ); gl->addWidget (cl[1], 3, 0); theNotSpecials.append( cl[1] ); cl[2] = new TQLabel( i18n("Others"), gb ); gl->addWidget (cl[2], 4, 0); theNotSpecials.append( cl[2] ); l = new TQLabel(i18n("Set UID"), gb); gl->addWidget(l, 2, 5); TQString setUidWhatsThis; if (isDir) setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be " "the owner of all new files."); else setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will " "be executed with the permissions of the owner."); TQWhatsThis::add(l, setUidWhatsThis); l = new TQLabel(i18n("Set GID"), gb); gl->addWidget(l, 3, 5); TQString setGidWhatsThis; if (isDir) setGidWhatsThis = i18n("If this flag is set, the group of this folder will be " "set for all new files."); else setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will " "be executed with the permissions of the group."); TQWhatsThis::add(l, setGidWhatsThis); l = new TQLabel(i18n("File permission", "Sticky"), gb); gl->addWidget(l, 4, 5); TQString stickyWhatsThis; if (isDir) stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner " "and root can delete or rename files. Otherwise everybody " "with write permissions can do this."); else stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may " "be used on some systems"); TQWhatsThis::add(l, stickyWhatsThis); mode_t aPermissions, aPartialPermissions; mode_t dummy1, dummy2; if (!d->isIrregular) { switch (d->pmode) { case PermissionsOnlyFiles: getPermissionMasks(aPartialPermissions, dummy1, aPermissions, dummy2); break; case PermissionsOnlyDirs: case PermissionsMixed: getPermissionMasks(dummy1, aPartialPermissions, dummy2, aPermissions); break; case PermissionsOnlyLinks: aPermissions = UniRead | UniWrite | UniExec | UniSpecial; aPartialPermissions = 0; break; } } else { aPermissions = permissions; aPartialPermissions = d->partialPermissions; } // Draw Checkboxes bool allDisable = true; TQCheckBox *cba[3][4]; for (int row = 0; row < 3 ; ++row) { for (int col = 0; col < 4; ++col) { TQCheckBox *cb = new TQCheckBox( gb ); if ( col != 3 ) theNotSpecials.append( cb ); cba[row][col] = cb; cb->setChecked(aPermissions & fperm[row][col]); if ( d->canChangePermissions ) { allDisable = false; } if ( aPartialPermissions & fperm[row][col] ) { cb->setTristate(); cb->setNoChange(); } else if (d->cbRecursive && d->cbRecursive->isChecked()) { cb->setTristate(); } cb->setEnabled( d->canChangePermissions ); gl->addWidget (cb, row+2, col+1); switch(col) { case 0: TQWhatsThis::add(cb, readWhatsThis); break; case 1: TQWhatsThis::add(cb, writeWhatsThis); break; case 2: TQWhatsThis::add(cb, execWhatsThis); break; case 3: switch(row) { case 0: TQWhatsThis::add(cb, setUidWhatsThis); break; case 1: TQWhatsThis::add(cb, setGidWhatsThis); break; case 2: TQWhatsThis::add(cb, stickyWhatsThis); break; } break; } } } gl->setColStretch(6, 10); #ifdef USE_POSIX_ACL KACLEditWidget *extendedACLs = 0; // FIXME make it work with partial entries if ( properties->items().count() == 1 ) { TQCString pathCString = TQFile::encodeName( properties->item()->url().path() ); d->fileSystemSupportsACLs = fileSystemSupportsACL( pathCString ); } if ( d->fileSystemSupportsACLs ) { std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &TQWidget::hide ) ); extendedACLs = new KACLEditWidget( mainVBox ); if ( d->extendedACL.isValid() && d->extendedACL.isExtended() ) extendedACLs->setACL( d->extendedACL ); else extendedACLs->setACL( KACL( aPermissions ) ); if ( d->defaultACL.isValid() ) extendedACLs->setDefaultACL( d->defaultACL ); if ( properties->items().first()->isDir() ) extendedACLs->setAllowDefaults( true ); if ( !d->canChangePermissions ) extendedACLs->setReadOnly( true ); } #endif if ( allDisable ) { dlg.enableButtonOK( false ); } if (dlg.exec() != KDialogBase::Accepted) return; mode_t andPermissions = mode_t(~0); mode_t orPermissions = 0; for (int row = 0; row < 3; ++row) for (int col = 0; col < 4; ++col) { switch (cba[row][col]->state()) { case TQCheckBox::On: orPermissions |= fperm[row][col]; //fall through case TQCheckBox::Off: andPermissions &= ~fperm[row][col]; break; default: // NoChange break; } } d->isIrregular = false; KFileItemList items = properties->items(); for (KFileItemListIterator it(items); it.current(); ++it) { if (isIrregular(((*it)->permissions() & andPermissions) | orPermissions, (*it)->isDir(), (*it)->isLink())) { d->isIrregular = true; break; } } permissions = orPermissions; d->partialPermissions = andPermissions; #ifdef USE_POSIX_ACL // override with the acls, if present if ( extendedACLs ) { d->extendedACL = extendedACLs->getACL(); d->defaultACL = extendedACLs->getDefaultACL(); d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid(); permissions = d->extendedACL.basePermissions(); permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX ); } #endif updateAccessControls(); emit changed(); } // TQString KFilePermissionsPropsPlugin::tabName () const // { // return i18n ("&Permissions"); // } KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin() { delete d; } bool KFilePermissionsPropsPlugin::supports( KFileItemList _items ) { KFileItemList::const_iterator it = _items.constBegin(); for ( ; it != _items.constEnd(); ++it ) { KFileItem *item = *it; if( !item->user().isEmpty() || !item->group().isEmpty() ) return true; } return false; } // sets a combo box in the Access Control frame void KFilePermissionsPropsPlugin::setComboContent(TQComboBox *combo, PermissionsTarget target, mode_t permissions, mode_t partial) { combo->clear(); if (d->pmode == PermissionsOnlyLinks) { combo->insertItem(i18n("Link")); combo->setCurrentItem(0); return; } mode_t tMask = permissionsMasks[target]; int textIndex; for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++) if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite))) break; Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++) combo->insertItem(i18n(permissionsTexts[(int)d->pmode][i])); if (partial & tMask & ~UniExec) { combo->insertItem(i18n("Varying (No Change)")); combo->setCurrentItem(3); } else combo->setCurrentItem(textIndex); } // permissions are irregular if they cant be displayed in a combo box. bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) { if (isLink) // links are always ok return false; mode_t p = permissions; if (p & (S_ISUID | S_ISGID)) // setuid/setgid -> irregular return true; if (isDir) { p &= ~S_ISVTX; // ignore sticky on dirs // check supported flag combinations mode_t p0 = p & UniOwner; if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner)) return true; p0 = p & UniGroup; if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup)) return true; p0 = p & UniOthers; if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers)) return true; return false; } if (p & S_ISVTX) // sticky on file -> irregular return true; // check supported flag combinations mode_t p0 = p & UniOwner; bool usrXPossible = !p0; // true if this file could be an executable if (p0 & S_IXUSR) { if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR))) return true; usrXPossible = true; } else if (p0 == S_IWUSR) return true; p0 = p & UniGroup; bool grpXPossible = !p0; // true if this file could be an executable if (p0 & S_IXGRP) { if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP))) return true; grpXPossible = true; } else if (p0 == S_IWGRP) return true; if (p0 == 0) grpXPossible = true; p0 = p & UniOthers; bool othXPossible = !p0; // true if this file could be an executable if (p0 & S_IXOTH) { if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH))) return true; othXPossible = true; } else if (p0 == S_IWOTH) return true; // check that there either all targets are executable-compatible, or none return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible); } // enables/disabled the widgets in the Access Control frame void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) { d->ownerPermCombo->setEnabled(enable); d->groupPermCombo->setEnabled(enable); d->othersPermCombo->setEnabled(enable); if (d->extraCheckbox) d->extraCheckbox->setEnabled(enable); if ( d->cbRecursive ) d->cbRecursive->setEnabled(enable); } // updates all widgets in the Access Control frame void KFilePermissionsPropsPlugin::updateAccessControls() { setComboContent(d->ownerPermCombo, PermissionsOwner, permissions, d->partialPermissions); setComboContent(d->groupPermCombo, PermissionsGroup, permissions, d->partialPermissions); setComboContent(d->othersPermCombo, PermissionsOthers, permissions, d->partialPermissions); switch(d->pmode) { case PermissionsOnlyLinks: enableAccessControls(false); break; case PermissionsOnlyFiles: enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL); if (d->canChangePermissions) d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ? i18n("This file uses advanced permissions", "These files use advanced permissions.", properties->items().count()) : ""); if (d->partialPermissions & UniExec) { d->extraCheckbox->setTristate(); d->extraCheckbox->setNoChange(); } else { d->extraCheckbox->setTristate(false); d->extraCheckbox->setChecked(permissions & UniExec); } break; case PermissionsOnlyDirs: enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL); // if this is a dir, and we can change permissions, don't dis-allow // recursive, we can do that for ACL setting. if ( d->cbRecursive ) d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular ); if (d->canChangePermissions) d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ? i18n("This folder uses advanced permissions.", "These folders use advanced permissions.", properties->items().count()) : ""); if (d->partialPermissions & S_ISVTX) { d->extraCheckbox->setTristate(); d->extraCheckbox->setNoChange(); } else { d->extraCheckbox->setTristate(false); d->extraCheckbox->setChecked(permissions & S_ISVTX); } break; case PermissionsMixed: enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL); if (d->canChangePermissions) d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ? i18n("These files use advanced permissions.") : ""); break; if (d->partialPermissions & S_ISVTX) { d->extraCheckbox->setTristate(); d->extraCheckbox->setNoChange(); } else { d->extraCheckbox->setTristate(false); d->extraCheckbox->setChecked(permissions & S_ISVTX); } break; } } // gets masks for files and dirs from the Access Control frame widgets void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions, mode_t &andDirPermissions, mode_t &orFilePermissions, mode_t &orDirPermissions) { andFilePermissions = mode_t(~UniSpecial); andDirPermissions = mode_t(~(S_ISUID|S_ISGID)); orFilePermissions = 0; orDirPermissions = 0; if (d->isIrregular) return; mode_t m = standardPermissions[d->ownerPermCombo->currentItem()]; if (m != (mode_t) -1) { orFilePermissions |= m & UniOwner; if ((m & UniOwner) && ((d->pmode == PermissionsMixed) || ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == TQButton::NoChange)))) andFilePermissions &= ~(S_IRUSR | S_IWUSR); else { andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR); if ((m & S_IRUSR) && (d->extraCheckbox->state() == TQButton::On)) orFilePermissions |= S_IXUSR; } orDirPermissions |= m & UniOwner; if (m & S_IRUSR) orDirPermissions |= S_IXUSR; andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR); } m = standardPermissions[d->groupPermCombo->currentItem()]; if (m != (mode_t) -1) { orFilePermissions |= m & UniGroup; if ((m & UniGroup) && ((d->pmode == PermissionsMixed) || ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == TQButton::NoChange)))) andFilePermissions &= ~(S_IRGRP | S_IWGRP); else { andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP); if ((m & S_IRGRP) && (d->extraCheckbox->state() == TQButton::On)) orFilePermissions |= S_IXGRP; } orDirPermissions |= m & UniGroup; if (m & S_IRGRP) orDirPermissions |= S_IXGRP; andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP); } m = standardPermissions[d->othersPermCombo->currentItem()]; if (m != (mode_t) -1) { orFilePermissions |= m & UniOthers; if ((m & UniOthers) && ((d->pmode == PermissionsMixed) || ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->state() == TQButton::NoChange)))) andFilePermissions &= ~(S_IROTH | S_IWOTH); else { andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH); if ((m & S_IROTH) && (d->extraCheckbox->state() == TQButton::On)) orFilePermissions |= S_IXOTH; } orDirPermissions |= m & UniOthers; if (m & S_IROTH) orDirPermissions |= S_IXOTH; andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH); } if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) && (d->extraCheckbox->state() != TQButton::NoChange)) { andDirPermissions &= ~S_ISVTX; if (d->extraCheckbox->state() == TQButton::On) orDirPermissions |= S_ISVTX; } } void KFilePermissionsPropsPlugin::applyChanges() { mode_t orFilePermissions; mode_t orDirPermissions; mode_t andFilePermissions; mode_t andDirPermissions; if (!d->canChangePermissions) return; if (!d->isIrregular) getPermissionMasks(andFilePermissions, andDirPermissions, orFilePermissions, orDirPermissions); else { orFilePermissions = permissions; andFilePermissions = d->partialPermissions; orDirPermissions = permissions; andDirPermissions = d->partialPermissions; } TQString owner, group; if (usrEdit) owner = usrEdit->text(); if (grpEdit) group = grpEdit->text(); else if (grpCombo) group = grpCombo->currentText(); if (owner == strOwner) owner = TQString::null; // no change if (group == strGroup) group = TQString::null; bool recursive = d->cbRecursive && d->cbRecursive->isChecked(); bool permissionChange = false; KFileItemList files, dirs; KFileItemList items = properties->items(); for (KFileItemListIterator it(items); it.current(); ++it) { if ((*it)->isDir()) { dirs.append(*it); if ((*it)->permissions() != (((*it)->permissions() & andDirPermissions) | orDirPermissions)) permissionChange = true; } else if ((*it)->isFile()) { files.append(*it); if ((*it)->permissions() != (((*it)->permissions() & andFilePermissions) | orFilePermissions)) permissionChange = true; } } const bool ACLChange = ( d->extendedACL != properties->item()->ACL() ); const bool defaultACLChange = ( d->defaultACL != properties->item()->defaultACL() ); if ( owner.isEmpty() && group.isEmpty() && !recursive && !permissionChange && !ACLChange && !defaultACLChange ) return; TDEIO::Job * job; if (files.count() > 0) { job = TDEIO::chmod( files, orFilePermissions, ~andFilePermissions, owner, group, false ); if ( ACLChange && d->fileSystemSupportsACLs ) job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" ); if ( defaultACLChange && d->fileSystemSupportsACLs ) job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" ); connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), TQT_SLOT( slotChmodResult( TDEIO::Job * ) ) ); // Wait for job TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); tqt_enter_modal(&dummy); tqApp->enter_loop(); tqt_leave_modal(&dummy); } if (dirs.count() > 0) { job = TDEIO::chmod( dirs, orDirPermissions, ~andDirPermissions, owner, group, recursive ); if ( ACLChange && d->fileSystemSupportsACLs ) job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" ); if ( defaultACLChange && d->fileSystemSupportsACLs ) job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" ); connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), TQT_SLOT( slotChmodResult( TDEIO::Job * ) ) ); // Wait for job TQWidget dummy(0,0,(WFlags)(WType_Dialog|WShowModal)); tqt_enter_modal(&dummy); tqApp->enter_loop(); tqt_leave_modal(&dummy); } } void KFilePermissionsPropsPlugin::slotChmodResult( TDEIO::Job * job ) { kdDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult" << endl; if (job->error()) job->showErrorDialog( d->m_frame ); // allow apply() to return tqApp->exit_loop(); } class KURLPropsPlugin::KURLPropsPluginPrivate { public: KURLPropsPluginPrivate() { } ~KURLPropsPluginPrivate() { } TQFrame *m_frame; }; KURLPropsPlugin::KURLPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) { d = new KURLPropsPluginPrivate; d->m_frame = properties->addPage(i18n("U&RL")); TQVBoxLayout *layout = new TQVBoxLayout(d->m_frame, 0, KDialog::spacingHint()); TQLabel *l; l = new TQLabel( d->m_frame, "Label_1" ); l->setText( i18n("URL:") ); layout->addWidget(l); URLEdit = new KURLRequester( d->m_frame, "URL Requester" ); layout->addWidget(URLEdit); TQString path = properties->kurl().path(); TQFile f( path ); if ( !f.open( IO_ReadOnly ) ) { return; } f.close(); KSimpleConfig config( path ); config.setDesktopGroup(); URLStr = config.readPathEntry( "URL" ); KFileItem * item = properties->item(); if (item && item->mimetype().startsWith("media/builtin-")) { URLEdit->setEnabled(false); } if ( !URLStr.isNull() ) { URLEdit->setURL( URLStr ); } connect( URLEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); layout->addStretch (1); } KURLPropsPlugin::~KURLPropsPlugin() { delete d; } // TQString KURLPropsPlugin::tabName () const // { // return i18n ("U&RL"); // } bool KURLPropsPlugin::supports( KFileItemList _items ) { if ( _items.count() != 1 ) return false; KFileItem * item = _items.first(); // check if desktop file if ( !KPropsDlgPlugin::isDesktopFile( item ) ) return false; // open file and check type KDesktopFile config( item->url().path(), true /* readonly */ ); return config.hasLinkType(); } void KURLPropsPlugin::applyChanges() { TQString path = properties->kurl().path(); KFileItem * item = properties->item(); if (item && item->mimetype().startsWith("media/builtin-")) { return; } TQFile f( path ); if ( !f.open( IO_ReadWrite ) ) { KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have " "sufficient access to write to <b>%1</b>.</qt>").arg(path)); return; } f.close(); KSimpleConfig config( path ); config.setDesktopGroup(); config.writeEntry( "Type", TQString::fromLatin1("Link")); config.writePathEntry( "URL", URLEdit->url() ); // Users can't create a Link .desktop file with a Name field, // but distributions can. Update the Name field in that case. if ( config.hasKey("Name") ) { TQString nameStr = nameFromFileName(properties->kurl().fileName()); config.writeEntry( "Name", nameStr ); config.writeEntry( "Name", nameStr, true, false, true ); } } /* ---------------------------------------------------- * * KBindingPropsPlugin * * -------------------------------------------------- */ class KBindingPropsPlugin::KBindingPropsPluginPrivate { public: KBindingPropsPluginPrivate() { } ~KBindingPropsPluginPrivate() { } TQFrame *m_frame; }; KBindingPropsPlugin::KBindingPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) { d = new KBindingPropsPluginPrivate; d->m_frame = properties->addPage(i18n("A&ssociation")); patternEdit = new KLineEdit( d->m_frame, "LineEdit_1" ); commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" ); mimeEdit = new KLineEdit( d->m_frame, "LineEdit_3" ); TQBoxLayout *mainlayout = new TQVBoxLayout(d->m_frame, 0, KDialog::spacingHint()); TQLabel* tmpQLabel; tmpQLabel = new TQLabel( d->m_frame, "Label_1" ); tmpQLabel->setText( i18n("Pattern ( example: *.html;*.htm )") ); tmpQLabel->setMinimumSize(tmpQLabel->sizeHint()); mainlayout->addWidget(tmpQLabel, 1); //patternEdit->setGeometry( 10, 40, 210, 30 ); //patternEdit->setText( "" ); patternEdit->setMaxLength( 512 ); patternEdit->setMinimumSize( patternEdit->sizeHint() ); patternEdit->setFixedHeight( fontHeight ); mainlayout->addWidget(patternEdit, 1); tmpQLabel = new TQLabel( d->m_frame, "Label_2" ); tmpQLabel->setText( i18n("Mime Type") ); tmpQLabel->setMinimumSize(tmpQLabel->sizeHint()); mainlayout->addWidget(tmpQLabel, 1); //mimeEdit->setGeometry( 10, 160, 210, 30 ); mimeEdit->setMaxLength( 256 ); mimeEdit->setMinimumSize( mimeEdit->sizeHint() ); mimeEdit->setFixedHeight( fontHeight ); mainlayout->addWidget(mimeEdit, 1); tmpQLabel = new TQLabel( d->m_frame, "Label_3" ); tmpQLabel->setText( i18n("Comment") ); tmpQLabel->setMinimumSize(tmpQLabel->sizeHint()); mainlayout->addWidget(tmpQLabel, 1); //commentEdit->setGeometry( 10, 100, 210, 30 ); commentEdit->setMaxLength( 256 ); commentEdit->setMinimumSize( commentEdit->sizeHint() ); commentEdit->setFixedHeight( fontHeight ); mainlayout->addWidget(commentEdit, 1); cbAutoEmbed = new TQCheckBox( i18n("Left click previews"), d->m_frame, "cbAutoEmbed" ); mainlayout->addWidget(cbAutoEmbed, 1); mainlayout->addStretch (10); mainlayout->activate(); TQFile f( _props->kurl().path() ); if ( !f.open( IO_ReadOnly ) ) return; f.close(); KSimpleConfig config( _props->kurl().path() ); config.setDesktopGroup(); TQString patternStr = config.readEntry( "Patterns" ); TQString iconStr = config.readEntry( "Icon" ); TQString commentStr = config.readEntry( "Comment" ); m_sMimeStr = config.readEntry( "MimeType" ); if ( !patternStr.isEmpty() ) patternEdit->setText( patternStr ); if ( !commentStr.isEmpty() ) commentEdit->setText( commentStr ); if ( !m_sMimeStr.isEmpty() ) mimeEdit->setText( m_sMimeStr ); cbAutoEmbed->setTristate(); if ( config.hasKey( "X-TDE-AutoEmbed" ) ) cbAutoEmbed->setChecked( config.readBoolEntry( "X-TDE-AutoEmbed" ) ); else cbAutoEmbed->setNoChange(); connect( patternEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( commentEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( mimeEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( cbAutoEmbed, TQT_SIGNAL( toggled( bool ) ), this, TQT_SIGNAL( changed() ) ); } KBindingPropsPlugin::~KBindingPropsPlugin() { delete d; } // TQString KBindingPropsPlugin::tabName () const // { // return i18n ("A&ssociation"); // } bool KBindingPropsPlugin::supports( KFileItemList _items ) { if ( _items.count() != 1 ) return false; KFileItem * item = _items.first(); // check if desktop file if ( !KPropsDlgPlugin::isDesktopFile( item ) ) return false; // open file and check type KDesktopFile config( item->url().path(), true /* readonly */ ); return config.hasMimeTypeType(); } void KBindingPropsPlugin::applyChanges() { TQString path = properties->kurl().path(); TQFile f( path ); if ( !f.open( IO_ReadWrite ) ) { KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have " "sufficient access to write to <b>%1</b>.</qt>").arg(path)); return; } f.close(); KSimpleConfig config( path ); config.setDesktopGroup(); config.writeEntry( "Type", TQString::fromLatin1("MimeType") ); config.writeEntry( "Patterns", patternEdit->text() ); config.writeEntry( "Comment", commentEdit->text() ); config.writeEntry( "Comment", commentEdit->text(), true, false, true ); // for compat config.writeEntry( "MimeType", mimeEdit->text() ); if ( cbAutoEmbed->state() == TQButton::NoChange ) config.deleteEntry( "X-TDE-AutoEmbed", false ); else config.writeEntry( "X-TDE-AutoEmbed", cbAutoEmbed->isChecked() ); config.sync(); } /* ---------------------------------------------------- * * KDevicePropsPlugin * * -------------------------------------------------- */ class KDevicePropsPlugin::KDevicePropsPluginPrivate { public: KDevicePropsPluginPrivate() { } ~KDevicePropsPluginPrivate() { } TQFrame *m_frame; TQStringList mountpointlist; TQLabel *m_freeSpaceText; TQLabel *m_freeSpaceLabel; TQProgressBar *m_freeSpaceBar; }; KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) { d = new KDevicePropsPluginPrivate; d->m_frame = properties->addPage(i18n("De&vice")); TQStringList devices; KMountPoint::List mountPoints = KMountPoint::possibleMountPoints(); for(KMountPoint::List::ConstIterator it = mountPoints.begin(); it != mountPoints.end(); ++it) { KMountPoint *mp = *it; TQString mountPoint = mp->mountPoint(); TQString device = mp->mountedFrom(); kdDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType()<<endl; if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty() && device != "none") { devices.append( device + TQString::fromLatin1(" (") + mountPoint + TQString::fromLatin1(")") ); m_devicelist.append(device); d->mountpointlist.append(mountPoint); } } TQGridLayout *layout = new TQGridLayout( d->m_frame, 0, 2, 0, KDialog::spacingHint()); layout->setColStretch(1, 1); TQLabel* label; label = new TQLabel( d->m_frame ); label->setText( devices.count() == 0 ? i18n("Device (/dev/fd0):") : // old style i18n("Device:") ); // new style (combobox) layout->addWidget(label, 0, 0); device = new TQComboBox( true, d->m_frame, "ComboBox_device" ); device->insertStringList( devices ); layout->addWidget(device, 0, 1); connect( device, TQT_SIGNAL( activated( int ) ), this, TQT_SLOT( slotActivated( int ) ) ); readonly = new TQCheckBox( d->m_frame, "CheckBox_readonly" ); readonly->setText( i18n("Read only") ); layout->addWidget(readonly, 1, 1); label = new TQLabel( d->m_frame ); label->setText( i18n("File system:") ); layout->addWidget(label, 2, 0); TQLabel *fileSystem = new TQLabel( d->m_frame ); layout->addWidget(fileSystem, 2, 1); label = new TQLabel( d->m_frame ); label->setText( devices.count()==0 ? i18n("Mount point (/mnt/floppy):") : // old style i18n("Mount point:")); // new style (combobox) layout->addWidget(label, 3, 0); mountpoint = new TQLabel( d->m_frame, "LineEdit_mountpoint" ); layout->addWidget(mountpoint, 3, 1); // show disk free d->m_freeSpaceText = new TQLabel(i18n("Free disk space:"), d->m_frame ); layout->addWidget(d->m_freeSpaceText, 4, 0); d->m_freeSpaceLabel = new TQLabel( d->m_frame ); layout->addWidget( d->m_freeSpaceLabel, 4, 1 ); d->m_freeSpaceBar = new TQProgressBar( d->m_frame, "freeSpaceBar" ); layout->addMultiCellWidget(d->m_freeSpaceBar, 5, 5, 0, 1); // we show it in the slot when we know the values d->m_freeSpaceText->hide(); d->m_freeSpaceLabel->hide(); d->m_freeSpaceBar->hide(); KSeparator* sep = new KSeparator( KSeparator::HLine, d->m_frame); layout->addMultiCellWidget(sep, 6, 6, 0, 1); unmounted = new TDEIconButton( d->m_frame ); int bsize = 66 + 2 * unmounted->style().pixelMetric(TQStyle::PM_ButtonMargin); unmounted->setFixedSize(bsize, bsize); unmounted->setIconType(TDEIcon::Desktop, TDEIcon::Device); layout->addWidget(unmounted, 7, 0); label = new TQLabel( i18n("Unmounted Icon"), d->m_frame ); layout->addWidget(label, 7, 1); layout->setRowStretch(8, 1); TQString path( _props->kurl().path() ); TQFile f( path ); if ( !f.open( IO_ReadOnly ) ) return; f.close(); KSimpleConfig config( path ); config.setDesktopGroup(); TQString deviceStr = config.readEntry( "Dev" ); TQString mountPointStr = config.readEntry( "MountPoint" ); bool ro = config.readBoolEntry( "ReadOnly", false ); TQString unmountedStr = config.readEntry( "UnmountIcon" ); TQString fsType = config.readEntry("FSType"); fileSystem->setText( (fsType.stripWhiteSpace() != "") ? i18n(fsType.local8Bit()) : "" ); device->setEditText( deviceStr ); if ( !deviceStr.isEmpty() ) { // Set default options for this device (first matching entry) int index = m_devicelist.findIndex(deviceStr); if (index != -1) { //kdDebug(250) << "found it " << index << endl; slotActivated( index ); } } if ( !mountPointStr.isEmpty() ) { mountpoint->setText( mountPointStr ); updateInfo(); } readonly->setChecked( ro ); if ( unmountedStr.isEmpty() ) unmountedStr = KMimeType::defaultMimeTypePtr()->KServiceType::icon(); // default icon unmounted->setIcon( unmountedStr ); connect( device, TQT_SIGNAL( activated( int ) ), this, TQT_SIGNAL( changed() ) ); connect( device, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( readonly, TQT_SIGNAL( toggled( bool ) ), this, TQT_SIGNAL( changed() ) ); connect( unmounted, TQT_SIGNAL( iconChanged( TQString ) ), this, TQT_SIGNAL( changed() ) ); connect( device, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SLOT( slotDeviceChanged() ) ); processLockouts(); } KDevicePropsPlugin::~KDevicePropsPlugin() { delete d; } // TQString KDevicePropsPlugin::tabName () const // { // return i18n ("De&vice"); // } void KDevicePropsPlugin::processLockouts() { if (device->currentText().stripWhiteSpace() != "") { properties->enableButtonOK(true); } else { properties->enableButtonOK(false); } } void KDevicePropsPlugin::updateInfo() { // we show it in the slot when we know the values d->m_freeSpaceText->hide(); d->m_freeSpaceLabel->hide(); d->m_freeSpaceBar->hide(); if ( !mountpoint->text().isEmpty() ) { KDiskFreeSp * job = new KDiskFreeSp; connect( job, TQT_SIGNAL( foundMountPoint( const unsigned long&, const unsigned long&, const unsigned long&, const TQString& ) ), this, TQT_SLOT( slotFoundMountPoint( const unsigned long&, const unsigned long&, const unsigned long&, const TQString& ) ) ); job->readDF( mountpoint->text() ); } processLockouts(); } void KDevicePropsPlugin::slotActivated( int index ) { // Update mountpoint so that it matches the device that was selected in the combo device->setEditText( m_devicelist[index] ); mountpoint->setText( d->mountpointlist[index] ); updateInfo(); } void KDevicePropsPlugin::slotDeviceChanged() { // Update mountpoint so that it matches the typed device int index = m_devicelist.findIndex( device->currentText() ); if ( index != -1 ) mountpoint->setText( d->mountpointlist[index] ); else mountpoint->setText( TQString::null ); updateInfo(); } void KDevicePropsPlugin::slotFoundMountPoint( const unsigned long& kBSize, const unsigned long& /*kBUsed*/, const unsigned long& kBAvail, const TQString& ) { d->m_freeSpaceText->show(); d->m_freeSpaceLabel->show(); int percUsed = 100 - (int)(100.0 * kBAvail / kBSize); d->m_freeSpaceLabel->setText( // xgettext:no-c-format -- Don't warn about translating the %1 out of %2 part. i18n("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)") .arg(TDEIO::convertSizeFromKB(kBAvail)) .arg(TDEIO::convertSizeFromKB(kBSize)) .arg( 100 - (int)(100.0 * kBAvail / kBSize) )); d->m_freeSpaceBar->setProgress(percUsed, 100); d->m_freeSpaceBar->show(); } bool KDevicePropsPlugin::supports( KFileItemList _items ) { if ( _items.count() != 1 ) return false; KFileItem * item = _items.first(); // check if desktop file if ( !KPropsDlgPlugin::isDesktopFile( item ) ) return false; // open file and check type KDesktopFile config( item->url().path(), true /* readonly */ ); return config.hasDeviceType(); } void KDevicePropsPlugin::applyChanges() { TQString path = properties->kurl().path(); TQFile f( path ); if ( !f.open( IO_ReadWrite ) ) { KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient " "access to write to <b>%1</b>.</qt>").arg(path)); return; } f.close(); KSimpleConfig config( path ); config.setDesktopGroup(); config.writeEntry( "Type", TQString::fromLatin1("FSDevice") ); config.writeEntry( "Dev", device->currentText() ); config.writeEntry( "MountPoint", mountpoint->text() ); config.writeEntry( "UnmountIcon", unmounted->icon() ); kdDebug(250) << "unmounted->icon() = " << unmounted->icon() << endl; config.writeEntry( "ReadOnly", readonly->isChecked() ); config.sync(); } /* ---------------------------------------------------- * * KDesktopPropsPlugin * * -------------------------------------------------- */ KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) { TQFrame *frame = properties->addPage(i18n("&Application")); TQVBoxLayout *mainlayout = new TQVBoxLayout( frame, 0, KDialog::spacingHint() ); w = new KPropertiesDesktopBase(frame); mainlayout->addWidget(w); bool bKDesktopMode = (TQCString(tqApp->name()) == "kdesktop"); // nasty heh? if (bKDesktopMode) { // Hide Name entry w->nameEdit->hide(); w->nameLabel->hide(); } w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly); w->pathEdit->lineEdit()->setAcceptDrops(false); connect( w->nameEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( w->genNameEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( w->commentEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( w->commandEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( w->pathEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( w->browseButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotBrowseExec() ) ); connect( w->addFiletypeButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotAddFiletype() ) ); connect( w->delFiletypeButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotDelFiletype() ) ); connect( w->advancedButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotAdvanced() ) ); // now populate the page TQString path = _props->kurl().path(); TQFile f( path ); if ( !f.open( IO_ReadOnly ) ) return; f.close(); KDesktopFile config( path ); TQString nameStr = config.readName(); TQString genNameStr = config.readGenericName(); TQString commentStr = config.readComment(); TQString commandStr = config.readPathEntry( "Exec" ); if (commandStr.left(12) == "ksystraycmd ") { commandStr.remove(0, 12); m_systrayBool = true; } else m_systrayBool = false; m_origCommandStr = commandStr; TQString pathStr = config.readPathEntry( "Path" ); m_terminalBool = config.readBoolEntry( "Terminal" ); m_terminalOptionStr = config.readEntry( "TerminalOptions" ); m_suidBool = config.readBoolEntry( "X-TDE-SubstituteUID" ) || config.readBoolEntry( "X-KDE-SubstituteUID" ); if( config.hasKey( "X-TDE-Username" )) m_suidUserStr = config.readEntry( "X-TDE-Username" ); else m_suidUserStr = config.readEntry( "X-KDE-Username" ); if( config.hasKey( "StartupNotify" )) m_startupBool = config.readBoolEntry( "StartupNotify", true ); else m_startupBool = config.readBoolEntry( "X-TDE-StartupNotify", true ); m_dcopServiceType = config.readEntry("X-DCOP-ServiceType").lower(); TQStringList mimeTypes = config.readListEntry( "MimeType", ';' ); if ( nameStr.isEmpty() || bKDesktopMode ) { // We'll use the file name if no name is specified // because we _need_ a Name for a valid file. // But let's do it in apply, not here, so that we pick up the right name. setDirty(); } if ( !bKDesktopMode ) w->nameEdit->setText(nameStr); w->genNameEdit->setText( genNameStr ); w->commentEdit->setText( commentStr ); w->commandEdit->setText( commandStr ); w->pathEdit->lineEdit()->setText( pathStr ); w->filetypeList->setAllColumnsShowFocus(true); KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr(); for(TQStringList::ConstIterator it = mimeTypes.begin(); it != mimeTypes.end(); ) { KMimeType::Ptr p = KMimeType::mimeType(*it); ++it; TQString preference; if (it != mimeTypes.end()) { bool numeric; (*it).toInt(&numeric); if (numeric) { preference = *it; ++it; } } if (p && (p != defaultMimetype)) { new TQListViewItem(w->filetypeList, p->name(), p->comment(), preference); } } } KDesktopPropsPlugin::~KDesktopPropsPlugin() { } void KDesktopPropsPlugin::slotSelectMimetype() { TQListView *w = (TQListView*)sender(); TQListViewItem *item = w->firstChild(); while(item) { if (item->isSelected()) w->setSelected(item, false); item = item->nextSibling(); } } void KDesktopPropsPlugin::slotAddFiletype() { KDialogBase dlg(w, "KPropertiesMimetypes", true, i18n("Add File Type for %1").arg(properties->kurl().fileName()), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok); KGuiItem okItem(i18n("&Add"), TQString::null /* no icon */, i18n("Add the selected file types to\nthe list of supported file types."), i18n("Add the selected file types to\nthe list of supported file types.")); dlg.setButtonOK(okItem); KPropertiesMimetypeBase *mw = new KPropertiesMimetypeBase(&dlg); dlg.setMainWidget(mw); { mw->listView->setRootIsDecorated(true); mw->listView->setSelectionMode(TQListView::Extended); mw->listView->setAllColumnsShowFocus(true); mw->listView->setFullWidth(true); mw->listView->setMinimumSize(500,400); connect(mw->listView, TQT_SIGNAL(selectionChanged()), this, TQT_SLOT(slotSelectMimetype())); connect(mw->listView, TQT_SIGNAL(doubleClicked( TQListViewItem *, const TQPoint &, int )), &dlg, TQT_SLOT( slotOk())); TQMap<TQString,TQListViewItem*> majorMap; TQListViewItem *majorGroup; KMimeType::List mimetypes = KMimeType::allMimeTypes(); TQValueListIterator<KMimeType::Ptr> it(mimetypes.begin()); for (; it != mimetypes.end(); ++it) { TQString mimetype = (*it)->name(); if (mimetype == KMimeType::defaultMimeType()) continue; int index = mimetype.find("/"); TQString maj = mimetype.left(index); TQString min = mimetype.mid(index+1); TQMapIterator<TQString,TQListViewItem*> mit = majorMap.find( maj ); if ( mit == majorMap.end() ) { majorGroup = new TQListViewItem( mw->listView, maj ); majorGroup->setExpandable(true); mw->listView->setOpen(majorGroup, true); majorMap.insert( maj, majorGroup ); } else { majorGroup = mit.data(); } TQListViewItem *item = new TQListViewItem(majorGroup, min, (*it)->comment()); item->setPixmap(0, (*it)->pixmap(TDEIcon::Small, IconSize(TDEIcon::Small))); } TQMapIterator<TQString,TQListViewItem*> mit = majorMap.find( "all" ); if ( mit != majorMap.end()) { mw->listView->setCurrentItem(mit.data()); mw->listView->ensureItemVisible(mit.data()); } } if (dlg.exec() == KDialogBase::Accepted) { KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr(); TQListViewItem *majorItem = mw->listView->firstChild(); while(majorItem) { TQString major = majorItem->text(0); TQListViewItem *minorItem = majorItem->firstChild(); while(minorItem) { if (minorItem->isSelected()) { TQString mimetype = major + "/" + minorItem->text(0); KMimeType::Ptr p = KMimeType::mimeType(mimetype); if (p && (p != defaultMimetype)) { mimetype = p->name(); bool found = false; TQListViewItem *item = w->filetypeList->firstChild(); while (item) { if (mimetype == item->text(0)) { found = true; break; } item = item->nextSibling(); } if (!found) { new TQListViewItem(w->filetypeList, p->name(), p->comment()); emit changed(); } } } minorItem = minorItem->nextSibling(); } majorItem = majorItem->nextSibling(); } } } void KDesktopPropsPlugin::slotDelFiletype() { delete w->filetypeList->currentItem(); emit changed(); } void KDesktopPropsPlugin::checkCommandChanged() { if (KRun::binaryName(w->commandEdit->text(), true) != KRun::binaryName(m_origCommandStr, true)) { TQString m_origCommandStr = w->commandEdit->text(); m_dcopServiceType= TQString::null; // Reset } } void KDesktopPropsPlugin::applyChanges() { kdDebug(250) << "KDesktopPropsPlugin::applyChanges" << endl; TQString path = properties->kurl().path(); TQFile f( path ); if ( !f.open( IO_ReadWrite ) ) { KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have " "sufficient access to write to <b>%1</b>.</qt>").arg(path)); return; } f.close(); // If the command is changed we reset certain settings that are strongly // coupled to the command. checkCommandChanged(); KSimpleConfig config( path ); config.setDesktopGroup(); config.writeEntry( "Type", TQString::fromLatin1("Application")); config.writeEntry( "Comment", w->commentEdit->text() ); config.writeEntry( "Comment", w->commentEdit->text(), true, false, true ); // for compat config.writeEntry( "GenericName", w->genNameEdit->text() ); config.writeEntry( "GenericName", w->genNameEdit->text(), true, false, true ); // for compat if (m_systrayBool) config.writePathEntry( "Exec", w->commandEdit->text().prepend("ksystraycmd ") ); else config.writePathEntry( "Exec", w->commandEdit->text() ); config.writePathEntry( "Path", w->pathEdit->lineEdit()->text() ); // Write mimeTypes TQStringList mimeTypes; for( TQListViewItem *item = w->filetypeList->firstChild(); item; item = item->nextSibling() ) { TQString preference = item->text(2); mimeTypes.append(item->text(0)); if (!preference.isEmpty()) mimeTypes.append(preference); } config.writeEntry( "MimeType", mimeTypes, ';' ); if ( !w->nameEdit->isHidden() ) { TQString nameStr = w->nameEdit->text(); config.writeEntry( "Name", nameStr ); config.writeEntry( "Name", nameStr, true, false, true ); } config.writeEntry("Terminal", m_terminalBool); config.writeEntry("TerminalOptions", m_terminalOptionStr); config.writeEntry("X-TDE-SubstituteUID", m_suidBool); config.writeEntry("X-TDE-Username", m_suidUserStr); config.writeEntry("StartupNotify", m_startupBool); config.writeEntry("X-DCOP-ServiceType", m_dcopServiceType); config.sync(); // KSycoca update needed? TQString sycocaPath = TDEGlobal::dirs()->relativeLocation("apps", path); bool updateNeeded = !sycocaPath.startsWith("/"); if (!updateNeeded) { sycocaPath = TDEGlobal::dirs()->relativeLocation("xdgdata-apps", path); updateNeeded = !sycocaPath.startsWith("/"); } if (updateNeeded) KService::rebuildKSycoca(w); } void KDesktopPropsPlugin::slotBrowseExec() { KURL f = KFileDialog::getOpenURL( TQString::null, TQString::null, w ); if ( f.isEmpty() ) return; if ( !f.isLocalFile()) { KMessageBox::sorry(w, i18n("Only executables on local file systems are supported.")); return; } TQString path = f.path(); KRun::shellQuote( path ); w->commandEdit->setText( path ); } void KDesktopPropsPlugin::slotAdvanced() { KDialogBase dlg(w, "KPropertiesDesktopAdv", true, i18n("Advanced Options for %1").arg(properties->kurl().fileName()), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok); KPropertiesDesktopAdvBase *w = new KPropertiesDesktopAdvBase(&dlg); dlg.setMainWidget(w); // If the command is changed we reset certain settings that are strongly // coupled to the command. checkCommandChanged(); // check to see if we use konsole if not do not add the nocloseonexit // because we don't know how to do this on other terminal applications TDEConfigGroup confGroup( TDEGlobal::config(), TQString::fromLatin1("General") ); TQString preferredTerminal = confGroup.readPathEntry("TerminalApplication", TQString::fromLatin1("konsole")); bool terminalCloseBool = false; if (preferredTerminal == "konsole") { terminalCloseBool = (m_terminalOptionStr.contains( "--noclose" ) > 0); w->terminalCloseCheck->setChecked(terminalCloseBool); m_terminalOptionStr.replace( "--noclose", ""); } else { w->terminalCloseCheck->hide(); } w->terminalCheck->setChecked(m_terminalBool); w->terminalEdit->setText(m_terminalOptionStr); w->terminalCloseCheck->setEnabled(m_terminalBool); w->terminalEdit->setEnabled(m_terminalBool); w->terminalEditLabel->setEnabled(m_terminalBool); w->suidCheck->setChecked(m_suidBool); w->suidEdit->setText(m_suidUserStr); w->suidEdit->setEnabled(m_suidBool); w->suidEditLabel->setEnabled(m_suidBool); w->startupInfoCheck->setChecked(m_startupBool); w->systrayCheck->setChecked(m_systrayBool); if (m_dcopServiceType == "unique") w->dcopCombo->setCurrentItem(2); else if (m_dcopServiceType == "multi") w->dcopCombo->setCurrentItem(1); else if (m_dcopServiceType == "wait") w->dcopCombo->setCurrentItem(3); else w->dcopCombo->setCurrentItem(0); // Provide username completion up to 1000 users. TDECompletion *kcom = new TDECompletion; kcom->setOrder(TDECompletion::Sorted); struct passwd *pw; int i, maxEntries = 1000; setpwent(); for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++) kcom->addItem(TQString::fromLatin1(pw->pw_name)); endpwent(); if (i < maxEntries) { w->suidEdit->setCompletionObject(kcom, true); w->suidEdit->setAutoDeleteCompletionObject( true ); w->suidEdit->setCompletionMode(TDEGlobalSettings::CompletionAuto); } else { delete kcom; } connect( w->terminalEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( w->terminalCloseCheck, TQT_SIGNAL( toggled( bool ) ), this, TQT_SIGNAL( changed() ) ); connect( w->terminalCheck, TQT_SIGNAL( toggled( bool ) ), this, TQT_SIGNAL( changed() ) ); connect( w->suidCheck, TQT_SIGNAL( toggled( bool ) ), this, TQT_SIGNAL( changed() ) ); connect( w->suidEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( w->startupInfoCheck, TQT_SIGNAL( toggled( bool ) ), this, TQT_SIGNAL( changed() ) ); connect( w->systrayCheck, TQT_SIGNAL( toggled( bool ) ), this, TQT_SIGNAL( changed() ) ); connect( w->dcopCombo, TQT_SIGNAL( highlighted( int ) ), this, TQT_SIGNAL( changed() ) ); if ( dlg.exec() == TQDialog::Accepted ) { m_terminalOptionStr = w->terminalEdit->text().stripWhiteSpace(); m_terminalBool = w->terminalCheck->isChecked(); m_suidBool = w->suidCheck->isChecked(); m_suidUserStr = w->suidEdit->text().stripWhiteSpace(); m_startupBool = w->startupInfoCheck->isChecked(); m_systrayBool = w->systrayCheck->isChecked(); if (w->terminalCloseCheck->isChecked()) { m_terminalOptionStr.append(" --noclose"); } switch(w->dcopCombo->currentItem()) { case 1: m_dcopServiceType = "multi"; break; case 2: m_dcopServiceType = "unique"; break; case 3: m_dcopServiceType = "wait"; break; default: m_dcopServiceType = "none"; break; } } } bool KDesktopPropsPlugin::supports( KFileItemList _items ) { if ( _items.count() != 1 ) return false; KFileItem * item = _items.first(); // check if desktop file if ( !KPropsDlgPlugin::isDesktopFile( item ) ) return false; // open file and check type KDesktopFile config( item->url().path(), true /* readonly */ ); return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access"); } void KPropertiesDialog::virtual_hook( int id, void* data ) { KDialogBase::virtual_hook( id, data ); } void KPropsDlgPlugin::virtual_hook( int, void* ) { /*BASE::virtual_hook( id, data );*/ } /** * The following code is obsolete and only kept for binary compatibility * To be removed in KDE 4 */ class KExecPropsPlugin::KExecPropsPluginPrivate { public: KExecPropsPluginPrivate() { } ~KExecPropsPluginPrivate() { } TQFrame *m_frame; TQCheckBox *nocloseonexitCheck; }; KExecPropsPlugin::KExecPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) { d = new KExecPropsPluginPrivate; d->m_frame = properties->addPage(i18n("E&xecute")); TQVBoxLayout * mainlayout = new TQVBoxLayout( d->m_frame, 0, KDialog::spacingHint()); // Now the widgets in the top layout TQLabel* l; l = new TQLabel( i18n( "Comman&d:" ), d->m_frame ); mainlayout->addWidget(l); TQHBoxLayout * hlayout; hlayout = new TQHBoxLayout(KDialog::spacingHint()); mainlayout->addLayout(hlayout); execEdit = new KLineEdit( d->m_frame ); TQWhatsThis::add(execEdit,i18n( "Following the command, you can have several place holders which will be replaced " "with the actual values when the actual program is run:\n" "%f - a single file name\n" "%F - a list of files; use for applications that can open several local files at once\n" "%u - a single URL\n" "%U - a list of URLs\n" "%d - the folder of the file to open\n" "%D - a list of folders\n" "%i - the icon\n" "%m - the mini-icon\n" "%c - the caption")); hlayout->addWidget(execEdit, 1); l->setBuddy( execEdit ); execBrowse = new TQPushButton( d->m_frame ); execBrowse->setText( i18n("&Browse...") ); hlayout->addWidget(execBrowse); // The groupbox about swallowing TQGroupBox* tmpQGroupBox; tmpQGroupBox = new TQGroupBox( i18n("Panel Embedding"), d->m_frame ); tmpQGroupBox->setColumnLayout( 0, TQt::Horizontal ); mainlayout->addWidget(tmpQGroupBox); TQGridLayout *grid = new TQGridLayout(tmpQGroupBox->layout(), 2, 2); grid->setSpacing( KDialog::spacingHint() ); grid->setColStretch(1, 1); l = new TQLabel( i18n( "&Execute on click:" ), tmpQGroupBox ); grid->addWidget(l, 0, 0); swallowExecEdit = new KLineEdit( tmpQGroupBox ); grid->addWidget(swallowExecEdit, 0, 1); l->setBuddy( swallowExecEdit ); l = new TQLabel( i18n( "&Window title:" ), tmpQGroupBox ); grid->addWidget(l, 1, 0); swallowTitleEdit = new KLineEdit( tmpQGroupBox ); grid->addWidget(swallowTitleEdit, 1, 1); l->setBuddy( swallowTitleEdit ); // The groupbox about run in terminal tmpQGroupBox = new TQGroupBox( d->m_frame ); tmpQGroupBox->setColumnLayout( 0, TQt::Horizontal ); mainlayout->addWidget(tmpQGroupBox); grid = new TQGridLayout(tmpQGroupBox->layout(), 3, 2); grid->setSpacing( KDialog::spacingHint() ); grid->setColStretch(1, 1); terminalCheck = new TQCheckBox( tmpQGroupBox ); terminalCheck->setText( i18n("&Run in terminal") ); grid->addMultiCellWidget(terminalCheck, 0, 0, 0, 1); // check to see if we use konsole if not do not add the nocloseonexit // because we don't know how to do this on other terminal applications TDEConfigGroup confGroup( TDEGlobal::config(), TQString::fromLatin1("General") ); TQString preferredTerminal = confGroup.readPathEntry("TerminalApplication", TQString::fromLatin1("konsole")); int posOptions = 1; d->nocloseonexitCheck = 0L; if (preferredTerminal == "konsole") { posOptions = 2; d->nocloseonexitCheck = new TQCheckBox( tmpQGroupBox ); d->nocloseonexitCheck->setText( i18n("Do not &close when command exits") ); grid->addMultiCellWidget(d->nocloseonexitCheck, 1, 1, 0, 1); } terminalLabel = new TQLabel( i18n( "&Terminal options:" ), tmpQGroupBox ); grid->addWidget(terminalLabel, posOptions, 0); terminalEdit = new KLineEdit( tmpQGroupBox ); grid->addWidget(terminalEdit, posOptions, 1); terminalLabel->setBuddy( terminalEdit ); // The groupbox about run with substituted uid. tmpQGroupBox = new TQGroupBox( d->m_frame ); tmpQGroupBox->setColumnLayout( 0, TQt::Horizontal ); mainlayout->addWidget(tmpQGroupBox); grid = new TQGridLayout(tmpQGroupBox->layout(), 2, 2); grid->setSpacing(KDialog::spacingHint()); grid->setColStretch(1, 1); suidCheck = new TQCheckBox(tmpQGroupBox); suidCheck->setText(i18n("Ru&n as a different user")); grid->addMultiCellWidget(suidCheck, 0, 0, 0, 1); suidLabel = new TQLabel(i18n( "&Username:" ), tmpQGroupBox); grid->addWidget(suidLabel, 1, 0); suidEdit = new KLineEdit(tmpQGroupBox); grid->addWidget(suidEdit, 1, 1); suidLabel->setBuddy( suidEdit ); mainlayout->addStretch(1); // now populate the page TQString path = _props->kurl().path(); TQFile f( path ); if ( !f.open( IO_ReadOnly ) ) return; f.close(); KSimpleConfig config( path ); config.setDollarExpansion( false ); config.setDesktopGroup(); execStr = config.readPathEntry( "Exec" ); swallowExecStr = config.readPathEntry( "SwallowExec" ); swallowTitleStr = config.readEntry( "SwallowTitle" ); termBool = config.readBoolEntry( "Terminal" ); termOptionsStr = config.readEntry( "TerminalOptions" ); suidBool = config.readBoolEntry( "X-TDE-SubstituteUID" ); suidUserStr = config.readEntry( "X-TDE-Username" ); if ( !swallowExecStr.isNull() ) swallowExecEdit->setText( swallowExecStr ); if ( !swallowTitleStr.isNull() ) swallowTitleEdit->setText( swallowTitleStr ); if ( !execStr.isNull() ) execEdit->setText( execStr ); if ( d->nocloseonexitCheck ) { d->nocloseonexitCheck->setChecked( (termOptionsStr.contains( "--noclose" ) > 0) ); termOptionsStr.replace( "--noclose", ""); } if ( !termOptionsStr.isNull() ) terminalEdit->setText( termOptionsStr ); terminalCheck->setChecked( termBool ); enableCheckedEdit(); suidCheck->setChecked( suidBool ); suidEdit->setText( suidUserStr ); enableSuidEdit(); // Provide username completion up to 1000 users. TDECompletion *kcom = new TDECompletion; kcom->setOrder(TDECompletion::Sorted); struct passwd *pw; int i, maxEntries = 1000; setpwent(); for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++) kcom->addItem(TQString::fromLatin1(pw->pw_name)); endpwent(); if (i < maxEntries) { suidEdit->setCompletionObject(kcom, true); suidEdit->setAutoDeleteCompletionObject( true ); suidEdit->setCompletionMode(TDEGlobalSettings::CompletionAuto); } else { delete kcom; } connect( swallowExecEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( swallowTitleEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( execEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( terminalEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); if (d->nocloseonexitCheck) connect( d->nocloseonexitCheck, TQT_SIGNAL( toggled( bool ) ), this, TQT_SIGNAL( changed() ) ); connect( terminalCheck, TQT_SIGNAL( toggled( bool ) ), this, TQT_SIGNAL( changed() ) ); connect( suidCheck, TQT_SIGNAL( toggled( bool ) ), this, TQT_SIGNAL( changed() ) ); connect( suidEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( execBrowse, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotBrowseExec() ) ); connect( terminalCheck, TQT_SIGNAL( clicked() ), this, TQT_SLOT( enableCheckedEdit() ) ); connect( suidCheck, TQT_SIGNAL( clicked() ), this, TQT_SLOT( enableSuidEdit() ) ); } KExecPropsPlugin::~KExecPropsPlugin() { delete d; } void KExecPropsPlugin::enableCheckedEdit() { bool checked = terminalCheck->isChecked(); terminalLabel->setEnabled( checked ); if (d->nocloseonexitCheck) d->nocloseonexitCheck->setEnabled( checked ); terminalEdit->setEnabled( checked ); } void KExecPropsPlugin::enableSuidEdit() { bool checked = suidCheck->isChecked(); suidLabel->setEnabled( checked ); suidEdit->setEnabled( checked ); } bool KExecPropsPlugin::supports( KFileItemList _items ) { if ( _items.count() != 1 ) return false; KFileItem * item = _items.first(); // check if desktop file if ( !KPropsDlgPlugin::isDesktopFile( item ) ) return false; // open file and check type KDesktopFile config( item->url().path(), true /* readonly */ ); return config.hasApplicationType() && kapp->authorize("run_desktop_files") && kapp->authorize("shell_access"); } void KExecPropsPlugin::applyChanges() { kdDebug(250) << "KExecPropsPlugin::applyChanges" << endl; TQString path = properties->kurl().path(); TQFile f( path ); if ( !f.open( IO_ReadWrite ) ) { KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have " "sufficient access to write to <b>%1</b>.</qt>").arg(path)); return; } f.close(); KSimpleConfig config( path ); config.setDesktopGroup(); config.writeEntry( "Type", TQString::fromLatin1("Application")); config.writePathEntry( "Exec", execEdit->text() ); config.writePathEntry( "SwallowExec", swallowExecEdit->text() ); config.writeEntry( "SwallowTitle", swallowTitleEdit->text() ); config.writeEntry( "Terminal", terminalCheck->isChecked() ); TQString temp = terminalEdit->text(); if (d->nocloseonexitCheck ) if ( d->nocloseonexitCheck->isChecked() ) temp += TQString::fromLatin1("--noclose "); temp = temp.stripWhiteSpace(); config.writeEntry( "TerminalOptions", temp ); config.writeEntry( "X-TDE-SubstituteUID", suidCheck->isChecked() ); config.writeEntry( "X-TDE-Username", suidEdit->text() ); } void KExecPropsPlugin::slotBrowseExec() { KURL f = KFileDialog::getOpenURL( TQString::null, TQString::null, d->m_frame ); if ( f.isEmpty() ) return; if ( !f.isLocalFile()) { KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported.")); return; } TQString path = f.path(); KRun::shellQuote( path ); execEdit->setText( path ); } class TDEApplicationPropsPlugin::TDEApplicationPropsPluginPrivate { public: TDEApplicationPropsPluginPrivate() { m_kdesktopMode = TQCString(tqApp->name()) == "kdesktop"; // nasty heh? } ~TDEApplicationPropsPluginPrivate() { } TQFrame *m_frame; bool m_kdesktopMode; }; TDEApplicationPropsPlugin::TDEApplicationPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) { d = new TDEApplicationPropsPluginPrivate; d->m_frame = properties->addPage(i18n("&Application")); TQVBoxLayout *toplayout = new TQVBoxLayout( d->m_frame, 0, KDialog::spacingHint()); TQIconSet iconSet; TQPixmap pixMap; addExtensionButton = new TQPushButton( TQString::null, d->m_frame ); iconSet = SmallIconSet( "back" ); addExtensionButton->setIconSet( iconSet ); pixMap = iconSet.pixmap( TQIconSet::Small, TQIconSet::Normal ); addExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 ); connect( addExtensionButton, TQT_SIGNAL( clicked() ), TQT_SLOT( slotAddExtension() ) ); delExtensionButton = new TQPushButton( TQString::null, d->m_frame ); iconSet = SmallIconSet( "forward" ); delExtensionButton->setIconSet( iconSet ); delExtensionButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 ); connect( delExtensionButton, TQT_SIGNAL( clicked() ), TQT_SLOT( slotDelExtension() ) ); TQLabel *l; TQGridLayout *grid = new TQGridLayout(2, 2); grid->setColStretch(1, 1); toplayout->addLayout(TQT_TQLAYOUT(grid)); if ( d->m_kdesktopMode ) { // in kdesktop the name field comes from the first tab nameEdit = 0L; } else { l = new TQLabel(i18n("Name:"), d->m_frame, "Label_4" ); grid->addWidget(l, 0, 0); nameEdit = new KLineEdit( d->m_frame, "LineEdit_3" ); grid->addWidget(nameEdit, 0, 1); } l = new TQLabel(i18n("Description:"), d->m_frame, "Label_5" ); grid->addWidget(l, 1, 0); genNameEdit = new KLineEdit( d->m_frame, "LineEdit_4" ); grid->addWidget(genNameEdit, 1, 1); l = new TQLabel(i18n("Comment:"), d->m_frame, "Label_3" ); grid->addWidget(l, 2, 0); commentEdit = new KLineEdit( d->m_frame, "LineEdit_2" ); grid->addWidget(commentEdit, 2, 1); l = new TQLabel(i18n("File types:"), d->m_frame); toplayout->addWidget(l, 0, AlignLeft); grid = new TQGridLayout(4, 3); grid->setColStretch(0, 1); grid->setColStretch(2, 1); grid->setRowStretch( 0, 1 ); grid->setRowStretch( 3, 1 ); toplayout->addLayout(TQT_TQLAYOUT(grid), 2); extensionsList = new TQListBox( d->m_frame ); extensionsList->setSelectionMode( TQListBox::Extended ); grid->addMultiCellWidget(extensionsList, 0, 3, 0, 0); grid->addWidget(addExtensionButton, 1, 1); grid->addWidget(delExtensionButton, 2, 1); availableExtensionsList = new TQListBox( d->m_frame ); availableExtensionsList->setSelectionMode( TQListBox::Extended ); grid->addMultiCellWidget(availableExtensionsList, 0, 3, 2, 2); TQString path = properties->kurl().path() ; TQFile f( path ); if ( !f.open( IO_ReadOnly ) ) return; f.close(); KDesktopFile config( path ); TQString commentStr = config.readComment(); TQString genNameStr = config.readGenericName(); TQStringList selectedTypes = config.readListEntry( "X-TDE-ServiceTypes" ); // For compatibility with KDE 1.x selectedTypes += config.readListEntry( "MimeType", ';' ); TQString nameStr = config.readName(); if ( nameStr.isEmpty() || d->m_kdesktopMode ) { // We'll use the file name if no name is specified // because we _need_ a Name for a valid file. // But let's do it in apply, not here, so that we pick up the right name. setDirty(); } commentEdit->setText( commentStr ); genNameEdit->setText( genNameStr ); if ( nameEdit ) nameEdit->setText( nameStr ); selectedTypes.sort(); TQStringList::Iterator sit = selectedTypes.begin(); for( ; sit != selectedTypes.end(); ++sit ) { if ( !((*sit).isEmpty()) ) extensionsList->insertItem( *sit ); } KMimeType::List mimeTypes = KMimeType::allMimeTypes(); TQValueListIterator<KMimeType::Ptr> it2 = mimeTypes.begin(); for ( ; it2 != mimeTypes.end(); ++it2 ) addMimeType ( (*it2)->name() ); updateButton(); connect( extensionsList, TQT_SIGNAL( highlighted( int ) ), this, TQT_SLOT( updateButton() ) ); connect( availableExtensionsList, TQT_SIGNAL( highlighted( int ) ), this, TQT_SLOT( updateButton() ) ); connect( addExtensionButton, TQT_SIGNAL( clicked() ), this, TQT_SIGNAL( changed() ) ); connect( delExtensionButton, TQT_SIGNAL( clicked() ), this, TQT_SIGNAL( changed() ) ); if ( nameEdit ) connect( nameEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( commentEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( genNameEdit, TQT_SIGNAL( textChanged( const TQString & ) ), this, TQT_SIGNAL( changed() ) ); connect( availableExtensionsList, TQT_SIGNAL( selected( int ) ), this, TQT_SIGNAL( changed() ) ); connect( extensionsList, TQT_SIGNAL( selected( int ) ), this, TQT_SIGNAL( changed() ) ); } TDEApplicationPropsPlugin::~TDEApplicationPropsPlugin() { delete d; } // TQString TDEApplicationPropsPlugin::tabName () const // { // return i18n ("&Application"); // } void TDEApplicationPropsPlugin::updateButton() { addExtensionButton->setEnabled(availableExtensionsList->currentItem()>-1); delExtensionButton->setEnabled(extensionsList->currentItem()>-1); } void TDEApplicationPropsPlugin::addMimeType( const TQString & name ) { // Add a mimetype to the list of available mime types if not in the extensionsList bool insert = true; for ( uint i = 0; i < extensionsList->count(); i++ ) if ( extensionsList->text( i ) == name ) insert = false; if ( insert ) { availableExtensionsList->insertItem( name ); availableExtensionsList->sort(); } } bool TDEApplicationPropsPlugin::supports( KFileItemList _items ) { // same constraints as KExecPropsPlugin : desktop file with Type = Application return KExecPropsPlugin::supports( _items ); } void TDEApplicationPropsPlugin::applyChanges() { TQString path = properties->kurl().path(); TQFile f( path ); if ( !f.open( IO_ReadWrite ) ) { KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not " "have sufficient access to write to <b>%1</b>.</qt>").arg(path)); return; } f.close(); KSimpleConfig config( path ); config.setDesktopGroup(); config.writeEntry( "Type", TQString::fromLatin1("Application")); config.writeEntry( "Comment", commentEdit->text() ); config.writeEntry( "Comment", commentEdit->text(), true, false, true ); // for compat config.writeEntry( "GenericName", genNameEdit->text() ); config.writeEntry( "GenericName", genNameEdit->text(), true, false, true ); // for compat TQStringList selectedTypes; for ( uint i = 0; i < extensionsList->count(); i++ ) selectedTypes.append( extensionsList->text( i ) ); config.writeEntry( "MimeType", selectedTypes, ';' ); config.writeEntry( "X-TDE-ServiceTypes", "" ); // hmm, actually it should probably be the contrary (but see also typeslistitem.cpp) TQString nameStr = nameEdit ? nameEdit->text() : TQString::null; if ( nameStr.isEmpty() ) // nothing entered, or widget not existing at all (kdesktop mode) nameStr = nameFromFileName(properties->kurl().fileName()); config.writeEntry( "Name", nameStr ); config.writeEntry( "Name", nameStr, true, false, true ); config.sync(); } void TDEApplicationPropsPlugin::slotAddExtension() { TQListBoxItem *item = availableExtensionsList->firstItem(); TQListBoxItem *nextItem; while ( item ) { nextItem = item->next(); if ( item->isSelected() ) { extensionsList->insertItem( item->text() ); availableExtensionsList->removeItem( availableExtensionsList->index( item ) ); } item = nextItem; } extensionsList->sort(); updateButton(); } void TDEApplicationPropsPlugin::slotDelExtension() { TQListBoxItem *item = extensionsList->firstItem(); TQListBoxItem *nextItem; while ( item ) { nextItem = item->next(); if ( item->isSelected() ) { availableExtensionsList->insertItem( item->text() ); extensionsList->removeItem( extensionsList->index( item ) ); } item = nextItem; } availableExtensionsList->sort(); updateButton(); } #include "kpropertiesdialog.moc"