File indexing completed on 2025-01-12 04:20:12
0001 /* 0002 SnoreNotify is a Notification Framework based on Qt 0003 Copyright (C) 2014-2015 Hannah von Reth <vonreth@kde.org> 0004 0005 SnoreNotify is free software: you can redistribute it and/or modify 0006 it under the terms of the GNU Lesser General Public License as published by 0007 the Free Software Foundation, either version 3 of the License, or 0008 (at your option) any later version. 0009 0010 SnoreNotify is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 GNU Lesser General Public License for more details. 0014 0015 You should have received a copy of the GNU Lesser General Public License 0016 along with SnoreNotify. If not, see <http://www.gnu.org/licenses/>. 0017 */ 0018 0019 #include "notifywidget.h" 0020 #include "snorenotifier.h" 0021 #include "snorebackendconstants.h" 0022 #include "libsnore/utils.h" 0023 0024 #include <QGuiApplication> 0025 #include <QQmlProperty> 0026 0027 using namespace Snore; 0028 using namespace SnorePlugin; 0029 0030 NotifyWidget::NotifyWidget(int id, const ::SnorePlugin::Snore *parent) : 0031 m_id(id), 0032 m_parent(parent), 0033 m_mem(QStringLiteral("SnoreNotifyWidget_rev%1_id%2").arg(QString::number(SHARED_MEM_TYPE_REV()), QString::number(m_id))), 0034 m_ready(true), 0035 m_fontFamily(qApp->font().family()) 0036 { 0037 0038 #ifdef Q_OS_WIN 0039 if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS8) { 0040 m_fontFamily = QStringLiteral("Segoe UI Symbol"); 0041 emit fontFamilyChanged(); 0042 } 0043 // Qt 5.7+ is broken and can only display it in black and white for Segoe UI Emoji. 0044 #if QT_VERSION >= QT_VERSION_CHECK(5,5,0) && QT_VERSION < QT_VERSION_CHECK(5,7,0) 0045 if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS10) { 0046 m_fontFamily = QStringLiteral("Segoe UI Emoji"); 0047 emit fontFamilyChanged(); 0048 } 0049 #endif 0050 #endif 0051 QQmlApplicationEngine *engine = new QQmlApplicationEngine(this); 0052 engine->rootContext()->setContextProperty(QStringLiteral("notifyWidget"), this); 0053 engine->load(QUrl::fromEncoded("qrc:/notification.qml")); 0054 m_window = qobject_cast<QQuickWindow *>(engine->rootObjects().value(0)); 0055 0056 // TODO: It looks like there is a Qt bug which make some Windows with this flag invisible in some cases...(Tested: Kubuntu willy) 0057 m_window->setFlags(Qt::WindowStaysOnTopHint | Qt::ToolTip 0058 #ifdef Q_OS_MAC 0059 // TODO: is this needed or is ToolTip working? 0060 //| Qt::SubWindow 0061 #endif 0062 ); 0063 0064 if (m_mem.create(sizeof(SHARED_MEM_TYPE))) { 0065 m_mem.lock(); 0066 SHARED_MEM_TYPE *data = (SHARED_MEM_TYPE *)m_mem.data(); 0067 data->free = true; 0068 data->date = QTime::currentTime().msecsSinceStartOfDay(); 0069 m_mem.unlock(); 0070 } else { 0071 if (!m_mem.attach()) { 0072 qCWarning(SNORE) << "Failed to atatche to shared mem"; 0073 } else { 0074 m_mem.lock(); 0075 SHARED_MEM_TYPE *data = (SHARED_MEM_TYPE *)m_mem.data(); 0076 m_mem.unlock(); 0077 int elapsed = (QTime::currentTime().msecsSinceStartOfDay() - data->date) / 1000; 0078 qCDebug(SNORE) << m_id << "State:" << data->free << "Time:" << elapsed << "Timeout:" << data->timeout; 0079 } 0080 } 0081 } 0082 0083 NotifyWidget::~NotifyWidget() 0084 { 0085 release(); 0086 } 0087 0088 void NotifyWidget::display(const Notification ¬ification) 0089 { 0090 qCDebug(SNORE) << m_id << notification.id() << m_window->isVisible(); 0091 m_notification = notification; 0092 QColor color; 0093 QVariant vcolor = notification.application().constHints().privateValue(parent(), "backgroundColor"); 0094 if (vcolor.isValid()) { 0095 color = vcolor.value<QColor>(); 0096 } else { 0097 color = computeBackgrondColor(notification.application().icon().pixmap(QSize(20, 20)).toImage()); 0098 notification.application().hints().setPrivateValue(parent(), "backgroundColor", color); 0099 } 0100 m_appIcon = QUrl::fromLocalFile(notification.application().icon().localUrl(QSize(m_appIconSize, m_appIconSize))); 0101 emit appIconChanged(); 0102 0103 m_image = QUrl::fromLocalFile(notification.icon().localUrl(QSize(m_imageSize, m_imageSize))); 0104 emit imageChanged(); 0105 0106 m_title = notification.title(Utils::AllMarkup); 0107 emit titleChanged(); 0108 m_body = notification.text(Utils::AllMarkup); 0109 emit bodyChanged(); 0110 0111 if (!notification.isUpdate()) { 0112 syncSettings(); 0113 m_color = color; 0114 m_textColor = compueTextColor(color); 0115 emit colorChanged(); 0116 emit textColorChanged(); 0117 m_window->show(); 0118 Utils::raiseWindowToFront(m_window); 0119 } 0120 } 0121 0122 bool NotifyWidget::acquire(int timeout) 0123 { 0124 if (!m_mem.isAttached()) { 0125 return true; 0126 } 0127 bool out = false; 0128 if (m_ready) { 0129 m_mem.lock(); 0130 SHARED_MEM_TYPE *data = (SHARED_MEM_TYPE *)m_mem.data(); 0131 int elapsed = (QTime::currentTime().msecsSinceStartOfDay() - data->date) / 1000; 0132 qCDebug(SNORE) << m_id << "State:" << data->free << "Time:" << elapsed << "Timeout:" << data->timeout; 0133 bool isTimedOut = elapsed > data->timeout; 0134 if (data->free || isTimedOut) { 0135 if (isTimedOut) { 0136 qCDebug(SNORE) << "Notification Lock timed out" << elapsed; 0137 } 0138 data->free = false; 0139 data->date = QTime::currentTime().msecsSinceStartOfDay(); 0140 data->timeout = timeout; 0141 m_ready = false; 0142 out = true; 0143 } 0144 m_mem.unlock(); 0145 } 0146 return out; 0147 } 0148 0149 bool NotifyWidget::release() 0150 { 0151 if (!m_mem.isAttached()) { 0152 return true; 0153 } 0154 bool out = false; 0155 if (!m_ready) { 0156 m_mem.lock(); 0157 SHARED_MEM_TYPE *data = (SHARED_MEM_TYPE *)m_mem.data(); 0158 int elapsed = (QTime::currentTime().msecsSinceStartOfDay() - data->date) / 1000; 0159 qCDebug(SNORE) << m_id << "State:" << data->free << "Time:" << elapsed << "Timeout:" << data->timeout; 0160 if (!data->free) { 0161 data->free = true; 0162 m_ready = true; 0163 out = true; 0164 } 0165 m_mem.unlock(); 0166 m_window->hide(); 0167 } 0168 return out; 0169 } 0170 0171 Notification &NotifyWidget::notification() 0172 { 0173 return m_notification; 0174 } 0175 0176 int NotifyWidget::id() const 0177 { 0178 return m_id; 0179 } 0180 0181 void NotifyWidget::syncSettings() 0182 { 0183 Qt::Corner c = static_cast<Qt::Corner>(m_parent->settingsValue(SnoreBackendConstants::Position).toInt()); 0184 if (c != m_corner || !m_initialized) { 0185 m_initialized = true; 0186 m_corner = c; 0187 emit positionChanged(); 0188 } 0189 } 0190 0191 QColor NotifyWidget::computeBackgrondColor(const QImage &img) 0192 { 0193 if (img.isNull()) 0194 return Qt::black; 0195 0196 int stepSize = img.depth() / 8; 0197 qulonglong r = 0; 0198 qulonglong g = 0; 0199 qulonglong b = 0; 0200 const uchar *end = img.constBits() + img.byteCount(); 0201 for (const uchar *bit = img.constBits(); bit != end; bit += stepSize) { 0202 const QRgb c = *reinterpret_cast<const QRgb *>(bit); 0203 r += qRed(c); 0204 g += qGreen(c); 0205 b += qBlue(c); 0206 } 0207 int size = img.byteCount() / stepSize; 0208 return QColor(r / size, g / size, b / size); 0209 } 0210 0211 QColor NotifyWidget::compueTextColor(const QColor &backgroundColor) 0212 { 0213 // based on http://stackoverflow.com/a/946734 0214 QRgb compColor = qGray(backgroundColor.rgb()) > 186 ? 0 : 255; 0215 return QColor(compColor, compColor, compColor); 0216 } 0217