/* This file is part of the KDE Display Manager Configuration package Copyright (C) 1997 Thomas Tanghus (tanghus@earthling.net) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 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. */ #include <unistd.h> #include <sys/types.h> #include <tqstyle.h> #include <tqlayout.h> #include <tqlabel.h> #include <tqtooltip.h> #include <tqvalidator.h> #include <tqwhatsthis.h> #include <tqvgroupbox.h> #include <tqpushbutton.h> #include <tdefiledialog.h> #include <kimageio.h> #include <kimagefilepreview.h> #include <tdeio/netaccess.h> #include <tdelocale.h> #include <kstandarddirs.h> #include <tdemessagebox.h> #include <kurldrag.h> #include "tdm-users.h" #include <sys/stat.h> extern KSimpleConfig *config; TDMUsersWidget::TDMUsersWidget(TQWidget *parent, const char *name) : TQWidget(parent, name) { #ifdef Q_OS_LINUX struct stat st; if (!stat( "/etc/debian_version", &st )) { /* debian */ defminuid = "1000"; defmaxuid = "29999"; } else if (!stat( "/usr/portage", &st )) { /* gentoo */ defminuid = "1000"; defmaxuid = "65000"; } else if (!stat( "/etc/mandrake-release", &st )) { /* mandrake - check before redhat! */ defminuid = "500"; defmaxuid = "65000"; } else if (!stat( "/etc/redhat-release", &st )) { /* redhat */ defminuid = "100"; defmaxuid = "65000"; } else /* if (!stat( "/etc/SuSE-release", &st )) */ { /* suse */ defminuid = "500"; defmaxuid = "65000"; } #elif defined(Q_OS_SOLARIS) struct stat st; if (stat( "/etc/dilos_version_build", &st ) == 0) { /* DilOS */ defminuid = "1000"; } else { /* other illumos distro */ defminuid = "100"; } defmaxuid = "60000"; #else /* default */ defminuid = "1000"; defmaxuid = "65000"; #endif /* default */ // We assume that $kde_datadir/tdm exists, but better check for pics/ and pics/users, // and create them if necessary. config->setGroup( "X-*-Greeter" ); m_userPixDir = config->readEntry( "FaceDir", TDEGlobal::dirs()->resourceDirs("data").last() + "tdm/faces" ) + '/'; m_notFirst = false; TQDir testDir( m_userPixDir ); if ( !testDir.exists() && !testDir.mkdir( testDir.absPath() ) && !geteuid() ) KMessageBox::sorry( this, i18n("Unable to create folder %1").arg( testDir.absPath() ) ); chmod( TQFile::encodeName( m_userPixDir ), 0755 ); m_defaultText = i18n("<default>"); TQString wtstr; minGroup = new TQGroupBox( 2, TQt::Horizontal, i18n("System U&IDs"), this ); TQWhatsThis::add( minGroup, i18n("Users with a UID (numerical user identification) outside this range will not be listed by TDM and this setup dialog." " Note that users with the UID 0 (typically root) are not affected by this and must be" " explicitly hidden in \"Not hidden\" mode.")); TQSizePolicy sp_ign_fix( TQSizePolicy::Ignored, TQSizePolicy::Fixed ); TQValidator *valid = new TQIntValidator( 0, 999999, TQT_TQOBJECT(minGroup) ); TQLabel *minlab = new TQLabel( i18n("Below:"), minGroup ); leminuid = new KLineEdit( minGroup ); minlab->setBuddy( leminuid ); leminuid->setSizePolicy( sp_ign_fix ); leminuid->setValidator( valid ); connect( leminuid, TQT_SIGNAL(textChanged( const TQString & )), TQT_SLOT(slotChanged()) ); connect( leminuid, TQT_SIGNAL(textChanged( const TQString & )), TQT_SLOT(slotMinMaxChanged()) ); TQLabel *maxlab = new TQLabel( i18n("Above:"), minGroup ); lemaxuid = new KLineEdit( minGroup ); maxlab->setBuddy( lemaxuid ); lemaxuid->setSizePolicy( sp_ign_fix ); lemaxuid->setValidator( valid ); connect(lemaxuid, TQT_SIGNAL(textChanged( const TQString & )), TQT_SLOT(slotChanged()) ); connect(lemaxuid, TQT_SIGNAL(textChanged( const TQString & )), TQT_SLOT(slotMinMaxChanged()) ); usrGroup = new TQButtonGroup( 5, TQt::Vertical, i18n("Users"), this ); connect( usrGroup, TQT_SIGNAL(clicked( int )), TQT_SLOT(slotShowOpts()) ); connect( usrGroup, TQT_SIGNAL(clicked( int )), TQT_SLOT(slotChanged()) ); cbshowlist = new TQCheckBox( i18n("Show list"), usrGroup ); TQWhatsThis::add( cbshowlist, i18n("If this option is checked, TDM will show a list of users," " so users can click on their name or image rather than typing in their login.") ); cbcomplete = new TQCheckBox( i18n("Autocompletion"), usrGroup ); TQWhatsThis::add( cbcomplete, i18n("If this option is checked, TDM will automatically complete" " user names while they are typed in the line edit.") ); cbinverted = new TQCheckBox( i18n("Inverse selection"), usrGroup ); TQWhatsThis::add( cbinverted, i18n("This option specifies how the users for \"Show list\" and \"Autocompletion\"" " are selected in the \"Select users and groups\" list: " "If not checked, select only the checked users. " "If checked, select all non-system users, except the checked ones.")); cbusrsrt = new TQCheckBox( i18n("Sor&t users"), usrGroup ); connect( cbusrsrt, TQT_SIGNAL(toggled( bool )), TQT_SLOT(slotChanged()) ); TQWhatsThis::add( cbusrsrt, i18n("If this is checked, TDM will alphabetically sort the user list." " Otherwise users are listed in the order they appear in the password file.") ); wstack = new TQWidgetStack( this ); s_label = new TQLabel( wstack, i18n("S&elect users and groups:"), this ); optinlv = new TDEListView( this ); optinlv->addColumn( i18n("Selected Users") ); optinlv->setResizeMode( TQListView::LastColumn ); TQWhatsThis::add( optinlv, i18n("TDM will show all checked users. Entries denoted with '@' are user groups. Checking a group is like checking all users in that group.") ); wstack->addWidget( optinlv ); connect( optinlv, TQT_SIGNAL(clicked( TQListViewItem * )), TQT_SLOT(slotUpdateOptIn( TQListViewItem * )) ); connect( optinlv, TQT_SIGNAL(clicked( TQListViewItem * )), TQT_SLOT(slotChanged()) ); optoutlv = new TDEListView( this ); optoutlv->addColumn( i18n("Hidden Users") ); optoutlv->setResizeMode( TQListView::LastColumn ); TQWhatsThis::add( optoutlv, i18n("TDM will show all non-checked non-system users. Entries denoted with '@' are user groups. Checking a group is like checking all users in that group.") ); wstack->addWidget( optoutlv ); connect( optoutlv, TQT_SIGNAL(clicked( TQListViewItem * )), TQT_SLOT(slotUpdateOptOut( TQListViewItem * )) ); connect( optoutlv, TQT_SIGNAL(clicked( TQListViewItem * )), TQT_SLOT(slotChanged()) ); faceGroup = new TQButtonGroup( 5, TQt::Vertical, i18n("User Image Source"), this ); TQWhatsThis::add( faceGroup, i18n("Here you can specify where TDM will obtain the images that represent users." " \"Admin\" represents the global folder; these are the pictures you can set below." " \"User\" means that TDM should read the user's $HOME/.face.icon file." " The two selections in the middle define the order of preference if both sources are available.") ); connect( faceGroup, TQT_SIGNAL(clicked( int )), TQT_SLOT(slotFaceOpts()) ); connect( faceGroup, TQT_SIGNAL(clicked( int )), TQT_SLOT(slotChanged()) ); rbadmonly = new TQRadioButton( i18n("Admin"), faceGroup ); rbprefadm = new TQRadioButton( i18n("Admin, user"), faceGroup ); rbprefusr = new TQRadioButton( i18n("User, admin"), faceGroup ); rbusronly = new TQRadioButton( i18n("User"), faceGroup ); TQGroupBox *picGroup = new TQVGroupBox( i18n("User Images"), this ); TQWidget *hlpw = new TQWidget( picGroup ); usercombo = new KComboBox( hlpw ); TQWhatsThis::add( usercombo, i18n("The user the image below belongs to.") ); connect( usercombo, TQT_SIGNAL(activated( int )), TQT_SLOT(slotUserSelected()) ); TQLabel *userlabel = new TQLabel( usercombo, i18n("User:"), hlpw ); userbutton = new TQPushButton( hlpw ); userbutton->setAcceptDrops( true ); userbutton->installEventFilter( this ); // for drag and drop uint sz = style().pixelMetric( TQStyle::PM_ButtonMargin ) * 2 + 48; userbutton->setFixedSize( sz, sz ); connect( userbutton, TQT_SIGNAL(clicked()), TQT_SLOT(slotUserButtonClicked()) ); TQToolTip::add( userbutton, i18n("Click or drop an image here") ); TQWhatsThis::add( userbutton, i18n("Here you can see the image assigned to the user selected in the combo box above. Click on the image button to select from a list" " of images or drag and drop your own image on to the button (e.g. from Konqueror).") ); rstuserbutton = new TQPushButton( i18n("Unset"), hlpw ); TQWhatsThis::add( rstuserbutton, i18n("Click this button to make TDM use the default image for the selected user.") ); connect( rstuserbutton, TQT_SIGNAL(clicked()), TQT_SLOT(slotUnsetUserPix()) ); TQGridLayout *hlpl = new TQGridLayout( hlpw, 3, 2, 0, KDialog::spacingHint() ); hlpl->addWidget( userlabel, 0, 0 ); // hlpl->addSpacing( KDialog::spacingHint() ); hlpl->addWidget( usercombo, 0, 1 ); hlpl->addMultiCellWidget( userbutton, 1,1, 0,1, TQt::AlignHCenter ); hlpl->addMultiCellWidget( rstuserbutton, 2,2, 0,1, TQt::AlignHCenter ); TQHBoxLayout *main = new TQHBoxLayout( this, 10 ); TQVBoxLayout *lLayout = new TQVBoxLayout( main, 10 ); lLayout->addWidget( minGroup ); lLayout->addWidget( usrGroup ); lLayout->addStretch( 1 ); TQVBoxLayout *mLayout = new TQVBoxLayout( main, 10 ); mLayout->addWidget( s_label ); mLayout->addWidget( wstack ); mLayout->setStretchFactor( wstack, 1 ); main->setStretchFactor( mLayout, 1 ); TQVBoxLayout *rLayout = new TQVBoxLayout( main, 10 ); rLayout->addWidget( faceGroup ); rLayout->addWidget( picGroup ); rLayout->addStretch( 1 ); } void TDMUsersWidget::makeReadOnly() { leminuid->setReadOnly(true); lemaxuid->setReadOnly(true); cbshowlist->setEnabled(false); cbcomplete->setEnabled(false); cbinverted->setEnabled(false); cbusrsrt->setEnabled(false); rbadmonly->setEnabled(false); rbprefadm->setEnabled(false); rbprefusr->setEnabled(false); rbusronly->setEnabled(false); wstack->setEnabled(false); disconnect( userbutton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotUserButtonClicked()) ); userbutton->setAcceptDrops(false); rstuserbutton->setEnabled(false); } void TDMUsersWidget::slotShowOpts() { bool en = cbshowlist->isChecked() || cbcomplete->isChecked(); cbinverted->setEnabled( en ); cbusrsrt->setEnabled( en ); wstack->setEnabled( en ); wstack->raiseWidget( cbinverted->isChecked() ? optoutlv : optinlv ); en = cbshowlist->isChecked(); faceGroup->setEnabled( en ); if (!en) { usercombo->setEnabled( false ); userbutton->setEnabled( false ); rstuserbutton->setEnabled( false ); } else slotFaceOpts(); } void TDMUsersWidget::slotFaceOpts() { bool en = !rbusronly->isChecked(); usercombo->setEnabled( en ); userbutton->setEnabled( en ); if (en) slotUserSelected(); else rstuserbutton->setEnabled( false ); } void TDMUsersWidget::slotUserSelected() { TQString user = usercombo->currentText(); TQImage p; if (user != m_defaultText && p.load( m_userPixDir + user + ".face.icon" )) { rstuserbutton->setEnabled( !getuid() ); } else { p.load( m_userPixDir + ".default.face.icon" ); rstuserbutton->setEnabled( false ); } if (!p.isNull()) { userbutton->setPixmap( p.smoothScale( 48, 48, TQImage::ScaleMin ) ); } } void TDMUsersWidget::changeUserPix(const TQString &pix) { TQString user( usercombo->currentText() ); if (user == m_defaultText) { user = ".default"; if (KMessageBox::questionYesNo(this, i18n("Save image as default image?"),TQString::null,KStdGuiItem::save(),KStdGuiItem::cancel()) != KMessageBox::Yes) return; } TQImage p( pix ); if (p.isNull()) { KMessageBox::sorry( this, i18n("There was an error loading the image\n" "%1").arg( pix ) ); return; } p = p.smoothScale( 48, 48, TQImage::ScaleMin ); TQString userpix = m_userPixDir + user + ".face.icon"; if (!p.save( userpix, "PNG" )) KMessageBox::sorry(this, i18n("There was an error saving the image:\n%1") .arg( userpix ) ); else chmod( TQFile::encodeName( userpix ), 0644 ); slotUserSelected(); } void TDMUsersWidget::slotUserButtonClicked() { KFileDialog dlg(m_notFirst ? TQString::null : TDEGlobal::dirs()->resourceDirs("data").last() + "tdm/pics/users", KImageIO::pattern( KImageIO::Reading ), this, 0, true); dlg.setOperationMode( KFileDialog::Opening ); dlg.setCaption( i18n("Choose Image") ); dlg.setMode( KFile::File | KFile::LocalOnly ); KImageFilePreview *ip = new KImageFilePreview( &dlg ); dlg.setPreviewWidget( ip ); if (dlg.exec() != TQDialog::Accepted) return; m_notFirst = true; changeUserPix( dlg.selectedFile() ); } void TDMUsersWidget::slotUnsetUserPix() { TQFile::remove( m_userPixDir + usercombo->currentText() + ".face.icon" ); slotUserSelected(); } bool TDMUsersWidget::eventFilter(TQObject *, TQEvent *e) { if (e->type() == TQEvent::DragEnter) { TQDragEnterEvent *ee = (TQDragEnterEvent *) e; ee->accept( KURLDrag::canDecode(ee) ); return true; } if (e->type() == TQEvent::Drop) { userButtonDropEvent((TQDropEvent *) e); return true; } return false; } KURL *decodeImgDrop(TQDropEvent *e, TQWidget *wdg); void TDMUsersWidget::userButtonDropEvent(TQDropEvent *e) { KURL *url = decodeImgDrop(e, this); if (url) { TQString pixpath; TDEIO::NetAccess::download(*url, pixpath, parentWidget()); changeUserPix( pixpath ); TDEIO::NetAccess::removeTempFile(pixpath); delete url; } } void TDMUsersWidget::save() { config->setGroup( "X-*-Greeter" ); config->writeEntry( "MinShowUID", leminuid->text() ); config->writeEntry( "MaxShowUID", lemaxuid->text() ); config->writeEntry( "UserList", cbshowlist->isChecked() ); config->writeEntry( "UserCompletion", cbcomplete->isChecked() ); config->writeEntry( "ShowUsers", cbinverted->isChecked() ? "NotHidden" : "Selected" ); config->writeEntry( "SortUsers", cbusrsrt->isChecked() ); config->writeEntry( "HiddenUsers", hiddenUsers ); config->writeEntry( "SelectedUsers", selectedUsers ); config->writeEntry( "FaceSource", rbadmonly->isChecked() ? "AdminOnly" : rbprefadm->isChecked() ? "PreferAdmin" : rbprefusr->isChecked() ? "PreferUser" : "UserOnly" ); } void TDMUsersWidget::updateOptList( TQListViewItem *item, TQStringList &list ) { if ( !item ) return; TQCheckListItem *itm = (TQCheckListItem *)item; TQStringList::iterator it = list.find( itm->text() ); if (itm->isOn()) { if (it == list.end()) list.append( itm->text() ); } else { if (it != list.end()) list.remove( it ); } } void TDMUsersWidget::slotUpdateOptIn( TQListViewItem *item ) { updateOptList( item, selectedUsers ); } void TDMUsersWidget::slotUpdateOptOut( TQListViewItem *item ) { updateOptList( item, hiddenUsers ); } void TDMUsersWidget::slotClearUsers() { optinlv->clear(); optoutlv->clear(); usercombo->clear(); usercombo->insertItem( m_defaultText ); } void TDMUsersWidget::slotAddUsers(const TQMap<TQString,int> &users) { TQMapConstIterator<TQString,int> it; for (it = users.begin(); it != users.end(); ++it) { const TQString *name = &it.key(); (new TQCheckListItem(optinlv, *name, TQCheckListItem::CheckBox))-> setOn(selectedUsers.find(*name) != selectedUsers.end()); (new TQCheckListItem(optoutlv, *name, TQCheckListItem::CheckBox))-> setOn(hiddenUsers.find(*name) != hiddenUsers.end()); if ((*name)[0] != '@') usercombo->insertItem(*name); } optinlv->sort(); optoutlv->sort(); if (usercombo->listBox()) usercombo->listBox()->sort(); } void TDMUsersWidget::slotDelUsers(const TQMap<TQString,int> &users) { TQMapConstIterator<TQString,int> it; for (it = users.begin(); it != users.end(); ++it) { const TQString *name = &it.key(); if (usercombo->listBox()) delete usercombo->listBox()->findItem( *name, ExactMatch | CaseSensitive ); delete optinlv->findItem( *name, 0 ); delete optoutlv->findItem( *name, 0 ); } } void TDMUsersWidget::load() { TQString str; config->setGroup("X-*-Greeter"); selectedUsers = config->readListEntry( "SelectedUsers"); hiddenUsers = config->readListEntry( "HiddenUsers"); leminuid->setText(config->readEntry("MinShowUID", defminuid)); lemaxuid->setText(config->readEntry("MaxShowUID", defmaxuid)); cbshowlist->setChecked( config->readBoolEntry( "UserList", true ) ); cbcomplete->setChecked( config->readBoolEntry( "UserCompletion", false ) ); cbinverted->setChecked( config->readEntry( "ShowUsers" ) != "Selected" ); cbusrsrt->setChecked(config->readBoolEntry("SortUsers", true)); TQString ps = config->readEntry( "FaceSource" ); if (ps == TQString::fromLatin1("UserOnly")) rbusronly->setChecked(true); else if (ps == TQString::fromLatin1("PreferUser")) rbprefusr->setChecked(true); else if (ps == TQString::fromLatin1("PreferAdmin")) rbprefadm->setChecked(true); else rbadmonly->setChecked(true); slotUserSelected(); slotShowOpts(); slotFaceOpts(); } void TDMUsersWidget::defaults() { leminuid->setText( defminuid ); lemaxuid->setText( defmaxuid ); cbshowlist->setChecked( true ); cbcomplete->setChecked( false ); cbinverted->setChecked( true ); cbusrsrt->setChecked( true ); rbadmonly->setChecked( true ); hiddenUsers.clear(); selectedUsers.clear(); slotShowOpts(); slotFaceOpts(); } void TDMUsersWidget::slotMinMaxChanged() { emit setMinMaxUID( leminuid->text().toInt(), lemaxuid->text().toInt() ); } void TDMUsersWidget::slotChanged() { emit changed(true); } #include "tdm-users.moc"