Logo Search packages:      
Sourcecode: kadu version File versions

notification-manager.cpp

/*
 * %kadu copyright begin%
 * Copyright 2010, 2011 Piotr Dąbrowski (ultr@ultr.pl)
 * Copyright 2007, 2008, 2009 Dawid Stawiarski (neeo@kadu.net)
 * Copyright 2004 Tomasz Jarzynka (tomee@cpi.pl)
 * Copyright 2010, 2011 Bartosz Brachaczek (b.brachaczek@gmail.com)
 * Copyright 2009 Wojciech Treter (juzefwt@gmail.com)
 * Copyright 2008, 2009, 2010, 2011 Piotr Galiszewski (piotr.galiszewski@kadu.im)
 * Copyright 2004, 2005, 2006, 2007 Marcin Ślusarz (joi@kadu.net)
 * Copyright 2004 Adrian Smarzewski (adrian@kadu.net)
 * Copyright 2007, 2008, 2009, 2010, 2011 Rafał Malinowski (rafal.przemyslaw.malinowski@gmail.com)
 * Copyright 2008, 2009 Michał Podsiadlik (michal@kadu.net)
 * Copyright 2010 Tomasz Rostański (rozteck@interia.pl)
 * Copyright 2009 Bartłomiej Zimoń (uzi18@o2.pl)
 * Copyright 2005 Paweł Płuciennik (pawel_p@kadu.net)
 * %kadu copyright end%
 *
 * This program 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 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <QtGui/QApplication>

#ifdef Q_WS_X11
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusReply>
#include <QtGui/QX11Info>
#endif

#include "accounts/account.h"
#include "accounts/account-manager.h"
#include "buddies/buddy-manager.h"
#include "buddies/buddy-shared.h"
#include "buddies/group.h"
#include "buddies/group-manager.h"
#include "chat/message/message.h"
#include "configuration/configuration-file.h"
#include "contacts/contact.h"
#include "contacts/contact-manager.h"
#include "core/core.h"
#include "gui/actions/action.h"
#include "gui/widgets/buddies-list-view-menu-manager.h"
#include "gui/widgets/chat-widget-manager.h"
#include "gui/widgets/custom-input.h"
#include "gui/windows/kadu-window.h"
#include "gui/windows/main-configuration-window.h"
#include "gui/windows/main-window.h"
#include "gui/windows/message-dialog.h"
#include "multilogon/multilogon-session.h"
#include "notify/account-notification.h"
#include "notify/buddy-notify-data.h"
#include "notify/multilogon-notification.h"
#include "notify/notification.h"
#include "notify/notifier.h"
#include "notify/notify-configuration-ui-handler.h"
#include "notify/window-notifier.h"
#include "protocols/services/chat-service.h"
#include "protocols/services/multilogon-service.h"
#include "status/status-container-manager.h"

#include "activate.h"
#include "debug.h"
#include "misc/misc.h"

#include "new-message-notification.h"
#include "status-changed-notification.h"

#ifdef Q_WS_X11
#include "os/x11tools.h" // this should be included as last one,
#undef Status            // and Status defined by Xlib.h must be undefined
#endif

#define FULLSCREENCHECKTIMER_INTERVAL 2000 /*ms*/

NotificationManager *NotificationManager::Instance = 0;

NotificationManager * NotificationManager::instance()
{
      if (!Instance)
      {
            Instance = new NotificationManager();
            Instance->init();

            MessageNotification::registerEvents();
            StatusChangedNotification::registerEvents();
            MultilogonNotification::registerEvents();
      }

      return Instance;
}

NotificationManager::NotificationManager()
{
#if defined(Q_WS_X11) && !defined(Q_WS_MAEMO_5)
      x11display = XOpenDisplay(0);
#endif
}

