diff --git a/CMakeLists.txt b/CMakeLists.txt index 2947f50a3..8359f2706 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -341,6 +341,7 @@ set(APP_SOURCES src/gui/dialogs/formdatabasecleanup.cpp src/gui/dialogs/formbackupdatabasesettings.cpp src/gui/dialogs/formrestoredatabasesettings.cpp + src/gui/notifications/notification.cpp src/gui/systemtrayicon.cpp src/gui/baselineedit.cpp src/gui/locationlineedit.cpp @@ -454,6 +455,7 @@ set(APP_HEADERS src/gui/dialogs/formrestoredatabasesettings.h src/gui/dialogs/formdatabasecleanup.h src/gui/dialogs/formupdate.h + src/gui/notifications/notification.h src/gui/systemtrayicon.h src/gui/baselineedit.h src/gui/locationlineedit.h @@ -700,6 +702,7 @@ include_directories ( ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/gui ${CMAKE_SOURCE_DIR}/src/gui/dialogs + ${CMAKE_SOURCE_DIR}/src/gui/notifications ${CMAKE_SOURCE_DIR}/src/network-web ${CMAKE_SOURCE_DIR}/src/network-web/adblock ${CMAKE_SOURCE_DIR}/src/dynamic-shortcuts diff --git a/src/gui/dialogs/formmain.cpp b/src/gui/dialogs/formmain.cpp index d856525d0..37fb7f91e 100755 --- a/src/gui/dialogs/formmain.cpp +++ b/src/gui/dialogs/formmain.cpp @@ -39,6 +39,7 @@ #include "gui/dialogs/formimportexport.h" #include "gui/dialogs/formbackupdatabasesettings.h" #include "gui/dialogs/formrestoredatabasesettings.h" +#include "gui/notifications/notification.h" #include #include @@ -78,6 +79,8 @@ FormMain::FormMain(QWidget *parent, Qt::WindowFlags f) // Initialize the web factory. WebFactory::instance()->loadState(); + + (new Notification())->notify("abcd abcd abcd abcd abcd abcd \naaa\n\n\nabcd abcd abcd abcd abcd", "def def def def def"); } FormMain::~FormMain() { diff --git a/src/gui/notifications/notification.cpp b/src/gui/notifications/notification.cpp new file mode 100644 index 000000000..3e81541ee --- /dev/null +++ b/src/gui/notifications/notification.cpp @@ -0,0 +1,233 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2015 by Martin Rotter +// +// RSS Guard 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. +// +// RSS Guard 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. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "gui/notifications/notification.h" + +#include "gui/messagebox.h" +#include "definitions/definitions.h" + +#include +#include +#include +#include +#include + +#if defined(Q_OS_MAC) +#include +#endif + + +Notification::Notification() : QWidget(0), m_title(QString()), m_text(QString()), m_icon(QPixmap()), m_screen(-1), + m_width(-1), m_height(-1), m_padding(5), m_widgetMargin(2 * m_padding) { + setupWidget(); + loadSettings(); +} + +Notification::~Notification() { +} + +void Notification::notify(const QString &text, const QString &title, QSystemTrayIcon::MessageIcon icon) { + hide(); + + // Set new values. + m_text = text; + m_title = title; + m_icon = MessageBox::iconForStatus((QMessageBox::Icon) icon).pixmap(64, 64); + + // Show it. + updateGeometries(); + repaint(); + show(); +} + +void Notification::updateGeometries() { + // Calculate width and height of notification with given icon and text. + m_width = m_padding + + m_icon.width() + m_padding + /* contents */ qMax(stringWidth(m_title), stringWidth(m_text)) + + m_padding; + m_height = m_padding + + /* contents */ + qMax(m_icon.height(), + stringHeight(m_title) + m_padding + stringHeight(m_text)) + + m_padding; + + // Calculate real position. + int x, y; + QRect screen_geometry = QApplication::desktop()->availableGeometry(m_screen); + + // TODO: dodělat pozice x, y + switch (m_position) { + case Qt::BottomLeftCorner: + x = m_widgetMargin; + y = screen_geometry.height() - m_widgetMargin - m_height; + break; + + case Qt::TopLeftCorner: + x = m_widgetMargin; + y = m_widgetMargin; + break; + + case Qt::TopRightCorner: + x = screen_geometry.width() - m_widgetMargin - m_width; + y = m_widgetMargin; + break; + + case Qt::BottomRightCorner: + default: + x = screen_geometry.width() - m_widgetMargin - m_width; + y = screen_geometry.height() - m_widgetMargin - m_height; + break; + } + + setGeometry(x, y, m_width, m_height); +} + + +void Notification::focusInEvent(QFocusEvent *event) { + QWidget::focusInEvent(event); + repaint(); +} + +void Notification::focusOutEvent(QFocusEvent *event) { + QWidget::focusOutEvent(event); + repaint(); +} + +void Notification::paintEvent(QPaintEvent *event) { + Q_UNUSED(event) + + QPainter painter(this); + painter.setFont(font()); + + QFontMetrics font_metrics = painter.fontMetrics(); + + if (!underMouse()) { + painter.setOpacity(0.7); + } + else { + painter.setOpacity(0.95); + } + + // Draw background. + painter.setRenderHints(QPainter::HighQualityAntialiasing | QPainter::Qt4CompatiblePainting); + painter.setBrush(QColor(220, 220, 220)); + + painter.setPen(Qt::NoPen); + painter.drawRoundedRect(0, 0, width(), height(), 5.0, 5.0); + + // Draw icon. + painter.drawPixmap(m_padding, m_padding, m_icon); + + // Draw text. + painter.setPen( Qt::black ); + + // Needed heighs/widths. + int title_height = stringHeight(m_title); + int remaining_width = width() - m_padding - m_icon.width() - m_padding /* - here comes contents */ - m_padding; + int remaining_height = height() - m_padding - title_height - m_padding /* - here comes contents */ - m_padding; + + painter.drawText(m_padding + m_icon.width() + m_padding, m_padding + title_height + m_padding, + remaining_width, remaining_height, + Qt::AlignLeft, m_text); + + // Draw heading. + QFont font = painter.font(); + font.setBold(true); + painter.setFont(font); + + painter.drawText(m_padding + m_icon.width() + m_padding, m_padding, + remaining_width, remaining_height, + Qt::AlignHCenter | Qt::AlignTop, m_title); +} + +void Notification::mousePressEvent(QMouseEvent *event) { + QWidget::mousePressEvent(event); + QTimer::singleShot(0, this, SLOT(hide())); +} + +void Notification::enterEvent(QEvent *event) { + QWidget::enterEvent(event); + repaint(); +} + +void Notification::leaveEvent(QEvent *event) { + QWidget::leaveEvent(event); + repaint(); +} + +int Notification::stringHeight(const QString &string) { + int count_lines = string.split(QL1C('\n')).size(); + return fontMetrics().height() * count_lines; +} + +int Notification::stringWidth(const QString &string) { + QStringList lines = string.split(QL1C('\n')); + int width = 0; + + foreach (const QString &line, lines) { + int line_width = fontMetrics().width(line); + + if (line_width > width) { + width = line_width; + } + } + + return width; +} + +void Notification::loadSettings() { + // TODO: načist z nastaveni. + m_position = Qt::BottomRightCorner; +} + +void Notification::setupWidget() { + // Set window flags. + Qt::WindowFlags window_flags = Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | + Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | + Qt::WindowDoesNotAcceptFocus; + +#if defined (Q_OS_MAC) + window_flags |= Qt::SubWindow; +#else + window_flags |= Qt::Tool; +#endif + + setWindowFlags(window_flags); + + // Set widget attributes. + setAttribute(Qt::WA_TranslucentBackground); + setAttribute(Qt::WA_X11DoNotAcceptFocus); + setAttribute(Qt::WA_ShowWithoutActivating); + +#if defined (Q_OS_MAC) + winId(); + + int setAttr[] = {kHIWindowBitDoesNotHide, kHIWindowBitDoesNotCycle, kHIWindowBitNoShadow, 0}; + int clearAttr[] = {0}; + HIWindowChangeAttributes(qt_mac_window_for(this), setAttr, clearAttr); +#endif + + // Window will be meant to be on top, but should not steal focus. + setFocusPolicy(Qt::NoFocus); + + // TODO: pokracovat + // https://github.com/binaryking/QNotify/blob/master/QNotify.cpp + // http://stackoverflow.com/questions/5823700/notification-window-in-mac-with-or-without-qt + // quiterss + // a odkazy z issue + // promyslet esli tam dat jen ciste label a ikonu, nebo i seznam nejnovesich zprav atp. +} diff --git a/src/gui/notifications/notification.h b/src/gui/notifications/notification.h new file mode 100644 index 000000000..2e08cd165 --- /dev/null +++ b/src/gui/notifications/notification.h @@ -0,0 +1,68 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2015 by Martin Rotter +// +// RSS Guard 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. +// +// RSS Guard 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. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef NOTIFICATION_H +#define NOTIFICATION_H + +#include + +#include + + +class Notification : public QWidget { + Q_OBJECT + + public: + // Constructors. + explicit Notification(); + virtual ~Notification(); + + public slots: + void notify(const QString &text, const QString &title, QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::Information); + + protected: + void focusInEvent(QFocusEvent *event); + void focusOutEvent(QFocusEvent *event); + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + + private: + int stringHeight(const QString &string); + int stringWidth(const QString &string); + + void loadSettings(); + void setupWidget(); + void updateGeometries(); + + QString m_title; + QString m_text; + QPixmap m_icon; + + // Defaults to -1, which means "default" (primary) screen. + int m_screen; + Qt::Corner m_position; + + // Is calculated according to contents. + int m_width; + int m_height; + int m_padding; + int m_widgetMargin; +}; + +#endif // NOTIFICATION_H