add more spacing
[personal-kdebase.git] / workspace / kcontrol / kdm / kdm-users.cpp
blob181a834c11094248451051f5e7dd043ebd768f9f
1 /*
2 Copyright (C) 1997 Thomas Tanghus (tanghus@earthling.net)
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "kdm-users.h"
22 #include <K3ListView>
23 #include <K3URLDrag>
24 #include <KComboBox>
25 #include <KGlobal>
26 #include <KIconDialog>
27 #include <KImageFilePreview>
28 #include <KImageIO>
29 #include <KIO/NetAccess>
30 #include <KLineEdit>
31 #include <KLocale>
32 #include <KMessageBox>
33 #include <KConfig>
34 #include <KConfigGroup>
35 #include <KStandardDirs>
36 #include <KStandardGuiItem>
38 #include <QButtonGroup>
39 #include <QCheckBox>
40 #include <QDir>
41 #include <QDragEnterEvent>
42 #include <QEvent>
43 #include <QFile>
44 #include <QGroupBox>
45 #include <QIntValidator>
46 #include <QLabel>
47 #include <QPushButton>
48 #include <QRadioButton>
49 #include <QStackedWidget>
50 #include <QStyle>
51 #include <QGridLayout>
52 #include <QHBoxLayout>
53 #include <QVBoxLayout>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <unistd.h>
58 #include <pwd.h>
60 extern KConfig *config;
62 KDMUsersWidget::KDMUsersWidget( QWidget *parent )
63 : QWidget( parent ), m_readOnly( false )
65 #ifdef __linux__
66 struct stat st;
67 if (!stat( "/etc/debian_version", &st )) { /* debian */
68 defminuid = "1000";
69 defmaxuid = "29999";
70 } else if (!stat( "/usr/portage", &st )) { /* gentoo */
71 defminuid = "1000";
72 defmaxuid = "65000";
73 } else if (!stat( "/etc/mandrake-release", &st )) { /* mandrake - check before redhat! */
74 defminuid = "500";
75 defmaxuid = "65000";
76 } else if (!stat( "/etc/redhat-release", &st )) { /* redhat */
77 defminuid = "100";
78 defmaxuid = "65000";
79 } else /* if (!stat( "/etc/SuSE-release", &st )) */ { /* suse */
80 defminuid = "500";
81 defmaxuid = "65000";
83 #else
84 defminuid = "1000";
85 defmaxuid = "65000";
86 #endif
88 // We assume that $kde_datadir/kdm exists, but better check for pics/ and pics/users,
89 // and create them if necessary.
90 m_userPixDir = config->group( "X-*-Greeter" ).readEntry( "FaceDir", KStandardDirs::installPath( "data" ) + "kdm/faces" ) + '/';
91 QDir testDir( m_userPixDir );
92 if (!testDir.exists() && !testDir.mkdir( testDir.absolutePath() ) && !geteuid())
93 KMessageBox::sorry( this, i18n("Unable to create folder %1", testDir.absolutePath() ) );
94 if (!getpwnam( "nobody" ) && !geteuid())
95 KMessageBox::sorry( this, i18n(
96 "User 'nobody' does not exist. "
97 "Displaying user images will not work in KDM.") );
99 m_defaultText = i18n("<placeholder>default</placeholder>");
101 QString wtstr;
103 minGroup = new QGroupBox( i18nc(
104 "@title:group UIDs belonging to system users like 'cron'", "System U&IDs"), this );
105 minGroup->setWhatsThis( i18n(
106 "Users with a UID (numerical user identification) outside this range "
107 "will not be listed by KDM and this setup dialog. "
108 "Note that users with the UID 0 (typically root) are not affected by "
109 "this and must be explicitly excluded in \"Inverse selection\" mode.") );
110 QSizePolicy sp_ign_fix( QSizePolicy::Ignored, QSizePolicy::Fixed );
111 QValidator *valid = new QIntValidator( 0, 999999, minGroup );
112 QLabel *minlab = new QLabel( i18nc("UIDs", "Below:"), minGroup );
113 leminuid = new KLineEdit( minGroup );
114 minlab->setBuddy( leminuid );
115 leminuid->setSizePolicy( sp_ign_fix );
116 leminuid->setValidator( valid );
117 connect( leminuid, SIGNAL(textChanged( const QString & )), SIGNAL(changed()) );
118 connect( leminuid, SIGNAL(textChanged( const QString & )), SLOT(slotMinMaxChanged()) );
119 QLabel *maxlab = new QLabel( i18nc("UIDs", "Above:"), minGroup );
120 lemaxuid = new KLineEdit( minGroup );
121 maxlab->setBuddy( lemaxuid );
122 lemaxuid->setSizePolicy( sp_ign_fix );
123 lemaxuid->setValidator( valid );
124 connect( lemaxuid, SIGNAL(textChanged( const QString & )), SIGNAL(changed()) );
125 connect( lemaxuid, SIGNAL(textChanged( const QString & )), SLOT(slotMinMaxChanged()) );
126 QGridLayout *grid = new QGridLayout( minGroup );
127 grid->addWidget( minlab, 0, 0 );
128 grid->addWidget( leminuid, 0, 1 );
129 grid->addWidget( maxlab, 1, 0 );
130 grid->addWidget( lemaxuid, 1, 1 );
132 usrGroup = new QGroupBox( i18nc("@title:group", "Users"), this );
133 cbshowlist = new QCheckBox( i18nc("... of users", "Show list"), usrGroup );
134 cbshowlist->setWhatsThis( i18n(
135 "If this option is checked, KDM will show a list of users, so users can "
136 "click on their name or image rather than typing in their login.") );
137 cbcomplete = new QCheckBox( i18nc("user ...", "Autocompletion"), usrGroup );
138 cbcomplete->setWhatsThis( i18n(
139 "If this option is checked, KDM will automatically complete "
140 "user names while they are typed in the line edit.") );
141 cbinverted = new QCheckBox( i18nc(
142 "@option:check mode of the user selection", "Inverse selection"), usrGroup );
143 cbinverted->setWhatsThis( i18n(
144 "This option specifies how the users for \"Show list\" and \"Autocompletion\" "
145 "are selected in the \"Select users and groups\" list: "
146 "If not checked, select only the checked users. "
147 "If checked, select all non-system users, except the checked ones.") );
148 cbusrsrt = new QCheckBox( i18n("Sor&t users"), usrGroup );
149 cbusrsrt->setWhatsThis( i18n(
150 "If this is checked, KDM will alphabetically sort the user list. "
151 "Otherwise users are listed in the order they appear in the password file.") );
152 QButtonGroup *buttonGroup = new QButtonGroup( usrGroup );
153 buttonGroup->setExclusive( false );
154 connect( buttonGroup, SIGNAL(buttonClicked( int )), SLOT(slotShowOpts()) );
155 connect( buttonGroup, SIGNAL(buttonClicked( int )), SIGNAL(changed()) );
156 buttonGroup->addButton( cbshowlist );
157 buttonGroup->addButton( cbcomplete );
158 buttonGroup->addButton( cbinverted );
159 buttonGroup->addButton( cbusrsrt );
160 QBoxLayout *box = new QVBoxLayout( usrGroup );
161 box->addWidget( cbshowlist );
162 box->addWidget( cbcomplete );
163 box->addWidget( cbinverted );
164 box->addWidget( cbusrsrt );
166 wstack = new QStackedWidget( this );
167 s_label = new QLabel( i18n("S&elect users and groups:"), this );
168 s_label->setBuddy( wstack );
169 optinlv = new K3ListView( this );
170 optinlv->addColumn( i18n("Selected Users") );
171 optinlv->setResizeMode( Q3ListView::LastColumn );
172 optinlv->setWhatsThis( i18n(
173 "KDM will show all checked users. Entries denoted with '@' are user groups. "
174 "Checking a group is like checking all users in that group.") );
175 wstack->addWidget( optinlv );
176 connect( optinlv, SIGNAL(clicked( Q3ListViewItem * )),
177 SLOT(slotUpdateOptIn( Q3ListViewItem * )) );
178 connect( optinlv, SIGNAL(clicked( Q3ListViewItem * )),
179 SIGNAL(changed()) );
180 optoutlv = new K3ListView( this );
181 optoutlv->addColumn( i18n("Excluded Users") );
182 optoutlv->setResizeMode( Q3ListView::LastColumn );
183 optoutlv->setWhatsThis( i18n(
184 "KDM will show all non-checked non-system users. Entries denoted with '@' "
185 "are user groups. Checking a group is like checking all users in that group.") );
186 wstack->addWidget( optoutlv );
187 connect( optoutlv, SIGNAL(clicked( Q3ListViewItem * )),
188 SLOT(slotUpdateOptOut( Q3ListViewItem * )) );
189 connect( optoutlv, SIGNAL(clicked( Q3ListViewItem * )),
190 SIGNAL(changed()) );
192 faceGroup = new QGroupBox( i18nc(
193 "@title:group source for user faces", "User Image Source"), this );
194 faceGroup->setWhatsThis( i18n(
195 "Here you can specify where KDM will obtain the images that represent users. "
196 "\"System\" represents the global folder; these are the pictures you can set below. "
197 "\"User\" means that KDM should read the user's $HOME/.face.icon file. "
198 "The two selections in the middle define the order of preference if both sources are available.") );
199 rbadmonly = new QRadioButton( i18nc("@option:radio image source", "System"), faceGroup );
200 rbprefadm = new QRadioButton( i18nc("@option:radio image source", "System, user"), faceGroup );
201 rbprefusr = new QRadioButton( i18nc("@option:radio image source", "User, system"), faceGroup );
202 rbusronly = new QRadioButton( i18nc("@option:radio image source", "User"), faceGroup );
203 buttonGroup = new QButtonGroup( faceGroup );
204 connect( buttonGroup, SIGNAL(buttonClicked( int )), SLOT(slotFaceOpts()) );
205 connect( buttonGroup, SIGNAL(buttonClicked( int )), SIGNAL(changed()) );
206 buttonGroup->addButton( rbadmonly );
207 buttonGroup->addButton( rbprefadm );
208 buttonGroup->addButton( rbprefusr );
209 buttonGroup->addButton( rbusronly );
210 box = new QVBoxLayout( faceGroup );
211 box->addWidget( rbadmonly );
212 box->addWidget( rbprefadm );
213 box->addWidget( rbprefusr );
214 box->addWidget( rbusronly );
216 QGroupBox *picGroup = new QGroupBox( i18nc(
217 "@title:group user face assignments", "User Images"), this );
218 usercombo = new KComboBox( picGroup );
219 usercombo->setWhatsThis( i18n("The user the image below belongs to.") );
220 connect( usercombo, SIGNAL(activated( int )),
221 SLOT(slotUserSelected()) );
222 QLabel *userlabel = new QLabel( i18n("User:"), picGroup );
223 userlabel->setBuddy( usercombo );
224 userbutton = new QPushButton( picGroup );
225 userbutton->setAcceptDrops( true );
226 userbutton->installEventFilter( this ); // for drag and drop
227 uint sz = style()->pixelMetric( QStyle::PM_ButtonMargin ) * 2 + 48;
228 userbutton->setFixedSize( sz, sz );
229 connect( userbutton, SIGNAL(clicked()),
230 SLOT(slotUserButtonClicked()) );
231 userbutton->setToolTip( i18n("Click or drop an image here") );
232 userbutton->setWhatsThis( i18n(
233 "Here you can see the image assigned to the user selected in the combo "
234 "box above. Click on the image button to select from a list of images "
235 "or drag and drop your own image on to the button (e.g. from Konqueror).") );
236 rstuserbutton = new QPushButton( i18nc(
237 "@action:button assign default user face", "R&eset"), picGroup );
238 rstuserbutton->setWhatsThis( i18n(
239 "Click this button to make KDM use the default image for the selected user.") );
240 connect( rstuserbutton, SIGNAL(clicked()),
241 SLOT(slotUnsetUserPix()) );
242 QGridLayout *hlpl = new QGridLayout( picGroup );
243 hlpl->setSpacing( KDialog::spacingHint() );
244 hlpl->addWidget( userlabel, 0, 0 );
245 hlpl->addWidget( usercombo, 0, 1 ); // XXX this makes the layout too wide
246 hlpl->addWidget( userbutton, 1, 0, 1, 2, Qt::AlignHCenter );
247 hlpl->addWidget( rstuserbutton, 2, 0, 1, 2, Qt::AlignHCenter );
249 QHBoxLayout *main = new QHBoxLayout( this );
250 main->setSpacing( 10 );
252 QVBoxLayout *lLayout = new QVBoxLayout();
253 main->addItem( lLayout );
254 lLayout->setSpacing( 10 );
255 lLayout->addWidget( minGroup );
256 lLayout->addWidget( usrGroup );
257 lLayout->addStretch( 1 );
259 QVBoxLayout *mLayout = new QVBoxLayout();
260 main->addItem( mLayout );
261 mLayout->setSpacing( 10 );
262 mLayout->addWidget( s_label );
263 mLayout->addWidget( wstack );
264 mLayout->setStretchFactor( wstack, 1 );
265 main->setStretchFactor( mLayout, 1 );
267 QVBoxLayout *rLayout = new QVBoxLayout();
268 main->addItem( rLayout );
269 rLayout->setSpacing( 10 );
270 rLayout->addWidget( faceGroup );
271 rLayout->addWidget( picGroup );
272 rLayout->addStretch( 1 );
276 void KDMUsersWidget::makeReadOnly()
278 m_readOnly = true;
279 leminuid->setReadOnly( true );
280 lemaxuid->setReadOnly( true );
281 cbshowlist->setEnabled( false );
282 cbcomplete->setEnabled( false );
283 cbinverted->setEnabled( false );
284 cbusrsrt->setEnabled( false );
285 rbadmonly->setEnabled( false );
286 rbprefadm->setEnabled( false );
287 rbprefusr->setEnabled( false );
288 rbusronly->setEnabled( false );
289 wstack->setEnabled( false );
290 disconnect( userbutton, SIGNAL(clicked()), this, SLOT(slotUserButtonClicked()) );
291 userbutton->setAcceptDrops( false );
292 rstuserbutton->setEnabled( false );
295 void KDMUsersWidget::slotShowOpts()
297 bool en = !m_readOnly && ( cbshowlist->isChecked() || cbcomplete->isChecked() );
298 cbinverted->setEnabled( en );
299 cbusrsrt->setEnabled( en );
300 wstack->setEnabled( en );
301 wstack->setCurrentWidget( cbinverted->isChecked() ? optoutlv : optinlv );
302 en = cbshowlist->isChecked();
303 faceGroup->setEnabled( en );
304 if (!en) {
305 usercombo->setEnabled( false );
306 userbutton->setEnabled( false );
307 rstuserbutton->setEnabled( false );
308 } else
309 slotFaceOpts();
312 void KDMUsersWidget::slotFaceOpts()
314 bool en = !m_readOnly && !rbusronly->isChecked();
315 usercombo->setEnabled( en );
316 userbutton->setEnabled( en );
317 if (en)
318 slotUserSelected();
319 else
320 rstuserbutton->setEnabled( false );
323 void KDMUsersWidget::slotUserSelected()
325 QString user = usercombo->currentText();
326 QImage p;
327 if (user != m_defaultText && p.load( m_userPixDir + user + ".face.icon" ))
328 rstuserbutton->setEnabled( !getuid() );
329 else {
330 p.load( m_userPixDir + ".default.face.icon" );
331 rstuserbutton->setEnabled( false );
333 userbutton->setIcon( QPixmap::fromImage( p.scaled( 48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation ) ) );
337 void KDMUsersWidget::changeUserPix( const QString &pix )
339 QString user( usercombo->currentText() );
340 if (user == m_defaultText) {
341 user = ".default";
342 if (KMessageBox::questionYesNo( this, i18n("Save image as default?"),
343 QString(), KStandardGuiItem::save(),
344 KStandardGuiItem::cancel() ) != KMessageBox::Yes)
345 return;
348 QImage p( pix );
349 if (p.isNull()) {
350 KMessageBox::sorry( this,
351 i18n("There was an error loading the image\n%1", pix ) );
352 return;
355 p = p.scaled( 48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation );
356 QString userpix = m_userPixDir + user + ".face.icon";
357 if (!p.save( userpix, "PNG" ))
358 KMessageBox::sorry( this,
359 i18n("There was an error saving the image:\n%1", userpix ) );
361 slotUserSelected();
364 void KDMUsersWidget::slotUserButtonClicked()
366 KIconDialog dlg;
367 dlg.setCustomLocation( KStandardDirs::installPath( "data" ) + "kdm/pics/users" );
368 dlg.setup( KIconLoader::NoGroup, KIconLoader::Any, false, 48, true, true, false );
369 QString ic = dlg.openDialog();
370 if (ic.isEmpty())
371 return;
372 changeUserPix( ic );
375 void KDMUsersWidget::slotUnsetUserPix()
377 QFile::remove( m_userPixDir + usercombo->currentText() + ".face.icon" );
378 slotUserSelected();
381 bool KDMUsersWidget::eventFilter( QObject *, QEvent *e )
383 if (e->type() == QEvent::DragEnter) {
384 QDragEnterEvent *ee = (QDragEnterEvent *)e;
385 ee->setAccepted( K3URLDrag::canDecode( ee ) );
386 return true;
389 if (e->type() == QEvent::Drop) {
390 userButtonDropEvent( (QDropEvent *)e );
391 return true;
394 return false;
397 KUrl *decodeImgDrop( QDropEvent *e, QWidget *wdg );
399 void KDMUsersWidget::userButtonDropEvent( QDropEvent *e )
401 KUrl *url = decodeImgDrop( e, this );
402 if (url) {
403 QString pixpath;
404 KIO::NetAccess::download( *url, pixpath, parentWidget() );
405 changeUserPix( pixpath );
406 KIO::NetAccess::removeTempFile( pixpath );
407 delete url;
411 void KDMUsersWidget::save()
413 KConfigGroup configGrp = config->group( "X-*-Greeter" );
415 configGrp.writeEntry( "MinShowUID", leminuid->text() );
416 configGrp.writeEntry( "MaxShowUID", lemaxuid->text() );
418 configGrp.writeEntry( "UserList", cbshowlist->isChecked() );
419 configGrp.writeEntry( "UserCompletion", cbcomplete->isChecked() );
420 configGrp.writeEntry( "ShowUsers",
421 cbinverted->isChecked() ? "NotHidden" : "Selected" );
422 configGrp.writeEntry( "SortUsers", cbusrsrt->isChecked() );
424 configGrp.writeEntry( "HiddenUsers", hiddenUsers );
425 configGrp.writeEntry( "SelectedUsers", selectedUsers );
427 configGrp.writeEntry( "FaceSource",
428 rbadmonly->isChecked() ? "AdminOnly" :
429 rbprefadm->isChecked() ? "PreferAdmin" :
430 rbprefusr->isChecked() ? "PreferUser" : "UserOnly" );
434 void KDMUsersWidget::updateOptList( Q3ListViewItem *item, QStringList &list )
436 if (!item)
437 return;
438 Q3CheckListItem *itm = (Q3CheckListItem *)item;
439 int ind = list.indexOf( itm->text() );
440 if (itm->isOn()) {
441 if (ind < 0)
442 list.append( itm->text() );
443 } else {
444 if (ind >= 0)
445 list.removeAt( ind );
449 void KDMUsersWidget::slotUpdateOptIn( Q3ListViewItem *item )
451 updateOptList( item, selectedUsers );
454 void KDMUsersWidget::slotUpdateOptOut( Q3ListViewItem *item )
456 updateOptList( item, hiddenUsers );
459 void KDMUsersWidget::slotClearUsers()
461 optinlv->clear();
462 optoutlv->clear();
463 usercombo->clear();
464 usercombo->addItem( m_defaultText );
467 void KDMUsersWidget::slotAddUsers( const QMap<QString,int> &users )
469 QMap<QString,int>::const_iterator it;
470 for (it = users.begin(); it != users.end(); ++it) {
471 const QString *name = &it.key();
472 (new Q3CheckListItem( optinlv, *name, Q3CheckListItem::CheckBox ))->
473 setOn( selectedUsers.contains( *name ) );
474 (new Q3CheckListItem( optoutlv, *name, Q3CheckListItem::CheckBox ))->
475 setOn( hiddenUsers.contains( *name ) );
476 if ((*name)[0] != '@')
477 usercombo->addItem( *name );
479 optinlv->sort();
480 optoutlv->sort();
481 usercombo->model()->sort( 0 );
482 slotUserSelected();
485 void KDMUsersWidget::slotDelUsers( const QMap<QString,int> &users )
487 QMap<QString,int>::const_iterator it;
488 for (it = users.begin(); it != users.end(); ++it) {
489 const QString *name = &it.key();
490 int idx = usercombo->findText( *name );
491 if (idx != -1)
492 usercombo->removeItem( idx );
493 delete optinlv->findItem( *name, 0 );
494 delete optoutlv->findItem( *name, 0 );
498 void KDMUsersWidget::load()
500 QString str;
502 KConfigGroup configGrp = config->group( "X-*-Greeter" );
504 selectedUsers = configGrp.readEntry( "SelectedUsers", QStringList() );
505 hiddenUsers = configGrp.readEntry( "HiddenUsers", QStringList() );
507 leminuid->setText( configGrp.readEntry( "MinShowUID", defminuid ) );
508 lemaxuid->setText( configGrp.readEntry( "MaxShowUID", defmaxuid ) );
510 cbshowlist->setChecked( configGrp.readEntry( "UserList", true ) );
511 cbcomplete->setChecked( configGrp.readEntry( "UserCompletion", false ) );
512 cbinverted->setChecked( configGrp.readEntry( "ShowUsers" ) != "Selected" );
513 cbusrsrt->setChecked( configGrp.readEntry( "SortUsers", true ) );
515 QString ps = configGrp.readEntry( "FaceSource" );
516 if (ps == QLatin1String("UserOnly"))
517 rbusronly->setChecked( true );
518 else if (ps == QLatin1String("PreferUser"))
519 rbprefusr->setChecked( true );
520 else if (ps == QLatin1String("PreferAdmin"))
521 rbprefadm->setChecked( true );
522 else
523 rbadmonly->setChecked( true );
525 slotUserSelected();
527 slotShowOpts();
528 slotFaceOpts();
531 void KDMUsersWidget::defaults()
533 leminuid->setText( defminuid );
534 lemaxuid->setText( defmaxuid );
535 cbshowlist->setChecked( true );
536 cbcomplete->setChecked( false );
537 cbinverted->setChecked( true );
538 cbusrsrt->setChecked( true );
539 rbadmonly->setChecked( true );
540 hiddenUsers.clear();
541 selectedUsers.clear();
542 slotShowOpts();
543 slotFaceOpts();
546 void KDMUsersWidget::slotMinMaxChanged()
548 emit setMinMaxUID( leminuid->text().toInt(), lemaxuid->text().toInt() );
551 #include "kdm-users.moc"