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"
26
#include <KIconDialog>
29
#include <KMessageBox>
30
#include <KTemporaryFile>
32
#include <KConfigGroup>
33
#include <KStandardDirs>
34
#include <KStandardGuiItem>
35
#include <KIO/NetAccess>
37
#include <QButtonGroup>
40
#include <QDragEnterEvent>
44
#include <QIntValidator>
46
#include <QPushButton>
47
#include <QRadioButton>
48
#include <QStackedWidget>
50
#include <QTreeWidget>
51
#include <QGridLayout>
52
#include <QHBoxLayout>
53
#include <QVBoxLayout>
55
#include <sys/types.h>
61
extern KConfig *config;
63
extern int handleActionReply(QWidget *parent, const KAuth::ActionReply &reply);
65
static int executeFaceAction(QWidget *parent, const QVariantMap &helperargs)
67
parent->setEnabled(false);
69
KAuth::Action action("org.kde.kcontrol.kcmkdm.managefaces");
70
action.setHelperID("org.kde.kcontrol.kcmkdm");
71
action.setArguments(helperargs);
73
KAuth::ActionReply reply = action.execute();
75
parent->setEnabled(true);
77
return handleActionReply(parent, reply);
80
KDMUsersWidget::KDMUsersWidget(QWidget *parent)
85
if (!stat("/etc/debian_version", &st)) { /* debian */
88
} else if (!stat("/usr/portage", &st)) { /* gentoo */
91
} else if (!stat("/etc/mandrake-release", &st)) { /* mandrake - check before redhat! */
94
} else if (!stat("/etc/redhat-release", &st)) { /* redhat */
97
} else /* if (!stat("/etc/SuSE-release", &st)) */ { /* suse */
106
m_userPixDir = config->group("X-*-Greeter").readEntry("FaceDir",
107
QString(KStandardDirs::installPath("data") + "kdm/faces" + '/'));
109
if (!getpwnam("nobody"))
110
KMessageBox::sorry(this, i18n(
111
"User 'nobody' does not exist. "
112
"Displaying user images will not work in KDM."));
114
m_defaultText = i18n("<placeholder>default</placeholder>");
116
minGroup = new QGroupBox(i18nc(
117
"@title:group UIDs belonging to system users like 'cron'", "System U&IDs"), this);
118
minGroup->setWhatsThis(i18n(
119
"Users with a UID (numerical user identification) outside this range "
120
"will not be listed by KDM and this setup dialog. "
121
"Note that users with the UID 0 (typically root) are not affected by "
122
"this and must be explicitly excluded in \"Inverse selection\" mode."));
123
QSizePolicy sp_ign_fix(QSizePolicy::Ignored, QSizePolicy::Fixed);
124
QValidator *valid = new QIntValidator(0, INT_MAX, minGroup);
125
QLabel *minlab = new QLabel(i18nc("UIDs", "Below:"), minGroup);
126
leminuid = new KLineEdit(minGroup);
127
minlab->setBuddy(leminuid);
128
leminuid->setSizePolicy(sp_ign_fix);
129
leminuid->setValidator(valid);
130
connect(leminuid, SIGNAL(textChanged(const QString &)), SIGNAL(changed()));
131
connect(leminuid, SIGNAL(textChanged(const QString &)), SLOT(slotMinMaxChanged()));
132
QLabel *maxlab = new QLabel(i18nc("UIDs", "Above:"), minGroup);
133
lemaxuid = new KLineEdit(minGroup);
134
maxlab->setBuddy(lemaxuid);
135
lemaxuid->setSizePolicy(sp_ign_fix);
136
lemaxuid->setValidator(valid);
137
connect(lemaxuid, SIGNAL(textChanged(const QString &)), SIGNAL(changed()));
138
connect(lemaxuid, SIGNAL(textChanged(const QString &)), SLOT(slotMinMaxChanged()));
139
QGridLayout *grid = new QGridLayout(minGroup);
140
grid->addWidget(minlab, 0, 0);
141
grid->addWidget(leminuid, 0, 1);
142
grid->addWidget(maxlab, 1, 0);
143
grid->addWidget(lemaxuid, 1, 1);
145
usrGroup = new QGroupBox(i18nc("@title:group", "Users"), this);
146
cbshowlist = new QCheckBox(i18nc("... of users", "Show list"), usrGroup);
147
cbshowlist->setWhatsThis(i18n(
148
"If this option is checked, KDM will show a list of users, so users can "
149
"click on their name or image rather than typing in their login."));
150
cbcomplete = new QCheckBox(i18nc("user ...", "Autocompletion"), usrGroup);
151
cbcomplete->setWhatsThis(i18n(
152
"If this option is checked, KDM will automatically complete "
153
"user names while they are typed in the line edit."));
154
cbinverted = new QCheckBox(i18nc(
155
"@option:check mode of the user selection", "Inverse selection"), usrGroup);
156
cbinverted->setWhatsThis(i18n(
157
"This option specifies how the users for \"Show list\" and \"Autocompletion\" "
158
"are selected in the \"Select users and groups\" list: "
159
"If not checked, select only the checked users. "
160
"If checked, select all non-system users, except the checked ones."));
161
cbusrsrt = new QCheckBox(i18n("Sor&t users"), usrGroup);
162
cbusrsrt->setWhatsThis(i18n(
163
"If this is checked, KDM will alphabetically sort the user list. "
164
"Otherwise users are listed in the order they appear in the password file."));
165
QButtonGroup *buttonGroup = new QButtonGroup(usrGroup);
166
buttonGroup->setExclusive(false);
167
connect(buttonGroup, SIGNAL(buttonClicked(int)), SLOT(slotShowOpts()));
168
connect(buttonGroup, SIGNAL(buttonClicked(int)), SIGNAL(changed()));
169
buttonGroup->addButton(cbshowlist);
170
buttonGroup->addButton(cbcomplete);
171
buttonGroup->addButton(cbinverted);
172
buttonGroup->addButton(cbusrsrt);
173
QBoxLayout *box = new QVBoxLayout(usrGroup);
174
box->addWidget(cbshowlist);
175
box->addWidget(cbcomplete);
176
box->addWidget(cbinverted);
177
box->addWidget(cbusrsrt);
179
wstack = new QStackedWidget(this);
180
s_label = new QLabel(i18n("S&elect users and groups:"), this);
181
s_label->setBuddy(wstack);
182
optinlv = new QTreeWidget(this);
183
optinlv->setRootIsDecorated(false);
184
optinlv->setHeaderLabel(i18n("Selected Users"));
185
optinlv->setWhatsThis(i18n(
186
"KDM will show all checked users. Entries denoted with '@' are user groups. "
187
"Checking a group is like checking all users in that group."));
188
wstack->addWidget(optinlv);
189
connect(optinlv, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
190
SLOT(slotUpdateOptIn(QTreeWidgetItem *)));
191
connect(optinlv, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
193
optoutlv = new QTreeWidget(this);
194
optoutlv->setRootIsDecorated(false);
195
optoutlv->setHeaderLabel(i18n("Excluded Users"));
196
optoutlv->setWhatsThis(i18n(
197
"KDM will show all non-checked non-system users. Entries denoted with '@' "
198
"are user groups. Checking a group is like checking all users in that group."));
199
wstack->addWidget(optoutlv);
200
connect(optoutlv, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
201
SLOT(slotUpdateOptOut(QTreeWidgetItem *)));
202
connect(optoutlv, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
205
faceGroup = new QGroupBox(i18nc(
206
"@title:group source for user faces", "User Image Source"), this);
207
faceGroup->setWhatsThis(i18n(
208
"Here you can specify where KDM will obtain the images that represent users. "
209
"\"System\" represents the global folder; these are the pictures you can set below. "
210
"\"User\" means that KDM should read the user's $HOME/.face.icon file. "
211
"The two selections in the middle define the order of preference if both sources are available."));
212
rbadmonly = new QRadioButton(i18nc("@option:radio image source", "System"), faceGroup);
213
rbprefadm = new QRadioButton(i18nc("@option:radio image source", "System, user"), faceGroup);
214
rbprefusr = new QRadioButton(i18nc("@option:radio image source", "User, system"), faceGroup);
215
rbusronly = new QRadioButton(i18nc("@option:radio image source", "User"), faceGroup);
216
buttonGroup = new QButtonGroup(faceGroup);
217
connect(buttonGroup, SIGNAL(buttonClicked(int)), SLOT(slotFaceOpts()));
218
connect(buttonGroup, SIGNAL(buttonClicked(int)), SIGNAL(changed()));
219
buttonGroup->addButton(rbadmonly);
220
buttonGroup->addButton(rbprefadm);
221
buttonGroup->addButton(rbprefusr);
222
buttonGroup->addButton(rbusronly);
223
box = new QVBoxLayout(faceGroup);
224
box->addWidget(rbadmonly);
225
box->addWidget(rbprefadm);
226
box->addWidget(rbprefusr);
227
box->addWidget(rbusronly);
229
QGroupBox *picGroup = new QGroupBox(i18nc(
230
"@title:group user face assignments", "User Images"), this);
231
usercombo = new KComboBox(picGroup);
232
usercombo->setWhatsThis(i18n("The user the image below belongs to."));
233
connect(usercombo, SIGNAL(activated(int)),
234
SLOT(slotUserSelected()));
235
QLabel *userlabel = new QLabel(i18n("User:"), picGroup);
236
userlabel->setBuddy(usercombo);
237
userbutton = new QPushButton(picGroup);
238
userbutton->setAcceptDrops(true);
239
userbutton->installEventFilter(this); // for drag and drop
240
uint sz = style()->pixelMetric(QStyle::PM_ButtonMargin) * 2 + 48;
241
userbutton->setFixedSize(sz, sz);
242
connect(userbutton, SIGNAL(clicked()),
243
SLOT(slotUserButtonClicked()));
244
userbutton->setToolTip(i18n("Click or drop an image here"));
245
userbutton->setWhatsThis(i18n(
246
"Here you can see the image assigned to the user selected in the combo "
247
"box above. Click on the image button to select from a list of images "
248
"or drag and drop your own image on to the button (e.g. from Konqueror)."));
249
rstuserbutton = new QPushButton(i18nc(
250
"@action:button assign default user face", "R&eset"), picGroup);
251
rstuserbutton->setWhatsThis(i18n(
252
"Click this button to make KDM use the default image for the selected user."));
253
connect(rstuserbutton, SIGNAL(clicked()),
254
SLOT(slotUnsetUserPix()));
255
QGridLayout *hlpl = new QGridLayout(picGroup);
256
hlpl->setSpacing(KDialog::spacingHint());
257
hlpl->addWidget(userlabel, 0, 0);
258
hlpl->addWidget(usercombo, 0, 1); // XXX this makes the layout too wide
259
hlpl->addWidget(userbutton, 1, 0, 1, 2, Qt::AlignHCenter);
260
hlpl->addWidget(rstuserbutton, 2, 0, 1, 2, Qt::AlignHCenter);
262
QHBoxLayout *main = new QHBoxLayout(this);
263
main->setSpacing(10);
265
QVBoxLayout *lLayout = new QVBoxLayout();
266
main->addItem(lLayout);
267
lLayout->setSpacing(10);
268
lLayout->addWidget(minGroup);
269
lLayout->addWidget(usrGroup);
270
lLayout->addStretch(1);
272
QVBoxLayout *mLayout = new QVBoxLayout();
273
main->addItem(mLayout);
274
mLayout->setSpacing(10);
275
mLayout->addWidget(s_label);
276
mLayout->addWidget(wstack);
277
mLayout->setStretchFactor(wstack, 1);
278
main->setStretchFactor(mLayout, 1);
280
QVBoxLayout *rLayout = new QVBoxLayout();
281
main->addItem(rLayout);
282
rLayout->setSpacing(10);
283
rLayout->addWidget(faceGroup);
284
rLayout->addWidget(picGroup);
285
rLayout->addStretch(1);
288
void KDMUsersWidget::slotShowOpts()
290
bool en = cbshowlist->isChecked() || cbcomplete->isChecked();
291
cbinverted->setEnabled(en);
292
cbusrsrt->setEnabled(en);
293
wstack->setEnabled(en);
294
wstack->setCurrentWidget(cbinverted->isChecked() ? optoutlv : optinlv);
295
en = cbshowlist->isChecked();
296
faceGroup->setEnabled(en);
298
usercombo->setEnabled(false);
299
userbutton->setEnabled(false);
300
rstuserbutton->setEnabled(false);
305
void KDMUsersWidget::slotFaceOpts()
307
bool en = !rbusronly->isChecked();
308
usercombo->setEnabled(en);
309
userbutton->setEnabled(en);
313
rstuserbutton->setEnabled(false);
316
void KDMUsersWidget::slotUserSelected()
318
QString user = usercombo->currentText();
320
if (user != m_defaultText && p.load(m_userPixDir + user + ".face.icon"))
321
rstuserbutton->setEnabled(true);
323
p.load(m_userPixDir + ".default.face.icon");
324
rstuserbutton->setEnabled(false);
326
userbutton->setIcon(QPixmap::fromImage(p.scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
330
void KDMUsersWidget::checkFacesDir()
332
QDir testDir(m_userPixDir);
333
if (!testDir.exists()) {
334
QVariantMap helperargs;
335
helperargs["subaction"] = Helper::CreateFacesDir;
337
if (executeFaceAction(parentWidget(), helperargs))
338
KMessageBox::sorry(this,
339
i18n("Unable to create folder %1", testDir.absolutePath()));
343
void KDMUsersWidget::changeUserPix(const QString &pix)
345
QString user(usercombo->currentText());
349
if (user == m_defaultText) {
351
if (KMessageBox::questionYesNo(this, i18n("Save image as default?"),
352
QString(), KStandardGuiItem::save(),
353
KStandardGuiItem::cancel()) != KMessageBox::Yes)
359
KMessageBox::sorry(this,
360
i18n("There was an error while loading the image\n%1", pix));
364
p = p.scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation);
366
KTemporaryFile sourceFile;
368
QString source = sourceFile.fileName();
369
p.save(source, "PNG");
370
QFile::setPermissions(source, sourceFile.permissions() | QFile::ReadOther);
372
QVariantMap helperargs;
373
helperargs["subaction"] = Helper::InstallFace;
374
helperargs["user"] = user;
375
helperargs["sourcefile"] = source;
377
if (executeFaceAction(parentWidget(), helperargs))
378
KMessageBox::error(this,
379
i18n("There was an error while saving the image:\n%1",
380
m_userPixDir + user + ".face.icon"));
385
void KDMUsersWidget::slotUserButtonClicked()
388
dlg.setCustomLocation(KStandardDirs::installPath("data") + "kdm/pics/users");
389
dlg.setup(KIconLoader::NoGroup, KIconLoader::Any, false, 48, true, true, false);
390
QString ic = dlg.openDialog();
396
void KDMUsersWidget::slotUnsetUserPix()
398
QString user(usercombo->currentText());
402
QVariantMap helperargs;
403
helperargs["subaction"] = Helper::RemoveFace;
404
helperargs["user"] = user;
406
if (executeFaceAction(parentWidget(), helperargs))
407
KMessageBox::error(this,
408
i18n("There was an error while removing the image:\n%1",
409
m_userPixDir + user + ".face.icon"));
414
bool KDMUsersWidget::eventFilter(QObject *, QEvent *e)
416
if (e->type() == QEvent::DragEnter) {
417
QDragEnterEvent *ee = (QDragEnterEvent *)e;
418
ee->setAccepted(KUrl::List::canDecode(ee->mimeData()));
422
if (e->type() == QEvent::Drop) {
423
userButtonDropEvent((QDropEvent *)e);
430
KUrl *decodeImgDrop(QDropEvent *e, QWidget *wdg);
432
void KDMUsersWidget::userButtonDropEvent(QDropEvent *e)
434
KUrl *url = decodeImgDrop(e, this);
437
KIO::NetAccess::download(*url, pixpath, parentWidget());
438
changeUserPix(pixpath);
439
KIO::NetAccess::removeTempFile(pixpath);
444
void KDMUsersWidget::save()
446
KConfigGroup configGrp = config->group("X-*-Greeter");
448
configGrp.writeEntry("MinShowUID", leminuid->text());
449
configGrp.writeEntry("MaxShowUID", lemaxuid->text());
451
configGrp.writeEntry("UserList", cbshowlist->isChecked());
452
configGrp.writeEntry("UserCompletion", cbcomplete->isChecked());
453
configGrp.writeEntry("ShowUsers",
454
cbinverted->isChecked() ? "NotHidden" : "Selected");
455
configGrp.writeEntry("SortUsers", cbusrsrt->isChecked());
457
configGrp.writeEntry("HiddenUsers", hiddenUsers);
458
configGrp.writeEntry("SelectedUsers", selectedUsers);
460
configGrp.writeEntry("FaceSource",
461
rbadmonly->isChecked() ? "AdminOnly" :
462
rbprefadm->isChecked() ? "PreferAdmin" :
463
rbprefusr->isChecked() ? "PreferUser" : "UserOnly");
467
void KDMUsersWidget::updateOptList(QTreeWidgetItem *item, QStringList &list)
471
int ind = list.indexOf(item->text(0));
472
if (item->checkState(0) == Qt::Checked) {
474
list.append(item->text(0));
481
void KDMUsersWidget::slotUpdateOptIn(QTreeWidgetItem *item)
483
updateOptList(item, selectedUsers);
486
void KDMUsersWidget::slotUpdateOptOut(QTreeWidgetItem *item)
488
updateOptList(item, hiddenUsers);
491
void KDMUsersWidget::slotClearUsers()
496
usercombo->addItem(m_defaultText);
499
void KDMUsersWidget::slotAddUsers(const QMap<QString, int> &users)
501
QMap<QString, int>::const_iterator it;
502
for (it = users.begin(); it != users.end(); ++it) {
503
const QString *name = &it.key();
504
(new QTreeWidgetItem(optinlv, QStringList() << *name))->
505
setCheckState(0, selectedUsers.contains(*name) ? Qt::Checked : Qt::Unchecked);
506
(new QTreeWidgetItem(optoutlv, QStringList() << *name))->
507
setCheckState(0, hiddenUsers.contains(*name) ? Qt::Checked : Qt::Unchecked);
508
if ((*name)[0] != '@')
509
usercombo->addItem(*name);
511
optinlv->sortItems(0, Qt::AscendingOrder);
512
optoutlv->sortItems(0, Qt::AscendingOrder);
513
usercombo->model()->sort(0);
517
void KDMUsersWidget::slotDelUsers(const QMap<QString, int> &users)
519
QMap<QString, int>::const_iterator it;
520
for (it = users.begin(); it != users.end(); ++it) {
521
const QString *name = &it.key();
522
int idx = usercombo->findText(*name);
524
usercombo->removeItem(idx);
525
qDeleteAll(optinlv->findItems(*name, Qt::MatchExactly | Qt::MatchCaseSensitive));
526
qDeleteAll(optoutlv->findItems(*name, Qt::MatchExactly | Qt::MatchCaseSensitive));
530
void KDMUsersWidget::load()
534
KConfigGroup configGrp = config->group("X-*-Greeter");
536
selectedUsers = configGrp.readEntry("SelectedUsers", QStringList());
537
hiddenUsers = configGrp.readEntry("HiddenUsers", QStringList());
539
leminuid->setText(configGrp.readEntry("MinShowUID", defminuid));
540
lemaxuid->setText(configGrp.readEntry("MaxShowUID", defmaxuid));
542
cbshowlist->setChecked(configGrp.readEntry("UserList", true));
543
cbcomplete->setChecked(configGrp.readEntry("UserCompletion", false));
544
cbinverted->setChecked(configGrp.readEntry("ShowUsers") != "Selected");
545
cbusrsrt->setChecked(configGrp.readEntry("SortUsers", true));
547
QString ps = configGrp.readEntry("FaceSource");
548
if (ps == QLatin1String("UserOnly"))
549
rbusronly->setChecked(true);
550
else if (ps == QLatin1String("PreferUser"))
551
rbprefusr->setChecked(true);
552
else if (ps == QLatin1String("PreferAdmin"))
553
rbprefadm->setChecked(true);
555
rbadmonly->setChecked(true);
563
void KDMUsersWidget::defaults()
565
leminuid->setText(defminuid);
566
lemaxuid->setText(defmaxuid);
567
cbshowlist->setChecked(true);
568
cbcomplete->setChecked(false);
569
cbinverted->setChecked(true);
570
cbusrsrt->setChecked(true);
571
rbadmonly->setChecked(true);
573
selectedUsers.clear();
578
void KDMUsersWidget::slotMinMaxChanged()
580
emit setMinMaxUID(leminuid->text().toInt(), lemaxuid->text().toInt());
583
#include "kdm-users.moc"