diff --git a/Telegram/SourceFiles/core/basic_types.cpp b/Telegram/SourceFiles/core/basic_types.cpp index 2eaa56db8..65d9910d7 100644 --- a/Telegram/SourceFiles/core/basic_types.cpp +++ b/Telegram/SourceFiles/core/basic_types.cpp @@ -254,7 +254,7 @@ namespace { namespace ThirdParty { void start() { - PlatformSpecific::ThirdParty::start(); + Platform::ThirdParty::start(); if (!RAND_status()) { // should be always inited in all modern OS char buf[16]; @@ -293,7 +293,7 @@ namespace ThirdParty { delete[] _sslLocks; _sslLocks = 0; - PlatformSpecific::ThirdParty::finish(); + Platform::ThirdParty::finish(); } } diff --git a/Telegram/SourceFiles/main.cpp b/Telegram/SourceFiles/main.cpp index 0b704044b..e010502d5 100644 --- a/Telegram/SourceFiles/main.cpp +++ b/Telegram/SourceFiles/main.cpp @@ -41,8 +41,8 @@ int main(int argc, char *argv[]) { } // both are finished in Application::closeApplication - Logs::start(); // must be started before PlatformSpecific is started - PlatformSpecific::start(); // must be started before QApplication is created + Logs::start(); // must be started before Platform is started + Platform::start(); // must be started before QApplication is created // prepare fake args to disable QT_STYLE_OVERRIDE env variable // currently this is required in some desktop environments, including Xubuntu 15.10 @@ -76,7 +76,7 @@ int main(int argc, char *argv[]) { } SignalHandlers::finish(); - PlatformSpecific::finish(); + Platform::finish(); Logs::finish(); return result; diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 3cc03d30f..aa0e4d4bf 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -364,7 +364,7 @@ NotifyWindow::~NotifyWindow() { if (App::wnd()) App::wnd()->notifyShowNext(this); } -MainWindow::MainWindow(QWidget *parent) : PsMainWindow(parent) { +MainWindow::MainWindow() { icon16 = icon256.scaledToWidth(16, Qt::SmoothTransformation); icon32 = icon256.scaledToWidth(32, Qt::SmoothTransformation); icon64 = icon256.scaledToWidth(64, Qt::SmoothTransformation); @@ -1081,7 +1081,7 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) { break; } - return PsMainWindow::eventFilter(obj, e); + return Platform::MainWindow::eventFilter(obj, e); } void MainWindow::mouseMoveEvent(QMouseEvent *e) { diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 8c5f19953..a599bf061 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "title.h" #include "pspecific.h" #include "ui/boxshadow.h" +#include "platform/platform_main_window.h" class MediaView; class TitleWidget; @@ -123,11 +124,11 @@ typedef QList NotifyWindows; class MediaPreviewWidget; -class MainWindow : public PsMainWindow { +class MainWindow : public Platform::MainWindow { Q_OBJECT public: - MainWindow(QWidget *parent = 0); + MainWindow(); ~MainWindow(); void init(); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp new file mode 100644 index 000000000..e0172a21b --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -0,0 +1,439 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "platform/linux/main_window_linux.h" + +#include "mainwindow.h" +#include "application.h" +#include "lang.h" +#include "localstorage.h" + +namespace Platform { +namespace { + +} // namespace + +MainWindow::MainWindow() : QMainWindow(), +posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/icon256.png")), iconbig256(icon256), wndIcon(QIcon::fromTheme("telegram", QIcon(QPixmap::fromImage(icon256, Qt::ColorOnly)))), _psCheckStatusIconLeft(100), _psLastIndicatorUpdate(0) { + connect(&_psCheckStatusIconTimer, SIGNAL(timeout()), this, SLOT(psStatusIconCheck())); + _psCheckStatusIconTimer.setSingleShot(false); + + connect(&_psUpdateIndicatorTimer, SIGNAL(timeout()), this, SLOT(psUpdateIndicator())); + _psUpdateIndicatorTimer.setSingleShot(true); +} + +bool MainWindow::psHasTrayIcon() const { + return trayIcon || ((useAppIndicator || (useStatusIcon && trayIconChecked)) && (cWorkMode() != dbiwmWindowOnly)); +} + +void MainWindow::psStatusIconCheck() { + _trayIconCheck(0); + if (cSupportTray() || !--_psCheckStatusIconLeft) { + _psCheckStatusIconTimer.stop(); + return; + } +} + +void MainWindow::psShowTrayMenu() { +} + +void MainWindow::psRefreshTaskbarIcon() { +} + +void MainWindow::psTrayMenuUpdated() { + if (noQtTrayIcon && (useAppIndicator || useStatusIcon)) { + const QList &actions = trayIconMenu->actions(); + if (_trayItems.isEmpty()) { + DEBUG_LOG(("Creating tray menu!")); + for (int32 i = 0, l = actions.size(); i != l; ++i) { + GtkWidget *item = ps_gtk_menu_item_new_with_label(actions.at(i)->text().toUtf8()); + ps_gtk_menu_shell_append(PS_GTK_MENU_SHELL(_trayMenu), item); + ps_g_signal_connect(item, "activate", G_CALLBACK(_trayMenuCallback), this); + ps_gtk_widget_show(item); + ps_gtk_widget_set_sensitive(item, actions.at(i)->isEnabled()); + + _trayItems.push_back(qMakePair(item, actions.at(i))); + } + } else { + DEBUG_LOG(("Updating tray menu!")); + for (int32 i = 0, l = actions.size(); i != l; ++i) { + if (i < _trayItems.size()) { + ps_gtk_menu_item_set_label(reinterpret_cast(_trayItems.at(i).first), actions.at(i)->text().toUtf8()); + ps_gtk_widget_set_sensitive(_trayItems.at(i).first, actions.at(i)->isEnabled()); + } + } + } + } +} + +void MainWindow::psSetupTrayIcon() { + if (noQtTrayIcon) { + if (!cSupportTray()) return; + psUpdateCounter(); + } else { + if (!trayIcon) { + trayIcon = new QSystemTrayIcon(this); + + QIcon icon(QPixmap::fromImage(App::wnd()->iconLarge(), Qt::ColorOnly)); + + trayIcon->setIcon(icon); + trayIcon->setToolTip(str_const_toString(AppName)); + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(toggleTray(QSystemTrayIcon::ActivationReason)), Qt::UniqueConnection); + connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showFromTray())); + App::wnd()->updateTrayMenu(); + } + psUpdateCounter(); + + trayIcon->show(); + psUpdateDelegate(); + } +} + +void MainWindow::psUpdateWorkmode() { + if (!cSupportTray()) return; + + if (cWorkMode() == dbiwmWindowOnly) { + if (noQtTrayIcon) { + if (useAppIndicator) { + ps_app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_PASSIVE); + } else if (useStatusIcon) { + ps_gtk_status_icon_set_visible(_trayIcon, false); + } + } else { + if (trayIcon) { + trayIcon->setContextMenu(0); + trayIcon->deleteLater(); + } + trayIcon = 0; + } + } else { + if (noQtTrayIcon) { + if (useAppIndicator) { + ps_app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_ACTIVE); + } else if (useStatusIcon) { + ps_gtk_status_icon_set_visible(_trayIcon, true); + } + } else { + psSetupTrayIcon(); + } + } +} + +void MainWindow::psUpdateIndicator() { + _psUpdateIndicatorTimer.stop(); + _psLastIndicatorUpdate = getms(); + QFileInfo f(_trayIconImageFile()); + if (f.exists()) { + QByteArray path = QFile::encodeName(f.absoluteFilePath()), name = QFile::encodeName(f.fileName()); + name = name.mid(0, name.size() - 4); + ps_app_indicator_set_icon_full(_trayIndicator, path.constData(), name); + } else { + useAppIndicator = false; + } +} + +void MainWindow::psUpdateCounter() { + setWindowIcon(wndIcon); + + int32 counter = App::histories().unreadBadge(); + + setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); + if (_psUnityLauncherEntry) { + if (counter > 0) { + ps_unity_launcher_entry_set_count(_psUnityLauncherEntry, (counter > 9999) ? 9999 : counter); + ps_unity_launcher_entry_set_count_visible(_psUnityLauncherEntry, TRUE); + } else { + ps_unity_launcher_entry_set_count_visible(_psUnityLauncherEntry, FALSE); + } + } + + if (noQtTrayIcon) { + if (useAppIndicator) { + if (getms() > _psLastIndicatorUpdate + 1000) { + psUpdateIndicator(); + } else if (!_psUpdateIndicatorTimer.isActive()) { + _psUpdateIndicatorTimer.start(100); + } + } else if (useStatusIcon && trayIconChecked) { + loadPixbuf(_trayIconImageGen()); + ps_gtk_status_icon_set_from_pixbuf(_trayIcon, _trayPixbuf); + } + } else if (trayIcon) { + int32 counter = App::histories().unreadBadge(); + bool muted = App::histories().unreadOnlyMuted(); + + style::color bg = muted ? st::counterMuteBG : st::counterBG; + QIcon iconSmall; + iconSmall.addPixmap(QPixmap::fromImage(iconWithCounter(16, counter, bg, true), Qt::ColorOnly)); + iconSmall.addPixmap(QPixmap::fromImage(iconWithCounter(32, counter, bg, true), Qt::ColorOnly)); + trayIcon->setIcon(iconSmall); + } +} + +void MainWindow::psUpdateDelegate() { +} + +void MainWindow::psInitSize() { + setMinimumWidth(st::wndMinWidth); + setMinimumHeight(st::wndMinHeight); + + TWindowPos pos(cWindowPos()); + QRect avail(QDesktopWidget().availableGeometry()); + bool maximized = false; + QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight); + if (pos.w && pos.h) { + QList screens = Application::screens(); + for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { + QByteArray name = (*i)->name().toUtf8(); + if (pos.moncrc == hashCrc32(name.constData(), name.size())) { + QRect screen((*i)->geometry()); + int32 w = screen.width(), h = screen.height(); + if (w >= st::wndMinWidth && h >= st::wndMinHeight) { + if (pos.w > w) pos.w = w; + if (pos.h > h) pos.h = h; + pos.x += screen.x(); + pos.y += screen.y(); + if (pos.x < screen.x() + screen.width() - 10 && pos.y < screen.y() + screen.height() - 10) { + geom = QRect(pos.x, pos.y, pos.w, pos.h); + } + } + break; + } + } + + if (pos.y < 0) pos.y = 0; + maximized = pos.maximized; + } + setGeometry(geom); +} + +void MainWindow::psInitFrameless() { + psUpdatedPositionTimer.setSingleShot(true); + connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); + + if (frameless) { + //setWindowFlags(Qt::FramelessWindowHint); + } +} + +void MainWindow::psSavePosition(Qt::WindowState state) { + if (state == Qt::WindowActive) state = windowHandle()->windowState(); + if (state == Qt::WindowMinimized || !posInited) return; + + TWindowPos pos(cWindowPos()), curPos = pos; + + if (state == Qt::WindowMaximized) { + curPos.maximized = 1; + } else { + QRect r(geometry()); + curPos.x = r.x(); + curPos.y = r.y(); + curPos.w = r.width(); + curPos.h = r.height(); + curPos.maximized = 0; + } + + int px = curPos.x + curPos.w / 2, py = curPos.y + curPos.h / 2, d = 0; + QScreen *chosen = 0; + QList screens = Application::screens(); + for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { + int dx = (*i)->geometry().x() + (*i)->geometry().width() / 2 - px; if (dx < 0) dx = -dx; + int dy = (*i)->geometry().y() + (*i)->geometry().height() / 2 - py; if (dy < 0) dy = -dy; + if (!chosen || dx + dy < d) { + d = dx + dy; + chosen = *i; + } + } + if (chosen) { + curPos.x -= chosen->geometry().x(); + curPos.y -= chosen->geometry().y(); + QByteArray name = chosen->name().toUtf8(); + curPos.moncrc = hashCrc32(name.constData(), name.size()); + } + + if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { + if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { + cSetWindowPos(curPos); + Local::writeSettings(); + } + } +} + +void MainWindow::psUpdatedPosition() { + psUpdatedPositionTimer.start(SaveWindowPositionTimeout); +} + +void MainWindow::psCreateTrayIcon() { + if (!noQtTrayIcon) { + cSetSupportTray(QSystemTrayIcon::isSystemTrayAvailable()); + return; + } + + if (useAppIndicator) { + DEBUG_LOG(("Trying to create AppIndicator")); + _trayMenu = ps_gtk_menu_new(); + if (_trayMenu) { + DEBUG_LOG(("Created gtk menu for appindicator!")); + QFileInfo f(_trayIconImageFile()); + if (f.exists()) { + QByteArray path = QFile::encodeName(f.absoluteFilePath()); + _trayIndicator = ps_app_indicator_new("Telegram Desktop", path.constData(), APP_INDICATOR_CATEGORY_APPLICATION_STATUS); + if (_trayIndicator) { + DEBUG_LOG(("Created appindicator!")); + } else { + DEBUG_LOG(("Failed to app_indicator_new()!")); + } + } else { + useAppIndicator = false; + DEBUG_LOG(("Failed to create image file!")); + } + } else { + DEBUG_LOG(("Failed to gtk_menu_new()!")); + } + if (_trayMenu && _trayIndicator) { + ps_app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_ACTIVE); + ps_app_indicator_set_menu(_trayIndicator, PS_GTK_MENU(_trayMenu)); + useStatusIcon = false; + } else { + DEBUG_LOG(("AppIndicator failed!")); + useAppIndicator = false; + } + } + if (useStatusIcon) { + if (ps_gdk_init_check(0, 0)) { + if (!_trayMenu) _trayMenu = ps_gtk_menu_new(); + if (_trayMenu) { + loadPixbuf(_trayIconImageGen()); + _trayIcon = ps_gtk_status_icon_new_from_pixbuf(_trayPixbuf); + if (_trayIcon) { + ps_g_signal_connect(_trayIcon, "popup-menu", GCallback(_trayIconPopup), _trayMenu); + ps_g_signal_connect(_trayIcon, "activate", GCallback(_trayIconActivate), _trayMenu); + ps_g_signal_connect(_trayIcon, "size-changed", GCallback(_trayIconResized), _trayMenu); + + ps_gtk_status_icon_set_title(_trayIcon, "Telegram Desktop"); + ps_gtk_status_icon_set_tooltip_text(_trayIcon, "Telegram Desktop"); + ps_gtk_status_icon_set_visible(_trayIcon, true); + } else { + useStatusIcon = false; + } + } else { + useStatusIcon = false; + } + } else { + useStatusIcon = false; + } + } + if (!useStatusIcon && !useAppIndicator) { + if (_trayMenu) { + ps_g_object_ref_sink(_trayMenu); + ps_g_object_unref(_trayMenu); + _trayMenu = 0; + } + } + cSetSupportTray(useAppIndicator); + if (useStatusIcon) { + ps_g_idle_add((GSourceFunc)_trayIconCheck, 0); + _psCheckStatusIconTimer.start(100); + } else { + psUpdateWorkmode(); + } +} + +void MainWindow::psFirstShow() { + psCreateTrayIcon(); + + if (useUnityCount) { + _psUnityLauncherEntry = ps_unity_launcher_entry_get_for_desktop_id("telegramdesktop.desktop"); + if (_psUnityLauncherEntry) { + LOG(("Found Unity Launcher entry telegramdesktop.desktop!")); + } else { + _psUnityLauncherEntry = ps_unity_launcher_entry_get_for_desktop_id("Telegram.desktop"); + if (_psUnityLauncherEntry) { + LOG(("Found Unity Launcher entry Telegram.desktop!")); + } else { + LOG(("Could not get Unity Launcher entry!")); + } + } + } else { + LOG(("Not using Unity Launcher count.")); + } + + finished = false; + + psUpdateMargins(); + + bool showShadows = true; + + show(); + //_private.enableShadow(winId()); + if (cWindowPos().maximized) { + setWindowState(Qt::WindowMaximized); + } + + if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) { + setWindowState(Qt::WindowMinimized); + if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { + hide(); + } else { + show(); + } + showShadows = false; + } else { + show(); + } + + posInited = true; +} + +bool MainWindow::psHandleTitle() { + return false; +} + +void MainWindow::psInitSysMenu() { +} + +void MainWindow::psUpdateSysMenu(Qt::WindowState state) { +} + +void MainWindow::psUpdateMargins() { +} + +void MainWindow::psFlash() { +} + +MainWindow::~MainWindow() { + if (_trayIcon) { + ps_g_object_unref(_trayIcon); + _trayIcon = 0; + } + if (_trayPixbuf) { + ps_g_object_unref(_trayPixbuf); + _trayPixbuf = 0; + } + if (_trayMenu) { + ps_g_object_ref_sink(_trayMenu); + ps_g_object_unref(_trayMenu); + _trayMenu = 0; + } + finished = true; +} + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h new file mode 100644 index 000000000..946a73939 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -0,0 +1,110 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "window/main_window.h" + +class NotifyWindow; + +namespace Platform { + +class MainWindow : public Window::MainWindow { + Q_OBJECT + +public: + MainWindow(); + + int32 psResizeRowWidth() const { + return 0;//st::wndResizeAreaWidth; + } + + void psInitFrameless(); + void psInitSize(); + + void psFirstShow(); + void psInitSysMenu(); + void psUpdateSysMenu(Qt::WindowState state); + void psUpdateMargins(); + void psUpdatedPosition(); + + bool psHandleTitle(); + + void psFlash(); + void psNotifySettingGot(); + + void psUpdateWorkmode(); + + void psRefreshTaskbarIcon(); + + bool psPosInited() const { + return posInited; + } + + void psActivateNotify(NotifyWindow *w); + void psClearNotifies(PeerId peerId = 0); + void psNotifyShown(NotifyWindow *w); + void psPlatformNotify(HistoryItem *item, int32 fwdCount); + + void psUpdateCounter(); + + bool psHasNativeNotifications() { + return false; + } + + virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; + + ~MainWindow(); + +public slots: + + void psUpdateDelegate(); + void psSavePosition(Qt::WindowState state = Qt::WindowActive); + void psShowTrayMenu(); + + void psStatusIconCheck(); + void psUpdateIndicator(); + +protected: + + bool psHasTrayIcon() const; + + bool posInited; + QSystemTrayIcon *trayIcon; + QMenu *trayIconMenu; + QImage icon256, iconbig256; + QIcon wndIcon; + + void psTrayMenuUpdated(); + void psSetupTrayIcon(); + + QTimer psUpdatedPositionTimer; + +private: + void psCreateTrayIcon(); + + QTimer _psCheckStatusIconTimer; + int _psCheckStatusIconLeft; + + QTimer _psUpdateIndicatorTimer; + uint64 _psLastIndicatorUpdate; +}; + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h new file mode 100644 index 000000000..a2fe866c1 --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -0,0 +1,140 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "window/main_window.h" +#include "pspecific_mac_p.h" + +class NotifyWindow; + +namespace Platform { + +class MacPrivate : public PsMacWindowPrivate { +public: + + void activeSpaceChanged(); + void darkModeChanged(); + void notifyClicked(unsigned long long peer, int msgid); + void notifyReplied(unsigned long long peer, int msgid, const char *str); + +}; + +class MainWindow : public Window::MainWindow { + Q_OBJECT + +public: + MainWindow(); + + int32 psResizeRowWidth() const { + return 0;//st::wndResizeAreaWidth; + } + + void psInitFrameless(); + void psInitSize(); + + void psFirstShow(); + void psInitSysMenu(); + void psUpdateSysMenu(Qt::WindowState state); + void psUpdateMargins(); + void psUpdatedPosition(); + + bool psHandleTitle(); + + void psFlash(); + + void psUpdateWorkmode(); + + void psRefreshTaskbarIcon(); + + bool psPosInited() const { + return posInited; + } + + bool psFilterNativeEvent(void *event); + + void psActivateNotify(NotifyWindow *w); + void psClearNotifies(PeerId peerId = 0); + void psNotifyShown(NotifyWindow *w); + void psPlatformNotify(HistoryItem *item, int32 fwdCount); + + bool eventFilter(QObject *obj, QEvent *evt); + + void psUpdateCounter(); + + bool psHasNativeNotifications() { + return !(QSysInfo::macVersion() < QSysInfo::MV_10_8); + } + + virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; + + ~MainWindow(); + +public slots: + + void psUpdateDelegate(); + void psSavePosition(Qt::WindowState state = Qt::WindowActive); + void psShowTrayMenu(); + + void psMacUndo(); + void psMacRedo(); + void psMacCut(); + void psMacCopy(); + void psMacPaste(); + void psMacDelete(); + void psMacSelectAll(); + +protected: + + void psNotIdle() const; + QImage psTrayIcon(bool selected = false) const; + bool psHasTrayIcon() const { + return trayIcon; + } + + void psMacUpdateMenu(); + + bool posInited; + QSystemTrayIcon *trayIcon; + QMenu *trayIconMenu; + QImage icon256, iconbig256; + QIcon wndIcon; + + QImage trayImg, trayImgSel; + + void psTrayMenuUpdated(); + void psSetupTrayIcon(); + virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0; + + QTimer psUpdatedPositionTimer; + +private: + struct PrivateData; + std_::unique_ptr _private; + + mutable bool psIdle; + mutable QTimer psIdleTimer; + + QMenuBar psMainMenu; + QAction *psLogout, *psUndo, *psRedo, *psCut, *psCopy, *psPaste, *psDelete, *psSelectAll, *psContacts, *psAddContact, *psNewGroup, *psNewChannel, *psShowTelegram; + +}; + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm new file mode 100644 index 000000000..3dc9e5ee1 --- /dev/null +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -0,0 +1,1110 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "pspecific_mac_p.h" + +#include "mainwindow.h" +#include "mainwidget.h" +#include "application.h" +#include "playerwidget.h" + +#include "lang.h" + +#include +#include +#include + +#include + +@interface qVisualize : NSObject { +} + ++ (id)str:(const QString &)str; +- (id)initWithString:(const QString &)str; + ++ (id)bytearr:(const QByteArray &)arr; +- (id)initWithByteArray:(const QByteArray &)arr; + +- (id)debugQuickLookObject; + +@end + +@implementation qVisualize { + NSString *value; +} + ++ (id)bytearr:(const QByteArray &)arr { + return [[qVisualize alloc] initWithByteArray:arr]; +} +- (id)initWithByteArray:(const QByteArray &)arr { + if (self = [super init]) { + value = [NSString stringWithUTF8String:arr.constData()]; + } + return self; +} + ++ (id)str:(const QString &)str { + return [[qVisualize alloc] initWithString:str]; +} +- (id)initWithString:(const QString &)str { + if (self = [super init]) { + value = [NSString stringWithUTF8String:str.toUtf8().constData()]; + } + return self; +} + +- (id)debugQuickLookObject { + return value; +} + +@end + +@interface ApplicationDelegate : NSObject { +} + +- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag; +- (void)applicationDidBecomeActive:(NSNotification *)aNotification; +- (void)receiveWakeNote:(NSNotification*)note; + +@end + +ApplicationDelegate *_sharedDelegate = nil; + +@implementation ApplicationDelegate { +} + +- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag { + if (App::wnd() && App::wnd()->isHidden()) App::wnd()->showFromTray(); + return YES; +} + +- (void)applicationDidBecomeActive:(NSNotification *)aNotification { + if (App::app()) App::app()->checkLocalTime(); +} + +- (void)receiveWakeNote:(NSNotification*)aNotification { + if (App::app()) App::app()->checkLocalTime(); +} + +@end + +class QNSString { +public: + QNSString(const QString &str) : _str([NSString stringWithUTF8String:str.toUtf8().constData()]) { + } + NSString *s() { + return _str; + } +private: + NSString *_str; +}; + +QNSString objc_lang(LangKey key) { + return QNSString(lang(key)); +} +QString objcString(NSString *str) { + return QString::fromUtf8([str cStringUsingEncoding:NSUTF8StringEncoding]); +} + +@interface ObserverHelper : NSObject { +} + +- (id) init:(PsMacWindowPrivate *)aWnd; +- (void) activeSpaceDidChange:(NSNotification *)aNotification; +- (void) darkModeChanged:(NSNotification *)aNotification; + +@end + +@interface NotifyHandler : NSObject { +} + +- (id) init:(PsMacWindowPrivate *)aWnd; + +- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification; + +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification; + +@end + +class PsMacWindowData { +public: + + PsMacWindowData(PsMacWindowPrivate *wnd) : + wnd(wnd), + observerHelper([[ObserverHelper alloc] init:wnd]), + notifyHandler([[NotifyHandler alloc] init:wnd]) { + } + + void onNotifyClick(NSUserNotification *notification) { + NSDictionary *dict = [notification userInfo]; + NSNumber *peerObj = [dict objectForKey:@"peer"], *msgObj = [dict objectForKey:@"msgid"]; + unsigned long long peerLong = peerObj ? [peerObj unsignedLongLongValue] : 0; + int msgId = msgObj ? [msgObj intValue] : 0; + wnd->notifyClicked(peerLong, msgId); + } + + void onNotifyReply(NSUserNotification *notification) { + NSDictionary *dict = [notification userInfo]; + NSNumber *peerObj = [dict objectForKey:@"peer"], *msgObj = [dict objectForKey:@"msgid"]; + unsigned long long peerLong = peerObj ? [peerObj unsignedLongLongValue] : 0; + int msgId = msgObj ? [msgObj intValue] : 0; + wnd->notifyReplied(peerLong, msgId, [[[notification response] string] UTF8String]); + } + + ~PsMacWindowData() { + [observerHelper release]; + [notifyHandler release]; + } + + PsMacWindowPrivate *wnd; + ObserverHelper *observerHelper; + NotifyHandler *notifyHandler; +}; + +@implementation ObserverHelper { + PsMacWindowPrivate *wnd; +} + +- (id) init:(PsMacWindowPrivate *)aWnd { + if (self = [super init]) { + wnd = aWnd; + } + return self; +} + +- (void) activeSpaceDidChange:(NSNotification *)aNotification { + wnd->activeSpaceChanged(); +} + +- (void) darkModeChanged:(NSNotification *)aNotification { + wnd->darkModeChanged(); +} + +@end + +@implementation NotifyHandler { + PsMacWindowPrivate *wnd; +} + +- (id) init:(PsMacWindowPrivate *)aWnd { + if (self = [super init]) { + wnd = aWnd; + } + return self; +} + +- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification { + NSNumber *instObj = [[notification userInfo] objectForKey:@"launch"]; + unsigned long long instLong = instObj ? [instObj unsignedLongLongValue] : 0; + DEBUG_LOG(("Received notification with instance %1").arg(instLong)); + if (instLong != Global::LaunchId()) { // other app instance notification + return; + } + if (notification.activationType == NSUserNotificationActivationTypeReplied) { + wnd->data->onNotifyReply(notification); + } else if (notification.activationType == NSUserNotificationActivationTypeContentsClicked) { + wnd->data->onNotifyClick(notification); + } + [center removeDeliveredNotification: notification]; +} + +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification { + return YES; +} + +@end + +PsMacWindowPrivate::PsMacWindowPrivate() : data(new PsMacWindowData(this)) { + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:data->observerHelper selector:@selector(activeSpaceDidChange:) name:NSWorkspaceActiveSpaceDidChangeNotification object:nil]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:data->observerHelper selector:@selector(darkModeChanged:) name:QNSString(strNotificationAboutThemeChange()).s() object:nil]; +} + +void PsMacWindowPrivate::setWindowBadge(const QString &str) { + [[NSApp dockTile] setBadgeLabel:QNSString(str).s()]; +} + +void PsMacWindowPrivate::startBounce() { + [NSApp requestUserAttention:NSInformationalRequest]; +} + +void PsMacWindowPrivate::updateDelegate() { + NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; + [center setDelegate:data->notifyHandler]; +} + +void objc_holdOnTop(WId winId) { + NSWindow *wnd = [reinterpret_cast(winId) window]; + [wnd setHidesOnDeactivate:NO]; +} + +bool objc_darkMode() { + NSDictionary *dict = [[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain]; + id style = [dict objectForKey:QNSString(strStyleOfInterface()).s()]; + BOOL darkModeOn = ( style && [style isKindOfClass:[NSString class]] && NSOrderedSame == [style caseInsensitiveCompare:@"dark"] ); + return darkModeOn ? true : false; +} + +void objc_showOverAll(WId winId, bool canFocus) { + NSWindow *wnd = [reinterpret_cast(winId) window]; + [wnd setLevel:NSPopUpMenuWindowLevel]; + if (!canFocus) { + [wnd setStyleMask:NSUtilityWindowMask | NSNonactivatingPanelMask]; + [wnd setCollectionBehavior:NSWindowCollectionBehaviorMoveToActiveSpace|NSWindowCollectionBehaviorStationary|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle]; + } +} + +void objc_bringToBack(WId winId) { + NSWindow *wnd = [reinterpret_cast(winId) window]; + [wnd setLevel:NSModalPanelWindowLevel]; +} + +void objc_activateWnd(WId winId) { + NSWindow *wnd = [reinterpret_cast(winId) window]; + [wnd orderFront:wnd]; +} + +NSImage *qt_mac_create_nsimage(const QPixmap &pm); + +void PsMacWindowPrivate::showNotify(uint64 peer, int32 msgId, const QPixmap &pix, const QString &title, const QString &subtitle, const QString &msg, bool withReply) { + NSUserNotification *notification = [[NSUserNotification alloc] init]; + NSImage *img = qt_mac_create_nsimage(pix); + + DEBUG_LOG(("Sending notification with userinfo: peer %1, msgId %2 and instance %3").arg(peer).arg(msgId).arg(Global::LaunchId())); + [notification setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedLongLong:peer],@"peer",[NSNumber numberWithInt:msgId],@"msgid",[NSNumber numberWithUnsignedLongLong:Global::LaunchId()],@"launch",nil]]; + + [notification setTitle:QNSString(title).s()]; + [notification setSubtitle:QNSString(subtitle).s()]; + [notification setInformativeText:QNSString(msg).s()]; + if ([notification respondsToSelector:@selector(setContentImage:)]) { + [notification setContentImage:img]; + } + + if (withReply && [notification respondsToSelector:@selector(setHasReplyButton:)]) { + [notification setHasReplyButton:YES]; + } + + [notification setSoundName:nil]; + + NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; + [center deliverNotification:notification]; + + if (img) [img release]; + [notification release]; +} + +void PsMacWindowPrivate::enableShadow(WId winId) { +// [[(NSView*)winId window] setStyleMask:NSBorderlessWindowMask]; +// [[(NSView*)winId window] setHasShadow:YES]; +} + +bool PsMacWindowPrivate::filterNativeEvent(void *event) { + NSEvent *e = static_cast(event); + if (e && [e type] == NSSystemDefined && [e subtype] == 8) { + int keyCode = (([e data1] & 0xFFFF0000) >> 16); + int keyFlags = ([e data1] & 0x0000FFFF); + int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA; + int keyRepeat = (keyFlags & 0x1); + + switch (keyCode) { + case NX_KEYTYPE_PLAY: + if (keyState == 0) { // Play pressed and released + if (App::main()) App::main()->player()->playPausePressed(); + return true; + } + break; + + case NX_KEYTYPE_FAST: + if (keyState == 0) { // Next pressed and released + if (App::main()) App::main()->player()->nextPressed(); + return true; + } + break; + + case NX_KEYTYPE_REWIND: + if (keyState == 0) { // Previous pressed and released + if (App::main()) App::main()->player()->prevPressed(); + return true; + } + break; + } + } + return false; +} + + +void PsMacWindowPrivate::clearNotifies(unsigned long long peer) { + NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; + if (peer) { + NSArray *notifies = [center deliveredNotifications]; + for (id notify in notifies) { + NSDictionary *dict = [notify userInfo]; + if ([[dict objectForKey:@"peer"] unsignedLongLongValue] == peer && [[dict objectForKey:@"launch"] unsignedLongLongValue] == Global::LaunchId()) { + [center removeDeliveredNotification:notify]; + } + } + } else { + [center removeAllDeliveredNotifications]; + } +} + +void objc_debugShowAlert(const QString &str) { + [[NSAlert alertWithMessageText:@"Debug Message" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"%@", QNSString(str).s()] runModal]; +} + +void objc_outputDebugString(const QString &str) { + NSLog(@"%@", QNSString(str).s()); +} + +PsMacWindowPrivate::~PsMacWindowPrivate() { + delete data; +} + +bool objc_idleSupported() { + int64 idleTime = 0; + return objc_idleTime(idleTime); +} + +bool objc_idleTime(int64 &idleTime) { // taken from https://github.com/trueinteractions/tint/issues/53 + CFMutableDictionaryRef properties = 0; + CFTypeRef obj; + mach_port_t masterPort; + io_iterator_t iter; + io_registry_entry_t curObj; + + IOMasterPort(MACH_PORT_NULL, &masterPort); + + /* Get IOHIDSystem */ + IOServiceGetMatchingServices(masterPort, IOServiceMatching("IOHIDSystem"), &iter); + if (iter == 0) { + return false; + } else { + curObj = IOIteratorNext(iter); + } + if (IORegistryEntryCreateCFProperties(curObj, &properties, kCFAllocatorDefault, 0) == KERN_SUCCESS && properties != NULL) { + obj = CFDictionaryGetValue(properties, CFSTR("HIDIdleTime")); + CFRetain(obj); + } else { + return false; + } + + uint64 err = ~0L, result = err; + if (obj) { + CFTypeID type = CFGetTypeID(obj); + + if (type == CFDataGetTypeID()) { + CFDataGetBytes((CFDataRef) obj, CFRangeMake(0, sizeof(result)), (UInt8*)&result); + } else if (type == CFNumberGetTypeID()) { + CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt64Type, &result); + } else { + // error + } + + CFRelease(obj); + + if (result != err) { + result /= 1000000; // return as ms + } + } else { + // error + } + + CFRelease((CFTypeRef)properties); + IOObjectRelease(curObj); + IOObjectRelease(iter); + if (result == err) return false; + + idleTime = int64(result); + return true; +} + +@interface OpenWithApp : NSObject { + NSString *fullname; + NSURL *app; + NSImage *icon; +} +@property (nonatomic, retain) NSString *fullname; +@property (nonatomic, retain) NSURL *app; +@property (nonatomic, retain) NSImage *icon; +@end + +@implementation OpenWithApp +@synthesize fullname, app, icon; + +- (void) dealloc { + [fullname release]; + [app release]; + [icon release]; + [super dealloc]; +} + +@end + +@interface OpenFileWithInterface : NSObject { +} + +- (id) init:(NSString *)file; +- (BOOL) popupAtX:(int)x andY:(int)y; +- (void) itemChosen:(id)sender; +- (void) dealloc; + +@end + +@implementation OpenFileWithInterface { + NSString *toOpen; + + NSURL *defUrl; + NSString *defBundle, *defName, *defVersion; + NSImage *defIcon; + + NSMutableArray *apps; + + NSMenu *menu; +} + +- (void) fillAppByUrl:(NSURL*)url bundle:(NSString**)bundle name:(NSString**)name version:(NSString**)version icon:(NSImage**)icon { + NSBundle *b = [NSBundle bundleWithURL:url]; + if (b) { + NSString *path = [url path]; + *name = [[NSFileManager defaultManager] displayNameAtPath: path]; + if (!*name) *name = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleDisplayName"]; + if (!*name) *name = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleName"]; + if (*name) { + *bundle = [b bundleIdentifier]; + if (bundle) { + *version = (NSString*)[b objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + *icon = [[NSWorkspace sharedWorkspace] iconForFile: path]; + if (*icon && [*icon isValid]) [*icon setSize: CGSizeMake(16., 16.)]; + return; + } + } + } + *bundle = *name = *version = nil; + *icon = nil; +} + +- (id) init:(NSString*)file { + toOpen = file; + if (self = [super init]) { + NSURL *url = [NSURL fileURLWithPath:file]; + defUrl = [[NSWorkspace sharedWorkspace] URLForApplicationToOpenURL:url]; + if (defUrl) { + [self fillAppByUrl:defUrl bundle:&defBundle name:&defName version:&defVersion icon:&defIcon]; + if (!defBundle || !defName) { + defUrl = nil; + } + } + NSArray *appsList = (NSArray*)LSCopyApplicationURLsForURL(CFURLRef(url), kLSRolesAll); + NSMutableDictionary *data = [NSMutableDictionary dictionaryWithCapacity:16]; + int fullcount = 0; + for (id app in appsList) { + if (fullcount > 15) break; + + NSString *bundle = nil, *name = nil, *version = nil; + NSImage *icon = nil; + [self fillAppByUrl:(NSURL*)app bundle:&bundle name:&name version:&version icon:&icon]; + if (bundle && name) { + if ([bundle isEqualToString:defBundle] && [version isEqualToString:defVersion]) continue; + NSString *key = [[NSArray arrayWithObjects:bundle, name, nil] componentsJoinedByString:@"|"]; + if (!version) version = @""; + + NSMutableDictionary *versions = (NSMutableDictionary*)[data objectForKey:key]; + if (!versions) { + versions = [NSMutableDictionary dictionaryWithCapacity:2]; + [data setValue:versions forKey:key]; + } + if (![versions objectForKey:version]) { + [versions setValue:[NSArray arrayWithObjects:name, icon, app, nil] forKey:version]; + ++fullcount; + } + } + } + if (fullcount || defUrl) { + apps = [NSMutableArray arrayWithCapacity:fullcount]; + for (id key in data) { + NSMutableDictionary *val = (NSMutableDictionary*)[data objectForKey:key]; + for (id ver in val) { + NSArray *app = (NSArray*)[val objectForKey:ver]; + OpenWithApp *a = [[OpenWithApp alloc] init]; + NSString *fullname = (NSString*)[app objectAtIndex:0], *version = (NSString*)ver; + BOOL showVersion = ([val count] > 1); + if (!showVersion) { + NSError *error = NULL; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^\\d+\\.\\d+\\.\\d+(\\.\\d+)?$" options:NSRegularExpressionCaseInsensitive error:&error]; + showVersion = ![regex numberOfMatchesInString:version options:NSMatchingWithoutAnchoringBounds range:{0,[version length]}]; + } + if (showVersion) fullname = [[NSArray arrayWithObjects:fullname, @" (", version, @")", nil] componentsJoinedByString:@""]; + [a setFullname:fullname]; + [a setIcon:(NSImage*)[app objectAtIndex:1]]; + [a setApp:(NSURL*)[app objectAtIndex:2]]; + [apps addObject:a]; + [a release]; + } + } + } + [apps sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"fullname" ascending:YES]]]; + [appsList release]; + menu = nil; + } + return self; +} + +- (BOOL) popupAtX:(int)x andY:(int)y { + if (![apps count] && !defName) return NO; + menu = [[NSMenu alloc] initWithTitle:@"Open With"]; + + int index = 0; + if (defName) { + NSMenuItem *item = [menu insertItemWithTitle:[[NSArray arrayWithObjects:defName, @" (default)", nil] componentsJoinedByString:@""] action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++]; + if (defIcon) [item setImage:defIcon]; + [item setTarget:self]; + [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; + } + if ([apps count]) { + for (id a in apps) { + OpenWithApp *app = (OpenWithApp*)a; + NSMenuItem *item = [menu insertItemWithTitle:[a fullname] action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++]; + if ([app icon]) [item setImage:[app icon]]; + [item setTarget:self]; + } + [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; + } + NSMenuItem *item = [menu insertItemWithTitle:objc_lang(lng_mac_choose_program_menu).s() action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++]; + [item setTarget:self]; + + [menu popUpMenuPositioningItem:nil atLocation:CGPointMake(x, y) inView:nil]; + + return YES; +} + +- (void) itemChosen:(id)sender { + NSArray *items = [menu itemArray]; + NSURL *url = nil; + for (int i = 0, l = [items count]; i < l; ++i) { + if ([items objectAtIndex:i] == sender) { + if (defName) i -= 2; + if (i < 0) { + url = defUrl; + } else if (i < int([apps count])) { + url = [(OpenWithApp*)[apps objectAtIndex:i] app]; + } + break; + } + } + if (url) { + [[NSWorkspace sharedWorkspace] openFile:toOpen withApplication:[url path]]; + } else { + objc_openFile(objcString(toOpen), true); + } +} + +- (void) dealloc { + if (apps) [apps release]; + [super dealloc]; + if (menu) [menu release]; +} + +@end + +bool objc_showOpenWithMenu(int x, int y, const QString &f) { + NSString *file = QNSString(f).s(); + @try { + OpenFileWithInterface *menu = [[OpenFileWithInterface alloc] init:file]; + QRect r = QApplication::desktop()->screenGeometry(QPoint(x, y)); + y = r.y() + r.height() - y; + return !![menu popupAtX:x andY:y]; + } + @catch (NSException *exception) { + } + @finally { + } + return false; +} + +void objc_showInFinder(const QString &file, const QString &path) { + [[NSWorkspace sharedWorkspace] selectFile:QNSString(file).s() inFileViewerRootedAtPath:QNSString(path).s()]; +} + +@interface NSURL(CompareUrls) + +- (BOOL) isEquivalent:(NSURL *)aURL; + +@end + +@implementation NSURL(CompareUrls) + +- (BOOL) isEquivalent:(NSURL *)aURL { + if ([self isEqual:aURL]) return YES; + if ([[self scheme] caseInsensitiveCompare:[aURL scheme]] != NSOrderedSame) return NO; + if ([[self host] caseInsensitiveCompare:[aURL host]] != NSOrderedSame) return NO; + if ([[self path] compare:[aURL path]] != NSOrderedSame) return NO; + if ([[self port] compare:[aURL port]] != NSOrderedSame) return NO; + if ([[self query] compare:[aURL query]] != NSOrderedSame) return NO; + return YES; +} + +@end + +@interface ChooseApplicationDelegate : NSObject { +} + +- (id) init:(NSArray *)recommendedApps withPanel:(NSOpenPanel *)creator withSelector:(NSPopUpButton *)menu withGood:(NSTextField *)goodLabel withBad:(NSTextField *)badLabel withIcon:(NSImageView *)badIcon withAccessory:(NSView *)acc; +- (BOOL) panel:(id)sender shouldEnableURL:(NSURL *)url; +- (void) panelSelectionDidChange:(id)sender; +- (void) menuDidClose; +- (void) dealloc; + +@end + +@implementation ChooseApplicationDelegate { + BOOL onlyRecommended; + NSArray *apps; + NSOpenPanel *panel; + NSPopUpButton *selector; + NSTextField *good, *bad; + NSImageView *icon; + NSString *recom; + NSView *accessory; +} + +- (id) init:(NSArray *)recommendedApps withPanel:(NSOpenPanel *)creator withSelector:(NSPopUpButton *)menu withGood:(NSTextField *)goodLabel withBad:(NSTextField *)badLabel withIcon:(NSImageView *)badIcon withAccessory:(NSView *)acc { + if (self = [super init]) { + onlyRecommended = YES; + recom = [objc_lang(lng_mac_recommended_apps).s() copy]; + apps = recommendedApps; + panel = creator; + selector = menu; + good = goodLabel; + bad = badLabel; + icon = badIcon; + accessory = acc; + [selector setAction:@selector(menuDidClose)]; + } + return self; +} + +- (BOOL) isRecommended:(NSURL *)url { + if (apps) { + for (id app in apps) { + if ([(NSURL*)app isEquivalent:url]) { + return YES; + } + } + } + return NO; +} + +- (BOOL) panel:(id)sender shouldEnableURL:(NSURL *)url { + NSNumber *isDirectory; + if ([url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil] && isDirectory != nil && [isDirectory boolValue]) { + if (onlyRecommended) { + CFStringRef ext = CFURLCopyPathExtension((CFURLRef)url); + NSNumber *isPackage; + if ([url getResourceValue:&isPackage forKey:NSURLIsPackageKey error:nil] && isPackage != nil && [isPackage boolValue]) { + return [self isRecommended:url]; + } + } + return YES; + } + return NO; +} + +- (void) panelSelectionDidChange:(id)sender { + NSArray *urls = [panel URLs]; + if ([urls count]) { + if ([self isRecommended:[urls firstObject]]) { + [bad removeFromSuperview]; + [icon removeFromSuperview]; + [accessory addSubview:good]; + } else { + [good removeFromSuperview]; + [accessory addSubview:bad]; + [accessory addSubview:icon]; + } + } else { + [good removeFromSuperview]; + [bad removeFromSuperview]; + [icon removeFromSuperview]; + } +} + +- (void) menuDidClose { + onlyRecommended = [[[selector selectedItem] title] isEqualToString:recom]; + [self refreshPanelTable]; +} + +- (BOOL) refreshDataInViews: (NSArray*)subviews { + for (id view in subviews) { + NSString *cls = [view className]; + if ([cls isEqualToString:QNSString(strNeedToReload()).s()]) { + [view reloadData]; + } else if ([cls isEqualToString:QNSString(strNeedToRefresh1()).s()] || [cls isEqualToString:QNSString(strNeedToRefresh2()).s()]) { + [view reloadData]; + return YES; + } else { + NSArray *next = [view subviews]; + if ([next count] && [self refreshDataInViews:next]) { + return YES; + } + } + } + + return NO; +} + + +- (void) refreshPanelTable { + [self refreshDataInViews:[[panel contentView] subviews]]; + [panel validateVisibleColumns]; +} + +- (void) dealloc { + if (apps) { + [apps release]; + [recom release]; + } + [super dealloc]; +} + +@end + +void objc_openFile(const QString &f, bool openwith) { + NSString *file = QNSString(f).s(); + if (openwith || [[NSWorkspace sharedWorkspace] openFile:file] == NO) { + @try { + NSURL *url = [NSURL fileURLWithPath:file]; + NSString *ext = [url pathExtension]; + NSArray *names =[url pathComponents]; + NSString *name = [names count] ? [names lastObject] : @""; + NSArray *apps = (NSArray*)LSCopyApplicationURLsForURL(CFURLRef(url), kLSRolesAll); + + NSOpenPanel *openPanel = [NSOpenPanel openPanel]; + + NSRect fullRect = { { 0., 0. }, { st::macAccessoryWidth, st::macAccessoryHeight } }; + NSView *accessory = [[NSView alloc] initWithFrame:fullRect]; + + [accessory setAutoresizesSubviews:YES]; + + NSPopUpButton *selector = [[NSPopUpButton alloc] init]; + [accessory addSubview:selector]; + [selector addItemWithTitle:objc_lang(lng_mac_recommended_apps).s()]; + [selector addItemWithTitle:objc_lang(lng_mac_all_apps).s()]; + [selector sizeToFit]; + + NSTextField *enableLabel = [[NSTextField alloc] init]; + [accessory addSubview:enableLabel]; + [enableLabel setStringValue:objc_lang(lng_mac_enable_filter).s()]; + [enableLabel setFont:[selector font]]; + [enableLabel setBezeled:NO]; + [enableLabel setDrawsBackground:NO]; + [enableLabel setEditable:NO]; + [enableLabel setSelectable:NO]; + [enableLabel sizeToFit]; + + NSRect selectorFrame = [selector frame], enableFrame = [enableLabel frame]; + enableFrame.size.width += st::macEnableFilterAdd; + enableFrame.origin.x = (fullRect.size.width - selectorFrame.size.width - enableFrame.size.width) / 2.; + selectorFrame.origin.x = (fullRect.size.width - selectorFrame.size.width + enableFrame.size.width) / 2.; + enableFrame.origin.y = fullRect.size.height - selectorFrame.size.height - st::macEnableFilterTop + (selectorFrame.size.height - enableFrame.size.height) / 2.; + selectorFrame.origin.y = fullRect.size.height - selectorFrame.size.height - st::macSelectorTop; + [enableLabel setFrame:enableFrame]; + [enableLabel setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin]; + [selector setFrame:selectorFrame]; + [selector setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin]; + + NSButton *button = [[NSButton alloc] init]; + [accessory addSubview:button]; + [button setButtonType:NSSwitchButton]; + [button setFont:[selector font]]; + [button setTitle:objc_lang(lng_mac_always_open_with).s()]; + [button sizeToFit]; + NSRect alwaysRect = [button frame]; + alwaysRect.origin.x = (fullRect.size.width - alwaysRect.size.width) / 2; + alwaysRect.origin.y = selectorFrame.origin.y - alwaysRect.size.height - st::macAlwaysThisAppTop; + [button setFrame:alwaysRect]; + [button setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin]; + NSTextField *goodLabel = [[NSTextField alloc] init]; + [goodLabel setStringValue:QNSString(lng_mac_this_app_can_open(lt_file, objcString(name))).s()]; + [goodLabel setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + [goodLabel setBezeled:NO]; + [goodLabel setDrawsBackground:NO]; + [goodLabel setEditable:NO]; + [goodLabel setSelectable:NO]; + [goodLabel sizeToFit]; + NSRect goodFrame = [goodLabel frame]; + goodFrame.origin.x = (fullRect.size.width - goodFrame.size.width) / 2.; + goodFrame.origin.y = alwaysRect.origin.y - goodFrame.size.height - st::macAppHintTop; + [goodLabel setFrame:goodFrame]; + + NSTextField *badLabel = [[NSTextField alloc] init]; + [badLabel setStringValue:QNSString(lng_mac_not_known_app(lt_file, objcString(name))).s()]; + [badLabel setFont:[goodLabel font]]; + [badLabel setBezeled:NO]; + [badLabel setDrawsBackground:NO]; + [badLabel setEditable:NO]; + [badLabel setSelectable:NO]; + [badLabel sizeToFit]; + NSImageView *badIcon = [[NSImageView alloc] init]; + NSImage *badImage = [NSImage imageNamed:NSImageNameCaution]; + [badIcon setImage:badImage]; + [badIcon setFrame:NSMakeRect(0, 0, st::macCautionIconSize, st::macCautionIconSize)]; + + NSRect badFrame = [badLabel frame], badIconFrame = [badIcon frame]; + badFrame.origin.x = (fullRect.size.width - badFrame.size.width + badIconFrame.size.width) / 2.; + badIconFrame.origin.x = (fullRect.size.width - badFrame.size.width - badIconFrame.size.width) / 2.; + badFrame.origin.y = alwaysRect.origin.y - badFrame.size.height - st::macAppHintTop; + badIconFrame.origin.y = badFrame.origin.y; + [badLabel setFrame:badFrame]; + [badIcon setFrame:badIconFrame]; + + [openPanel setAccessoryView:accessory]; + + ChooseApplicationDelegate *delegate = [[ChooseApplicationDelegate alloc] init:apps withPanel:openPanel withSelector:selector withGood:goodLabel withBad:badLabel withIcon:badIcon withAccessory:accessory]; + [openPanel setDelegate:delegate]; + + [openPanel setCanChooseDirectories:NO]; + [openPanel setCanChooseFiles:YES]; + [openPanel setAllowsMultipleSelection:NO]; + [openPanel setResolvesAliases:YES]; + [openPanel setTitle:objc_lang(lng_mac_choose_app).s()]; + [openPanel setMessage:QNSString(lng_mac_choose_text(lt_file, objcString(name))).s()]; + + NSArray *appsPaths = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationDirectory inDomains:NSLocalDomainMask]; + if ([appsPaths count]) [openPanel setDirectoryURL:[appsPaths firstObject]]; + [openPanel beginWithCompletionHandler:^(NSInteger result){ + if (result == NSFileHandlingPanelOKButton) { + if ([[openPanel URLs] count] > 0) { + NSURL *app = [[openPanel URLs] objectAtIndex:0]; + NSString *path = [app path]; + if ([button state] == NSOnState) { + NSArray *UTIs = (NSArray *)UTTypeCreateAllIdentifiersForTag(kUTTagClassFilenameExtension, + (CFStringRef)ext, + nil); + for (NSString *UTI in UTIs) { + OSStatus result = LSSetDefaultRoleHandlerForContentType((CFStringRef)UTI, + kLSRolesAll, + (CFStringRef)[[NSBundle bundleWithPath:path] bundleIdentifier]); + DEBUG_LOG(("App Info: set default handler for '%1' UTI result: %2").arg(objcString(UTI)).arg(result)); + } + + [UTIs release]; + } + [[NSWorkspace sharedWorkspace] openFile:file withApplication:[app path]]; + } + } + [selector release]; + [button release]; + [enableLabel release]; + [goodLabel release]; + [badLabel release]; + [badIcon release]; + [accessory release]; + [delegate release]; + }]; + } + @catch (NSException *exception) { + [[NSWorkspace sharedWorkspace] openFile:file]; + } + @finally { + } + } +} + +void objc_start() { + _sharedDelegate = [[ApplicationDelegate alloc] init]; + [[NSApplication sharedApplication] setDelegate:_sharedDelegate]; + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: _sharedDelegate + selector: @selector(receiveWakeNote:) + name: NSWorkspaceDidWakeNotification object: NULL]; +} + +namespace { + NSURL *_downloadPathUrl = nil; +} + +void objc_finish() { + [_sharedDelegate release]; + if (_downloadPathUrl) { + [_downloadPathUrl stopAccessingSecurityScopedResource]; + _downloadPathUrl = nil; + } +} + +void objc_registerCustomScheme() { + #ifndef TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME + OSStatus result = LSSetDefaultHandlerForURLScheme(CFSTR("tg"), (CFStringRef)[[NSBundle mainBundle] bundleIdentifier]); + DEBUG_LOG(("App Info: set default handler for 'tg' scheme result: %1").arg(result)); + #endif +} + +BOOL _execUpdater(BOOL update = YES, const QString &crashreport = QString()) { + NSString *path = @"", *args = @""; + @try { + path = [[NSBundle mainBundle] bundlePath]; + if (!path) { + LOG(("Could not get bundle path!!")); + return NO; + } + path = [path stringByAppendingString:@"/Contents/Frameworks/Updater"]; + + NSMutableArray *args = [[NSMutableArray alloc] initWithObjects:@"-workpath", QNSString(cWorkingDir()).s(), @"-procid", nil]; + [args addObject:[NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]]]; + if (cRestartingToSettings()) [args addObject:@"-tosettings"]; + if (!update) [args addObject:@"-noupdate"]; + if (cLaunchMode() == LaunchModeAutoStart) [args addObject:@"-autostart"]; + if (cDebug()) [args addObject:@"-debug"]; + if (cStartInTray()) [args addObject:@"-startintray"]; + if (cTestMode()) [args addObject:@"-testmode"]; + if (cDataFile() != qsl("data")) { + [args addObject:@"-key"]; + [args addObject:QNSString(cDataFile()).s()]; + } + if (!crashreport.isEmpty()) { + [args addObject:@"-crashreport"]; + [args addObject:QNSString(crashreport).s()]; + } + + DEBUG_LOG(("Application Info: executing %1 %2").arg(objcString(path)).arg(objcString([args componentsJoinedByString:@" "]))); + Logs::closeMain(); + SignalHandlers::finish(); + if (![NSTask launchedTaskWithLaunchPath:path arguments:args]) { + DEBUG_LOG(("Task not launched while executing %1 %2").arg(objcString(path)).arg(objcString([args componentsJoinedByString:@" "]))); + return NO; + } + } + @catch (NSException *exception) { + LOG(("Exception caught while executing %1 %2").arg(objcString(path)).arg(objcString(args))); + return NO; + } + @finally { + } + return YES; +} + +bool objc_execUpdater() { + return !!_execUpdater(); +} + +void objc_execTelegram(const QString &crashreport) { + _execUpdater(NO, crashreport); +} + +void objc_activateProgram(WId winId) { + [NSApp activateIgnoringOtherApps:YES]; + if (winId) { + NSWindow *w = [reinterpret_cast(winId) window]; + [w makeKeyAndOrderFront:NSApp]; + } +} + +bool objc_moveFile(const QString &from, const QString &to) { + NSString *f = QNSString(from).s(), *t = QNSString(to).s(); + if ([[NSFileManager defaultManager] fileExistsAtPath:t]) { + NSData *data = [NSData dataWithContentsOfFile:f]; + if (data) { + if ([data writeToFile:t atomically:YES]) { + if ([[NSFileManager defaultManager] removeItemAtPath:f error:nil]) { + return true; + } + } + } + } else { + if ([[NSFileManager defaultManager] moveItemAtPath:f toPath:t error:nil]) { + return true; + } + } + return false; +} + +void objc_deleteDir(const QString &dir) { + [[NSFileManager defaultManager] removeItemAtPath:QNSString(dir).s() error:nil]; +} + +double objc_appkitVersion() { + return NSAppKitVersionNumber; +} + +QString objc_appDataPath() { + NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; + if (url) { + return QString::fromUtf8([[url path] fileSystemRepresentation]) + '/' + str_const_toString(AppName) + '/'; + } + return QString(); +} + +QString objc_downloadPath() { + NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSDownloadsDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; + if (url) { + return QString::fromUtf8([[url path] fileSystemRepresentation]) + '/' + str_const_toString(AppName) + '/'; + } + return QString(); +} + +QString objc_currentCountry() { + NSLocale *currentLocale = [NSLocale currentLocale]; // get the current locale. + NSString *countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; + return countryCode ? objcString(countryCode) : QString(); +} + +QString objc_currentLang() { + NSLocale *currentLocale = [NSLocale currentLocale]; // get the current locale. + NSString *currentLang = [currentLocale objectForKey:NSLocaleLanguageCode]; + return currentLang ? objcString(currentLang) : QString(); +} + +QString objc_convertFileUrl(const QString &url) { + NSString *nsurl = [[[NSURL URLWithString: [NSString stringWithUTF8String: (qsl("file://") + url).toUtf8().constData()]] filePathURL] path]; + if (!nsurl) return QString(); + + return objcString(nsurl); +} + +QByteArray objc_downloadPathBookmark(const QString &path) { + return QByteArray(); +} + +QByteArray objc_pathBookmark(const QString &path) { + return QByteArray(); +} + +void objc_downloadPathEnableAccess(const QByteArray &bookmark) { +} + +objc_FileBookmark::objc_FileBookmark(const QByteArray &bookmark) { +} + +bool objc_FileBookmark::valid() const { + return true; +} + +bool objc_FileBookmark::enable() const { + return true; +} + +void objc_FileBookmark::disable() const { +} + +const QString &objc_FileBookmark::name(const QString &original) const { + return original; +} + +QByteArray objc_FileBookmark::bookmark() const { + return QByteArray(); +} + +objc_FileBookmark::~objc_FileBookmark() { +} diff --git a/Telegram/SourceFiles/platform/platform_main_window.h b/Telegram/SourceFiles/platform/platform_main_window.h new file mode 100644 index 000000000..97fe810ed --- /dev/null +++ b/Telegram/SourceFiles/platform/platform_main_window.h @@ -0,0 +1,31 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#ifdef Q_OS_MAC +#include "platform/mac/main_window_mac.h" +#elif defined Q_OS_LINUX // Q_OS_MAC +#include "platform/linux/main_window_linux.h" +#elif defined Q_OS_WINRT // Q_OS_MAC || Q_OS_LINUX +#include "platform/winrt/main_window_winrt.h" +#elif defined Q_OS_WIN // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT +#include "platform/win/main_window_win.h" +#endif // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT || Q_OS_WIN diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp new file mode 100644 index 000000000..d7dc77ec6 --- /dev/null +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -0,0 +1,1099 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "platform/win/main_window_win.h" + +#include "platform/win/windows_toasts.h" +#include "platform/win/windows_dlls.h" +#include "mainwindow.h" +#include "application.h" +#include "lang.h" +#include "localstorage.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) < (b) ? (b) : (a)) +#include +#undef min +#undef max + +HICON qt_pixmapToWinHICON(const QPixmap &); + +using namespace Microsoft::WRL; + +namespace Platform { +namespace { + +HICON createHIconFromQIcon(const QIcon &icon, int xSize, int ySize) { + if (!icon.isNull()) { + const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize))); + if (!pm.isNull()) { + return qt_pixmapToWinHICON(pm); + } + } + return nullptr; +} + +HWND createTaskbarHider() { + HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0); + HWND hWnd = 0; + + QString cn = QString("TelegramTaskbarHider"); + LPCWSTR _cn = (LPCWSTR)cn.utf16(); + WNDCLASSEX wc; + + wc.cbSize = sizeof(wc); + wc.style = 0; + wc.lpfnWndProc = DefWindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = appinst; + wc.hIcon = 0; + wc.hCursor = 0; + wc.hbrBackground = 0; + wc.lpszMenuName = NULL; + wc.lpszClassName = _cn; + wc.hIconSm = 0; + if (!RegisterClassEx(&wc)) { + DEBUG_LOG(("Application Error: could not register taskbar hider window class, error: %1").arg(GetLastError())); + return hWnd; + } + + hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, _cn, 0, WS_POPUP, 0, 0, 0, 0, 0, 0, appinst, 0); + if (!hWnd) { + DEBUG_LOG(("Application Error: could not create taskbar hider window class, error: %1").arg(GetLastError())); + return hWnd; + } + return hWnd; +} + +enum { + _PsInitHor = 0x01, + _PsInitVer = 0x02, +}; + +int32 _psSize = 0; +class _PsShadowWindows { +public: + + using Change = MainWindow::ShadowsChange; + using Changes = MainWindow::ShadowsChanges; + + _PsShadowWindows() : screenDC(0), max_w(0), max_h(0), _x(0), _y(0), _w(0), _h(0), hidden(true), r(0), g(0), b(0), noKeyColor(RGB(255, 255, 255)) { + for (int i = 0; i < 4; ++i) { + dcs[i] = 0; + bitmaps[i] = 0; + hwnds[i] = 0; + } + } + + void setColor(QColor c) { + r = c.red(); + g = c.green(); + b = c.blue(); + + if (!hwnds[0]) return; + Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); + for (int i = 0; i < 4; ++i) { + Gdiplus::Graphics graphics(dcs[i]); + graphics.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); + if ((i % 2) && _h || !(i % 2) && _w) { + graphics.FillRectangle(&brush, 0, 0, (i % 2) ? _size : _w, (i % 2) ? _h : _size); + } + } + initCorners(); + + _x = _y = _w = _h = 0; + update(Change::Moved | Change::Resized); + } + + bool init(QColor c) { + _fullsize = st::wndShadow.rect().width(); + _shift = st::wndShadowShift; + QImage cornersImage(_fullsize, _fullsize, QImage::Format_ARGB32_Premultiplied); + { + Painter p(&cornersImage); + p.drawSprite(0, 0, st::wndShadow); + } + if (rtl()) cornersImage = cornersImage.mirrored(true, false); + uchar *bits = cornersImage.bits(); + if (bits) { + for ( + quint32 *p = (quint32*)bits, *end = (quint32*)(bits + cornersImage.byteCount()); + p < end; + ++p + ) { + *p = (*p ^ 0x00ffffff) << 24; + } + } + + _metaSize = _fullsize + 2 * _shift; + _alphas.reserve(_metaSize); + _colors.reserve(_metaSize * _metaSize); + for (int32 j = 0; j < _metaSize; ++j) { + for (int32 i = 0; i < _metaSize; ++i) { + _colors.push_back((i < 2 * _shift || j < 2 * _shift) ? 1 : qMax(BYTE(1), BYTE(cornersImage.pixel(QPoint(i - 2 * _shift, j - 2 * _shift)) >> 24))); + } + } + uchar prev = 0; + for (int32 i = 0; i < _metaSize; ++i) { + uchar a = _colors[(_metaSize - 1) * _metaSize + i]; + if (a < prev) break; + + _alphas.push_back(a); + prev = a; + } + _psSize = _size = _alphas.size() - 2 * _shift; + + setColor(c); + + Gdiplus::GdiplusStartupInput gdiplusStartupInput; + ULONG_PTR gdiplusToken; + Gdiplus::Status gdiRes = Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); + + if (gdiRes != Gdiplus::Ok) { + LOG(("Application Error: could not init GDI+, error: %1").arg((int)gdiRes)); + return false; + } + blend.AlphaFormat = AC_SRC_ALPHA; + blend.SourceConstantAlpha = 255; + blend.BlendFlags = 0; + blend.BlendOp = AC_SRC_OVER; + + screenDC = GetDC(0); + if (!screenDC) { + LOG(("Application Error: could not GetDC(0), error: %2").arg(GetLastError())); + return false; + } + + QRect avail(Sandbox::availableGeometry()); + max_w = avail.width(); + if (max_w < st::wndMinWidth) max_w = st::wndMinWidth; + max_h = avail.height(); + if (max_h < st::wndMinHeight) max_h = st::wndMinHeight; + + HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0); + HWND hwnd = App::wnd() ? App::wnd()->psHwnd() : 0; + + for (int i = 0; i < 4; ++i) { + QString cn = QString("TelegramShadow%1").arg(i); + LPCWSTR _cn = (LPCWSTR)cn.utf16(); + WNDCLASSEX wc; + + wc.cbSize = sizeof(wc); + wc.style = 0; + wc.lpfnWndProc = wndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = appinst; + wc.hIcon = 0; + wc.hCursor = 0; + wc.hbrBackground = 0; + wc.lpszMenuName = NULL; + wc.lpszClassName = _cn; + wc.hIconSm = 0; + if (!RegisterClassEx(&wc)) { + LOG(("Application Error: could not register shadow window class %1, error: %2").arg(i).arg(GetLastError())); + destroy(); + return false; + } + + hwnds[i] = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOOLWINDOW, _cn, 0, WS_POPUP, 0, 0, 0, 0, 0, 0, appinst, 0); + if (!hwnds[i]) { + LOG(("Application Error: could not create shadow window class %1, error: %2").arg(i).arg(GetLastError())); + destroy(); + return false; + } + SetWindowLong(hwnds[i], GWL_HWNDPARENT, (LONG)hwnd); + + dcs[i] = CreateCompatibleDC(screenDC); + if (!dcs[i]) { + LOG(("Application Error: could not create dc for shadow window class %1, error: %2").arg(i).arg(GetLastError())); + destroy(); + return false; + } + + bitmaps[i] = CreateCompatibleBitmap(screenDC, (i % 2) ? _size : max_w, (i % 2) ? max_h : _size); + if (!bitmaps[i]) { + LOG(("Application Error: could not create bitmap for shadow window class %1, error: %2").arg(i).arg(GetLastError())); + destroy(); + return false; + } + + SelectObject(dcs[i], bitmaps[i]); + } + + initCorners(); + return true; + } + + void initCorners(int directions = (_PsInitHor | _PsInitVer)) { + bool hor = (directions & _PsInitHor), ver = (directions & _PsInitVer); + Gdiplus::Graphics graphics0(dcs[0]), graphics1(dcs[1]), graphics2(dcs[2]), graphics3(dcs[3]); + graphics0.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); + graphics1.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); + graphics2.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); + graphics3.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); + + Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); + if (hor) graphics0.FillRectangle(&brush, 0, 0, _fullsize - (_size - _shift), 2 * _shift); + + if (ver) { + graphics1.FillRectangle(&brush, 0, 0, _size, 2 * _shift); + graphics3.FillRectangle(&brush, 0, 0, _size, 2 * _shift); + graphics1.FillRectangle(&brush, _size - _shift, 2 * _shift, _shift, _fullsize); + graphics3.FillRectangle(&brush, 0, 2 * _shift, _shift, _fullsize); + } + + if (hor) { + for (int j = 2 * _shift; j < _size; ++j) { + for (int k = 0; k < _fullsize - (_size - _shift); ++k) { + brush.SetColor(Gdiplus::Color(_colors[j * _metaSize + k + (_size + _shift)], r, g, b)); + graphics0.FillRectangle(&brush, k, j, 1, 1); + graphics2.FillRectangle(&brush, k, _size - (j - 2 * _shift) - 1, 1, 1); + } + } + for (int j = _size; j < _size + 2 * _shift; ++j) { + for (int k = 0; k < _fullsize - (_size - _shift); ++k) { + brush.SetColor(Gdiplus::Color(_colors[j * _metaSize + k + (_size + _shift)], r, g, b)); + graphics2.FillRectangle(&brush, k, _size - (j - 2 * _shift) - 1, 1, 1); + } + } + } + if (ver) { + for (int j = 2 * _shift; j < _fullsize + 2 * _shift; ++j) { + for (int k = _shift; k < _size; ++k) { + brush.SetColor(Gdiplus::Color(_colors[j * _metaSize + (k + _shift)], r, g, b)); + graphics1.FillRectangle(&brush, _size - k - 1, j, 1, 1); + graphics3.FillRectangle(&brush, k, j, 1, 1); + } + } + } + } + void verCorners(int h, Gdiplus::Graphics *pgraphics1, Gdiplus::Graphics *pgraphics3) { + Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); + pgraphics1->FillRectangle(&brush, _size - _shift, h - _fullsize, _shift, _fullsize); + pgraphics3->FillRectangle(&brush, 0, h - _fullsize, _shift, _fullsize); + for (int j = 0; j < _fullsize; ++j) { + for (int k = _shift; k < _size; ++k) { + brush.SetColor(Gdiplus::Color(_colors[(j + 2 * _shift) * _metaSize + k + _shift], r, g, b)); + pgraphics1->FillRectangle(&brush, _size - k - 1, h - j - 1, 1, 1); + pgraphics3->FillRectangle(&brush, k, h - j - 1, 1, 1); + } + } + } + void horCorners(int w, Gdiplus::Graphics *pgraphics0, Gdiplus::Graphics *pgraphics2) { + Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); + pgraphics0->FillRectangle(&brush, w - 2 * _size - (_fullsize - (_size - _shift)), 0, _fullsize - (_size - _shift), 2 * _shift); + for (int j = 2 * _shift; j < _size; ++j) { + for (int k = 0; k < _fullsize - (_size - _shift); ++k) { + brush.SetColor(Gdiplus::Color(_colors[j * _metaSize + k + (_size + _shift)], r, g, b)); + pgraphics0->FillRectangle(&brush, w - 2 * _size - k - 1, j, 1, 1); + pgraphics2->FillRectangle(&brush, w - 2 * _size - k - 1, _size - (j - 2 * _shift) - 1, 1, 1); + } + } + for (int j = _size; j < _size + 2 * _shift; ++j) { + for (int k = 0; k < _fullsize - (_size - _shift); ++k) { + brush.SetColor(Gdiplus::Color(_colors[j * _metaSize + k + (_size + _shift)], r, g, b)); + pgraphics2->FillRectangle(&brush, w - 2 * _size - k - 1, _size - (j - 2 * _shift) - 1, 1, 1); + } + } + } + + void update(Changes changes, WINDOWPOS *pos = 0) { + HWND hwnd = App::wnd() ? App::wnd()->psHwnd() : 0; + if (!hwnd || !hwnds[0]) return; + + if (changes == Changes(Change::Activate)) { + for (int i = 0; i < 4; ++i) { + SetWindowPos(hwnds[i], hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } + return; + } + + if (changes & Change::Hidden) { + if (!hidden) { + for (int i = 0; i < 4; ++i) { + hidden = true; + ShowWindow(hwnds[i], SW_HIDE); + } + } + return; + } + if (!App::wnd()->psPosInited()) return; + + int x = _x, y = _y, w = _w, h = _h; + if (pos && (!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE) || !(pos->flags & SWP_NOREPOSITION))) { + if (!(pos->flags & SWP_NOMOVE)) { + x = pos->x - _size; + y = pos->y - _size; + } else if (pos->flags & SWP_NOSIZE) { + for (int i = 0; i < 4; ++i) { + SetWindowPos(hwnds[i], hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + } + return; + } + if (!(pos->flags & SWP_NOSIZE)) { + w = pos->cx + 2 * _size; + h = pos->cy + 2 * _size; + } + } else { + RECT r; + GetWindowRect(hwnd, &r); + x = r.left - _size; + y = r.top - _size; + w = r.right + _size - x; + h = r.bottom + _size - y; + } + if (h < 2 * _fullsize + 2 * _shift) { + h = 2 * _fullsize + 2 * _shift; + } + if (w < 2 * (_fullsize + _shift)) { + w = 2 * (_fullsize + _shift); + } + + if (w != _w) { + int from = (_w > 2 * (_fullsize + _shift)) ? (_w - _size - _fullsize - _shift) : (_fullsize - (_size - _shift)); + int to = w - _size - _fullsize - _shift; + if (w > max_w) { + from = _fullsize - (_size - _shift); + max_w *= 2; + for (int i = 0; i < 4; i += 2) { + DeleteObject(bitmaps[i]); + bitmaps[i] = CreateCompatibleBitmap(screenDC, max_w, _size); + SelectObject(dcs[i], bitmaps[i]); + } + initCorners(_PsInitHor); + } + Gdiplus::Graphics graphics0(dcs[0]), graphics2(dcs[2]); + graphics0.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); + graphics2.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); + Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); + if (to > from) { + graphics0.FillRectangle(&brush, from, 0, to - from, 2 * _shift); + for (int i = 2 * _shift; i < _size; ++i) { + Gdiplus::Pen pen(Gdiplus::Color(_alphas[i], r, g, b)); + graphics0.DrawLine(&pen, from, i, to, i); + graphics2.DrawLine(&pen, from, _size - (i - 2 * _shift) - 1, to, _size - (i - 2 * _shift) - 1); + } + for (int i = _size; i < _size + 2 * _shift; ++i) { + Gdiplus::Pen pen(Gdiplus::Color(_alphas[i], r, g, b)); + graphics2.DrawLine(&pen, from, _size - (i - 2 * _shift) - 1, to, _size - (i - 2 * _shift) - 1); + } + } + if (_w > w) { + graphics0.FillRectangle(&brush, w - _size - _fullsize - _shift, 0, _fullsize - (_size - _shift), _size); + graphics2.FillRectangle(&brush, w - _size - _fullsize - _shift, 0, _fullsize - (_size - _shift), _size); + } + horCorners(w, &graphics0, &graphics2); + POINT p0 = { x + _size, y }, p2 = { x + _size, y + h - _size }, f = { 0, 0 }; + SIZE s = { w - 2 * _size, _size }; + updateWindow(0, &p0, &s); + updateWindow(2, &p2, &s); + } else if (x != _x || y != _y) { + POINT p0 = { x + _size, y }, p2 = { x + _size, y + h - _size }; + updateWindow(0, &p0); + updateWindow(2, &p2); + } else if (h != _h) { + POINT p2 = { x + _size, y + h - _size }; + updateWindow(2, &p2); + } + + if (h != _h) { + int from = (_h > 2 * _fullsize + 2 * _shift) ? (_h - _fullsize) : (_fullsize + 2 * _shift); + int to = h - _fullsize; + if (h > max_h) { + from = (_fullsize + 2 * _shift); + max_h *= 2; + for (int i = 1; i < 4; i += 2) { + DeleteObject(bitmaps[i]); + bitmaps[i] = CreateCompatibleBitmap(dcs[i], _size, max_h); + SelectObject(dcs[i], bitmaps[i]); + } + initCorners(_PsInitVer); + } + Gdiplus::Graphics graphics1(dcs[1]), graphics3(dcs[3]); + graphics1.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); + graphics3.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); + + Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); + if (to > from) { + graphics1.FillRectangle(&brush, _size - _shift, from, _shift, to - from); + graphics3.FillRectangle(&brush, 0, from, _shift, to - from); + for (int i = 2 * _shift; i < _size + _shift; ++i) { + Gdiplus::Pen pen(Gdiplus::Color(_alphas[i], r, g, b)); + graphics1.DrawLine(&pen, _size + _shift - i - 1, from, _size + _shift - i - 1, to); + graphics3.DrawLine(&pen, i - _shift, from, i - _shift, to); + } + } + if (_h > h) { + graphics1.FillRectangle(&brush, 0, h - _fullsize, _size, _fullsize); + graphics3.FillRectangle(&brush, 0, h - _fullsize, _size, _fullsize); + } + verCorners(h, &graphics1, &graphics3); + + POINT p1 = { x + w - _size, y }, p3 = { x, y }, f = { 0, 0 }; + SIZE s = { _size, h }; + updateWindow(1, &p1, &s); + updateWindow(3, &p3, &s); + } else if (x != _x || y != _y) { + POINT p1 = { x + w - _size, y }, p3 = { x, y }; + updateWindow(1, &p1); + updateWindow(3, &p3); + } else if (w != _w) { + POINT p1 = { x + w - _size, y }; + updateWindow(1, &p1); + } + _x = x; + _y = y; + _w = w; + _h = h; + + if (hidden && (changes & Change::Shown)) { + for (int i = 0; i < 4; ++i) { + SetWindowPos(hwnds[i], hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE); + } + hidden = false; + } + } + + void updateWindow(int i, POINT *p, SIZE *s = 0) { + static POINT f = { 0, 0 }; + if (s) { + UpdateLayeredWindow(hwnds[i], (s ? screenDC : 0), p, s, (s ? dcs[i] : 0), (s ? (&f) : 0), noKeyColor, &blend, ULW_ALPHA); + } else { + SetWindowPos(hwnds[i], 0, p->x, p->y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); + } + } + + void destroy() { + for (int i = 0; i < 4; ++i) { + if (dcs[i]) DeleteDC(dcs[i]); + if (bitmaps[i]) DeleteObject(bitmaps[i]); + if (hwnds[i]) DestroyWindow(hwnds[i]); + dcs[i] = 0; + bitmaps[i] = 0; + hwnds[i] = 0; + } + if (screenDC) ReleaseDC(0, screenDC); + } + +private: + + int _x, _y, _w, _h; + int _metaSize, _fullsize, _size, _shift; + QVector _alphas, _colors; + + bool hidden; + + HWND hwnds[4]; + HDC dcs[4], screenDC; + HBITMAP bitmaps[4]; + int max_w, max_h; + BLENDFUNCTION blend; + + BYTE r, g, b; + COLORREF noKeyColor; + + static LRESULT CALLBACK _PsShadowWindows::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + +}; +_PsShadowWindows _psShadowWindows; +QColor _shActive(0, 0, 0), _shInactive(0, 0, 0); + +LRESULT CALLBACK _PsShadowWindows::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + auto wnd = App::wnd(); + if (!wnd || !wnd->shadowsWorking()) return DefWindowProc(hwnd, msg, wParam, lParam); + + int i; + for (i = 0; i < 4; ++i) { + if (_psShadowWindows.hwnds[i] && hwnd == _psShadowWindows.hwnds[i]) { + break; + } + } + if (i == 4) return DefWindowProc(hwnd, msg, wParam, lParam); + + switch (msg) { + case WM_CLOSE: + App::wnd()->close(); + break; + + case WM_NCHITTEST: { + int32 xPos = GET_X_LPARAM(lParam), yPos = GET_Y_LPARAM(lParam); + switch (i) { + case 0: return HTTOP; + case 1: return (yPos < _psShadowWindows._y + _psSize) ? HTTOPRIGHT : ((yPos >= _psShadowWindows._y + _psShadowWindows._h - _psSize) ? HTBOTTOMRIGHT : HTRIGHT); + case 2: return HTBOTTOM; + case 3: return (yPos < _psShadowWindows._y + _psSize) ? HTTOPLEFT : ((yPos >= _psShadowWindows._y + _psShadowWindows._h - _psSize) ? HTBOTTOMLEFT : HTLEFT); + } + return HTTRANSPARENT; + } break; + + case WM_NCACTIVATE: return DefWindowProc(hwnd, msg, wParam, lParam); + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONUP: + case WM_NCLBUTTONDBLCLK: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONUP: + case WM_NCMBUTTONDBLCLK: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONUP: + case WM_NCRBUTTONDBLCLK: + case WM_NCXBUTTONDOWN: + case WM_NCXBUTTONUP: + case WM_NCXBUTTONDBLCLK: + case WM_NCMOUSEHOVER: + case WM_NCMOUSELEAVE: + case WM_NCMOUSEMOVE: + case WM_NCPOINTERUPDATE: + case WM_NCPOINTERDOWN: + case WM_NCPOINTERUP: + if (App::wnd() && App::wnd()->psHwnd()) { + if (msg == WM_NCLBUTTONDOWN) { + ::SetForegroundWindow(App::wnd()->psHwnd()); + } + LRESULT res = SendMessage(App::wnd()->psHwnd(), msg, wParam, lParam); + return res; + } + return 0; + break; + case WM_ACTIVATE: + if (App::wnd() && App::wnd()->psHwnd() && wParam == WA_ACTIVE) { + if ((HWND)lParam != App::wnd()->psHwnd()) { + ::SetForegroundWindow(hwnd); + ::SetWindowPos(App::wnd()->psHwnd(), hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + } + return DefWindowProc(hwnd, msg, wParam, lParam); + break; + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + return 0; +} + +ComPtr taskbarList; + +bool handleSessionNotification = false; + +} // namespace + +UINT MainWindow::_taskbarCreatedMsgId = 0; + +MainWindow::MainWindow() +: icon256(qsl(":/gui/art/icon256.png")) +, iconbig256(qsl(":/gui/art/iconbig256.png")) +, wndIcon(QPixmap::fromImage(icon256, Qt::ColorOnly)) +, ps_tbHider_hWnd(createTaskbarHider()) { + if (!_taskbarCreatedMsgId) { + _taskbarCreatedMsgId = RegisterWindowMessage(L"TaskbarButtonCreated"); + } + connect(&ps_cleanNotifyPhotosTimer, SIGNAL(timeout()), this, SLOT(psCleanNotifyPhotos())); +} + +void MainWindow::TaskbarCreated() { + HRESULT hr = CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&taskbarList)); + if (!SUCCEEDED(hr)) { + taskbarList.Reset(); + } +} + +void MainWindow::shadowsUpdate(ShadowsChanges changes, WINDOWPOS *position) { + _psShadowWindows.update(changes, position); +} + +void MainWindow::shadowsActivate() { + _psShadowWindows.setColor(_shActive); + shadowsUpdate(ShadowsChange::Activate); +} + +void MainWindow::shadowsDeactivate() { + _psShadowWindows.setColor(_shInactive); +} + +void MainWindow::psShowTrayMenu() { + trayIconMenu->popup(QCursor::pos()); +} + +void MainWindow::psCleanNotifyPhotosIn(int32 dt) { + if (dt < 0) { + if (ps_cleanNotifyPhotosTimer.isActive() && ps_cleanNotifyPhotosTimer.remainingTime() <= -dt) return; + dt = -dt; + } + ps_cleanNotifyPhotosTimer.start(dt); +} + +void MainWindow::psCleanNotifyPhotos() { + auto ms = getms(true); + auto minuntil = Toasts::clearImages(ms); + if (minuntil) { + psCleanNotifyPhotosIn(int32(minuntil - ms)); + } +} + +void MainWindow::psRefreshTaskbarIcon() { + QWidget *w = new QWidget(this); + w->setWindowFlags(::operator|(Qt::Tool, Qt::FramelessWindowHint)); + w->setGeometry(x() + 1, y() + 1, 1, 1); + QPalette p(w->palette()); + p.setColor(QPalette::Background, st::titleBG->c); + QWindow *wnd = w->windowHandle(); + w->setPalette(p); + w->show(); + w->activateWindow(); + delete w; +} + +void MainWindow::psTrayMenuUpdated() { +} + +void MainWindow::psSetupTrayIcon() { + if (!trayIcon) { + trayIcon = new QSystemTrayIcon(this); + + QIcon icon(QPixmap::fromImage(App::wnd()->iconLarge(), Qt::ColorOnly)); + + trayIcon->setIcon(icon); + trayIcon->setToolTip(str_const_toString(AppName)); + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(toggleTray(QSystemTrayIcon::ActivationReason)), Qt::UniqueConnection); + connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showFromTray())); + App::wnd()->updateTrayMenu(); + } + psUpdateCounter(); + + trayIcon->show(); + psUpdateDelegate(); +} + +void MainWindow::psUpdateWorkmode() { + switch (cWorkMode()) { + case dbiwmWindowAndTray: { + psSetupTrayIcon(); + HWND psOwner = (HWND)GetWindowLong(ps_hWnd, GWL_HWNDPARENT); + if (psOwner) { + SetWindowLong(ps_hWnd, GWL_HWNDPARENT, 0); + psRefreshTaskbarIcon(); + } + } break; + + case dbiwmTrayOnly: { + psSetupTrayIcon(); + HWND psOwner = (HWND)GetWindowLong(ps_hWnd, GWL_HWNDPARENT); + if (!psOwner) { + SetWindowLong(ps_hWnd, GWL_HWNDPARENT, (LONG)ps_tbHider_hWnd); + } + } break; + + case dbiwmWindowOnly: { + if (trayIcon) { + trayIcon->setContextMenu(0); + trayIcon->deleteLater(); + } + trayIcon = 0; + + HWND psOwner = (HWND)GetWindowLong(ps_hWnd, GWL_HWNDPARENT); + if (psOwner) { + SetWindowLong(ps_hWnd, GWL_HWNDPARENT, 0); + psRefreshTaskbarIcon(); + } + } break; + } +} + +void MainWindow::psUpdateCounter() { + int32 counter = App::histories().unreadBadge(); + bool muted = App::histories().unreadOnlyMuted(); + + style::color bg = muted ? st::counterMuteBG : st::counterBG; + QIcon iconSmall, iconBig; + iconSmall.addPixmap(QPixmap::fromImage(iconWithCounter(16, counter, bg, true), Qt::ColorOnly)); + iconSmall.addPixmap(QPixmap::fromImage(iconWithCounter(32, counter, bg, true), Qt::ColorOnly)); + iconBig.addPixmap(QPixmap::fromImage(iconWithCounter(32, taskbarList.Get() ? 0 : counter, bg, false), Qt::ColorOnly)); + iconBig.addPixmap(QPixmap::fromImage(iconWithCounter(64, taskbarList.Get() ? 0 : counter, bg, false), Qt::ColorOnly)); + if (trayIcon) { + trayIcon->setIcon(iconSmall); + } + + setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); + psDestroyIcons(); + ps_iconSmall = createHIconFromQIcon(iconSmall, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); + ps_iconBig = createHIconFromQIcon(iconBig, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); + SendMessage(ps_hWnd, WM_SETICON, 0, (LPARAM)ps_iconSmall); + SendMessage(ps_hWnd, WM_SETICON, 1, (LPARAM)(ps_iconBig ? ps_iconBig : ps_iconSmall)); + if (taskbarList.Get()) { + if (counter > 0) { + QIcon iconOverlay; + iconOverlay.addPixmap(QPixmap::fromImage(iconWithCounter(-16, counter, bg, false), Qt::ColorOnly)); + iconOverlay.addPixmap(QPixmap::fromImage(iconWithCounter(-32, counter, bg, false), Qt::ColorOnly)); + ps_iconOverlay = createHIconFromQIcon(iconOverlay, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); + } + QString description = counter > 0 ? QString("%1 unread messages").arg(counter) : qsl("No unread messages"); + taskbarList->SetOverlayIcon(ps_hWnd, ps_iconOverlay, description.toStdWString().c_str()); + } + SetWindowPos(ps_hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); +} + +void MainWindow::psUpdateDelegate() { +} + +namespace { +HMONITOR enumMonitor = 0; +RECT enumMonitorWork; + +BOOL CALLBACK _monitorEnumProc( + _In_ HMONITOR hMonitor, + _In_ HDC hdcMonitor, + _In_ LPRECT lprcMonitor, + _In_ LPARAM dwData +) { + MONITORINFOEX info; + info.cbSize = sizeof(info); + GetMonitorInfo(hMonitor, &info); + if (dwData == hashCrc32(info.szDevice, sizeof(info.szDevice))) { + enumMonitor = hMonitor; + enumMonitorWork = info.rcWork; + return FALSE; + } + return TRUE; +} +} // namespace + +void MainWindow::psInitSize() { + setMinimumWidth(st::wndMinWidth); + setMinimumHeight(st::wndMinHeight); + + TWindowPos pos(cWindowPos()); + QRect avail(Sandbox::availableGeometry()); + bool maximized = false; + QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight); + if (pos.w && pos.h) { + if (pos.y < 0) pos.y = 0; + enumMonitor = 0; + EnumDisplayMonitors(0, 0, &_monitorEnumProc, pos.moncrc); + if (enumMonitor) { + int32 w = enumMonitorWork.right - enumMonitorWork.left, h = enumMonitorWork.bottom - enumMonitorWork.top; + if (w >= st::wndMinWidth && h >= st::wndMinHeight) { + if (pos.w > w) pos.w = w; + if (pos.h > h) pos.h = h; + pos.x += enumMonitorWork.left; + pos.y += enumMonitorWork.top; + if (pos.x < enumMonitorWork.right - 10 && pos.y < enumMonitorWork.bottom - 10) { + geom = QRect(pos.x, pos.y, pos.w, pos.h); + } + } + } + maximized = pos.maximized; + } + setGeometry(geom); +} + +void MainWindow::psInitFrameless() { + psUpdatedPositionTimer.setSingleShot(true); + connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); + + QPlatformNativeInterface *i = QGuiApplication::platformNativeInterface(); + ps_hWnd = static_cast(i->nativeResourceForWindow(QByteArrayLiteral("handle"), windowHandle())); + + if (!ps_hWnd) return; + + handleSessionNotification = (Dlls::WTSRegisterSessionNotification != nullptr) && (Dlls::WTSUnRegisterSessionNotification != nullptr); + if (handleSessionNotification) { + Dlls::WTSRegisterSessionNotification(ps_hWnd, NOTIFY_FOR_THIS_SESSION); + } + +// RegisterApplicationRestart(NULL, 0); + Toasts::start(); + + psInitSysMenu(); +} + +void MainWindow::psSavePosition(Qt::WindowState state) { + if (state == Qt::WindowActive) state = windowHandle()->windowState(); + if (state == Qt::WindowMinimized || !posInited) return; + + TWindowPos pos(cWindowPos()), curPos = pos; + + if (state == Qt::WindowMaximized) { + curPos.maximized = 1; + } else { + RECT w; + GetWindowRect(ps_hWnd, &w); + curPos.x = w.left; + curPos.y = w.top; + curPos.w = w.right - w.left; + curPos.h = w.bottom - w.top; + curPos.maximized = 0; + } + + HMONITOR hMonitor = MonitorFromWindow(ps_hWnd, MONITOR_DEFAULTTONEAREST); + if (hMonitor) { + MONITORINFOEX info; + info.cbSize = sizeof(info); + GetMonitorInfo(hMonitor, &info); + if (!curPos.maximized) { + curPos.x -= info.rcWork.left; + curPos.y -= info.rcWork.top; + } + curPos.moncrc = hashCrc32(info.szDevice, sizeof(info.szDevice)); + } + + if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { + if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { + cSetWindowPos(curPos); + Local::writeSettings(); + } + } +} + +void MainWindow::psUpdatedPosition() { + psUpdatedPositionTimer.start(SaveWindowPositionTimeout); +} + +bool MainWindow::psHasNativeNotifications() { + return Toasts::supported(); +} + +Q_DECLARE_METATYPE(QMargins); +void MainWindow::psFirstShow() { + if (Toasts::supported()) { + cSetCustomNotifies(!cWindowsNotifications()); + } else { + cSetCustomNotifies(true); + } + + _psShadowWindows.init(_shActive); + _shadowsWorking = true; + + psUpdateMargins(); + + shadowsUpdate(ShadowsChange::Hidden); + bool showShadows = true; + + show(); + if (cWindowPos().maximized) { + setWindowState(Qt::WindowMaximized); + } + + if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) { + setWindowState(Qt::WindowMinimized); + if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { + hide(); + } else { + show(); + } + showShadows = false; + } else { + show(); + } + + posInited = true; + if (showShadows) { + shadowsUpdate(ShadowsChange::Moved | ShadowsChange::Resized | ShadowsChange::Shown); + } +} + +bool MainWindow::psHandleTitle() { + return true; +} + +void MainWindow::psInitSysMenu() { + Qt::WindowStates states = windowState(); + ps_menu = GetSystemMenu(ps_hWnd, FALSE); + psUpdateSysMenu(windowHandle()->windowState()); +} + +void MainWindow::psUpdateSysMenu(Qt::WindowState state) { + if (!ps_menu) return; + + int menuToDisable = SC_RESTORE; + if (state == Qt::WindowMaximized) { + menuToDisable = SC_MAXIMIZE; + } else if (state == Qt::WindowMinimized) { + menuToDisable = SC_MINIMIZE; + } + int itemCount = GetMenuItemCount(ps_menu); + for (int i = 0; i < itemCount; ++i) { + MENUITEMINFO itemInfo = { 0 }; + itemInfo.cbSize = sizeof(itemInfo); + itemInfo.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID; + if (GetMenuItemInfo(ps_menu, i, TRUE, &itemInfo)) { + if (itemInfo.fType & MFT_SEPARATOR) { + continue; + } + if (itemInfo.wID && !(itemInfo.fState & MFS_DEFAULT)) { + UINT fOldState = itemInfo.fState, fState = itemInfo.fState & ~MFS_DISABLED; + if (itemInfo.wID == SC_CLOSE) { + fState |= MFS_DEFAULT; + } else if (itemInfo.wID == menuToDisable || (itemInfo.wID != SC_MINIMIZE && itemInfo.wID != SC_MAXIMIZE && itemInfo.wID != SC_RESTORE)) { + fState |= MFS_DISABLED; + } + itemInfo.fMask = MIIM_STATE; + itemInfo.fState = fState; + if (!SetMenuItemInfo(ps_menu, i, TRUE, &itemInfo)) { + DEBUG_LOG(("PS Error: could not set state %1 to menu item %2, old state %3, error %4").arg(fState).arg(itemInfo.wID).arg(fOldState).arg(GetLastError())); + DestroyMenu(ps_menu); + ps_menu = 0; + break; + } + } + } else { + DEBUG_LOG(("PS Error: could not get state, menu item %1 of %2, error %3").arg(i).arg(itemCount).arg(GetLastError())); + DestroyMenu(ps_menu); + ps_menu = 0; + break; + } + } +} + +void MainWindow::psUpdateMargins() { + if (!ps_hWnd) return; + + RECT r, a; + + GetClientRect(ps_hWnd, &r); + a = r; + + LONG style = GetWindowLong(ps_hWnd, GWL_STYLE), styleEx = GetWindowLong(ps_hWnd, GWL_EXSTYLE); + AdjustWindowRectEx(&a, style, false, styleEx); + QMargins margins = QMargins(a.left - r.left, a.top - r.top, r.right - a.right, r.bottom - a.bottom); + if (style & WS_MAXIMIZE) { + RECT w, m; + GetWindowRect(ps_hWnd, &w); + m = w; + + HMONITOR hMonitor = MonitorFromRect(&w, MONITOR_DEFAULTTONEAREST); + if (hMonitor) { + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + m = mi.rcWork; + } + + _deltaLeft = w.left - m.left; + _deltaTop = w.top - m.top; + + margins.setLeft(margins.left() - w.left + m.left); + margins.setRight(margins.right() - m.right + w.right); + margins.setBottom(margins.bottom() - m.bottom + w.bottom); + margins.setTop(margins.top() - w.top + m.top); + } else { + _deltaLeft = _deltaTop = 0; + } + + QPlatformNativeInterface *i = QGuiApplication::platformNativeInterface(); + i->setWindowProperty(windowHandle()->handle(), qsl("WindowsCustomMargins"), QVariant::fromValue(margins)); + if (!_themeInited) { + _themeInited = true; + if (QSysInfo::WindowsVersion < QSysInfo::WV_WINDOWS8) { + if (Dlls::SetWindowTheme != nullptr) { + Dlls::SetWindowTheme(ps_hWnd, L" ", L" "); + QApplication::setStyle(QStyleFactory::create(qsl("Windows"))); + } + } + } +} + +void MainWindow::psFlash() { + if (GetForegroundWindow() == ps_hWnd) return; + + FLASHWINFO info; + info.cbSize = sizeof(info); + info.hwnd = ps_hWnd; + info.dwFlags = FLASHW_ALL; + info.dwTimeout = 0; + info.uCount = 1; + FlashWindowEx(&info); +} + +HWND MainWindow::psHwnd() const { + return ps_hWnd; +} + +HMENU MainWindow::psMenu() const { + return ps_menu; +} + +void MainWindow::psDestroyIcons() { + if (ps_iconBig) { + DestroyIcon(ps_iconBig); + ps_iconBig = 0; + } + if (ps_iconSmall) { + DestroyIcon(ps_iconSmall); + ps_iconSmall = 0; + } + if (ps_iconOverlay) { + DestroyIcon(ps_iconOverlay); + ps_iconOverlay = 0; + } +} + +MainWindow::~MainWindow() { + if (handleSessionNotification) { + QPlatformNativeInterface *i = QGuiApplication::platformNativeInterface(); + if (HWND hWnd = static_cast(i->nativeResourceForWindow(QByteArrayLiteral("handle"), windowHandle()))) { + Dlls::WTSUnRegisterSessionNotification(hWnd); + } + } + + if (taskbarList) taskbarList.Reset(); + + Toasts::finish(); + + _shadowsWorking = false; + if (ps_menu) DestroyMenu(ps_menu); + psDestroyIcons(); + _psShadowWindows.destroy(); + if (ps_tbHider_hWnd) DestroyWindow(ps_tbHider_hWnd); +} + +void MainWindow::psActivateNotify(NotifyWindow *w) { +} + +void MainWindow::psClearNotifies(PeerId peerId) { + Toasts::clearNotifies(peerId); +} + +void MainWindow::psNotifyShown(NotifyWindow *w) { +} + +void MainWindow::psPlatformNotify(HistoryItem *item, int32 fwdCount) { + QString title = (!App::passcoded() && cNotifyView() <= dbinvShowName) ? item->history()->peer->name : qsl("Telegram Desktop"); + QString subtitle = (!App::passcoded() && cNotifyView() <= dbinvShowName) ? item->notificationHeader() : QString(); + bool showpix = (!App::passcoded() && cNotifyView() <= dbinvShowName); + QString msg = (!App::passcoded() && cNotifyView() <= dbinvShowPreview) ? (fwdCount < 2 ? item->notificationText() : lng_forward_messages(lt_count, fwdCount)) : lang(lng_notification_preview); + + Toasts::create(item->history()->peer, item->id, showpix, title, subtitle, msg); +} + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h new file mode 100644 index 000000000..c3bca3270 --- /dev/null +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -0,0 +1,157 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "window/main_window.h" + +#include + +class NotifyWindow; + +namespace Platform { + +class MainWindow : public Window::MainWindow { + Q_OBJECT + +public: + MainWindow(); + + int32 psResizeRowWidth() const { + return 0;//st::wndResizeAreaWidth; + } + + void psInitFrameless(); + void psInitSize(); + HWND psHwnd() const; + HMENU psMenu() const; + + void psFirstShow(); + void psInitSysMenu(); + void psUpdateSysMenu(Qt::WindowState state); + void psUpdateMargins(); + void psUpdatedPosition(); + + bool psHandleTitle(); + + void psFlash(); + void psNotifySettingGot(); + + void psUpdateWorkmode(); + + void psRefreshTaskbarIcon(); + + bool psPosInited() const { + return posInited; + } + + void psActivateNotify(NotifyWindow *w); + void psClearNotifies(PeerId peerId = 0); + void psNotifyShown(NotifyWindow *w); + void psPlatformNotify(HistoryItem *item, int32 fwdCount); + + void psUpdateCounter(); + + bool psHasNativeNotifications(); + void psCleanNotifyPhotosIn(int32 dt); + + virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; + + static UINT TaskbarCreatedMsgId() { + return _taskbarCreatedMsgId; + } + static void TaskbarCreated(); + + // Custom shadows. + enum class ShadowsChange { + Moved = 0x01, + Resized = 0x02, + Shown = 0x04, + Hidden = 0x08, + Activate = 0x10, + }; + Q_DECLARE_FLAGS(ShadowsChanges, ShadowsChange); + + bool shadowsWorking() const { + return _shadowsWorking; + } + void shadowsActivate(); + void shadowsDeactivate(); + void shadowsUpdate(ShadowsChanges changes, WINDOWPOS *position = nullptr); + + int deltaLeft() const { + return _deltaLeft; + } + int deltaTop() const { + return _deltaTop; + } + + ~MainWindow(); + +public slots: + + void psUpdateDelegate(); + void psSavePosition(Qt::WindowState state = Qt::WindowActive); + void psShowTrayMenu(); + + void psCleanNotifyPhotos(); + +protected: + + bool psHasTrayIcon() const { + return trayIcon; + } + + bool posInited = false; + QSystemTrayIcon *trayIcon = nullptr; + PopupMenu *trayIconMenu = nullptr; + QImage icon256, iconbig256; + QIcon wndIcon; + + void psTrayMenuUpdated(); + void psSetupTrayIcon(); + + QTimer psUpdatedPositionTimer; + +private: + void psDestroyIcons(); + + static UINT _taskbarCreatedMsgId; + + bool _shadowsWorking = false; + bool _themeInited = false; + + HWND ps_hWnd = nullptr; + HWND ps_tbHider_hWnd = nullptr; + HMENU ps_menu = nullptr; + HICON ps_iconBig = nullptr; + HICON ps_iconSmall = nullptr; + HICON ps_iconOverlay = nullptr; + + SingleTimer ps_cleanNotifyPhotosTimer; + + int _deltaLeft = 0; + int _deltaTop = 0; + +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(MainWindow::ShadowsChanges); + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp new file mode 100644 index 000000000..fcd9be3b8 --- /dev/null +++ b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp @@ -0,0 +1,331 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "platform/win/windows_app_user_model_id.h" + +#include "platform/win/windows_dlls.h" +#include +#include + +#include +#include +#include +#include + +using namespace Microsoft::WRL; + +namespace Platform { +namespace AppUserModelId { +namespace { + +const PROPERTYKEY pkey_AppUserModel_ID = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 5 }; +const PROPERTYKEY pkey_AppUserModel_StartPinOption = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 12 }; + +const WCHAR AppUserModelIdRelease[] = L"Telegram.TelegramDesktop"; +const WCHAR AppUserModelIdBeta[] = L"Telegram.TelegramDesktop.Beta"; + +} // namespace + +QString pinnedPath() { + static const int maxFileLen = MAX_PATH * 10; + WCHAR wstrPath[maxFileLen]; + if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) { + QDir appData(QString::fromStdWString(std::wstring(wstrPath))); + return appData.absolutePath() + qsl("/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar/"); + } + return QString(); +} + +void checkPinned() { + if (!Dlls::PropVariantToString) return; + + static const int maxFileLen = MAX_PATH * 10; + + HRESULT hr = CoInitialize(0); + if (!SUCCEEDED(hr)) return; + + QString path = pinnedPath(); + std::wstring p = QDir::toNativeSeparators(path).toStdWString(); + + WCHAR src[MAX_PATH]; + GetModuleFileName(GetModuleHandle(0), src, MAX_PATH); + BY_HANDLE_FILE_INFORMATION srcinfo = { 0 }; + HANDLE srcfile = CreateFile(src, 0x00, 0x00, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (srcfile == INVALID_HANDLE_VALUE) return; + BOOL srcres = GetFileInformationByHandle(srcfile, &srcinfo); + CloseHandle(srcfile); + if (!srcres) return; + LOG(("Checking...")); + WIN32_FIND_DATA findData; + HANDLE findHandle = FindFirstFileEx((p + L"*").c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, 0, 0); + if (findHandle == INVALID_HANDLE_VALUE) { + LOG(("Init Error: could not find files in pinned folder")); + return; + } + do { + std::wstring fname = p + findData.cFileName; + LOG(("Checking %1").arg(QString::fromStdWString(fname))); + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + continue; + } else { + DWORD attributes = GetFileAttributes(fname.c_str()); + if (attributes >= 0xFFFFFFF) continue; // file does not exist + + ComPtr shellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); + if (!SUCCEEDED(hr)) continue; + + ComPtr persistFile; + hr = shellLink.As(&persistFile); + if (!SUCCEEDED(hr)) continue; + + hr = persistFile->Load(fname.c_str(), STGM_READWRITE); + if (!SUCCEEDED(hr)) continue; + + WCHAR dst[MAX_PATH]; + hr = shellLink->GetPath(dst, MAX_PATH, 0, 0); + if (!SUCCEEDED(hr)) continue; + + BY_HANDLE_FILE_INFORMATION dstinfo = { 0 }; + HANDLE dstfile = CreateFile(dst, 0x00, 0x00, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (dstfile == INVALID_HANDLE_VALUE) continue; + BOOL dstres = GetFileInformationByHandle(dstfile, &dstinfo); + CloseHandle(dstfile); + if (!dstres) continue; + + if (srcinfo.dwVolumeSerialNumber == dstinfo.dwVolumeSerialNumber && srcinfo.nFileIndexLow == dstinfo.nFileIndexLow && srcinfo.nFileIndexHigh == dstinfo.nFileIndexHigh) { + ComPtr propertyStore; + hr = shellLink.As(&propertyStore); + if (!SUCCEEDED(hr)) return; + + PROPVARIANT appIdPropVar; + hr = propertyStore->GetValue(getKey(), &appIdPropVar); + if (!SUCCEEDED(hr)) return; + LOG(("Reading...")); + WCHAR already[MAX_PATH]; + hr = Dlls::PropVariantToString(appIdPropVar, already, MAX_PATH); + if (SUCCEEDED(hr)) { + if (std::wstring(getId()) == already) { + LOG(("Already!")); + PropVariantClear(&appIdPropVar); + return; + } + } + if (appIdPropVar.vt != VT_EMPTY) { + PropVariantClear(&appIdPropVar); + return; + } + PropVariantClear(&appIdPropVar); + + hr = InitPropVariantFromString(getId(), &appIdPropVar); + if (!SUCCEEDED(hr)) return; + + hr = propertyStore->SetValue(getKey(), appIdPropVar); + PropVariantClear(&appIdPropVar); + if (!SUCCEEDED(hr)) return; + + hr = propertyStore->Commit(); + if (!SUCCEEDED(hr)) return; + + if (persistFile->IsDirty() == S_OK) { + persistFile->Save(fname.c_str(), TRUE); + } + return; + } + } + } while (FindNextFile(findHandle, &findData)); + DWORD errorCode = GetLastError(); + if (errorCode && errorCode != ERROR_NO_MORE_FILES) { // everything is found + LOG(("Init Error: could not find some files in pinned folder")); + return; + } + FindClose(findHandle); +} + +QString systemShortcutPath() { + static const int maxFileLen = MAX_PATH * 10; + WCHAR wstrPath[maxFileLen]; + if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) { + QDir appData(QString::fromStdWString(std::wstring(wstrPath))); + return appData.absolutePath() + qsl("/Microsoft/Windows/Start Menu/Programs/"); + } + return QString(); +} + +void cleanupShortcut() { + static const int maxFileLen = MAX_PATH * 10; + + QString path = systemShortcutPath() + qsl("Telegram.lnk"); + std::wstring p = QDir::toNativeSeparators(path).toStdWString(); + + DWORD attributes = GetFileAttributes(p.c_str()); + if (attributes >= 0xFFFFFFF) return; // file does not exist + + ComPtr shellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); + if (!SUCCEEDED(hr)) return; + + ComPtr persistFile; + hr = shellLink.As(&persistFile); + if (!SUCCEEDED(hr)) return; + + hr = persistFile->Load(p.c_str(), STGM_READWRITE); + if (!SUCCEEDED(hr)) return; + + WCHAR szGotPath[MAX_PATH]; + WIN32_FIND_DATA wfd; + hr = shellLink->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATA*)&wfd, SLGP_SHORTPATH); + if (!SUCCEEDED(hr)) return; + + if (QDir::toNativeSeparators(cExeDir() + cExeName()).toStdWString() == szGotPath) { + QFile().remove(path); + } +} + +bool validateShortcutAt(const QString &path) { + static const int maxFileLen = MAX_PATH * 10; + + std::wstring p = QDir::toNativeSeparators(path).toStdWString(); + + DWORD attributes = GetFileAttributes(p.c_str()); + if (attributes >= 0xFFFFFFF) return false; // file does not exist + + ComPtr shellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); + if (!SUCCEEDED(hr)) return false; + + ComPtr persistFile; + hr = shellLink.As(&persistFile); + if (!SUCCEEDED(hr)) return false; + + hr = persistFile->Load(p.c_str(), STGM_READWRITE); + if (!SUCCEEDED(hr)) return false; + + ComPtr propertyStore; + hr = shellLink.As(&propertyStore); + if (!SUCCEEDED(hr)) return false; + + PROPVARIANT appIdPropVar; + hr = propertyStore->GetValue(getKey(), &appIdPropVar); + if (!SUCCEEDED(hr)) return false; + + WCHAR already[MAX_PATH]; + hr = Dlls::PropVariantToString(appIdPropVar, already, MAX_PATH); + if (SUCCEEDED(hr)) { + if (std::wstring(getId()) == already) { + PropVariantClear(&appIdPropVar); + return true; + } + } + if (appIdPropVar.vt != VT_EMPTY) { + PropVariantClear(&appIdPropVar); + return false; + } + PropVariantClear(&appIdPropVar); + + hr = InitPropVariantFromString(getId(), &appIdPropVar); + if (!SUCCEEDED(hr)) return false; + + hr = propertyStore->SetValue(getKey(), appIdPropVar); + PropVariantClear(&appIdPropVar); + if (!SUCCEEDED(hr)) return false; + + hr = propertyStore->Commit(); + if (!SUCCEEDED(hr)) return false; + + if (persistFile->IsDirty() == S_OK) { + persistFile->Save(p.c_str(), TRUE); + } + + return true; +} + +bool validateShortcut() { + QString path = systemShortcutPath(); + if (path.isEmpty()) return false; + + if (cBetaVersion()) { + path += qsl("TelegramBeta.lnk"); + if (validateShortcutAt(path)) return true; + } else { + if (validateShortcutAt(path + qsl("Telegram Desktop/Telegram.lnk"))) return true; + if (validateShortcutAt(path + qsl("Telegram Win (Unofficial)/Telegram.lnk"))) return true; + + path += qsl("Telegram.lnk"); + if (validateShortcutAt(path)) return true; + } + + ComPtr shellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); + if (!SUCCEEDED(hr)) return false; + + hr = shellLink->SetPath(QDir::toNativeSeparators(cExeDir() + cExeName()).toStdWString().c_str()); + if (!SUCCEEDED(hr)) return false; + + hr = shellLink->SetArguments(L""); + if (!SUCCEEDED(hr)) return false; + + hr = shellLink->SetWorkingDirectory(QDir::toNativeSeparators(QDir(cWorkingDir()).absolutePath()).toStdWString().c_str()); + if (!SUCCEEDED(hr)) return false; + + ComPtr propertyStore; + hr = shellLink.As(&propertyStore); + if (!SUCCEEDED(hr)) return false; + + PROPVARIANT appIdPropVar; + hr = InitPropVariantFromString(getId(), &appIdPropVar); + if (!SUCCEEDED(hr)) return false; + + hr = propertyStore->SetValue(getKey(), appIdPropVar); + PropVariantClear(&appIdPropVar); + if (!SUCCEEDED(hr)) return false; + + PROPVARIANT startPinPropVar; + hr = InitPropVariantFromUInt32(APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL, &startPinPropVar); + if (!SUCCEEDED(hr)) return false; + + hr = propertyStore->SetValue(pkey_AppUserModel_StartPinOption, startPinPropVar); + PropVariantClear(&startPinPropVar); + if (!SUCCEEDED(hr)) return false; + + hr = propertyStore->Commit(); + if (!SUCCEEDED(hr)) return false; + + ComPtr persistFile; + hr = shellLink.As(&persistFile); + if (!SUCCEEDED(hr)) return false; + + hr = persistFile->Save(QDir::toNativeSeparators(path).toStdWString().c_str(), TRUE); + if (!SUCCEEDED(hr)) return false; + + return true; +} + +const WCHAR *getId() { + return cBetaVersion() ? AppUserModelIdBeta : AppUserModelIdRelease; +} + +const PROPERTYKEY &getKey() { + return pkey_AppUserModel_ID; +} + +} // namespace AppUserModelId +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h new file mode 100644 index 000000000..02758014c --- /dev/null +++ b/Telegram/SourceFiles/platform/win/windows_app_user_model_id.h @@ -0,0 +1,37 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include + +namespace Platform { +namespace AppUserModelId { + +void cleanupShortcut(); +void checkPinned(); + +const WCHAR *getId(); +bool validateShortcut(); + +const PROPERTYKEY &getKey(); + +} // namespace AppUserModelId +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/windows_dlls.cpp b/Telegram/SourceFiles/platform/win/windows_dlls.cpp new file mode 100644 index 000000000..ae7bef04e --- /dev/null +++ b/Telegram/SourceFiles/platform/win/windows_dlls.cpp @@ -0,0 +1,76 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "platform/win/windows_dlls.h" + +namespace Platform { +namespace Dlls { + +f_SetWindowTheme SetWindowTheme; +f_OpenAs_RunDLL OpenAs_RunDLL; +f_SHOpenWithDialog SHOpenWithDialog; +f_SHAssocEnumHandlers SHAssocEnumHandlers; +f_SHCreateItemFromParsingName SHCreateItemFromParsingName; +f_WTSRegisterSessionNotification WTSRegisterSessionNotification; +f_WTSUnRegisterSessionNotification WTSUnRegisterSessionNotification; +f_SHQueryUserNotificationState SHQueryUserNotificationState; +f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; +f_RoGetActivationFactory RoGetActivationFactory; +f_WindowsCreateStringReference WindowsCreateStringReference; +f_WindowsDeleteString WindowsDeleteString; +f_PropVariantToString PropVariantToString; + +HINSTANCE LibUxTheme; +HINSTANCE LibShell32; +HINSTANCE LibWtsApi32; +HINSTANCE LibPropSys; +HINSTANCE LibComBase; +HINSTANCE LibWinRtString; + +void start() { + LibUxTheme = LoadLibrary(L"UXTHEME.DLL"); + load(LibUxTheme, "SetWindowTheme", SetWindowTheme); + + LibShell32 = LoadLibrary(L"SHELL32.DLL"); + load(LibShell32, "SHAssocEnumHandlers", SHAssocEnumHandlers); + load(LibShell32, "SHCreateItemFromParsingName", SHCreateItemFromParsingName); + load(LibShell32, "SHOpenWithDialog", SHOpenWithDialog); + load(LibShell32, "OpenAs_RunDLLW", OpenAs_RunDLL); + load(LibShell32, "SHQueryUserNotificationState", SHQueryUserNotificationState); + load(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); + + LibWtsApi32 = LoadLibrary(L"WTSAPI32.DLL"); + load(LibWtsApi32, "WTSRegisterSessionNotification", WTSRegisterSessionNotification); + load(LibWtsApi32, "WTSUnRegisterSessionNotification", WTSUnRegisterSessionNotification); + + LibPropSys = LoadLibrary(L"PROPSYS.DLL"); + load(LibPropSys, "PropVariantToString", PropVariantToString); + + LibComBase = LoadLibrary(L"COMBASE.DLL"); + load(LibComBase, "RoGetActivationFactory", RoGetActivationFactory); + + LibWinRtString = LoadLibrary(L"api-ms-win-core-winrt-string-l1-1-0.dll"); + load(LibWinRtString, "WindowsCreateStringReference", WindowsCreateStringReference); + load(LibWinRtString, "WindowsDeleteString", WindowsDeleteString); +} + +} // namespace Dlls +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/windows_dlls.h b/Telegram/SourceFiles/platform/win/windows_dlls.h new file mode 100644 index 000000000..2a998a37e --- /dev/null +++ b/Telegram/SourceFiles/platform/win/windows_dlls.h @@ -0,0 +1,90 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include +#include +#include + +namespace Platform { +namespace Dlls { + +void start(); + +template +bool load(HINSTANCE library, LPCSTR name, Function &func) { + if (!library) return false; + + func = reinterpret_cast(GetProcAddress(library, name)); + return (func != nullptr); +} + +// UXTHEME.DLL +typedef HRESULT (FAR STDAPICALLTYPE *f_SetWindowTheme)(HWND hWnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList); +extern f_SetWindowTheme SetWindowTheme; + +// SHELL32.DLL +typedef HRESULT (FAR STDAPICALLTYPE *f_SHAssocEnumHandlers)(PCWSTR pszExtra, ASSOC_FILTER afFilter, IEnumAssocHandlers **ppEnumHandler); +extern f_SHAssocEnumHandlers SHAssocEnumHandlers; + +typedef HRESULT (FAR STDAPICALLTYPE *f_SHCreateItemFromParsingName)(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv); +extern f_SHCreateItemFromParsingName SHCreateItemFromParsingName; + +typedef HRESULT (FAR STDAPICALLTYPE *f_SHOpenWithDialog)(HWND hwndParent, const OPENASINFO *poainfo); +extern f_SHOpenWithDialog SHOpenWithDialog; + +typedef HRESULT (FAR STDAPICALLTYPE *f_OpenAs_RunDLL)(HWND hWnd, HINSTANCE hInstance, LPCWSTR lpszCmdLine, int nCmdShow); +extern f_OpenAs_RunDLL OpenAs_RunDLL; + +typedef HRESULT (FAR STDAPICALLTYPE *f_SHQueryUserNotificationState)(QUERY_USER_NOTIFICATION_STATE *pquns); +extern f_SHQueryUserNotificationState SHQueryUserNotificationState; + +typedef HRESULT (FAR STDAPICALLTYPE *f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); +extern f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; + +// WTSAPI32.DLL + +typedef BOOL (FAR STDAPICALLTYPE *f_WTSRegisterSessionNotification)(HWND hWnd, DWORD dwFlags); +extern f_WTSRegisterSessionNotification WTSRegisterSessionNotification; + +typedef BOOL (FAR STDAPICALLTYPE *f_WTSUnRegisterSessionNotification)(HWND hWnd); +extern f_WTSUnRegisterSessionNotification WTSUnRegisterSessionNotification; + +// PROPSYS.DLL + +typedef HRESULT (FAR STDAPICALLTYPE *f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); +extern f_PropVariantToString PropVariantToString; + +// COMBASE.DLL + +typedef HRESULT (FAR STDAPICALLTYPE *f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void ** factory); +extern f_RoGetActivationFactory RoGetActivationFactory; + +// api-ms-win-core-winrt-string-l1-1-0.dll + +typedef HRESULT (FAR STDAPICALLTYPE *f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER * hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string); +extern f_WindowsCreateStringReference WindowsCreateStringReference; + +typedef HRESULT (FAR STDAPICALLTYPE *f_WindowsDeleteString)(_In_opt_ HSTRING string); +extern f_WindowsDeleteString WindowsDeleteString; + +} // namespace Dlls +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/windows_event_filter.cpp b/Telegram/SourceFiles/platform/win/windows_event_filter.cpp new file mode 100644 index 000000000..f782a9eb9 --- /dev/null +++ b/Telegram/SourceFiles/platform/win/windows_event_filter.cpp @@ -0,0 +1,260 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "platform/win/windows_event_filter.h" + +#include "mainwindow.h" + +namespace Platform { +namespace { + +EventFilter *instance = nullptr; + +int menuShown = 0, menuHidden = 0; + +} // namespace + +EventFilter *EventFilter::createInstance() { + destroy(); + instance = new EventFilter(); + return getInstance(); +} + +EventFilter *EventFilter::getInstance() { + return instance; +} + +void EventFilter::destroy() { + delete instance; + instance = nullptr; +} + +bool EventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result) { + auto wnd = App::wnd(); + if (!wnd) return false; + + MSG *msg = (MSG*)message; + if (msg->message == WM_ENDSESSION) { + App::quit(); + return false; + } + if (msg->hwnd == wnd->psHwnd() || msg->hwnd && !wnd->psHwnd()) { + return mainWindowEvent(msg->hwnd, msg->message, msg->wParam, msg->lParam, (LRESULT*)result); + } + return false; +} + +bool EventFilter::mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *result) { + using ShadowsChange = MainWindow::ShadowsChange; + + if (auto tbCreatedMsgId = Platform::MainWindow::TaskbarCreatedMsgId()) { + if (msg == tbCreatedMsgId) { + Platform::MainWindow::TaskbarCreated(); + } + } + + switch (msg) { + + case WM_TIMECHANGE: { + App::wnd()->checkAutoLockIn(100); + } return false; + + case WM_WTSSESSION_CHANGE: { + if (wParam == WTS_SESSION_LOGOFF || wParam == WTS_SESSION_LOCK) { + setSessionLoggedOff(true); + } else if (wParam == WTS_SESSION_LOGON || wParam == WTS_SESSION_UNLOCK) { + setSessionLoggedOff(false); + } + } return false; + + case WM_DESTROY: { + App::quit(); + } return false; + + case WM_ACTIVATE: { + if (LOWORD(wParam) == WA_CLICKACTIVE) { + App::wnd()->inactivePress(true); + } + if (LOWORD(wParam) != WA_INACTIVE) { + App::wnd()->shadowsActivate(); + } else { + App::wnd()->shadowsDeactivate(); + } + if (Global::started()) { + App::wnd()->update(); + } + } return false; + + case WM_NCPAINT: if (QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8) return false; *result = 0; return true; + + case WM_NCCALCSIZE: { + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + if (GetWindowPlacement(hWnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) { + LPNCCALCSIZE_PARAMS params = (LPNCCALCSIZE_PARAMS)lParam; + LPRECT r = (wParam == TRUE) ? ¶ms->rgrc[0] : (LPRECT)lParam; + HMONITOR hMonitor = MonitorFromPoint({ (r->left + r->right) / 2, (r->top + r->bottom) / 2 }, MONITOR_DEFAULTTONEAREST); + if (hMonitor) { + MONITORINFO mi; + mi.cbSize = sizeof(mi); + if (GetMonitorInfo(hMonitor, &mi)) { + *r = mi.rcWork; + } + } + } + *result = 0; + return true; + } + + case WM_NCACTIVATE: { + *result = DefWindowProc(hWnd, msg, wParam, -1); + } return true; + + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: { + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + if (GetWindowPlacement(hWnd, &wp) && (wp.showCmd == SW_SHOWMAXIMIZED || wp.showCmd == SW_SHOWMINIMIZED)) { + App::wnd()->shadowsUpdate(ShadowsChange::Hidden); + } else { + App::wnd()->shadowsUpdate(ShadowsChange::Moved | ShadowsChange::Resized, (WINDOWPOS*)lParam); + } + } return false; + + case WM_SIZE: { + if (App::wnd()) { + if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED || wParam == SIZE_MINIMIZED) { + if (wParam != SIZE_RESTORED || App::wnd()->windowState() != Qt::WindowNoState) { + Qt::WindowState state = Qt::WindowNoState; + if (wParam == SIZE_MAXIMIZED) { + state = Qt::WindowMaximized; + } else if (wParam == SIZE_MINIMIZED) { + state = Qt::WindowMinimized; + } + emit App::wnd()->windowHandle()->windowStateChanged(state); + } else { + App::wnd()->psUpdatedPosition(); + } + App::wnd()->psUpdateMargins(); + MainWindow::ShadowsChanges changes = (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXIMIZED) ? ShadowsChange::Hidden : (ShadowsChange::Resized | ShadowsChange::Shown); + App::wnd()->shadowsUpdate(changes); + } + } + } return false; + + case WM_SHOWWINDOW: { + LONG style = GetWindowLong(hWnd, GWL_STYLE); + auto changes = ShadowsChange::Resized | ((wParam && !(style & (WS_MAXIMIZE | WS_MINIMIZE))) ? ShadowsChange::Shown : ShadowsChange::Hidden); + App::wnd()->shadowsUpdate(changes); + } return false; + + case WM_MOVE: { + App::wnd()->shadowsUpdate(ShadowsChange::Moved); + App::wnd()->psUpdatedPosition(); + } return false; + + case WM_NCHITTEST: { + POINTS p = MAKEPOINTS(lParam); + RECT r; + GetWindowRect(hWnd, &r); + HitTestType res = App::wnd()->hitTest(QPoint(p.x - r.left + App::wnd()->deltaLeft(), p.y - r.top + App::wnd()->deltaTop())); + switch (res) { + case HitTestClient: + case HitTestSysButton: *result = HTCLIENT; break; + case HitTestIcon: *result = HTCAPTION; break; + case HitTestCaption: *result = HTCAPTION; break; + case HitTestTop: *result = HTTOP; break; + case HitTestTopRight: *result = HTTOPRIGHT; break; + case HitTestRight: *result = HTRIGHT; break; + case HitTestBottomRight: *result = HTBOTTOMRIGHT; break; + case HitTestBottom: *result = HTBOTTOM; break; + case HitTestBottomLeft: *result = HTBOTTOMLEFT; break; + case HitTestLeft: *result = HTLEFT; break; + case HitTestTopLeft: *result = HTTOPLEFT; break; + case HitTestNone: + default: *result = HTTRANSPARENT; break; + }; + } return true; + + case WM_NCRBUTTONUP: { + SendMessage(hWnd, WM_SYSCOMMAND, SC_MOUSEMENU, lParam); + } return true; + + case WM_NCLBUTTONDOWN: { + POINTS p = MAKEPOINTS(lParam); + RECT r; + GetWindowRect(hWnd, &r); + HitTestType res = App::wnd()->hitTest(QPoint(p.x - r.left + App::wnd()->deltaLeft(), p.y - r.top + App::wnd()->deltaTop())); + switch (res) { + case HitTestIcon: + if (menuHidden && getms() < menuHidden + 10) { + menuHidden = 0; + if (getms() < menuShown + GetDoubleClickTime()) { + App::wnd()->close(); + } + } else { + QRect icon = App::wnd()->iconRect(); + p.x = r.left - App::wnd()->deltaLeft() + icon.left(); + p.y = r.top - App::wnd()->deltaTop() + icon.top() + icon.height(); + App::wnd()->psUpdateSysMenu(App::wnd()->windowHandle()->windowState()); + menuShown = getms(); + menuHidden = 0; + TrackPopupMenu(App::wnd()->psMenu(), TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, p.x, p.y, 0, hWnd, 0); + menuHidden = getms(); + } + return true; + }; + } return false; + + case WM_NCLBUTTONDBLCLK: { + POINTS p = MAKEPOINTS(lParam); + RECT r; + GetWindowRect(hWnd, &r); + HitTestType res = App::wnd()->hitTest(QPoint(p.x - r.left + App::wnd()->deltaLeft(), p.y - r.top + App::wnd()->deltaTop())); + switch (res) { + case HitTestIcon: App::wnd()->close(); return true; + }; + } return false; + + case WM_SYSCOMMAND: { + if (wParam == SC_MOUSEMENU) { + POINTS p = MAKEPOINTS(lParam); + App::wnd()->psUpdateSysMenu(App::wnd()->windowHandle()->windowState()); + TrackPopupMenu(App::wnd()->psMenu(), TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, p.x, p.y, 0, hWnd, 0); + } + } return false; + + case WM_COMMAND: { + if (HIWORD(wParam)) return false; + int cmd = LOWORD(wParam); + switch (cmd) { + case SC_CLOSE: App::wnd()->close(); return true; + case SC_MINIMIZE: App::wnd()->setWindowState(Qt::WindowMinimized); return true; + case SC_MAXIMIZE: App::wnd()->setWindowState(Qt::WindowMaximized); return true; + case SC_RESTORE: App::wnd()->setWindowState(Qt::WindowNoState); return true; + } + } return true; + + } + return false; +} + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/windows_event_filter.h b/Telegram/SourceFiles/platform/win/windows_event_filter.h new file mode 100644 index 000000000..d6ebd2b40 --- /dev/null +++ b/Telegram/SourceFiles/platform/win/windows_event_filter.h @@ -0,0 +1,51 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include + +namespace Platform { + +class EventFilter : public QAbstractNativeEventFilter { +public: + bool nativeEventFilter(const QByteArray &eventType, void *message, long *result); + bool mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *result); + + bool sessionLoggedOff() const { + return _sessionLoggedOff; + } + void setSessionLoggedOff(bool loggedOff) { + _sessionLoggedOff = loggedOff; + } + + static EventFilter *createInstance(); + static EventFilter *getInstance(); + static void destroy(); + +private: + EventFilter() { + } + + bool _sessionLoggedOff = false; + +}; + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/windows_toasts.cpp b/Telegram/SourceFiles/platform/win/windows_toasts.cpp new file mode 100644 index 000000000..17aa620cf --- /dev/null +++ b/Telegram/SourceFiles/platform/win/windows_toasts.cpp @@ -0,0 +1,554 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "platform/win/windows_toasts.h" + +#include "platform/win/windows_app_user_model_id.h" +#include "platform/win/windows_dlls.h" +#include "mainwindow.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +HICON qt_pixmapToWinHICON(const QPixmap &); + +using namespace Microsoft::WRL; +using namespace ABI::Windows::UI::Notifications; +using namespace ABI::Windows::Data::Xml::Dom; +using namespace Windows::Foundation; + +namespace Platform { +namespace Toasts { +namespace { + +bool _supported = false; + +ComPtr _notificationManager; +ComPtr _notifier; +ComPtr _notificationFactory; + +struct NotificationPtr { + NotificationPtr() { + } + NotificationPtr(const ComPtr &ptr) : p(ptr) { + } + ComPtr p; +}; +using Notifications = QMap>; +Notifications _notifications; +struct Image { + uint64 until; + QString path; +}; +using Images = QMap; +Images _images; +bool _imageSavedFlag = false; + +class StringReferenceWrapper { +public: + StringReferenceWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() { + HRESULT hr = Dlls::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); + if (!SUCCEEDED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); + } + } + + ~StringReferenceWrapper() { + Dlls::WindowsDeleteString(_hstring); + } + + template + StringReferenceWrapper(_In_reads_(N) wchar_t const (&stringRef)[N]) throw() { + UINT32 length = N - 1; + HRESULT hr = Dlls::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); + if (!SUCCEEDED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); + } + } + + template + StringReferenceWrapper(_In_reads_(_) wchar_t(&stringRef)[_]) throw() { + UINT32 length; + HRESULT hr = SizeTToUInt32(wcslen(stringRef), &length); + if (!SUCCEEDED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); + } + + Dlls::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); + } + + HSTRING Get() const throw() { + return _hstring; + } + +private: + HSTRING _hstring; + HSTRING_HEADER _header; + +}; + +template +_Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { + return Dlls::RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); +} + +template +inline HRESULT wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) throw() { + return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); +} + +bool init() { + if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS8) { + return false; + } + if ((Dlls::SetCurrentProcessExplicitAppUserModelID == nullptr) + || (Dlls::PropVariantToString == nullptr) + || (Dlls::RoGetActivationFactory == nullptr) + || (Dlls::WindowsCreateStringReference == nullptr) + || (Dlls::WindowsDeleteString == nullptr)) { + return false; + } + + if (!AppUserModelId::validateShortcut()) { + return false; + } + + auto appUserModelId = AppUserModelId::getId(); + if (!SUCCEEDED(Dlls::SetCurrentProcessExplicitAppUserModelID(appUserModelId))) { + return false; + } + if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &_notificationManager))) { + return false; + } + if (!SUCCEEDED(_notificationManager->CreateToastNotifierWithId(StringReferenceWrapper(appUserModelId, wcslen(appUserModelId)).Get(), &_notifier))) { + return false; + } + if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &_notificationFactory))) { + return false; + } + QDir().mkpath(cWorkingDir() + qsl("tdata/temp")); + return true; +} + +} // namespace + +void start() { + _supported = init(); +} + +bool supported() { + return _supported; +} + +uint64 clearImages(uint64 ms) { + uint64 result = 0; + for (auto i = _images.begin(); i != _images.end();) { + if (!i->until) { + ++i; + continue; + } + if (i->until <= ms) { + QFile(i->path).remove(); + i = _images.erase(i); + } else { + if (!result) { + result = i->until; + } else { + accumulate_min(result, i->until); + } + ++i; + } + } + return result; +} + +void clearNotifies(PeerId peerId) { + if (!_notifier) return; + + if (peerId) { + auto i = _notifications.find(peerId); + if (i != _notifications.cend()) { + auto temp = createAndSwap(i.value()); + _notifications.erase(i); + + for (auto j = temp.cbegin(), e = temp.cend(); j != e; ++j) { + _notifier->Hide(j->p.Get()); + } + } + } else { + auto temp = createAndSwap(_notifications); + for_const (auto ¬ifications, temp) { + for_const (auto ¬ification, notifications) { + _notifier->Hide(notification.p.Get()); + } + } + } +} + +void finish() { + _notifications.clear(); + if (_notificationManager) _notificationManager.Reset(); + if (_notifier) _notifier.Reset(); + if (_notificationFactory) _notificationFactory.Reset(); + + if (_imageSavedFlag) { + psDeleteDir(cWorkingDir() + qsl("tdata/temp")); + } +} + +HRESULT SetNodeValueString(_In_ HSTRING inputString, _In_ IXmlNode *node, _In_ IXmlDocument *xml) { + ComPtr inputText; + + HRESULT hr = xml->CreateTextNode(inputString, &inputText); + if (!SUCCEEDED(hr)) return hr; + ComPtr inputTextNode; + + hr = inputText.As(&inputTextNode); + if (!SUCCEEDED(hr)) return hr; + + ComPtr pAppendedChild; + return node->AppendChild(inputTextNode.Get(), &pAppendedChild); +} + +HRESULT SetAudioSilent(_In_ IXmlDocument *toastXml) { + ComPtr nodeList; + HRESULT hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"audio").Get(), &nodeList); + if (!SUCCEEDED(hr)) return hr; + + ComPtr audioNode; + hr = nodeList->Item(0, &audioNode); + if (!SUCCEEDED(hr)) return hr; + + if (audioNode) { + ComPtr audioElement; + hr = audioNode.As(&audioElement); + if (!SUCCEEDED(hr)) return hr; + + hr = audioElement->SetAttribute(StringReferenceWrapper(L"silent").Get(), StringReferenceWrapper(L"true").Get()); + if (!SUCCEEDED(hr)) return hr; + } else { + ComPtr audioElement; + hr = toastXml->CreateElement(StringReferenceWrapper(L"audio").Get(), &audioElement); + if (!SUCCEEDED(hr)) return hr; + + hr = audioElement->SetAttribute(StringReferenceWrapper(L"silent").Get(), StringReferenceWrapper(L"true").Get()); + if (!SUCCEEDED(hr)) return hr; + + ComPtr audioNode; + hr = audioElement.As(&audioNode); + if (!SUCCEEDED(hr)) return hr; + + ComPtr nodeList; + hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"toast").Get(), &nodeList); + if (!SUCCEEDED(hr)) return hr; + + ComPtr toastNode; + hr = nodeList->Item(0, &toastNode); + if (!SUCCEEDED(hr)) return hr; + + ComPtr appendedNode; + hr = toastNode->AppendChild(audioNode.Get(), &appendedNode); + } + return hr; +} + +HRESULT SetImageSrc(_In_z_ const wchar_t *imagePath, _In_ IXmlDocument *toastXml) { + wchar_t imageSrc[MAX_PATH] = L"file:///"; + HRESULT hr = StringCchCat(imageSrc, ARRAYSIZE(imageSrc), imagePath); + if (!SUCCEEDED(hr)) return hr; + + ComPtr nodeList; + hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"image").Get(), &nodeList); + if (!SUCCEEDED(hr)) return hr; + + ComPtr imageNode; + hr = nodeList->Item(0, &imageNode); + if (!SUCCEEDED(hr)) return hr; + + ComPtr attributes; + hr = imageNode->get_Attributes(&attributes); + if (!SUCCEEDED(hr)) return hr; + + ComPtr srcAttribute; + hr = attributes->GetNamedItem(StringReferenceWrapper(L"src").Get(), &srcAttribute); + if (!SUCCEEDED(hr)) return hr; + + return SetNodeValueString(StringReferenceWrapper(imageSrc).Get(), srcAttribute.Get(), toastXml); +} + +typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastActivatedEventHandler; +typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastDismissedEventHandler; +typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastFailedEventHandler; + +class ToastEventHandler : public Implements { +public: + + ToastEventHandler::ToastEventHandler(const PeerId &peer, MsgId msg) : _ref(1), _peerId(peer), _msgId(msg) { + } + ~ToastEventHandler() { + } + + // DesktopToastActivatedEventHandler + IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IInspectable* args) { + auto i = _notifications.find(_peerId); + if (i != _notifications.cend()) { + i.value().remove(_msgId); + if (i.value().isEmpty()) { + _notifications.erase(i); + } + } + if (App::wnd()) { + History *history = App::history(_peerId); + + App::wnd()->showFromTray(); + if (App::passcoded()) { + App::wnd()->setInnerFocus(); + App::wnd()->notifyClear(); + } else { + App::wnd()->hideSettings(); + bool tomsg = !history->peer->isUser() && (_msgId > 0); + if (tomsg) { + HistoryItem *item = App::histItemById(peerToChannel(_peerId), _msgId); + if (!item || !item->mentionsMe()) { + tomsg = false; + } + } + Ui::showPeerHistory(history, tomsg ? _msgId : ShowAtUnreadMsgId); + App::wnd()->notifyClear(history); + } + SetForegroundWindow(App::wnd()->psHwnd()); + } + return S_OK; + } + + // DesktopToastDismissedEventHandler + IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IToastDismissedEventArgs *e) { + ToastDismissalReason tdr; + if (SUCCEEDED(e->get_Reason(&tdr))) { + switch (tdr) { + case ToastDismissalReason_ApplicationHidden: + break; + case ToastDismissalReason_UserCanceled: + case ToastDismissalReason_TimedOut: + default: + auto i = _notifications.find(_peerId); + if (i != _notifications.cend()) { + i.value().remove(_msgId); + if (i.value().isEmpty()) { + _notifications.erase(i); + } + } + break; + } + } + return S_OK; + } + + // DesktopToastFailedEventHandler + IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IToastFailedEventArgs *e) { + auto i = _notifications.find(_peerId); + if (i != _notifications.cend()) { + i.value().remove(_msgId); + if (i.value().isEmpty()) { + _notifications.erase(i); + } + } + return S_OK; + } + + // IUnknown + IFACEMETHODIMP_(ULONG) AddRef() { + return InterlockedIncrement(&_ref); + } + + IFACEMETHODIMP_(ULONG) Release() { + ULONG l = InterlockedDecrement(&_ref); + if (l == 0) delete this; + return l; + } + + IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_ void **ppv) { + if (IsEqualIID(riid, IID_IUnknown)) + *ppv = static_cast(static_cast(this)); + else if (IsEqualIID(riid, __uuidof(DesktopToastActivatedEventHandler))) + *ppv = static_cast(this); + else if (IsEqualIID(riid, __uuidof(DesktopToastDismissedEventHandler))) + *ppv = static_cast(this); + else if (IsEqualIID(riid, __uuidof(DesktopToastFailedEventHandler))) + *ppv = static_cast(this); + else *ppv = nullptr; + + if (*ppv) { + reinterpret_cast(*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + +private: + + ULONG _ref; + PeerId _peerId; + MsgId _msgId; +}; + +QString getImage(const StorageKey &key, PeerData *peer) { + uint64 ms = getms(true); + auto i = _images.find(key); + if (i != _images.cend()) { + if (i->until) { + i->until = ms + NotifyDeletePhotoAfter; + if (App::wnd()) App::wnd()->psCleanNotifyPhotosIn(-NotifyDeletePhotoAfter); + } + } else { + Image v; + if (key.first) { + v.until = ms + NotifyDeletePhotoAfter; + if (App::wnd()) App::wnd()->psCleanNotifyPhotosIn(-NotifyDeletePhotoAfter); + } else { + v.until = 0; + } + v.path = cWorkingDir() + qsl("tdata/temp/") + QString::number(rand_value(), 16) + qsl(".png"); + if (key.first || key.second) { + peer->saveUserpic(v.path); + } else { + App::wnd()->iconLarge().save(v.path, "PNG"); + } + i = _images.insert(key, v); + _imageSavedFlag = true; + } + return i->path; +} + +bool create(PeerData *peer, int32 msgId, bool showpix, const QString &title, const QString &subtitle, const QString &msg) { + if (!supported() || !_notificationManager || !_notifier || !_notificationFactory) return false; + + ComPtr toastXml; + bool withSubtitle = !subtitle.isEmpty(); + + HRESULT hr = _notificationManager->GetTemplateContent(withSubtitle ? ToastTemplateType_ToastImageAndText04 : ToastTemplateType_ToastImageAndText02, &toastXml); + if (!SUCCEEDED(hr)) return false; + + hr = SetAudioSilent(toastXml.Get()); + if (!SUCCEEDED(hr)) return false; + + StorageKey key; + QString imagePath; + if (showpix) { + key = peer->userpicUniqueKey(); + } else { + key = StorageKey(0, 0); + } + QString image = getImage(key, peer); + std::wstring wimage = QDir::toNativeSeparators(image).toStdWString(); + + hr = SetImageSrc(wimage.c_str(), toastXml.Get()); + if (!SUCCEEDED(hr)) return false; + + ComPtr nodeList; + hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"text").Get(), &nodeList); + if (!SUCCEEDED(hr)) return false; + + UINT32 nodeListLength; + hr = nodeList->get_Length(&nodeListLength); + if (!SUCCEEDED(hr)) return false; + + if (nodeListLength < (withSubtitle ? 3U : 2U)) return false; + + { + ComPtr textNode; + hr = nodeList->Item(0, &textNode); + if (!SUCCEEDED(hr)) return false; + + std::wstring wtitle = title.toStdWString(); + hr = SetNodeValueString(StringReferenceWrapper(wtitle.data(), wtitle.size()).Get(), textNode.Get(), toastXml.Get()); + if (!SUCCEEDED(hr)) return false; + } + if (withSubtitle) { + ComPtr textNode; + hr = nodeList->Item(1, &textNode); + if (!SUCCEEDED(hr)) return false; + + std::wstring wsubtitle = subtitle.toStdWString(); + hr = SetNodeValueString(StringReferenceWrapper(wsubtitle.data(), wsubtitle.size()).Get(), textNode.Get(), toastXml.Get()); + if (!SUCCEEDED(hr)) return false; + } + { + ComPtr textNode; + hr = nodeList->Item(withSubtitle ? 2 : 1, &textNode); + if (!SUCCEEDED(hr)) return false; + + std::wstring wmsg = msg.toStdWString(); + hr = SetNodeValueString(StringReferenceWrapper(wmsg.data(), wmsg.size()).Get(), textNode.Get(), toastXml.Get()); + if (!SUCCEEDED(hr)) return false; + } + + ComPtr toast; + hr = _notificationFactory->CreateToastNotification(toastXml.Get(), &toast); + if (!SUCCEEDED(hr)) return false; + + EventRegistrationToken activatedToken, dismissedToken, failedToken; + ComPtr eventHandler(new ToastEventHandler(peer->id, msgId)); + + hr = toast->add_Activated(eventHandler.Get(), &activatedToken); + if (!SUCCEEDED(hr)) return false; + + hr = toast->add_Dismissed(eventHandler.Get(), &dismissedToken); + if (!SUCCEEDED(hr)) return false; + + hr = toast->add_Failed(eventHandler.Get(), &failedToken); + if (!SUCCEEDED(hr)) return false; + + auto i = _notifications.find(peer->id); + if (i != _notifications.cend()) { + auto j = i->find(msgId); + if (j != i->cend()) { + ComPtr notify = j->p; + i->erase(j); + _notifier->Hide(notify.Get()); + i = _notifications.find(peer->id); + } + } + if (i == _notifications.cend()) { + i = _notifications.insert(peer->id, QMap()); + } + hr = _notifier->Show(toast.Get()); + if (!SUCCEEDED(hr)) { + i = _notifications.find(peer->id); + if (i != _notifications.cend() && i->isEmpty()) _notifications.erase(i); + return false; + } + _notifications[peer->id].insert(msgId, toast); + + return true; +} + +} // namespace Toasts +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/windows_toasts.h b/Telegram/SourceFiles/platform/win/windows_toasts.h new file mode 100644 index 000000000..b271abc4c --- /dev/null +++ b/Telegram/SourceFiles/platform/win/windows_toasts.h @@ -0,0 +1,38 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +namespace Platform { +namespace Toasts { + +void start(); +void finish(); + +bool supported(); +bool create(PeerData *peer, int32 msgId, bool showpix, const QString &title, const QString &subtitle, const QString &msg); + +// Returns the next ms when clearImages() should be called. +uint64 clearImages(uint64 ms); + +void clearNotifies(PeerId peerId); + +} // namespace Toasts +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/winrt/main_window_winrt.cpp b/Telegram/SourceFiles/platform/winrt/main_window_winrt.cpp new file mode 100644 index 000000000..8933702c5 --- /dev/null +++ b/Telegram/SourceFiles/platform/winrt/main_window_winrt.cpp @@ -0,0 +1,29 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "platform/win/main_window_winrt.h" + +namespace Platform { +namespace { + +} // namespace + +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/winrt/main_window_winrt.h b/Telegram/SourceFiles/platform/winrt/main_window_winrt.h new file mode 100644 index 000000000..4902e7dfd --- /dev/null +++ b/Telegram/SourceFiles/platform/winrt/main_window_winrt.h @@ -0,0 +1,31 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "window/main_window.h" + +namespace Platform { + +class MainWindow : public Window::MainWindow { + +}; + +} // namespace Platform diff --git a/Telegram/SourceFiles/pspecific.h b/Telegram/SourceFiles/pspecific.h index f0128a173..cc0345c09 100644 --- a/Telegram/SourceFiles/pspecific.h +++ b/Telegram/SourceFiles/pspecific.h @@ -26,26 +26,23 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #ifdef Q_OS_MAC #include "pspecific_mac.h" -#endif // Q_OS_MAC - -#ifdef Q_OS_LINUX +#elif defined Q_OS_LINUX // Q_OS_MAC #include "pspecific_linux.h" -#endif // Q_OS_LINUX - -#ifdef Q_OS_WINRT +#elif defined Q_OS_WINRT // Q_OS_MAC || Q_OS_LINUX #include "pspecific_winrt.h" -#elif defined Q_OS_WIN // Q_OS_WINRT +#elif defined Q_OS_WIN // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT #include "pspecific_win.h" -#endif // Q_OS_WIN* +#endif // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT || Q_OS_WIN -namespace PlatformSpecific { +namespace Platform { - void start(); - void finish(); +void start(); +void finish(); - namespace ThirdParty { - void start(); - void finish(); - } +namespace ThirdParty { -} +void start(); +void finish(); + +} // namespace ThirdParty +} // namespace Platform diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp index 4fb50519f..bd7ce62ce 100644 --- a/Telegram/SourceFiles/pspecific_linux.cpp +++ b/Telegram/SourceFiles/pspecific_linux.cpp @@ -499,412 +499,6 @@ namespace { UnityLauncherEntry *_psUnityLauncherEntry = 0; }; -PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent), -posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/icon256.png")), iconbig256(icon256), wndIcon(QIcon::fromTheme("telegram", QIcon(QPixmap::fromImage(icon256, Qt::ColorOnly)))), _psCheckStatusIconLeft(100), _psLastIndicatorUpdate(0) { - connect(&_psCheckStatusIconTimer, SIGNAL(timeout()), this, SLOT(psStatusIconCheck())); - _psCheckStatusIconTimer.setSingleShot(false); - - connect(&_psUpdateIndicatorTimer, SIGNAL(timeout()), this, SLOT(psUpdateIndicator())); - _psUpdateIndicatorTimer.setSingleShot(true); -} - -bool PsMainWindow::psHasTrayIcon() const { - return trayIcon || ((useAppIndicator || (useStatusIcon && trayIconChecked)) && (cWorkMode() != dbiwmWindowOnly)); -} - -void PsMainWindow::psStatusIconCheck() { - _trayIconCheck(0); - if (cSupportTray() || !--_psCheckStatusIconLeft) { - _psCheckStatusIconTimer.stop(); - return; - } -} - -void PsMainWindow::psShowTrayMenu() { -} - -void PsMainWindow::psRefreshTaskbarIcon() { -} - -void PsMainWindow::psTrayMenuUpdated() { - if (noQtTrayIcon && (useAppIndicator || useStatusIcon)) { - const QList &actions = trayIconMenu->actions(); - if (_trayItems.isEmpty()) { - DEBUG_LOG(("Creating tray menu!")); - for (int32 i = 0, l = actions.size(); i != l; ++i) { - GtkWidget *item = ps_gtk_menu_item_new_with_label(actions.at(i)->text().toUtf8()); - ps_gtk_menu_shell_append(PS_GTK_MENU_SHELL(_trayMenu), item); - ps_g_signal_connect(item, "activate", G_CALLBACK(_trayMenuCallback), this); - ps_gtk_widget_show(item); - ps_gtk_widget_set_sensitive(item, actions.at(i)->isEnabled()); - - _trayItems.push_back(qMakePair(item, actions.at(i))); - } - } else { - DEBUG_LOG(("Updating tray menu!")); - for (int32 i = 0, l = actions.size(); i != l; ++i) { - if (i < _trayItems.size()) { - ps_gtk_menu_item_set_label(reinterpret_cast(_trayItems.at(i).first), actions.at(i)->text().toUtf8()); - ps_gtk_widget_set_sensitive(_trayItems.at(i).first, actions.at(i)->isEnabled()); - } - } - } - } -} - -void PsMainWindow::psSetupTrayIcon() { - if (noQtTrayIcon) { - if (!cSupportTray()) return; - psUpdateCounter(); - } else { - if (!trayIcon) { - trayIcon = new QSystemTrayIcon(this); - - QIcon icon(QPixmap::fromImage(App::wnd()->iconLarge(), Qt::ColorOnly)); - - trayIcon->setIcon(icon); - trayIcon->setToolTip(str_const_toString(AppName)); - connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(toggleTray(QSystemTrayIcon::ActivationReason)), Qt::UniqueConnection); - connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showFromTray())); - App::wnd()->updateTrayMenu(); - } - psUpdateCounter(); - - trayIcon->show(); - psUpdateDelegate(); - } -} - -void PsMainWindow::psUpdateWorkmode() { - if (!cSupportTray()) return; - - if (cWorkMode() == dbiwmWindowOnly) { - if (noQtTrayIcon) { - if (useAppIndicator) { - ps_app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_PASSIVE); - } else if (useStatusIcon) { - ps_gtk_status_icon_set_visible(_trayIcon, false); - } - } else { - if (trayIcon) { - trayIcon->setContextMenu(0); - trayIcon->deleteLater(); - } - trayIcon = 0; - } - } else { - if (noQtTrayIcon) { - if (useAppIndicator) { - ps_app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_ACTIVE); - } else if (useStatusIcon) { - ps_gtk_status_icon_set_visible(_trayIcon, true); - } - } else { - psSetupTrayIcon(); - } - } -} - -void PsMainWindow::psUpdateIndicator() { - _psUpdateIndicatorTimer.stop(); - _psLastIndicatorUpdate = getms(); - QFileInfo f(_trayIconImageFile()); - if (f.exists()) { - QByteArray path = QFile::encodeName(f.absoluteFilePath()), name = QFile::encodeName(f.fileName()); - name = name.mid(0, name.size() - 4); - ps_app_indicator_set_icon_full(_trayIndicator, path.constData(), name); - } else { - useAppIndicator = false; - } -} - -void PsMainWindow::psUpdateCounter() { - setWindowIcon(wndIcon); - - int32 counter = App::histories().unreadBadge(); - - setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); - if (_psUnityLauncherEntry) { - if (counter > 0) { - ps_unity_launcher_entry_set_count(_psUnityLauncherEntry, (counter > 9999) ? 9999 : counter); - ps_unity_launcher_entry_set_count_visible(_psUnityLauncherEntry, TRUE); - } else { - ps_unity_launcher_entry_set_count_visible(_psUnityLauncherEntry, FALSE); - } - } - - if (noQtTrayIcon) { - if (useAppIndicator) { - if (getms() > _psLastIndicatorUpdate + 1000) { - psUpdateIndicator(); - } else if (!_psUpdateIndicatorTimer.isActive()) { - _psUpdateIndicatorTimer.start(100); - } - } else if (useStatusIcon && trayIconChecked) { - loadPixbuf(_trayIconImageGen()); - ps_gtk_status_icon_set_from_pixbuf(_trayIcon, _trayPixbuf); - } - } else if (trayIcon) { - int32 counter = App::histories().unreadBadge(); - bool muted = App::histories().unreadOnlyMuted(); - - style::color bg = muted ? st::counterMuteBG : st::counterBG; - QIcon iconSmall; - iconSmall.addPixmap(QPixmap::fromImage(iconWithCounter(16, counter, bg, true), Qt::ColorOnly)); - iconSmall.addPixmap(QPixmap::fromImage(iconWithCounter(32, counter, bg, true), Qt::ColorOnly)); - trayIcon->setIcon(iconSmall); - } -} - -void PsMainWindow::psUpdateDelegate() { -} - -void PsMainWindow::psInitSize() { - setMinimumWidth(st::wndMinWidth); - setMinimumHeight(st::wndMinHeight); - - TWindowPos pos(cWindowPos()); - QRect avail(QDesktopWidget().availableGeometry()); - bool maximized = false; - QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight); - if (pos.w && pos.h) { - QList screens = Application::screens(); - for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { - QByteArray name = (*i)->name().toUtf8(); - if (pos.moncrc == hashCrc32(name.constData(), name.size())) { - QRect screen((*i)->geometry()); - int32 w = screen.width(), h = screen.height(); - if (w >= st::wndMinWidth && h >= st::wndMinHeight) { - if (pos.w > w) pos.w = w; - if (pos.h > h) pos.h = h; - pos.x += screen.x(); - pos.y += screen.y(); - if (pos.x < screen.x() + screen.width() - 10 && pos.y < screen.y() + screen.height() - 10) { - geom = QRect(pos.x, pos.y, pos.w, pos.h); - } - } - break; - } - } - - if (pos.y < 0) pos.y = 0; - maximized = pos.maximized; - } - setGeometry(geom); -} - -void PsMainWindow::psInitFrameless() { - psUpdatedPositionTimer.setSingleShot(true); - connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); - - if (frameless) { - //setWindowFlags(Qt::FramelessWindowHint); - } -} - -void PsMainWindow::psSavePosition(Qt::WindowState state) { - if (state == Qt::WindowActive) state = windowHandle()->windowState(); - if (state == Qt::WindowMinimized || !posInited) return; - - TWindowPos pos(cWindowPos()), curPos = pos; - - if (state == Qt::WindowMaximized) { - curPos.maximized = 1; - } else { - QRect r(geometry()); - curPos.x = r.x(); - curPos.y = r.y(); - curPos.w = r.width(); - curPos.h = r.height(); - curPos.maximized = 0; - } - - int px = curPos.x + curPos.w / 2, py = curPos.y + curPos.h / 2, d = 0; - QScreen *chosen = 0; - QList screens = Application::screens(); - for (QList::const_iterator i = screens.cbegin(), e = screens.cend(); i != e; ++i) { - int dx = (*i)->geometry().x() + (*i)->geometry().width() / 2 - px; if (dx < 0) dx = -dx; - int dy = (*i)->geometry().y() + (*i)->geometry().height() / 2 - py; if (dy < 0) dy = -dy; - if (!chosen || dx + dy < d) { - d = dx + dy; - chosen = *i; - } - } - if (chosen) { - curPos.x -= chosen->geometry().x(); - curPos.y -= chosen->geometry().y(); - QByteArray name = chosen->name().toUtf8(); - curPos.moncrc = hashCrc32(name.constData(), name.size()); - } - - if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { - if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { - cSetWindowPos(curPos); - Local::writeSettings(); - } - } -} - -void PsMainWindow::psUpdatedPosition() { - psUpdatedPositionTimer.start(SaveWindowPositionTimeout); -} - -void PsMainWindow::psCreateTrayIcon() { - if (!noQtTrayIcon) { - cSetSupportTray(QSystemTrayIcon::isSystemTrayAvailable()); - return; - } - - if (useAppIndicator) { - DEBUG_LOG(("Trying to create AppIndicator")); - _trayMenu = ps_gtk_menu_new(); - if (_trayMenu) { - DEBUG_LOG(("Created gtk menu for appindicator!")); - QFileInfo f(_trayIconImageFile()); - if (f.exists()) { - QByteArray path = QFile::encodeName(f.absoluteFilePath()); - _trayIndicator = ps_app_indicator_new("Telegram Desktop", path.constData(), APP_INDICATOR_CATEGORY_APPLICATION_STATUS); - if (_trayIndicator) { - DEBUG_LOG(("Created appindicator!")); - } else { - DEBUG_LOG(("Failed to app_indicator_new()!")); - } - } else { - useAppIndicator = false; - DEBUG_LOG(("Failed to create image file!")); - } - } else { - DEBUG_LOG(("Failed to gtk_menu_new()!")); - } - if (_trayMenu && _trayIndicator) { - ps_app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_ACTIVE); - ps_app_indicator_set_menu(_trayIndicator, PS_GTK_MENU(_trayMenu)); - useStatusIcon = false; - } else { - DEBUG_LOG(("AppIndicator failed!")); - useAppIndicator = false; - } - } - if (useStatusIcon) { - if (ps_gdk_init_check(0, 0)) { - if (!_trayMenu) _trayMenu = ps_gtk_menu_new(); - if (_trayMenu) { - loadPixbuf(_trayIconImageGen()); - _trayIcon = ps_gtk_status_icon_new_from_pixbuf(_trayPixbuf); - if (_trayIcon) { - ps_g_signal_connect(_trayIcon, "popup-menu", GCallback(_trayIconPopup), _trayMenu); - ps_g_signal_connect(_trayIcon, "activate", GCallback(_trayIconActivate), _trayMenu); - ps_g_signal_connect(_trayIcon, "size-changed", GCallback(_trayIconResized), _trayMenu); - - ps_gtk_status_icon_set_title(_trayIcon, "Telegram Desktop"); - ps_gtk_status_icon_set_tooltip_text(_trayIcon, "Telegram Desktop"); - ps_gtk_status_icon_set_visible(_trayIcon, true); - } else { - useStatusIcon = false; - } - } else { - useStatusIcon = false; - } - } else { - useStatusIcon = false; - } - } - if (!useStatusIcon && !useAppIndicator) { - if (_trayMenu) { - ps_g_object_ref_sink(_trayMenu); - ps_g_object_unref(_trayMenu); - _trayMenu = 0; - } - } - cSetSupportTray(useAppIndicator); - if (useStatusIcon) { - ps_g_idle_add((GSourceFunc)_trayIconCheck, 0); - _psCheckStatusIconTimer.start(100); - } else { - psUpdateWorkmode(); - } -} - -void PsMainWindow::psFirstShow() { - psCreateTrayIcon(); - - if (useUnityCount) { - _psUnityLauncherEntry = ps_unity_launcher_entry_get_for_desktop_id("telegramdesktop.desktop"); - if (_psUnityLauncherEntry) { - LOG(("Found Unity Launcher entry telegramdesktop.desktop!")); - } else { - _psUnityLauncherEntry = ps_unity_launcher_entry_get_for_desktop_id("Telegram.desktop"); - if (_psUnityLauncherEntry) { - LOG(("Found Unity Launcher entry Telegram.desktop!")); - } else { - LOG(("Could not get Unity Launcher entry!")); - } - } - } else { - LOG(("Not using Unity Launcher count.")); - } - - finished = false; - - psUpdateMargins(); - - bool showShadows = true; - - show(); - //_private.enableShadow(winId()); - if (cWindowPos().maximized) { - setWindowState(Qt::WindowMaximized); - } - - if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) { - setWindowState(Qt::WindowMinimized); - if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { - hide(); - } else { - show(); - } - showShadows = false; - } else { - show(); - } - - posInited = true; -} - -bool PsMainWindow::psHandleTitle() { - return false; -} - -void PsMainWindow::psInitSysMenu() { -} - -void PsMainWindow::psUpdateSysMenu(Qt::WindowState state) { -} - -void PsMainWindow::psUpdateMargins() { -} - -void PsMainWindow::psFlash() { - //_private.startBounce(); -} - -PsMainWindow::~PsMainWindow() { - if (_trayIcon) { - ps_g_object_unref(_trayIcon); - _trayIcon = 0; - } - if (_trayPixbuf) { - ps_g_object_unref(_trayPixbuf); - _trayPixbuf = 0; - } - if (_trayMenu) { - ps_g_object_ref_sink(_trayMenu); - ps_g_object_unref(_trayMenu); - _trayMenu = 0; - } - finished = true; -} - namespace { QRect _monitorRect; uint64 _monitorLastGot = 0; @@ -1235,36 +829,38 @@ void psShowInFolder(const QString &name) { system(("xdg-open " + escapeShell(QFile::encodeName(QFileInfo(name).absoluteDir().absolutePath()))).constData()); } -namespace PlatformSpecific { - - void start() { - } - - void finish() { - delete _psEventFilter; - _psEventFilter = 0; - } - - namespace ThirdParty { - void start() { - QString cdesktop = QString(getenv("XDG_CURRENT_DESKTOP")).toLower(); - noQtTrayIcon = (cdesktop == qstr("pantheon")) || (cdesktop == qstr("gnome")); - tryAppIndicator = (cdesktop == qstr("xfce")); - noTryUnity = (cdesktop != qstr("unity")); - - if (noQtTrayIcon) cSetSupportTray(false); - - DEBUG_LOG(("Loading libraries")); - setupGtk(); - setupUnity(); - } - - void finish() { - } - } +namespace Platform { +void start() { } +void finish() { + delete _psEventFilter; + _psEventFilter = nullptr; +} + +namespace ThirdParty { + +void start() { + QString cdesktop = QString(getenv("XDG_CURRENT_DESKTOP")).toLower(); + noQtTrayIcon = (cdesktop == qstr("pantheon")) || (cdesktop == qstr("gnome")); + tryAppIndicator = (cdesktop == qstr("xfce")); + noTryUnity = (cdesktop != qstr("unity")); + + if (noQtTrayIcon) cSetSupportTray(false); + + DEBUG_LOG(("Loading libraries")); + setupGtk(); + setupUnity(); +} + +void finish() { +} + +} // namespace ThirdParty + +} // namespace Platform + namespace { bool _psRunCommand(const QByteArray &command) { int result = system(command.constData()); diff --git a/Telegram/SourceFiles/pspecific_linux.h b/Telegram/SourceFiles/pspecific_linux.h index fa4c5a3d9..9e13d9ab9 100644 --- a/Telegram/SourceFiles/pspecific_linux.h +++ b/Telegram/SourceFiles/pspecific_linux.h @@ -30,89 +30,6 @@ inline void psCheckLocalSocket(const QString &serverName) { } } -class NotifyWindow; - -class PsMainWindow : public QMainWindow { - Q_OBJECT - -public: - PsMainWindow(QWidget *parent = 0); - - int32 psResizeRowWidth() const { - return 0;//st::wndResizeAreaWidth; - } - - void psInitFrameless(); - void psInitSize(); - - void psFirstShow(); - void psInitSysMenu(); - void psUpdateSysMenu(Qt::WindowState state); - void psUpdateMargins(); - void psUpdatedPosition(); - - bool psHandleTitle(); - - void psFlash(); - void psNotifySettingGot(); - - void psUpdateWorkmode(); - - void psRefreshTaskbarIcon(); - - bool psPosInited() const { - return posInited; - } - - void psActivateNotify(NotifyWindow *w); - void psClearNotifies(PeerId peerId = 0); - void psNotifyShown(NotifyWindow *w); - void psPlatformNotify(HistoryItem *item, int32 fwdCount); - - void psUpdateCounter(); - - bool psHasNativeNotifications() { - return false; - } - - virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; - - ~PsMainWindow(); - -public slots: - - void psUpdateDelegate(); - void psSavePosition(Qt::WindowState state = Qt::WindowActive); - void psShowTrayMenu(); - - void psStatusIconCheck(); - void psUpdateIndicator(); - -protected: - - bool psHasTrayIcon() const; - - bool posInited; - QSystemTrayIcon *trayIcon; - QMenu *trayIconMenu; - QImage icon256, iconbig256; - QIcon wndIcon; - - void psTrayMenuUpdated(); - void psSetupTrayIcon(); - - QTimer psUpdatedPositionTimer; - -private: - void psCreateTrayIcon(); - - QTimer _psCheckStatusIconTimer; - int _psCheckStatusIconLeft; - - QTimer _psUpdateIndicatorTimer; - uint64 _psLastIndicatorUpdate; -}; - void psWriteDump(); QString psPrepareCrashDump(const QByteArray &crashdump, QString dumpfile); diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp index a58b4715b..59a3ff7f1 100644 --- a/Telegram/SourceFiles/pspecific_mac.cpp +++ b/Telegram/SourceFiles/pspecific_mac.cpp @@ -854,29 +854,30 @@ void psShowInFolder(const QString &name) { objc_showInFinder(name, QFileInfo(name).absolutePath()); } -namespace PlatformSpecific { - - void start() { - objc_start(); - } - - void finish() { - delete _psEventFilter; - _psEventFilter = 0; - - objc_finish(); - } - - namespace ThirdParty { - void start() { - } - - void finish() { - } - } +namespace Platform { +void start() { + objc_start(); } +void finish() { + delete _psEventFilter; + _psEventFilter = nullptr; + + objc_finish(); +} + +namespace ThirdParty { + +void start() { +} + +void finish() { +} + +} // namespace ThirdParty +} // namespace Platform + void psNewVersion() { objc_registerCustomScheme(); } diff --git a/Telegram/SourceFiles/pspecific_mac.h b/Telegram/SourceFiles/pspecific_mac.h index cfeafabc8..700d115b9 100644 --- a/Telegram/SourceFiles/pspecific_mac.h +++ b/Telegram/SourceFiles/pspecific_mac.h @@ -29,117 +29,6 @@ inline void psCheckLocalSocket(const QString &serverName) { } } -class MacPrivate : public PsMacWindowPrivate { -public: - - void activeSpaceChanged(); - void darkModeChanged(); - void notifyClicked(unsigned long long peer, int msgid); - void notifyReplied(unsigned long long peer, int msgid, const char *str); - -}; - -class NotifyWindow; - -class PsMainWindow : public QMainWindow { - Q_OBJECT - -public: - PsMainWindow(QWidget *parent = 0); - - int32 psResizeRowWidth() const { - return 0;//st::wndResizeAreaWidth; - } - - void psInitFrameless(); - void psInitSize(); - - void psFirstShow(); - void psInitSysMenu(); - void psUpdateSysMenu(Qt::WindowState state); - void psUpdateMargins(); - void psUpdatedPosition(); - - bool psHandleTitle(); - - void psFlash(); - - void psUpdateWorkmode(); - - void psRefreshTaskbarIcon(); - - bool psPosInited() const { - return posInited; - } - - bool psFilterNativeEvent(void *event); - - void psActivateNotify(NotifyWindow *w); - void psClearNotifies(PeerId peerId = 0); - void psNotifyShown(NotifyWindow *w); - void psPlatformNotify(HistoryItem *item, int32 fwdCount); - - bool eventFilter(QObject *obj, QEvent *evt); - - void psUpdateCounter(); - - bool psHasNativeNotifications() { - return !(QSysInfo::macVersion() < QSysInfo::MV_10_8); - } - - virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; - - ~PsMainWindow(); - -public slots: - - void psUpdateDelegate(); - void psSavePosition(Qt::WindowState state = Qt::WindowActive); - void psShowTrayMenu(); - - void psMacUndo(); - void psMacRedo(); - void psMacCut(); - void psMacCopy(); - void psMacPaste(); - void psMacDelete(); - void psMacSelectAll(); - -protected: - - void psNotIdle() const; - QImage psTrayIcon(bool selected = false) const; - bool psHasTrayIcon() const { - return trayIcon; - } - - void psMacUpdateMenu(); - - bool posInited; - QSystemTrayIcon *trayIcon; - QMenu *trayIconMenu; - QImage icon256, iconbig256; - QIcon wndIcon; - - QImage trayImg, trayImgSel; - - void psTrayMenuUpdated(); - void psSetupTrayIcon(); - virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0; - - QTimer psUpdatedPositionTimer; - -private: - MacPrivate _private; - - mutable bool psIdle; - mutable QTimer psIdleTimer; - - QMenuBar psMainMenu; - QAction *psLogout, *psUndo, *psRedo, *psCut, *psCopy, *psPaste, *psDelete, *psSelectAll, *psContacts, *psAddContact, *psNewGroup, *psNewChannel, *psShowTelegram; - -}; - void psWriteDump(); QString psPrepareCrashDump(const QByteArray &crashdump, QString dumpfile); diff --git a/Telegram/SourceFiles/pspecific_win.cpp b/Telegram/SourceFiles/pspecific_win.cpp index 6718db848..e75983310 100644 --- a/Telegram/SourceFiles/pspecific_win.cpp +++ b/Telegram/SourceFiles/pspecific_win.cpp @@ -21,6 +21,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "pspecific.h" +#include "platform/win/main_window_win.h" +#include "platform/win/windows_toasts.h" +#include "platform/win/windows_app_user_model_id.h" +#include "platform/win/windows_dlls.h" +#include "platform/win/windows_event_filter.h" #include "lang.h" #include "application.h" #include "mainwidget.h" @@ -61,11 +66,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include -#define min(a, b) ((a) < (b) ? (a) : (b)) -#define max(a, b) ((a) < (b) ? (b) : (a)) - -#include - #ifndef DCX_USESTYLE #define DCX_USESTYLE 0x00010000 #endif @@ -76,1492 +76,39 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #define WM_NCPOINTERUP 0x0243 #endif -const WCHAR AppUserModelIdRelease[] = L"Telegram.TelegramDesktop"; -const WCHAR AppUserModelIdBeta[] = L"Telegram.TelegramDesktop.Beta"; - -const WCHAR *AppUserModelId() { - return cBetaVersion() ? AppUserModelIdBeta : AppUserModelIdRelease; -} - -static const PROPERTYKEY pkey_AppUserModel_ID = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 5 }; -static const PROPERTYKEY pkey_AppUserModel_StartPinOption = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 12 }; - using namespace Microsoft::WRL; using namespace ABI::Windows::UI::Notifications; using namespace ABI::Windows::Data::Xml::Dom; using namespace Windows::Foundation; +using namespace Platform; namespace { QStringList _initLogs; - bool frameless = true; - bool useTheme = false; bool useOpenWith = false; bool useOpenAs = false; - bool useWtsapi = false; bool useShellapi = false; - bool useToast = false; bool themeInited = false; bool finished = true; - int menuShown = 0, menuHidden = 0; - int dleft = 0, dtop = 0; QMargins simpleMargins, margins; HICON bigIcon = 0, smallIcon = 0, overlayIcon = 0; - bool sessionLoggedOff = false; - - UINT tbCreatedMsgId = 0; - - ComPtr taskbarList; - - ComPtr toastNotificationManager; - ComPtr toastNotifier; - ComPtr toastNotificationFactory; - struct ToastNotificationPtr { - ToastNotificationPtr() { - } - ToastNotificationPtr(const ComPtr &ptr) : p(ptr) { - } - ComPtr p; - }; - typedef QMap > ToastNotifications; - ToastNotifications toastNotifications; - struct ToastImage { - uint64 until; - QString path; - }; - typedef QMap ToastImages; - ToastImages toastImages; - bool ToastImageSavedFlag = false; - - HWND createTaskbarHider() { - HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0); - HWND hWnd = 0; - - QString cn = QString("TelegramTaskbarHider"); - LPCWSTR _cn = (LPCWSTR)cn.utf16(); - WNDCLASSEX wc; - - wc.cbSize = sizeof(wc); - wc.style = 0; - wc.lpfnWndProc = DefWindowProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = appinst; - wc.hIcon = 0; - wc.hCursor = 0; - wc.hbrBackground = 0; - wc.lpszMenuName = NULL; - wc.lpszClassName = _cn; - wc.hIconSm = 0; - if (!RegisterClassEx(&wc)) { - DEBUG_LOG(("Application Error: could not register taskbar hider window class, error: %1").arg(GetLastError())); - return hWnd; - } - - hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, _cn, 0, WS_POPUP, 0, 0, 0, 0, 0, 0, appinst, 0); - if (!hWnd) { - DEBUG_LOG(("Application Error: could not create taskbar hider window class, error: %1").arg(GetLastError())); - return hWnd; - } - return hWnd; - } - - enum { - _PsShadowMoved = 0x01, - _PsShadowResized = 0x02, - _PsShadowShown = 0x04, - _PsShadowHidden = 0x08, - _PsShadowActivate = 0x10, - }; - - enum { - _PsInitHor = 0x01, - _PsInitVer = 0x02, - }; - - int32 _psSize = 0; - class _PsShadowWindows { - public: - - _PsShadowWindows() : screenDC(0), max_w(0), max_h(0), _x(0), _y(0), _w(0), _h(0), hidden(true), r(0), g(0), b(0), noKeyColor(RGB(255, 255, 255)) { - for (int i = 0; i < 4; ++i) { - dcs[i] = 0; - bitmaps[i] = 0; - hwnds[i] = 0; - } - } - - void setColor(QColor c) { - r = c.red(); - g = c.green(); - b = c.blue(); - - if (!hwnds[0]) return; - Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); - for (int i = 0; i < 4; ++i) { - Gdiplus::Graphics graphics(dcs[i]); - graphics.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); - if ((i % 2) && _h || !(i % 2) && _w) { - graphics.FillRectangle(&brush, 0, 0, (i % 2) ? _size : _w, (i % 2) ? _h : _size); - } - } - initCorners(); - - _x = _y = _w = _h = 0; - update(_PsShadowMoved | _PsShadowResized); - } - - bool init(QColor c) { - _fullsize = st::wndShadow.rect().width(); - _shift = st::wndShadowShift; - QImage cornersImage(_fullsize, _fullsize, QImage::Format_ARGB32_Premultiplied); - { - Painter p(&cornersImage); - p.drawSprite(0, 0, st::wndShadow); - } - if (rtl()) cornersImage = cornersImage.mirrored(true, false); - uchar *bits = cornersImage.bits(); - if (bits) { - for ( - quint32 *p = (quint32*)bits, *end = (quint32*)(bits + cornersImage.byteCount()); - p < end; - ++p - ) { - *p = (*p ^ 0x00ffffff) << 24; - } - } - - _metaSize = _fullsize + 2 * _shift; - _alphas.reserve(_metaSize); - _colors.reserve(_metaSize * _metaSize); - for (int32 j = 0; j < _metaSize; ++j) { - for (int32 i = 0; i < _metaSize; ++i) { - _colors.push_back((i < 2 * _shift || j < 2 * _shift) ? 1 : qMax(BYTE(1), BYTE(cornersImage.pixel(QPoint(i - 2 * _shift, j - 2 * _shift)) >> 24))); - } - } - uchar prev = 0; - for (int32 i = 0; i < _metaSize; ++i) { - uchar a = _colors[(_metaSize - 1) * _metaSize + i]; - if (a < prev) break; - - _alphas.push_back(a); - prev = a; - } - _psSize = _size = _alphas.size() - 2 * _shift; - - setColor(c); - - Gdiplus::GdiplusStartupInput gdiplusStartupInput; - ULONG_PTR gdiplusToken; - Gdiplus::Status gdiRes = Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); - - if (gdiRes != Gdiplus::Ok) { - LOG(("Application Error: could not init GDI+, error: %1").arg((int)gdiRes)); - return false; - } - blend.AlphaFormat = AC_SRC_ALPHA; - blend.SourceConstantAlpha = 255; - blend.BlendFlags = 0; - blend.BlendOp = AC_SRC_OVER; - - screenDC = GetDC(0); - if (!screenDC) { - LOG(("Application Error: could not GetDC(0), error: %2").arg(GetLastError())); - return false; - } - - QRect avail(Sandbox::availableGeometry()); - max_w = avail.width(); - if (max_w < st::wndMinWidth) max_w = st::wndMinWidth; - max_h = avail.height(); - if (max_h < st::wndMinHeight) max_h = st::wndMinHeight; - - HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0); - HWND hwnd = App::wnd() ? App::wnd()->psHwnd() : 0; - - for (int i = 0; i < 4; ++i) { - QString cn = QString("TelegramShadow%1").arg(i); - LPCWSTR _cn = (LPCWSTR)cn.utf16(); - WNDCLASSEX wc; - - wc.cbSize = sizeof(wc); - wc.style = 0; - wc.lpfnWndProc = wndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = appinst; - wc.hIcon = 0; - wc.hCursor = 0; - wc.hbrBackground = 0; - wc.lpszMenuName = NULL; - wc.lpszClassName = _cn; - wc.hIconSm = 0; - if (!RegisterClassEx(&wc)) { - LOG(("Application Error: could not register shadow window class %1, error: %2").arg(i).arg(GetLastError())); - destroy(); - return false; - } - - hwnds[i] = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOOLWINDOW, _cn, 0, WS_POPUP, 0, 0, 0, 0, 0, 0, appinst, 0); - if (!hwnds[i]) { - LOG(("Application Error: could not create shadow window class %1, error: %2").arg(i).arg(GetLastError())); - destroy(); - return false; - } - SetWindowLong(hwnds[i], GWL_HWNDPARENT, (LONG)hwnd); - - dcs[i] = CreateCompatibleDC(screenDC); - if (!dcs[i]) { - LOG(("Application Error: could not create dc for shadow window class %1, error: %2").arg(i).arg(GetLastError())); - destroy(); - return false; - } - - bitmaps[i] = CreateCompatibleBitmap(screenDC, (i % 2) ? _size : max_w, (i % 2) ? max_h : _size); - if (!bitmaps[i]) { - LOG(("Application Error: could not create bitmap for shadow window class %1, error: %2").arg(i).arg(GetLastError())); - destroy(); - return false; - } - - SelectObject(dcs[i], bitmaps[i]); - } - - initCorners(); - return true; - } - - void initCorners(int directions = (_PsInitHor | _PsInitVer)) { - bool hor = (directions & _PsInitHor), ver = (directions & _PsInitVer); - Gdiplus::Graphics graphics0(dcs[0]), graphics1(dcs[1]), graphics2(dcs[2]), graphics3(dcs[3]); - graphics0.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); - graphics1.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); - graphics2.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); - graphics3.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); - - Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); - if (hor) graphics0.FillRectangle(&brush, 0, 0, _fullsize - (_size - _shift), 2 * _shift); - - if (ver) { - graphics1.FillRectangle(&brush, 0, 0, _size, 2 * _shift); - graphics3.FillRectangle(&brush, 0, 0, _size, 2 * _shift); - graphics1.FillRectangle(&brush, _size - _shift, 2 * _shift, _shift, _fullsize); - graphics3.FillRectangle(&brush, 0, 2 * _shift, _shift, _fullsize); - } - - if (hor) { - for (int j = 2 * _shift; j < _size; ++j) { - for (int k = 0; k < _fullsize - (_size - _shift); ++k) { - brush.SetColor(Gdiplus::Color(_colors[j * _metaSize + k + (_size + _shift)], r, g, b)); - graphics0.FillRectangle(&brush, k, j, 1, 1); - graphics2.FillRectangle(&brush, k, _size - (j - 2 * _shift) - 1, 1, 1); - } - } - for (int j = _size; j < _size + 2 * _shift; ++j) { - for (int k = 0; k < _fullsize - (_size - _shift); ++k) { - brush.SetColor(Gdiplus::Color(_colors[j * _metaSize + k + (_size + _shift)], r, g, b)); - graphics2.FillRectangle(&brush, k, _size - (j - 2 * _shift) - 1, 1, 1); - } - } - } - if (ver) { - for (int j = 2 * _shift; j < _fullsize + 2 * _shift; ++j) { - for (int k = _shift; k < _size; ++k) { - brush.SetColor(Gdiplus::Color(_colors[j * _metaSize + (k + _shift)], r, g, b)); - graphics1.FillRectangle(&brush, _size - k - 1, j, 1, 1); - graphics3.FillRectangle(&brush, k, j, 1, 1); - } - } - } - } - void verCorners(int h, Gdiplus::Graphics *pgraphics1, Gdiplus::Graphics *pgraphics3) { - Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); - pgraphics1->FillRectangle(&brush, _size - _shift, h - _fullsize, _shift, _fullsize); - pgraphics3->FillRectangle(&brush, 0, h - _fullsize, _shift, _fullsize); - for (int j = 0; j < _fullsize; ++j) { - for (int k = _shift; k < _size; ++k) { - brush.SetColor(Gdiplus::Color(_colors[(j + 2 * _shift) * _metaSize + k + _shift], r, g, b)); - pgraphics1->FillRectangle(&brush, _size - k - 1, h - j - 1, 1, 1); - pgraphics3->FillRectangle(&brush, k, h - j - 1, 1, 1); - } - } - } - void horCorners(int w, Gdiplus::Graphics *pgraphics0, Gdiplus::Graphics *pgraphics2) { - Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); - pgraphics0->FillRectangle(&brush, w - 2 * _size - (_fullsize - (_size - _shift)), 0, _fullsize - (_size - _shift), 2 * _shift); - for (int j = 2 * _shift; j < _size; ++j) { - for (int k = 0; k < _fullsize - (_size - _shift); ++k) { - brush.SetColor(Gdiplus::Color(_colors[j * _metaSize + k + (_size + _shift)], r, g, b)); - pgraphics0->FillRectangle(&brush, w - 2 * _size - k - 1, j, 1, 1); - pgraphics2->FillRectangle(&brush, w - 2 * _size - k - 1, _size - (j - 2 * _shift) - 1, 1, 1); - } - } - for (int j = _size; j < _size + 2 * _shift; ++j) { - for (int k = 0; k < _fullsize - (_size - _shift); ++k) { - brush.SetColor(Gdiplus::Color(_colors[j * _metaSize + k + (_size + _shift)], r, g, b)); - pgraphics2->FillRectangle(&brush, w - 2 * _size - k - 1, _size - (j - 2 * _shift) - 1, 1, 1); - } - } - } - - void update(int changes, WINDOWPOS *pos = 0) { - HWND hwnd = App::wnd() ? App::wnd()->psHwnd() : 0; - if (!hwnd || !hwnds[0]) return; - - if (changes == _PsShadowActivate) { - for (int i = 0; i < 4; ++i) { - SetWindowPos(hwnds[i], hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - } - return; - } - - if (changes & _PsShadowHidden) { - if (!hidden) { - for (int i = 0; i < 4; ++i) { - hidden = true; - ShowWindow(hwnds[i], SW_HIDE); - } - } - return; - } - if (!App::wnd()->psPosInited()) return; - - int x = _x, y = _y, w = _w, h = _h; - if (pos && (!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE) || !(pos->flags & SWP_NOREPOSITION))) { - if (!(pos->flags & SWP_NOMOVE)) { - x = pos->x - _size; - y = pos->y - _size; - } else if (pos->flags & SWP_NOSIZE) { - for (int i = 0; i < 4; ++i) { - SetWindowPos(hwnds[i], hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - } - return; - } - if (!(pos->flags & SWP_NOSIZE)) { - w = pos->cx + 2 * _size; - h = pos->cy + 2 * _size; - } - } else { - RECT r; - GetWindowRect(hwnd, &r); - x = r.left - _size; - y = r.top - _size; - w = r.right + _size - x; - h = r.bottom + _size - y; - } - if (h < 2 * _fullsize + 2 * _shift) { - h = 2 * _fullsize + 2 * _shift; - } - if (w < 2 * (_fullsize + _shift)) { - w = 2 * (_fullsize + _shift); - } - - if (w != _w) { - int from = (_w > 2 * (_fullsize + _shift)) ? (_w - _size - _fullsize - _shift) : (_fullsize - (_size - _shift)); - int to = w - _size - _fullsize - _shift; - if (w > max_w) { - from = _fullsize - (_size - _shift); - max_w *= 2; - for (int i = 0; i < 4; i += 2) { - DeleteObject(bitmaps[i]); - bitmaps[i] = CreateCompatibleBitmap(screenDC, max_w, _size); - SelectObject(dcs[i], bitmaps[i]); - } - initCorners(_PsInitHor); - } - Gdiplus::Graphics graphics0(dcs[0]), graphics2(dcs[2]); - graphics0.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); - graphics2.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); - Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); - if (to > from) { - graphics0.FillRectangle(&brush, from, 0, to - from, 2 * _shift); - for (int i = 2 * _shift; i < _size; ++i) { - Gdiplus::Pen pen(Gdiplus::Color(_alphas[i], r, g, b)); - graphics0.DrawLine(&pen, from, i, to, i); - graphics2.DrawLine(&pen, from, _size - (i - 2 * _shift) - 1, to, _size - (i - 2 * _shift) - 1); - } - for (int i = _size; i < _size + 2 * _shift; ++i) { - Gdiplus::Pen pen(Gdiplus::Color(_alphas[i], r, g, b)); - graphics2.DrawLine(&pen, from, _size - (i - 2 * _shift) - 1, to, _size - (i - 2 * _shift) - 1); - } - } - if (_w > w) { - graphics0.FillRectangle(&brush, w - _size - _fullsize - _shift, 0, _fullsize - (_size - _shift), _size); - graphics2.FillRectangle(&brush, w - _size - _fullsize - _shift, 0, _fullsize - (_size - _shift), _size); - } - horCorners(w, &graphics0, &graphics2); - POINT p0 = { x + _size, y }, p2 = { x + _size, y + h - _size }, f = { 0, 0 }; - SIZE s = { w - 2 * _size, _size }; - updateWindow(0, &p0, &s); - updateWindow(2, &p2, &s); - } else if (x != _x || y != _y) { - POINT p0 = { x + _size, y }, p2 = { x + _size, y + h - _size }; - updateWindow(0, &p0); - updateWindow(2, &p2); - } else if (h != _h) { - POINT p2 = { x + _size, y + h - _size }; - updateWindow(2, &p2); - } - - if (h != _h) { - int from = (_h > 2 * _fullsize + 2 * _shift) ? (_h - _fullsize) : (_fullsize + 2 * _shift); - int to = h - _fullsize; - if (h > max_h) { - from = (_fullsize + 2 * _shift); - max_h *= 2; - for (int i = 1; i < 4; i += 2) { - DeleteObject(bitmaps[i]); - bitmaps[i] = CreateCompatibleBitmap(dcs[i], _size, max_h); - SelectObject(dcs[i], bitmaps[i]); - } - initCorners(_PsInitVer); - } - Gdiplus::Graphics graphics1(dcs[1]), graphics3(dcs[3]); - graphics1.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); - graphics3.SetCompositingMode(Gdiplus::CompositingModeSourceCopy); - - Gdiplus::SolidBrush brush(Gdiplus::Color(_alphas[0], r, g, b)); - if (to > from) { - graphics1.FillRectangle(&brush, _size - _shift, from, _shift, to - from); - graphics3.FillRectangle(&brush, 0, from, _shift, to - from); - for (int i = 2 * _shift; i < _size + _shift; ++i) { - Gdiplus::Pen pen(Gdiplus::Color(_alphas[i], r, g, b)); - graphics1.DrawLine(&pen, _size + _shift - i - 1, from, _size + _shift - i - 1, to); - graphics3.DrawLine(&pen, i - _shift, from, i - _shift, to); - } - } - if (_h > h) { - graphics1.FillRectangle(&brush, 0, h - _fullsize, _size, _fullsize); - graphics3.FillRectangle(&brush, 0, h - _fullsize, _size, _fullsize); - } - verCorners(h, &graphics1, &graphics3); - - POINT p1 = {x + w - _size, y}, p3 = {x, y}, f = {0, 0}; - SIZE s = { _size, h }; - updateWindow(1, &p1, &s); - updateWindow(3, &p3, &s); - } else if (x != _x || y != _y) { - POINT p1 = { x + w - _size, y }, p3 = { x, y }; - updateWindow(1, &p1); - updateWindow(3, &p3); - } else if (w != _w) { - POINT p1 = { x + w - _size, y }; - updateWindow(1, &p1); - } - _x = x; - _y = y; - _w = w; - _h = h; - - if (hidden && (changes & _PsShadowShown)) { - for (int i = 0; i < 4; ++i) { - SetWindowPos(hwnds[i], hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE); - } - hidden = false; - } - } - - void updateWindow(int i, POINT *p, SIZE *s = 0) { - static POINT f = {0, 0}; - if (s) { - UpdateLayeredWindow(hwnds[i], (s ? screenDC : 0), p, s, (s ? dcs[i] : 0), (s ? (&f) : 0), noKeyColor, &blend, ULW_ALPHA); - } else { - SetWindowPos(hwnds[i], 0, p->x, p->y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); - } - } - - void destroy() { - for (int i = 0; i < 4; ++i) { - if (dcs[i]) DeleteDC(dcs[i]); - if (bitmaps[i]) DeleteObject(bitmaps[i]); - if (hwnds[i]) DestroyWindow(hwnds[i]); - dcs[i] = 0; - bitmaps[i] = 0; - hwnds[i] = 0; - } - if (screenDC) ReleaseDC(0, screenDC); - } - - private: - - int _x, _y, _w, _h; - int _metaSize, _fullsize, _size, _shift; - QVector _alphas, _colors; - - bool hidden; - - HWND hwnds[4]; - HDC dcs[4], screenDC; - HBITMAP bitmaps[4]; - int max_w, max_h; - BLENDFUNCTION blend; - - BYTE r, g, b; - COLORREF noKeyColor; - - static LRESULT CALLBACK _PsShadowWindows::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - - }; - _PsShadowWindows _psShadowWindows; - - LRESULT CALLBACK _PsShadowWindows::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if (finished) return DefWindowProc(hwnd, msg, wParam, lParam); - - int i; - for (i = 0; i < 4; ++i) { - if (_psShadowWindows.hwnds[i] && hwnd == _psShadowWindows.hwnds[i]) { - break; - } - } - if (i == 4) return DefWindowProc(hwnd, msg, wParam, lParam); - - switch (msg) { - case WM_CLOSE: - App::wnd()->close(); - break; - - case WM_NCHITTEST: { - int32 xPos = GET_X_LPARAM(lParam), yPos = GET_Y_LPARAM(lParam); - switch (i) { - case 0: return HTTOP; - case 1: return (yPos < _psShadowWindows._y + _psSize) ? HTTOPRIGHT : ((yPos >= _psShadowWindows._y + _psShadowWindows._h - _psSize) ? HTBOTTOMRIGHT : HTRIGHT); - case 2: return HTBOTTOM; - case 3: return (yPos < _psShadowWindows._y + _psSize) ? HTTOPLEFT : ((yPos >= _psShadowWindows._y + _psShadowWindows._h - _psSize) ? HTBOTTOMLEFT : HTLEFT); - } - return HTTRANSPARENT; - } break; - - case WM_NCACTIVATE: return DefWindowProc(hwnd, msg, wParam, lParam); - case WM_NCLBUTTONDOWN: - case WM_NCLBUTTONUP: - case WM_NCLBUTTONDBLCLK: - case WM_NCMBUTTONDOWN: - case WM_NCMBUTTONUP: - case WM_NCMBUTTONDBLCLK: - case WM_NCRBUTTONDOWN: - case WM_NCRBUTTONUP: - case WM_NCRBUTTONDBLCLK: - case WM_NCXBUTTONDOWN: - case WM_NCXBUTTONUP: - case WM_NCXBUTTONDBLCLK: - case WM_NCMOUSEHOVER: - case WM_NCMOUSELEAVE: - case WM_NCMOUSEMOVE: - case WM_NCPOINTERUPDATE: - case WM_NCPOINTERDOWN: - case WM_NCPOINTERUP: - if (App::wnd() && App::wnd()->psHwnd()) { - if (msg == WM_NCLBUTTONDOWN) { - ::SetForegroundWindow(App::wnd()->psHwnd()); - } - LRESULT res = SendMessage(App::wnd()->psHwnd(), msg, wParam, lParam); - return res; - } - return 0; - break; - case WM_ACTIVATE: - if (App::wnd() && App::wnd()->psHwnd() && wParam == WA_ACTIVE) { - if ((HWND)lParam != App::wnd()->psHwnd()) { - ::SetForegroundWindow(hwnd); - ::SetWindowPos(App::wnd()->psHwnd(), hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - } - } - return DefWindowProc(hwnd, msg, wParam, lParam); - break; - default: - return DefWindowProc(hwnd, msg, wParam, lParam); - } - return 0; - } - - QColor _shActive(0, 0, 0), _shInactive(0, 0, 0); - - typedef HRESULT (FAR STDAPICALLTYPE *f_setWindowTheme)(HWND hWnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList); - f_setWindowTheme setWindowTheme = 0; - - typedef HRESULT (FAR STDAPICALLTYPE *f_openAs_RunDLL)(HWND hWnd, HINSTANCE hInstance, LPCWSTR lpszCmdLine, int nCmdShow); - f_openAs_RunDLL openAs_RunDLL = 0; - - typedef HRESULT (FAR STDAPICALLTYPE *f_shOpenWithDialog)(HWND hwndParent, const OPENASINFO *poainfo); - f_shOpenWithDialog shOpenWithDialog = 0; - - typedef HRESULT (FAR STDAPICALLTYPE *f_shAssocEnumHandlers)(PCWSTR pszExtra, ASSOC_FILTER afFilter, IEnumAssocHandlers **ppEnumHandler); - f_shAssocEnumHandlers shAssocEnumHandlers = 0; - - typedef HRESULT (FAR STDAPICALLTYPE *f_shCreateItemFromParsingName)(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv); - f_shCreateItemFromParsingName shCreateItemFromParsingName = 0; - - typedef BOOL (FAR STDAPICALLTYPE *f_wtsRegisterSessionNotification)(HWND hWnd, DWORD dwFlags); - f_wtsRegisterSessionNotification wtsRegisterSessionNotification = 0; - - typedef BOOL (FAR STDAPICALLTYPE *f_wtsUnRegisterSessionNotification)(HWND hWnd); - f_wtsUnRegisterSessionNotification wtsUnRegisterSessionNotification = 0; - - typedef HRESULT (FAR STDAPICALLTYPE *f_shQueryUserNotificationState)(QUERY_USER_NOTIFICATION_STATE *pquns); - f_shQueryUserNotificationState shQueryUserNotificationState = 0; - - typedef HRESULT (FAR STDAPICALLTYPE *f_setCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); - f_setCurrentProcessExplicitAppUserModelID setCurrentProcessExplicitAppUserModelID = 0; - - typedef HRESULT (FAR STDAPICALLTYPE *f_roGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void ** factory); - f_roGetActivationFactory roGetActivationFactory = 0; - - typedef HRESULT (FAR STDAPICALLTYPE *f_windowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER * hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string); - f_windowsCreateStringReference windowsCreateStringReference = 0; - - typedef HRESULT (FAR STDAPICALLTYPE *f_windowsDeleteString)(_In_opt_ HSTRING string); - f_windowsDeleteString windowsDeleteString = 0; - - typedef HRESULT (FAR STDAPICALLTYPE *f_propVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); - f_propVariantToString propVariantToString = 0; - - template - bool loadFunction(HINSTANCE dll, LPCSTR name, TFunction &func) { - if (!dll) return false; - - func = (TFunction)GetProcAddress(dll, name); - return !!func; - } class _PsInitializer { public: _PsInitializer() { - frameless = false; + Dlls::start(); - setupUx(); - setupShell(); - setupWtsapi(); - setupPropSys(); - setupCombase(); - - useTheme = !!setWindowTheme; - } - void setupUx() { - HINSTANCE procId = LoadLibrary(L"UXTHEME.DLL"); - - loadFunction(procId, "SetWindowTheme", setWindowTheme); - } - void setupShell() { - HINSTANCE procId = LoadLibrary(L"SHELL32.DLL"); - setupOpenWith(procId); - setupOpenAs(procId); - setupShellapi(procId); - setupAppUserModel(procId); - } - void setupOpenWith(HINSTANCE procId) { - if (!loadFunction(procId, "SHAssocEnumHandlers", shAssocEnumHandlers)) return; - if (!loadFunction(procId, "SHCreateItemFromParsingName", shCreateItemFromParsingName)) return; - useOpenWith = true; - } - void setupOpenAs(HINSTANCE procId) { - if (!loadFunction(procId, "SHOpenWithDialog", shOpenWithDialog) && !loadFunction(procId, "OpenAs_RunDLLW", openAs_RunDLL)) return; - useOpenAs = true; - } - void setupShellapi(HINSTANCE procId) { - if (!loadFunction(procId, "SHQueryUserNotificationState", shQueryUserNotificationState)) return; - useShellapi = true; - } - void setupAppUserModel(HINSTANCE procId) { - if (!loadFunction(procId, "SetCurrentProcessExplicitAppUserModelID", setCurrentProcessExplicitAppUserModelID)) return; - } - void setupWtsapi() { - HINSTANCE procId = LoadLibrary(L"WTSAPI32.DLL"); - - if (!loadFunction(procId, "WTSRegisterSessionNotification", wtsRegisterSessionNotification)) return; - if (!loadFunction(procId, "WTSUnRegisterSessionNotification", wtsUnRegisterSessionNotification)) return; - useWtsapi = true; - } - void setupCombase() { - if (!setCurrentProcessExplicitAppUserModelID) return; - - HINSTANCE procId = LoadLibrary(L"COMBASE.DLL"); - setupToast(procId); - } - void setupPropSys() { - HINSTANCE procId = LoadLibrary(L"PROPSYS.DLL"); - if (!loadFunction(procId, "PropVariantToString", propVariantToString)) return; - } - void setupToast(HINSTANCE procId) { - if (!propVariantToString) return; - if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS8) return; - if (!loadFunction(procId, "RoGetActivationFactory", roGetActivationFactory)) return; - - HINSTANCE otherProcId = LoadLibrary(L"api-ms-win-core-winrt-string-l1-1-0.dll"); - if (!loadFunction(otherProcId, "WindowsCreateStringReference", windowsCreateStringReference)) return; - if (!loadFunction(otherProcId, "WindowsDeleteString", windowsDeleteString)) return; - - useToast = true; + useOpenWith = (Dlls::SHAssocEnumHandlers != nullptr) && (SHCreateItemFromParsingName != nullptr); + useOpenAs = (Dlls::SHOpenWithDialog != nullptr) || (Dlls::OpenAs_RunDLL != nullptr); + useShellapi = (Dlls::SHQueryUserNotificationState != nullptr); } }; _PsInitializer _psInitializer; - class _PsEventFilter : public QAbstractNativeEventFilter { - public: - _PsEventFilter() { - } - - bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) { - auto wnd = App::wnd(); - if (!wnd) return false; - - MSG *msg = (MSG*)message; - if (msg->message == WM_ENDSESSION) { - App::quit(); - return false; - } - if (msg->hwnd == wnd->psHwnd() || msg->hwnd && !wnd->psHwnd()) { - return mainWindowEvent(msg->hwnd, msg->message, msg->wParam, msg->lParam, (LRESULT*)result); - } - return false; - } - - bool mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *result) { - if (tbCreatedMsgId && msg == tbCreatedMsgId) { - HRESULT hr = CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&taskbarList)); - if (!SUCCEEDED(hr)) { - taskbarList.Reset(); - } - } - switch (msg) { - - case WM_TIMECHANGE: { - App::wnd()->checkAutoLockIn(100); - } return false; - - case WM_WTSSESSION_CHANGE: { - if (wParam == WTS_SESSION_LOGOFF || wParam == WTS_SESSION_LOCK) { - sessionLoggedOff = true; - } else if (wParam == WTS_SESSION_LOGON || wParam == WTS_SESSION_UNLOCK) { - sessionLoggedOff = false; - } - } return false; - - case WM_DESTROY: { - App::quit(); - } return false; - - case WM_ACTIVATE: { - if (LOWORD(wParam) == WA_CLICKACTIVE) { - App::wnd()->inactivePress(true); - } - if (LOWORD(wParam) != WA_INACTIVE) { - _psShadowWindows.setColor(_shActive); - _psShadowWindows.update(_PsShadowActivate); - } else { - _psShadowWindows.setColor(_shInactive); - } - if (Global::started()) { - App::wnd()->update(); - } - } return false; - - case WM_NCPAINT: if (QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS8) return false; *result = 0; return true; - - case WM_NCCALCSIZE: { - WINDOWPLACEMENT wp; - wp.length = sizeof(WINDOWPLACEMENT); - if (GetWindowPlacement(hWnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) { - LPNCCALCSIZE_PARAMS params = (LPNCCALCSIZE_PARAMS)lParam; - LPRECT r = (wParam == TRUE) ? ¶ms->rgrc[0] : (LPRECT)lParam; - HMONITOR hMonitor = MonitorFromPoint({ (r->left + r->right) / 2, (r->top + r->bottom) / 2 }, MONITOR_DEFAULTTONEAREST); - if (hMonitor) { - MONITORINFO mi; - mi.cbSize = sizeof(mi); - if (GetMonitorInfo(hMonitor, &mi)) { - *r = mi.rcWork; - } - } - } - *result = 0; - return true; - } - - case WM_NCACTIVATE: { - *result = DefWindowProc(hWnd, msg, wParam, -1); - } return true; - - case WM_WINDOWPOSCHANGING: - case WM_WINDOWPOSCHANGED: { - WINDOWPLACEMENT wp; - wp.length = sizeof(WINDOWPLACEMENT); - if (GetWindowPlacement(hWnd, &wp) && (wp.showCmd == SW_SHOWMAXIMIZED || wp.showCmd == SW_SHOWMINIMIZED)) { - _psShadowWindows.update(_PsShadowHidden); - } else { - _psShadowWindows.update(_PsShadowMoved | _PsShadowResized, (WINDOWPOS*)lParam); - } - } return false; - - case WM_SIZE: { - if (App::wnd()) { - if (wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED || wParam == SIZE_MINIMIZED) { - if (wParam != SIZE_RESTORED || App::wnd()->windowState() != Qt::WindowNoState) { - Qt::WindowState state = Qt::WindowNoState; - if (wParam == SIZE_MAXIMIZED) { - state = Qt::WindowMaximized; - } else if (wParam == SIZE_MINIMIZED) { - state = Qt::WindowMinimized; - } - emit App::wnd()->windowHandle()->windowStateChanged(state); - } else { - App::wnd()->psUpdatedPosition(); - } - App::wnd()->psUpdateMargins(); - int changes = (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXIMIZED) ? _PsShadowHidden : (_PsShadowResized | _PsShadowShown); - _psShadowWindows.update(changes); - } - } - } return false; - - case WM_SHOWWINDOW: { - LONG style = GetWindowLong(hWnd, GWL_STYLE); - int changes = _PsShadowResized | ((wParam && !(style & (WS_MAXIMIZE | WS_MINIMIZE))) ? _PsShadowShown : _PsShadowHidden); - _psShadowWindows.update(changes); - } return false; - - case WM_MOVE: { - _psShadowWindows.update(_PsShadowMoved); - App::wnd()->psUpdatedPosition(); - } return false; - - case WM_NCHITTEST: { - POINTS p = MAKEPOINTS(lParam); - RECT r; - GetWindowRect(hWnd, &r); - HitTestType res = App::wnd()->hitTest(QPoint(p.x - r.left + dleft, p.y - r.top + dtop)); - switch (res) { - case HitTestClient: - case HitTestSysButton: *result = HTCLIENT; break; - case HitTestIcon: *result = HTCAPTION; break; - case HitTestCaption: *result = HTCAPTION; break; - case HitTestTop: *result = HTTOP; break; - case HitTestTopRight: *result = HTTOPRIGHT; break; - case HitTestRight: *result = HTRIGHT; break; - case HitTestBottomRight: *result = HTBOTTOMRIGHT; break; - case HitTestBottom: *result = HTBOTTOM; break; - case HitTestBottomLeft: *result = HTBOTTOMLEFT; break; - case HitTestLeft: *result = HTLEFT; break; - case HitTestTopLeft: *result = HTTOPLEFT; break; - case HitTestNone: - default: *result = HTTRANSPARENT; break; - }; - } return true; - - case WM_NCRBUTTONUP: { - SendMessage(hWnd, WM_SYSCOMMAND, SC_MOUSEMENU, lParam); - } return true; - - case WM_NCLBUTTONDOWN: { - POINTS p = MAKEPOINTS(lParam); - RECT r; - GetWindowRect(hWnd, &r); - HitTestType res = App::wnd()->hitTest(QPoint(p.x - r.left + dleft, p.y - r.top + dtop)); - switch (res) { - case HitTestIcon: - if (menuHidden && getms() < menuHidden + 10) { - menuHidden = 0; - if (getms() < menuShown + GetDoubleClickTime()) { - App::wnd()->close(); - } - } else { - QRect icon = App::wnd()->iconRect(); - p.x = r.left - dleft + icon.left(); - p.y = r.top - dtop + icon.top() + icon.height(); - App::wnd()->psUpdateSysMenu(App::wnd()->windowHandle()->windowState()); - menuShown = getms(); - menuHidden = 0; - TrackPopupMenu(App::wnd()->psMenu(), TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, p.x, p.y, 0, hWnd, 0); - menuHidden = getms(); - } - return true; - }; - } return false; - - case WM_NCLBUTTONDBLCLK: { - POINTS p = MAKEPOINTS(lParam); - RECT r; - GetWindowRect(hWnd, &r); - HitTestType res = App::wnd()->hitTest(QPoint(p.x - r.left + dleft, p.y - r.top + dtop)); - switch (res) { - case HitTestIcon: App::wnd()->close(); return true; - }; - } return false; - - case WM_SYSCOMMAND: { - if (wParam == SC_MOUSEMENU) { - POINTS p = MAKEPOINTS(lParam); - App::wnd()->psUpdateSysMenu(App::wnd()->windowHandle()->windowState()); - TrackPopupMenu(App::wnd()->psMenu(), TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, p.x, p.y, 0, hWnd, 0); - } - } return false; - - case WM_COMMAND: { - if (HIWORD(wParam)) return false; - int cmd = LOWORD(wParam); - switch (cmd) { - case SC_CLOSE: App::wnd()->close(); return true; - case SC_MINIMIZE: App::wnd()->setWindowState(Qt::WindowMinimized); return true; - case SC_MAXIMIZE: App::wnd()->setWindowState(Qt::WindowMaximized); return true; - case SC_RESTORE: App::wnd()->setWindowState(Qt::WindowNoState); return true; - } - } return true; - - } - return false; - } - }; - _PsEventFilter *_psEventFilter = 0; - }; -PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent), ps_hWnd(0), ps_menu(0), icon256(qsl(":/gui/art/icon256.png")), iconbig256(qsl(":/gui/art/iconbig256.png")), wndIcon(QPixmap::fromImage(icon256, Qt::ColorOnly)), - ps_iconBig(0), ps_iconSmall(0), ps_iconOverlay(0), trayIcon(0), trayIconMenu(0), posInited(false), ps_tbHider_hWnd(createTaskbarHider()) { - tbCreatedMsgId = RegisterWindowMessage(L"TaskbarButtonCreated"); - connect(&ps_cleanNotifyPhotosTimer, SIGNAL(timeout()), this, SLOT(psCleanNotifyPhotos())); -} - -void PsMainWindow::psShowTrayMenu() { - trayIconMenu->popup(QCursor::pos()); -} - -void PsMainWindow::psCleanNotifyPhotosIn(int32 dt) { - if (dt < 0) { - if (ps_cleanNotifyPhotosTimer.isActive() && ps_cleanNotifyPhotosTimer.remainingTime() <= -dt) return; - dt = -dt; - } - ps_cleanNotifyPhotosTimer.start(dt); -} - -void PsMainWindow::psCleanNotifyPhotos() { - uint64 ms = getms(true), minuntil = 0; - for (ToastImages::iterator i = toastImages.begin(); i != toastImages.end();) { - if (!i->until) { - ++i; - continue; - } - if (i->until <= ms) { - QFile(i->path).remove(); - i = toastImages.erase(i); - } else { - if (!minuntil || minuntil > i->until) { - minuntil = i->until; - } - ++i; - } - } - if (minuntil) psCleanNotifyPhotosIn(int32(minuntil - ms)); -} - -void PsMainWindow::psRefreshTaskbarIcon() { - QWidget *w = new QWidget(this); - w->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); - w->setGeometry(x() + 1, y() + 1, 1, 1); - QPalette p(w->palette()); - p.setColor(QPalette::Background, st::titleBG->c); - QWindow *wnd = w->windowHandle(); - w->setPalette(p); - w->show(); - w->activateWindow(); - delete w; -} - -void PsMainWindow::psTrayMenuUpdated() { -} - -void PsMainWindow::psSetupTrayIcon() { - if (!trayIcon) { - trayIcon = new QSystemTrayIcon(this); - - QIcon icon(QPixmap::fromImage(App::wnd()->iconLarge(), Qt::ColorOnly)); - - trayIcon->setIcon(icon); - trayIcon->setToolTip(str_const_toString(AppName)); - connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(toggleTray(QSystemTrayIcon::ActivationReason)), Qt::UniqueConnection); - connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showFromTray())); - App::wnd()->updateTrayMenu(); - } - psUpdateCounter(); - - trayIcon->show(); - psUpdateDelegate(); -} - -void PsMainWindow::psUpdateWorkmode() { - switch (cWorkMode()) { - case dbiwmWindowAndTray: { - psSetupTrayIcon(); - HWND psOwner = (HWND)GetWindowLong(ps_hWnd, GWL_HWNDPARENT); - if (psOwner) { - SetWindowLong(ps_hWnd, GWL_HWNDPARENT, 0); - psRefreshTaskbarIcon(); - } - } break; - - case dbiwmTrayOnly: { - psSetupTrayIcon(); - HWND psOwner = (HWND)GetWindowLong(ps_hWnd, GWL_HWNDPARENT); - if (!psOwner) { - SetWindowLong(ps_hWnd, GWL_HWNDPARENT, (LONG)ps_tbHider_hWnd); - } - } break; - - case dbiwmWindowOnly: { - if (trayIcon) { - trayIcon->setContextMenu(0); - trayIcon->deleteLater(); - } - trayIcon = 0; - - HWND psOwner = (HWND)GetWindowLong(ps_hWnd, GWL_HWNDPARENT); - if (psOwner) { - SetWindowLong(ps_hWnd, GWL_HWNDPARENT, 0); - psRefreshTaskbarIcon(); - } - } break; - } -} - -HICON qt_pixmapToWinHICON(const QPixmap &); -HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &, int hbitmapFormat); - -static HICON _qt_createHIcon(const QIcon &icon, int xSize, int ySize) { - if (!icon.isNull()) { - const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize))); - if (!pm.isNull()) - return qt_pixmapToWinHICON(pm); - } - return 0; -} - -void PsMainWindow::psUpdateCounter() { - int32 counter = App::histories().unreadBadge(); - bool muted = App::histories().unreadOnlyMuted(); - - style::color bg = muted ? st::counterMuteBG : st::counterBG; - QIcon iconSmall, iconBig; - iconSmall.addPixmap(QPixmap::fromImage(iconWithCounter(16, counter, bg, true), Qt::ColorOnly)); - iconSmall.addPixmap(QPixmap::fromImage(iconWithCounter(32, counter, bg, true), Qt::ColorOnly)); - iconBig.addPixmap(QPixmap::fromImage(iconWithCounter(32, taskbarList.Get() ? 0 : counter, bg, false), Qt::ColorOnly)); - iconBig.addPixmap(QPixmap::fromImage(iconWithCounter(64, taskbarList.Get() ? 0 : counter, bg, false), Qt::ColorOnly)); - if (trayIcon) { - trayIcon->setIcon(iconSmall); - } - - setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); - psDestroyIcons(); - ps_iconSmall = _qt_createHIcon(iconSmall, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); - ps_iconBig = _qt_createHIcon(iconBig, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); - SendMessage(ps_hWnd, WM_SETICON, 0, (LPARAM)ps_iconSmall); - SendMessage(ps_hWnd, WM_SETICON, 1, (LPARAM)(ps_iconBig ? ps_iconBig : ps_iconSmall)); - if (taskbarList.Get()) { - if (counter > 0) { - QIcon iconOverlay; - iconOverlay.addPixmap(QPixmap::fromImage(iconWithCounter(-16, counter, bg, false), Qt::ColorOnly)); - iconOverlay.addPixmap(QPixmap::fromImage(iconWithCounter(-32, counter, bg, false), Qt::ColorOnly)); - ps_iconOverlay = _qt_createHIcon(iconOverlay, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); - } - QString description = counter > 0 ? QString("%1 unread messages").arg(counter) : qsl("No unread messages"); - taskbarList->SetOverlayIcon(ps_hWnd, ps_iconOverlay, description.toStdWString().c_str()); - } - SetWindowPos(ps_hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); -} - -void PsMainWindow::psUpdateDelegate() { -} - -namespace { - HMONITOR enumMonitor = 0; - RECT enumMonitorWork; - - BOOL CALLBACK _monitorEnumProc( - _In_ HMONITOR hMonitor, - _In_ HDC hdcMonitor, - _In_ LPRECT lprcMonitor, - _In_ LPARAM dwData - ) { - MONITORINFOEX info; - info.cbSize = sizeof(info); - GetMonitorInfo(hMonitor, &info); - if (dwData == hashCrc32(info.szDevice, sizeof(info.szDevice))) { - enumMonitor = hMonitor; - enumMonitorWork = info.rcWork; - return FALSE; - } - return TRUE; - } -} - -void PsMainWindow::psInitSize() { - setMinimumWidth(st::wndMinWidth); - setMinimumHeight(st::wndMinHeight); - - TWindowPos pos(cWindowPos()); - QRect avail(Sandbox::availableGeometry()); - bool maximized = false; - QRect geom(avail.x() + (avail.width() - st::wndDefWidth) / 2, avail.y() + (avail.height() - st::wndDefHeight) / 2, st::wndDefWidth, st::wndDefHeight); - if (pos.w && pos.h) { - if (pos.y < 0) pos.y = 0; - enumMonitor = 0; - EnumDisplayMonitors(0, 0, &_monitorEnumProc, pos.moncrc); - if (enumMonitor) { - int32 w = enumMonitorWork.right - enumMonitorWork.left, h = enumMonitorWork.bottom - enumMonitorWork.top; - if (w >= st::wndMinWidth && h >= st::wndMinHeight) { - if (pos.w > w) pos.w = w; - if (pos.h > h) pos.h = h; - pos.x += enumMonitorWork.left; - pos.y += enumMonitorWork.top; - if (pos.x < enumMonitorWork.right - 10 && pos.y < enumMonitorWork.bottom - 10) { - geom = QRect(pos.x, pos.y, pos.w, pos.h); - } - } - } - maximized = pos.maximized; - } - setGeometry(geom); -} - -bool InitToastManager(); -bool CreateToast(PeerData *peer, int32 msgId, bool showpix, const QString &title, const QString &subtitle, const QString &msg); -void CheckPinnedAppUserModelId(); -void CleanupAppUserModelIdShortcut(); - -void PsMainWindow::psInitFrameless() { - psUpdatedPositionTimer.setSingleShot(true); - connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); - - QPlatformNativeInterface *i = QGuiApplication::platformNativeInterface(); - ps_hWnd = static_cast(i->nativeResourceForWindow(QByteArrayLiteral("handle"), windowHandle())); - - if (!ps_hWnd) return; - - if (useWtsapi) wtsRegisterSessionNotification(ps_hWnd, NOTIFY_FOR_THIS_SESSION); - - if (frameless) { - setWindowFlags(Qt::FramelessWindowHint); - } - -// RegisterApplicationRestart(NULL, 0); - if (!InitToastManager()) { - useToast = false; - } - - psInitSysMenu(); -} - -void PsMainWindow::psSavePosition(Qt::WindowState state) { - if (state == Qt::WindowActive) state = windowHandle()->windowState(); - if (state == Qt::WindowMinimized || !posInited) return; - - TWindowPos pos(cWindowPos()), curPos = pos; - - if (state == Qt::WindowMaximized) { - curPos.maximized = 1; - } else { - RECT w; - GetWindowRect(ps_hWnd, &w); - curPos.x = w.left; - curPos.y = w.top; - curPos.w = w.right - w.left; - curPos.h = w.bottom - w.top; - curPos.maximized = 0; - } - - HMONITOR hMonitor = MonitorFromWindow(ps_hWnd, MONITOR_DEFAULTTONEAREST); - if (hMonitor) { - MONITORINFOEX info; - info.cbSize = sizeof(info); - GetMonitorInfo(hMonitor, &info); - if (!curPos.maximized) { - curPos.x -= info.rcWork.left; - curPos.y -= info.rcWork.top; - } - curPos.moncrc = hashCrc32(info.szDevice, sizeof(info.szDevice)); - } - - if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { - if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { - cSetWindowPos(curPos); - Local::writeSettings(); - } - } -} - -void PsMainWindow::psUpdatedPosition() { - psUpdatedPositionTimer.start(SaveWindowPositionTimeout); -} - -bool PsMainWindow::psHasNativeNotifications() { - return useToast; -} - -Q_DECLARE_METATYPE(QMargins); -void PsMainWindow::psFirstShow() { - if (useToast) { - cSetCustomNotifies(!cWindowsNotifications()); - } else { - cSetCustomNotifies(true); - } - - _psShadowWindows.init(_shActive); - finished = false; - - psUpdateMargins(); - - _psShadowWindows.update(_PsShadowHidden); - bool showShadows = true; - - show(); - if (cWindowPos().maximized) { - setWindowState(Qt::WindowMaximized); - } - - if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) { - setWindowState(Qt::WindowMinimized); - if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { - hide(); - } else { - show(); - } - showShadows = false; - } else { - show(); - } - - posInited = true; - if (showShadows) { - _psShadowWindows.update(_PsShadowMoved | _PsShadowResized | _PsShadowShown); - } -} - -bool PsMainWindow::psHandleTitle() { - return true; -} - -void PsMainWindow::psInitSysMenu() { - Qt::WindowStates states = windowState(); - ps_menu = GetSystemMenu(ps_hWnd, FALSE); - psUpdateSysMenu(windowHandle()->windowState()); -} - -void PsMainWindow::psUpdateSysMenu(Qt::WindowState state) { - if (!ps_menu) return; - - int menuToDisable = SC_RESTORE; - if (state == Qt::WindowMaximized) { - menuToDisable = SC_MAXIMIZE; - } else if (state == Qt::WindowMinimized) { - menuToDisable = SC_MINIMIZE; - } - int itemCount = GetMenuItemCount(ps_menu); - for (int i = 0; i < itemCount; ++i) { - MENUITEMINFO itemInfo = {0}; - itemInfo.cbSize = sizeof(itemInfo); - itemInfo.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID; - if (GetMenuItemInfo(ps_menu, i, TRUE, &itemInfo)) { - if (itemInfo.fType & MFT_SEPARATOR) { - continue; - } - if (itemInfo.wID && !(itemInfo.fState & MFS_DEFAULT)) { - UINT fOldState = itemInfo.fState, fState = itemInfo.fState & ~MFS_DISABLED; - if (itemInfo.wID == SC_CLOSE) { - fState |= MFS_DEFAULT; - } else if (itemInfo.wID == menuToDisable || (itemInfo.wID != SC_MINIMIZE && itemInfo.wID != SC_MAXIMIZE && itemInfo.wID != SC_RESTORE)) { - fState |= MFS_DISABLED; - } - itemInfo.fMask = MIIM_STATE; - itemInfo.fState = fState; - if (!SetMenuItemInfo(ps_menu, i, TRUE, &itemInfo)) { - DEBUG_LOG(("PS Error: could not set state %1 to menu item %2, old state %3, error %4").arg(fState).arg(itemInfo.wID).arg(fOldState).arg(GetLastError())); - DestroyMenu(ps_menu); - ps_menu = 0; - break; - } - } - } else { - DEBUG_LOG(("PS Error: could not get state, menu item %1 of %2, error %3").arg(i).arg(itemCount).arg(GetLastError())); - DestroyMenu(ps_menu); - ps_menu = 0; - break; - } - } -} - -void PsMainWindow::psUpdateMargins() { - if (!ps_hWnd) return; - - RECT r, a; - - GetClientRect(ps_hWnd, &r); - a = r; - - LONG style = GetWindowLong(ps_hWnd, GWL_STYLE), styleEx = GetWindowLong(ps_hWnd, GWL_EXSTYLE); - AdjustWindowRectEx(&a, style, false, styleEx); - QMargins margins = QMargins(a.left - r.left, a.top - r.top, r.right - a.right, r.bottom - a.bottom); - if (style & WS_MAXIMIZE) { - RECT w, m; - GetWindowRect(ps_hWnd, &w); - m = w; - - HMONITOR hMonitor = MonitorFromRect(&w, MONITOR_DEFAULTTONEAREST); - if (hMonitor) { - MONITORINFO mi; - mi.cbSize = sizeof(mi); - GetMonitorInfo(hMonitor, &mi); - m = mi.rcWork; - } - - dleft = w.left - m.left; - dtop = w.top - m.top; - - margins.setLeft(margins.left() - w.left + m.left); - margins.setRight(margins.right() - m.right + w.right); - margins.setBottom(margins.bottom() - m.bottom + w.bottom); - margins.setTop(margins.top() - w.top + m.top); - } else { - dleft = dtop = 0; - } - - QPlatformNativeInterface *i = QGuiApplication::platformNativeInterface(); - i->setWindowProperty(windowHandle()->handle(), qsl("WindowsCustomMargins"), QVariant::fromValue(margins)); - if (!themeInited) { - themeInited = true; - if (useTheme) { - if (QSysInfo::WindowsVersion < QSysInfo::WV_WINDOWS8) { - setWindowTheme(ps_hWnd, L" ", L" "); - QApplication::setStyle(QStyleFactory::create(qsl("Windows"))); - } - } - } -} - -void PsMainWindow::psFlash() { - if (GetForegroundWindow() == ps_hWnd) return; - - FLASHWINFO info; - info.cbSize = sizeof(info); - info.hwnd = ps_hWnd; - info.dwFlags = FLASHW_ALL; - info.dwTimeout = 0; - info.uCount = 1; - FlashWindowEx(&info); -} - -HWND PsMainWindow::psHwnd() const { - return ps_hWnd; -} - -HMENU PsMainWindow::psMenu() const { - return ps_menu; -} - -void PsMainWindow::psDestroyIcons() { - if (ps_iconBig) { - DestroyIcon(ps_iconBig); - ps_iconBig = 0; - } - if (ps_iconSmall) { - DestroyIcon(ps_iconSmall); - ps_iconSmall = 0; - } - if (ps_iconOverlay) { - DestroyIcon(ps_iconOverlay); - ps_iconOverlay = 0; - } -} - -PsMainWindow::~PsMainWindow() { - if (useWtsapi) { - QPlatformNativeInterface *i = QGuiApplication::platformNativeInterface(); - if (HWND hWnd = static_cast(i->nativeResourceForWindow(QByteArrayLiteral("handle"), windowHandle()))) { - wtsUnRegisterSessionNotification(hWnd); - } - } - - if (taskbarList) taskbarList.Reset(); - - toastNotifications.clear(); - if (toastNotificationManager) toastNotificationManager.Reset(); - if (toastNotifier) toastNotifier.Reset(); - if (toastNotificationFactory) toastNotificationFactory.Reset(); - - finished = true; - if (ps_menu) DestroyMenu(ps_menu); - psDestroyIcons(); - _psShadowWindows.destroy(); - if (ps_tbHider_hWnd) DestroyWindow(ps_tbHider_hWnd); -} - -namespace { - QRect _monitorRect; - uint64 _monitorLastGot = 0; -} - -QRect psDesktopRect() { - uint64 tnow = getms(); - if (tnow > _monitorLastGot + 1000 || tnow < _monitorLastGot) { - _monitorLastGot = tnow; - HMONITOR hMonitor = MonitorFromWindow(App::wnd()->psHwnd(), MONITOR_DEFAULTTONEAREST); - if (hMonitor) { - MONITORINFOEX info; - info.cbSize = sizeof(info); - GetMonitorInfo(hMonitor, &info); - _monitorRect = QRect(info.rcWork.left, info.rcWork.top, info.rcWork.right - info.rcWork.left, info.rcWork.bottom - info.rcWork.top); - } else { - _monitorRect = QApplication::desktop()->availableGeometry(App::wnd()); - } - } - return _monitorRect; -} - -void psShowOverAll(QWidget *w, bool canFocus) { -} - -void psBringToBack(QWidget *w) { -} - -void PsMainWindow::psActivateNotify(NotifyWindow *w) { -} - -void PsMainWindow::psClearNotifies(PeerId peerId) { - if (!toastNotifier) return; - - if (peerId) { - ToastNotifications::iterator i = toastNotifications.find(peerId); - if (i != toastNotifications.cend()) { - QMap temp = i.value(); - toastNotifications.erase(i); - - for (QMap::const_iterator j = temp.cbegin(), e = temp.cend(); j != e; ++j) { - toastNotifier->Hide(j->p.Get()); - } - } - } else { - ToastNotifications temp = toastNotifications; - toastNotifications.clear(); - - for (ToastNotifications::const_iterator i = temp.cbegin(), end = temp.cend(); i != end; ++i) { - for (QMap::const_iterator j = i->cbegin(), e = i->cend(); j != e; ++j) { - toastNotifier->Hide(j->p.Get()); - } - } - } -} - -void PsMainWindow::psNotifyShown(NotifyWindow *w) { -} - -void PsMainWindow::psPlatformNotify(HistoryItem *item, int32 fwdCount) { - QString title = (!App::passcoded() && cNotifyView() <= dbinvShowName) ? item->history()->peer->name : qsl("Telegram Desktop"); - QString subtitle = (!App::passcoded() && cNotifyView() <= dbinvShowName) ? item->notificationHeader() : QString(); - bool showpix = (!App::passcoded() && cNotifyView() <= dbinvShowName); - QString msg = (!App::passcoded() && cNotifyView() <= dbinvShowPreview) ? (fwdCount < 2 ? item->notificationText() : lng_forward_messages(lt_count, fwdCount)) : lang(lng_notification_preview); - - CreateToast(item->history()->peer, item->id, showpix, title, subtitle, msg); -} - QAbstractNativeEventFilter *psNativeEventFilter() { - delete _psEventFilter; - _psEventFilter = new _PsEventFilter(); - return _psEventFilter; + return EventFilter::createInstance(); } void psDeleteDir(const QString &dir) { @@ -1613,7 +160,7 @@ namespace { void psUserActionDone() { _lastUserAction = getms(true); - if (sessionLoggedOff) sessionLoggedOff = false; + EventFilter::getInstance()->setSessionLoggedOff(false); } bool psIdleSupported() { @@ -1630,15 +177,15 @@ uint64 psIdleTime() { bool psSkipAudioNotify() { QUERY_USER_NOTIFICATION_STATE state; - if (useShellapi && SUCCEEDED(shQueryUserNotificationState(&state))) { + if (useShellapi && SUCCEEDED(Dlls::SHQueryUserNotificationState(&state))) { if (state == QUNS_NOT_PRESENT || state == QUNS_PRESENTATION_MODE) return true; } - return sessionLoggedOff; + return EventFilter::getInstance()->sessionLoggedOff(); } bool psSkipDesktopNotify() { QUERY_USER_NOTIFICATION_STATE state; - if (useShellapi && SUCCEEDED(shQueryUserNotificationState(&state))) { + if (useShellapi && SUCCEEDED(Dlls::SHQueryUserNotificationState(&state))) { if (state == QUNS_PRESENTATION_MODE || state == QUNS_RUNNING_D3D_FULL_SCREEN/* || state == QUNS_BUSY*/) return true; } return false; @@ -1879,11 +426,39 @@ void psDoCleanup() { try { psAutoStart(false, true); psSendToMenu(false, true); - CleanupAppUserModelIdShortcut(); + AppUserModelId::cleanupShortcut(); } catch (...) { } } +namespace { +QRect _monitorRect; +uint64 _monitorLastGot = 0; +} + +QRect psDesktopRect() { + uint64 tnow = getms(); + if (tnow > _monitorLastGot + 1000 || tnow < _monitorLastGot) { + _monitorLastGot = tnow; + HMONITOR hMonitor = MonitorFromWindow(App::wnd()->psHwnd(), MONITOR_DEFAULTTONEAREST); + if (hMonitor) { + MONITORINFOEX info; + info.cbSize = sizeof(info); + GetMonitorInfo(hMonitor, &info); + _monitorRect = QRect(info.rcWork.left, info.rcWork.top, info.rcWork.right - info.rcWork.left, info.rcWork.bottom - info.rcWork.top); + } else { + _monitorRect = QApplication::desktop()->availableGeometry(App::wnd()); + } + } + return _monitorRect; +} + +void psShowOverAll(QWidget *w, bool canFocus) { +} + +void psBringToBack(QWidget *w) { +} + int psCleanup() { __try { @@ -1976,6 +551,8 @@ void psPostprocessFile(const QString &name) { } } +HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &, int hbitmapFormat); + namespace { struct OpenWithApp { OpenWithApp(const QString &name, HBITMAP icon, IAssocHandler *handler) : name(name), icon(icon), handler(handler) { @@ -2035,7 +612,7 @@ bool psShowOpenWithMenu(int x, int y, const QString &file) { bool result = false; QList handlers; IShellItem* pItem = nullptr; - if (SUCCEEDED(shCreateItemFromParsingName(QDir::toNativeSeparators(file).toStdWString().c_str(), nullptr, IID_PPV_ARGS(&pItem)))) { + if (SUCCEEDED(Dlls::SHCreateItemFromParsingName(QDir::toNativeSeparators(file).toStdWString().c_str(), nullptr, IID_PPV_ARGS(&pItem)))) { IEnumAssocHandlers *assocHandlers = 0; if (SUCCEEDED(pItem->BindToHandler(nullptr, BHID_EnumAssocHandlers, IID_PPV_ARGS(&assocHandlers)))) { HRESULT hr = S_FALSE; @@ -2136,15 +713,15 @@ void psOpenFile(const QString &name, bool openWith) { std::wstring wname = mailtoScheme ? name.toStdWString() : QDir::toNativeSeparators(name).toStdWString(); if (openWith && useOpenAs) { - if (shOpenWithDialog) { + if (Dlls::SHOpenWithDialog) { OPENASINFO info; info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC; if (mailtoScheme) info.oaifInFlags |= OAIF_FILE_IS_URI | OAIF_URL_PROTOCOL; info.pcszClass = NULL; info.pcszFile = wname.c_str(); - shOpenWithDialog(0, &info); + Dlls::SHOpenWithDialog(0, &info); } else { - openAs_RunDLL(0, 0, wname.c_str(), SW_SHOWNORMAL); + Dlls::OpenAs_RunDLL(0, 0, wname.c_str(), SW_SHOWNORMAL); } } else { ShellExecute(0, L"open", wname.c_str(), 0, 0, SW_SHOWNORMAL); @@ -2157,30 +734,26 @@ void psShowInFolder(const QString &name) { } -namespace PlatformSpecific { - - void start() { - } - - void finish() { - delete _psEventFilter; - _psEventFilter = 0; - - if (ToastImageSavedFlag) { - psDeleteDir(cWorkingDir() + qsl("tdata/temp")); - } - } - - namespace ThirdParty { - void start() { - } - - void finish() { - } - } +namespace Platform { +void start() { } +void finish() { + EventFilter::destroy(); +} + +namespace ThirdParty { + +void start() { +} + +void finish() { +} + +} // namespace ThirdParty +} // namespace Platform + namespace { void _psLogError(const char *str, LSTATUS code) { LPTSTR errorText = NULL, errorTextDefault = L"(Unknown error)"; @@ -2256,7 +829,7 @@ void RegisterCustomScheme() { void psNewVersion() { RegisterCustomScheme(); if (Local::oldSettingsVersion() < 8051) { - CheckPinnedAppUserModelId(); + AppUserModelId::checkPinned(); } } @@ -2321,9 +894,9 @@ void _manageAppLnk(bool create, bool silent, int path_csidl, const wchar_t *args hr = shellLink.As(&propertyStore); if (SUCCEEDED(hr)) { PROPVARIANT appIdPropVar; - hr = InitPropVariantFromString(AppUserModelId(), &appIdPropVar); + hr = InitPropVariantFromString(AppUserModelId::getId(), &appIdPropVar); if (SUCCEEDED(hr)) { - hr = propertyStore->SetValue(pkey_AppUserModel_ID, appIdPropVar); + hr = propertyStore->SetValue(AppUserModelId::getKey(), appIdPropVar); PropVariantClear(&appIdPropVar); if (SUCCEEDED(hr)) { hr = propertyStore->Commit(); @@ -2971,683 +1544,6 @@ void psWriteStackTrace() { #endif // !TDESKTOP_DISABLE_CRASH_REPORTS } -class StringReferenceWrapper { -public: - - StringReferenceWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() { - HRESULT hr = windowsCreateStringReference(stringRef, length, &_header, &_hstring); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - } - - ~StringReferenceWrapper() { - windowsDeleteString(_hstring); - } - - template - StringReferenceWrapper(_In_reads_(N) wchar_t const (&stringRef)[N]) throw() { - UINT32 length = N - 1; - HRESULT hr = windowsCreateStringReference(stringRef, length, &_header, &_hstring); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - } - - template - StringReferenceWrapper(_In_reads_(_) wchar_t(&stringRef)[_]) throw() { - UINT32 length; - HRESULT hr = SizeTToUInt32(wcslen(stringRef), &length); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - - windowsCreateStringReference(stringRef, length, &_header, &_hstring); - } - - HSTRING Get() const throw() { - return _hstring; - } - -private: - HSTRING _hstring; - HSTRING_HEADER _header; - -}; - -HRESULT SetNodeValueString(_In_ HSTRING inputString, _In_ IXmlNode *node, _In_ IXmlDocument *xml) { - ComPtr inputText; - - HRESULT hr = xml->CreateTextNode(inputString, &inputText); - if (!SUCCEEDED(hr)) return hr; - ComPtr inputTextNode; - - hr = inputText.As(&inputTextNode); - if (!SUCCEEDED(hr)) return hr; - - ComPtr pAppendedChild; - return node->AppendChild(inputTextNode.Get(), &pAppendedChild); -} - -HRESULT SetAudioSilent(_In_ IXmlDocument *toastXml) { - ComPtr nodeList; - HRESULT hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"audio").Get(), &nodeList); - if (!SUCCEEDED(hr)) return hr; - - ComPtr audioNode; - hr = nodeList->Item(0, &audioNode); - if (!SUCCEEDED(hr)) return hr; - - if (audioNode) { - ComPtr audioElement; - hr = audioNode.As(&audioElement); - if (!SUCCEEDED(hr)) return hr; - - hr = audioElement->SetAttribute(StringReferenceWrapper(L"silent").Get(), StringReferenceWrapper(L"true").Get()); - if (!SUCCEEDED(hr)) return hr; - } else { - ComPtr audioElement; - hr = toastXml->CreateElement(StringReferenceWrapper(L"audio").Get(), &audioElement); - if (!SUCCEEDED(hr)) return hr; - - hr = audioElement->SetAttribute(StringReferenceWrapper(L"silent").Get(), StringReferenceWrapper(L"true").Get()); - if (!SUCCEEDED(hr)) return hr; - - ComPtr audioNode; - hr = audioElement.As(&audioNode); - if (!SUCCEEDED(hr)) return hr; - - ComPtr nodeList; - hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"toast").Get(), &nodeList); - if (!SUCCEEDED(hr)) return hr; - - ComPtr toastNode; - hr = nodeList->Item(0, &toastNode); - if (!SUCCEEDED(hr)) return hr; - - ComPtr appendedNode; - hr = toastNode->AppendChild(audioNode.Get(), &appendedNode); - } - return hr; -} - -HRESULT SetImageSrc(_In_z_ const wchar_t *imagePath, _In_ IXmlDocument *toastXml) { - wchar_t imageSrc[MAX_PATH] = L"file:///"; - HRESULT hr = StringCchCat(imageSrc, ARRAYSIZE(imageSrc), imagePath); - if (!SUCCEEDED(hr)) return hr; - - ComPtr nodeList; - hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"image").Get(), &nodeList); - if (!SUCCEEDED(hr)) return hr; - - ComPtr imageNode; - hr = nodeList->Item(0, &imageNode); - if (!SUCCEEDED(hr)) return hr; - - ComPtr attributes; - hr = imageNode->get_Attributes(&attributes); - if (!SUCCEEDED(hr)) return hr; - - ComPtr srcAttribute; - hr = attributes->GetNamedItem(StringReferenceWrapper(L"src").Get(), &srcAttribute); - if (!SUCCEEDED(hr)) return hr; - - return SetNodeValueString(StringReferenceWrapper(imageSrc).Get(), srcAttribute.Get(), toastXml); -} - -typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastActivatedEventHandler; -typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastDismissedEventHandler; -typedef ABI::Windows::Foundation::ITypedEventHandler DesktopToastFailedEventHandler; - -class ToastEventHandler : public Implements { -public: - - ToastEventHandler::ToastEventHandler(const PeerId &peer, MsgId msg) : _ref(1), _peerId(peer), _msgId(msg) { - } - ~ToastEventHandler() { - } - - // DesktopToastActivatedEventHandler - IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IInspectable* args) { - ToastNotifications::iterator i = toastNotifications.find(_peerId); - if (i != toastNotifications.cend()) { - i.value().remove(_msgId); - if (i.value().isEmpty()) { - toastNotifications.erase(i); - } - } - if (App::wnd()) { - History *history = App::history(_peerId); - - App::wnd()->showFromTray(); - if (App::passcoded()) { - App::wnd()->setInnerFocus(); - App::wnd()->notifyClear(); - } else { - App::wnd()->hideSettings(); - bool tomsg = !history->peer->isUser() && (_msgId > 0); - if (tomsg) { - HistoryItem *item = App::histItemById(peerToChannel(_peerId), _msgId); - if (!item || !item->mentionsMe()) { - tomsg = false; - } - } - Ui::showPeerHistory(history, tomsg ? _msgId : ShowAtUnreadMsgId); - App::wnd()->notifyClear(history); - } - SetForegroundWindow(App::wnd()->psHwnd()); - } - return S_OK; - } - - // DesktopToastDismissedEventHandler - IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IToastDismissedEventArgs *e) { - ToastDismissalReason tdr; - if (SUCCEEDED(e->get_Reason(&tdr))) { - switch (tdr) { - case ToastDismissalReason_ApplicationHidden: - break; - case ToastDismissalReason_UserCanceled: - case ToastDismissalReason_TimedOut: - default: - ToastNotifications::iterator i = toastNotifications.find(_peerId); - if (i != toastNotifications.cend()) { - i.value().remove(_msgId); - if (i.value().isEmpty()) { - toastNotifications.erase(i); - } - } - break; - } - } - return S_OK; - } - - // DesktopToastFailedEventHandler - IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IToastFailedEventArgs *e) { - ToastNotifications::iterator i = toastNotifications.find(_peerId); - if (i != toastNotifications.cend()) { - i.value().remove(_msgId); - if (i.value().isEmpty()) { - toastNotifications.erase(i); - } - } - return S_OK; - } - - // IUnknown - IFACEMETHODIMP_(ULONG) AddRef() { - return InterlockedIncrement(&_ref); - } - - IFACEMETHODIMP_(ULONG) Release() { - ULONG l = InterlockedDecrement(&_ref); - if (l == 0) delete this; - return l; - } - - IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_ void **ppv) { - if (IsEqualIID(riid, IID_IUnknown)) - *ppv = static_cast(static_cast(this)); - else if (IsEqualIID(riid, __uuidof(DesktopToastActivatedEventHandler))) - *ppv = static_cast(this); - else if (IsEqualIID(riid, __uuidof(DesktopToastDismissedEventHandler))) - *ppv = static_cast(this); - else if (IsEqualIID(riid, __uuidof(DesktopToastFailedEventHandler))) - *ppv = static_cast(this); - else *ppv = nullptr; - - if (*ppv) { - reinterpret_cast(*ppv)->AddRef(); - return S_OK; - } - - return E_NOINTERFACE; - } - -private: - - ULONG _ref; - PeerId _peerId; - MsgId _msgId; -}; - -template -_Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { - return roGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); -} - -template -inline HRESULT wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) throw() { - return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); -} - -QString toastImage(const StorageKey &key, PeerData *peer) { - uint64 ms = getms(true); - ToastImages::iterator i = toastImages.find(key); - if (i != toastImages.cend()) { - if (i->until) { - i->until = ms + NotifyDeletePhotoAfter; - if (App::wnd()) App::wnd()->psCleanNotifyPhotosIn(-NotifyDeletePhotoAfter); - } - } else { - ToastImage v; - if (key.first) { - v.until = ms + NotifyDeletePhotoAfter; - if (App::wnd()) App::wnd()->psCleanNotifyPhotosIn(-NotifyDeletePhotoAfter); - } else { - v.until = 0; - } - v.path = cWorkingDir() + qsl("tdata/temp/") + QString::number(rand_value(), 16) + qsl(".png"); - if (key.first || key.second) { - peer->saveUserpic(v.path); - } else { - App::wnd()->iconLarge().save(v.path, "PNG"); - } - i = toastImages.insert(key, v); - ToastImageSavedFlag = true; - } - return i->path; -} - -bool CreateToast(PeerData *peer, int32 msgId, bool showpix, const QString &title, const QString &subtitle, const QString &msg) { - if (!useToast || !toastNotificationManager || !toastNotifier || !toastNotificationFactory) return false; - - ComPtr toastXml; - bool withSubtitle = !subtitle.isEmpty(); - - HRESULT hr = toastNotificationManager->GetTemplateContent(withSubtitle ? ToastTemplateType_ToastImageAndText04 : ToastTemplateType_ToastImageAndText02, &toastXml); - if (!SUCCEEDED(hr)) return false; - - hr = SetAudioSilent(toastXml.Get()); - if (!SUCCEEDED(hr)) return false; - - StorageKey key; - QString imagePath; - if (showpix) { - key = peer->userpicUniqueKey(); - } else { - key = StorageKey(0, 0); - } - QString image = toastImage(key, peer); - std::wstring wimage = QDir::toNativeSeparators(image).toStdWString(); - - hr = SetImageSrc(wimage.c_str(), toastXml.Get()); - if (!SUCCEEDED(hr)) return false; - - ComPtr nodeList; - hr = toastXml->GetElementsByTagName(StringReferenceWrapper(L"text").Get(), &nodeList); - if (!SUCCEEDED(hr)) return false; - - UINT32 nodeListLength; - hr = nodeList->get_Length(&nodeListLength); - if (!SUCCEEDED(hr)) return false; - - if (nodeListLength < (withSubtitle ? 3U : 2U)) return false; - - { - ComPtr textNode; - hr = nodeList->Item(0, &textNode); - if (!SUCCEEDED(hr)) return false; - - std::wstring wtitle = title.toStdWString(); - hr = SetNodeValueString(StringReferenceWrapper(wtitle.data(), wtitle.size()).Get(), textNode.Get(), toastXml.Get()); - if (!SUCCEEDED(hr)) return false; - } - if (withSubtitle) { - ComPtr textNode; - hr = nodeList->Item(1, &textNode); - if (!SUCCEEDED(hr)) return false; - - std::wstring wsubtitle = subtitle.toStdWString(); - hr = SetNodeValueString(StringReferenceWrapper(wsubtitle.data(), wsubtitle.size()).Get(), textNode.Get(), toastXml.Get()); - if (!SUCCEEDED(hr)) return false; - } - { - ComPtr textNode; - hr = nodeList->Item(withSubtitle ? 2 : 1, &textNode); - if (!SUCCEEDED(hr)) return false; - - std::wstring wmsg = msg.toStdWString(); - hr = SetNodeValueString(StringReferenceWrapper(wmsg.data(), wmsg.size()).Get(), textNode.Get(), toastXml.Get()); - if (!SUCCEEDED(hr)) return false; - } - - ComPtr toast; - hr = toastNotificationFactory->CreateToastNotification(toastXml.Get(), &toast); - if (!SUCCEEDED(hr)) return false; - - EventRegistrationToken activatedToken, dismissedToken, failedToken; - ComPtr eventHandler(new ToastEventHandler(peer->id, msgId)); - - hr = toast->add_Activated(eventHandler.Get(), &activatedToken); - if (!SUCCEEDED(hr)) return false; - - hr = toast->add_Dismissed(eventHandler.Get(), &dismissedToken); - if (!SUCCEEDED(hr)) return false; - - hr = toast->add_Failed(eventHandler.Get(), &failedToken); - if (!SUCCEEDED(hr)) return false; - - ToastNotifications::iterator i = toastNotifications.find(peer->id); - if (i != toastNotifications.cend()) { - QMap::iterator j = i->find(msgId); - if (j != i->cend()) { - ComPtr notify = j->p; - i->erase(j); - toastNotifier->Hide(notify.Get()); - i = toastNotifications.find(peer->id); - } - } - if (i == toastNotifications.cend()) { - i = toastNotifications.insert(peer->id, QMap()); - } - hr = toastNotifier->Show(toast.Get()); - if (!SUCCEEDED(hr)) { - i = toastNotifications.find(peer->id); - if (i != toastNotifications.cend() && i->isEmpty()) toastNotifications.erase(i); - return false; - } - toastNotifications[peer->id].insert(msgId, toast); - - return true; -} - -QString pinnedPath() { - static const int maxFileLen = MAX_PATH * 10; - WCHAR wstrPath[maxFileLen]; - if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) { - QDir appData(QString::fromStdWString(std::wstring(wstrPath))); - return appData.absolutePath() + qsl("/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar/"); - } - return QString(); -} - -void CheckPinnedAppUserModelId() { - if (!propVariantToString) return; - - static const int maxFileLen = MAX_PATH * 10; - - HRESULT hr = CoInitialize(0); - if (!SUCCEEDED(hr)) return; - - QString path = pinnedPath(); - std::wstring p = QDir::toNativeSeparators(path).toStdWString(); - - WCHAR src[MAX_PATH]; - GetModuleFileName(GetModuleHandle(0), src, MAX_PATH); - BY_HANDLE_FILE_INFORMATION srcinfo = { 0 }; - HANDLE srcfile = CreateFile(src, 0x00, 0x00, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (srcfile == INVALID_HANDLE_VALUE) return; - BOOL srcres = GetFileInformationByHandle(srcfile, &srcinfo); - CloseHandle(srcfile); - if (!srcres) return; - LOG(("Checking...")); - WIN32_FIND_DATA findData; - HANDLE findHandle = FindFirstFileEx((p + L"*").c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, 0, 0); - if (findHandle == INVALID_HANDLE_VALUE) { - LOG(("Init Error: could not find files in pinned folder")); - return; - } - do { - std::wstring fname = p + findData.cFileName; - LOG(("Checking %1").arg(QString::fromStdWString(fname))); - if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - continue; - } else { - DWORD attributes = GetFileAttributes(fname.c_str()); - if (attributes >= 0xFFFFFFF) continue; // file does not exist - - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (!SUCCEEDED(hr)) continue; - - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (!SUCCEEDED(hr)) continue; - - hr = persistFile->Load(fname.c_str(), STGM_READWRITE); - if (!SUCCEEDED(hr)) continue; - - WCHAR dst[MAX_PATH]; - hr = shellLink->GetPath(dst, MAX_PATH, 0, 0); - if (!SUCCEEDED(hr)) continue; - - BY_HANDLE_FILE_INFORMATION dstinfo = { 0 }; - HANDLE dstfile = CreateFile(dst, 0x00, 0x00, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (dstfile == INVALID_HANDLE_VALUE) continue; - BOOL dstres = GetFileInformationByHandle(dstfile, &dstinfo); - CloseHandle(dstfile); - if (!dstres) continue; - - if (srcinfo.dwVolumeSerialNumber == dstinfo.dwVolumeSerialNumber && srcinfo.nFileIndexLow == dstinfo.nFileIndexLow && srcinfo.nFileIndexHigh == dstinfo.nFileIndexHigh) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (!SUCCEEDED(hr)) return; - - PROPVARIANT appIdPropVar; - hr = propertyStore->GetValue(pkey_AppUserModel_ID, &appIdPropVar); - if (!SUCCEEDED(hr)) return; - LOG(("Reading...")); - WCHAR already[MAX_PATH]; - hr = propVariantToString(appIdPropVar, already, MAX_PATH); - if (SUCCEEDED(hr)) { - if (std::wstring(AppUserModelId()) == already) { - LOG(("Already!")); - PropVariantClear(&appIdPropVar); - return; - } - } - if (appIdPropVar.vt != VT_EMPTY) { - PropVariantClear(&appIdPropVar); - return; - } - PropVariantClear(&appIdPropVar); - - hr = InitPropVariantFromString(AppUserModelId(), &appIdPropVar); - if (!SUCCEEDED(hr)) return; - - hr = propertyStore->SetValue(pkey_AppUserModel_ID, appIdPropVar); - PropVariantClear(&appIdPropVar); - if (!SUCCEEDED(hr)) return; - - hr = propertyStore->Commit(); - if (!SUCCEEDED(hr)) return; - - if (persistFile->IsDirty() == S_OK) { - persistFile->Save(fname.c_str(), TRUE); - } - return; - } - } - } while (FindNextFile(findHandle, &findData)); - DWORD errorCode = GetLastError(); - if (errorCode && errorCode != ERROR_NO_MORE_FILES) { // everything is found - LOG(("Init Error: could not find some files in pinned folder")); - return; - } - FindClose(findHandle); -} - -QString systemShortcutPath() { - static const int maxFileLen = MAX_PATH * 10; - WCHAR wstrPath[maxFileLen]; - if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) { - QDir appData(QString::fromStdWString(std::wstring(wstrPath))); - return appData.absolutePath() + qsl("/Microsoft/Windows/Start Menu/Programs/"); - } - return QString(); -} - -void CleanupAppUserModelIdShortcut() { - static const int maxFileLen = MAX_PATH * 10; - - QString path = systemShortcutPath() + qsl("Telegram.lnk"); - std::wstring p = QDir::toNativeSeparators(path).toStdWString(); - - DWORD attributes = GetFileAttributes(p.c_str()); - if (attributes >= 0xFFFFFFF) return; // file does not exist - - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (!SUCCEEDED(hr)) return; - - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (!SUCCEEDED(hr)) return; - - hr = persistFile->Load(p.c_str(), STGM_READWRITE); - if (!SUCCEEDED(hr)) return; - - WCHAR szGotPath[MAX_PATH]; - WIN32_FIND_DATA wfd; - hr = shellLink->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATA*)&wfd, SLGP_SHORTPATH); - if (!SUCCEEDED(hr)) return; - - if (QDir::toNativeSeparators(cExeDir() + cExeName()).toStdWString() == szGotPath) { - QFile().remove(path); - } -} - -bool ValidateAppUserModelIdShortcutAt(const QString &path) { - static const int maxFileLen = MAX_PATH * 10; - - std::wstring p = QDir::toNativeSeparators(path).toStdWString(); - - DWORD attributes = GetFileAttributes(p.c_str()); - if (attributes >= 0xFFFFFFF) return false; // file does not exist - - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (!SUCCEEDED(hr)) return false; - - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (!SUCCEEDED(hr)) return false; - - hr = persistFile->Load(p.c_str(), STGM_READWRITE); - if (!SUCCEEDED(hr)) return false; - - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (!SUCCEEDED(hr)) return false; - - PROPVARIANT appIdPropVar; - hr = propertyStore->GetValue(pkey_AppUserModel_ID, &appIdPropVar); - if (!SUCCEEDED(hr)) return false; - - WCHAR already[MAX_PATH]; - hr = propVariantToString(appIdPropVar, already, MAX_PATH); - if (SUCCEEDED(hr)) { - if (std::wstring(AppUserModelId()) == already) { - PropVariantClear(&appIdPropVar); - return true; - } - } - if (appIdPropVar.vt != VT_EMPTY) { - PropVariantClear(&appIdPropVar); - return false; - } - PropVariantClear(&appIdPropVar); - - hr = InitPropVariantFromString(AppUserModelId(), &appIdPropVar); - if (!SUCCEEDED(hr)) return false; - - hr = propertyStore->SetValue(pkey_AppUserModel_ID, appIdPropVar); - PropVariantClear(&appIdPropVar); - if (!SUCCEEDED(hr)) return false; - - hr = propertyStore->Commit(); - if (!SUCCEEDED(hr)) return false; - - if (persistFile->IsDirty() == S_OK) { - persistFile->Save(p.c_str(), TRUE); - } - - return true; -} - -bool ValidateAppUserModelIdShortcut() { - if (!useToast) return false; - - QString path = systemShortcutPath(); - if (path.isEmpty()) return false; - - if (cBetaVersion()) { - path += qsl("TelegramBeta.lnk"); - if (ValidateAppUserModelIdShortcutAt(path)) return true; - } else { - if (ValidateAppUserModelIdShortcutAt(path + qsl("Telegram Desktop/Telegram.lnk"))) return true; - if (ValidateAppUserModelIdShortcutAt(path + qsl("Telegram Win (Unofficial)/Telegram.lnk"))) return true; - - path += qsl("Telegram.lnk"); - if (ValidateAppUserModelIdShortcutAt(path)) return true; - } - - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (!SUCCEEDED(hr)) return false; - - hr = shellLink->SetPath(QDir::toNativeSeparators(cExeDir() + cExeName()).toStdWString().c_str()); - if (!SUCCEEDED(hr)) return false; - - hr = shellLink->SetArguments(L""); - if (!SUCCEEDED(hr)) return false; - - hr = shellLink->SetWorkingDirectory(QDir::toNativeSeparators(QDir(cWorkingDir()).absolutePath()).toStdWString().c_str()); - if (!SUCCEEDED(hr)) return false; - - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (!SUCCEEDED(hr)) return false; - - PROPVARIANT appIdPropVar; - hr = InitPropVariantFromString(AppUserModelId(), &appIdPropVar); - if (!SUCCEEDED(hr)) return false; - - hr = propertyStore->SetValue(pkey_AppUserModel_ID, appIdPropVar); - PropVariantClear(&appIdPropVar); - if (!SUCCEEDED(hr)) return false; - - PROPVARIANT startPinPropVar; - hr = InitPropVariantFromUInt32(APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL, &startPinPropVar); - if (!SUCCEEDED(hr)) return false; - - hr = propertyStore->SetValue(pkey_AppUserModel_StartPinOption, startPinPropVar); - PropVariantClear(&startPinPropVar); - if (!SUCCEEDED(hr)) return false; - - hr = propertyStore->Commit(); - if (!SUCCEEDED(hr)) return false; - - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (!SUCCEEDED(hr)) return false; - - hr = persistFile->Save(QDir::toNativeSeparators(path).toStdWString().c_str(), TRUE); - if (!SUCCEEDED(hr)) return false; - - return true; -} - -bool InitToastManager() { - if (!useToast || !ValidateAppUserModelIdShortcut()) return false; - if (!SUCCEEDED(setCurrentProcessExplicitAppUserModelID(AppUserModelId()))) { - return false; - } - if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &toastNotificationManager))) { - return false; - } - if (!SUCCEEDED(toastNotificationManager->CreateToastNotifierWithId(StringReferenceWrapper(AppUserModelId(), wcslen(AppUserModelId())).Get(), &toastNotifier))) { - return false; - } - if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &toastNotificationFactory))) { - return false; - } - QDir().mkpath(cWorkingDir() + qsl("tdata/temp")); - return true; -} - bool psLaunchMaps(const LocationCoords &coords) { return QDesktopServices::openUrl(qsl("bingmaps:?lvl=16&collection=point.%1_%2_Point").arg(coords.lat).arg(coords.lon)); } diff --git a/Telegram/SourceFiles/pspecific_win.h b/Telegram/SourceFiles/pspecific_win.h index e85046568..a5f20bd7b 100644 --- a/Telegram/SourceFiles/pspecific_win.h +++ b/Telegram/SourceFiles/pspecific_win.h @@ -1,4 +1,3 @@ - /* This file is part of Telegram Desktop, the official desktop version of Telegram messaging app, see https://telegram.org @@ -29,92 +28,6 @@ inline QString psServerPrefix() { inline void psCheckLocalSocket(const QString &) { } -class NotifyWindow; - -class PsMainWindow : public QMainWindow { - Q_OBJECT - -public: - PsMainWindow(QWidget *parent = 0); - - int32 psResizeRowWidth() const { - return 0;//st::wndResizeAreaWidth; - } - - void psInitFrameless(); - void psInitSize(); - HWND psHwnd() const; - HMENU psMenu() const; - - void psFirstShow(); - void psInitSysMenu(); - void psUpdateSysMenu(Qt::WindowState state); - void psUpdateMargins(); - void psUpdatedPosition(); - - bool psHandleTitle(); - - void psFlash(); - void psNotifySettingGot(); - - void psUpdateWorkmode(); - - void psRefreshTaskbarIcon(); - - bool psPosInited() const { - return posInited; - } - - void psActivateNotify(NotifyWindow *w); - void psClearNotifies(PeerId peerId = 0); - void psNotifyShown(NotifyWindow *w); - void psPlatformNotify(HistoryItem *item, int32 fwdCount); - - void psUpdateCounter(); - - bool psHasNativeNotifications(); - void psCleanNotifyPhotosIn(int32 dt); - - virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; - - ~PsMainWindow(); - -public slots: - - void psUpdateDelegate(); - void psSavePosition(Qt::WindowState state = Qt::WindowActive); - void psShowTrayMenu(); - - void psCleanNotifyPhotos(); - -protected: - - bool psHasTrayIcon() const { - return trayIcon; - } - - bool posInited; - QSystemTrayIcon *trayIcon; - PopupMenu *trayIconMenu; - QImage icon256, iconbig256; - QIcon wndIcon; - - void psTrayMenuUpdated(); - void psSetupTrayIcon(); - - QTimer psUpdatedPositionTimer; - -private: - HWND ps_hWnd; - HWND ps_tbHider_hWnd; - HMENU ps_menu; - HICON ps_iconBig, ps_iconSmall, ps_iconOverlay; - - SingleTimer ps_cleanNotifyPhotosTimer; - - void psDestroyIcons(); -}; - void psWriteDump(); void psWriteStackTrace(); QString psPrepareCrashDump(const QByteArray &crashdump, QString dumpfile); diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp new file mode 100644 index 000000000..0ae9526ba --- /dev/null +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -0,0 +1,36 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "window/main_window.h" + +namespace Window { + +MainWindow::MainWindow() { +} + +MainWindow::~MainWindow() { +} + +void MainWindow::closeWithoutDestroy() { + hide(); +} + +} // namespace Window diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h new file mode 100644 index 000000000..99413ba2f --- /dev/null +++ b/Telegram/SourceFiles/window/main_window.h @@ -0,0 +1,36 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +namespace Window { + +class MainWindow : public QMainWindow { +public: + MainWindow(); + + virtual ~MainWindow(); + +protected: + virtual void closeWithoutDestroy(); + +}; + +} // namespace Window diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index 98437d307..764a5d330 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -354,6 +354,15 @@ true true + + true + true + true + + + true + true + true true @@ -422,25 +431,6 @@ true true - - true - true - true - - - true - true - true - - - true - true - - - true - true - true - true true @@ -685,6 +675,20 @@ true true + + true + true + true + + + true + true + true + + + true + true + true true @@ -753,20 +757,6 @@ true true - - true - true - true - - - true - true - true - - - true - true - true true @@ -1042,6 +1032,20 @@ true true + + true + true + true + + + true + true + true + + + true + true + true true @@ -1110,20 +1114,6 @@ true true - - true - true - true - - - true - true - true - - - true - true - true true @@ -1266,6 +1256,37 @@ + + true + true + true + + + true + true + true + + + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + + + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + + + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + + + + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + C:\Program Files (x86)\Windows Kits\8.1\Include\winrt;C:\Program Files (x86)\Windows Kits\8.1\Include\shared;C:\Program Files (x86)\Windows Kits\8.1\Include\um;%(AdditionalIncludeDirectories) + @@ -1345,6 +1366,7 @@ + @@ -1657,6 +1679,64 @@ .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/profile/profile_actions_widget.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing main_window_mac.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/platform/mac/main_window_mac.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing main_window_mac.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/platform/mac/main_window_mac.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing main_window_mac.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/platform/mac/main_window_mac.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" + true + true + true + + + true + true + true + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing main_window_linux.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/platform/linux/main_window_linux.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing main_window_linux.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/platform/linux/main_window_linux.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing main_window_linux.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/platform/linux/main_window_linux.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" + + + + $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing main_window_win.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-IC:\Program Files (x86)\Windows Kits\8.1\Include\winrt" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\shared" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\um" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/platform/win/main_window_win.h" + $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing main_window_win.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-IC:\Program Files (x86)\Windows Kits\8.1\Include\winrt" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\shared" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\um" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include" "-fstdafx.h" "-f../../SourceFiles/platform/win/main_window_win.h" + $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing main_window_win.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-IC:\Program Files (x86)\Windows Kits\8.1\Include\winrt" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\shared" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\um" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/platform/win/main_window_win.h" + + + true + true + true + + + + + @@ -1921,6 +2001,7 @@ .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/window/section_widget.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" + @@ -2493,18 +2574,24 @@ $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing pspecific_win.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-IC:\Program Files (x86)\Windows Kits\8.1\Include\winrt" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\shared" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\um" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include" "-fstdafx.h" "-f../../SourceFiles/pspecific_win.h" - Moc%27ing pspecific_win.h... - Moc%27ing pspecific_win.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-IC:\Program Files (x86)\Windows Kits\8.1\Include\winrt" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\shared" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\um" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/pspecific_win.h" - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-IC:\Program Files (x86)\Windows Kits\8.1\Include\winrt" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\shared" "-IC:\Program Files (x86)\Windows Kits\8.1\Include\um" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/pspecific_win.h" - $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) - $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) - $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) + + + + + + + + + + + + + + + + + + $(QTDIR)\bin\moc.exe;%(FullPath) @@ -2550,35 +2637,47 @@ - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing pspecific_linux.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/pspecific_linux.h" - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing pspecific_linux.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include" "-fstdafx.h" "-f../../SourceFiles/pspecific_linux.h" - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing pspecific_linux.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/pspecific_linux.h" + + + + + + + + + + + + + + + + + + true true true - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing pspecific_mac.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/pspecific_mac.h" - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing pspecific_mac.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl_debug\Debug\include" "-fstdafx.h" "-f../../SourceFiles/pspecific_mac.h" - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing pspecific_mac.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore\5.6.0\QtCore" "-I$(QTDIR)\include\QtGui\5.6.0\QtGui" "-I.\..\..\Libraries\breakpad\src" "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\ThirdParty\minizip" "-I.\..\..\Libraries\openssl\Release\include" "-fstdafx.h" "-f../../SourceFiles/pspecific_mac.h" + + + + + + + + + + + + + + + + + + true true true @@ -2706,6 +2805,11 @@ + + true + true + true + true true diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index f683c4582..ec92871bb 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -97,6 +97,21 @@ {b761f2a4-0e8c-4e52-b179-7a3185c046c4} + + {a2724cb3-028c-43a8-ad3d-5176b63c7998} + + + {64f57f23-7eb4-4949-8fc8-4a5db87a31c8} + + + {c3de8600-519e-489c-8b25-14137dda87c6} + + + {920a21f5-22df-47da-bffd-de23e6b4ede1} + + + {385d4cd5-f702-41b7-9e39-707d16b118d5} + @@ -498,27 +513,9 @@ SourceFiles - - GeneratedFiles\Deploy - - - GeneratedFiles\Debug - - - GeneratedFiles\Release - SourceFiles - - GeneratedFiles\Deploy - - - GeneratedFiles\Debug - - - GeneratedFiles\Release - GeneratedFiles\Deploy @@ -681,15 +678,6 @@ SourceFiles\intro - - GeneratedFiles\Deploy - - - GeneratedFiles\Debug - - - GeneratedFiles\Release - SourceFiles @@ -699,9 +687,6 @@ GeneratedFiles\Deploy - - GeneratedFiles\Debug - GeneratedFiles\Release @@ -1296,6 +1281,54 @@ SourceFiles\data + + GeneratedFiles\Deploy + + + GeneratedFiles\Debug + + + GeneratedFiles\Release + + + SourceFiles\platform\win + + + SourceFiles\window + + + SourceFiles\platform\win + + + SourceFiles\platform\win + + + SourceFiles\platform\win + + + SourceFiles\platform\win + + + SourceFiles\platform\winrt + + + GeneratedFiles\Deploy + + + GeneratedFiles\Release + + + SourceFiles\platform\linux + + + GeneratedFiles\Deploy + + + GeneratedFiles\Debug + + + GeneratedFiles\Release + @@ -1526,6 +1559,24 @@ SourceFiles\data + + SourceFiles\platform + + + SourceFiles\platform\win + + + SourceFiles\platform\win + + + SourceFiles\platform\win + + + SourceFiles\platform\win + + + SourceFiles\platform\winrt + @@ -1801,6 +1852,18 @@ SourceFiles\boxes + + SourceFiles\platform\win + + + SourceFiles\window + + + SourceFiles\platform\mac + + + SourceFiles\platform\linux + @@ -1851,6 +1914,9 @@ Resources\langs + + SourceFiles\platform\mac +