// // This file is part of Kepka, // an unofficial desktop version of Telegram messaging app, // see https://github.com/procxx/kepka // // Kepka 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/procxx/kepka/blob/master/LICENSE // Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org // Copyright (c) 2017- Kepka Contributors, https://github.com/procxx // #include "platform/linux/main_window_linux.h" #include "app.h" #include "application.h" #include "facades.h" #include "lang/lang_keys.h" #include "mainwindow.h" #include "messenger.h" #include "platform/platform_notifications_manager.h" #include "storage/localstorage.h" #include "styles/style_window.h" namespace Platform { namespace { qint32 _trayIconSize = 22; bool _trayIconMuted = true; qint32 _trayIconCount = 0; QImage _trayIconImageBack, _trayIconImage; #define QT_RED 0 #define QT_GREEN 1 #define QT_BLUE 2 #define QT_ALPHA 3 QImage _trayIconImageGen() { qint32 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 = Messenger::Instance().logo().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 (qint32 y = 0; y < h; ++y) { for (qint32 x = 0; x < w; ++x) { qint32 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); qint32 layerSize = -16; if (_trayIconSize >= 48) { layerSize = -32; } else if (_trayIconSize >= 36) { layerSize = -24; } else if (_trayIconSize >= 32) { layerSize = -20; } auto &bg = (muted ? st::trayCounterBgMute : st::trayCounterBg); auto &fg = st::trayCounterFg; auto layer = App::wnd()->iconWithCounter(layerSize, counter, bg, fg, false); p.drawImage(_trayIconImage.width() - layer.width() - 1, _trayIconImage.height() - layer.height() - 1, layer); } } return _trayIconImage; } QString _trayIconImageFile() { qint32 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(); } } // namespace bool MainWindow::hasTrayIcon() const { return trayIcon; } void MainWindow::psSetupTrayIcon() { if (!trayIcon) { trayIcon = new QSystemTrayIcon(this); QIcon icon; QFileInfo iconFile(_trayIconImageFile()); if (iconFile.exists()) { QByteArray path = QFile::encodeName(iconFile.absoluteFilePath()); icon = QIcon(path.constData()); } else { icon = Window::CreateIcon(); } trayIcon->setIcon(icon); trayIcon->setToolTip(str_const_toString(AppName)); connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(toggleTray(QSystemTrayIcon::ActivationReason)), Qt::UniqueConnection); // This is very important for native notifications via libnotify! // Some notification servers compose several notifications with a "Reply" // action into one and after that a click on "Reply" button does not call // the specified callback from any of the sent notification - libnotify // just ignores ibus messages, but Qt tray icon at least emits this signal. connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(showFromTray())); App::wnd()->updateTrayMenu(); } updateIconCounters(); trayIcon->show(); } void MainWindow::workmodeUpdated(DBIWorkMode mode) { if (!cSupportTray()) return; if (mode == dbiwmWindowOnly) { if (trayIcon) { trayIcon->setContextMenu(0); trayIcon->deleteLater(); } trayIcon = 0; } else { psSetupTrayIcon(); } } void MainWindow::unreadCounterChangedHook() { setWindowTitle(titleText()); updateIconCounters(); } void MainWindow::updateIconCounters() { updateWindowIcon(); if (trayIcon) { QIcon icon; QFileInfo iconFile(_trayIconImageFile()); if (iconFile.exists()) { QByteArray path = QFile::encodeName(iconFile.absoluteFilePath()); icon = QIcon(path.constData()); } else { qint32 counter = App::histories().unreadBadge(); bool muted = App::histories().unreadOnlyMuted(); auto &bg = (muted ? st::trayCounterBgMute : st::trayCounterBg); auto &fg = st::trayCounterFg; icon.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(16, counter, bg, fg, true))); icon.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(32, counter, bg, fg, true))); } trayIcon->setIcon(icon); } } void MainWindow::psCreateTrayIcon() { LOG(("Tray Icon: Using Qt tray icon, available: %1").arg(Logs::b(QSystemTrayIcon::isSystemTrayAvailable()))); cSetSupportTray(QSystemTrayIcon::isSystemTrayAvailable()); return; } void MainWindow::psFirstShow() { psCreateTrayIcon(); show(); if (cWindowPos().maximized) { DEBUG_LOG(("Window Pos: First show, setting maximized.")); setWindowState(Qt::WindowMaximized); } if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) { setWindowState(Qt::WindowMinimized); if (Global::WorkMode().value() == dbiwmTrayOnly || Global::WorkMode().value() == dbiwmWindowAndTray) { hide(); } else { show(); } } else { show(); } setPositionInited(); } } // namespace Platform