void NotificationManager::init()
{
      kdebugf();

      Notification::registerParserTags();
      AccountNotification::registerParserTags();

      UiHandler = new NotifyConfigurationUiHandler(this);
      MainConfigurationWindow::registerUiHandler(UiHandler);

      SilentMode = false;

      FullScreenCheckTimer.setInterval(FULLSCREENCHECKTIMER_INTERVAL);
      connect(&FullScreenCheckTimer, SIGNAL(timeout()), this, SLOT(checkFullScreen()));
      IsFullScreen = false;

      createDefaultConfiguration();
      AutoSilentMode = false;
      //TODO 0.10.0:
      //triggerAllAccountsRegistered();

      notifyAboutUserActionDescription = new ActionDescription(this,
            ActionDescription::TypeUser, "notifyAboutUserAction",
            this, SLOT(notifyAboutUserActionActivated(QAction *, bool)),
            KaduIcon("kadu_icons/notify-about-buddy"), tr("Notify About Buddy"), true,
            checkNotify
      );

      SilentModeActionDescription = new ActionDescription(this,
            ActionDescription::TypeGlobal, "silentModeAction",
            this, SLOT(silentModeActionActivated(QAction *, bool)),
            KaduIcon("kadu_icons/enable-notifications"), tr("Enable Notifications"), true
      );
      configurationUpdated();
      connect(SilentModeActionDescription, SIGNAL(actionCreated(Action *)), this, SLOT(silentModeActionCreated(Action *)));

      connect(StatusContainerManager::instance(), SIGNAL(statusUpdated()), this, SLOT(statusUpdated()));

      foreach (const Group &group, GroupManager::instance()->items())
            groupAdded(group);

      new WindowNotifier(this);
      kdebugf2();
}

NotificationManager::~NotificationManager()
{
      kdebugf();

      FullScreenCheckTimer.stop();

      MainConfigurationWindow::unregisterUiHandler(UiHandler);

      StatusChangedNotification::unregisterEvents();
      MessageNotification::unregisterEvents();
      MultilogonNotification::unregisterEvents();

      triggerAllAccountsUnregistered();

      while (!Notifiers.isEmpty())
      {
            kdebugm(KDEBUG_WARNING, "WARNING: not unregistered notifiers found! (%u)\n", Notifiers.size());
            unregisterNotifier(Notifiers.at(0));
      }

      AccountNotification::unregisterParserTags();
      Notification::unregisterParserTags();

#if defined(Q_WS_X11) && !defined(Q_WS_MAEMO_5)
      XCloseDisplay(x11display);
#endif

      kdebugf2();
}

void NotificationManager::setSilentMode(bool silentMode)
{
      if (silentMode != SilentMode)
      {
            SilentMode = silentMode;
            foreach (Action *action, SilentModeActionDescription->actions())
                  action->setChecked(!silentMode);

            config_file.writeEntry("Notify", "SilentMode", SilentMode);

            emit silentModeToggled(SilentMode);
      }
}

bool NotificationManager::silentMode()
{
      return SilentMode || (IsFullScreen && config_file.readBoolEntry("Notify", "FullscreenSilentMode", false));
}

void NotificationManager::notifyAboutUserActionActivated(QAction *sender, bool toggled)
{
      Q_UNUSED(toggled)

      kdebugf();

      Action *action = qobject_cast<Action *>(sender);
      if (!action)
            return;

      BuddySet buddies = action->buddies();

      bool on = true;
      foreach (const Buddy &buddy, buddies)
      {
            BuddyNotifyData *bnd = 0;
            if (buddy.data())
                  bnd = buddy.data()->moduleStorableData<BuddyNotifyData>("notify", this, false);

            if (!bnd || !bnd->notify())
            {
                  on = false;
                  break;
            }
      }

      if (NotifyAboutAll)
      {
            NotifyAboutAll = false;
            config_file.writeEntry("Notify", "NotifyAboutAll", false);
      }

      foreach (const Buddy &buddy, buddies)
      {
            if (buddy.isNull() || buddy.isAnonymous())
                  continue;

            BuddyNotifyData *bnd = 0;
            if (buddy.data())
                  bnd = buddy.data()->moduleStorableData<BuddyNotifyData>("notify", this, true);
            if (!bnd)
                  continue;

            if (bnd->notify() == on)
            {
                  bnd->setNotify(!on);
                  bnd->store();
            }
      }

      foreach (Action *action, notifyAboutUserActionDescription->actions())
            if (action->contacts().toBuddySet() == buddies)
                  action->setChecked(!on);

      kdebugf2();
}

void NotificationManager::silentModeActionCreated(Action *action)
{
      action->setChecked(!SilentMode);
}

void NotificationManager::silentModeActionActivated(QAction *sender, bool toggled)
{
      Q_UNUSED(sender)

      setSilentMode(!toggled);
}

