diff --git a/kdm/kfrontend/CMakeLists.txt b/kdm/kfrontend/CMakeLists.txt index 829aa21..6b508df 100644 --- a/kdm/kfrontend/CMakeLists.txt +++ b/kdm/kfrontend/CMakeLists.txt @@ -7,9 +7,9 @@ add_subdirectory( sessions ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../kcm/background ${KDEBASE_WORKSPACE_SOURCE_DIR}/libs/kdm + ${KDEBASE_WORKSPACE_SOURCE_DIR}/kcontrol/keyboard ${QIMAGEBLITZ_INCLUDES} ) - set(kdmthemer_SRCS themer/kdmthemer.cpp themer/kdmthemer.h @@ -30,6 +30,10 @@ set(kdmthemer_SRCS themer/parse.cpp themer/parse.h ) +set(x11helper_SRCS + ${KDEBASE_WORKSPACE_SOURCE_DIR}/kcontrol/keyboard/x11_helper.cpp + ${KDEBASE_WORKSPACE_SOURCE_DIR}/kcontrol/keyboard/x11_helper.h +) set(kdm_greet_SRCS kdm_greet.c kdm_greet.h @@ -52,6 +56,7 @@ set(kdm_greet_SRCS utils.cpp utils.h ${kdmthemer_SRCS} + ${x11helper_SRCS} ) if (XDMCP) diff --git a/kdm/kfrontend/kgreeter.cpp b/kdm/kfrontend/kgreeter.cpp index 3a595cb..0fa0faa 100644 --- a/kdm/kfrontend/kgreeter.cpp +++ b/kdm/kfrontend/kgreeter.cpp @@ -4,6 +4,7 @@ Greeter widget for kdm Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org> Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org> +Copyright (C) 2011 ROSA Lab. <ural.mullabaev@rosalab.ru> This program is free software; you can redistribute it and/or modify @@ -27,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "kdmconfig.h" #include "kdmclock.h" #include "kdm_greet.h" +#include "kdmshutdown.h" #include "themer/kdmthemer.h" #include "themer/kdmitem.h" #include "themer/kdmlabel.h" @@ -38,6 +40,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include <kstandarddirs.h> #include <kstringhandler.h> +#include <QApplication> #include <QAction> #include <QBuffer> #include <QDir> @@ -50,9 +53,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include <QMenu> #include <QMovie> #include <QPainter> +#include <QLineEdit> #include <QPushButton> #include <QShortcut> #include <QStyle> +#include <QTextLayout> +#include <QParallelAnimationGroup> +#include <QX11Info> #include <sys/types.h> #include <sys/stat.h> @@ -66,112 +73,864 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include <X11/Xlib.h> #include <fixx11h.h> -class UserListView : public QListWidget { - public: - UserListView(QWidget *parent = 0) - : QListWidget(parent) - , cachedSizeHint(-1, 0) - { - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Ignored); - setUniformItemSizes(true); - setVerticalScrollMode(ScrollPerPixel); - setIconSize(QSize(48, 48)); - setAlternatingRowColors(true); +#define LIST_IMAGE_SIZE 48 +#define SLIDER_IMAGE_SIZE 150 +#define SELECTED_IMAGE_RATIO 1.5 + +UserListView::UserListView(QWidget *parent) + : QListWidget(parent) + , cachedSizeHint(-1, 0) +{ + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Ignored); + setUniformItemSizes(true); + setVerticalScrollMode(ScrollPerPixel); + setIconSize(QSize(LIST_IMAGE_SIZE, LIST_IMAGE_SIZE)); + setAlternatingRowColors(true); +} + +QSize +UserListView::sizeHint() const +{ + if (!cachedSizeHint.isValid()) { + ensurePolished(); + QStyleOptionViewItem vo(viewOptions()); + QAbstractListModel *md(static_cast<QAbstractListModel *>(model())); + uint maxw = 0, h = 0; + for (int i = 0, rc = md->rowCount(); i < rc; i++) { + QSize sh = itemDelegate()->sizeHint(vo, md->index(i)); + uint thisw = sh.width(); + if (thisw > maxw) + maxw = thisw; + h += sh.height(); + } + cachedSizeHint.setWidth( + style()->pixelMetric(QStyle::PM_ScrollBarExtent) + + (frameWidth() + + (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents) ? + style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0)) * 2 + + maxw); + cachedSizeHint.setHeight(frameWidth() * 2 + h); } + return cachedSizeHint; +} - mutable QSize cachedSizeHint; +void +UserListView::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: + if (currentItem()) + emit itemDoubleClicked(currentItem()); + event->accept(); + break; + case Qt::Key_Space: + if (currentItem()) + emit itemClicked(currentItem()); + event->accept(); + break; + default: + QListWidget::keyPressEvent(event); + break; + } +} - protected: - virtual QSize sizeHint() const - { - if (!cachedSizeHint.isValid()) { - ensurePolished(); - QStyleOptionViewItem vo(viewOptions()); - QAbstractListModel *md(static_cast<QAbstractListModel *>(model())); - uint maxw = 0, h = 0; - for (int i = 0, rc = md->rowCount(); i < rc; i++) { - QSize sh = itemDelegate()->sizeHint(vo, md->index(i)); - uint thisw = sh.width(); - if (thisw > maxw) - maxw = thisw; - h += sh.height(); - } - cachedSizeHint.setWidth( - style()->pixelMetric(QStyle::PM_ScrollBarExtent) + - (frameWidth() + - (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents) ? - style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0)) * 2 + - maxw); - cachedSizeHint.setHeight(frameWidth() * 2 + h); +void +UserListView::mousePressEvent(QMouseEvent *event) +{ + m_suppressClick = false; + QListWidget::mousePressEvent(event); +} + +void +UserListView::mouseReleaseEvent(QMouseEvent *event) +{ + if (m_suppressClick) + m_suppressClick = false; + else + QListWidget::mouseReleaseEvent(event); +} + +void +UserListView::mouseDoubleClickEvent(QMouseEvent *event) +{ + m_suppressClick = true; + QListWidget::mouseDoubleClickEvent(event); +} + + +UserListViewItem::UserListViewItem(UserListView *parent, const QString &text, + const QIcon &icon, const QString &username) + : QListWidgetItem(parent) + , login(username) +{ + setIcon(icon); + setText(text); + parent->cachedSizeHint.setWidth(-1); +} + + +UserSliderView::UserSliderView(QWidget *parent) + : UserListView(parent) +{ + setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); + setIconSize(QSize(SLIDER_IMAGE_SIZE * SELECTED_IMAGE_RATIO, SLIDER_IMAGE_SIZE * SELECTED_IMAGE_RATIO)); + setUniformItemSizes(false); + setAlternatingRowColors(false); + setViewMode(QListView::IconMode); + setSelectionMode(QAbstractItemView::NoSelection); + setWrapping(false); + setSpacing(10); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setAcceptDrops(false); +} + +QWidget* +UserSliderView::itemEditor(const int row) +{ + return itemEditors.value(row); +} + +void +UserSliderView::setItemEditor(const int row, QWidget *widget) +{ + itemEditors.resize(count()); + itemEditors.replace(row, widget); +} + +void +UserSliderView::layoutItems() +{ + QStyleOptionViewItem vo(viewOptions()); + QAbstractListModel *md(static_cast<QAbstractListModel *>(model())); + int rc = model()->rowCount(); + int w = 0; + for (int i = 0; i < rc; i++) { + QSize sh = itemDelegate()->sizeHint(vo, md->index(i)); + w += sh.width(); + } + int lw = size().width(); + int lh = size().height(); + int s = spacing(); + int os = (lw - w - s * (rc-1)) / 2; + int l = os; + + for (int i = 0; i < rc; i++) { + QSize sh = itemDelegate()->sizeHint(vo, md->index(i)); + setPositionForIndex(QPoint(l, (lh - sh.height()) / 2), md->index(i)); + l += sh.width() + spacing(); + } + updateGeometries(); +} + +void +UserSliderView::updateGeometries() +{ + QListWidget::updateGeometries(); +} + +void +UserSliderView::resizeEvent(QResizeEvent *event) +{ + QListWidget::resizeEvent(event); + + const int margin = 3; //style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; + const int text = 24; + QSize size = QSize(SLIDER_IMAGE_SIZE + 16 * margin, + SLIDER_IMAGE_SIZE + 18 * margin + text); + for (int i = 0; i < model()->rowCount(); i++) { + item(i)->setSizeHint(size); + } + layoutItems(); +} + +void +UserSliderView::dragEnterEvent (QDragEnterEvent *event) +{ + event->ignore(); +} + +void +UserSliderView::dragMoveEvent (QDragMoveEvent *event) +{ + event->ignore(); +} + +void +UserSliderView::dragLeaveEvent (QDragLeaveEvent *event) +{ + event->ignore(); +} + +void +UserSliderView::dropEvent (QDropEvent *event) +{ + event->ignore(); +} + +void +UserSliderView::wheelEvent (QWheelEvent *event) +{ + event->ignore(); +} + +/* +bool +UserSliderView::eventFilter(QObject *object, QEvent *event) +{ +qDebug("UserSliderView::eventFilter ObjectName %s", object->metaObject()->className()); + if (object->metaObject()->className() == "PasswordEditor") { + if (event->type() == QEvent::KeyPress) { +qDebug("UserSliderView::eventFilter QEvent::KeyPress"); + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); + if (keyEvent->key() == Qt::Key_Tab || + keyEvent->key() == Qt::Key_Backtab) { +qDebug("UserSliderView::eventFilter Qt::Key_Tab"); + //emit tabPressed(keyEvent->key()); + return true; + } + } + return false; + } + else { + return UserListView::eventFilter(object, event); + } +} +*/ +UserSliderViewItem::UserSliderViewItem(UserSliderView *parent, const QString &text, + const QIcon &icon, const QString &username) + : QObject() + , UserListViewItem(parent, text, icon, username) +{ + parent->openPersistentEditor(this); +} + +UserSliderViewItem::~UserSliderViewItem() +{ +} + + +UserSliderViewItemAnimation::UserSliderViewItemAnimation(QObject *parent) + : QPropertyAnimation(parent) +{ +} + +void +UserSliderViewItemAnimation::updateCurrentValue(const QVariant &value) +{ + QPropertyAnimation::updateCurrentValue(value); + emit sizeChanged(); +} + +void +UserItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + + const QWidget *widget = opt.widget; + QStyle *style = widget->style(); + + if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(&opt)) { + painter->save(); + painter->setClipRect(opt.rect); + + painter->setRenderHint(QPainter::Antialiasing); + const int margin = 3; //widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1; + const int offset = 2 * margin; + const int shadow = 4 * margin; + const int frame1 = 2 * margin; + const int frame2 = 2; + const int radius = 2 * margin; + const int spacing = 2 * margin; + const int text = 24; + + QRect iconRect = QRect(vopt->rect.left() + offset + shadow + frame1 + frame2, + vopt->rect.top() + offset + shadow + frame1 + frame2, + vopt->rect.width() - 2 * (offset + shadow + frame1 + frame2), + vopt->rect.width() - 2 * (offset + shadow + frame1 + frame2)); + + QRect textRect = QRect(vopt->rect.left(), + vopt->rect.top() + vopt->rect.height() - text - spacing - offset, + vopt->rect.width(), + text); + + QTextLayout textLayout; + textLayout.setFont(vopt->font); + textLayout.setText(vopt->text); + textLayout.beginLayout(); + QTextLine line = textLayout.createLine(); + line.setLineWidth(textRect.width()); + textLayout.endLayout(); + textRect.setWidth(line.naturalTextWidth() + 4 * margin); + textRect.moveLeft(vopt->rect.left() + vopt->rect.width() / 2 - textRect.width() / 2); + + // draw the background + QPalette::ColorGroup cg = (widget ? widget->isEnabled() : (vopt->state & QStyle::State_Enabled)) + ? QPalette::Normal : QPalette::Disabled; + QRect shadowLeftTopRect = QRect(vopt->rect.left() + offset, + vopt->rect.top() + offset, + 2 * (shadow + radius), + 2 * (shadow + radius)); + QRect shadowRightTopRect = QRect(vopt->rect.left() + vopt->rect.width() - offset - 2 * (shadow + radius), + vopt->rect.top() + offset, + 2 * (shadow + radius), + 2 * (shadow + radius)); + QRect shadowRightBottomRect = QRect(vopt->rect.left() + vopt->rect.width() - offset - 2 * (shadow + radius), + vopt->rect.top() + vopt->rect.height() - 2 * (shadow + radius) - spacing - text - offset, + 2 * (shadow + radius), + 2 * (shadow + radius)); + QRect shadowLeftBottomRect = QRect(vopt->rect.left() + offset, + vopt->rect.top() + vopt->rect.height() - 2 * (shadow + radius) - spacing - text - offset, + 2 * (shadow + radius), + 2 * (shadow + radius)); + + QRect shadowLeftRect = QRect(vopt->rect.left() + offset, + vopt->rect.top() + offset + shadow + radius, + shadow + radius, + vopt->rect.height() - 2 * (offset + shadow + radius) - spacing - text); + QRect shadowTopRect = QRect(vopt->rect.left() + offset + shadow + radius, + vopt->rect.top() + offset, + vopt->rect.width() - 2 * (offset + shadow + radius), + shadow + radius); + QRect shadowRightRect = QRect(vopt->rect.left() + vopt->rect.width() - offset - shadow - radius, + vopt->rect.top() + offset + shadow + radius, + shadow + radius, + vopt->rect.height() - 2 * (offset + shadow + radius) - spacing - text); + QRect shadowBottomRect = QRect(vopt->rect.left() + offset + shadow + radius, + vopt->rect.top() + vopt->rect.height() - shadow - radius - spacing - text - offset, + vopt->rect.width() - 2 * (offset + shadow + radius), + shadow + radius); + + painter->setPen(QPen(QColor(0, 0, 0, 0), 0)); + + QRadialGradient radialGrad; + radialGrad.setColorAt(0, QColor(0, 0, 0, 48)); + radialGrad.setColorAt(1, QColor(0, 0, 0, 0)); + radialGrad.setRadius(shadow + radius); + + radialGrad.setCenter(shadowLeftTopRect.left() + shadow + radius + 1, shadowLeftTopRect.top() + shadow + radius + 1); + radialGrad.setFocalPoint(shadowLeftTopRect.left() + shadow + radius + 1, shadowLeftTopRect.top() + shadow + radius + 1); + painter->setBrush(radialGrad); + painter->drawPie(shadowLeftTopRect, 90 * 16, 90 * 16); + + radialGrad.setCenter(shadowRightTopRect.right() - shadow - radius - 1, shadowRightTopRect.top() + shadow + radius + 1); + radialGrad.setFocalPoint(shadowRightTopRect.right() - shadow - radius - 1, shadowRightTopRect.top() + shadow + radius + 1); + painter->setBrush(radialGrad); + painter->drawPie(shadowRightTopRect, 0 * 16, 90 * 16); + + radialGrad.setCenter(shadowRightBottomRect.right() - shadow - radius - 1, shadowRightBottomRect.bottom() - shadow - radius - 1); + radialGrad.setFocalPoint(shadowRightBottomRect.right() - shadow - radius - 1, shadowRightBottomRect.bottom() - shadow - radius - 1); + painter->setBrush(radialGrad); + painter->drawPie(shadowRightBottomRect, 270 * 16, 90 * 16); + + radialGrad.setCenter(shadowLeftBottomRect.left() + shadow + radius + 1, shadowLeftBottomRect.bottom() - shadow - radius - 1); + radialGrad.setFocalPoint(shadowLeftBottomRect.left() + shadow + radius + 1, shadowLeftBottomRect.bottom() - shadow - radius - 1); + painter->setBrush(radialGrad); + painter->drawPie(shadowLeftBottomRect, 180 * 16, 90 * 16); + + QLinearGradient linearGrad; + linearGrad.setColorAt(0, QColor(0, 0, 0, 48)); + linearGrad.setColorAt(1, QColor(0, 0, 0, 0)); + + linearGrad.setStart(shadowLeftRect.right(), 0); + linearGrad.setFinalStop(shadowLeftRect.left(), 0); + painter->setBrush(linearGrad); + painter->drawRect(shadowLeftRect); + + linearGrad.setStart(0, shadowTopRect.bottom()); + linearGrad.setFinalStop(0, shadowTopRect.top()); + painter->setBrush(linearGrad); + painter->drawRect(shadowTopRect); + + linearGrad.setStart(shadowRightRect.left(), 0); + linearGrad.setFinalStop(shadowRightRect.right(), 0); + painter->setBrush(linearGrad); + painter->drawRect(shadowRightRect); + + linearGrad.setStart(0, shadowBottomRect.top()); + linearGrad.setFinalStop(0, shadowBottomRect.bottom()); + painter->setBrush(linearGrad); + painter->drawRect(shadowBottomRect); + + QRect frame0Rect = QRect(vopt->rect.left() + offset + shadow + frame1, + vopt->rect.top() + vopt->rect.width() - (offset + shadow + frame1), + vopt->rect.width() - 2 * (offset + shadow + frame1), + vopt->rect.height() - vopt->rect.width() - spacing - text); + painter->setBrush(QColor(0, 0, 0, 48)); + painter->drawRect(frame0Rect); + + QRect frame1Rect = QRect(vopt->rect.left() + offset + shadow, + vopt->rect.top() + offset + shadow, + vopt->rect.width() - 2 * (offset + shadow), + vopt->rect.height() - 2 * (offset + shadow) - spacing - text); + painter->setBrush(QColor(0, 0, 0, 148)); + painter->drawRoundedRect(frame1Rect, radius, radius); + QRect frame2Rect = QRect(vopt->rect.left() + offset + shadow + frame1, + vopt->rect.top() + offset + shadow + frame1, + vopt->rect.width() - 2 * (offset + shadow + frame1), + vopt->rect.width() - 2 * (offset + shadow + frame1)); + painter->setBrush(QColor(255, 255, 255, 255)); + painter->drawRoundedRect(frame2Rect, margin, margin); + + // draw the icon + QIcon::Mode mode = QIcon::Normal; + if (!(vopt->state & QStyle::State_Enabled)) + mode = QIcon::Disabled; + else if (vopt->state & QStyle::State_Selected) + mode = QIcon::Selected; + QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; + vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); + + // draw the text + if (!vopt->text.isEmpty()) { + painter->setPen(QColor(255, 255, 255, 255)); + drawText(painter, vopt, textRect); } - return cachedSizeHint; + painter->restore(); } - virtual void keyPressEvent(QKeyEvent *event) - { - switch (event->key()) { - case Qt::Key_Enter: - case Qt::Key_Return: - if (currentItem()) - emit itemDoubleClicked(currentItem()); - event->accept(); +} + +QWidget * +UserItemDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const + +{ + PasswordEditor *editor = new PasswordEditor(parent); + connect(editor, SIGNAL(textEntered(const QString &)), + this, SLOT(slotTextEntered(const QString &))); + connect(editor, SIGNAL(tabPressed(const int)), + this, SLOT(slotTabPressed(const int))); + + QStyleOptionViewItemV4 opt = option; + QWidget *widget = const_cast<QWidget *>(opt.widget); + UserSliderView *view = static_cast<UserSliderView *>(widget); + view->setItemEditor(index.row(), editor); + view->setFocusProxy(editor); + editor->setFocus(); + + return editor; +} + +void +UserItemDelegate::setEditorData(QWidget * /*editor*/, + const QModelIndex &/*index*/) const +{ +} + +void +UserItemDelegate::setModelData(QWidget * /*editor*/, QAbstractItemModel * /*model*/, + const QModelIndex &/*index*/) const +{ +} + +void +UserItemDelegate::updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, + const QModelIndex & /*index*/) const +{ + QStyleOptionViewItemV4 opt = option; + const int margin = 3; //opt.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, opt.widget) + 1; + const int text = 24; //opt.widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, opt.widget) + 1; + + int x = option.rect.left() + 8 * margin; + int y = option.rect.top() + option.rect.width() - 6 * margin - 3; + int w = option.rect.width() - 16 * margin; + int h = option.rect.height() - option.rect.width() - 4 * margin - text + 6; + //qDebug("updateEditorGeometry height %d, rh %d, rw", h, option.rect.height(), option.rect.width()); + editor->setGeometry(QRect(x, y, w, h)); +} + +void +UserItemDelegate::slotTextEntered(const QString &text) +{ + emit passwordEntered(text/*static_cast<PasswordEditor *>(sender())->text()*/); +} + +void +UserItemDelegate::slotTabPressed(const int key) +{ + emit tabPressed(key); +} + +void +UserItemDelegate::drawText(QPainter *p, const QStyleOptionViewItemV4 *option, const QRect &rect) const +{ + const QWidget *widget = option->widget; + const int textMargin = 3; //widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1; + + QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); + const bool wrapText = option->features & QStyleOptionViewItemV2::WrapText; + QTextOption textOption; + textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap); + textOption.setTextDirection(option->direction); + textOption.setAlignment(QStyle::visualAlignment(option->direction, option->displayAlignment)); + QTextLayout textLayout; + textLayout.setTextOption(textOption); + textLayout.setFont(option->font); + textLayout.setText(option->text); + + qreal heightUsed = 0; + qreal widthUsed = 0; + textLayout.beginLayout(); + while (true) { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) break; - case Qt::Key_Space: - if (currentItem()) - emit itemClicked(currentItem()); - event->accept(); + line.setLineWidth(textRect.width()); + line.setPosition(QPointF(0, heightUsed)); + heightUsed += line.height(); + widthUsed = qMax(widthUsed, line.naturalTextWidth()); + } + textLayout.endLayout(); + QSizeF textLayoutSize = QSizeF(widthUsed, heightUsed); + + QString elidedText; + qreal height = 0; + qreal width = 0; + int elidedIndex = -1; + const int lineCount = textLayout.lineCount(); + for (int j = 0; j < lineCount; ++j) { + const QTextLine line = textLayout.lineAt(j); + if (j + 1 <= lineCount - 1) { + const QTextLine nextLine = textLayout.lineAt(j + 1); + if ((nextLine.y() + nextLine.height()) > textRect.height()) { + int start = line.textStart(); + int length = line.textLength() + nextLine.textLength(); + //const QStackTextEngine engine(textLayout.text().mid(start, length), option->font); + elidedText = /*engine*/option->fontMetrics.elidedText(textLayout.text().mid(start, length), option->textElideMode, textRect.width()); + height += line.height(); + width = textRect.width(); + elidedIndex = j; + break; + } + } + if (line.naturalTextWidth() > textRect.width()) { + int start = line.textStart(); + int length = line.textLength(); + //const QStackTextEngine engine(textLayout.text().mid(start, length), option->font); + elidedText = /*engine*/option->fontMetrics.elidedText(textLayout.text().mid(start, length), option->textElideMode, textRect.width()); + height += line.height(); + width = textRect.width(); + elidedIndex = j; break; - default: - QListWidget::keyPressEvent(event); + } + width = qMax<qreal>(width, line.width()); + height += line.height(); + } + + QFont f = option->font; + f.setFamily("Droid Sans"); +//qDebug("font family=%s", f.family().toAscii().data()); + + const QRect layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment, + QSize(int(width), int(height)), textRect); + const QPointF position = layoutRect.topLeft(); + for (int i = 0; i < lineCount; ++i) { + const QTextLine line = textLayout.lineAt(i); + if (i == elidedIndex) { + qreal x = position.x() + line.x(); + qreal y = position.y() + line.y() + line.ascent(); + p->save(); + p->setFont(f); + p->drawText(QPointF(x, y), elidedText); +//qDebug("font family=%s", p->font().family().toAscii().data()); + p->restore(); break; } + line.draw(p, position); } +} - virtual void mousePressEvent(QMouseEvent *event) - { - m_suppressClick = false; - QListWidget::mousePressEvent(event); + +PasswordEditor::PasswordEditor(QWidget *parent) + : QWidget(parent) +{ + layout = new QVBoxLayout(this); + layout->setMargin(0); + edit = new LineEdit(this); + edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + edit->setMinimumHeight(30); + connect(edit, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed())); + connect(edit, SIGNAL(loginPressed()), this, SLOT(slotReturnPressed())); + connect(edit, SIGNAL(tabPressed(const int)), this, SLOT(slotTabPressed(const int))); + layout->addWidget(edit); + setLayout(layout); + setContentsMargins(0, 0, 0, 0); + setFocusProxy(edit); +} + +PasswordEditor::~PasswordEditor() +{ + delete edit; + delete layout; +} + +LineEdit* +PasswordEditor::lineEdit() const +{ + return edit; +} + +void +PasswordEditor::slotReturnPressed() +{ + emit textEntered(edit->text()); +} + +void +PasswordEditor::slotTabPressed(const int key) +{ + emit tabPressed(key); +} + +LineEdit::LineEdit(QWidget *parent) + : QLineEdit(parent), + language("us"), + capsLocked(false), + loginHover(false), + loginActive(false), + indicatorChanged(false) +{ + setEchoMode(QLineEdit::Password); + //setLangIndicator("us"); + //setCapsIndicator(false); + setFrame(false); + //QApplication::setStyle("qtcurve"); + loginIcon.addPixmap(QPixmap(KStandardDirs::locate("data", "kdm/pics/login-normal.png")), QIcon::Normal); + loginIcon.addPixmap(QPixmap(KStandardDirs::locate("data", "kdm/pics/login-disabled.png")), QIcon::Disabled); + loginIcon.addPixmap(QPixmap(KStandardDirs::locate("data", "kdm/pics/login-active.png")), QIcon::Active); + loginIcon.addPixmap(QPixmap(KStandardDirs::locate("data", "kdm/pics/login-prelight.png")), QIcon::Selected); + + setStyleSheet("* { lineedit-password-character: 9679 }"); + setStyleSheet("* { background: transparent }"); +} + +void +LineEdit::setLangIndicator(const QString &lang) +{ + language = lang; + indicatorChanged = true; + repaint(); +} + +void +LineEdit::setCapsIndicator(const bool caps) +{ + capsLocked = caps; + indicatorChanged = true; + repaint(); +} + +bool +LineEdit::event(QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { +//qDebug("LineEdit::event QEvent::KeyPress"); + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); + if (keyEvent->key() == Qt::Key_Tab || + keyEvent->key() == Qt::Key_Backtab) { +//qDebug("LineEdit::event Qt::Key_Tab"); + emit tabPressed(keyEvent->key()); + return true; + } } + return QLineEdit::event(event); +} - virtual void mouseReleaseEvent(QMouseEvent *event) - { - if (m_suppressClick) - m_suppressClick = false; - else - QListWidget::mouseReleaseEvent(event); +void +LineEdit::leaveEvent(QEvent *event) +{ + loginHover = false; + QLineEdit::leaveEvent(event); +} +void +LineEdit::mouseMoveEvent(QMouseEvent *event) +{ + if (event->x() > width() - buttonWidth) { + if (!loginActive) { + loginHover = true; + } + setCursor(Qt::ArrowCursor); + } + else { + loginHover = false; + setCursor(Qt::IBeamCursor); } + QLineEdit::mouseMoveEvent(event); +} - virtual void mouseDoubleClickEvent(QMouseEvent *event) - { - m_suppressClick = true; - QListWidget::mouseDoubleClickEvent(event); +void +LineEdit::mousePressEvent(QMouseEvent *event) +{ + if (event->x() > width() - buttonWidth) { + loginHover = false; + loginActive = true; } + QLineEdit::mousePressEvent(event); +} - private: - bool m_suppressClick; -}; +void +LineEdit::mouseReleaseEvent(QMouseEvent *event) +{ + if (loginActive) { + emit loginPressed(); + loginActive = false; + } + if (event->x() > width() - buttonWidth) { + loginHover = true; + setCursor(Qt::ArrowCursor); + } + QLineEdit::mouseReleaseEvent(event); +} -class UserListViewItem : public QListWidgetItem { - public: - UserListViewItem(UserListView *parent, const QString &text, - const QPixmap &pixmap, const QString &username) - : QListWidgetItem(parent) - , login(username) - { - setIcon(pixmap); - setText(text); - parent->cachedSizeHint.setWidth(-1); +void +LineEdit::paintEvent(QPaintEvent *event) +{ + + const int frameWidth = 1; + const int horizontalMargin = 3; + const int verticalMargin = 5; + const int indicatorWidth = 17; + const int indicatorHeight = 15; + //const int buttonWidth = 33; + //const int buttonHeight = 30; + const int spacing = 3; + const int radius = 3; + + QFont f = font(); + const int fontSize = 14; + f.setPixelSize(fontSize); + setFont(f); + + QPainter p(this); + QRect r = rect(); + QPalette pal = palette(); + + QString langText, capsText; + QRect borderRect, langFrameRect, langTextRect, capsFrameRect, capsTextRect, buttonRect; + + borderRect = QRect(r.left() + frameWidth, r.top() + frameWidth, r.width() - 2 * frameWidth, r.height() - 2 * frameWidth); + p.setRenderHint(QPainter::Antialiasing); + p.setPen(QColor(156, 181, 198, 255)); + p.setBrush(QColor(255, 255, 255, 255)); + p.drawRoundedRect(borderRect, radius, radius); + + QLineEdit::paintEvent(event); + + QFont fnt = font(); + fnt.setPixelSize(qMax(1, r.height() * 3 / 8)); +// fnt.setBold(true); + QFontMetrics ifm(fnt); + const bool localLang = !language.contains("us"); + buttonRect = QRect(r.right() - buttonWidth, + r.top() + frameWidth, + buttonWidth, + r.height() - 2 * frameWidth); + + if (capsLocked) { + capsText = capsLocked ? QString("A") : QString("a"); + int capsTextWidth = ifm.width(capsText); + int capsTextHeight = ifm.height(); + capsFrameRect = QRect(r.right() - indicatorWidth - spacing - buttonWidth, + r.top() + verticalMargin + frameWidth, + indicatorWidth, + r.height() - 2 * (verticalMargin + frameWidth)); + capsTextRect = QRect(capsFrameRect.left() + (capsFrameRect.width() - capsTextWidth) / 2, + capsFrameRect.top() + (capsFrameRect.height() - capsTextHeight) / 2, + capsTextWidth, + capsTextHeight); } - QString login; -}; + if (localLang) { + langText = language; + int langTextWidth = ifm.width(langText); + int langTextHeight = ifm.height(); + if (capsLocked) + langFrameRect = QRect(r.right() - 2 * (indicatorWidth + spacing) - buttonWidth, + r.top() + verticalMargin + frameWidth, + indicatorWidth, + r.height() - 2 * (verticalMargin + frameWidth)); + else + langFrameRect = QRect(r.right() - indicatorWidth - spacing - buttonWidth, + r.top() + verticalMargin + frameWidth, + indicatorWidth, + r.height() - 2 * (verticalMargin + frameWidth)); + langTextRect = QRect(langFrameRect.left() + (langFrameRect.width() - langTextWidth) / 2, + langFrameRect.top() + (langFrameRect.height() - langTextHeight) / 2, + langTextWidth, + langTextHeight); + } + + if (indicatorChanged) { + if (localLang) + if (capsLocked) + setTextMargins(spacing, 0, 2 * (indicatorWidth + spacing) + buttonWidth, 0); + else + setTextMargins(spacing, 0, indicatorWidth + spacing + buttonWidth, 0); + else + if (capsLocked) + setTextMargins(spacing, 0, indicatorWidth + spacing + buttonWidth, 0); + else + setTextMargins(spacing, 0, buttonWidth + horizontalMargin + frameWidth, 0); + indicatorChanged = false; + } + p.save(); + p.setFont(fnt); + if (localLang) { + p.setPen(Qt::NoPen); + p.fillRect(langFrameRect, pal.highlight()); + p.setPen(QColor(255, 255, 255, 255)); + p.drawText(langTextRect, langText); + } + if (capsLocked) { + p.setPen(Qt::NoPen); + p.fillRect(capsFrameRect, pal.highlight()); + p.setPen(QColor(255, 255, 255, 255)); + p.drawText(capsTextRect, capsText); + } + p.restore(); + + QPixmap pixmap; + if (loginHover) + pixmap = loginIcon.pixmap(buttonRect.width(), buttonRect.height(), QIcon::Selected); + else if (loginActive) + pixmap = loginIcon.pixmap(buttonRect.width(), buttonRect.height(), QIcon::Active); + else + pixmap = loginIcon.pixmap(buttonRect.width(), buttonRect.height(), QIcon::Normal); + if (buttonRect.height() > 0) + p.drawPixmap(buttonRect, pixmap.scaled(buttonRect.width(), buttonRect.height())); +} + +/* +void +LineEdit::focusOutEvent(QFocusEvent *event) +{ + if (event->reason() == Qt::TabFocusReason) { +qDebug("LineEdit::focusOutEvent Qt::TabFocusReason"); + } + else if (event->reason() == Qt::BacktabFocusReason) { +qDebug("LineEdit::focusOutEvent Qt::BackTabFocusReason"); + } + else +qDebug("LineEdit::focusOutEvent Qt::OtherReason"); + + QLineEdit::focusOutEvent(event); +} +*/ int KGreeter::curPlugin = -1; PluginList KGreeter::pluginList; KGreeter::KGreeter(bool framed) : inherited(framed) + + , imageSize(LIST_IMAGE_SIZE) , dName(dname) , userView(0) , userList(0) @@ -217,9 +976,9 @@ KGreeter::~KGreeter() delete stsGroup; } -#define FILE_LIMIT_ICON 20 +#define FILE_LIMIT_ICON 200 //U 20 #define FILE_LIMIT_IMAGE 200 -#define PIXEL_LIMIT_ICON 100 +#define PIXEL_LIMIT_ICON 300 //U 100 #define PIXEL_LIMIT_IMAGE 300 // replace this with a simple !access(..., X_OK) once we run with non-root real uid @@ -234,7 +993,7 @@ dirAccessible(const char *dir) } static bool -loadFace(QByteArray &fn, QImage &p, const QByteArray &pp, bool complain = false) +loadFace(QByteArray &fn, QIcon &icon, const int size, const QByteArray &pp, bool complain = false) { int fd, ico; if ((fd = open(fn.data(), O_RDONLY | O_NONBLOCK)) < 0) { @@ -246,7 +1005,7 @@ loadFace(QByteArray &fn, QImage &p, const QByteArray &pp, bool complain = false) } fn.chop(5); if ((fd = open(fn.data(), O_RDONLY | O_NONBLOCK)) < 0) { - if ((complain || errno != ENOENT) && + if ((complain || errno != ENOENT) && (pp.isEmpty() || dirAccessible(pp.data()))) (complain ? logError : logInfo) ("Cannot load %s: %m\n", fn.data()); @@ -266,6 +1025,7 @@ loadFace(QByteArray &fn, QImage &p, const QByteArray &pp, bool complain = false) } QByteArray fc = f.read(fs); ::close(fd); + int is = size * SELECTED_IMAGE_RATIO; QBuffer buf(&fc); buf.open(QBuffer::ReadOnly); QImageReader ir(&buf); @@ -276,32 +1036,27 @@ loadFace(QByteArray &fn, QImage &p, const QByteArray &pp, bool complain = false) fn.data(), lim, lim); return false; } - sz.scale(48, 48, Qt::KeepAspectRatio); + sz.scale(is, is, Qt::KeepAspectRatio); ir.setScaledSize(sz); - p = ir.read(); + QImage p = ir.read(); if (p.isNull()) { logInfo("%s is no valid image\n", fn.data()); return false; } - if (p.width() < 48) { - QImage np(48, p.height(), QImage::Format_ARGB32); + if (p.width() < is) { + QImage np(is, p.height(), QImage::Format_ARGB32); np.fill(0); QPainter pnt(&np); - pnt.drawImage((48 - p.width()) / 2, 0, p); + pnt.drawImage((is - p.width()) / 2, 0, p); p = np; } + icon.addPixmap(QPixmap::fromImage(p), QIcon::Normal); return true; } -void -KGreeter::insertUser(const QImage &default_pix, - const QString &username, struct passwd *ps) +static void +getImage(const QIcon &default_icon, QIcon &i, const int size, struct passwd *ps) { - if (userList) - userList->append(username); - if (!userView) - return; - int dp = 0, nd = 0; if (_faceSource == FACE_USER_ONLY || _faceSource == FACE_PREFER_USER) @@ -309,31 +1064,45 @@ KGreeter::insertUser(const QImage &default_pix, if (_faceSource != FACE_USER_ONLY && _faceSource != FACE_ADMIN_ONLY) nd = 1; - QImage p; do { dp ^= 1; QByteArray pp, fn; if (!dp) { fn = pp = QByteArray(ps->pw_dir); fn += '/'; + fn += ".face.icon"; } else { fn = QFile::encodeName(_faceDir); fn += '/'; fn += ps->pw_name; + fn += ".png"; } - fn += ".face.icon"; - if (loadFace(fn, p, pp)) - goto gotit; + if (loadFace(fn, i, size, pp)) + return; } while (--nd >= 0); - p = default_pix; - gotit: + i = default_icon; + return; +} + +void +KGreeter::insertUser(const QIcon &default_icon, + const QString &username, struct passwd *ps) +{ + if (userList) + userList->append(username); + if (!userView) + return; + + QIcon icon; + getImage(default_icon, icon, imageSize, ps); + QString realname = KStringHandler::from8Bit(ps->pw_gecos); realname.truncate(realname.indexOf(',') & (~0U >> 1)); if (realname.isEmpty() || realname == username) { - new UserListViewItem(userView, username, QPixmap::fromImage(p), username); + new UserListViewItem(userView, username, icon, username); } else { realname.append("\n").append(username); - new UserListViewItem(userView, realname, QPixmap::fromImage(p), username); + new UserListViewItem(userView, realname, icon, username); } } @@ -381,12 +1150,18 @@ KGreeter::insertUsers() } } - QImage default_pix; + QIcon default_icon; if (userView) { - QByteArray fn = QFile::encodeName(_faceDir) + "/.default.face.icon"; - if (!loadFace(fn, default_pix, QByteArray(), true)) { - default_pix = QImage(48, 48, QImage::Format_ARGB32); - default_pix.fill(0); + QByteArray fn = QFile::encodeName(_faceDir) + "/default.png"/*"/.default.face.icon"*/; + if (!loadFace(fn, default_icon, imageSize, QByteArray(), true)) { + QImage img; + img = QImage(imageSize, imageSize, QImage::Format_ARGB32); + img.fill(0); + default_icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal); + img = QImage(imageSize * SELECTED_IMAGE_RATIO, imageSize * SELECTED_IMAGE_RATIO, + QImage::Format_ARGB32); + img.fill(0); + default_icon.addPixmap(QPixmap::fromImage(img), QIcon::Selected); } } if (_showUsers == SHOW_ALL) { @@ -403,7 +1178,7 @@ KGreeter::insertUsers() QString username(QFile::decodeName(ps->pw_name)); if (!dupes.contains(username)) { dupes.insert(username); - insertUser(default_pix, username, ps); + insertUser(default_icon, username, ps); } } } @@ -422,14 +1197,14 @@ KGreeter::insertUsers() QString username(QFile::decodeName(ps->pw_name)); if (!dupes.contains(username)) { dupes.insert(username); - insertUser(default_pix, username, ps); + insertUser(default_icon, username, ps); } } } } else { for (int i = 0; _users[i]; i++) if ((ps = getpwnam(_users[i])) && (ps->pw_uid || _showRoot)) - insertUser(default_pix, QFile::decodeName(_users[i]), ps); + insertUser(default_icon, QFile::decodeName(_users[i]), ps); } } endpwent(); @@ -448,6 +1223,15 @@ KGreeter::insertUsers() } void +KGreeter::removeUsers() +{ + if (userView) { + userView->model()->removeRows(0, userView->model()->rowCount()); + } +} + + +void KGreeter::putSession(const QString &type, const QString &name, bool hid, const char *exe) { int prio = exe ? (!strcmp(exe, "default") ? 0 : @@ -734,7 +1518,8 @@ KStdGreeter::KStdGreeter() main_box->setSpacing(layout()->margin()); if (userView) - main_box->addWidget(userView); + main_box->addWidget( + userView); QBoxLayout *inner_box = new QVBoxLayout(); main_box->addLayout(inner_box); @@ -887,6 +1672,8 @@ KStdGreeter::verifyRetry() KThemedGreeter::KThemedGreeter(KdmThemer *_themer) : KGreeter(true) , themer(_themer) + , userSliderView(0) +// , xEventNotifier() // , clock(0) { // We do all painting ourselves @@ -913,6 +1700,11 @@ KThemedGreeter::KThemedGreeter(KdmThemer *_themer) userlist_rect = themer->findNode("userlist-rect"); if (!userlist_rect) userlist_rect = userlist_node; + userslider_node = themer->findNode("userslider"); + userslider_rect = themer->findNode("userslider-rect"); + if (!userslider_rect) + userslider_rect = userslider_node; + caps_warning = themer->findNode("caps-lock-warning"); xauth_warning = themer->findNode("xauth-warning"); // kdm ext pam_error = themer->findNode("pam-error"); @@ -936,6 +1728,46 @@ KThemedGreeter::KThemedGreeter(KdmThemer *_themer) console_rect->setVisible(false); } + if (userslider_node) { + if (userlist_node) + removeUsers(); + + userItemDelegate = new UserItemDelegate(); + connect(userItemDelegate, SIGNAL(passwordEntered(const QString &)), + this, SLOT(slotPasswordEntered(const QString &))); + connect(userItemDelegate, SIGNAL(tabPressed(const int)), + this, SLOT(slotTabPressed(const int))); + + userSliderView = new UserSliderView(this); + userSliderView->setItemDelegate(userItemDelegate); + imageSize = SLIDER_IMAGE_SIZE; + + //connect(userSliderView, SIGNAL(itemClicked(QListWidgetItem *)), + // SLOT(slotUserClicked(QListWidgetItem *))); + //connect(userSliderView, SIGNAL(itemDoubleClicked(QListWidgetItem *)), + // SLOT(accept())); + connect(userSliderView, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), + SLOT(slotCurrentItemChanged(QListWidgetItem *, QListWidgetItem *))); + + insertUsers(); + + animGroup = new QParallelAnimationGroup; + animation1 = new UserSliderViewItemAnimation(); + animation2 = new UserSliderViewItemAnimation(); + connect(animation2, SIGNAL(sizeChanged()), userSliderView, SLOT(layoutItems())); + connect(animation2, SIGNAL(finished()), this, SLOT(slotAnimationFinished())); + + //if (userSliderView->count()) + // userSliderView->setCurrentItem(userSliderView->item(0)); + +// connect(&xEventNotifier, SIGNAL(layoutChanged()), this, SLOT(slotLayoutChanged())); +// connect(&xEventNotifier, SIGNAL(layoutMapChanged()), this, SLOT(slotLayoutChanged())); +// xEventNotifier.start(); + slotLayoutChanged(); + updateLockStatus(); + //verify->resume(); + } + if (xauth_warning && (_authorized || !_authComplain)) xauth_warning->setVisible(false); @@ -944,7 +1776,9 @@ KThemedGreeter::KThemedGreeter(KdmThemer *_themer) // clock = new KdmClock(this, "clock"); QWidget *prec; - if (userView) + if (userSliderView) + prec = userSliderView; + else if (userView) prec = userView; #ifdef WITH_KDM_XCONSOLE else if (consoleView) @@ -993,10 +1827,18 @@ KThemedGreeter::KThemedGreeter(KdmThemer *_themer) pluginSetup(); verify->start(); + + setPreselUser(); } KThemedGreeter::~KThemedGreeter() { + if (userSliderView) { +// xEventNotifier.stop(); + delete animation2; + delete animation1; + delete animGroup; + } themer->setWidget(0); } @@ -1007,11 +1849,66 @@ KThemedGreeter::slotDebugToggled() themer->slotNeedPlacement(); } +void +KThemedGreeter::insertUser(const QIcon &default_icon, + const QString &username, struct passwd *ps) +{ + + if (!userSliderView) { + inherited::insertUser(default_icon, username, ps); + return; + } + + QIcon icon; + getImage(default_icon, icon, imageSize, ps); + + UserSliderViewItem *item = new UserSliderViewItem(userSliderView, username, icon, username); +} +/* +void +KThemedGreeter::insertUsers() +{ + if (userSliderView) { + QIcon default_icon; + QByteArray fn = QFile::encodeName(_faceDir) + "/default.png"; + if (!loadFace(fn, default_icon, imageSize, QByteArray(), true)) { + QImage img; + img = QImage(imageSize, imageSize, QImage::Format_ARGB32); + img.fill(0); + default_icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal); + img = QImage(imageSize * SELECTED_IMAGE_RATIO, imageSize * SELECTED_IMAGE_RATIO, + QImage::Format_ARGB32); + img.fill(0); + default_icon.addPixmap(QPixmap::fromImage(img), QIcon::Selected); + } + if (_sortUsers) { + userSliderView->sortItems(); + } + } + inherited::insertUsers(); +} +*/ + bool KThemedGreeter::event(QEvent *e) { if (themer) themer->widgetEvent(e); + + if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) + updateLockStatus(); + + if (e->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e); + //qDebug("KThemedGreeter::event"); + if (keyEvent->key() == Qt::Key_Tab || + keyEvent->key() == Qt::Key_Backtab) { + //qDebug("KThemedGreeter::event TAB"); + /*emit*/ slotTabPressed(keyEvent->key()); + //return true; + } + } + return inherited::event(e); } @@ -1020,32 +1917,70 @@ KThemedGreeter::pluginSetup() { inherited::pluginSetup(); - if (userView && verify->entitiesLocal() && verify->entityPresettable() && userlist_node) { - userlist_node->setWidget(userView); - userlist_rect->setVisible(true); - } else { + if (userSliderView && verify->entitiesLocal() && verify->entityPresettable() && userslider_node) { + userslider_node->setWidget(userSliderView); + userslider_rect->setVisible(true); if (userView) - userView->hide(); - if (userlist_rect) - userlist_rect->setVisible(false); + userView->hide(); + if (userlist_rect) + userlist_rect->setVisible(false); + } else { + if (userView && verify->entitiesLocal() && verify->entityPresettable() && userlist_node) { + userlist_node->setWidget(userView); + userlist_rect->setVisible(true); + } else { + if (userSliderView) + userSliderView->hide(); + if (userslider_rect) + userslider_rect->setVisible(false); + if (userView) + userView->hide(); + if (userlist_rect) + userlist_rect->setVisible(false); + } + } +} + +void +KThemedGreeter::verifyClear() +{ + inherited::verifyClear(); + if (userSliderView) { + userSliderView->setFocus(); + QListWidgetItem *current = userSliderView->currentItem(); + int row = userSliderView->row(current); + PasswordEditor *editor = static_cast<PasswordEditor *>(userSliderView->itemEditor(row)); + editor->lineEdit()->clear(); + userSliderView->setFocusProxy(editor); + editor->setFocus(); } } -#if 0 + void KThemedGreeter::verifyFailed() { -// goButton->setEnabled(false); + if (userSliderView) + userSliderView->setEnabled(false); inherited::verifyFailed(); } void KThemedGreeter::verifyRetry() { -// goButton->setEnabled(true); + if (userSliderView) { + userSliderView->setEnabled(true); + + QListWidgetItem *current = userSliderView->currentItem(); + int row = userSliderView->row(current); + PasswordEditor *editor = static_cast<PasswordEditor *>(userSliderView->itemEditor(row)); + userSliderView->setFocusProxy(editor); + editor->setFocus(); + LineEdit *edit = editor->lineEdit(); + edit->setSelection(0, edit->text().length()); + } inherited::verifyRetry(); } -#endif void KThemedGreeter::updateStatus(bool fail, bool caps, int timedleft) @@ -1070,6 +2005,76 @@ KThemedGreeter::updateStatus(bool fail, bool caps, int timedleft) } void +KThemedGreeter::updateLockStatus() +{ + if (userSliderView) { + unsigned int lmask; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + XQueryPointer(QX11Info::display(), DefaultRootWindow(QX11Info::display()), + &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, + &lmask); + bool capsLocked = lmask & LockMask; + + //qDebug("KThemedGreeter::updateLockStatus %d", capsLocked); + for (int i = 0; i < userSliderView->model()->rowCount(); i++) { + PasswordEditor *editor = static_cast<PasswordEditor*>(userSliderView->itemEditor(i)); + editor->lineEdit()->setCapsIndicator(capsLocked); + } + //userSliderView->layoutItems(); + } +} + +void +KThemedGreeter::setPreselUser() +{ +//qDebug("setPreselUser"); + if (userSliderView) { + curUser = verify->getUser(); + int rc = userSliderView->model()->rowCount(); +//qDebug("setPreselUser %s", curUser.toAscii().data()); + for (int i = 0; i < rc; i++) { + UserSliderViewItem *item = + static_cast<UserSliderViewItem *>(userSliderView->item(i)); + if (item->login == curUser) { + userSliderView->setCurrentItem(item); + return; + } + } + if (rc > 0) + userSliderView->setCurrentItem(userSliderView->item(0)); + } +} + +void +KThemedGreeter::slotUserEntered() +{ + + if (userSliderView) { + struct passwd *pw; + if ((pw = getpwnam(curUser.toLocal8Bit().data()))) { + QString theUser = QString::fromLocal8Bit(pw->pw_name); + for (int i = 0, rc = userSliderView->model()->rowCount(); i < rc; i++) { + UserSliderViewItem *item = + static_cast<UserSliderViewItem *>(userSliderView->item(i)); + if (item->login == theUser) { + userSliderView->setCurrentItem(item); + goto oke; + } + } + } + userSliderView->clearSelection(); + oke: + if (isVisible()) + slotLoadPrevWM(); + else + QTimer::singleShot(0, this, SLOT(slotLoadPrevWM())); + return; + } + inherited::slotUserEntered(); +} + +void KThemedGreeter::slotThemeActivated(const QString &id) { if (id == "login_button") @@ -1078,6 +2083,10 @@ KThemedGreeter::slotThemeActivated(const QString &id) slotSessMenu(); else if (id == "system_button") slotActionMenu(); + else if (id == "halt_button") + slotHalt(); + else if (id == "reboot_button") + slotReboot(); } void @@ -1096,12 +2105,128 @@ KThemedGreeter::slotActionMenu() } void +KThemedGreeter::slotHalt() +{ + KDMSlimShutdown::externShutdown(SHUT_HALT, 0, 0, false); +} + +void +KThemedGreeter::slotReboot() +{ + KDMSlimShutdown::externShutdown(SHUT_REBOOT, 0, 0, false); +} + +void +KThemedGreeter::slotCurrentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +{ + if (animGroup->indexOfAnimation(animation1) >= 0) + animGroup->removeAnimation(animation1); + if (animGroup->indexOfAnimation(animation2) >= 0) + animGroup->removeAnimation(animation2); + if (animGroup->state() != QAbstractAnimation::Stopped) + animGroup->stop(); + + int margin = 3; //style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; + int editor = 32; + int text = 24; + int spacing = 2 * margin; + int frame2 = 4; + QSize unselectedSize = QSize(imageSize + 16 * margin + frame2, + imageSize + 16 * margin + frame2 + spacing + text ); + QSize selectedSize = QSize(imageSize * SELECTED_IMAGE_RATIO + 16 * margin + frame2, + imageSize * SELECTED_IMAGE_RATIO + spacing + editor + 16 * margin + frame2 + spacing + text); + + if (previous) { + UserSliderViewItem *prevItem = static_cast<UserSliderViewItem *>(previous); + animation1->setTargetObject(prevItem); + animation1->setPropertyName("sizeHint"); + //animation1->setDuration(500); + animation1->setStartValue(selectedSize); + animation1->setEndValue(unselectedSize); + animGroup->addAnimation(animation1); + } + if (current) { + UserSliderViewItem *currItem = static_cast<UserSliderViewItem *>(current); + animation2->setTargetObject(currItem); + animation2->setPropertyName("sizeHint"); + //animation2->setDuration(500); + animation2->setStartValue(unselectedSize); + animation2->setEndValue(selectedSize); + animGroup->addAnimation(animation2); + } + animGroup->start(); +} + +void +KThemedGreeter::slotPasswordEntered(const QString &password) +{ + QString user = static_cast<UserSliderViewItem *>(userSliderView->currentItem())->login; + verify->setUser(user); + slotLoadPrevWM(); + verify->setPassword(password); + accept(); +} + + +void +KThemedGreeter::slotTabPressed(const int key) +{ + QListWidgetItem *current = userSliderView->currentItem(); + int next = 0; + if (current) { + int count = userSliderView->count(); + int row = userSliderView->row(current); + switch (key) { + case Qt::Key_Tab: { + next = 0; + if (row < count - 1) + next = row + 1; + } + break; + case Qt::Key_Backtab: { + next = count - 1; + if (row > 0) + next = row - 1; + } + break; + } + } + userSliderView->setCurrentItem(userSliderView->item(next)); +} + +void +KThemedGreeter::slotAnimationFinished() +{ + QListWidgetItem *current = userSliderView->currentItem(); + int row = userSliderView->row(current); + QWidget *editor = userSliderView->itemEditor(row); + userSliderView->setFocusProxy(editor); + editor->setFocus(); +} + +void +KThemedGreeter::slotLayoutChanged() +{ +// qDebug("KThemedGreeter::slotLayoutChanged"); + if (userSliderView) { + LayoutUnit layoutUnit = X11Helper::getCurrentLayout(); + QString lang = layoutUnit.layout.left(2); + for (int i = 0; i < userSliderView->model()->rowCount(); i++) { + PasswordEditor *editor = static_cast<PasswordEditor*>(userSliderView->itemEditor(i)); + editor->lineEdit()->setLangIndicator(lang); + } + } +} + +void KThemedGreeter::keyPressEvent(QKeyEvent *e) { inherited::keyPressEvent(e); if (!(e->modifiers() & ~Qt::KeypadModifier) && (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)) accept(); + + slotLayoutChanged(); } #include "kgreeter.moc" diff --git a/kdm/kfrontend/kgreeter.h b/kdm/kfrontend/kgreeter.h index aabf256..865945c 100644 --- a/kdm/kfrontend/kgreeter.h +++ b/kdm/kfrontend/kgreeter.h @@ -4,6 +4,7 @@ Greeter widget for kdm Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org> Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org> +Copyright (C) 2011 ROSA Lab. <ural.mullabaev@rosalab.ru> This program is free software; you can redistribute it and/or modify @@ -29,13 +30,185 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "kgverify.h" #include "kgdialog.h" -class UserListView; +#include <x11_helper.h> + +#include <QListWidget> +#include <QListWidgetItem> +#include <QPropertyAnimation> +#include <QStyledItemDelegate> +#include <QLineEdit> + class KdmClock; class KdmItem; class KConfigGroup; class QListWidgetItem; class QActionGroup; +class QParallelAnimationGroup; +//class XEventNotifier; + +class UserSliderViewItem; +class LineEdit; + +class UserListView : public QListWidget { + public: + UserListView(QWidget *parent = 0); + + mutable QSize cachedSizeHint; + + protected: + virtual QSize sizeHint() const; + virtual void keyPressEvent(QKeyEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *event); + + private: + + bool m_suppressClick; +}; + +class UserListViewItem : public QListWidgetItem { + public: + UserListViewItem(UserListView *parent, const QString &text, + const QIcon &icon, const QString &username); + + QString login; +}; + +class UserSliderView : public UserListView { + Q_OBJECT + public: + UserSliderView(QWidget *parent = 0); + + QWidget *itemEditor(const int row); + void setItemEditor(const int row, QWidget *widget); + + public Q_SLOTS: + void layoutItems(); + + protected: + virtual void updateGeometries(); + virtual void resizeEvent(QResizeEvent *event); + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dragLeaveEvent(QDragLeaveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void wheelEvent (QWheelEvent *event); + //virtual bool eventFilter(QObject *object, QEvent *event); + + private: + QVector<QWidget *> itemEditors; +}; + +class UserSliderViewItem : public QObject, public UserListViewItem { + Q_OBJECT + Q_PROPERTY(QSize sizeHint READ sizeHint WRITE setSizeHint) + public: + UserSliderViewItem(UserSliderView *parent, const QString &text, + const QIcon &icon, const QString &username); + ~UserSliderViewItem(); +}; + +class UserSliderViewItemAnimation : public QPropertyAnimation { + Q_OBJECT + public: + UserSliderViewItemAnimation(QObject *parent = 0); + + Q_SIGNALS: + void sizeChanged(); + + protected: + void updateCurrentValue(const QVariant &value); +}; + +class UserItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + + public: + UserItemDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {} + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, + const QModelIndex & index) const; + + Q_SIGNALS: + void passwordEntered(const QString &password); + void tabPressed(const int key); + + private slots: + void slotTextEntered(const QString &text); + void slotTabPressed(const int key); + + private: + void drawText(QPainter *p, const QStyleOptionViewItemV4 *option, const QRect &rect) const; +}; + +class PasswordEditor : public QWidget +{ + Q_OBJECT + + public: + PasswordEditor(QWidget *parent = 0); + ~PasswordEditor(); + + LineEdit* lineEdit() const; + + Q_SIGNALS: + void textEntered(const QString &text); + void tabPressed(const int key); + + private slots: + void slotReturnPressed(); + void slotTabPressed(const int key); + + private: + QVBoxLayout *layout; + LineEdit *edit; + QPushButton *button; +}; + +class LineEdit : public QLineEdit +{ + Q_OBJECT + + public: + LineEdit(QWidget *parent = 0); + ~LineEdit() {}; + + void setLangIndicator(const QString &lang); + void setCapsIndicator(const bool caps); + + Q_SIGNALS: + void tabPressed(const int key); + void loginPressed(); + + protected: + virtual bool event(QEvent *event); + virtual void leaveEvent(QEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void paintEvent(QPaintEvent *event); + + private: + enum { buttonWidth = 33 }; + + QString language; + bool capsLocked; + bool loginActive; + bool loginHover; + QIcon loginIcon; + bool indicatorChanged; +}; + struct SessType { QString name, type; @@ -61,6 +234,8 @@ class KGreeter : public KGDialog, public KGVerifyHandler { KGreeter(bool themed = false); ~KGreeter(); + int imageSize; + public Q_SLOTS: void accept(); void reject(); @@ -69,8 +244,9 @@ class KGreeter : public KGDialog, public KGVerifyHandler { void slotUserEntered(); protected: - void insertUser(const QImage &, const QString &, struct passwd *); - void insertUsers(); + virtual void insertUser(const QIcon &, const QString &, struct passwd *); + virtual void insertUsers(); + virtual void removeUsers(); void putSession(const QString &, const QString &, bool, const char *); void insertSessions(); virtual void pluginSetup(); @@ -91,7 +267,7 @@ class KGreeter : public KGDialog, public KGVerifyHandler { static int curPlugin; static PluginList pluginList; - private Q_SLOTS: + protected Q_SLOTS: void slotLoadPrevWM(); public: // from KGVerifyHandler @@ -132,13 +308,25 @@ class KThemedGreeter : public KGreeter { ~KThemedGreeter(); public Q_SLOTS: + void slotUserEntered(); void slotThemeActivated(const QString &id); void slotSessMenu(); void slotActionMenu(); + void slotHalt(); + void slotReboot(); void slotDebugToggled(); + void slotCurrentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + void slotPasswordEntered(const QString &password); + void slotTabPressed(const int key); + void slotAnimationFinished(); + void slotLayoutChanged(); protected: + virtual void insertUser(const QIcon &, const QString &, struct passwd *); +// virtual void insertUsers(); virtual void updateStatus(bool fail, bool caps, int timedleft); + virtual void updateLockStatus(); + virtual void setPreselUser(); virtual void pluginSetup(); virtual void keyPressEvent(QKeyEvent *); virtual bool event(QEvent *e); @@ -147,12 +335,22 @@ class KThemedGreeter : public KGreeter { // KdmClock *clock; KdmThemer *themer; KdmItem *caps_warning, *xauth_warning, *pam_error, *timed_label, - *userlist_node, *userlist_rect, - *session_button, *system_button; + *userlist_node, *userlist_rect, *userslider_node, *userslider_rect, + *session_button, *system_button, *halt_button, *reboot_button; + + UserSliderView *userSliderView; + UserItemDelegate *userItemDelegate; + + QParallelAnimationGroup *animGroup; + UserSliderViewItemAnimation *animation1; + UserSliderViewItemAnimation *animation2; + +// XEventNotifier xEventNotifier; -// public: // from KGVerifyHandler -// virtual void verifyFailed(); -// virtual void verifyRetry(); + public: // from KGVerifyHandler + virtual void verifyClear(); + virtual void verifyFailed(); + virtual void verifyRetry(); }; #endif /* KGREETER_H */ diff --git a/kdm/kfrontend/kgverify.cpp b/kdm/kfrontend/kgverify.cpp index 453f36d..0330989 100644 --- a/kdm/kfrontend/kgverify.cpp +++ b/kdm/kfrontend/kgverify.cpp @@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "kgverify.h" #include "kdmconfig.h" #include "kdm_greet.h" +#include "kgreet_classic.h" #include "themer/kdmthemer.h" #include "themer/kdmitem.h" @@ -301,6 +302,22 @@ KGVerify::setUser(const QString &user) talkerEdits(); } +QString +KGVerify::getUser() const +{ + return curUser; +} + +void +KGVerify::setPassword(const QString &password) +{ + if (!isClassic()) + return; + debug("%s->setPassword(%\"s)\n", pName.data(), true ? "<masked>" : qPrintable(password)); + KClassicGreeter *greeter = static_cast<KClassicGreeter *>(greet); + greeter->setPassword(password); +} + void KGVerify::start() { @@ -385,11 +402,12 @@ KGVerify::doReject(bool initial) debug("%s->abort()\n", pName.data()); greet->abort(); } - handler->verifyClear(); + //handler->verifyClear(); debug("%s->clear()\n", pName.data()); greet->clear(); curUser.clear(); pamUser.clear(); + handler->verifyClear(); if (!scheduleAutoLogin(initial)) { isClear = !(isClear && applyPreset()); if (running) { diff --git a/kdm/kfrontend/kgverify.h b/kdm/kfrontend/kgverify.h index c71dc48..ab44eaa 100644 --- a/kdm/kfrontend/kgverify.h +++ b/kdm/kfrontend/kgverify.h @@ -102,6 +102,8 @@ class KGVerify : public QObject, public KGreeterPluginHandler { void presetEntity(const QString &entity, int field); QString getEntity() const; void setUser(const QString &user); + QString getUser() const; + void setPassword(const QString &password); virtual void selectPlugin(int id); bool entitiesLocal() const; bool entitiesFielded() const; diff --git a/libs/kdm/kgreet_classic.cpp b/libs/kdm/kgreet_classic.cpp index a9d920e..f0e97b5 100644 --- a/libs/kdm/kgreet_classic.cpp +++ b/libs/kdm/kgreet_classic.cpp @@ -191,6 +191,12 @@ KClassicGreeter::setUser(const QString &user) } void // virtual +KClassicGreeter::setPassword(const QString &password) +{ + passwdEdit->setText(password); +} + +void // virtual KClassicGreeter::setEnabled(bool enable) { // assert(!passwd1Label); diff --git a/libs/kdm/kgreet_classic.h b/libs/kdm/kgreet_classic.h index 612f7f3..03b0a7a 100644 --- a/libs/kdm/kgreet_classic.h +++ b/libs/kdm/kgreet_classic.h @@ -47,6 +47,7 @@ class KClassicGreeter : public QObject, public KGreeterPlugin { virtual void presetEntity(const QString &entity, int field); virtual QString getEntity() const; virtual void setUser(const QString &user); + virtual void setPassword(const QString &password); virtual void setEnabled(bool on); virtual bool textMessage(const char *message, bool error); virtual void textPrompt(const char *prompt, bool echo, bool nonBlocking);