/*
* Copyright (c) 2012-2015 Christian Surlykke, Petr Vanek
*
* This file is part of qt-lightdm-greeter
* It is distributed under the LGPL 2.1 or later license.
* Please refer to the LICENSE file for a copy of the license.
*/
#include <QRect>
#include <QApplication>
#include <QDesktopWidget>
#include <QPalette>
#include <QString>
#include <QtDebug>
#include <QTextEdit>
#include <QStyleFactory>
#include <QLabel>
#include <QSvgWidget>
#include <QSvgRenderer>
#include <QAbstractTextDocumentLayout>
#include "mainwindow.h"
#include "loginform.h"
#include "settings.h"
#include "global.h"
static const Settings _settings;
MainWindow::MainWindow(bool primary, int screen, const QRect &screenRect, QWidget *parent) :
QWidget(parent),
m_ScreenRect(screenRect),
m_Primary(primary),
m_messages(nullptr)
{
setObjectName(QString("MainWindow_%1").arg(screen));
setGeometry(screenRect);
setBackground();
// TODO: Check if testMode == false and greeter == NULL, if so display big error message
// instead of exiting/crashing
// display login dialog only in the main screen
int spaceY = screenRect.height() / 2;
if (showLoginForm()) {
m_LoginForm = new LoginForm(this);
spaceY -= m_LoginForm->height() / 2;
int maxX = screenRect.width() - m_LoginForm->width();
int maxY = screenRect.height() - m_LoginForm->height();
int defaultX = 50*maxX/100;
int defaultY = 50*maxY/100;
int offsetX = getOffset(_settings.offsetX(), maxX, defaultX);
int offsetY = getOffset(_settings.offsetY(), maxY, defaultY);
m_LoginForm->move(offsetX, offsetY);
m_LoginForm->show();
// This hack ensures that the primary screen will have focus
// if there are more screens (move the mouse cursor in the center
// of primary screen - not in the center of all X area). It
// won't affect single-screen environments.
int centerX = screenRect.width()/2 + screenRect.x();
int centerY = screenRect.height()/2 + screenRect.y();
QCursor::setPos(centerX, centerY);
}
// Banner
if (!_settings.bannerImagePath().isEmpty()) {
qWarning() << "Have banner " << _settings.bannerImagePath();
QSvgWidget *banner = new QSvgWidget(_settings.bannerImagePath(), this);
qWarning() << banner->sizeHint();
if (banner->renderer()->isValid()) {
int bw, bh;
QSize sh = banner->sizeHint();
if (sh.height() < spaceY) {
banner->setFixedSize(sh);
bw = sh.width();
bh = sh.height();
} else {
bh = spaceY - 20;
bw = sh.width() * bh / sh.height();
banner->setFixedSize(bw, bh);
}
banner->move((screenRect.width() - bw) / 2, (spaceY - bh) / 2);
}
}
int ls = (spaceY > 500 ? 500 : spaceY);
if (ls > screenRect.height() / 5) ls = screenRect.height() / 5;
if (ls > screenRect.width() / 5) ls = screenRect.width() / 5;
QRect logoRect(QPoint(0, screenRect.height() - ls), QSize(ls, ls));
QSize logoSize = createLogo(logoRect);
QRect distroRect(QPoint(screenRect.width() - ls, screenRect.height() - ls), QSize(ls, ls));
QSize distroSize = createDistro(distroRect);
if (showLoginForm()) {
QRect lwSize(QPoint(logoSize.width(), screenRect.height() * 3/4), QPoint(screenRect.width() - distroSize.width(), screenRect.height()));
lwSize.adjust(10, 10, -10, -10);
createLogWindow(lwSize);
}
}
MainWindow::~MainWindow()
{
}
QSize MainWindow::createLogo(const QRect &max)
{
QString path = _settings.bottomLeftLogoPath();
if (path.isEmpty())
return QSize(0, 0);
QSvgWidget *img = new QSvgWidget(path, this);
if (!img->renderer()->isValid())
return QSize(0, 0);
QSize sh = img->sizeHint();
int w = sh.width(), h = sh.height();
// This requires that the given rect is square
if (w > h) {
if (w != max.width()) {
h = h * max.width() / w;
w = max.width();
}
} else {
if (h != max.height()) {
w = w * max.height() / h;
h = max.height();
}
}
QSize size(w, h);
QRect c(max);
c.adjust(0, max.height() - h, -(max.width() - w), 0);
c.adjust(10, 10, -10, -10);
img->setGeometry(c);
return size;
}
QSize MainWindow::createDistro(const QRect &max)
{
QImage img(QString("/etc/distro.png"));
if (img.isNull())
return QSize(0, 0);
int w = img.width(), h = img.height();
if (w+20 > max.width() || h+20 > max.height()) {
// This requires that the given rect is square
QRect cmp(max);
cmp.adjust(0, 0, -20, -20);
if (w > h) {
if (w != cmp.width()) {
h = h * cmp.width() / w;
w = cmp.width();
}
} else {
if (h != cmp.height()) {
w = w * cmp.height() / h;
h = cmp.height();
}
}
img = img.scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation);
cmp.adjust(0, 0, 20, 20);
}
w += 20;
h += 20;
QPixmap pixmap(QPixmap::fromImage(img));
QLabel *lbl = new QLabel(this);
lbl->setPixmap(pixmap);
QSize size(w, h);
QRect c(max);
c.adjust(max.width() - w, max.height() - h, 0, 0);
c.adjust(10, 10, -10, -10);
lbl->setGeometry(c);
return size;
}
void MainWindow::createLogWindow(const QRect& geom)
{
QString path = _settings.logMessageFile();
if (path.isEmpty())
return;
QFile f(path);
if (f.size() == 0 || !f.open(QFile::ReadOnly))
return;
m_messages = new QTextEdit(this);
m_messages->setGeometry(geom);
int ps = geom.height() / 20;
if (ps > 20) ps = 20;
m_messages->setFontPointSize(ps);
QTextStream stream(&f);
const QColor black(Qt::black);
while (!stream.atEnd()) {
bool ok = false;
QString line(stream.readLine());
int i = line.indexOf(' ');
if (i > 0) {
QString scol(line.left(i));
uint col = scol.toUInt(&ok, 16);
if (ok) {
m_messages->setTextColor(QColor(QRgb(col)));
line = line.mid(i + 1);
}
}
if (!ok) {
m_messages->setTextColor(black);
}
m_messages->append(line);
}
m_messages->setReadOnly(true);
m_messages->setStyleSheet("border:none; background:rgba(255,255,255,.33); border-radius:5px");
}
bool MainWindow::showLoginForm()
{
return m_Primary;
}
void MainWindow::setFocus(Qt::FocusReason reason)
{
if (m_LoginForm) {
m_LoginForm->setFocus(reason);
}
else {
QWidget::setFocus(reason);
}
}
int MainWindow::getOffset(QString settingsOffset, int maxVal, int defaultVal)
{
int offset = defaultVal > maxVal ? maxVal : defaultVal;
if (! settingsOffset.isEmpty()) {
if (QRegExp("^\\d+px$", Qt::CaseInsensitive).exactMatch(settingsOffset)) {
offset = settingsOffset.left(settingsOffset.size() - 2).toInt();
if (offset > maxVal) offset = maxVal;
}
else if (QRegExp("^\\d+%$", Qt::CaseInsensitive).exactMatch(settingsOffset)) {
int offsetPct = settingsOffset.left(settingsOffset.size() -1).toInt();
if (offsetPct > 100) offsetPct = 100;
offset = (maxVal * offsetPct)/100;
}
else {
qWarning() << "Could not understand" << settingsOffset
<< "- must be of form <positivenumber>px or <positivenumber>%, e.g. 35px or 25%" ;
}
}
return offset;
}
void MainWindow::setBackground()
{
Qt::AspectRatioMode arMode = Qt::KeepAspectRatioByExpanding;
QString bgPath = _settings.backgrundImagePath();
if (bgPath.length() != 0) {
m_background = QImage(bgPath);
if (m_background.isNull()) {
qWarning() << "Not able to read" << bgPath << "as image";
}
}
if (m_background.isNull()) {
m_background = Global::getConfigGradient();
arMode = Qt::IgnoreAspectRatio;
}
if (m_background.isNull()) {
// Hard-coded default: Gradient
m_background = QImage(2, 2, QImage::Format_RGB32);
m_background.setPixel(0, 0, 0xffffffff);
m_background.setPixel(1, 0, 0xffffffff);
m_background.setPixel(0, 1, 0xff888687);
m_background.setPixel(1, 1, 0xfff9a72b);
}
QPalette palette;
if (m_background.isNull()) {
palette.setColor(QPalette::Background, Qt::black);
}
else {
m_background = m_background.scaled(m_ScreenRect.width(), m_ScreenRect.height(), arMode, Qt::SmoothTransformation);
int xoff = (m_background.width() - m_ScreenRect.width()) / 2;
int yoff = (m_background.height() - m_ScreenRect.height()) / 2;
if (xoff != 0 || yoff != 0) {
m_background = m_background.copy(xoff, yoff, m_background.width(), m_background.height());
}
QBrush brush(m_background);
palette.setBrush(this->backgroundRole(), brush);
}
this->setPalette(palette);
}