void NotificationManager::statusUpdated()
{
      if (SilentModeWhenDnD && !silentMode() && StatusContainerManager::instance()->status().type() == "DoNotDisturb")
      {
            foreach (Action *action, SilentModeActionDescription->actions())
                  action->setChecked(false);

            AutoSilentMode = true;
      }
      else if (!silentMode() && AutoSilentMode)
      {
            foreach (Action *action, SilentModeActionDescription->actions())
                  action->setChecked(true);

            AutoSilentMode = false;
      }
}

void NotificationManager::accountRegistered(Account account)
{
      Protocol *protocol = account.protocolHandler();
      if (!protocol)
            return;

      connect(account, SIGNAL(buddyStatusChanged(Contact, Status)),
                  this, SLOT(contactStatusChanged(Contact, Status)));
      connect(account, SIGNAL(connected()), this, SLOT(accountConnected()));

      ChatService *chatService = protocol->chatService();
      if (chatService)
            connect(chatService, SIGNAL(messageReceived(const Message &)),
                        this, SLOT(messageReceived(const Message &)));

      MultilogonService *multilogonService = protocol->multilogonService();
      if (multilogonService)
      {
            connect(multilogonService, SIGNAL(multilogonSessionConnected(MultilogonSession*)),
                        this, SLOT(multilogonSessionConnected(MultilogonSession*)));
            connect(multilogonService, SIGNAL(multilogonSessionDisconnected(MultilogonSession*)),
                        this, SLOT(multilogonSessionDisconnected(MultilogonSession*)));
      }
}

void NotificationManager::accountUnregistered(Account account)
{
      Protocol *protocol = account.protocolHandler();

      if (!protocol)
            return;

      disconnect(account, SIGNAL(buddyStatusChanged(Contact, Status)),
                  this, SLOT(contactStatusChanged(Contact, Status)));
      disconnect(account, SIGNAL(connected()), this, SLOT(accountConnected()));

      ChatService *chatService = protocol->chatService();
      if (chatService)
            disconnect(chatService, SIGNAL(messageReceived(const Message &)),
                        this, SLOT(messageReceived(const Message &)));

      MultilogonService *multilogonService = protocol->multilogonService();
      if (multilogonService)
      {
            disconnect(multilogonService, SIGNAL(multilogonSessionConnected(MultilogonSession*)),
                        this, SLOT(multilogonSessionConnected(MultilogonSession*)));
            disconnect(multilogonService, SIGNAL(multilogonSessionDisconnected(MultilogonSession*)),
                        this, SLOT(multilogonSessionDisconnected(MultilogonSession*)));
      }
}

void NotificationManager::accountConnected()
{
      Account account(sender());
      if (!account)
            return;

      if (NotifyIgnoreOnConnection)
      {
            QDateTime *dateTime = account.data()->moduleData<QDateTime>("notify-account-connected", true);
            *dateTime = QDateTime::currentDateTime().addSecs(10);
      }
}

void NotificationManager::contactStatusChanged(Contact contact, Status oldStatus)
{
      kdebugf();

      if (!contact.contactAccount())
            return;

      Protocol *protocol = contact.contactAccount().protocolHandler();
      if (!protocol || !protocol->isConnected())
            return;

      if (NotifyIgnoreOnConnection)
      {
            QDateTime *dateTime = contact.contactAccount().data()->moduleData<QDateTime>("notify-account-connected");
            if (dateTime && (*dateTime >= QDateTime::currentDateTime()))
                  return;
      }

      bool notify_contact = true;
      BuddyNotifyData *bnd = 0;
      bnd = contact.ownerBuddy().data()->moduleStorableData<BuddyNotifyData>("notify", this, false);

      if (!bnd || !bnd->notify())
            notify_contact = false;

      if (!notify_contact && !NotifyAboutAll)
      {
            kdebugmf(KDEBUG_FUNCTION_END, "end: not notifying user AND not notifying all users\n");
            return;
      }

      if (!contact.contactAccount())
            return;

      if (contact == contact.contactAccount().accountContact()) // myself
            return;

      Status status = contact.currentStatus();
      if (oldStatus == status)
            return;

      if (IgnoreOnlineToOnline &&
                  !status.isDisconnected() &&
                  !oldStatus.isDisconnected())
            return;

      QString changedTo = "/To" + status.type();

      StatusChangedNotification *statusChangedNotification = new StatusChangedNotification(changedTo, contact);

      notify(statusChangedNotification);

      kdebugf2();
}

