/* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (c) 1999, 2000 Preston Brown Copyright (c) 2000 Simon Hausmann Copyright (c) 2000 David Faure Copyright (c) 2003 Waldo Bastian 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 * some layout management by * Bertrand Leconte * the rest of the layout management, bug fixes, adaptation to libkio, * template feature by * David Faure * More layout, cleanups, and fixes by * Preston Brown * Plugin capability, cleanups and port to KDialogBase by * Simon Hausmann * KDesktopPropsPlugin by * Waldo Bastian */ #include extern "C" { #include #include #include #include } #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_POSIX_ACL extern "C" { #include #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_XATTR_H #include #endif } #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kfilesharedlg.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 #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 = KIO::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(KIO::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 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( "","Properties for %n Selected Items",_items.count()) : i18n( "Properties for %1" ).arg(KIO::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(KIO::decodeFileName(_url.fileName())), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, parent, name, modal), m_singleUrl( _url ) { d = new KPropertiesDialogPrivate; KIO::UDSEntry entry; KIO::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(KIO::decodeFileName(_url.fileName())), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, parent, name, modal), m_singleUrl( _url ) { d = new KPropertiesDialogPrivate; KIO::UDSEntry entry; KIO::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(KIO::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(it); if ( plugin ) { plugin->setFileNameReadOnly( ro ); break; } } } void KPropertiesDialog::slotStatResult( KIO::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(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->authorizeKAction("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; KTrader::OfferList offers = KTrader::self()->query( mimetype, query ); KTrader::OfferList::ConstIterator it = offers.begin(); KTrader::OfferList::ConstIterator end = offers.end(); for (; it != end; ++it ) { KPropsDlgPlugin *plugin = KParts::ComponentFactory ::createInstanceFromLibrary( (*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 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; KIO::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; 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; // 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 kio_trash or kio_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 = "kmultiple"; 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 && !isTrash && (bDesktopFile || S_ISDIR(mode)) && !d->bMultiple /*not implemented for multiple*/ ) { KIconButton *iconButton = new KIconButton( 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( KIcon::Desktop, KIcon::Device ); else iconButton->setIconType( KIcon::Desktop, KIcon::Application ); } else iconButton->setIconType( KIcon::Desktop, KIcon::FileSystem ); 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); iconLabel->setPixmap( KGlobal::iconLoader()->loadIcon( iconStr, KIcon::Desktop, 48) ); iconArea = iconLabel; } grid->addWidget(iconArea, curRow, 0, Qt::AlignLeft); if (d->bMultiple || isTrash || isDevice || hasRoot) { TQLabel *lab = new TQLabel(d->m_frame ); if ( d->bMultiple ) lab->setText( KIO::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 && !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->authorizeKAction("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(KIO::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, and not for '/' if ( isLocal && !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(KIO::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(KGlobal::locale()->formatDateTime(dt), d->m_frame ); grid->addWidget(l, curRow++, 2); } tim = item->time(KIO::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(KGlobal::locale()->formatDateTime(dt), d->m_frame ); grid->addWidget(l, curRow++, 2); } tim = item->time(KIO::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(KGlobal::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 = KIO::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()) + " " + KProcess::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 =KGlobal::dirs()->relativeLocation("mime", path); if (m_sRelativePath.startsWith("/")) m_sRelativePath = TQString::null; } else { m_sRelativePath =KGlobal::dirs()->relativeLocation("apps", path); if (m_sRelativePath.startsWith("/")) { m_sRelativePath =KGlobal::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(KIO::convertSizeFromKB(kBAvail)) .arg(KIO::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(KIO::convertSizeFromKB(kBAvail)) .arg(KIO::convertSizeFromKB(kBSize)) .arg( 100 - (int)(100.0 * kBAvail / kBSize) )); } void KFilePropsPlugin::slotDirSizeUpdate() { KIO::filesize_t totalSize = d->dirSizeJob->totalSize(); KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles(); KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs(); m_sizeLabel->setText( i18n("Calculating... %1 (%2)\n%3, %4") .arg(KIO::convertSize(totalSize)) .arg(KGlobal::locale()->formatNumber(totalSize, 0)) .arg(i18n("1 file","%n files",totalFiles)) .arg(i18n("1 sub-folder","%n sub-folders",totalSubdirs))); } void KFilePropsPlugin::slotDirSizeFinished( KIO::Job * job ) { if (job->error()) m_sizeLabel->setText( job->errorString() ); else { KIO::filesize_t totalSize = static_cast(job)->totalSize(); KIO::filesize_t totalFiles = static_cast(job)->totalFiles(); KIO::filesize_t totalSubdirs = static_cast(job)->totalSubdirs(); m_sizeLabel->setText( TQString::fromLatin1("%1 (%2)\n%3, %4") .arg(KIO::convertSize(totalSize)) .arg(KGlobal::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( KIO::Job * ) ), TQT_SLOT( slotDirSizeFinished( KIO::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 = KIO::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_OBJECT_NAME_STRING)) { 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 KIO::Job * job = 0L; KURL oldurl = properties->kurl(); TQString newFileName = KIO::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 = KIO::move( oldurl, properties->kurl() ); else // Copying a template job = KIO::copy( oldurl, properties->kurl() ); connect( job, TQT_SIGNAL( result( KIO::Job * ) ), TQT_SLOT( slotCopyFinished( KIO::Job * ) ) ); connect( job, TQT_SIGNAL( renamed( KIO::Job *, const KURL &, const KURL & ) ), TQT_SLOT( slotFileRenamed( KIO::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( KIO::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(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() { KIconButton *iconButton = ::tqqt_cast( iconArea ); if ( !iconButton || !d->bIconChanged ) return; // handle icon changes - only local files (or pseudo-local) for now // TODO: Use KTempFile and KIO::file_copy with overwrite = true KURL url = properties->kurl(); url = KIO::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("Could not save properties. You do not " "have sufficient access to write to %1.").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( KIO::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 //KIO::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, Qt::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, Qt::AlignRight); connect(pbAdvancedPerm, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotShowAdvancedPermissions() )); } else d->extraCheckbox = 0; /**** Group: Ownership ****/ gb = new TQGroupBox ( 0, Qt::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; struct group *ge; /* 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 ); KCompletion *kcom = usrEdit->completionObject(); kcom->setOrder(KCompletion::Sorted); setpwent(); for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); i++) kcom->addItem(TQString::fromLatin1(user->pw_name)); endpwent(); usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto : KGlobalSettings::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 setgrent(); for (i=0; ((ge = getgrent()) != 0L) && (i < maxEntries); i++) { if (IamRoot) groupList += TQString::fromLatin1(ge->gr_name); else { /* pick the groups to which the user belongs */ char ** members = ge->gr_mem; char * member; while ((member = *members) != 0L) { if (strUser == member) { groupList += TQString::fromLocal8Bit(ge->gr_name); break; } ++members; } } } endgrent(); #endif //Q_OS_UNIX /* 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; } 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); KCompletion *kcom = new KCompletion; kcom->setItems(groupList); grpEdit->setCompletionObject(kcom, true); grpEdit->setAutoDeleteCompletionObject( true ); grpEdit->setCompletionMode(KGlobalSettings::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, Qt::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 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 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 ( 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 (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; KIO::Job * job; if (files.count() > 0) { job = KIO::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( KIO::Job * ) ), TQT_SLOT( slotChmodResult( KIO::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 = KIO::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( KIO::Job * ) ), TQT_SLOT( slotChmodResult( KIO::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( KIO::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" ); 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(); TQFile f( path ); if ( !f.open( IO_ReadWrite ) ) { KMessageBox::sorry( 0, i18n("Could not save properties. You do not have " "sufficient access to write to %1.").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("Could not save properties. You do not have " "sufficient access to write to %1.").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 :"<mountType() :"<mountType()<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 KIconButton( d->m_frame ); int bsize = 66 + 2 * unmounted->style().pixelMetric(TQStyle::PM_ButtonMargin); unmounted->setFixedSize(bsize, bsize); unmounted->setIconType(KIcon::Desktop, KIcon::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" ); fileSystem->setText( i18n(config.readEntry("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() ) ); } KDevicePropsPlugin::~KDevicePropsPlugin() { delete d; } // TQString KDevicePropsPlugin::tabName () const // { // return i18n ("De&vice"); // } 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() ); } } 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(KIO::convertSizeFromKB(kBAvail)) .arg(KIO::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("Could not save properties. You do not have sufficient " "access to write to %1.").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" ); m_suidUserStr = config.readEntry( "X-TDE-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 majorMap; TQListViewItem *majorGroup; KMimeType::List mimetypes = KMimeType::allMimeTypes(); TQValueListIterator 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 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(KIcon::Small, IconSize(KIcon::Small))); } TQMapIterator 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("Could not save properties. You do not have " "sufficient access to write to %1.").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 = KGlobal::dirs()->relativeLocation("apps", path); bool updateNeeded = !sycocaPath.startsWith("/"); if (!updateNeeded) { sycocaPath = KGlobal::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 KConfigGroup confGroup( KGlobal::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. KCompletion *kcom = new KCompletion; kcom->setOrder(KCompletion::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(KGlobalSettings::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, Qt::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, Qt::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 KConfigGroup confGroup( KGlobal::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, Qt::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. KCompletion *kcom = new KCompletion; kcom->setOrder(KCompletion::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(KGlobalSettings::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("Could not save properties. You do not have " "sufficient access to write to %1.").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 KApplicationPropsPlugin::KApplicationPropsPluginPrivate { public: KApplicationPropsPluginPrivate() { m_kdesktopMode = TQCString(tqApp->name()) == "kdesktop"; // nasty heh? } ~KApplicationPropsPluginPrivate() { } TQFrame *m_frame; bool m_kdesktopMode; }; KApplicationPropsPlugin::KApplicationPropsPlugin( KPropertiesDialog *_props ) : KPropsDlgPlugin( _props ) { d = new KApplicationPropsPluginPrivate; 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( "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 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() ) ); } KApplicationPropsPlugin::~KApplicationPropsPlugin() { delete d; } // TQString KApplicationPropsPlugin::tabName () const // { // return i18n ("&Application"); // } void KApplicationPropsPlugin::updateButton() { addExtensionButton->setEnabled(availableExtensionsList->currentItem()>-1); delExtensionButton->setEnabled(extensionsList->currentItem()>-1); } void KApplicationPropsPlugin::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 KApplicationPropsPlugin::supports( KFileItemList _items ) { // same constraints as KExecPropsPlugin : desktop file with Type = Application return KExecPropsPlugin::supports( _items ); } void KApplicationPropsPlugin::applyChanges() { TQString path = properties->kurl().path(); TQFile f( path ); if ( !f.open( IO_ReadWrite ) ) { KMessageBox::sorry( 0, i18n("Could not save properties. You do not " "have sufficient access to write to %1.").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( "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 KApplicationPropsPlugin::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 KApplicationPropsPlugin::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"