tdesktop/Telegram/SourceFiles/window.cpp

656 lines
16 KiB
C++

/*
This file is part of Telegram Desktop,
an unofficial desktop messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#include "stdafx.h"
#include "style.h"
#include "lang.h"
#include "window.h"
#include "application.h"
#include "pspecific.h"
#include "title.h"
#include "intro/intro.h"
#include "mainwidget.h"
#include "layerwidget.h"
#include "settingswidget.h"
ConnectingWidget::ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect) : QWidget(parent), _reconnect(this, QString()), _shadow(st::boxShadow) {
set(text, reconnect);
connect(&_reconnect, SIGNAL(clicked()), this, SLOT(onReconnect()));
}
void ConnectingWidget::set(const QString &text, const QString &reconnect) {
_text = text;
_textWidth = st::linkFont->m.width(_text) + st::linkFont->spacew;
int32 _reconnectWidth = 0;
if (reconnect.isEmpty()) {
_reconnect.hide();
} else {
_reconnect.setText(reconnect);
_reconnect.show();
_reconnect.move(st::connectingPadding.left() + _textWidth, st::boxShadow.pxHeight() + st::connectingPadding.top());
_reconnectWidth = _reconnect.width();
}
resize(st::connectingPadding.left() + _textWidth + _reconnectWidth + st::connectingPadding.right() + st::boxShadow.pxWidth(), st::boxShadow.pxHeight() + st::connectingPadding.top() + st::linkFont->height + st::connectingPadding.bottom());
update();
}
void ConnectingWidget::paintEvent(QPaintEvent *e) {
QPainter p(this);
_shadow.paint(p, QRect(0, st::boxShadow.pxHeight(), width() - st::boxShadow.pxWidth(), height() - st::boxShadow.pxHeight()), QPoint(0, 0), BoxShadow::Top | BoxShadow::Right);
p.fillRect(0, st::boxShadow.pxHeight(), width() - st::boxShadow.pxWidth(), height() - st::boxShadow.pxHeight(), st::connectingBG->b);
p.setFont(st::linkFont->f);
p.setPen(st::connectingColor->p);
p.drawText(st::connectingPadding.left(), st::boxShadow.pxHeight() + st::connectingPadding.top() + st::linkFont->ascent, _text);
}
void ConnectingWidget::onReconnect() {
MTP::restart();
}
TempDirDeleter::TempDirDeleter(QThread *thread) {
moveToThread(thread);
connect(thread, SIGNAL(started()), this, SLOT(onStart()));
}
void TempDirDeleter::onStart() {
if (QDir(cTempDir()).removeRecursively()) {
emit succeed();
} else {
emit failed();
}
}
Window::Window(QWidget *parent) : PsMainWindow(parent),
dragging(false), intro(0), main(0), settings(0), layer(0), layerBG(0), myIcon(QPixmap::fromImage(icon256)), _topWidget(0),
_connecting(0), _inactivePress(false), _tempDeleter(0), _tempDeleterThread(0) {
if (objectName().isEmpty())
setObjectName(qsl("MainWindow"));
resize(st::wndDefWidth, st::wndDefHeight);
setWindowOpacity(1);
setLocale(QLocale(QLocale::English, QLocale::UnitedStates));
centralwidget = new QWidget(this);
centralwidget->setObjectName(qsl("centralwidget"));
setCentralWidget(centralwidget);
QMetaObject::connectSlotsByName(this);
_inactiveTimer.setSingleShot(true);
connect(&_inactiveTimer, SIGNAL(timeout()), this, SLOT(onInactiveTimer()));
}
void Window::inactivePress(bool inactive) {
_inactivePress = inactive;
if (_inactivePress) {
_inactiveTimer.start(200);
} else {
_inactiveTimer.stop();
}
}
bool Window::inactivePress() const {
return _inactivePress;
}
void Window::onInactiveTimer() {
inactivePress(false);
}
void Window::init() {
psInitFrameless();
setWindowIcon(myIcon);
App::app()->installEventFilter(this);
connect(windowHandle(), SIGNAL(activeChanged()), this, SLOT(checkHistoryActivation()));
QPalette p(palette());
p.setColor(QPalette::Window, st::wndBG->c);
setPalette(p);
title = new TitleWidget(this);
psInitSize();
psUpdateWorkmode();
}
void Window::clearWidgets() {
layerHidden();
if (settings) {
anim::stop(settings);
settings->hide();
settings->deleteLater();
settings = 0;
}
if (main) {
anim::stop(main);
main->hide();
main->deleteLater();
main = 0;
}
if (intro) {
anim::stop(intro);
intro->hide();
intro->deleteLater();
intro = 0;
}
}
void Window::setupIntro(bool anim) {
if (intro && (intro->animating() || intro->isVisible()) && !main) return;
QPixmap bg = myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight));
clearWidgets();
intro = new IntroWidget(this);
intro->move(0, st::titleHeight);
if (anim) {
intro->animShow(bg);
}
fixOrder();
updateTitleStatus();
}
void Window::getNotifySetting(const MTPInputNotifyPeer &peer, uint32 msWait) {
MTP::send(MTPaccount_GetNotifySettings(peer), main->rpcDone(&MainWidget::gotNotifySetting, peer), main->rpcFail(&MainWidget::failNotifySetting, peer), 0, msWait);
}
void Window::setupMain(bool anim) {
QPixmap bg = myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight));
clearWidgets();
main = new MainWidget(this);
main->move(0, st::titleHeight);
if (anim) {
main->animShow(bg);
} else {
MTP::send(MTPusers_GetUsers(MTP_vector<MTPInputUser>(QVector<MTPInputUser>(1, MTP_inputUserSelf()))), main->rpcDone(&MainWidget::startFull));
main->activate();
}
fixOrder();
updateTitleStatus();
}
void Window::showSettings() {
App::wnd()->hideLayer();
if (settings) {
return hideSettings();
}
QPixmap bg = myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight));
if (intro) {
anim::stop(intro);
intro->hide();
} else if (main) {
anim::stop(main);
main->hide();
}
settings = new Settings(this);
settings->animShow(bg);
fixOrder();
}
void Window::hideSettings(bool fast) {
if (!settings) return;
if (fast) {
anim::stop(settings);
settings->hide();
settings->deleteLater();
settings = 0;
if (intro) {
intro->show();
} else {
main->show();
}
} else {
QPixmap bg = myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight));
anim::stop(settings);
settings->hide();
settings->deleteLater();
settings = 0;
if (intro) {
intro->animShow(bg, true);
} else {
main->animShow(bg, true);
}
}
fixOrder();
}
void Window::startMain(const MTPUser &user) {
if (main) main->start(user);
title->resizeEvent(0);
}
void Window::mtpStateChanged(int32 dc, int32 state) {
if (dc == MTP::maindc()) {
updateTitleStatus();
if (settings) settings->updateConnectionType();
}
}
void Window::updateTitleStatus() {
int32 state = MTP::dcstate();
if (state == MTProtoConnection::Connecting || state == MTProtoConnection::Disconnected || state < 0 && state > -600) {
if (main || getms() > 5000 || _connecting) {
showConnecting(lang(lng_connecting));
}
} else if (state < 0) {
showConnecting(lang(lng_reconnecting).arg((-state) / 1000), lang(lng_reconnecting_try_now));
QTimer::singleShot((-state) % 1000, this, SLOT(updateTitleStatus()));
} else {
hideConnecting();
}
}
IntroWidget *Window::introWidget() {
return intro;
}
MainWidget *Window::mainWidget() {
return main;
}
Settings *Window::settingsWidget() {
return settings;
}
void Window::showPhoto(const PhotoLink *lnk, HistoryItem *item) {
return showPhoto(lnk->photo(), item);
}
void Window::showPhoto(PhotoData *photo, HistoryItem *item) {
layerHidden();
layer = new LayerWidget(this, photo, item);
}
PhotoData *Window::photoShown() {
return layer ? layer->photoShown() : 0;
}
/*
void Window::showVideo(const VideoOpenLink *lnk, HistoryItem *item) {
layerHidden();
VideoData *video = App::video(lnk->video());
layer = new LayerWidget(this, video, item);
}
/**/
void Window::showLayer(LayeredWidget *w) {
layerHidden();
layerBG = new BackgroundWidget(this, w);
}
void Window::showConnecting(const QString &text, const QString &reconnect) {
if (_connecting) {
_connecting->set(text, reconnect);
} else {
_connecting = new ConnectingWidget(this, text, reconnect);
resizeEvent(0);
}
}
void Window::hideConnecting() {
if (_connecting) {
_connecting->deleteLater();
_connecting = 0;
}
}
void Window::replaceLayer(LayeredWidget *w) {
if (layer) layer->deleteLater();
layer = 0;
if (layerBG) {
layerBG->replaceInner(w);
} else {
layerBG = new BackgroundWidget(this, w);
}
}
void Window::hideLayer() {
if (layerBG) {
layerBG->onClose();
}
}
bool Window::layerShown() {
return !!layerBG || !!_topWidget;
}
bool Window::historyIsActive(int state) const {
return psIsActive(state) && main && main->historyIsActive() && (!settings || !settings->isVisible());
}
void Window::checkHistoryActivation(int state) {
if (main && MTP::authedId() && historyIsActive(state)) {
main->historyWasRead();
}
}
void Window::layerHidden() {
if (layer) layer->deleteLater();
layer = 0;
if (layerBG) layerBG->deleteLater();
layerBG = 0;
if (main) main->setInnerFocus();
}
QRect Window::clientRect() const {
return QRect(0, st::titleHeight, width(), height() - st::titleHeight);
}
QRect Window::photoRect() const {
if (settings) {
return settings->geometry();
} else if (main) {
QRect r(main->historyRect());
r.moveLeft(r.left() + main->x());
r.moveTop(r.top() + main->y());
return r;
}
return QRect(0, 0, 0, 0);
}
void Window::wStartDrag(QMouseEvent *e) {
dragStart = e->globalPos() - frameGeometry().topLeft();
dragging = true;
}
void Window::paintEvent(QPaintEvent *e) {
}
HitTestType Window::hitTest(const QPoint &p) const {
int x(p.x()), y(p.y()), w(width()), h(height());
const uint32 raw = psResizeRowWidth();
if (!windowState().testFlag(Qt::WindowMaximized)) {
if (y < raw) {
if (x < raw) {
return HitTestTopLeft;
} else if (x > w - raw - 1) {
return HitTestTopRight;
}
return HitTestTop;
} else if (y > h - raw - 1) {
if (x < raw) {
return HitTestBottomLeft;
} else if (x > w - raw - 1) {
return HitTestBottomRight;
}
return HitTestBottom;
} else if (x < raw) {
return HitTestLeft;
} else if (x > w - raw - 1) {
return HitTestRight;
}
}
HitTestType titleTest = title->hitTest(p - title->geometry().topLeft());
if (titleTest && (!layer || titleTest != HitTestCaption)) {
return titleTest;
} else if (x >= 0 && y >= 0 && x < w && y < h) {
return HitTestClient;
}
return HitTestNone;
}
bool Window::getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const {
if (main && main->getPhotoCoords(photo, x, y, w)) {
x += main->x();
y += main->y();
return true;
} else if (settings && settings->getPhotoCoords(photo, x, y, w)) {
x += main->x();
y += main->y();
return true;
}
return false;
}
bool Window::getVideoCoords(VideoData *video, int32 &x, int32 &y, int32 &w) const {
if (main && main->getVideoCoords(video, x, y, w)) {
x += main->x();
y += main->y();
return true;
}
return false;
}
QRect Window::iconRect() const {
return QRect(st::titleIconPos + title->geometry().topLeft(), st::titleIconRect.pxSize());
}
bool Window::eventFilter(QObject *obj, QEvent *evt) {
if (obj == App::app() && (evt->type() == QEvent::ApplicationActivate)) {
QTimer::singleShot(1, this, SLOT(checkHistoryActivation()));
}
return PsMainWindow::eventFilter(obj, evt);
}
void Window::mouseMoveEvent(QMouseEvent *e) {
if (e->buttons() & Qt::LeftButton) {
if (dragging) {
if (windowState().testFlag(Qt::WindowMaximized)) {
setWindowState(windowState() & ~Qt::WindowMaximized);
dragStart = e->globalPos() - frameGeometry().topLeft();
} else {
move(e->globalPos() - dragStart);
}
}
} else if (dragging) {
dragging = false;
}
}
void Window::mouseReleaseEvent(QMouseEvent *e) {
dragging = false;
}
bool Window::minimizeToTray() {
if (App::quiting() || !trayIcon) return false;
hide();
if (!cSeenTrayTooltip()) {
trayIcon->showMessage(QString::fromStdWString(AppName), lang(lng_tray_icon_text), QSystemTrayIcon::Information, 10000);
cSetSeenTrayTooltip(true);
App::writeConfig();
}
if (App::main()) App::main()->setOnline(windowState());
return true;
}
void Window::setupTrayIcon() {
if (!trayIcon) {
if (trayIconMenu) trayIconMenu->deleteLater();
trayIconMenu = new QMenu(this);
trayIconMenu->setFont(QFont("Tahoma"));
QAction *a;
a = trayIconMenu->addAction(lang(lng_open_from_tray), this, SLOT(showFromTray()));
a->setEnabled(true);
a = trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray()));
a->setEnabled(true);
if (trayIcon) trayIcon->deleteLater();
trayIcon = new QSystemTrayIcon(this);
trayIcon->setIcon(this->windowIcon());
trayIcon->setContextMenu(trayIconMenu);
trayIcon->setToolTip(QString::fromStdWString(AppName));
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(toggleTray(QSystemTrayIcon::ActivationReason)));
connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showFromTray()));
}
psUpdateCounter();
trayIcon->show();
}
void Window::quitFromTray() {
App::quit();
}
void Window::activate() {
bool wasHidden = !isVisible();
setWindowState(windowState() & ~Qt::WindowMinimized);
setVisible(true);
activateWindow();
if (wasHidden) {
if (main) {
main->windowShown();
}
}
}
void Window::noIntro(IntroWidget *was) {
if (was == intro) {
intro = 0;
}
}
void Window::noSettings(Settings *was) {
if (was == settings) {
settings = 0;
}
checkHistoryActivation();
}
void Window::noMain(MainWidget *was) {
if (was == main) {
main = 0;
}
}
void Window::noLayer(LayerWidget *was) {
if (was == layer) {
layer = 0;
}
fixOrder();
}
void Window::noBox(BackgroundWidget *was) {
if (was == layerBG) {
layerBG = 0;
}
}
void Window::fixOrder() {
title->raise();
if (layer) layer->raise();
if (layerBG) layerBG->raise();
if (_topWidget) _topWidget->raise();
if (_connecting) _connecting->raise();
}
void Window::topWidget(QWidget *w) {
_topWidget = w;
}
void Window::noTopWidget(QWidget *w) {
if (_topWidget == w) {
_topWidget = 0;
}
}
void Window::showFromTray(QSystemTrayIcon::ActivationReason reason) {
if (reason != QSystemTrayIcon::Context) {
activate();
setWindowIcon(myIcon);
psUpdateCounter();
if (App::main()) App::main()->setOnline(windowState());
}
}
void Window::toggleTray(QSystemTrayIcon::ActivationReason reason) {
if (reason != QSystemTrayIcon::Context) {
if (psIsActive()) {
minimizeToTray();
} else {
showFromTray(reason);
}
}
}
void Window::closeEvent(QCloseEvent *e) {
if (MTP::authedId() && minimizeToTray()) {
e->ignore();
} else {
App::quit();
}
}
TitleWidget *Window::getTitle() {
return title;
}
void Window::resizeEvent(QResizeEvent *e) {
title->setGeometry(QRect(0, 0, width(), st::titleHeight + st::titleShadow));
if (layer) layer->resize(width(), height());
if (layerBG) layerBG->resize(width(), height());
if (_connecting) _connecting->setGeometry(0, height() - _connecting->height(), _connecting->width(), _connecting->height());
emit resized(QSize(width(), height() - st::titleHeight));
}
Window::TempDirState Window::tempDirState() {
if (_tempDeleter) {
return TempDirRemoving;
}
return QDir(cTempDir()).exists() ? TempDirExists : TempDirEmpty;
}
void Window::tempDirDelete() {
if (_tempDeleter) return;
_tempDeleterThread = new QThread();
_tempDeleter = new TempDirDeleter(_tempDeleterThread);
connect(_tempDeleter, SIGNAL(succeed()), this, SLOT(onTempDirCleared()));
connect(_tempDeleter, SIGNAL(failed()), this, SLOT(onTempDirClearFailed()));
_tempDeleterThread->start();
}
void Window::onTempDirCleared() {
_tempDeleter->deleteLater();
_tempDeleter = 0;
_tempDeleterThread->deleteLater();
_tempDeleterThread = 0;
emit tempDirCleared();
}
void Window::onTempDirClearFailed() {
_tempDeleter->deleteLater();
_tempDeleter = 0;
_tempDeleterThread->deleteLater();
_tempDeleterThread = 0;
emit tempDirClearFailed();
}
Window::~Window() {
delete _tempDeleter;
delete _tempDeleterThread;
delete _connecting;
delete trayIcon;
delete trayIconMenu;
delete intro;
delete main;
delete layer;
delete settings;
}