void NotificationManager::messageReceived(const Message &message)
{
      kdebugf();

      ChatWidget *chatWidget = ChatWidgetManager::instance()->byChat(message.messageChat());
      if (!chatWidget)
            notify(new MessageNotification(MessageNotification::NewChat, message));
      else if (!NewMessageOnlyIfInactive || !_isWindowActiveOrFullyVisible(chatWidget))
            notify(new MessageNotification(MessageNotification::NewMessage, message));

      kdebugf2();
}

void NotificationManager::multilogonSessionConnected(MultilogonSession *session)
{
      MultilogonNotification::notifyMultilogonSessionConnected(session);
}

void NotificationManager::multilogonSessionDisconnected(MultilogonSession *session)
{
      MultilogonNotification::notifyMultilogonSessionDisonnected(session);
}

void NotificationManager::registerNotifyEvent(NotifyEvent *notifyEvent)
{
      kdebugf();

      NotifyEvents.append(notifyEvent);
      emit notifyEventRegistered(notifyEvent);

      kdebugf2();
}

void NotificationManager::unregisterNotifyEvent(NotifyEvent *notifyEvent)
{
      kdebugf();

      NotifyEvents.removeAll(notifyEvent);
      emit notifyEventUnregistered(notifyEvent);

      kdebugf2();
}

void NotificationManager::registerNotifier(Notifier *notifier)
{
      kdebugf();
      if (Notifiers.contains(notifier))
      {
            kdebugm(KDEBUG_WARNING, "WARNING: '%s' already exists in notifiers! "
            "strange... unregistering old Notifier\n", qPrintable(notifier->name()));

            unregisterNotifier(notifier);
      }

      Notifiers.append(notifier);
      emit notiferRegistered(notifier);

      kdebugf2();
}

void NotificationManager::unregisterNotifier(Notifier *notifier)
{
      kdebugf();

      if (!Notifiers.contains(notifier))
      {
            kdebugm(KDEBUG_WARNING, "WARNING: '%s' not registered!\n", qPrintable(notifier->name()));
            return;
      }

      emit notiferUnregistered(notifier);
      Notifiers.removeAll(notifier);

      kdebugf2();
}

const QList<Notifier *> & NotificationManager::notifiers() const
{
      return Notifiers;
}

const QList<NotifyEvent *> & NotificationManager::notifyEvents() const
{
      return NotifyEvents;
}

bool NotificationManager::ignoreNotifications()
{
      if (silentMode())
            return true;

      if (AutoSilentMode)
            return true;

      return false;
}

void NotificationManager::notify(Notification *notification)
{
      kdebugf();

      QString notifyType = notification->key();
      bool foundNotifier = false;
      bool foundNotifierWithCallbackSupported = !notification->requireCallback();

      if (ignoreNotifications())
      {
            notification->callbackDiscard();
            return;
      }

      notification->acquire();

      foreach (Notifier *notifier, Notifiers)
      {
            if (config_file.readBoolEntry("Notify", notifyType + '_' + notifier->name()))
            {
                  notifier->notify(notification);
                  foundNotifier = true;
                  foundNotifierWithCallbackSupported = foundNotifierWithCallbackSupported ||
                              (Notifier::CallbackSupported == notifier->callbackCapacity());
            }
      }

      if (!foundNotifierWithCallbackSupported)
            foreach (Notifier *notifier, Notifiers)
            {
                  if (Notifier::CallbackSupported == notifier->callbackCapacity())
                  {
                        notifier->notify(notification);
                        foundNotifier = true;
                        foundNotifierWithCallbackSupported = true;
                        break;
                  }
            }

      if (!foundNotifier)
            notification->callbackDiscard();

      notification->release();

      if (!foundNotifierWithCallbackSupported)
            MessageDialog::show(KaduIcon("dialog-warning"), tr("Kadu"), tr("Unable to find notifier for %1 event").arg(notification->type()));

      kdebugf2();
}

void NotificationManager::groupAdded(const Group &group)
{
      connect(group, SIGNAL(updated()), this, SLOT(groupUpdated()));
}

void NotificationManager::groupUpdated()
{
      Group group = sender();
      if (group.isNull())
            return;

      bool notify = group.notifyAboutStatusChanges();

      if (NotifyAboutAll && !notify)
      {
            NotifyAboutAll = false;
            config_file.writeEntry("Notify", "NotifyAboutAll", false);
      }

      foreach (const Buddy &buddy, BuddyManager::instance()->items())
      {
            if (buddy.isNull() || buddy.isAnonymous() || buddy.groups().contains(group))
                  continue;

            BuddyNotifyData *bnd = 0;
            if (buddy.data())
                  buddy.data()->moduleStorableData<BuddyNotifyData>("notify", this, true);
            if (!bnd)
                  continue;

            bnd->setNotify(notify);
            bnd->store();
      }
}

