From fd91893b51443c3de5baf13d15e474c80e4990e8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 16 Jun 2016 15:59:54 +0300 Subject: [PATCH 01/24] Moved PsMainWindow to Platform::MainWindow, outside of pspecific module. Currently only MSVC build is Ok, Xcode and QtCreator are broken. --- Telegram/SourceFiles/core/basic_types.cpp | 4 +- Telegram/SourceFiles/main.cpp | 6 +- Telegram/SourceFiles/mainwindow.cpp | 4 +- Telegram/SourceFiles/mainwindow.h | 5 +- .../platform/linux/main_window_linux.cpp | 439 ++++ .../platform/linux/main_window_linux.h | 110 + .../platform/mac/main_window_mac.h | 140 + .../platform/mac/main_window_mac.mm | 1110 ++++++++ .../platform/platform_main_window.h | 31 + .../platform/win/main_window_win.cpp | 1099 ++++++++ .../platform/win/main_window_win.h | 157 ++ .../win/windows_app_user_model_id.cpp | 331 +++ .../platform/win/windows_app_user_model_id.h | 37 + .../SourceFiles/platform/win/windows_dlls.cpp | 76 + .../SourceFiles/platform/win/windows_dlls.h | 90 + .../platform/win/windows_event_filter.cpp | 260 ++ .../platform/win/windows_event_filter.h | 51 + .../platform/win/windows_toasts.cpp | 554 ++++ .../SourceFiles/platform/win/windows_toasts.h | 38 + .../platform/winrt/main_window_winrt.cpp | 29 + .../platform/winrt/main_window_winrt.h | 31 + Telegram/SourceFiles/pspecific.h | 29 +- Telegram/SourceFiles/pspecific_linux.cpp | 462 +--- Telegram/SourceFiles/pspecific_linux.h | 83 - Telegram/SourceFiles/pspecific_mac.cpp | 41 +- Telegram/SourceFiles/pspecific_mac.h | 111 - Telegram/SourceFiles/pspecific_win.cpp | 2244 +---------------- Telegram/SourceFiles/pspecific_win.h | 87 - Telegram/SourceFiles/window/main_window.cpp | 36 + Telegram/SourceFiles/window/main_window.h | 36 + Telegram/Telegram.vcxproj | 270 +- Telegram/Telegram.vcxproj.filters | 126 +- 32 files changed, 5081 insertions(+), 3046 deletions(-) create mode 100644 Telegram/SourceFiles/platform/linux/main_window_linux.cpp create mode 100644 Telegram/SourceFiles/platform/linux/main_window_linux.h create mode 100644 Telegram/SourceFiles/platform/mac/main_window_mac.h create mode 100644 Telegram/SourceFiles/platform/mac/main_window_mac.mm create mode 100644 Telegram/SourceFiles/platform/platform_main_window.h create mode 100644 Telegram/SourceFiles/platform/win/main_window_win.cpp create mode 100644 Telegram/SourceFiles/platform/win/main_window_win.h create mode 100644 Telegram/SourceFiles/platform/win/windows_app_user_model_id.cpp create mode 100644 Telegram/SourceFiles/platform/win/windows_app_user_model_id.h create mode 100644 Telegram/SourceFiles/platform/win/windows_dlls.cpp create mode 100644 Telegram/SourceFiles/platform/win/windows_dlls.h create mode 100644 Telegram/SourceFiles/platform/win/windows_event_filter.cpp create mode 100644 Telegram/SourceFiles/platform/win/windows_event_filter.h create mode 100644 Telegram/SourceFiles/platform/win/windows_toasts.cpp create mode 100644 Telegram/SourceFiles/platform/win/windows_toasts.h create mode 100644 Telegram/SourceFiles/platform/winrt/main_window_winrt.cpp create mode 100644 Telegram/SourceFiles/platform/winrt/main_window_winrt.h create mode 100644 Telegram/SourceFiles/window/main_window.cpp create mode 100644 Telegram/SourceFiles/window/main_window.h 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 + From 8e78cfed85016b3bae64c9938e8a20fb03545994 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 16 Jun 2016 18:17:39 +0300 Subject: [PATCH 02/24] Fixed Xcode build. When hiding fullscreen window first exit fullscreen. --- Telegram/SourceFiles/facades.cpp | 2 +- Telegram/SourceFiles/mainwindow.cpp | 8 +- Telegram/SourceFiles/mainwindow.h | 6 +- .../platform/mac/main_window_mac.h | 35 +- .../platform/mac/main_window_mac.mm | 1416 +++++------------ Telegram/SourceFiles/pspecific_mac.cpp | 455 ------ Telegram/SourceFiles/pspecific_mac_p.mm | 2 +- Telegram/SourceFiles/ui/scrollarea.h | 22 +- Telegram/SourceFiles/window/main_window.h | 5 +- Telegram/Telegram.xcodeproj/project.pbxproj | 86 + Telegram/Telegram.xcodeproj/qt_preprocess.mak | 12 +- 11 files changed, 551 insertions(+), 1498 deletions(-) diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 21ab5d2af..03ba4065d 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -276,7 +276,7 @@ bool hideWindowNoQuit() { if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { return w->minimizeToTray(); } else if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) { - w->hide(); + w->closeWithoutDestroy(); w->updateIsActive(Global::OfflineBlurTimeout()); w->updateGlobalMenu(); return true; diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index aa0e4d4bf..c4952508e 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -417,7 +417,9 @@ void MainWindow::onInactiveTimer() { inactivePress(false); } -void MainWindow::stateChanged(Qt::WindowState state) { +void MainWindow::onStateChanged(Qt::WindowState state) { + stateChangedHook(state); + psUserActionDone(); updateIsActive((state == Qt::WindowMinimized) ? Global::OfflineBlurTimeout() : Global::OnlineFocusTimeout()); @@ -1069,7 +1071,7 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) { case QEvent::WindowStateChange: if (obj == this) { Qt::WindowState state = (windowState() & Qt::WindowMinimized) ? Qt::WindowMinimized : ((windowState() & Qt::WindowMaximized) ? Qt::WindowMaximized : ((windowState() & Qt::WindowFullScreen) ? Qt::WindowFullScreen : Qt::WindowNoState)); - stateChanged(state); + onStateChanged(state); } break; @@ -1107,7 +1109,7 @@ void MainWindow::mouseReleaseEvent(QMouseEvent *e) { bool MainWindow::minimizeToTray() { if (App::quitting() || !psHasTrayIcon()) return false; - hide(); + closeWithoutDestroy(); if (cPlatform() == dbipWindows && trayIcon && !cSeenTrayTooltip()) { trayIcon->showMessage(str_const_toString(AppName), lang(lng_tray_icon_text), QSystemTrayIcon::Information, 10000); cSetSeenTrayTooltip(true); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index a599bf061..5a7826c61 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -249,9 +249,7 @@ public: PeerData *ui_getPeerForMouseAction(); public slots: - void updateIsActive(int timeout = 0); - void stateChanged(Qt::WindowState state); void checkHistoryActivation(); @@ -289,7 +287,6 @@ public slots: void app_activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button); signals: - void resized(const QSize &size); void tempDirCleared(int task); void tempDirClearFailed(int task); @@ -297,6 +294,9 @@ signals: void imageLoaded(); +private slots: + void onStateChanged(Qt::WindowState state); + private: QPixmap grabInner(); diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index a2fe866c1..2bbe76dc4 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -75,7 +75,7 @@ public: void psNotifyShown(NotifyWindow *w); void psPlatformNotify(HistoryItem *item, int32 fwdCount); - bool eventFilter(QObject *obj, QEvent *evt); + bool eventFilter(QObject *obj, QEvent *evt) override; void psUpdateCounter(); @@ -85,10 +85,11 @@ public: virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; + void closeWithoutDestroy() override; + ~MainWindow(); public slots: - void psUpdateDelegate(); void psSavePosition(Qt::WindowState state = Qt::WindowActive); void psShowTrayMenu(); @@ -101,9 +102,12 @@ public slots: void psMacDelete(); void psMacSelectAll(); -protected: +private slots: + void onHideAfterFullScreen(); + +protected: + void stateChangedHook(Qt::WindowState state) override; - void psNotIdle() const; QImage psTrayIcon(bool selected = false) const; bool psHasTrayIcon() const { return trayIcon; @@ -112,8 +116,8 @@ protected: void psMacUpdateMenu(); bool posInited; - QSystemTrayIcon *trayIcon; - QMenu *trayIconMenu; + QSystemTrayIcon *trayIcon = nullptr; + QMenu *trayIconMenu = nullptr; QImage icon256, iconbig256; QIcon wndIcon; @@ -126,14 +130,27 @@ protected: QTimer psUpdatedPositionTimer; private: - struct PrivateData; - std_::unique_ptr _private; + MacPrivate _private; mutable bool psIdle; mutable QTimer psIdleTimer; + QTimer _hideAfterFullScreenTimer; + QMenuBar psMainMenu; - QAction *psLogout, *psUndo, *psRedo, *psCut, *psCopy, *psPaste, *psDelete, *psSelectAll, *psContacts, *psAddContact, *psNewGroup, *psNewChannel, *psShowTelegram; + QAction *psLogout = nullptr; + QAction *psUndo = nullptr; + QAction *psRedo = nullptr; + QAction *psCut = nullptr; + QAction *psCopy = nullptr; + QAction *psPaste = nullptr; + QAction *psDelete = nullptr; + QAction *psSelectAll = nullptr; + QAction *psContacts = nullptr; + QAction *psAddContact = nullptr; + QAction *psNewGroup = nullptr; + QAction *psNewChannel = nullptr; + QAction *psShowTelegram = nullptr; }; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index 3dc9e5ee1..d493c9cf7 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -16,12 +16,14 @@ 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 "platform/mac/main_window_mac.h" #include "mainwindow.h" #include "mainwidget.h" #include "application.h" #include "playerwidget.h" +#include "historywidget.h" +#include "localstorage.h" #include "lang.h" @@ -31,1080 +33,478 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include -@interface qVisualize : NSObject { -} +namespace Platform { -+ (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 MacPrivate::activeSpaceChanged() { + if (App::wnd()) { + App::wnd()->notifyActivateAll(); } } -void objc_bringToBack(WId winId) { - NSWindow *wnd = [reinterpret_cast(winId) window]; - [wnd setLevel:NSModalPanelWindowLevel]; +void MacPrivate::darkModeChanged() { + Notify::unreadCounterUpdated(); } -void objc_activateWnd(WId winId) { - NSWindow *wnd = [reinterpret_cast(winId) window]; - [wnd orderFront:wnd]; -} +void MacPrivate::notifyClicked(unsigned long long peer, int msgid) { + History *history = App::history(PeerId(peer)); -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]]; + App::wnd()->showFromTray(); + if (App::passcoded()) { + App::wnd()->setInnerFocus(); + App::wnd()->notifyClear(); } else { - objc_openFile(objcString(toOpen), true); + App::wnd()->hideSettings(); + bool tomsg = !history->peer->isUser() && (msgid > 0); + if (tomsg) { + HistoryItem *item = App::histItemById(peerToChannel(PeerId(peer)), MsgId(msgid)); + if (!item || !item->mentionsMe()) { + tomsg = false; + } + } + Ui::showPeerHistory(history, tomsg ? msgid : ShowAtUnreadMsgId); + App::wnd()->notifyClear(history); } } -- (void) dealloc { - if (apps) [apps release]; - [super dealloc]; - if (menu) [menu release]; +void MacPrivate::notifyReplied(unsigned long long peer, int msgid, const char *str) { + History *history = App::history(PeerId(peer)); + + MainWidget::MessageToSend message; + message.history = history; + message.textWithTags = { QString::fromUtf8(str), TextWithTags::Tags() }; + message.replyTo = (msgid > 0 && !history->peer->isUser()) ? msgid : 0; + message.silent = false; + message.clearDraft = false; + App::main()->sendMessage(message); } -@end +MainWindow::MainWindow() +: posInited(false) +, icon256(qsl(":/gui/art/icon256.png")) +, iconbig256(qsl(":/gui/art/iconbig256.png")) +, wndIcon(QPixmap::fromImage(iconbig256, Qt::ColorOnly)) { + QImage tray(qsl(":/gui/art/osxtray.png")); + trayImg = tray.copy(0, cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4)); + trayImgSel = tray.copy(tray.width() / (cRetina() ? 2 : 4), cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4)); -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]; + _hideAfterFullScreenTimer.setSingleShot(true); + connect(&_hideAfterFullScreenTimer, SIGNAL(timeout()), this, SLOT(onHideAfterFullScreen())); +} + +void MainWindow::closeWithoutDestroy() { + NSWindow *nsWindow = [reinterpret_cast(winId()) window]; + bool isFullScreen = (([nsWindow styleMask] & NSFullScreenWindowMask) == NSFullScreenWindowMask); + if (isFullScreen) { + _hideAfterFullScreenTimer.start(3000); + [nsWindow toggleFullScreen:nsWindow]; + } else { + hide(); } - @catch (NSException *exception) { +} + +void MainWindow::stateChangedHook(Qt::WindowState state) { + if (_hideAfterFullScreenTimer.isActive()) { + _hideAfterFullScreenTimer.stop(); + QTimer::singleShot(0, this, SLOT(onHideAfterFullScreen())); } - @finally { +} + +void MainWindow::onHideAfterFullScreen() { + hide(); +} + +QImage MainWindow::psTrayIcon(bool selected) const { + return selected ? trayImgSel : trayImg; +} + +void MainWindow::psShowTrayMenu() { +} + +void MainWindow::psRefreshTaskbarIcon() { +} + +void MainWindow::psTrayMenuUpdated() { +} + +void MainWindow::psSetupTrayIcon() { + if (!trayIcon) { + trayIcon = new QSystemTrayIcon(this); + + QIcon icon(QPixmap::fromImage(psTrayIcon(), Qt::ColorOnly)); + icon.addPixmap(QPixmap::fromImage(psTrayIcon(true), Qt::ColorOnly), QIcon::Selected); + + trayIcon->setIcon(icon); + trayIcon->setToolTip(str_const_toString(AppName)); + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(toggleTray(QSystemTrayIcon::ActivationReason)), Qt::UniqueConnection); + App::wnd()->updateTrayMenu(); } - return false; + psUpdateCounter(); + + trayIcon->show(); } -void objc_showInFinder(const QString &file, const QString &path) { - [[NSWorkspace sharedWorkspace] selectFile:QNSString(file).s() inFileViewerRootedAtPath:QNSString(path).s()]; +void MainWindow::psUpdateWorkmode() { + psSetupTrayIcon(); + if (cWorkMode() == dbiwmWindowOnly) { + if (trayIcon) { + trayIcon->setContextMenu(0); + delete trayIcon; + } + trayIcon = 0; + } + psUpdateDelegate(); + setWindowIcon(wndIcon); } -@interface NSURL(CompareUrls) +void _placeCounter(QImage &img, int size, int count, style::color bg, style::color color) { + if (!count) return; -- (BOOL) isEquivalent:(NSURL *)aURL; + QPainter p(&img); + QString cnt = (count < 100) ? QString("%1").arg(count) : QString("..%1").arg(count % 100, 2, 10, QChar('0')); + int32 cntSize = cnt.size(); -@end + p.setBrush(bg->b); + p.setPen(Qt::NoPen); + p.setRenderHint(QPainter::Antialiasing); + int32 fontSize, skip; + if (size == 22) { + skip = 1; + fontSize = 8; + } else { + skip = 2; + fontSize = 16; + } + style::font f(fontSize, 0, 0); + int32 w = f->width(cnt), d, r; + if (size == 22) { + d = (cntSize < 2) ? 3 : 2; + r = (cntSize < 2) ? 6 : 5; + } else { + d = (cntSize < 2) ? 6 : 5; + r = (cntSize < 2) ? 9 : 11; + } + p.drawRoundedRect(QRect(size - w - d * 2 - skip, size - f->height - skip, w + d * 2, f->height), r, r); -@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; + p.setCompositionMode(QPainter::CompositionMode_Source); + p.setFont(f->f); + p.setPen(color->p); + p.drawText(size - w - d - skip, size - f->height + f->ascent - skip, cnt); } -@end +void MainWindow::psUpdateCounter() { + int32 counter = App::histories().unreadBadge(); -@interface ChooseApplicationDelegate : NSObject { + setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); + setWindowIcon(wndIcon); + + QString cnt = (counter < 1000) ? QString("%1").arg(counter) : QString("..%1").arg(counter % 100, 2, 10, QChar('0')); + _private.setWindowBadge(counter ? cnt : QString()); + + if (trayIcon) { + bool muted = App::histories().unreadOnlyMuted(); + bool dm = objc_darkMode(); + + style::color bg = muted ? st::counterMuteBG : st::counterBG; + QIcon icon; + QImage img(psTrayIcon(dm)), imgsel(psTrayIcon(true)); + img.detach(); + imgsel.detach(); + int32 size = cRetina() ? 44 : 22; + _placeCounter(img, size, counter, bg, (dm && muted) ? st::counterMacInvColor : st::counterColor); + _placeCounter(imgsel, size, counter, st::white, st::counterMacInvColor); + icon.addPixmap(QPixmap::fromImage(img, Qt::ColorOnly)); + icon.addPixmap(QPixmap::fromImage(imgsel, Qt::ColorOnly), QIcon::Selected); + trayIcon->setIcon(icon); + } } -- (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; +void MainWindow::psUpdateDelegate() { + _private.updateDelegate(); } -- (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; +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); } -- (BOOL) isRecommended:(NSURL *)url { - if (apps) { - for (id app in apps) { - if ([(NSURL*)app isEquivalent:url]) { - return YES; - } - } - } - return NO; +void MainWindow::psInitFrameless() { + psUpdatedPositionTimer.setSingleShot(true); + connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); } -- (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 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) 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 MainWindow::psUpdatedPosition() { + psUpdatedPositionTimer.start(SaveWindowPositionTimeout); } -- (void) menuDidClose { - onlyRecommended = [[[selector selectedItem] title] isEqualToString:recom]; - [self refreshPanelTable]; -} +void MainWindow::psFirstShow() { + psUpdateMargins(); -- (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; - } - } - } + bool showShadows = true; - return NO; -} + 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(); + } -- (void) refreshPanelTable { - [self refreshDataInViews:[[panel contentView] subviews]]; - [panel validateVisibleColumns]; -} + posInited = true; -- (void) dealloc { - if (apps) { - [apps release]; - [recom release]; - } - [super dealloc]; -} + // init global menu + QMenu *main = psMainMenu.addMenu(qsl("Telegram")); + main->addAction(lng_mac_menu_about_telegram(lt_telegram, qsl("Telegram")), App::wnd()->getTitle(), SLOT(onAbout()))->setMenuRole(QAction::AboutQtRole); + main->addSeparator(); + QAction *prefs = main->addAction(lang(lng_mac_menu_preferences), App::wnd(), SLOT(showSettings()), QKeySequence(Qt::ControlModifier | Qt::Key_Comma)); + prefs->setMenuRole(QAction::PreferencesRole); -@end + QMenu *file = psMainMenu.addMenu(lang(lng_mac_menu_file)); + psLogout = file->addAction(lang(lng_mac_menu_logout), App::wnd(), SLOT(onLogout())); -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); + QMenu *edit = psMainMenu.addMenu(lang(lng_mac_menu_edit)); + psUndo = edit->addAction(lang(lng_mac_menu_undo), this, SLOT(psMacUndo()), QKeySequence::Undo); + psRedo = edit->addAction(lang(lng_mac_menu_redo), this, SLOT(psMacRedo()), QKeySequence::Redo); + edit->addSeparator(); + psCut = edit->addAction(lang(lng_mac_menu_cut), this, SLOT(psMacCut()), QKeySequence::Cut); + psCopy = edit->addAction(lang(lng_mac_menu_copy), this, SLOT(psMacCopy()), QKeySequence::Copy); + psPaste = edit->addAction(lang(lng_mac_menu_paste), this, SLOT(psMacPaste()), QKeySequence::Paste); + psDelete = edit->addAction(lang(lng_mac_menu_delete), this, SLOT(psMacDelete()), QKeySequence(Qt::ControlModifier | Qt::Key_Backspace)); + edit->addSeparator(); + psSelectAll = edit->addAction(lang(lng_mac_menu_select_all), this, SLOT(psMacSelectAll()), QKeySequence::SelectAll); - NSOpenPanel *openPanel = [NSOpenPanel openPanel]; + QMenu *window = psMainMenu.addMenu(lang(lng_mac_menu_window)); + psContacts = window->addAction(lang(lng_mac_menu_contacts), App::wnd()->getTitle(), SLOT(onContacts())); + psAddContact = window->addAction(lang(lng_mac_menu_add_contact), App::wnd(), SLOT(onShowAddContact())); + window->addSeparator(); + psNewGroup = window->addAction(lang(lng_mac_menu_new_group), App::wnd(), SLOT(onShowNewGroup())); + psNewChannel = window->addAction(lang(lng_mac_menu_new_channel), App::wnd(), SLOT(onShowNewChannel())); + window->addSeparator(); + psShowTelegram = window->addAction(lang(lng_mac_menu_show), App::wnd(), SLOT(showFromTray())); - 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]; + psMacUpdateMenu(); } 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; + void _sendKeySequence(Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier) { + QWidget *focused = QApplication::focusWidget(); + if (qobject_cast(focused) || qobject_cast(focused) || qobject_cast(focused)) { + QApplication::postEvent(focused, new QKeyEvent(QEvent::KeyPress, key, modifiers)); + QApplication::postEvent(focused, new QKeyEvent(QEvent::KeyRelease, key, modifiers)); } } - @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; + void _forceDisabled(QAction *action, bool disabled) { + if (action->isEnabled()) { + if (disabled) action->setDisabled(true); + } else if (!disabled) { + action->setDisabled(false); } } +} + +void MainWindow::psMacUndo() { + _sendKeySequence(Qt::Key_Z, Qt::ControlModifier); +} + +void MainWindow::psMacRedo() { + _sendKeySequence(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier); +} + +void MainWindow::psMacCut() { + _sendKeySequence(Qt::Key_X, Qt::ControlModifier); +} + +void MainWindow::psMacCopy() { + _sendKeySequence(Qt::Key_C, Qt::ControlModifier); +} + +void MainWindow::psMacPaste() { + _sendKeySequence(Qt::Key_V, Qt::ControlModifier); +} + +void MainWindow::psMacDelete() { + _sendKeySequence(Qt::Key_Delete); +} + +void MainWindow::psMacSelectAll() { + _sendKeySequence(Qt::Key_A, Qt::ControlModifier); +} + +bool MainWindow::psHandleTitle() { return false; } -void objc_deleteDir(const QString &dir) { - [[NSFileManager defaultManager] removeItemAtPath:QNSString(dir).s() error:nil]; +void MainWindow::psInitSysMenu() { } -double objc_appkitVersion() { - return NSAppKitVersionNumber; +void MainWindow::psUpdateSysMenu(Qt::WindowState state) { } -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) + '/'; +void MainWindow::psUpdateMargins() { +} + +void MainWindow::psMacUpdateMenu() { + if (!posInited) return; + + QWidget *focused = QApplication::focusWidget(); + bool isLogged = !!App::self(), canUndo = false, canRedo = false, canCut = false, canCopy = false, canPaste = false, canDelete = false, canSelectAll = false; + if (QLineEdit *edit = qobject_cast(focused)) { + canCut = canCopy = canDelete = edit->hasSelectedText(); + canSelectAll = !edit->text().isEmpty(); + canUndo = edit->isUndoAvailable(); + canRedo = edit->isRedoAvailable(); + canPaste = !Application::clipboard()->text().isEmpty(); + } else if (FlatTextarea *edit = qobject_cast(focused)) { + canCut = canCopy = canDelete = edit->textCursor().hasSelection(); + canSelectAll = !edit->isEmpty(); + canUndo = edit->isUndoAvailable(); + canRedo = edit->isRedoAvailable(); + canPaste = !Application::clipboard()->text().isEmpty(); + } else if (HistoryInner *list = qobject_cast(focused)) { + canCopy = list->canCopySelected(); + canDelete = list->canDeleteSelected(); } - return QString(); + _forceDisabled(psLogout, !isLogged && !App::passcoded()); + _forceDisabled(psUndo, !canUndo); + _forceDisabled(psRedo, !canRedo); + _forceDisabled(psCut, !canCut); + _forceDisabled(psCopy, !canCopy); + _forceDisabled(psPaste, !canPaste); + _forceDisabled(psDelete, !canDelete); + _forceDisabled(psSelectAll, !canSelectAll); + _forceDisabled(psContacts, !isLogged || App::passcoded()); + _forceDisabled(psAddContact, !isLogged || App::passcoded()); + _forceDisabled(psNewGroup, !isLogged || App::passcoded()); + _forceDisabled(psNewChannel, !isLogged || App::passcoded()); + _forceDisabled(psShowTelegram, App::wnd()->isActive(false)); } -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) + '/'; +void MainWindow::psFlash() { + _private.startBounce(); +} + +void MainWindow::psClearNotifies(PeerId peerId) { + _private.clearNotifies(peerId); +} + +void MainWindow::psActivateNotify(NotifyWindow *w) { + objc_activateWnd(w->winId()); +} + +bool MainWindow::psFilterNativeEvent(void *event) { + return _private.filterNativeEvent(event); +} + +void MainWindow::psNotifyShown(NotifyWindow *w) { + w->hide(); + objc_holdOnTop(w->winId()); + w->show(); + psShowOverAll(w, false); +} + +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(); + QPixmap pix = (!App::passcoded() && cNotifyView() <= dbinvShowName) ? item->history()->peer->genUserpic(st::notifyMacPhotoSize) : QPixmap(); + QString msg = (!App::passcoded() && cNotifyView() <= dbinvShowPreview) ? (fwdCount < 2 ? item->notificationText() : lng_forward_messages(lt_count, fwdCount)) : lang(lng_notification_preview); + + bool withReply = !App::passcoded() && (cNotifyView() <= dbinvShowPreview) && item->history()->peer->canWrite(); + + _private.showNotify(item->history()->peer->id, item->id, pix, title, subtitle, msg, withReply); +} + +bool MainWindow::eventFilter(QObject *obj, QEvent *evt) { + QEvent::Type t = evt->type(); + if (t == QEvent::FocusIn || t == QEvent::FocusOut) { + if (qobject_cast(obj) || qobject_cast(obj) || qobject_cast(obj)) { + psMacUpdateMenu(); + } } - return QString(); + return Window::MainWindow::eventFilter(obj, evt); } -QString objc_currentCountry() { - NSLocale *currentLocale = [NSLocale currentLocale]; // get the current locale. - NSString *countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; - return countryCode ? objcString(countryCode) : QString(); +MainWindow::~MainWindow() { } -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() { -} +} // namespace diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp index 59a3ff7f1..dc7761b14 100644 --- a/Telegram/SourceFiles/pspecific_mac.cpp +++ b/Telegram/SourceFiles/pspecific_mac.cpp @@ -31,9 +31,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace { QStringList _initLogs; - bool frameless = true; - bool finished = true; - class _PsEventFilter : public QAbstractNativeEventFilter { public: _PsEventFilter() { @@ -50,430 +47,6 @@ namespace { }; -void MacPrivate::activeSpaceChanged() { - if (App::wnd()) { - App::wnd()->notifyActivateAll(); - } -} - -void MacPrivate::darkModeChanged() { - Notify::unreadCounterUpdated(); -} - -void MacPrivate::notifyClicked(unsigned long long peer, int msgid) { - History *history = App::history(PeerId(peer)); - - 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(peer)), MsgId(msgid)); - if (!item || !item->mentionsMe()) { - tomsg = false; - } - } - Ui::showPeerHistory(history, tomsg ? msgid : ShowAtUnreadMsgId); - App::wnd()->notifyClear(history); - } -} - -void MacPrivate::notifyReplied(unsigned long long peer, int msgid, const char *str) { - History *history = App::history(PeerId(peer)); - - MainWidget::MessageToSend message; - message.history = history; - message.textWithTags = { QString::fromUtf8(str), TextWithTags::Tags() }; - message.replyTo = (msgid > 0 && !history->peer->isUser()) ? msgid : 0; - message.silent = false; - message.clearDraft = false; - App::main()->sendMessage(message); -} - -PsMainWindow::PsMainWindow(QWidget *parent) : QMainWindow(parent), -posInited(false), trayIcon(0), trayIconMenu(0), icon256(qsl(":/gui/art/icon256.png")), iconbig256(qsl(":/gui/art/iconbig256.png")), wndIcon(QPixmap::fromImage(iconbig256, Qt::ColorOnly)), -psLogout(0), psUndo(0), psRedo(0), psCut(0), psCopy(0), psPaste(0), psDelete(0), psSelectAll(0), psContacts(0), psAddContact(0), psNewGroup(0), psNewChannel(0), psShowTelegram(0) { - QImage tray(qsl(":/gui/art/osxtray.png")); - trayImg = tray.copy(0, cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4)); - trayImgSel = tray.copy(tray.width() / (cRetina() ? 2 : 4), cRetina() ? 0 : tray.width() / 2, tray.width() / (cRetina() ? 2 : 4), tray.width() / (cRetina() ? 2 : 4)); -} - -QImage PsMainWindow::psTrayIcon(bool selected) const { - return selected ? trayImgSel : trayImg; -} - -void PsMainWindow::psShowTrayMenu() { -} - -void PsMainWindow::psRefreshTaskbarIcon() { -} - -void PsMainWindow::psTrayMenuUpdated() { -} - -void PsMainWindow::psSetupTrayIcon() { - if (!trayIcon) { - trayIcon = new QSystemTrayIcon(this); - - QIcon icon(QPixmap::fromImage(psTrayIcon(), Qt::ColorOnly)); - icon.addPixmap(QPixmap::fromImage(psTrayIcon(true), Qt::ColorOnly), QIcon::Selected); - - trayIcon->setIcon(icon); - trayIcon->setToolTip(str_const_toString(AppName)); - connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(toggleTray(QSystemTrayIcon::ActivationReason)), Qt::UniqueConnection); - App::wnd()->updateTrayMenu(); - } - psUpdateCounter(); - - trayIcon->show(); -} - -void PsMainWindow::psUpdateWorkmode() { - psSetupTrayIcon(); - if (cWorkMode() == dbiwmWindowOnly) { - if (trayIcon) { - trayIcon->setContextMenu(0); - delete trayIcon; - } - trayIcon = 0; - } - psUpdateDelegate(); - setWindowIcon(wndIcon); -} - -void _placeCounter(QImage &img, int size, int count, style::color bg, style::color color) { - if (!count) return; - - QPainter p(&img); - QString cnt = (count < 100) ? QString("%1").arg(count) : QString("..%1").arg(count % 100, 2, 10, QChar('0')); - int32 cntSize = cnt.size(); - - p.setBrush(bg->b); - p.setPen(Qt::NoPen); - p.setRenderHint(QPainter::Antialiasing); - int32 fontSize, skip; - if (size == 22) { - skip = 1; - fontSize = 8; - } else { - skip = 2; - fontSize = 16; - } - style::font f(fontSize, 0, 0); - int32 w = f->width(cnt), d, r; - if (size == 22) { - d = (cntSize < 2) ? 3 : 2; - r = (cntSize < 2) ? 6 : 5; - } else { - d = (cntSize < 2) ? 6 : 5; - r = (cntSize < 2) ? 9 : 11; - } - p.drawRoundedRect(QRect(size - w - d * 2 - skip, size - f->height - skip, w + d * 2, f->height), r, r); - - p.setCompositionMode(QPainter::CompositionMode_Source); - p.setFont(f->f); - p.setPen(color->p); - p.drawText(size - w - d - skip, size - f->height + f->ascent - skip, cnt); -} - -void PsMainWindow::psUpdateCounter() { - int32 counter = App::histories().unreadBadge(); - - setWindowTitle((counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram")); - setWindowIcon(wndIcon); - - QString cnt = (counter < 1000) ? QString("%1").arg(counter) : QString("..%1").arg(counter % 100, 2, 10, QChar('0')); - _private.setWindowBadge(counter ? cnt : QString()); - - if (trayIcon) { - bool muted = App::histories().unreadOnlyMuted(); - bool dm = objc_darkMode(); - - style::color bg = muted ? st::counterMuteBG : st::counterBG; - QIcon icon; - QImage img(psTrayIcon(dm)), imgsel(psTrayIcon(true)); - img.detach(); - imgsel.detach(); - int32 size = cRetina() ? 44 : 22; - _placeCounter(img, size, counter, bg, (dm && muted) ? st::counterMacInvColor : st::counterColor); - _placeCounter(imgsel, size, counter, st::white, st::counterMacInvColor); - icon.addPixmap(QPixmap::fromImage(img, Qt::ColorOnly)); - icon.addPixmap(QPixmap::fromImage(imgsel, Qt::ColorOnly), QIcon::Selected); - trayIcon->setIcon(icon); - } -} - -void PsMainWindow::psUpdateDelegate() { - _private.updateDelegate(); -} - -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::psFirstShow() { - 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; - - // init global menu - QMenu *main = psMainMenu.addMenu(qsl("Telegram")); - main->addAction(lng_mac_menu_about_telegram(lt_telegram, qsl("Telegram")), App::wnd()->getTitle(), SLOT(onAbout()))->setMenuRole(QAction::AboutQtRole); - main->addSeparator(); - QAction *prefs = main->addAction(lang(lng_mac_menu_preferences), App::wnd(), SLOT(showSettings()), QKeySequence(Qt::ControlModifier | Qt::Key_Comma)); - prefs->setMenuRole(QAction::PreferencesRole); - - QMenu *file = psMainMenu.addMenu(lang(lng_mac_menu_file)); - psLogout = file->addAction(lang(lng_mac_menu_logout), App::wnd(), SLOT(onLogout())); - - QMenu *edit = psMainMenu.addMenu(lang(lng_mac_menu_edit)); - psUndo = edit->addAction(lang(lng_mac_menu_undo), this, SLOT(psMacUndo()), QKeySequence::Undo); - psRedo = edit->addAction(lang(lng_mac_menu_redo), this, SLOT(psMacRedo()), QKeySequence::Redo); - edit->addSeparator(); - psCut = edit->addAction(lang(lng_mac_menu_cut), this, SLOT(psMacCut()), QKeySequence::Cut); - psCopy = edit->addAction(lang(lng_mac_menu_copy), this, SLOT(psMacCopy()), QKeySequence::Copy); - psPaste = edit->addAction(lang(lng_mac_menu_paste), this, SLOT(psMacPaste()), QKeySequence::Paste); - psDelete = edit->addAction(lang(lng_mac_menu_delete), this, SLOT(psMacDelete()), QKeySequence(Qt::ControlModifier | Qt::Key_Backspace)); - edit->addSeparator(); - psSelectAll = edit->addAction(lang(lng_mac_menu_select_all), this, SLOT(psMacSelectAll()), QKeySequence::SelectAll); - - QMenu *window = psMainMenu.addMenu(lang(lng_mac_menu_window)); - psContacts = window->addAction(lang(lng_mac_menu_contacts), App::wnd()->getTitle(), SLOT(onContacts())); - psAddContact = window->addAction(lang(lng_mac_menu_add_contact), App::wnd(), SLOT(onShowAddContact())); - window->addSeparator(); - psNewGroup = window->addAction(lang(lng_mac_menu_new_group), App::wnd(), SLOT(onShowNewGroup())); - psNewChannel = window->addAction(lang(lng_mac_menu_new_channel), App::wnd(), SLOT(onShowNewChannel())); - window->addSeparator(); - psShowTelegram = window->addAction(lang(lng_mac_menu_show), App::wnd(), SLOT(showFromTray())); - - psMacUpdateMenu(); -} - -namespace { - void _sendKeySequence(Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier) { - QWidget *focused = QApplication::focusWidget(); - if (qobject_cast(focused) || qobject_cast(focused) || qobject_cast(focused)) { - QApplication::postEvent(focused, new QKeyEvent(QEvent::KeyPress, key, modifiers)); - QApplication::postEvent(focused, new QKeyEvent(QEvent::KeyRelease, key, modifiers)); - } - } - void _forceDisabled(QAction *action, bool disabled) { - if (action->isEnabled()) { - if (disabled) action->setDisabled(true); - } else if (!disabled) { - action->setDisabled(false); - } - } -} - -void PsMainWindow::psMacUndo() { - _sendKeySequence(Qt::Key_Z, Qt::ControlModifier); -} - -void PsMainWindow::psMacRedo() { - _sendKeySequence(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier); -} - -void PsMainWindow::psMacCut() { - _sendKeySequence(Qt::Key_X, Qt::ControlModifier); -} - -void PsMainWindow::psMacCopy() { - _sendKeySequence(Qt::Key_C, Qt::ControlModifier); -} - -void PsMainWindow::psMacPaste() { - _sendKeySequence(Qt::Key_V, Qt::ControlModifier); -} - -void PsMainWindow::psMacDelete() { - _sendKeySequence(Qt::Key_Delete); -} - -void PsMainWindow::psMacSelectAll() { - _sendKeySequence(Qt::Key_A, Qt::ControlModifier); -} - -bool PsMainWindow::psHandleTitle() { - return false; -} - -void PsMainWindow::psInitSysMenu() { -} - -void PsMainWindow::psUpdateSysMenu(Qt::WindowState state) { -} - -void PsMainWindow::psUpdateMargins() { -} - -void PsMainWindow::psMacUpdateMenu() { - if (!posInited) return; - - QWidget *focused = QApplication::focusWidget(); - bool isLogged = !!App::self(), canUndo = false, canRedo = false, canCut = false, canCopy = false, canPaste = false, canDelete = false, canSelectAll = false; - if (QLineEdit *edit = qobject_cast(focused)) { - canCut = canCopy = canDelete = edit->hasSelectedText(); - canSelectAll = !edit->text().isEmpty(); - canUndo = edit->isUndoAvailable(); - canRedo = edit->isRedoAvailable(); - canPaste = !Application::clipboard()->text().isEmpty(); - } else if (FlatTextarea *edit = qobject_cast(focused)) { - canCut = canCopy = canDelete = edit->textCursor().hasSelection(); - canSelectAll = !edit->isEmpty(); - canUndo = edit->isUndoAvailable(); - canRedo = edit->isRedoAvailable(); - canPaste = !Application::clipboard()->text().isEmpty(); - } else if (HistoryInner *list = qobject_cast(focused)) { - canCopy = list->canCopySelected(); - canDelete = list->canDeleteSelected(); - } - _forceDisabled(psLogout, !isLogged && !App::passcoded()); - _forceDisabled(psUndo, !canUndo); - _forceDisabled(psRedo, !canRedo); - _forceDisabled(psCut, !canCut); - _forceDisabled(psCopy, !canCopy); - _forceDisabled(psPaste, !canPaste); - _forceDisabled(psDelete, !canDelete); - _forceDisabled(psSelectAll, !canSelectAll); - _forceDisabled(psContacts, !isLogged || App::passcoded()); - _forceDisabled(psAddContact, !isLogged || App::passcoded()); - _forceDisabled(psNewGroup, !isLogged || App::passcoded()); - _forceDisabled(psNewChannel, !isLogged || App::passcoded()); - _forceDisabled(psShowTelegram, App::wnd()->isActive(false)); -} - -void PsMainWindow::psFlash() { - _private.startBounce(); -} - -PsMainWindow::~PsMainWindow() { - finished = true; -} - -void PsMainWindow::psClearNotifies(PeerId peerId) { - _private.clearNotifies(peerId); -} - -void PsMainWindow::psActivateNotify(NotifyWindow *w) { - objc_activateWnd(w->winId()); -} - -bool PsMainWindow::psFilterNativeEvent(void *event) { - return _private.filterNativeEvent(event); -} - namespace { QRect _monitorRect; uint64 _monitorLastGot = 0; @@ -496,34 +69,6 @@ void psBringToBack(QWidget *w) { objc_bringToBack(w->winId()); } -void PsMainWindow::psNotifyShown(NotifyWindow *w) { - w->hide(); - objc_holdOnTop(w->winId()); - w->show(); - psShowOverAll(w, false); -} - -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(); - QPixmap pix = (!App::passcoded() && cNotifyView() <= dbinvShowName) ? item->history()->peer->genUserpic(st::notifyMacPhotoSize) : QPixmap(); - QString msg = (!App::passcoded() && cNotifyView() <= dbinvShowPreview) ? (fwdCount < 2 ? item->notificationText() : lng_forward_messages(lt_count, fwdCount)) : lang(lng_notification_preview); - - bool withReply = !App::passcoded() && (cNotifyView() <= dbinvShowPreview) && item->history()->peer->canWrite(); - - _private.showNotify(item->history()->peer->id, item->id, pix, title, subtitle, msg, withReply); -} - -bool PsMainWindow::eventFilter(QObject *obj, QEvent *evt) { - QEvent::Type t = evt->type(); - if (t == QEvent::FocusIn || t == QEvent::FocusOut) { - if (qobject_cast(obj) || qobject_cast(obj) || qobject_cast(obj)) { - psMacUpdateMenu(); - } - } - return QMainWindow::eventFilter(obj, evt); -} - QAbstractNativeEventFilter *psNativeEventFilter() { delete _psEventFilter; _psEventFilter = new _PsEventFilter(); diff --git a/Telegram/SourceFiles/pspecific_mac_p.mm b/Telegram/SourceFiles/pspecific_mac_p.mm index 3dc9e5ee1..f60aa9644 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.mm +++ b/Telegram/SourceFiles/pspecific_mac_p.mm @@ -255,7 +255,7 @@ void objc_holdOnTop(WId winId) { 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"] ); + BOOL darkModeOn = (style && [style isKindOfClass:[NSString class]] && NSOrderedSame == [style caseInsensitiveCompare:@"dark"]); return darkModeOn ? true : false; } diff --git a/Telegram/SourceFiles/ui/scrollarea.h b/Telegram/SourceFiles/ui/scrollarea.h index 06ee0ccf5..1552cf5ad 100644 --- a/Telegram/SourceFiles/ui/scrollarea.h +++ b/Telegram/SourceFiles/ui/scrollarea.h @@ -166,15 +166,6 @@ public: ScrollArea(QWidget *parent, const style::flatScroll &st = st::scrollDef, bool handleTouch = true); - bool viewportEvent(QEvent *e); - void touchEvent(QTouchEvent *e); - - bool eventFilter(QObject *obj, QEvent *e); - - void resizeEvent(QResizeEvent *e); - void moveEvent(QMoveEvent *e); - void keyPressEvent(QKeyEvent *e); - int scrollWidth() const; int scrollHeight() const; int scrollLeftMax() const; @@ -190,13 +181,22 @@ public: void updateColors(const style::color &bar, const style::color &bg, const style::color &barOver, const style::color &bgOver); - bool focusNextPrevChild(bool next); + bool focusNextPrevChild(bool next) override; void setMovingByScrollBar(bool movingByScrollBar); + bool viewportEvent(QEvent *e) override; + void keyPressEvent(QKeyEvent *e) override; + ~ScrollArea(); protected: + bool eventFilter(QObject *obj, QEvent *e) override; + + void resizeEvent(QResizeEvent *e) override; + void moveEvent(QMoveEvent *e) override; + void touchEvent(QTouchEvent *e); + void enterEventHook(QEvent *e); void leaveEventHook(QEvent *e); @@ -223,7 +223,7 @@ signals: protected: - void scrollContentsBy(int dx, int dy); + void scrollContentsBy(int dx, int dy) override; private: diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index 99413ba2f..ae15bd5f9 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -26,10 +26,13 @@ class MainWindow : public QMainWindow { public: MainWindow(); + virtual void closeWithoutDestroy(); + virtual ~MainWindow(); protected: - virtual void closeWithoutDestroy(); + virtual void stateChangedHook(Qt::WindowState state) { + } }; diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index 3540524c0..9eb17fbb6 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -181,6 +181,9 @@ 07DE92AA1AA4928200A18F6F /* moc_autolockbox.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07DE92A91AA4928200A18F6F /* moc_autolockbox.cpp */; }; 07DE92AD1AA4928B00A18F6F /* moc_passcodebox.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07DE92AB1AA4928B00A18F6F /* moc_passcodebox.cpp */; }; 07DE92AE1AA4928B00A18F6F /* moc_passcodewidget.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07DE92AC1AA4928B00A18F6F /* moc_passcodewidget.cpp */; }; + 07E1B1911D12DB3F00722BC7 /* main_window_mac.mm in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07E1B1901D12DB3F00722BC7 /* main_window_mac.mm */; }; + 07E1B1931D12DED700722BC7 /* moc_main_window_mac.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07E1B1921D12DED700722BC7 /* moc_main_window_mac.cpp */; }; + 07E1B1961D12DFD200722BC7 /* main_window.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07E1B1941D12DFD200722BC7 /* main_window.cpp */; }; 07E373941CBBC11000934F77 /* peer_avatar_button.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 07E373921CBBC11000934F77 /* peer_avatar_button.cpp */; }; 0CB7DE9A54CC9BF86FB7B5CA /* facade.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = 6D50D70712776D7ED3B00E5C /* facade.cpp */; settings = {ATTRIBUTES = (); }; }; 0F7872E39EA570249D420912 /* moc_introwidget.cpp in Compile Sources */ = {isa = PBXBuildFile; fileRef = A37C7E516201B0264A4CDA38 /* moc_introwidget.cpp */; settings = {ATTRIBUTES = (); }; }; @@ -631,6 +634,26 @@ 07DE92A91AA4928200A18F6F /* moc_autolockbox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_autolockbox.cpp; path = GeneratedFiles/Debug/moc_autolockbox.cpp; sourceTree = SOURCE_ROOT; }; 07DE92AB1AA4928B00A18F6F /* moc_passcodebox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_passcodebox.cpp; path = GeneratedFiles/Debug/moc_passcodebox.cpp; sourceTree = SOURCE_ROOT; }; 07DE92AC1AA4928B00A18F6F /* moc_passcodewidget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_passcodewidget.cpp; path = GeneratedFiles/Debug/moc_passcodewidget.cpp; sourceTree = SOURCE_ROOT; }; + 07E1B1781D12DAF100722BC7 /* platform_main_window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = platform_main_window.h; path = SourceFiles/platform/platform_main_window.h; sourceTree = SOURCE_ROOT; }; + 07E1B1791D12DB0700722BC7 /* main_window_win.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main_window_win.cpp; path = SourceFiles/platform/win/main_window_win.cpp; sourceTree = SOURCE_ROOT; }; + 07E1B17A1D12DB0700722BC7 /* main_window_win.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = main_window_win.h; path = SourceFiles/platform/win/main_window_win.h; sourceTree = SOURCE_ROOT; }; + 07E1B17B1D12DB0700722BC7 /* windows_app_user_model_id.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = windows_app_user_model_id.cpp; path = SourceFiles/platform/win/windows_app_user_model_id.cpp; sourceTree = SOURCE_ROOT; }; + 07E1B17C1D12DB0700722BC7 /* windows_app_user_model_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = windows_app_user_model_id.h; path = SourceFiles/platform/win/windows_app_user_model_id.h; sourceTree = SOURCE_ROOT; }; + 07E1B17D1D12DB0700722BC7 /* windows_dlls.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = windows_dlls.cpp; path = SourceFiles/platform/win/windows_dlls.cpp; sourceTree = SOURCE_ROOT; }; + 07E1B17E1D12DB0700722BC7 /* windows_dlls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = windows_dlls.h; path = SourceFiles/platform/win/windows_dlls.h; sourceTree = SOURCE_ROOT; }; + 07E1B17F1D12DB0700722BC7 /* windows_event_filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = windows_event_filter.cpp; path = SourceFiles/platform/win/windows_event_filter.cpp; sourceTree = SOURCE_ROOT; }; + 07E1B1801D12DB0700722BC7 /* windows_event_filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = windows_event_filter.h; path = SourceFiles/platform/win/windows_event_filter.h; sourceTree = SOURCE_ROOT; }; + 07E1B1811D12DB0700722BC7 /* windows_toasts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = windows_toasts.cpp; path = SourceFiles/platform/win/windows_toasts.cpp; sourceTree = SOURCE_ROOT; }; + 07E1B1821D12DB0700722BC7 /* windows_toasts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = windows_toasts.h; path = SourceFiles/platform/win/windows_toasts.h; sourceTree = SOURCE_ROOT; }; + 07E1B1891D12DB2900722BC7 /* main_window_winrt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main_window_winrt.cpp; path = SourceFiles/platform/winrt/main_window_winrt.cpp; sourceTree = SOURCE_ROOT; }; + 07E1B18A1D12DB2900722BC7 /* main_window_winrt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = main_window_winrt.h; path = SourceFiles/platform/winrt/main_window_winrt.h; sourceTree = SOURCE_ROOT; }; + 07E1B18C1D12DB3500722BC7 /* main_window_linux.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main_window_linux.cpp; path = SourceFiles/platform/linux/main_window_linux.cpp; sourceTree = SOURCE_ROOT; }; + 07E1B18D1D12DB3500722BC7 /* main_window_linux.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = main_window_linux.h; path = SourceFiles/platform/linux/main_window_linux.h; sourceTree = SOURCE_ROOT; }; + 07E1B18F1D12DB3F00722BC7 /* main_window_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = main_window_mac.h; path = SourceFiles/platform/mac/main_window_mac.h; sourceTree = SOURCE_ROOT; }; + 07E1B1901D12DB3F00722BC7 /* main_window_mac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = main_window_mac.mm; path = SourceFiles/platform/mac/main_window_mac.mm; sourceTree = SOURCE_ROOT; }; + 07E1B1921D12DED700722BC7 /* moc_main_window_mac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = moc_main_window_mac.cpp; path = GeneratedFiles/Debug/moc_main_window_mac.cpp; sourceTree = SOURCE_ROOT; }; + 07E1B1941D12DFD200722BC7 /* main_window.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main_window.cpp; path = SourceFiles/window/main_window.cpp; sourceTree = SOURCE_ROOT; }; + 07E1B1951D12DFD200722BC7 /* main_window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = main_window.h; path = SourceFiles/window/main_window.h; sourceTree = SOURCE_ROOT; }; 07E373921CBBC11000934F77 /* peer_avatar_button.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = peer_avatar_button.cpp; path = SourceFiles/ui/buttons/peer_avatar_button.cpp; sourceTree = SOURCE_ROOT; }; 07E373931CBBC11000934F77 /* peer_avatar_button.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = peer_avatar_button.h; path = SourceFiles/ui/buttons/peer_avatar_button.h; sourceTree = SOURCE_ROOT; }; 08A7682548FB7E671FF03822 /* boxshadow.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = boxshadow.cpp; path = SourceFiles/ui/boxshadow.cpp; sourceTree = ""; }; @@ -1076,6 +1099,8 @@ 076B1C581CBFC8DF002C0BC2 /* window */ = { isa = PBXGroup; children = ( + 07E1B1941D12DFD200722BC7 /* main_window.cpp */, + 07E1B1951D12DFD200722BC7 /* main_window.h */, 0716C97B1D058F2400797B22 /* section_memento.h */, 0716C97C1D058F2400797B22 /* section_widget.cpp */, 0716C97D1D058F2400797B22 /* section_widget.h */, @@ -1176,6 +1201,62 @@ name = toast; sourceTree = ""; }; + 07E1B1731D12DAC000722BC7 /* platform */ = { + isa = PBXGroup; + children = ( + 07E1B1751D12DACB00722BC7 /* linux */, + 07E1B1761D12DAD100722BC7 /* mac */, + 07E1B1771D12DAD700722BC7 /* win */, + 07E1B1881D12DB1500722BC7 /* winrt */, + 07E1B1781D12DAF100722BC7 /* platform_main_window.h */, + ); + name = platform; + sourceTree = ""; + }; + 07E1B1751D12DACB00722BC7 /* linux */ = { + isa = PBXGroup; + children = ( + 07E1B18C1D12DB3500722BC7 /* main_window_linux.cpp */, + 07E1B18D1D12DB3500722BC7 /* main_window_linux.h */, + ); + name = linux; + sourceTree = ""; + }; + 07E1B1761D12DAD100722BC7 /* mac */ = { + isa = PBXGroup; + children = ( + 07E1B18F1D12DB3F00722BC7 /* main_window_mac.h */, + 07E1B1901D12DB3F00722BC7 /* main_window_mac.mm */, + ); + name = mac; + sourceTree = ""; + }; + 07E1B1771D12DAD700722BC7 /* win */ = { + isa = PBXGroup; + children = ( + 07E1B1791D12DB0700722BC7 /* main_window_win.cpp */, + 07E1B17A1D12DB0700722BC7 /* main_window_win.h */, + 07E1B17B1D12DB0700722BC7 /* windows_app_user_model_id.cpp */, + 07E1B17C1D12DB0700722BC7 /* windows_app_user_model_id.h */, + 07E1B17D1D12DB0700722BC7 /* windows_dlls.cpp */, + 07E1B17E1D12DB0700722BC7 /* windows_dlls.h */, + 07E1B17F1D12DB0700722BC7 /* windows_event_filter.cpp */, + 07E1B1801D12DB0700722BC7 /* windows_event_filter.h */, + 07E1B1811D12DB0700722BC7 /* windows_toasts.cpp */, + 07E1B1821D12DB0700722BC7 /* windows_toasts.h */, + ); + name = win; + sourceTree = ""; + }; + 07E1B1881D12DB1500722BC7 /* winrt */ = { + isa = PBXGroup; + children = ( + 07E1B1891D12DB2900722BC7 /* main_window_winrt.cpp */, + 07E1B18A1D12DB2900722BC7 /* main_window_winrt.h */, + ); + name = winrt; + sourceTree = ""; + }; 07E373901CBBBFDE00934F77 /* buttons */ = { isa = PBXGroup; children = ( @@ -1316,6 +1397,7 @@ 5E35A03E5F2C51353EBCBF00 /* intro */, 1A6AA22F4A758C4B5F5138FB /* mtproto */, 076B1C5C1CBFC97D002C0BC2 /* overview */, + 07E1B1731D12DAC000722BC7 /* platform */, 0716C92C1D05898D00797B22 /* profile */, 0702E99F1CB8D290007A7495 /* serialize */, 579DA7AEF5751DF4988869A0 /* ui */, @@ -1453,6 +1535,7 @@ 1FE45A67215BEA2434F588E8 /* moc_layerwidget.cpp */, 1D7899ACAA9F973CADFA34C1 /* moc_localimageloader.cpp */, 07BE85111A20961F008ACB9F /* moc_localstorage.cpp */, + 07E1B1921D12DED700722BC7 /* moc_main_window_mac.cpp */, 3A220FD1AE5AD9FE3DC073A4 /* moc_mainwidget.cpp */, 6B46A0EE3C3B9D3B5A24946E /* moc_mainwindow.cpp */, 07A6933419927B160099CB9F /* moc_mediaview.cpp */, @@ -1835,6 +1918,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 07E1B1911D12DB3F00722BC7 /* main_window_mac.mm in Compile Sources */, 1299DDAE203A7EDFED9F5D6B /* main.cpp in Compile Sources */, D87463318C8E5211C8C8670A /* stdafx.cpp in Compile Sources */, 7BEFA1D273AD62772AA33D73 /* app.cpp in Compile Sources */, @@ -1872,6 +1956,7 @@ 0716C97A1D058C8600797B22 /* moc_report_box.cpp in Compile Sources */, 077A4AF81CA41C38002188D2 /* connection_auto.cpp in Compile Sources */, 4078D5D614EB3ECF7F1848C7 /* basic_types.cpp in Compile Sources */, + 07E1B1931D12DED700722BC7 /* moc_main_window_mac.cpp in Compile Sources */, 68FFEB7CA30BF0149161B809 /* mainwindow.cpp in Compile Sources */, 0716C9831D05931400797B22 /* moc_section_widget.cpp in Compile Sources */, 0CB7DE9A54CC9BF86FB7B5CA /* facade.cpp in Compile Sources */, @@ -2045,6 +2130,7 @@ 074968D01A44D14C00394F46 /* languagebox.cpp in Compile Sources */, 077A4AF91CA41C38002188D2 /* connection_http.cpp in Compile Sources */, 07BE85121A20961F008ACB9F /* moc_localstorage.cpp in Compile Sources */, + 07E1B1961D12DFD200722BC7 /* main_window.cpp in Compile Sources */, 07AF95F41AFD03B90060B057 /* qrc_telegram_emojis.cpp in Compile Sources */, 07C759721B1F7E2800662169 /* moc_autoupdater.cpp in Compile Sources */, 0716C9601D058C6600797B22 /* style_profile.cpp in Compile Sources */, diff --git a/Telegram/Telegram.xcodeproj/qt_preprocess.mak b/Telegram/Telegram.xcodeproj/qt_preprocess.mak index cbf445390..5d7ef7490 100644 --- a/Telegram/Telegram.xcodeproj/qt_preprocess.mak +++ b/Telegram/Telegram.xcodeproj/qt_preprocess.mak @@ -95,6 +95,7 @@ compilers: GeneratedFiles/qrc_telegram.cpp\ GeneratedFiles/Debug/moc_layerwidget.cpp\ GeneratedFiles/Debug/moc_localimageloader.cpp\ GeneratedFiles/Debug/moc_localstorage.cpp\ + GeneratedFiles/Debug/moc_main_window_mac.cpp\ GeneratedFiles/Debug/moc_mainwidget.cpp\ GeneratedFiles/Debug/moc_mainwindow.cpp\ GeneratedFiles/Debug/moc_mediaview.cpp\ @@ -114,7 +115,6 @@ compilers: GeneratedFiles/qrc_telegram.cpp\ GeneratedFiles/Debug/moc_profile_settings_widget.cpp\ GeneratedFiles/Debug/moc_profile_shared_media_widget.cpp\ GeneratedFiles/Debug/moc_profile_widget.cpp\ - GeneratedFiles/Debug/moc_pspecific_mac.cpp\ GeneratedFiles/Debug/moc_report_box.cpp\ GeneratedFiles/Debug/moc_scrollarea.cpp\ GeneratedFiles/Debug/moc_section_widget.cpp\ @@ -232,6 +232,7 @@ compiler_moc_header_make_all: GeneratedFiles/Debug/moc_aboutbox.cpp\ GeneratedFiles/Debug/moc_layerwidget.cpp\ GeneratedFiles/Debug/moc_localimageloader.cpp\ GeneratedFiles/Debug/moc_localstorage.cpp\ + GeneratedFiles/Debug/moc_main_window_mac.cpp\ GeneratedFiles/Debug/moc_mainwidget.cpp\ GeneratedFiles/Debug/moc_mainwindow.cpp\ GeneratedFiles/Debug/moc_mediaview.cpp\ @@ -251,7 +252,6 @@ compiler_moc_header_make_all: GeneratedFiles/Debug/moc_aboutbox.cpp\ GeneratedFiles/Debug/moc_profile_settings_widget.cpp\ GeneratedFiles/Debug/moc_profile_shared_media_widget.cpp\ GeneratedFiles/Debug/moc_profile_widget.cpp\ - GeneratedFiles/Debug/moc_pspecific_mac.cpp\ GeneratedFiles/Debug/moc_report_box.cpp\ GeneratedFiles/Debug/moc_scrollarea.cpp\ GeneratedFiles/Debug/moc_section_widget.cpp\ @@ -312,6 +312,7 @@ compiler_moc_header_clean: GeneratedFiles/Debug/moc_layerwidget.cpp\ GeneratedFiles/Debug/moc_localimageloader.cpp\ GeneratedFiles/Debug/moc_localstorage.cpp\ + GeneratedFiles/Debug/moc_main_window_mac.cpp\ GeneratedFiles/Debug/moc_mainwidget.cpp\ GeneratedFiles/Debug/moc_mainwindow.cpp\ GeneratedFiles/Debug/moc_mediaview.cpp\ @@ -331,7 +332,6 @@ compiler_moc_header_clean: GeneratedFiles/Debug/moc_profile_settings_widget.cpp\ GeneratedFiles/Debug/moc_profile_shared_media_widget.cpp\ GeneratedFiles/Debug/moc_profile_widget.cpp\ - GeneratedFiles/Debug/moc_pspecific_mac.cpp\ GeneratedFiles/Debug/moc_report_box.cpp\ GeneratedFiles/Debug/moc_scrollarea.cpp\ GeneratedFiles/Debug/moc_section_widget.cpp\ @@ -483,6 +483,9 @@ GeneratedFiles/Debug/moc_localimageloader.cpp: SourceFiles/localimageloader.h GeneratedFiles/Debug/moc_localstorage.cpp: SourceFiles/localstorage.h $(MOC_FILE) SourceFiles/localstorage.h -o GeneratedFiles/Debug/moc_localstorage.cpp +GeneratedFiles/Debug/moc_main_window_mac.cpp: SourceFiles/platform/mac/main_window_mac.h + $(MOC_FILE) SourceFiles/platform/mac/main_window_mac.h -o GeneratedFiles/Debug/moc_main_window_mac.cpp + GeneratedFiles/Debug/moc_mainwidget.cpp: SourceFiles/mainwidget.h $(MOC_FILE) SourceFiles/mainwidget.h -o GeneratedFiles/Debug/moc_mainwidget.cpp @@ -540,9 +543,6 @@ GeneratedFiles/Debug/moc_profile_shared_media_widget.cpp: SourceFiles/profile/pr GeneratedFiles/Debug/moc_profile_widget.cpp: SourceFiles/profile/profile_widget.h $(MOC_FILE) SourceFiles/profile/profile_widget.h -o GeneratedFiles/Debug/moc_profile_widget.cpp -GeneratedFiles/Debug/moc_pspecific_mac.cpp: SourceFiles/pspecific_mac.h - $(MOC_FILE) SourceFiles/pspecific_mac.h -o GeneratedFiles/Debug/moc_pspecific_mac.cpp - GeneratedFiles/Debug/moc_report_box.cpp: SourceFiles/boxes/report_box.h $(MOC_FILE) SourceFiles/boxes/report_box.h -o GeneratedFiles/Debug/moc_report_box.cpp From 7a9dfb50e11b00ed5e4936cd7c596d39a50fe2b1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 16 Jun 2016 20:20:58 +0300 Subject: [PATCH 03/24] Build fixed for QtCreator. Linux libs loading order changed. --- Telegram/SourceFiles/mainwindow.cpp | 2 +- .../SourceFiles/platform/linux/linux_libs.cpp | 184 +++++++ .../SourceFiles/platform/linux/linux_libs.h | 174 +++++++ .../platform/linux/main_window_linux.cpp | 320 +++++++++++-- .../platform/linux/main_window_linux.h | 12 +- Telegram/SourceFiles/pspecific_linux.cpp | 452 +----------------- Telegram/SourceFiles/title.cpp | 6 +- Telegram/SourceFiles/title.h | 2 +- Telegram/Telegram.pro | 34 +- 9 files changed, 672 insertions(+), 514 deletions(-) create mode 100644 Telegram/SourceFiles/platform/linux/linux_libs.cpp create mode 100644 Telegram/SourceFiles/platform/linux/linux_libs.h diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index c4952508e..35ed14b87 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -436,7 +436,7 @@ void MainWindow::init() { setWindowIcon(wndIcon); Application::instance()->installEventFilter(this); - connect(windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(stateChanged(Qt::WindowState))); + connect(windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onStateChanged(Qt::WindowState))); connect(windowHandle(), SIGNAL(activeChanged()), this, SLOT(checkHistoryActivation()), Qt::QueuedConnection); QPalette p(palette()); diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.cpp b/Telegram/SourceFiles/platform/linux/linux_libs.cpp new file mode 100644 index 000000000..2beea27ab --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_libs.cpp @@ -0,0 +1,184 @@ +/* +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/linux_libs.h" + +namespace Platform { +namespace Libs { +namespace { + +bool loadLibrary(QLibrary &lib, const char *name, int version) { + DEBUG_LOG(("Loading '%1' with version %2...").arg(QLatin1String(name)).arg(version)); + lib.setFileNameAndVersion(QLatin1String(name), version); + if (lib.load()) { + DEBUG_LOG(("Loaded '%1' with version %2!").arg(QLatin1String(name)).arg(version)); + return true; + } + lib.setFileNameAndVersion(QLatin1String(name), QString()); + if (lib.load()) { + DEBUG_LOG(("Loaded '%1' without version!").arg(QLatin1String(name))); + return true; + } + LOG(("Could not load '%1' with version %2 :(").arg(QLatin1String(name)).arg(version)); + return false; +} + +bool setupGtkBase(QLibrary &lib_gtk) { + if (!load(lib_gtk, "gtk_init_check", gtk_init_check)) return false; + if (!load(lib_gtk, "gtk_menu_new", gtk_menu_new)) return false; + if (!load(lib_gtk, "gtk_menu_get_type", gtk_menu_get_type)) return false; + + if (!load(lib_gtk, "gtk_menu_item_new_with_label", gtk_menu_item_new_with_label)) return false; + if (!load(lib_gtk, "gtk_menu_item_set_label", gtk_menu_item_set_label)) return false; + if (!load(lib_gtk, "gtk_menu_shell_append", gtk_menu_shell_append)) return false; + if (!load(lib_gtk, "gtk_menu_shell_get_type", gtk_menu_shell_get_type)) return false; + if (!load(lib_gtk, "gtk_widget_show", gtk_widget_show)) return false; + if (!load(lib_gtk, "gtk_widget_get_toplevel", gtk_widget_get_toplevel)) return false; + if (!load(lib_gtk, "gtk_widget_get_visible", gtk_widget_get_visible)) return false; + if (!load(lib_gtk, "gtk_widget_set_sensitive", gtk_widget_set_sensitive)) return false; + + if (!load(lib_gtk, "g_type_check_instance_cast", g_type_check_instance_cast)) return false; + if (!load(lib_gtk, "g_signal_connect_data", g_signal_connect_data)) return false; + + if (!load(lib_gtk, "g_object_ref_sink", g_object_ref_sink)) return false; + if (!load(lib_gtk, "g_object_unref", g_object_unref)) return false; + + DEBUG_LOG(("Library gtk functions loaded!")); + if (!gtk_init_check(0, 0)) { + gtk_init_check = nullptr; + DEBUG_LOG(("Failed to gtk_init_check(0, 0)!")); + return false; + } + + DEBUG_LOG(("Checked gtk with gtk_init_check!")); + return true; +} + +bool setupAppIndicator(QLibrary &lib_indicator) { + if (!load(lib_indicator, "app_indicator_new", app_indicator_new)) return false; + if (!load(lib_indicator, "app_indicator_set_status", app_indicator_set_status)) return false; + if (!load(lib_indicator, "app_indicator_set_menu", app_indicator_set_menu)) return false; + if (!load(lib_indicator, "app_indicator_set_icon_full", app_indicator_set_icon_full)) return false; + + DEBUG_LOG(("Library appindicator functions loaded!")); + return true; +} + +} // namespace + +f_gtk_init_check gtk_init_check = nullptr; +f_gtk_menu_new gtk_menu_new = nullptr; +f_gtk_menu_get_type gtk_menu_get_type = nullptr; +f_gtk_menu_item_new_with_label gtk_menu_item_new_with_label = nullptr; +f_gtk_menu_item_set_label gtk_menu_item_set_label = nullptr; +f_gtk_menu_shell_append gtk_menu_shell_append = nullptr; +f_gtk_menu_shell_get_type gtk_menu_shell_get_type = nullptr; +f_gtk_widget_show gtk_widget_show = nullptr; +f_gtk_widget_get_toplevel gtk_widget_get_toplevel = nullptr; +f_gtk_widget_get_visible gtk_widget_get_visible = nullptr; +f_gtk_widget_set_sensitive gtk_widget_set_sensitive = nullptr; +f_g_type_check_instance_cast g_type_check_instance_cast = nullptr; +f_g_signal_connect_data g_signal_connect_data = nullptr; +f_app_indicator_new app_indicator_new = nullptr; +f_app_indicator_set_status app_indicator_set_status = nullptr; +f_app_indicator_set_menu app_indicator_set_menu = nullptr; +f_app_indicator_set_icon_full app_indicator_set_icon_full = nullptr; +f_gdk_init_check gdk_init_check = nullptr; +f_gdk_pixbuf_new_from_data gdk_pixbuf_new_from_data = nullptr; +f_gtk_status_icon_new_from_pixbuf gtk_status_icon_new_from_pixbuf = nullptr; +f_gtk_status_icon_set_from_pixbuf gtk_status_icon_set_from_pixbuf = nullptr; +f_gtk_status_icon_set_title gtk_status_icon_set_title = nullptr; +f_gtk_status_icon_set_tooltip_text gtk_status_icon_set_tooltip_text = nullptr; +f_gtk_status_icon_set_visible gtk_status_icon_set_visible = nullptr; +f_gtk_status_icon_is_embedded gtk_status_icon_is_embedded = nullptr; +f_gtk_status_icon_get_geometry gtk_status_icon_get_geometry = nullptr; +f_gtk_status_icon_position_menu gtk_status_icon_position_menu = nullptr; +f_gtk_menu_popup gtk_menu_popup = nullptr; +f_gtk_get_current_event_time gtk_get_current_event_time = nullptr; +f_g_object_ref_sink g_object_ref_sink = nullptr; +f_g_object_unref g_object_unref = nullptr; +f_g_idle_add g_idle_add = nullptr; +f_unity_launcher_entry_set_count unity_launcher_entry_set_count = nullptr; +f_unity_launcher_entry_set_count_visible unity_launcher_entry_set_count_visible = nullptr; +f_unity_launcher_entry_get_for_desktop_id unity_launcher_entry_get_for_desktop_id = nullptr; + +void start() { + DEBUG_LOG(("Loading libraries")); + + bool gtkLoaded = false; + bool indicatorLoaded = false; + QLibrary lib_gtk, lib_indicator; + if (loadLibrary(lib_indicator, "appindicator3", 1)) { + if (loadLibrary(lib_gtk, "gtk-3", 0)) { + gtkLoaded = setupGtkBase(lib_gtk); + indicatorLoaded = setupAppIndicator(lib_indicator); + } + } + if (!gtkLoaded || !indicatorLoaded) { + if (loadLibrary(lib_indicator, "appindicator", 1)) { + if (loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) { + gtkLoaded = indicatorLoaded = false; + gtkLoaded = setupGtkBase(lib_gtk); + indicatorLoaded = setupAppIndicator(lib_indicator); + } + } + } + + // If no appindicator, try at least load gtk. + if (!gtkLoaded && !indicatorLoaded) { + if (loadLibrary(lib_gtk, "gtk-3", 0)) { + gtkLoaded = setupGtkBase(lib_gtk); + } + if (!gtkLoaded && loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) { + gtkLoaded = setupGtkBase(lib_gtk); + } + } + + if (gtkLoaded) { + load(lib_gtk, "gdk_init_check", gdk_init_check); + load(lib_gtk, "gdk_pixbuf_new_from_data", gdk_pixbuf_new_from_data); + load(lib_gtk, "gtk_status_icon_new_from_pixbuf", gtk_status_icon_new_from_pixbuf); + load(lib_gtk, "gtk_status_icon_set_from_pixbuf", gtk_status_icon_set_from_pixbuf); + load(lib_gtk, "gtk_status_icon_set_title", gtk_status_icon_set_title); + load(lib_gtk, "gtk_status_icon_set_tooltip_text", gtk_status_icon_set_tooltip_text); + load(lib_gtk, "gtk_status_icon_set_visible", gtk_status_icon_set_visible); + load(lib_gtk, "gtk_status_icon_is_embedded", gtk_status_icon_is_embedded); + load(lib_gtk, "gtk_status_icon_get_geometry", gtk_status_icon_get_geometry); + load(lib_gtk, "gtk_status_icon_position_menu", gtk_status_icon_position_menu); + load(lib_gtk, "gtk_menu_popup", gtk_menu_popup); + load(lib_gtk, "gtk_get_current_event_time", gtk_get_current_event_time); + load(lib_gtk, "g_idle_add", g_idle_add); + } else { + LOG(("Could not load gtk-x11-2.0!")); + } + + if (QString(getenv("XDG_CURRENT_DESKTOP")).toLower() == qstr("unity")) { + QLibrary lib_unity(qstr("unity"), 9, 0); + loadLibrary(lib_unity, "unity", 9); + + load(lib_unity, "unity_launcher_entry_get_for_desktop_id", unity_launcher_entry_get_for_desktop_id); + load(lib_unity, "unity_launcher_entry_set_count", unity_launcher_entry_set_count); + load(lib_unity, "unity_launcher_entry_set_count_visible", unity_launcher_entry_set_count_visible); + } +} + +} // namespace Libs +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.h b/Telegram/SourceFiles/platform/linux/linux_libs.h new file mode 100644 index 000000000..83da7b6a8 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_libs.h @@ -0,0 +1,174 @@ +/* +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 + +extern "C" { +#undef signals +#include +#include +#define signals public +} // extern "C" + +#include + +namespace Platform { +namespace Libs { + +void start(); + +template +bool load(QLibrary &lib, const char *name, Function &func) { + func = nullptr; + if (!lib.isLoaded()) { + return false; + } + + func = reinterpret_cast(lib.resolve(name)); + if (func) { + return true; + } + LOG(("Error: failed to load '%1' function!").arg(name)); + return false; +} + +typedef gboolean (*f_gtk_init_check)(int *argc, char ***argv); +extern f_gtk_init_check gtk_init_check; + +typedef GtkWidget* (*f_gtk_menu_new)(void); +extern f_gtk_menu_new gtk_menu_new; + +typedef GType (*f_gtk_menu_get_type)(void) G_GNUC_CONST; +extern f_gtk_menu_get_type gtk_menu_get_type; + +typedef GtkWidget* (*f_gtk_menu_item_new_with_label)(const gchar *label); +extern f_gtk_menu_item_new_with_label gtk_menu_item_new_with_label; + +typedef void (*f_gtk_menu_item_set_label)(GtkMenuItem *menu_item, const gchar *label); +extern f_gtk_menu_item_set_label gtk_menu_item_set_label; + +typedef void (*f_gtk_menu_shell_append)(GtkMenuShell *menu_shell, GtkWidget *child); +extern f_gtk_menu_shell_append gtk_menu_shell_append; + +typedef GType (*f_gtk_menu_shell_get_type)(void) G_GNUC_CONST; +extern f_gtk_menu_shell_get_type gtk_menu_shell_get_type; + +typedef void (*f_gtk_widget_show)(GtkWidget *widget); +extern f_gtk_widget_show gtk_widget_show; + +typedef GtkWidget* (*f_gtk_widget_get_toplevel)(GtkWidget *widget); +extern f_gtk_widget_get_toplevel gtk_widget_get_toplevel; + +typedef gboolean (*f_gtk_widget_get_visible)(GtkWidget *widget); +extern f_gtk_widget_get_visible gtk_widget_get_visible; + +typedef void (*f_gtk_widget_set_sensitive)(GtkWidget *widget, gboolean sensitive); +extern f_gtk_widget_set_sensitive gtk_widget_set_sensitive; + +typedef GTypeInstance* (*f_g_type_check_instance_cast)(GTypeInstance *instance, GType iface_type); +extern f_g_type_check_instance_cast g_type_check_instance_cast; + +template +inline Result *g_type_cic_helper(Object *instance, GType iface_type) { + return reinterpret_cast(g_type_check_instance_cast(reinterpret_cast(instance), iface_type)); +} +template +inline GtkMenu *gtk_menu_cast(Object *obj) { + return g_type_cic_helper(obj, gtk_menu_get_type()); +} +template +inline GtkMenuShell *gtk_menu_shell_cast(Object *obj) { + return g_type_cic_helper(obj, gtk_menu_get_type()); +} + +typedef gulong (*f_g_signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags); +extern f_g_signal_connect_data g_signal_connect_data; +inline gulong g_signal_connect_helper(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data) { + return g_signal_connect_data(instance, detailed_signal, c_handler, data, NULL, (GConnectFlags)0); +} + +typedef AppIndicator* (*f_app_indicator_new)(const gchar *id, const gchar *icon_name, AppIndicatorCategory category); +extern f_app_indicator_new app_indicator_new; + +typedef void (*f_app_indicator_set_status)(AppIndicator *self, AppIndicatorStatus status); +extern f_app_indicator_set_status app_indicator_set_status; + +typedef void (*f_app_indicator_set_menu)(AppIndicator *self, GtkMenu *menu); +extern f_app_indicator_set_menu app_indicator_set_menu; + +typedef void (*f_app_indicator_set_icon_full)(AppIndicator *self, const gchar *icon_name, const gchar *icon_desc); +extern f_app_indicator_set_icon_full app_indicator_set_icon_full; + +typedef gboolean (*f_gdk_init_check)(gint *argc, gchar ***argv); +extern f_gdk_init_check gdk_init_check; + +typedef GdkPixbuf* (*f_gdk_pixbuf_new_from_data)(const guchar *data, GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height, int rowstride, GdkPixbufDestroyNotify destroy_fn, gpointer destroy_fn_data); +extern f_gdk_pixbuf_new_from_data gdk_pixbuf_new_from_data; + +typedef GtkStatusIcon* (*f_gtk_status_icon_new_from_pixbuf)(GdkPixbuf *pixbuf); +extern f_gtk_status_icon_new_from_pixbuf gtk_status_icon_new_from_pixbuf; + +typedef void (*f_gtk_status_icon_set_from_pixbuf)(GtkStatusIcon *status_icon, GdkPixbuf *pixbuf); +extern f_gtk_status_icon_set_from_pixbuf gtk_status_icon_set_from_pixbuf; + +typedef void (*f_gtk_status_icon_set_title)(GtkStatusIcon *status_icon, const gchar *title); +extern f_gtk_status_icon_set_title gtk_status_icon_set_title; + +typedef void (*f_gtk_status_icon_set_tooltip_text)(GtkStatusIcon *status_icon, const gchar *title); +extern f_gtk_status_icon_set_tooltip_text gtk_status_icon_set_tooltip_text; + +typedef void (*f_gtk_status_icon_set_visible)(GtkStatusIcon *status_icon, gboolean visible); +extern f_gtk_status_icon_set_visible gtk_status_icon_set_visible; + +typedef gboolean (*f_gtk_status_icon_is_embedded)(GtkStatusIcon *status_icon); +extern f_gtk_status_icon_is_embedded gtk_status_icon_is_embedded; + +typedef gboolean (*f_gtk_status_icon_get_geometry)(GtkStatusIcon *status_icon, GdkScreen **screen, GdkRectangle *area, GtkOrientation *orientation); +extern f_gtk_status_icon_get_geometry gtk_status_icon_get_geometry; + +typedef void (*f_gtk_status_icon_position_menu)(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data); +extern f_gtk_status_icon_position_menu gtk_status_icon_position_menu; + +typedef void (*f_gtk_menu_popup)(GtkMenu *menu, GtkWidget *parent_menu_shell, GtkWidget *parent_menu_item, GtkMenuPositionFunc func, gpointer data, guint button, guint32 activate_time); +extern f_gtk_menu_popup gtk_menu_popup; + +typedef guint32 (*f_gtk_get_current_event_time)(void); +extern f_gtk_get_current_event_time gtk_get_current_event_time; + +typedef gpointer (*f_g_object_ref_sink)(gpointer object); +extern f_g_object_ref_sink g_object_ref_sink; + +typedef void (*f_g_object_unref)(gpointer object); +extern f_g_object_unref g_object_unref; + +typedef guint (*f_g_idle_add)(GSourceFunc function, gpointer data); +extern f_g_idle_add g_idle_add; + +typedef void (*f_unity_launcher_entry_set_count)(UnityLauncherEntry* self, gint64 value); +extern f_unity_launcher_entry_set_count unity_launcher_entry_set_count; + +typedef void (*f_unity_launcher_entry_set_count_visible)(UnityLauncherEntry* self, gboolean value); +extern f_unity_launcher_entry_set_count_visible unity_launcher_entry_set_count_visible; + +typedef UnityLauncherEntry* (*f_unity_launcher_entry_get_for_desktop_id)(const gchar* desktop_id); +extern f_unity_launcher_entry_get_for_desktop_id unity_launcher_entry_get_for_desktop_id; + +} // namespace Libs +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index e0172a21b..968854d2d 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "platform/linux/main_window_linux.h" +#include "platform/linux/linux_libs.h" #include "mainwindow.h" #include "application.h" #include "lang.h" @@ -29,10 +30,165 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org namespace Platform { namespace { +bool noQtTrayIcon = false, tryAppIndicator = false; +bool useGtkBase = false, useAppIndicator = false, useStatusIcon = false, trayIconChecked = false, useUnityCount = false; + +AppIndicator *_trayIndicator = 0; +GtkStatusIcon *_trayIcon = 0; +GtkWidget *_trayMenu = 0; +GdkPixbuf *_trayPixbuf = 0; +QByteArray _trayPixbufData; +QList > _trayItems; + +int32 _trayIconSize = 22; +bool _trayIconMuted = true; +int32 _trayIconCount = 0; +QImage _trayIconImageBack, _trayIconImage; + +void _trayIconPopup(GtkStatusIcon *status_icon, guint button, guint32 activate_time, gpointer popup_menu) { + Libs::gtk_menu_popup(Libs::gtk_menu_cast(popup_menu), NULL, NULL, Libs::gtk_status_icon_position_menu, status_icon, button, activate_time); +} + +void _trayIconActivate(GtkStatusIcon *status_icon, gpointer popup_menu) { + if (App::wnd()->isActiveWindow() && App::wnd()->isVisible()) { + Libs::gtk_menu_popup(Libs::gtk_menu_cast(popup_menu), NULL, NULL, Libs::gtk_status_icon_position_menu, status_icon, 0, Libs::gtk_get_current_event_time()); + } else { + App::wnd()->showFromTray(); + } +} + +gboolean _trayIconResized(GtkStatusIcon *status_icon, gint size, gpointer popup_menu) { + _trayIconSize = size; + if (App::wnd()) App::wnd()->psUpdateCounter(); + return FALSE; +} + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + +#define QT_RED 3 +#define QT_GREEN 2 +#define QT_BLUE 1 +#define QT_ALPHA 0 + +#else + +#define QT_RED 0 +#define QT_GREEN 1 +#define QT_BLUE 2 +#define QT_ALPHA 3 + +#endif + +#define GTK_RED 2 +#define GTK_GREEN 1 +#define GTK_BLUE 0 +#define GTK_ALPHA 3 + +QImage _trayIconImageGen() { + int32 counter = App::histories().unreadBadge(), counterSlice = (counter >= 1000) ? (1000 + (counter % 100)) : counter; + bool muted = App::histories().unreadOnlyMuted(); + if (_trayIconImage.isNull() || _trayIconImage.width() != _trayIconSize || muted != _trayIconMuted || counterSlice != _trayIconCount) { + if (_trayIconImageBack.isNull() || _trayIconImageBack.width() != _trayIconSize) { + _trayIconImageBack = App::wnd()->iconLarge().scaled(_trayIconSize, _trayIconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + _trayIconImageBack = _trayIconImageBack.convertToFormat(QImage::Format_ARGB32); + int w = _trayIconImageBack.width(), h = _trayIconImageBack.height(), perline = _trayIconImageBack.bytesPerLine(); + uchar *bytes = _trayIconImageBack.bits(); + for (int32 y = 0; y < h; ++y) { + for (int32 x = 0; x < w; ++x) { + int32 srcoff = y * perline + x * 4; + bytes[srcoff + QT_RED ] = qMax(bytes[srcoff + QT_RED ], uchar(224)); + bytes[srcoff + QT_GREEN] = qMax(bytes[srcoff + QT_GREEN], uchar(165)); + bytes[srcoff + QT_BLUE ] = qMax(bytes[srcoff + QT_BLUE ], uchar(44)); + } + } + } + _trayIconImage = _trayIconImageBack; + if (counter > 0) { + QPainter p(&_trayIconImage); + int32 layerSize = -16; + if (_trayIconSize >= 48) { + layerSize = -32; + } else if (_trayIconSize >= 36) { + layerSize = -24; + } else if (_trayIconSize >= 32) { + layerSize = -20; + } + QImage layer = App::wnd()->iconWithCounter(layerSize, counter, (muted ? st::counterMuteBG : st::counterBG), false); + p.drawImage(_trayIconImage.width() - layer.width() - 1, _trayIconImage.height() - layer.height() - 1, layer); + } + } + return _trayIconImage; +} + +QString _trayIconImageFile() { + int32 counter = App::histories().unreadBadge(), counterSlice = (counter >= 1000) ? (1000 + (counter % 100)) : counter; + bool muted = App::histories().unreadOnlyMuted(); + + QString name = cWorkingDir() + qsl("tdata/ticons/ico%1_%2_%3.png").arg(muted ? "mute" : "").arg(_trayIconSize).arg(counterSlice); + QFileInfo info(name); + if (info.exists()) return name; + + QImage img = _trayIconImageGen(); + if (img.save(name, "PNG")) return name; + + QDir dir(info.absoluteDir()); + if (!dir.exists()) { + dir.mkpath(dir.absolutePath()); + if (img.save(name, "PNG")) return name; + } + + return QString(); +} + +void loadPixbuf(QImage image) { + int w = image.width(), h = image.height(), perline = image.bytesPerLine(), s = image.byteCount(); + _trayPixbufData.resize(w * h * 4); + uchar *result = (uchar*)_trayPixbufData.data(), *bytes = image.bits(); + for (int32 y = 0; y < h; ++y) { + for (int32 x = 0; x < w; ++x) { + int32 offset = (y * w + x) * 4, srcoff = y * perline + x * 4; + result[offset + GTK_RED ] = bytes[srcoff + QT_RED ]; + result[offset + GTK_GREEN] = bytes[srcoff + QT_GREEN]; + result[offset + GTK_BLUE ] = bytes[srcoff + QT_BLUE ]; + result[offset + GTK_ALPHA] = bytes[srcoff + QT_ALPHA]; + } + } + + if (_trayPixbuf) Libs::g_object_unref(_trayPixbuf); + _trayPixbuf = Libs::gdk_pixbuf_new_from_data(result, GDK_COLORSPACE_RGB, true, 8, w, h, w * 4, 0, 0); +} + +void _trayMenuCallback(GtkMenu *menu, gpointer data) { + for (int32 i = 0, l = _trayItems.size(); i < l; ++i) { + if ((void*)_trayItems.at(i).first == (void*)menu) { + QMetaObject::invokeMethod(_trayItems.at(i).second, "triggered"); + } + } +} + +static gboolean _trayIconCheck(gpointer/* pIn*/) { + if (useStatusIcon && !trayIconChecked) { + if (Libs::gtk_status_icon_is_embedded(_trayIcon)) { + trayIconChecked = true; + cSetSupportTray(true); + if (App::wnd()) { + App::wnd()->psUpdateWorkmode(); + App::wnd()->psUpdateCounter(); + App::wnd()->updateTrayMenu(); + } + } + } + return FALSE; +} + +UnityLauncherEntry *_psUnityLauncherEntry = nullptr; + } // 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) { +MainWindow::MainWindow() +: icon256(qsl(":/gui/art/icon256.png")) +, iconbig256(icon256) +, wndIcon(QIcon::fromTheme("telegram", QIcon(QPixmap::fromImage(icon256, Qt::ColorOnly)))) { connect(&_psCheckStatusIconTimer, SIGNAL(timeout()), this, SLOT(psStatusIconCheck())); _psCheckStatusIconTimer.setSingleShot(false); @@ -64,11 +220,11 @@ void MainWindow::psTrayMenuUpdated() { 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()); + GtkWidget *item = Libs::gtk_menu_item_new_with_label(actions.at(i)->text().toUtf8()); + Libs::gtk_menu_shell_append(Libs::gtk_menu_shell_cast(_trayMenu), item); + Libs::g_signal_connect_helper(item, "activate", G_CALLBACK(_trayMenuCallback), this); + Libs::gtk_widget_show(item); + Libs::gtk_widget_set_sensitive(item, actions.at(i)->isEnabled()); _trayItems.push_back(qMakePair(item, actions.at(i))); } @@ -76,8 +232,8 @@ void MainWindow::psTrayMenuUpdated() { 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()); + Libs::gtk_menu_item_set_label(reinterpret_cast(_trayItems.at(i).first), actions.at(i)->text().toUtf8()); + Libs::gtk_widget_set_sensitive(_trayItems.at(i).first, actions.at(i)->isEnabled()); } } } @@ -113,9 +269,9 @@ void MainWindow::psUpdateWorkmode() { if (cWorkMode() == dbiwmWindowOnly) { if (noQtTrayIcon) { if (useAppIndicator) { - ps_app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_PASSIVE); + Libs::app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_PASSIVE); } else if (useStatusIcon) { - ps_gtk_status_icon_set_visible(_trayIcon, false); + Libs::gtk_status_icon_set_visible(_trayIcon, false); } } else { if (trayIcon) { @@ -127,9 +283,9 @@ void MainWindow::psUpdateWorkmode() { } else { if (noQtTrayIcon) { if (useAppIndicator) { - ps_app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_ACTIVE); + Libs::app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_ACTIVE); } else if (useStatusIcon) { - ps_gtk_status_icon_set_visible(_trayIcon, true); + Libs::gtk_status_icon_set_visible(_trayIcon, true); } } else { psSetupTrayIcon(); @@ -144,7 +300,7 @@ void MainWindow::psUpdateIndicator() { 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); + Libs::app_indicator_set_icon_full(_trayIndicator, path.constData(), name); } else { useAppIndicator = false; } @@ -158,10 +314,10 @@ void MainWindow::psUpdateCounter() { 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); + Libs::unity_launcher_entry_set_count(_psUnityLauncherEntry, (counter > 9999) ? 9999 : counter); + Libs::unity_launcher_entry_set_count_visible(_psUnityLauncherEntry, TRUE); } else { - ps_unity_launcher_entry_set_count_visible(_psUnityLauncherEntry, FALSE); + Libs::unity_launcher_entry_set_count_visible(_psUnityLauncherEntry, FALSE); } } @@ -174,7 +330,7 @@ void MainWindow::psUpdateCounter() { } } else if (useStatusIcon && trayIconChecked) { loadPixbuf(_trayIconImageGen()); - ps_gtk_status_icon_set_from_pixbuf(_trayIcon, _trayPixbuf); + Libs::gtk_status_icon_set_from_pixbuf(_trayIcon, _trayPixbuf); } } else if (trayIcon) { int32 counter = App::histories().unreadBadge(); @@ -188,6 +344,65 @@ void MainWindow::psUpdateCounter() { } } +void MainWindow::LibsLoaded() { + QString cdesktop = QString(getenv("XDG_CURRENT_DESKTOP")).toLower(); + noQtTrayIcon = (cdesktop == qstr("pantheon")) || (cdesktop == qstr("gnome")); + tryAppIndicator = (cdesktop == qstr("xfce")); + + if (noQtTrayIcon) cSetSupportTray(false); + + useGtkBase = (Libs::gtk_init_check != nullptr) + && (Libs::gtk_menu_new != nullptr) + && (Libs::gtk_menu_get_type != nullptr) + && (Libs::gtk_menu_item_new_with_label != nullptr) + && (Libs::gtk_menu_item_set_label != nullptr) + && (Libs::gtk_menu_shell_append != nullptr) + && (Libs::gtk_menu_shell_get_type != nullptr) + && (Libs::gtk_widget_show != nullptr) + && (Libs::gtk_widget_get_toplevel != nullptr) + && (Libs::gtk_widget_get_visible != nullptr) + && (Libs::gtk_widget_set_sensitive != nullptr) + && (Libs::g_type_check_instance_cast != nullptr) + && (Libs::g_signal_connect_data != nullptr) + && (Libs::g_object_ref_sink != nullptr) + && (Libs::g_object_unref != nullptr); + + useAppIndicator = useGtkBase + && (Libs::app_indicator_new != nullptr) + && (Libs::app_indicator_set_status != nullptr) + && (Libs::app_indicator_set_menu != nullptr) + && (Libs::app_indicator_set_icon_full != nullptr); + + if (tryAppIndicator && useGtkBase && useAppIndicator) { + noQtTrayIcon = true; + cSetSupportTray(false); + } + + useStatusIcon = (Libs::gdk_init_check != nullptr) + && (Libs::gdk_pixbuf_new_from_data != nullptr) + && (Libs::gtk_status_icon_new_from_pixbuf != nullptr) + && (Libs::gtk_status_icon_set_from_pixbuf != nullptr) + && (Libs::gtk_status_icon_set_title != nullptr) + && (Libs::gtk_status_icon_set_tooltip_text != nullptr) + && (Libs::gtk_status_icon_set_visible != nullptr) + && (Libs::gtk_status_icon_is_embedded != nullptr) + && (Libs::gtk_status_icon_get_geometry != nullptr) + && (Libs::gtk_status_icon_position_menu != nullptr) + && (Libs::gtk_menu_popup != nullptr) + && (Libs::gtk_get_current_event_time != nullptr) + && (Libs::g_idle_add != nullptr); + if (useStatusIcon) { + DEBUG_LOG(("Status icon api loaded!")); + } + + useUnityCount = (Libs::unity_launcher_entry_get_for_desktop_id != nullptr) + && (Libs::unity_launcher_entry_set_count != nullptr) + && (Libs::unity_launcher_entry_set_count_visible != nullptr); + if (useUnityCount) { + DEBUG_LOG(("Unity count api loaded!")); + } +} + void MainWindow::psUpdateDelegate() { } @@ -228,10 +443,6 @@ void MainWindow::psInitSize() { void MainWindow::psInitFrameless() { psUpdatedPositionTimer.setSingleShot(true); connect(&psUpdatedPositionTimer, SIGNAL(timeout()), this, SLOT(psSavePosition())); - - if (frameless) { - //setWindowFlags(Qt::FramelessWindowHint); - } } void MainWindow::psSavePosition(Qt::WindowState state) { @@ -289,13 +500,13 @@ void MainWindow::psCreateTrayIcon() { if (useAppIndicator) { DEBUG_LOG(("Trying to create AppIndicator")); - _trayMenu = ps_gtk_menu_new(); + _trayMenu = Libs::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); + _trayIndicator = Libs::app_indicator_new("Telegram Desktop", path.constData(), APP_INDICATOR_CATEGORY_APPLICATION_STATUS); if (_trayIndicator) { DEBUG_LOG(("Created appindicator!")); } else { @@ -309,8 +520,8 @@ void MainWindow::psCreateTrayIcon() { 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)); + Libs::app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_ACTIVE); + Libs::app_indicator_set_menu(_trayIndicator, Libs::gtk_menu_cast(_trayMenu)); useStatusIcon = false; } else { DEBUG_LOG(("AppIndicator failed!")); @@ -318,19 +529,19 @@ void MainWindow::psCreateTrayIcon() { } } if (useStatusIcon) { - if (ps_gdk_init_check(0, 0)) { - if (!_trayMenu) _trayMenu = ps_gtk_menu_new(); + if (Libs::gdk_init_check(0, 0)) { + if (!_trayMenu) _trayMenu = Libs::gtk_menu_new(); if (_trayMenu) { loadPixbuf(_trayIconImageGen()); - _trayIcon = ps_gtk_status_icon_new_from_pixbuf(_trayPixbuf); + _trayIcon = Libs::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); + Libs::g_signal_connect_helper(_trayIcon, "popup-menu", GCallback(_trayIconPopup), _trayMenu); + Libs::g_signal_connect_helper(_trayIcon, "activate", GCallback(_trayIconActivate), _trayMenu); + Libs::g_signal_connect_helper(_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); + Libs::gtk_status_icon_set_title(_trayIcon, "Telegram Desktop"); + Libs::gtk_status_icon_set_tooltip_text(_trayIcon, "Telegram Desktop"); + Libs::gtk_status_icon_set_visible(_trayIcon, true); } else { useStatusIcon = false; } @@ -343,14 +554,14 @@ void MainWindow::psCreateTrayIcon() { } if (!useStatusIcon && !useAppIndicator) { if (_trayMenu) { - ps_g_object_ref_sink(_trayMenu); - ps_g_object_unref(_trayMenu); + Libs::g_object_ref_sink(_trayMenu); + Libs::g_object_unref(_trayMenu); _trayMenu = 0; } } cSetSupportTray(useAppIndicator); if (useStatusIcon) { - ps_g_idle_add((GSourceFunc)_trayIconCheck, 0); + Libs::g_idle_add((GSourceFunc)_trayIconCheck, 0); _psCheckStatusIconTimer.start(100); } else { psUpdateWorkmode(); @@ -361,11 +572,11 @@ void MainWindow::psFirstShow() { psCreateTrayIcon(); if (useUnityCount) { - _psUnityLauncherEntry = ps_unity_launcher_entry_get_for_desktop_id("telegramdesktop.desktop"); + _psUnityLauncherEntry = Libs::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"); + _psUnityLauncherEntry = Libs::unity_launcher_entry_get_for_desktop_id("Telegram.desktop"); if (_psUnityLauncherEntry) { LOG(("Found Unity Launcher entry Telegram.desktop!")); } else { @@ -376,8 +587,6 @@ void MainWindow::psFirstShow() { LOG(("Not using Unity Launcher count.")); } - finished = false; - psUpdateMargins(); bool showShadows = true; @@ -419,21 +628,32 @@ void MainWindow::psUpdateMargins() { void MainWindow::psFlash() { } +void MainWindow::psActivateNotify(NotifyWindow *w) { +} + +void MainWindow::psClearNotifies(PeerId peerId) { +} + +void MainWindow::psNotifyShown(NotifyWindow *w) { +} + +void MainWindow::psPlatformNotify(HistoryItem *item, int32 fwdCount) { +} + MainWindow::~MainWindow() { if (_trayIcon) { - ps_g_object_unref(_trayIcon); - _trayIcon = 0; + Libs::g_object_unref(_trayIcon); + _trayIcon = nullptr; } if (_trayPixbuf) { - ps_g_object_unref(_trayPixbuf); - _trayPixbuf = 0; + Libs::g_object_unref(_trayPixbuf); + _trayPixbuf = nullptr; } if (_trayMenu) { - ps_g_object_ref_sink(_trayMenu); - ps_g_object_unref(_trayMenu); - _trayMenu = 0; + Libs::g_object_ref_sink(_trayMenu); + Libs::g_object_unref(_trayMenu); + _trayMenu = nullptr; } - finished = true; } } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 946a73939..2526a9c5f 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -71,6 +71,8 @@ public: virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0; + static void LibsLoaded(); + ~MainWindow(); public slots: @@ -86,9 +88,9 @@ protected: bool psHasTrayIcon() const; - bool posInited; - QSystemTrayIcon *trayIcon; - QMenu *trayIconMenu; + bool posInited = false; + QSystemTrayIcon *trayIcon = nullptr; + QMenu *trayIconMenu = nullptr; QImage icon256, iconbig256; QIcon wndIcon; @@ -101,10 +103,10 @@ private: void psCreateTrayIcon(); QTimer _psCheckStatusIconTimer; - int _psCheckStatusIconLeft; + int _psCheckStatusIconLeft = 100; QTimer _psUpdateIndicatorTimer; - uint64 _psLastIndicatorUpdate; + uint64 _psLastIndicatorUpdate = 0; }; } // namespace Platform diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp index bd7ce62ce..f5e880cac 100644 --- a/Telegram/SourceFiles/pspecific_linux.cpp +++ b/Telegram/SourceFiles/pspecific_linux.cpp @@ -18,6 +18,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "pspecific.h" +#include "platform/linux/linux_libs.h" #include "lang.h" #include "application.h" #include "mainwidget.h" @@ -33,14 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include -extern "C" { -#undef signals -#include -#include -#define signals public -} // extern "C" - -#include +using namespace Platform; namespace { QByteArray escapeShell(const QByteArray &str) { @@ -66,422 +60,6 @@ namespace { return result; } - bool frameless = true; - bool finished = true; - bool noQtTrayIcon = false, noTryUnity = false, tryAppIndicator = false; - bool useGtkBase = false, useAppIndicator = false, useStatusIcon = false, trayIconChecked = false, useUnityCount = false; - - AppIndicator *_trayIndicator = 0; - GtkStatusIcon *_trayIcon = 0; - GtkWidget *_trayMenu = 0; - GdkPixbuf *_trayPixbuf = 0; - QByteArray _trayPixbufData; - QList > _trayItems; - - int32 _trayIconSize = 22; - bool _trayIconMuted = true; - int32 _trayIconCount = 0; - QImage _trayIconImageBack, _trayIconImage; - - typedef gboolean (*f_gtk_init_check)(int *argc, char ***argv); - f_gtk_init_check ps_gtk_init_check = 0; - - typedef GtkWidget* (*f_gtk_menu_new)(void); - f_gtk_menu_new ps_gtk_menu_new = 0; - - typedef GType (*f_gtk_menu_get_type)(void) G_GNUC_CONST; - f_gtk_menu_get_type ps_gtk_menu_get_type = 0; - - typedef GtkWidget* (*f_gtk_menu_item_new_with_label)(const gchar *label); - f_gtk_menu_item_new_with_label ps_gtk_menu_item_new_with_label = 0; - - typedef void (*f_gtk_menu_item_set_label)(GtkMenuItem *menu_item, const gchar *label); - f_gtk_menu_item_set_label ps_gtk_menu_item_set_label = 0; - - typedef void (*f_gtk_menu_shell_append)(GtkMenuShell *menu_shell, GtkWidget *child); - f_gtk_menu_shell_append ps_gtk_menu_shell_append = 0; - - typedef GType (*f_gtk_menu_shell_get_type)(void) G_GNUC_CONST; - f_gtk_menu_shell_get_type ps_gtk_menu_shell_get_type = 0; - - typedef void (*f_gtk_widget_show)(GtkWidget *widget); - f_gtk_widget_show ps_gtk_widget_show = 0; - - typedef GtkWidget* (*f_gtk_widget_get_toplevel)(GtkWidget *widget); - f_gtk_widget_get_toplevel ps_gtk_widget_get_toplevel = 0; - - typedef gboolean (*f_gtk_widget_get_visible)(GtkWidget *widget); - f_gtk_widget_get_visible ps_gtk_widget_get_visible = 0; - - typedef void (*f_gtk_widget_set_sensitive)(GtkWidget *widget, gboolean sensitive); - f_gtk_widget_set_sensitive ps_gtk_widget_set_sensitive = 0; - - typedef GTypeInstance* (*f_g_type_check_instance_cast)(GTypeInstance *instance, GType iface_type); - f_g_type_check_instance_cast ps_g_type_check_instance_cast = 0; - -#define _PS_G_TYPE_CIC(ip, gt, ct) ((ct*)ps_g_type_check_instance_cast((GTypeInstance*) ip, gt)) -#define PS_G_TYPE_CHECK_INSTANCE_CAST(instance, g_type, c_type) (_PS_G_TYPE_CIC((instance), (g_type), c_type)) -#define PS_GTK_TYPE_MENU (ps_gtk_menu_get_type()) -#define PS_GTK_MENU(obj) (PS_G_TYPE_CHECK_INSTANCE_CAST((obj), PS_GTK_TYPE_MENU, GtkMenu)) -#define PS_GTK_TYPE_MENU_SHELL (ps_gtk_menu_shell_get_type()) -#define PS_GTK_MENU_SHELL(obj) (PS_G_TYPE_CHECK_INSTANCE_CAST((obj), PS_GTK_TYPE_MENU_SHELL, GtkMenuShell)) - - typedef gulong (*f_g_signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags); - f_g_signal_connect_data ps_g_signal_connect_data = 0; - -#define ps_g_signal_connect(instance, detailed_signal, c_handler, data) ps_g_signal_connect_data((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags)0) - - typedef AppIndicator* (*f_app_indicator_new)(const gchar *id, const gchar *icon_name, AppIndicatorCategory category); - f_app_indicator_new ps_app_indicator_new = 0; - - typedef void (*f_app_indicator_set_status)(AppIndicator *self, AppIndicatorStatus status); - f_app_indicator_set_status ps_app_indicator_set_status = 0; - - typedef void (*f_app_indicator_set_menu)(AppIndicator *self, GtkMenu *menu); - f_app_indicator_set_menu ps_app_indicator_set_menu = 0; - - typedef void (*f_app_indicator_set_icon_full)(AppIndicator *self, const gchar *icon_name, const gchar *icon_desc); - f_app_indicator_set_icon_full ps_app_indicator_set_icon_full = 0; - - typedef gboolean (*f_gdk_init_check)(gint *argc, gchar ***argv); - f_gdk_init_check ps_gdk_init_check = 0; - - typedef GdkPixbuf* (*f_gdk_pixbuf_new_from_data)(const guchar *data, GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height, int rowstride, GdkPixbufDestroyNotify destroy_fn, gpointer destroy_fn_data); - f_gdk_pixbuf_new_from_data ps_gdk_pixbuf_new_from_data = 0; - - typedef GtkStatusIcon* (*f_gtk_status_icon_new_from_pixbuf)(GdkPixbuf *pixbuf); - f_gtk_status_icon_new_from_pixbuf ps_gtk_status_icon_new_from_pixbuf = 0; - - typedef void (*f_gtk_status_icon_set_from_pixbuf)(GtkStatusIcon *status_icon, GdkPixbuf *pixbuf); - f_gtk_status_icon_set_from_pixbuf ps_gtk_status_icon_set_from_pixbuf = 0; - - typedef void (*f_gtk_status_icon_set_title)(GtkStatusIcon *status_icon, const gchar *title); - f_gtk_status_icon_set_title ps_gtk_status_icon_set_title = 0; - - typedef void (*f_gtk_status_icon_set_tooltip_text)(GtkStatusIcon *status_icon, const gchar *title); - f_gtk_status_icon_set_tooltip_text ps_gtk_status_icon_set_tooltip_text = 0; - - typedef void (*f_gtk_status_icon_set_visible)(GtkStatusIcon *status_icon, gboolean visible); - f_gtk_status_icon_set_visible ps_gtk_status_icon_set_visible = 0; - - typedef gboolean (*f_gtk_status_icon_is_embedded)(GtkStatusIcon *status_icon); - f_gtk_status_icon_is_embedded ps_gtk_status_icon_is_embedded = 0; - - typedef gboolean (*f_gtk_status_icon_get_geometry)(GtkStatusIcon *status_icon, GdkScreen **screen, GdkRectangle *area, GtkOrientation *orientation); - f_gtk_status_icon_get_geometry ps_gtk_status_icon_get_geometry = 0; - - typedef void (*f_gtk_status_icon_position_menu)(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data); - f_gtk_status_icon_position_menu ps_gtk_status_icon_position_menu = 0; - - typedef void (*f_gtk_menu_popup)(GtkMenu *menu, GtkWidget *parent_menu_shell, GtkWidget *parent_menu_item, GtkMenuPositionFunc func, gpointer data, guint button, guint32 activate_time); - f_gtk_menu_popup ps_gtk_menu_popup = 0; - - typedef guint32 (*f_gtk_get_current_event_time)(void); - f_gtk_get_current_event_time ps_gtk_get_current_event_time = 0; - - typedef gpointer (*f_g_object_ref_sink)(gpointer object); - f_g_object_ref_sink ps_g_object_ref_sink = 0; - - typedef void (*f_g_object_unref)(gpointer object); - f_g_object_unref ps_g_object_unref = 0; - - typedef guint (*f_g_idle_add)(GSourceFunc function, gpointer data); - f_g_idle_add ps_g_idle_add = 0; - - typedef void (*f_unity_launcher_entry_set_count)(UnityLauncherEntry* self, gint64 value); - f_unity_launcher_entry_set_count ps_unity_launcher_entry_set_count = 0; - - typedef void (*f_unity_launcher_entry_set_count_visible)(UnityLauncherEntry* self, gboolean value); - f_unity_launcher_entry_set_count_visible ps_unity_launcher_entry_set_count_visible = 0; - - typedef UnityLauncherEntry* (*f_unity_launcher_entry_get_for_desktop_id)(const gchar* desktop_id); - f_unity_launcher_entry_get_for_desktop_id ps_unity_launcher_entry_get_for_desktop_id = 0; - - template - bool loadFunction(QLibrary &lib, const char *name, TFunction &func) { - if (!lib.isLoaded()) return false; - - func = (TFunction)lib.resolve(name); - if (func) { - return true; - } else { - LOG(("Error: failed to load '%1' function!").arg(name)); - return false; - } - } - - void _trayIconPopup(GtkStatusIcon *status_icon, guint button, guint32 activate_time, gpointer popup_menu) { - ps_gtk_menu_popup(PS_GTK_MENU(popup_menu), NULL, NULL, ps_gtk_status_icon_position_menu, status_icon, button, activate_time); - } - - void _trayIconActivate(GtkStatusIcon *status_icon, gpointer popup_menu) { - if (App::wnd()->isActiveWindow() && App::wnd()->isVisible()) { - ps_gtk_menu_popup(PS_GTK_MENU(popup_menu), NULL, NULL, ps_gtk_status_icon_position_menu, status_icon, 0, ps_gtk_get_current_event_time()); - } else { - App::wnd()->showFromTray(); - } - } - - gboolean _trayIconResized(GtkStatusIcon *status_icon, gint size, gpointer popup_menu) { - _trayIconSize = size; - if (App::wnd()) App::wnd()->psUpdateCounter(); - return FALSE; - } - -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - -#define QT_RED 3 -#define QT_GREEN 2 -#define QT_BLUE 1 -#define QT_ALPHA 0 - -#else - -#define QT_RED 0 -#define QT_GREEN 1 -#define QT_BLUE 2 -#define QT_ALPHA 3 - -#endif - -#define GTK_RED 2 -#define GTK_GREEN 1 -#define GTK_BLUE 0 -#define GTK_ALPHA 3 - - QImage _trayIconImageGen() { - int32 counter = App::histories().unreadBadge(), counterSlice = (counter >= 1000) ? (1000 + (counter % 100)) : counter; - bool muted = App::histories().unreadOnlyMuted(); - if (_trayIconImage.isNull() || _trayIconImage.width() != _trayIconSize || muted != _trayIconMuted || counterSlice != _trayIconCount) { - if (_trayIconImageBack.isNull() || _trayIconImageBack.width() != _trayIconSize) { - _trayIconImageBack = App::wnd()->iconLarge().scaled(_trayIconSize, _trayIconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - _trayIconImageBack = _trayIconImageBack.convertToFormat(QImage::Format_ARGB32); - int w = _trayIconImageBack.width(), h = _trayIconImageBack.height(), perline = _trayIconImageBack.bytesPerLine(); - uchar *bytes = _trayIconImageBack.bits(); - for (int32 y = 0; y < h; ++y) { - for (int32 x = 0; x < w; ++x) { - int32 srcoff = y * perline + x * 4; - bytes[srcoff + QT_RED ] = qMax(bytes[srcoff + QT_RED ], uchar(224)); - bytes[srcoff + QT_GREEN] = qMax(bytes[srcoff + QT_GREEN], uchar(165)); - bytes[srcoff + QT_BLUE ] = qMax(bytes[srcoff + QT_BLUE ], uchar(44)); - } - } - } - _trayIconImage = _trayIconImageBack; - if (counter > 0) { - QPainter p(&_trayIconImage); - int32 layerSize = -16; - if (_trayIconSize >= 48) { - layerSize = -32; - } else if (_trayIconSize >= 36) { - layerSize = -24; - } else if (_trayIconSize >= 32) { - layerSize = -20; - } - QImage layer = App::wnd()->iconWithCounter(layerSize, counter, (muted ? st::counterMuteBG : st::counterBG), false); - p.drawImage(_trayIconImage.width() - layer.width() - 1, _trayIconImage.height() - layer.height() - 1, layer); - } - } - return _trayIconImage; - } - - QString _trayIconImageFile() { - int32 counter = App::histories().unreadBadge(), counterSlice = (counter >= 1000) ? (1000 + (counter % 100)) : counter; - bool muted = App::histories().unreadOnlyMuted(); - - QString name = cWorkingDir() + qsl("tdata/ticons/ico%1_%2_%3.png").arg(muted ? "mute" : "").arg(_trayIconSize).arg(counterSlice); - QFileInfo info(name); - if (info.exists()) return name; - - QImage img = _trayIconImageGen(); - if (img.save(name, "PNG")) return name; - - QDir dir(info.absoluteDir()); - if (!dir.exists()) { - dir.mkpath(dir.absolutePath()); - if (img.save(name, "PNG")) return name; - } - - return QString(); - } - - void loadPixbuf(QImage image) { - int w = image.width(), h = image.height(), perline = image.bytesPerLine(), s = image.byteCount(); - _trayPixbufData.resize(w * h * 4); - uchar *result = (uchar*)_trayPixbufData.data(), *bytes = image.bits(); - for (int32 y = 0; y < h; ++y) { - for (int32 x = 0; x < w; ++x) { - int32 offset = (y * w + x) * 4, srcoff = y * perline + x * 4; - result[offset + GTK_RED ] = bytes[srcoff + QT_RED ]; - result[offset + GTK_GREEN] = bytes[srcoff + QT_GREEN]; - result[offset + GTK_BLUE ] = bytes[srcoff + QT_BLUE ]; - result[offset + GTK_ALPHA] = bytes[srcoff + QT_ALPHA]; - } - } - - if (_trayPixbuf) ps_g_object_unref(_trayPixbuf); - _trayPixbuf = ps_gdk_pixbuf_new_from_data(result, GDK_COLORSPACE_RGB, true, 8, w, h, w * 4, 0, 0); - } - - void _trayMenuCallback(GtkMenu *menu, gpointer data) { - for (int32 i = 0, l = _trayItems.size(); i < l; ++i) { - if ((void*)_trayItems.at(i).first == (void*)menu) { - QMetaObject::invokeMethod(_trayItems.at(i).second, "triggered"); - } - } - } - - static gboolean _trayIconCheck(gpointer/* pIn*/) { - if (useStatusIcon && !trayIconChecked) { - if (ps_gtk_status_icon_is_embedded(_trayIcon)) { - trayIconChecked = true; - cSetSupportTray(true); - if (App::wnd()) { - App::wnd()->psUpdateWorkmode(); - App::wnd()->psUpdateCounter(); - App::wnd()->updateTrayMenu(); - } - } - } - return FALSE; - } - - bool loadLibrary(QLibrary &lib, const char *name, int version) { - DEBUG_LOG(("Loading '%1' with version %2...").arg(QLatin1String(name)).arg(version)); - lib.setFileNameAndVersion(QLatin1String(name), version); - if (lib.load()) { - DEBUG_LOG(("Loaded '%1' with version %2!").arg(QLatin1String(name)).arg(version)); - return true; - } - lib.setFileNameAndVersion(QLatin1String(name), QString()); - if (lib.load()) { - DEBUG_LOG(("Loaded '%1' without version!").arg(QLatin1String(name))); - return true; - } - LOG(("Could not load '%1' with version %2 :(").arg(QLatin1String(name)).arg(version)); - return false; - } - - void setupGtkBase(QLibrary &lib_gtk) { - if (!loadFunction(lib_gtk, "gtk_init_check", ps_gtk_init_check)) return; - if (!loadFunction(lib_gtk, "gtk_menu_new", ps_gtk_menu_new)) return; - if (!loadFunction(lib_gtk, "gtk_menu_get_type", ps_gtk_menu_get_type)) return; - - if (!loadFunction(lib_gtk, "gtk_menu_item_new_with_label", ps_gtk_menu_item_new_with_label)) return; - if (!loadFunction(lib_gtk, "gtk_menu_item_set_label", ps_gtk_menu_item_set_label)) return; - if (!loadFunction(lib_gtk, "gtk_menu_shell_append", ps_gtk_menu_shell_append)) return; - if (!loadFunction(lib_gtk, "gtk_menu_shell_get_type", ps_gtk_menu_shell_get_type)) return; - if (!loadFunction(lib_gtk, "gtk_widget_show", ps_gtk_widget_show)) return; - if (!loadFunction(lib_gtk, "gtk_widget_get_toplevel", ps_gtk_widget_get_toplevel)) return; - if (!loadFunction(lib_gtk, "gtk_widget_get_visible", ps_gtk_widget_get_visible)) return; - if (!loadFunction(lib_gtk, "gtk_widget_set_sensitive", ps_gtk_widget_set_sensitive)) return; - - if (!loadFunction(lib_gtk, "g_type_check_instance_cast", ps_g_type_check_instance_cast)) return; - if (!loadFunction(lib_gtk, "g_signal_connect_data", ps_g_signal_connect_data)) return; - - if (!loadFunction(lib_gtk, "g_object_ref_sink", ps_g_object_ref_sink)) return; - if (!loadFunction(lib_gtk, "g_object_unref", ps_g_object_unref)) return; - - DEBUG_LOG(("Library gtk functions loaded!")); - if (ps_gtk_init_check(0, 0)) { - DEBUG_LOG(("Checked gtk with gtk_init_check!")); - useGtkBase = true; - } else { - DEBUG_LOG(("Failed to gtk_init_check(0, 0)!")); - } - } - - void setupAppIndicator(QLibrary &lib_indicator) { - if (!loadFunction(lib_indicator, "app_indicator_new", ps_app_indicator_new)) return; - if (!loadFunction(lib_indicator, "app_indicator_set_status", ps_app_indicator_set_status)) return; - if (!loadFunction(lib_indicator, "app_indicator_set_menu", ps_app_indicator_set_menu)) return; - if (!loadFunction(lib_indicator, "app_indicator_set_icon_full", ps_app_indicator_set_icon_full)) return; - useAppIndicator = true; - DEBUG_LOG(("Library appindicator functions loaded!")); - } - - void setupGtk() { - QLibrary lib_gtk, lib_indicator; - if (!noQtTrayIcon && !tryAppIndicator) { - if (!noTryUnity) { - if (loadLibrary(lib_gtk, "gtk-3", 0)) { - setupGtkBase(lib_gtk); - } - if (!useGtkBase) { - if (loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) { - setupGtkBase(lib_gtk); - } - } - if (!useGtkBase) { - noTryUnity = true; - } - } - return; - } - - if (loadLibrary(lib_indicator, "appindicator3", 1)) { - if (loadLibrary(lib_gtk, "gtk-3", 0)) { - setupGtkBase(lib_gtk); - setupAppIndicator(lib_indicator); - } - } - if (!useGtkBase || !useAppIndicator) { - if (loadLibrary(lib_indicator, "appindicator", 1)) { - if (loadLibrary(lib_gtk, "gtk-x11-2.0", 0)) { - useGtkBase = useAppIndicator = false; - setupGtkBase(lib_gtk); - setupAppIndicator(lib_indicator); - } - } - } - if (tryAppIndicator) { - if (useGtkBase && useAppIndicator) { - noQtTrayIcon = true; - cSetSupportTray(false); - } - return; - } - - if (!useGtkBase && lib_gtk.isLoaded()) { - LOG(("Could not load appindicator, trying to load gtk...")); - setupGtkBase(lib_gtk); - } - if (!useGtkBase) { - useAppIndicator = false; - LOG(("Could not load gtk-x11-2.0!")); - return; - } - - if (!loadFunction(lib_gtk, "gdk_init_check", ps_gdk_init_check)) return; - if (!loadFunction(lib_gtk, "gdk_pixbuf_new_from_data", ps_gdk_pixbuf_new_from_data)) return; - if (!loadFunction(lib_gtk, "gtk_status_icon_new_from_pixbuf", ps_gtk_status_icon_new_from_pixbuf)) return; - if (!loadFunction(lib_gtk, "gtk_status_icon_set_from_pixbuf", ps_gtk_status_icon_set_from_pixbuf)) return; - if (!loadFunction(lib_gtk, "gtk_status_icon_set_title", ps_gtk_status_icon_set_title)) return; - if (!loadFunction(lib_gtk, "gtk_status_icon_set_tooltip_text", ps_gtk_status_icon_set_tooltip_text)) return; - if (!loadFunction(lib_gtk, "gtk_status_icon_set_visible", ps_gtk_status_icon_set_visible)) return; - if (!loadFunction(lib_gtk, "gtk_status_icon_is_embedded", ps_gtk_status_icon_is_embedded)) return; - if (!loadFunction(lib_gtk, "gtk_status_icon_get_geometry", ps_gtk_status_icon_get_geometry)) return; - if (!loadFunction(lib_gtk, "gtk_status_icon_position_menu", ps_gtk_status_icon_position_menu)) return; - if (!loadFunction(lib_gtk, "gtk_menu_popup", ps_gtk_menu_popup)) return; - if (!loadFunction(lib_gtk, "gtk_get_current_event_time", ps_gtk_get_current_event_time)) return; - if (!loadFunction(lib_gtk, "g_idle_add", ps_g_idle_add)) return; - useStatusIcon = true; - DEBUG_LOG(("Status icon api loaded!")); - } - - void setupUnity() { - if (noTryUnity) return; - - QLibrary lib_unity(qstr("unity"), 9, 0); - if (!loadLibrary(lib_unity, "unity", 9)) return; - - if (!loadFunction(lib_unity, "unity_launcher_entry_get_for_desktop_id", ps_unity_launcher_entry_get_for_desktop_id)) return; - if (!loadFunction(lib_unity, "unity_launcher_entry_set_count", ps_unity_launcher_entry_set_count)) return; - if (!loadFunction(lib_unity, "unity_launcher_entry_set_count_visible", ps_unity_launcher_entry_set_count_visible)) return; - useUnityCount = true; - DEBUG_LOG(("Unity count api loaded!")); - } - class _PsEventFilter : public QAbstractNativeEventFilter { public: _PsEventFilter() { @@ -495,8 +73,6 @@ namespace { } }; _PsEventFilter *_psEventFilter = 0; - - UnityLauncherEntry *_psUnityLauncherEntry = 0; }; namespace { @@ -521,18 +97,6 @@ void psBringToBack(QWidget *w) { w->hide(); } -void PsMainWindow::psActivateNotify(NotifyWindow *w) { -} - -void PsMainWindow::psClearNotifies(PeerId peerId) { -} - -void PsMainWindow::psNotifyShown(NotifyWindow *w) { -} - -void PsMainWindow::psPlatformNotify(HistoryItem *item, int32 fwdCount) { -} - QAbstractNativeEventFilter *psNativeEventFilter() { delete _psEventFilter; _psEventFilter = new _PsEventFilter(); @@ -842,16 +406,8 @@ void finish() { 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(); + Libs::start(); + MainWindow::LibsLoaded(); } void finish() { diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp index 5115c16d2..5aec57778 100644 --- a/Telegram/SourceFiles/title.cpp +++ b/Telegram/SourceFiles/title.cpp @@ -80,14 +80,14 @@ TitleWidget::TitleWidget(MainWindow *window) : TWidget(window) ) { showUpdateBtn(); } - stateChanged(); + onWindowStateChanged(); connect(&_back, SIGNAL(clicked()), window, SLOT(hideSettings())); connect(&_cancel, SIGNAL(clicked()), this, SIGNAL(hiderClicked())); connect(&_settings, SIGNAL(clicked()), window, SLOT(showSettings())); connect(&_contacts, SIGNAL(clicked()), this, SLOT(onContacts())); connect(&_about, SIGNAL(clicked()), this, SLOT(onAbout())); - connect(wnd->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(stateChanged(Qt::WindowState))); + connect(wnd->windowHandle(), SIGNAL(windowStateChanged(Qt::WindowState)), this, SLOT(onWindowStateChanged(Qt::WindowState))); #ifndef TDESKTOP_DISABLE_AUTOUPDATE Sandbox::connect(SIGNAL(updateReady()), this, SLOT(showUpdateBtn())); @@ -308,7 +308,7 @@ void TitleWidget::mouseDoubleClickEvent(QMouseEvent *e) { } } -void TitleWidget::stateChanged(Qt::WindowState state) { +void TitleWidget::onWindowStateChanged(Qt::WindowState state) { if (state == Qt::WindowMinimized) return; maximizedChanged(state == Qt::WindowMaximized); } diff --git a/Telegram/SourceFiles/title.h b/Telegram/SourceFiles/title.h index a0cf52226..e6ef17cc5 100644 --- a/Telegram/SourceFiles/title.h +++ b/Telegram/SourceFiles/title.h @@ -67,7 +67,7 @@ public: public slots: - void stateChanged(Qt::WindowState state = Qt::WindowNoState); + void onWindowStateChanged(Qt::WindowState state = Qt::WindowNoState); void showUpdateBtn(); void onContacts(); void onAbout(); diff --git a/Telegram/Telegram.pro b/Telegram/Telegram.pro index b2824c378..321ae0d44 100644 --- a/Telegram/Telegram.pro +++ b/Telegram/Telegram.pro @@ -19,8 +19,6 @@ CONFIG(release, debug|release) { macx { QMAKE_INFO_PLIST = ./SourceFiles/Telegram.plist - OBJECTIVE_SOURCES += ./SourceFiles/pspecific_mac_p.mm - OBJECTIVE_HEADERS += ./SourceFiles/pspecific_mac_p.h QMAKE_LFLAGS += -framework Cocoa } @@ -160,6 +158,8 @@ SOURCES += \ ./SourceFiles/mtproto/scheme_auto.cpp \ ./SourceFiles/mtproto/session.cpp \ ./SourceFiles/overview/overview_layout.cpp \ + ./SourceFiles/platform/linux/linux_libs.cpp \ + ./SourceFiles/platform/linux/main_window_linux.cpp \ ./SourceFiles/profile/profile_actions_widget.cpp \ ./SourceFiles/profile/profile_block_widget.cpp \ ./SourceFiles/profile/profile_cover_drop_area.cpp \ @@ -206,6 +206,7 @@ SOURCES += \ ./SourceFiles/ui/images.cpp \ ./SourceFiles/ui/scrollarea.cpp \ ./SourceFiles/ui/twidget.cpp \ + ./SourceFiles/window/main_window.cpp \ ./SourceFiles/window/section_widget.cpp \ ./SourceFiles/window/slide_animation.cpp \ ./SourceFiles/window/top_bar_widget.cpp @@ -312,6 +313,9 @@ HEADERS += \ ./SourceFiles/mtproto/scheme_auto.h \ ./SourceFiles/mtproto/session.h \ ./SourceFiles/overview/overview_layout.h \ + ./SourceFiles/platform/platform_main_window.h \ + ./SourceFiles/platform/linux/linux_libs.h \ + ./SourceFiles/platform/linux/main_window_linux.h \ ./SourceFiles/profile/profile_actions_widget.h \ ./SourceFiles/profile/profile_block_widget.h \ ./SourceFiles/profile/profile_cover_drop_area.h \ @@ -359,6 +363,7 @@ HEADERS += \ ./SourceFiles/ui/images.h \ ./SourceFiles/ui/scrollarea.h \ ./SourceFiles/ui/twidget.h \ + ./SourceFiles/window/main_window.h \ ./SourceFiles/window/section_memento.h \ ./SourceFiles/window/section_widget.h \ ./SourceFiles/window/slide_animation.h \ @@ -366,16 +371,27 @@ HEADERS += \ win32 { SOURCES += \ - ./SourceFiles/pspecific_win.cpp + ./SourceFiles/pspecific_win.cpp \ + ./SourceFiles/platform/win/windows_app_user_model_id.cpp \ + ./SourceFiles/platform/win/windows_dlls.cpp \ + ./SourceFiles/platform/win/windows_event_filter.cpp \ + ./SourceFiles/platform/win/windows_toasts.cpp + HEADERS += \ - ./SourceFiles/pspecific_win.h + ./SourceFiles/pspecific_win.h \ + ./SourceFiles/platform/win/windows_app_user_model_id.h \ + ./SourceFiles/platform/win/windows_dlls.h \ + ./SourceFiles/platform/win/windows_event_filter.h \ + ./SourceFiles/platform/win/windows_toasts.h } winrt { SOURCES += \ - ./SourceFiles/pspecific_winrt.cpp + ./SourceFiles/pspecific_winrt.cpp \ + ./SourceFiles/platform/winrt/main_window_winrt.cpp HEADERS += \ - ./SourceFiles/pspecific_winrt.h + ./SourceFiles/pspecific_winrt.h \ + ./Sourcefiles/platform/winrt/main_window_winrt.h } macx { @@ -383,6 +399,12 @@ SOURCES += \ ./SourceFiles/pspecific_mac.cpp HEADERS += \ ./SourceFiles/pspecific_mac.h +OBJECTIVE_SOURCES += \ + ./SourceFiles/pspecific_mac_p.mm \ + ./SourceFiles/platform/mac/main_window_mac.mm +HEADERS += \ + ./SourceFiles/pspecific_mac_p.h \ + ./SourceFiles/platform/mac/main_window_mac.h } SOURCES += \ From d089d4e692dc7e2b8a13eee051df36184d532a80 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 16 Jun 2016 20:22:45 +0300 Subject: [PATCH 04/24] Version 0.9.52 alpha. --- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 6 +++--- Telegram/Telegram.xcodeproj/project.pbxproj | 4 ++-- Telegram/build/version | 8 ++++---- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 77e020e3f..3d4e59e66 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,9,51,0 - PRODUCTVERSION 0,9,51,0 + FILEVERSION 0,9,52,0 + PRODUCTVERSION 0,9,52,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Telegram Messenger LLP" - VALUE "FileVersion", "0.9.51.0" + VALUE "FileVersion", "0.9.52.0" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.9.51.0" + VALUE "ProductVersion", "0.9.52.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index a357b54cc..243033855 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,9,51,0 - PRODUCTVERSION 0,9,51,0 + FILEVERSION 0,9,52,0 + PRODUCTVERSION 0,9,52,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,10 +43,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram Messenger LLP" VALUE "FileDescription", "Telegram Updater" - VALUE "FileVersion", "0.9.51.0" + VALUE "FileVersion", "0.9.52.0" VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "0.9.51.0" + VALUE "ProductVersion", "0.9.52.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 03575e9a0..79b5b85bf 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #define BETA_VERSION_MACRO (0ULL) -constexpr int AppVersion = 9051; -constexpr str_const AppVersionStr = "0.9.51"; -constexpr bool AppAlphaVersion = false; +constexpr int AppVersion = 9052; +constexpr str_const AppVersionStr = "0.9.52"; +constexpr bool AppAlphaVersion = true; constexpr uint64 AppBetaVersion = BETA_VERSION_MACRO; diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index 9eb17fbb6..ad6fdcc12 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -2368,7 +2368,7 @@ SDKROOT = macosx; SYMROOT = ./../Mac; TDESKTOP_MAJOR_VERSION = 0.9; - TDESKTOP_VERSION = 0.9.51; + TDESKTOP_VERSION = 0.9.52; ZLIB_PATH = /usr/local; }; name = Release; @@ -2509,7 +2509,7 @@ SDKROOT = macosx; SYMROOT = ./../Mac; TDESKTOP_MAJOR_VERSION = 0.9; - TDESKTOP_VERSION = 0.9.51; + TDESKTOP_VERSION = 0.9.52; ZLIB_PATH = /usr/local; }; name = Debug; diff --git a/Telegram/build/version b/Telegram/build/version index 45de83471..004c586ad 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,6 +1,6 @@ -AppVersion 9051 +AppVersion 9052 AppVersionStrMajor 0.9 -AppVersionStrSmall 0.9.51 -AppVersionStr 0.9.51 -AlphaChannel 0 +AppVersionStrSmall 0.9.52 +AppVersionStr 0.9.52 +AlphaChannel 1 BetaVersion 0 From e38b7cd46504579206499bd9dc3abe87c3d9b9ed Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 17 Jun 2016 15:38:08 +0300 Subject: [PATCH 05/24] Fixed dynamic library loading for Windows XP. Versioned dll removed, loading from combase.dll instead. --- Telegram/SourceFiles/platform/win/windows_dlls.cpp | 7 ++----- Telegram/SourceFiles/pspecific_win.cpp | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/platform/win/windows_dlls.cpp b/Telegram/SourceFiles/platform/win/windows_dlls.cpp index ae7bef04e..048aed929 100644 --- a/Telegram/SourceFiles/platform/win/windows_dlls.cpp +++ b/Telegram/SourceFiles/platform/win/windows_dlls.cpp @@ -43,7 +43,6 @@ HINSTANCE LibShell32; HINSTANCE LibWtsApi32; HINSTANCE LibPropSys; HINSTANCE LibComBase; -HINSTANCE LibWinRtString; void start() { LibUxTheme = LoadLibrary(L"UXTHEME.DLL"); @@ -66,10 +65,8 @@ void start() { 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); + load(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference); + load(LibComBase, "WindowsDeleteString", WindowsDeleteString); } } // namespace Dlls diff --git a/Telegram/SourceFiles/pspecific_win.cpp b/Telegram/SourceFiles/pspecific_win.cpp index e75983310..effc56d8a 100644 --- a/Telegram/SourceFiles/pspecific_win.cpp +++ b/Telegram/SourceFiles/pspecific_win.cpp @@ -98,7 +98,7 @@ namespace { _PsInitializer() { Dlls::start(); - useOpenWith = (Dlls::SHAssocEnumHandlers != nullptr) && (SHCreateItemFromParsingName != nullptr); + useOpenWith = (Dlls::SHAssocEnumHandlers != nullptr) && (Dlls::SHCreateItemFromParsingName != nullptr); useOpenAs = (Dlls::SHOpenWithDialog != nullptr) || (Dlls::OpenAs_RunDLL != nullptr); useShellapi = (Dlls::SHQueryUserNotificationState != nullptr); } From 963000386fdcd5b94dc6523501235aa5d5df12ae Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 17 Jun 2016 16:27:21 +0300 Subject: [PATCH 06/24] Fixed crash in BotKeyboard resize. Fixed elided single-line Text layout. --- Telegram/SourceFiles/historywidget.cpp | 58 ++++++------------- Telegram/SourceFiles/historywidget.h | 13 +---- .../SourceFiles/platform/win/windows_dlls.h | 2 - Telegram/SourceFiles/ui/text/text.cpp | 12 +++- 4 files changed, 30 insertions(+), 55 deletions(-) diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 6a30d790c..9c8b6c3f9 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -2445,21 +2445,6 @@ int BotKeyboard::Style::minButtonWidth(HistoryMessageReplyMarkup::Button::Type t return result; } -void BotKeyboard::resizeEvent(QResizeEvent *e) { - if (!_impl) return; - - updateStyle(); - - _height = _impl->naturalHeight() + st::botKbScroll.deltat + st::botKbScroll.deltab; - if (_maximizeSize) _height = qMax(_height, _maxOuterHeight); - if (height() != _height) { - resize(width(), _height); - return; - } - - _impl->resize(width() - _st->margin - st::botKbScroll.width, _height - (st::botKbScroll.deltat + st::botKbScroll.deltab)); -} - void BotKeyboard::mousePressEvent(QMouseEvent *e) { _lastMousePos = e->globalPos(); updateSelected(); @@ -2529,14 +2514,8 @@ bool BotKeyboard::updateMarkup(HistoryItem *to, bool force) { } } - updateStyle(); - _height = st::botKbScroll.deltat + st::botKbScroll.deltab + (_impl ? _impl->naturalHeight() : 0); - if (_maximizeSize) _height = qMax(_height, _maxOuterHeight); - if (height() != _height) { - resize(width(), _height); - } else { - resizeEvent(nullptr); - } + resizeToWidth(width(), _maxOuterHeight); + return true; } @@ -2548,13 +2527,23 @@ bool BotKeyboard::forceReply() const { return _forceReply; } -void BotKeyboard::resizeToWidth(int width, int maxOuterHeight) { - updateStyle(width); - _height = st::botKbScroll.deltat + st::botKbScroll.deltab + (_impl ? _impl->naturalHeight() : 0); +void BotKeyboard::resizeToWidth(int newWidth, int maxOuterHeight) { _maxOuterHeight = maxOuterHeight; - if (_maximizeSize) _height = qMax(_height, _maxOuterHeight); - resize(width, _height); + updateStyle(newWidth); + _height = st::botKbScroll.deltat + st::botKbScroll.deltab + (_impl ? _impl->naturalHeight() : 0); + if (_maximizeSize) { + accumulate_max(_height, _maxOuterHeight); + } + if (_impl) { + int implWidth = newWidth - _st->margin - st::botKbScroll.width; + int implHeight = _height - (st::botKbScroll.deltat + st::botKbScroll.deltab); + _impl->resize(implWidth, implHeight); + } + QSize newSize(newWidth, _height); + if (newSize != size()) { + resize(newSize); + } } bool BotKeyboard::maximizeSize() const { @@ -2565,10 +2554,10 @@ bool BotKeyboard::singleUse() const { return _singleUse; } -void BotKeyboard::updateStyle(int32 w) { +void BotKeyboard::updateStyle(int newWidth) { if (!_impl) return; - int implWidth = ((w < 0) ? width() : w) - st::botKbButton.margin - st::botKbScroll.width; + int implWidth = newWidth - st::botKbButton.margin - st::botKbScroll.width; _st = _impl->isEnoughSpace(implWidth, st::botKbButton) ? &st::botKbButton : &st::botKbTinyButton; _impl->setStyle(std_::make_unique