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/definitions/definitions.h.in b/src/definitions/definitions.h.in index e7e76c267..b5acf1709 100755 --- a/src/definitions/definitions.h.in +++ b/src/definitions/definitions.h.in @@ -82,6 +82,7 @@ #define ACCEPT_HEADER_FOR_FEED_DOWNLOADER "application/atom+xml,application/xml;q=0.9,text/xml;q=0.8,*/*;q=0.7" #define MIME_TYPE_ITEM_POINTER "@APP_LOW_NAME@/itempointer" #define DOWNLOADER_ICON_SIZE 48 +#define NOTIFICATION_ICON_SIZE 64 #define GOOGLE_SEARCH_URL "https://www.google.com/search?q=%1&ie=utf-8&oe=utf-8" #define GOOGLE_SUGGEST_URL "http://suggestqueries.google.com/complete/search?output=toolbar&hl=en&q=%1" diff --git a/src/gui/dialogs/formmain.cpp b/src/gui/dialogs/formmain.cpp index 238527d88..d6e0761fd 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..f1f68be9d --- /dev/null +++ b/src/gui/notifications/notification.cpp @@ -0,0 +1,223 @@ +// 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, const QIcon &icon) { + hide(); + + // Set new values. + m_text = text; + m_title = title; + m_icon = icon.pixmap(NOTIFICATION_ICON_SIZE, NOTIFICATION_ICON_SIZE); + + // Show it. + updateGeometries(); + repaint(); + show(); +} + +void Notification::notify(const QString &text, const QString &title, QSystemTrayIcon::MessageIcon icon) { + notify(text, title, MessageBox::iconForStatus((QMessageBox::Icon) icon)); +} + +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); + + 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::paintEvent(QPaintEvent *event) { + Q_UNUSED(event) + + QPainter painter(this); + painter.setFont(font()); + + 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..ae01b2215 --- /dev/null +++ b/src/gui/notifications/notification.h @@ -0,0 +1,67 @@ +// 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, const QIcon &icon); + void notify(const QString &text, const QString &title, QSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::Information); + + protected: + 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 diff --git a/src/gui/systemtrayicon.cpp b/src/gui/systemtrayicon.cpp index d71cfc201..4bbadd712 100755 --- a/src/gui/systemtrayicon.cpp +++ b/src/gui/systemtrayicon.cpp @@ -35,8 +35,7 @@ TrayIconMenu::~TrayIconMenu() { } bool TrayIconMenu::event(QEvent *event) { - if (Application::activeModalWidget() != NULL && - event->type() == QEvent::Show) { + if (Application::activeModalWidget() != NULL && event->type() == QEvent::Show) { QTimer::singleShot(0, this, SLOT(hide())); qApp->trayIcon()->showMessage(QSL(APP_LONG_NAME), tr("Close opened modal dialogs first."), @@ -46,9 +45,7 @@ bool TrayIconMenu::event(QEvent *event) { } #endif -SystemTrayIcon::SystemTrayIcon(const QString &normal_icon, - const QString &plain_icon, - FormMain *parent) +SystemTrayIcon::SystemTrayIcon(const QString &normal_icon, const QString &plain_icon, FormMain *parent) : QSystemTrayIcon(parent), m_normalIcon(normal_icon), m_plainPixmap(plain_icon), @@ -116,7 +113,7 @@ void SystemTrayIcon::show() { #endif } -void SystemTrayIcon::setNumber(int number, bool any_unread_message) { +void SystemTrayIcon::setNumber(int number, bool any_new_message) { if (number <= 0) { setToolTip(QSL(APP_LONG_NAME)); QSystemTrayIcon::setIcon(QIcon(m_normalIcon)); @@ -129,7 +126,7 @@ void SystemTrayIcon::setNumber(int number, bool any_unread_message) { // TODO: Here draw different background instead of different color of number. tray_painter.begin(&background); - tray_painter.setPen(any_unread_message ? Qt::blue : Qt::black); + tray_painter.setPen(any_new_message ? Qt::blue : Qt::black); tray_painter.setRenderHint(QPainter::SmoothPixmapTransform, true); tray_painter.setRenderHint(QPainter::TextAntialiasing, true); @@ -137,7 +134,6 @@ void SystemTrayIcon::setNumber(int number, bool any_unread_message) { // infinity symbol in that case. if (number > 999) { m_font.setPixelSize(100); - tray_painter.setFont(m_font); tray_painter.drawText(QRect(0, 0, 128, 128), Qt::AlignVCenter | Qt::AlignCenter, QChar(8734)); } diff --git a/src/gui/systemtrayicon.h b/src/gui/systemtrayicon.h index 4bf725838..f1339c564 100755 --- a/src/gui/systemtrayicon.h +++ b/src/gui/systemtrayicon.h @@ -54,7 +54,7 @@ class SystemTrayIcon : public QSystemTrayIcon { virtual ~SystemTrayIcon(); // Sets the number to be visible in the tray icon, number <= 0 removes it. - void setNumber(int number = -1, bool any_unread_message = false); + void setNumber(int number = -1, bool any_new_message = false); void showMessage(const QString &title, const QString &message, MessageIcon icon = Information, int milliseconds_timeout_hint = TRAY_ICON_BUBBLE_TIMEOUT, QObject *click_target = NULL, diff --git a/src/miscellaneous/application.cpp b/src/miscellaneous/application.cpp index 4f6bcac7d..5fea05a46 100755 --- a/src/miscellaneous/application.cpp +++ b/src/miscellaneous/application.cpp @@ -26,19 +26,19 @@ #include "gui/statusbar.h" #include "gui/dialogs/formmain.h" #include "exceptions/applicationexception.h" +#include "adblock/adblockmanager.h" #include #include #include -#include - Application::Application(const QString &id, int &argc, char **argv) : QtSingleApplication(id, argc, argv), m_updateFeedsLock(NULL), m_userActions(QList()), m_mainForm(NULL), m_trayIcon(NULL), m_settings(NULL), m_system(NULL), m_skins(NULL), - m_localization(NULL), m_icons(NULL), m_database(NULL), m_downloadManager(NULL), m_shouldRestart(false) { + m_localization(NULL), m_icons(NULL), m_database(NULL), m_downloadManager(NULL), m_shouldRestart(false), + m_notification(NULL) { connect(this, SIGNAL(aboutToQuit()), this, SLOT(onAboutToQuit())); connect(this, SIGNAL(commitDataRequest(QSessionManager&)), this, SLOT(onCommitData(QSessionManager&))); connect(this, SIGNAL(saveStateRequest(QSessionManager&)), this, SLOT(onSaveState(QSessionManager&))); @@ -84,7 +84,7 @@ Mutex *Application::feedUpdateLock() { } void Application::backupDatabaseSettings(bool backup_database, bool backup_settings, - const QString &target_path, const QString &backup_name) { + const QString &target_path, const QString &backup_name) { if (!QFileInfo(target_path).isWritable()) { throw ApplicationException(tr("Output directory is not writable.")); } @@ -168,15 +168,18 @@ void Application::deleteTrayIcon() { } } -void Application::showGuiMessage(const QString& title, const QString& message, +void Application::showGuiMessage(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon message_type, QWidget *parent, int duration) { - if (SystemTrayIcon::isSystemTrayActivated()) { - // TODO: Maybe show OSD instead if tray icon bubble, depending on settings. + /*if (true) { + // Show OSD instead if tray icon bubble, depending on settings. + notification()->notify(message, title, message_type); + } + else */if (SystemTrayIcon::isSystemTrayActivated()) { trayIcon()->showMessage(title, message, message_type, duration); } else { - // TODO: Tray icon or OSD is not available, display simple text box. + // Tray icon or OSD is not available, display simple text box. MessageBox::show(parent, (QMessageBox::Icon) message_type, title, message); } } @@ -225,6 +228,11 @@ void Application::onAboutToQuit() { qDebug("Close lock timed-out."); } + if (m_notification != NULL) { + m_notification->deleteLater(); + m_notification = NULL; + } + // Now, we can check if application should just quit or restart itself. if (m_shouldRestart) { finish(); diff --git a/src/miscellaneous/application.h b/src/miscellaneous/application.h index b25bddb76..d874af0d1 100755 --- a/src/miscellaneous/application.h +++ b/src/miscellaneous/application.h @@ -28,6 +28,7 @@ #include "miscellaneous/databasefactory.h" #include "miscellaneous/iofactory.h" #include "gui/systemtrayicon.h" +#include "gui/notifications/notification.h" #include "network-web/downloadmanager.h" #include @@ -63,6 +64,14 @@ class Application : public QtSingleApplication { return m_system; } + inline Notification *notification() { + if (m_notification == NULL) { + m_notification = new Notification(); + } + + return m_notification; + } + inline SkinFactory *skins() { if (m_skins == NULL) { m_skins = new SkinFactory(this); @@ -184,6 +193,7 @@ class Application : public QtSingleApplication { DatabaseFactory *m_database; DownloadManager *m_downloadManager; bool m_shouldRestart; + Notification *m_notification; }; #endif // APPLICATION_H