void NotificationManager::configurationUpdated()
{
      NotifyAboutAll = config_file.readBoolEntry("Notify", "NotifyAboutAll");
      NewMessageOnlyIfInactive = config_file.readBoolEntry("Notify", "NewMessageOnlyIfInactive");
      NotifyIgnoreOnConnection = config_file.readBoolEntry("Notify", "NotifyIgnoreOnConnection");
      IgnoreOnlineToOnline = config_file.readBoolEntry("Notify", "IgnoreOnlineToOnline");
      SilentModeWhenDnD = config_file.readBoolEntry("Notify", "AwaySilentMode", false);
      SilentModeWhenFullscreen = config_file.readBoolEntry("Notify", "FullscreenSilentMode", false);
      setSilentMode(config_file.readBoolEntry("Notify", "SilentMode", false));
#if defined(Q_WS_X11) && !defined(Q_WS_MAEMO_5)
      if (SilentModeWhenFullscreen)
            FullScreenCheckTimer.start();
      else
      {
            FullScreenCheckTimer.stop();
            IsFullScreen = false;
      }
#endif
}

void NotificationManager::createDefaultConfiguration()
{
      config_file.addVariable("Notify", "IgnoreOnlineToOnline", false);
      config_file.addVariable("Notify", "NewMessageOnlyIfInactive", true);
      config_file.addVariable("Notify", "NotifyAboutAll", true);
      config_file.addVariable("Notify", "NotifyIgnoreOnConnection", true);
}

QString NotificationManager::notifyConfigurationKey(const QString &eventType)
{
      QString event = eventType;

      while (true)
      {
            int slashPosition = event.lastIndexOf('/');
            if (-1 == slashPosition)
                  return event;

            if (config_file.readBoolEntry("Notify", event + "_UseCustomSettings", false))
                  return event;

            event = event.left(slashPosition);
      }
}

ConfigurationUiHandler * NotificationManager::configurationUiHandler()
{
      return UiHandler;
}

void NotificationManager::checkFullScreen()
{
#if defined(Q_WS_X11) && !defined(Q_WS_MAEMO_5)
      bool wasSilent = silentMode();
      IsFullScreen = X11_checkFullScreen(x11display) && (!isScreenSaverRunning());
      if (silentMode() != wasSilent)
            emit silentModeToggled(silentMode());
#endif
}

bool NotificationManager::isScreenSaverRunning()
{
#if defined(Q_WS_X11) && !defined(Q_WS_MAEMO_5)
      // org.freedesktop.ScreenSaver
      {
            QDBusInterface dbus("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver", QDBusConnection::sessionBus());
            if (dbus.isValid())
            {
                  QDBusReply<bool> reply = dbus.call("GetActive");
                  if (reply.isValid() && reply.value())
                        return true;
            }
      }
      // org.kde.screensaver
      {
            QDBusInterface dbus("org.kde.screensaver", "/ScreenSaver", "org.freedesktop.ScreenSaver", QDBusConnection::sessionBus());
            if (dbus.isValid())
            {
                  QDBusReply<bool> reply = dbus.call("GetActive");
                  if (reply.isValid() && reply.value())
                        return true;
            }
      }
      // org.gnome.ScreenSaver
      {
            QDBusInterface dbus("org.gnome.ScreenSaver", "/", "org.gnome.ScreenSaver", QDBusConnection::sessionBus());
            if (dbus.isValid())
            {
                  QDBusReply<bool> reply = dbus.call("GetActive");
                  if (reply.isValid() && reply.value())
                        return true;
            }
      }
#endif
      // no screensaver
      return false;
}

void checkNotify(Action *action)
{
      kdebugf();

      action->setEnabled(action->buddies().count());

      bool on = true;
      foreach (const Buddy &buddy, action->contacts().toBuddySet())
      {
            BuddyNotifyData *bnd = 0;
            if (buddy.data())
                  bnd = buddy.data()->moduleStorableData<BuddyNotifyData>("notify", NotificationManager::instance(), false);

            if (!bnd || !bnd->notify())
            {
                  on = false;
                  break;
            }
      }

      action->setChecked(on);

      kdebugf2();
}

Generated by  Doxygen 1.6.0   Back to index