/*
    kopetewindow.cpp  -  Kopete Main Window

    Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
    Copyright (c) 2001-2002 by Stefan Gehn            <metz AT gehn.net>
    Copyright (c) 2002-2003 by Martijn Klingens       <klingens@kde.org>
    Copyright (c) 2002-2009 by Olivier Goffart        <ogoffart at kde.org>
    Copyright (c) 2005-2006 by Will Stephenson        <wstephenson@kde.org>
    Copyright (c) 2008      by Roman Jarosz           <kedgedev@centrum.cz>

    Kopete    (c) 2002-2008 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * 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.                                   *
    *                                                                       *
    *************************************************************************
*/

#include "kopetewindow.h"

#include <QUrl>
#include <QIcon>
#include <QMenu>
#include <QTimer>
#include <QEvent>
#include <QLabel>
#include <QAction>
#include <QCursor>
#include <QLayout>
#include <QPixmap>
#include <QLineEdit>
#include <QTextEdit>
#include <QCloseEvent>
#include <QMouseEvent>
#include <QShowEvent>
#include <QSignalMapper>
#include <QStatusBar>

#include <khbox.h>
#include <kvbox.h>
#include <KIcon>
#include <kaction.h>
#include <kactionmenu.h>
#include <kactioncollection.h>
#include <ktoggleaction.h>
#include <kconfig.h>
#include <kdebug.h>
#include <KGlobal>
//#include <KGlobalAccel>
#include <KLocalizedString>
#include <kiconloader.h>
#include <kmessagebox.h>
#include <knotifyconfigwidget.h>
#include <KShortcut>
#include <kshortcutsdialog.h>
#include <kedittoolbar.h>
#include <QMenuBar>
#include <kwindowsystem.h>

#include <kplugininfo.h>
#include <ksqueezedtextlabel.h>
#include <kstringhandler.h>
#include <kxmlguifactory.h>
#include <ktoolbar.h>
#include <kdialog.h>
#include <kstandardaction.h>
#include <solid/networking.h>
#include <kstatusbarofflineindicator.h>
#include <kfilterproxysearchline.h>
#include <KSharedConfig>

#include "addcontactpage.h"
#include "addressbooklinkwidget.h"
#include "ui_groupkabcselectorwidget.h"
#include "kabcexport.h"
#include "kopeteappearancesettings.h"
#include "kopeteapplication.h"
#include "kopeteaccount.h"
#include "kopeteaccountmanager.h"
#include "kopeteaccountstatusbaricon.h"
#include "kopeteidentitystatusbaricon.h"
#include "kopetebehaviorsettings.h"
#include "kopetecontact.h"
#include "kopetecontactlist.h"
#include "kopetegroup.h"
#include "kopeteidentity.h"
#include "kopeteidentitymanager.h"
#include "kopetelistviewsearchline.h"
#include "kopetechatsessionmanager.h"
#include "kopetepluginmanager.h"
#include "kopeteprotocol.h"
#include "kopetestdaction.h"
#include "kopeteuiglobal.h"
#include "systemtray.h"
#include "kopeteonlinestatusmanager.h"
#include "identitystatuswidget.h"
#include "kopetestatusmanager.h"
#include "kopetestatusrootaction.h"
#include "kopetestatuseditaction.h"
#include "kopeteemoticons.h"
#include "kopeteinfoeventmanager.h"
#include "infoeventwidget.h"
#include "contactlisttreemodel.h"
#include "contactlistplainmodel.h"
#include "contactlistproxymodel.h"
#include "kopeteitemdelegate.h"
#include "kopetemetacontact.h"
#include "kopetecontactlistview.h"
#include "kopetestatusitems.h"

//BEGIN GlobalStatusMessageIconLabel
GlobalStatusMessageIconLabel::GlobalStatusMessageIconLabel (QWidget *parent)
    : QLabel(parent)
{
    setCursor(QCursor(Qt::PointingHandCursor));
    setFixedSize(16, 16);
    setPixmap(SmallIcon(QStringLiteral("im-status-message-edit")));
    setToolTip(i18n("Global status message"));
}

void GlobalStatusMessageIconLabel::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) {
        emit iconClicked(event->globalPos());
        event->accept();
    }
}

//END GlobalStatusMessageIconLabel

//BEGIN InfoEventIconLabel
InfoEventIconLabel::InfoEventIconLabel(QWidget *parent)
    : QLabel(parent)
{
    setCursor(QCursor(Qt::PointingHandCursor));
    setFixedSize(16, 16);
    setPixmap(SmallIcon(QStringLiteral("flag-black")));
    setToolTip(i18n("Service messages"));

    connect(Kopete::InfoEventManager::self(), SIGNAL(changed()), this, SLOT(updateIcon()));
}

void InfoEventIconLabel::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton || event->button() == Qt::RightButton) {
        emit clicked();
        event->accept();
    }
}

void InfoEventIconLabel::updateIcon()
{
    if (Kopete::InfoEventManager::self()->eventCount() > 0) {
        setPixmap(SmallIcon(QStringLiteral("flag-green")));
    } else {
        setPixmap(SmallIcon(QStringLiteral("flag-black")));
        emit clicked();
    }
}

//END InfoEventIconLabel

class KopeteWindow::Private
{
public:
    Private()
        : contactlist(0)
        , model(0)
        , proxyModel(0)
        , identitywidget(0)
        , infoEventWidget(0)
        , actionAddContact(0)
        , actionDisconnect(0)
        , actionExportContacts(0)
        , actionStatusMenu(0)
        , actionDockMenu(0)
        , actionSetAway(0)
        , actionSetBusy(0)
        , actionSetAvailable(0)
        , actionSetInvisible(0)
        , actionPrefs(0)
        , actionQuit(0)
        , actionSave(0)
        , menubarAction(0)
        , statusbarAction(0)
        , actionShowOfflineUsers(0)
        , actionShowEmptyGroups(0)
        , docked(0)
        , deskRight(0)
        , statusBarWidget(0)
        , tray(0)
        , hidden(false)
        , autoHide(false)
        , autoHideTimeout(0)
        , autoHideTimer(0)
        , addContactMapper(0)
        , showIdentityIcons(Kopete::AppearanceSettings::self()->showIdentityIcons())
        , globalStatusMessage(0)
    {
    }

    ~Private()
    {
    }

    KopeteContactListView *contactlist;
    Kopete::UI::ContactListModel *model;
    Kopete::UI::ContactListProxyModel *proxyModel;

    IdentityStatusWidget *identitywidget;
    InfoEventWidget *infoEventWidget;

    // Some Actions
    KActionMenu *actionAddContact;

    QAction *actionDisconnect;
    QAction *actionExportContacts;

    KActionMenu *actionStatusMenu;
    KActionMenu *actionDockMenu;
    QAction *actionSetAway;
    QAction *actionSetBusy;
    QAction *actionSetAvailable;
    QAction *actionSetInvisible;

    QAction *actionPrefs;
    QAction *actionQuit;
    QAction *actionSave;
    KToggleAction *menubarAction;
    KToggleAction *statusbarAction;
    KToggleAction *actionShowAllOfflineEmpty;
    KToggleAction *actionShowOfflineUsers;
    KToggleAction *actionShowEmptyGroups;

    int docked;
    int deskRight;
    QPoint position;
    KHBox *statusBarWidget;
    KopeteSystemTray *tray;
    bool appDestroyed;
    bool hidden;
    bool autoHide;
    unsigned int autoHideTimeout;
    QTimer *autoHideTimer;
    QTimer *autoResizeTimer;
    QSignalMapper *addContactMapper;

    bool showIdentityIcons;
    QHash<const Kopete::Identity *, KopeteIdentityStatusBarIcon *> identityStatusBarIcons;
    QHash<const Kopete::Account *, KopeteAccountStatusBarIcon *> accountStatusBarIcons;
    KSqueezedTextLabel *globalStatusMessage;
};

/* KMainWindow is very broken from our point of view - it deref()'s the app
 * when the last visible KMainWindow is destroyed. But when our main window is
 * hidden when it's in the tray,closing the last chatwindow would cause the app
 * to quit. - Richard
 *
 * Fortunately KMainWindow checks queryExit before deref()ing the Kapplication.
 * KopeteWindow reimplements queryExit() and only returns true if it is shutting down
 * (either because the user quit Kopete, or the session manager did).
 *
 * KopeteWindow and ChatWindows are closed by session management.
 * App shutdown is not performed by the KopeteWindow but by KopeteApplication:
 * 1) user quit - KopeteWindow::slotQuit() was called, calls KopeteApplication::quitKopete(),
 *                which closes all chatwindows and the KopeteWindow.  The last window to close
 *                shuts down the PluginManager in queryExit().  When the PluginManager has completed its
 *                shutdown, the app is finally deref()ed, and the contact list and accountmanager
 *                are saved.
 *                and calling KApplication::quit()
 * 2) session   - KopeteWindow and all chatwindows are closed by KApplication session management.
 *     quit        Then the shutdown proceeds as above.
 *
 * queryClose() is honoured so groupchats and chats receiving recent messages can interrupt
 * (session) quit.
 */

KopeteWindow::KopeteWindow (QWidget *parent)
    : KXmlGuiWindow(parent)
    , d(new Private)
{
    d->appDestroyed = false;
    connect(qApp, SIGNAL(destroyed()), this, SLOT(slotAppDestroyed()));
    setAttribute(Qt::WA_DeleteOnClose, false);
    setAttribute(Qt::WA_QuitOnClose, false);
    // Applications should ensure that their StatusBar exists before calling createGUI()
    // so that the StatusBar is always correctly positioned when KDE is configured to use
    // a MacOS-style MenuBar.
    // This fixes a "statusbar drawn over the top of the toolbar" bug
    // e.g. it can happen when you switch desktops on Kopete startup
    d->statusBarWidget = new KHBox(statusBar());
    d->statusBarWidget->setMargin(2);
    d->statusBarWidget->setSpacing(1);
    window()->setAttribute(Qt::WA_AlwaysShowToolTips);
    statusBar()->addPermanentWidget(d->statusBarWidget, 0);
    QWidget *statusBarMessage = new QWidget(statusBar());
    QHBoxLayout *statusBarMessageLayout = new QHBoxLayout(statusBarMessage);
    statusBarMessageLayout->setMargin(2);

    KStatusBarOfflineIndicator *indicator = new KStatusBarOfflineIndicator(this);
    statusBar()->addPermanentWidget(indicator, 0);

    GlobalStatusMessageIconLabel *label = new GlobalStatusMessageIconLabel(statusBarMessage);
    connect(label, SIGNAL(iconClicked(QPoint)),
            this, SLOT(slotGlobalStatusMessageIconClicked(QPoint)));
    statusBarMessageLayout->addWidget(label);
    statusBarMessageLayout->addSpacing(1);

    InfoEventIconLabel *infoLabel = new InfoEventIconLabel(statusBarMessage);
    connect(infoLabel, SIGNAL(clicked()), this, SLOT(slotInfoIconClicked()));
    statusBarMessageLayout->addWidget(infoLabel);
    statusBarMessageLayout->addSpacing(1);
    connect(Kopete::InfoEventManager::self(), SIGNAL(eventAdded(Kopete::InfoEvent*)), this, SLOT(slotNewInfoEvent()));

    d->globalStatusMessage = new KSqueezedTextLabel(statusBarMessage);
    connect(Kopete::StatusManager::self(), SIGNAL(globalStatusChanged()),
            this, SLOT(globalStatusChanged()));
    statusBarMessageLayout->addWidget(d->globalStatusMessage);
    statusBar()->addWidget(statusBarMessage, 1);

    d->autoHideTimer = new QTimer(this);
    d->autoResizeTimer = new QTimer(this);
    d->autoResizeTimer->setSingleShot(true);

    // --------------------------------------------------------------------------------
    initView();
    initActions();
    d->contactlist->initActions(actionCollection());
    initSystray();
    // --------------------------------------------------------------------------------

    // Trap all loaded plugins, so we can add their status bar icons accordingly , also used to add XMLGUIClient
    connect(Kopete::PluginManager::self(), SIGNAL(pluginLoaded(Kopete::Plugin*)),
            this, SLOT(slotPluginLoaded(Kopete::Plugin*)));
    connect(Kopete::PluginManager::self(), SIGNAL(allPluginsLoaded()),
            this, SLOT(slotAllPluginsLoaded()));

    // Connect all identity signals
    connect(Kopete::IdentityManager::self(), SIGNAL(identityRegistered(Kopete::Identity*)),
            this, SLOT(slotIdentityRegistered(Kopete::Identity*)));
    connect(Kopete::IdentityManager::self(), SIGNAL(identityUnregistered(const Kopete::Identity*)),
            this, SLOT(slotIdentityUnregistered(const Kopete::Identity*)));

    connect(d->autoHideTimer, SIGNAL(timeout()), this, SLOT(slotAutoHide()));
    connect(d->contactlist, SIGNAL(visibleContentHeightChanged()), this, SLOT(slotStartAutoResizeTimer()));
    connect(d->autoResizeTimer, SIGNAL(timeout()), this, SLOT(slotUpdateSize()));
    connect(Kopete::AppearanceSettings::self(), SIGNAL(contactListAppearanceChanged()),
            this, SLOT(slotContactListAppearanceChanged()));
    createGUI(QStringLiteral("kopeteui.rc"));

    // call this _after_ createGUI(), otherwise menubar is not set up correctly
    loadOptions();

    // If some plugins are already loaded, merge the GUI
    Kopete::PluginList plugins = Kopete::PluginManager::self()->loadedPlugins();
    foreach (Kopete::Plugin *plug, plugins) {
        slotPluginLoaded(plug);
    }

    // If some identity already registered, build the status icon
    Kopete::Identity::List identityList = Kopete::IdentityManager::self()->identities();
    foreach (Kopete::Identity *i, identityList) {
        slotIdentityRegistered(i);
    }

    //install an event filter for the quick search toolbar so we can
    //catch the hide events
    toolBar(QStringLiteral("quickSearchBar"))->installEventFilter(this);
}

void KopeteWindow::slotAppDestroyed()
{
    d->appDestroyed = true;
}

void KopeteWindow::initView()
{
    QWidget *w = new QWidget(this);
    QVBoxLayout *l = new QVBoxLayout(w);
    d->contactlist = new KopeteContactListView(w);

    if (Kopete::AppearanceSettings::self()->groupContactByGroup()) {
        d->model = new Kopete::UI::ContactListTreeModel(this);
    } else {
        d->model = new Kopete::UI::ContactListPlainModel(this);
    }

    d->model->init();
    d->proxyModel = new Kopete::UI::ContactListProxyModel(this);
    d->proxyModel->setSourceModel(d->model);
    d->contactlist->setModel(d->proxyModel);
    l->addWidget(d->contactlist);
    l->setSpacing(0);
    l->setContentsMargins(0, 0, 0, 0);
    d->identitywidget = new IdentityStatusWidget(0, w);
    d->identitywidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum));
    d->identitywidget->setVisible(false);
    l->addWidget(d->identitywidget);
    d->infoEventWidget = new InfoEventWidget(w);
    d->infoEventWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum));
    d->infoEventWidget->setVisible(false);
    connect(d->infoEventWidget, SIGNAL(showRequest()), this, SLOT(slotShowInfoEventWidget()));
    l->addWidget(d->infoEventWidget);

    setCentralWidget(w);
    d->contactlist->setFocus();
}

void KopeteWindow::initActions()
{
    // this action menu contains one action per account and is updated when accounts are registered/unregistered
    d->actionAddContact = new KActionMenu(QIcon::fromTheme(QStringLiteral("list-add-user")), i18n("&Add Contact"), this);
    d->actionAddContact->setIconText(i18n("Add"));
    actionCollection()->addAction(QStringLiteral("AddContact"), d->actionAddContact);
    d->actionAddContact->setDelayed(false);
    // this signal mapper is needed to call slotAddContact with the correct arguments
    d->addContactMapper = new QSignalMapper(this);
    connect(d->addContactMapper, SIGNAL(mapped(QString)),
            this, SLOT(slotAddContactDialogInternal(QString)));

    d->actionDisconnect = new QAction(QIcon::fromTheme(QStringLiteral("user-offline")), QString(i18n("Offline")), this);
    actionCollection()->addAction(QStringLiteral("DisconnectAll"), d->actionDisconnect);
    connect(d->actionDisconnect, SIGNAL(triggered(bool)), this, SLOT(slotDisconnectAll()));
    d->actionDisconnect->setEnabled(false);

    d->actionExportContacts = new QAction(i18n("&Export Contacts..."), this);
    d->actionExportContacts->setIcon(QIcon("document-export"));
    actionCollection()->addAction(QStringLiteral("ExportContacts"), d->actionExportContacts);
    connect(d->actionExportContacts, SIGNAL(triggered(bool)), this, SLOT(showExportDialog()));

    d->actionSetAway = new QAction(QIcon::fromTheme(QStringLiteral("user-away")), i18n("&Away"), this);
    actionCollection()->addAction(QStringLiteral("SetAwayAll"), d->actionSetAway);
    connect(d->actionSetAway, SIGNAL(triggered(bool)), this, SLOT(slotGlobalAway()));

    d->actionSetBusy = new QAction(QIcon::fromTheme(QStringLiteral("user-busy")), i18n("&Busy"), this);
    actionCollection()->addAction(QStringLiteral("SetBusyAll"), d->actionSetBusy);
    connect(d->actionSetBusy, SIGNAL(triggered(bool)), this, SLOT(slotGlobalBusy()));

    d->actionSetInvisible = new QAction(QIcon::fromTheme(QStringLiteral("user-invisible")), i18n("&Invisible"), this);
    actionCollection()->addAction(QStringLiteral("SetInvisibleAll"), d->actionSetInvisible);
    connect(d->actionSetInvisible, SIGNAL(triggered(bool)), this, SLOT(slotSetInvisibleAll()));

    d->actionSetAvailable = new QAction(QIcon::fromTheme(QStringLiteral("user-online")), i18n("&Online"), this);
    actionCollection()->addAction(QStringLiteral("SetAvailableAll"), d->actionSetAvailable);
    connect(d->actionSetAvailable, SIGNAL(triggered(bool)), this, SLOT(slotGlobalAvailable()));

    d->actionStatusMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("user-online")), i18n("&Set Status"), this);
    d->actionStatusMenu->setIconText(i18n("Status"));
    actionCollection()->addAction(QStringLiteral("Status"), d->actionStatusMenu);
    d->actionStatusMenu->setDelayed(false);

    // Will be automatically deleted when the actionStatusMenu is deleted.
    Kopete::StatusRootAction *statusAction = new Kopete::StatusRootAction(d->actionStatusMenu);

    connect(statusAction, SIGNAL(changeStatus(uint,Kopete::StatusMessage)),
            this, SLOT(setOnlineStatus(uint,Kopete::StatusMessage)));
    connect(statusAction, SIGNAL(updateMessage(Kopete::StatusRootAction*)),
            this, SLOT(updateStatusMenuMessage(Kopete::StatusRootAction*)));
    connect(statusAction, SIGNAL(changeMessage(Kopete::StatusMessage)),
            this, SLOT(setStatusMessage(Kopete::StatusMessage)));

    d->actionPrefs = KopeteStdAction::preferences(actionCollection(), "settings_prefs");

    KStandardAction::quit(this, SLOT(slotQuit()), actionCollection());

    setStandardToolBarMenuEnabled(true);
    d->menubarAction = KStandardAction::showMenubar(menuBar(), SLOT(setVisible(bool)), actionCollection());
    actionCollection()->addAction(QStringLiteral("settings_showmenubar"), d->menubarAction);
    d->statusbarAction = KStandardAction::showStatusbar(statusBar(), SLOT(setVisible(bool)), actionCollection());
    actionCollection()->addAction(QStringLiteral("settings_showstatusbar"), d->statusbarAction);

    QAction *act = KStandardAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), actionCollection());
    actionCollection()->addAction(QStringLiteral("settings_keys"), act);

    QAction *configureGlobalShortcutsAction = new QAction(QIcon::fromTheme(QStringLiteral("configure-shortcuts")), i18n("Configure &Global Shortcuts..."), this);
    configureGlobalShortcutsAction->setMenuRole(QAction::NoRole);   //OS X: prevent Qt heuristics to move action to app menu->"Preferences"
    actionCollection()->addAction(QStringLiteral("settings_global"), configureGlobalShortcutsAction);
    connect(configureGlobalShortcutsAction, SIGNAL(triggered(bool)), this, SLOT(slotConfGlobalKeys()));

    KStandardAction::configureToolbars(this, SLOT(slotConfToolbar()), actionCollection());
    act = KStandardAction::configureNotifications(this, SLOT(slotConfNotifications()), actionCollection());
    actionCollection()->addAction(QStringLiteral("settings_notifications"), act);

    d->actionShowAllOfflineEmpty = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-user-offline-kopete")), i18n("Show &All"), this);
    actionCollection()->addAction(QStringLiteral("settings_show_all_offline_empty"), d->actionShowAllOfflineEmpty);
    actionCollection()->setDefaultShortcut(d->actionShowAllOfflineEmpty, QKeySequence(Qt::CTRL + Qt::Key_U));
    connect(d->actionShowAllOfflineEmpty, SIGNAL(triggered(bool)), this, SLOT(slotToggleShowAllOfflineEmpty(bool)));

    d->actionShowOfflineUsers = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-user-offline-kopete")), i18n("Show Offline &Users"), this);
    actionCollection()->addAction(QStringLiteral("settings_show_offliners"), d->actionShowOfflineUsers);
    connect(d->actionShowOfflineUsers, SIGNAL(triggered(bool)), this, SLOT(slotToggleShowOfflineUsers()));

    d->actionShowEmptyGroups = new KToggleAction(QIcon::fromTheme(QStringLiteral("folder-grey")), i18n("Show Empty &Groups"), this);
    actionCollection()->addAction(QStringLiteral("settings_show_empty_groups"), d->actionShowEmptyGroups);
    actionCollection()->setDefaultShortcut(d->actionShowEmptyGroups, QKeySequence(Qt::CTRL + Qt::Key_G));
    connect(d->actionShowEmptyGroups, SIGNAL(triggered(bool)), this, SLOT(slotToggleShowEmptyGroups()));

    /* The following are highly misleading together with the checkbox, consider removing them - ahartmetz
    d->actionShowAllOfflineEmpty->setCheckedState ( KGuiItem ( i18n ( "Hide O&ffline" ) ) );
    d->actionShowOfflineUsers->setCheckedState ( KGuiItem ( i18n ( "Hide Offline &Users" ) ) );
    d->actionShowEmptyGroups->setCheckedState ( KGuiItem ( i18n ( "Hide Empty &Groups" ) ) );
    */

    KFilterProxySearchLine *searchLine = new KFilterProxySearchLine(this);
    searchLine->setProxy(d->proxyModel);
    QWidgetAction *quickSearch = new QWidgetAction(this);
    actionCollection()->addAction(QStringLiteral("quicksearch_bar"), quickSearch);
    quickSearch->setDefaultWidget(searchLine);

    // sync actions, config and prefs-dialog
    connect(Kopete::AppearanceSettings::self(), SIGNAL(configChanged()), this, SLOT(slotConfigChanged()));
    slotConfigChanged();

    // Global actions
    QAction *globalReadMessage = new QAction(i18n("Read Message"), this);
    actionCollection()->addAction(QStringLiteral("ReadMessage"), globalReadMessage);
    connect(globalReadMessage, SIGNAL(triggered(bool)), Kopete::ChatSessionManager::self(), SLOT(slotReadMessage()));
    actionCollection()->setDefaultShortcut(globalReadMessage, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I));
    globalReadMessage->setWhatsThis(i18n("Read the next pending message"));

    QAction *globalShowContactList = new QAction(i18n("Show/Hide Contact List"), this);
    actionCollection()->addAction(QStringLiteral("ShowContactList"), globalShowContactList);
    connect(globalShowContactList, SIGNAL(triggered(bool)), this, SLOT(slotShowHide()));
    actionCollection()->setDefaultShortcut(globalShowContactList, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_T));
    globalShowContactList->setWhatsThis(i18n("Show or hide the contact list"));

    QAction *globalSetAway = new QAction(i18n("Set Away/Back"), this);
    actionCollection()->addAction(QStringLiteral("Set_Away_Back"), globalSetAway);
    connect(globalSetAway, SIGNAL(triggered(bool)), this, SLOT(slotToggleAway()));
    actionCollection()->setDefaultShortcut(globalSetAway, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W));
    globalSetAway->setWhatsThis(i18n("Set Away/Back"));
}

void KopeteWindow::slotShowHide()
{
    if (isActiveWindow()) {
        d->autoHideTimer->stop(); //no timeouts if active
        hide();
    } else {
        show();
#ifdef Q_WS_X11
        if (!KWindowSystem::windowInfo(winId(), NET::WMDesktop).onAllDesktops()) {
            KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop());
        }
#endif
        raise();
        KWindowSystem::forceActiveWindow(winId());
    }
}

void KopeteWindow::slotToggleAway()
{
    kDebug(14000);
    Kopete::StatusManager *statusManager = Kopete::StatusManager::self();
    const Kopete::Status::StatusItem *item = 0;
    bool away = Kopete::StatusManager::self()->globalAway();

    foreach (const Kopete::Status::StatusItem *i, statusManager->getRootGroup()->childList()) {
        if (i->title() == QLatin1String("Online") && away) {
            item = i;
            break;
        } else if (i->title() == QLatin1String("Away") && !away) {
            item = i;
            break;
        }
    }

    const Kopete::Status::Status *status = qobject_cast<const Kopete::Status::Status *>(item);
    if (status) {
        statusManager->setGlobalStatusMessage(Kopete::StatusMessage(status->title(), status->message()));
    }

    if (away) {
        slotGlobalAvailable();
    } else {
        slotGlobalAway();
    }
}

void KopeteWindow::initSystray()
{
    if (Kopete::BehaviorSettings::self()->showSystemTray()) {
        d->tray = KopeteSystemTray::systemTray(this);

        QObject::connect(d->tray, SIGNAL(aboutToShowMenu(QMenu*)),
                         this, SLOT(slotTrayAboutToShowMenu(QMenu*)));
        d->tray->setStandardActionsEnabled(true);
        QObject::connect(d->tray, SIGNAL(quit()), this, SLOT(slotQuit()));
    }
}

KopeteWindow::~KopeteWindow()
{
    delete d;
}

bool KopeteWindow::eventFilter(QObject *target, QEvent *event)
{
    KToolBar *toolBar = dynamic_cast<KToolBar *>(target);
    QAction *resetAction = actionCollection()->action(QStringLiteral("quicksearch_reset"));

    if (toolBar && resetAction && resetAction->associatedWidgets().contains(toolBar)) {
        if (event->type() == QEvent::Hide) {
            resetAction->trigger();
            return true;
        }
        return KXmlGuiWindow::eventFilter(target, event);
    }

    return KXmlGuiWindow::eventFilter(target, event);
}

void KopeteWindow::loadOptions()
{
    KSharedConfig::Ptr config = KSharedConfig::openConfig();

    toolBar(QStringLiteral("mainToolBar"))->applySettings(config->group("ToolBar Settings"));
    toolBar(QStringLiteral("quickSearchBar"))->applySettings(config->group("QuickSearchBar Settings"));

    applyMainWindowSettings(config->group("General Options"));
    KConfigGroup cg(config, "General Options");
    QPoint pos = cg.readEntry("Position", QPoint(-1, -1));
    if (pos.x() != -1 || pos.y() != -1) {
        move(pos);
    }

    QSize size = cg.readEntry("Geometry", QSize());
    if (size.isEmpty()) { // Default size
        resize(QSize(375, 425));
    } else {
        resize(size);
    }

    d->autoHide = Kopete::AppearanceSettings::self()->contactListAutoHide();
    d->autoHideTimeout = Kopete::AppearanceSettings::self()->contactListAutoHideTimeout();

    QString tmp = cg.readEntry("WindowState", "Shown");
    if (tmp == QLatin1String("Minimized") && Kopete::BehaviorSettings::self()->showSystemTray()) {
        showMinimized();
    } else if (tmp == QLatin1String("Hidden") && Kopete::BehaviorSettings::self()->showSystemTray()) {
        hide();
    } else if (!Kopete::BehaviorSettings::self()->startDocked() || !Kopete::BehaviorSettings::self()->showSystemTray()) {
        show();
    }

    d->menubarAction->setChecked(!menuBar()->isHidden());
    d->statusbarAction->setChecked(!statusBar()->isHidden());
}

void KopeteWindow::saveOptions()
{
    KConfigGroup mainToolbarGroup(KSharedConfig::openConfig(), "ToolBar Settings");
    toolBar(QStringLiteral("mainToolBar"))->saveSettings(mainToolbarGroup);
    KConfigGroup qsbGroup(KSharedConfig::openConfig(), "QuickSearchBar Settings");
    toolBar(QStringLiteral("quickSearchBar"))->saveSettings(qsbGroup);

    KConfigGroup cg(KSharedConfig::openConfig(), "General Options");
    saveMainWindowSettings(cg);

    cg.writeEntry("Position", pos());
    cg.writeEntry("Geometry", size());

    if (isMinimized()) {
        cg.writeEntry("WindowState", "Minimized");
    } else if (isHidden()) {
        cg.writeEntry("WindowState", "Hidden");
    } else {
        cg.writeEntry("WindowState", "Shown");
    }

    Kopete::Identity *identity = d->identitywidget->identity();
    if (identity) {
        cg.writeEntry("ShownIdentityId", identity->id());
    } else {
        cg.writeEntry("ShownIdentityId", QString());
    }

    cg.sync();
}

void KopeteWindow::slotToggleShowAllOfflineEmpty(bool toggled)
{
    d->actionShowOfflineUsers->setChecked(toggled);
    d->actionShowEmptyGroups->setChecked(toggled);
    Kopete::AppearanceSettings::self()->setShowOfflineUsers(toggled);
    Kopete::AppearanceSettings::self()->setShowEmptyGroups(toggled);
    Kopete::AppearanceSettings::self()->save();
}

void KopeteWindow::slotToggleShowOfflineUsers()
{
    Kopete::AppearanceSettings::self()->setShowOfflineUsers(d->actionShowOfflineUsers->isChecked());
    Kopete::AppearanceSettings::self()->save();
}

void KopeteWindow::slotToggleShowEmptyGroups()
{
    Kopete::AppearanceSettings::self()->setShowEmptyGroups(d->actionShowEmptyGroups->isChecked());
    Kopete::AppearanceSettings::self()->save();
}

static bool compareOnlineStatus(const Kopete::Account *a, const Kopete::Account *b);
static bool invertedCompareOnlineStatus(const Kopete::Account *a, const Kopete::Account *b);

void KopeteWindow::slotConfigChanged()
{
    bool groupContactByGroupModel = qobject_cast<Kopete::UI::ContactListTreeModel *>(d->model);
    if (groupContactByGroupModel != Kopete::AppearanceSettings::self()->groupContactByGroup()) {
        Kopete::UI::ContactListModel *oldModel = d->model;
        if (Kopete::AppearanceSettings::self()->groupContactByGroup()) {
            d->model = new Kopete::UI::ContactListTreeModel(this);
        } else {
            d->model = new Kopete::UI::ContactListPlainModel(this);
        }

        d->model->init();
        d->proxyModel->setSourceModel(d->model);
        oldModel->deleteLater();
    }

    if (isHidden() && !Kopete::BehaviorSettings::self()->showSystemTray()) { // user disabled systray while kopete is hidden, show it!
        show();
    }

    d->actionShowAllOfflineEmpty->setChecked(Kopete::AppearanceSettings::self()->showOfflineUsers() && Kopete::AppearanceSettings::self()->showEmptyGroups());
    d->actionShowOfflineUsers->setChecked(Kopete::AppearanceSettings::self()->showOfflineUsers());
    d->actionShowEmptyGroups->setChecked(Kopete::AppearanceSettings::self()->showEmptyGroups());

    if (d->showIdentityIcons != Kopete::AppearanceSettings::self()->showIdentityIcons()) {
        // Delete status bar icons
        if (d->showIdentityIcons) {
            if (d->identitywidget->isVisible()) {
                d->identitywidget->setIdentity(0);
                d->identitywidget->setVisible(false);
            }

            qDeleteAll(d->identityStatusBarIcons);
            d->identityStatusBarIcons.clear();
        } else {
            qDeleteAll(d->accountStatusBarIcons);
            d->accountStatusBarIcons.clear();
        }

        // Add new status bar icons
        d->showIdentityIcons = Kopete::AppearanceSettings::self()->showIdentityIcons();
        if (d->showIdentityIcons) {
            Kopete::Identity::List identityList = Kopete::IdentityManager::self()->identities();
            foreach (Kopete::Identity *identity, identityList) {
                KopeteIdentityStatusBarIcon *sbIcon = new KopeteIdentityStatusBarIcon(identity, d->statusBarWidget);
                connect(sbIcon, SIGNAL(leftClicked(Kopete::Identity*,QPoint)), this,
                        SLOT(slotIdentityStatusIconLeftClicked(Kopete::Identity*,QPoint)));

                d->identityStatusBarIcons.insert(identity, sbIcon);
                slotIdentityStatusIconChanged(identity);
                slotIdentityToolTipChanged(identity);
            }
        } else {
            QList<Kopete::Account *> accountList = Kopete::AccountManager::self()->accounts();
            qSort(accountList.begin(), accountList.end(), invertedCompareOnlineStatus);
            foreach (Kopete::Account *account, accountList) {
                KopeteAccountStatusBarIcon *sbIcon = new KopeteAccountStatusBarIcon(account, d->statusBarWidget);
                d->accountStatusBarIcons.insert(account, sbIcon);
            }
        }
    }
}

void KopeteWindow::slotContactListAppearanceChanged()
{
    d->autoHide = Kopete::AppearanceSettings::self()->contactListAutoHide();
    d->autoHideTimeout = Kopete::AppearanceSettings::self()->contactListAutoHideTimeout();

    startAutoHideTimer();
}

void KopeteWindow::slotConfNotifications()
{
    KNotifyConfigWidget::configure(this);
}

void KopeteWindow::slotConfGlobalKeys()
{
    KShortcutsDialog::configure(actionCollection());
}

void KopeteWindow::slotConfToolbar()
{
    KConfigGroup cg(KSharedConfig::openConfig(), "General Options");
    saveMainWindowSettings(cg);
    KEditToolBar *dlg = new KEditToolBar(factory());
    connect(dlg, SIGNAL(newToolBarConfig()), this, SLOT(slotUpdateToolbar()));
    connect(dlg, SIGNAL(finished()), dlg, SLOT(deleteLater()));
    dlg->show();
}

void KopeteWindow::slotUpdateToolbar()
{
    applyMainWindowSettings(KSharedConfig::openConfig()->group("General Options"));
}

void KopeteWindow::slotGlobalAway()
{
    Kopete::AccountManager::self()->setOnlineStatus(Kopete::OnlineStatusManager::Away,
                                                    Kopete::StatusManager::self()->globalStatusMessage());
}

void KopeteWindow::slotGlobalBusy()
{
    Kopete::AccountManager::self()->setOnlineStatus(Kopete::OnlineStatusManager::Busy,
                                                    Kopete::StatusManager::self()->globalStatusMessage());
}

void KopeteWindow::slotGlobalAvailable()
{
    Kopete::AccountManager::self()->setOnlineStatus(Kopete::OnlineStatusManager::Online,
                                                    Kopete::StatusManager::self()->globalStatusMessage());
}

void KopeteWindow::slotSetInvisibleAll()
{
    Kopete::AccountManager::self()->setOnlineStatus(Kopete::OnlineStatusManager::Invisible,
                                                    Kopete::StatusManager::self()->globalStatusMessage());
}

void KopeteWindow::slotDisconnectAll()
{
    Kopete::AccountManager::self()->setOnlineStatus(Kopete::OnlineStatusManager::Offline,
                                                    Kopete::StatusManager::self()->globalStatusMessage());
}

bool KopeteWindow::queryClose()
{
    KopeteApplication *app = static_cast<KopeteApplication *>(qApp);
    if (app->isSavingSession() || app->isShuttingDown()) {
        // we are shutting down, don't show any message
        return true;
    }

    Kopete::PluginList list = Kopete::PluginManager::self()->loadedPlugins();
    foreach (Kopete::Plugin *plugin, list) {
        bool shown = false;
        QMetaObject::invokeMethod(plugin, "showCloseWindowMessage", Qt::DirectConnection, Q_RETURN_ARG(bool, shown));
        if (shown) {
            // A message box has just been shown. Stop now, we do not want
            // to spam the user with multiple message boxes.
            return true;
        }
    }

    if (Kopete::BehaviorSettings::self()->showSystemTray()
        && !isHidden()) {
        // I would make this a KMessageBox::queuedMessageBox but there doesn't seem to be don'tShowAgain support for those
        KMessageBox::information(this,
                                 i18n("<qt>Closing the main window will keep Kopete running in the "
                                      "system tray. Use 'Quit' from the 'File' menu to quit the application.</qt>"),
                                 i18n("Docking in System Tray"), QStringLiteral("hideOnCloseInfo"));
    }
	else{
        // we are shutting down either user initiated or session management
        Kopete::PluginManager::self()->shutdown();
    }
    return true;
}

bool KopeteWindow::shouldExitOnClose() const
{
    const Kopete::PluginList &list = Kopete::PluginManager::self()->loadedPlugins();
    foreach (Kopete::Plugin *plugin, list) {
        bool ok = true;
        QMetaObject::invokeMethod(plugin, "shouldExitOnclose", Qt::DirectConnection, Q_RETURN_ARG(bool, ok));
        if (!ok) {
            kDebug(14000) << "plugin" << plugin->displayName() << "does not want to exit";
            return false;
        }
    }
    // If all plugins are OK, consider ourself OK only if there is no tray icon
    return !Kopete::BehaviorSettings::self()->showSystemTray();
}

bool KopeteWindow::queryExit()
{
    KopeteApplication *app = static_cast<KopeteApplication *>(qApp);
    if (app->isSavingSession()
        || app->isShuttingDown()     /* only set if KopeteApplication::quitKopete() or
                                    KopeteApplication::commitData() called */
        || shouldExitOnClose()
        || isHidden()) {
        saveOptions();
        kDebug(14000) << " shutting down plugin manager";
        Kopete::PluginList list = Kopete::PluginManager::self()->loadedPlugins();
        foreach (Kopete::Plugin *plugin, list) {
            guiFactory()->removeClient(plugin);
        }
        Kopete::PluginManager::self()->shutdown();
        return true;
    } else {
        return false;
    }
}

void KopeteWindow::closeEvent(QCloseEvent *e)
{
    // if we are not ok to exit on close and we are not shutting down then just do what needs to be done if a
    // window is closed.
    KopeteApplication *app = static_cast<KopeteApplication *>(qApp);
    if (!shouldExitOnClose() && !app->isShuttingDown() && !app->isSavingSession()) {
        // BEGIN of code borrowed from KMainWindow::closeEvent
        // Save settings if auto-save is enabled, and settings have changed
        if (settingsDirty() && autoSaveSettings()) {
            saveAutoSaveSettings();
        }

        if (queryClose()) {
            e->accept();
        }
        // END of code borrowed from KMainWindow::closeEvent
        kDebug(14000) << "just closing because we have a system tray icon";
    } else {
        kDebug(14000) << "delegating to KXmlGuiWindow::closeEvent()";
        KXmlGuiWindow::closeEvent(e);
    }
}

void KopeteWindow::slotQuit()
{
    KopeteApplication *app = static_cast<KopeteApplication *>(qApp);
    app->quitKopete();

    if (d->tray && app->isShuttingDown()) {
        d->tray->deleteLater();
        d->tray = 0;
    }
}

void KopeteWindow::slotPluginLoaded(Kopete::Plugin *p)
{
    guiFactory()->addClient(p);
}

void KopeteWindow::slotAllPluginsLoaded()
{
//	actionConnect->setEnabled(true);
    d->actionDisconnect->setEnabled(true);

    KConfigGroup cg(KSharedConfig::openConfig(), "General Options");

    // If some account already loaded, build the status icon
    QList<Kopete::Account *> accountList = Kopete::AccountManager::self()->accounts();
    qSort(accountList.begin(), accountList.end(), invertedCompareOnlineStatus);
    foreach (Kopete::Account *a, accountList) {
        slotAccountRegistered(a);
    }

    //Connect the appropriate account signals
    /* Please note that I tried to put this in the slotAllPluginsLoaded() function
     * but it seemed to break the account icons in the statusbar --Matt */
    connect(Kopete::AccountManager::self(), SIGNAL(accountRegistered(Kopete::Account*)),
            this, SLOT(slotAccountRegistered(Kopete::Account*)));
    connect(Kopete::AccountManager::self(), SIGNAL(accountUnregistered(const Kopete::Account*)),
            this, SLOT(slotAccountUnregistered(const Kopete::Account*)));

    if (d->showIdentityIcons) {
        QString identityId = cg.readEntry("ShownIdentityId", Kopete::IdentityManager::self()->defaultIdentity()->id());
        if (!identityId.isEmpty()) {
            Kopete::Identity *identity = Kopete::IdentityManager::self()->findIdentity(identityId);
            if (identity) {
                slotIdentityStatusIconLeftClicked(identity, QPoint());
            }
        }
    }
}

void KopeteWindow::slotIdentityRegistered(Kopete::Identity *identity)
{
    if (!identity) {
        return;
    }

    connect(identity, SIGNAL(onlineStatusChanged(Kopete::Identity*)),
            this, SLOT(slotIdentityStatusIconChanged(Kopete::Identity*)));
    connect(identity, SIGNAL(identityChanged(Kopete::Identity*)),
            this, SLOT(slotIdentityStatusIconChanged(Kopete::Identity*)));
    connect(identity, SIGNAL(toolTipChanged(Kopete::Identity*)),
            this, SLOT(slotIdentityToolTipChanged(Kopete::Identity*)));

    if (d->showIdentityIcons) {
        KopeteIdentityStatusBarIcon *sbIcon = new KopeteIdentityStatusBarIcon(identity, d->statusBarWidget);
        connect(sbIcon, SIGNAL(leftClicked(Kopete::Identity*,QPoint)),
                SLOT(slotIdentityStatusIconLeftClicked(Kopete::Identity*,QPoint)));

        d->identityStatusBarIcons.insert(identity, sbIcon);
    }

    slotIdentityStatusIconChanged(identity);
    slotIdentityToolTipChanged(identity);
}

void KopeteWindow::slotIdentityUnregistered(const Kopete::Identity *identity)
{
    kDebug(14000);

    if (d->showIdentityIcons) {
        KopeteIdentityStatusBarIcon *sbIcon = d->identityStatusBarIcons.value(identity, 0);
        if (sbIcon) {
            d->identityStatusBarIcons.remove(identity);
            delete sbIcon;
        }
    }

    makeTrayToolTip();
}

void KopeteWindow::slotIdentityToolTipChanged(Kopete::Identity *identity)
{
    if (d->appDestroyed) {
        return;
    }

    KopeteApplication *app = static_cast<KopeteApplication *>(qApp);
    if (app->isSavingSession() || app->isShuttingDown()) {
        return;
    }

    // Adds tooltip for each status icon, useful in case you have many accounts
    // over one protocol
    KopeteIdentityStatusBarIcon *i = d->identityStatusBarIcons.value(identity, 0);
    if (i) {
        i->setToolTip(identity->toolTip());
    }

    makeTrayToolTip();
}

void KopeteWindow::slotIdentityStatusIconChanged(Kopete::Identity *identity)
{
    kDebug(14000) << identity->property(Kopete::Global::Properties::self()->statusMessage()).value();
    // update the global status label if the change doesn't
//	QString newAwayMessage = contact->property( Kopete::Global::Properties::self()->awayMessage() ).value().toString();
//	if ( status.status() != Kopete::OnlineStatus::Connecting )
//	{
//		QString globalMessage = m_globalStatusMessage->text();
//		if ( newAwayMessage != globalMessage )
//			m_globalStatusMessage->setText( ""i18n("status message to show when different accounts have different status messages", "(multiple)" );
//	}
//	kDebug(14000) << "Icons: '" <<
//		status.overlayIcons() << "'" << endl;

    if (d->appDestroyed) {
        return;
    }

    KopeteApplication *app = static_cast<KopeteApplication *>(qApp);
    if (app->isSavingSession() || app->isShuttingDown()) {
        return;
    }

    if (identity->onlineStatus() != Kopete::OnlineStatus::Connecting) {
        // FIXME: It's not global status so don't save it
        //Kopete::StatusManager::self()->setGlobalStatusMessage( identity->property( Kopete::Global::Properties::self()->statusMessage() ).value().toString() );
    }

    KopeteIdentityStatusBarIcon *i = d->identityStatusBarIcons.value(identity, 0);
    if (!i) {
        return;
    }

    QPixmap pm;
    switch (identity->onlineStatus()) {
    case Kopete::OnlineStatus::Offline:
    case Kopete::OnlineStatus::Connecting:
        pm = SmallIcon(QStringLiteral("user-identity"), 0, KIconLoader::DefaultState,
                       QStringList() << QString() << QStringLiteral("user-offline"));
        break;
    case Kopete::OnlineStatus::Invisible:
        pm = SmallIcon(QStringLiteral("user-identity"), 0, KIconLoader::DefaultState,
                       QStringList() << QString() << QStringLiteral("user-invisible"));
        break;
    case Kopete::OnlineStatus::Away:
        pm = SmallIcon(QStringLiteral("user-identity"), 0, KIconLoader::DefaultState,
                       QStringList() << QString() << QStringLiteral("user-away"));
        break;
    case Kopete::OnlineStatus::Busy:
        pm = SmallIcon(QStringLiteral("user-identity"), 0, KIconLoader::DefaultState,
                       QStringList() << QString() << QStringLiteral("user-busy"));
        break;
    case Kopete::OnlineStatus::Online:
        pm = SmallIcon(QStringLiteral("user-identity"), 0, KIconLoader::DefaultState,
                       QStringList() << QString() << QStringLiteral("user-online"));
        break;
    case Kopete::OnlineStatus::Unknown:
        pm = SmallIcon(QStringLiteral("user-identity"));
        break;
    }

    // No Pixmap found, fallback to Unknown
    if (pm.isNull()) {
        i->setPixmap(SmallIcon(QStringLiteral("user-identity")));
    } else {
        i->setPixmap(pm);
    }
}

static bool compareOnlineStatus(const Kopete::Account *a, const Kopete::Account *b)
{
    int c = 0;

    if (a->identity() && b->identity()) {
        c = QString::localeAwareCompare(a->identity()->label(), b->identity()->label());
    }

    if (c == 0) {
        c = a->myself()->onlineStatus().status() - b->myself()->onlineStatus().status();

        if (c == 0) {
            return QString::localeAwareCompare(a->protocol()->displayName(), b->protocol()->displayName()) < 0;
        }
        return c > 0;
    }
    return c < 0;
}

static bool invertedCompareOnlineStatus(const Kopete::Account *a, const Kopete::Account *b)
{
    return !compareOnlineStatus(a, b);
}

void KopeteWindow::makeTrayToolTip()
{
    //FIXME: maybe use identities here?
    //the tool-tip of the systemtray.
    if (d->tray) {
        QString tt = QStringLiteral("<qt>");
        QList<Kopete::Account *> accountList = Kopete::AccountManager::self()->accounts();
        qSort(accountList.begin(), accountList.end(), compareOnlineStatus);
        foreach (Kopete::Account *a, accountList) {
            Kopete::Contact *self = a->myself();
            /*tt += i18nc ( "Account tooltip information: <nobr>ICON <b>PROTOCOL:</b> NAME (<i>STATUS</i>)</nobr><br />",
                          "<nobr><img src=\"kopete-account-icon:%3:%4\" /> <b>%1:</b> %2 (<i>%5</i>)</nobr><br />",
                          a->protocol()->displayName(), a->accountLabel(), QString ( QUrl::toPercentEncoding ( a->protocol()->pluginId() ) ),
                          QString ( QUrl::toPercentEncoding ( a->accountId() ) ), self->onlineStatus().description() );*/
            tt += i18nc("Account tooltip information: <nobr>ICON <b>PROTOCOL:</b> NAME (<i>STATUS</i>)</nobr><br />",
                        "<nobr><img src=\"%3\" width=\"16\" height=\"16\" /> <b>%1:</b> %2 (<i>%4</i>)</nobr><br />",
                        a->protocol()->displayName(), a->accountLabel(),
                        a->accountIconPath(KIconLoader::Small), self->onlineStatus().description());
        }
        tt += QLatin1String("</qt>");
        d->tray->setToolTip(QStringLiteral("kopete"), i18n("Kopete"), tt);
    }
}

void KopeteWindow::slotIdentityStatusIconLeftClicked(Kopete::Identity *identity, const QPoint &p)
{
    Q_UNUSED(p)
    if (d->identitywidget->isVisible() && d->identitywidget->identity() == identity) {
        d->identitywidget->setIdentity(0);
        d->identitywidget->setVisible(false);
        return;
    }

    if (d->infoEventWidget->isVisible()) {
        d->infoEventWidget->setVisible(false);
    }

    d->identitywidget->setIdentity(identity);
    d->identitywidget->setVisible(true);
}

void KopeteWindow::slotShowInfoEventWidget()
{
    if (d->identitywidget->isVisible()) {
        d->identitywidget->setIdentity(0);
        d->identitywidget->setVisible(false);
    }

    if (!d->infoEventWidget->isVisible()) {
        d->infoEventWidget->setVisible(true);
    }

    if (!isActiveWindow()) {
        slotShowHide();
    }
}

void KopeteWindow::slotInfoIconClicked()
{
    if (d->infoEventWidget->isVisible()) {
        d->infoEventWidget->setVisible(false);
    } else {
        if (d->identitywidget->isVisible()) {
            d->identitywidget->setIdentity(0);
            d->identitywidget->setVisible(false);
        }
        d->infoEventWidget->setVisible(true);
    }
}

void KopeteWindow::slotAccountRegistered(Kopete::Account *account)
{
    //enable the connect all toolbar button
//	actionConnect->setEnabled(true);
    d->actionDisconnect->setEnabled(true);

    // add an item for this account to the add contact actionmenu
    QString s = QStringLiteral("actionAdd%1Contact").arg(account->accountId());
    QAction *action = new QAction(QIcon(account->accountIcon()), account->accountLabel(), this);
    actionCollection()->addAction(s, action);
    connect(action, SIGNAL(triggered(bool)), d->addContactMapper, SLOT(map()));
    connect(account, SIGNAL(colorChanged(QColor)), this, SLOT(slotAccountColorChanged()));

    d->addContactMapper->setMapping(action, account->protocol()->pluginId() + QChar(0xE000) + account->accountId());
    d->actionAddContact->addAction(action);

    if (!d->showIdentityIcons) {
        KopeteAccountStatusBarIcon *sbIcon = new KopeteAccountStatusBarIcon(account, d->statusBarWidget);
        d->accountStatusBarIcons.insert(account, sbIcon);
    }
}

void KopeteWindow::slotAccountColorChanged()
{
    Kopete::Account *account = qobject_cast<Kopete::Account *>(sender());
    Q_ASSERT(account);

    // update add contact actionmenu
    QString s = QStringLiteral("actionAdd%1Contact").arg(account->accountId());
    QAction *action = actionCollection()->action(s);
    if (action) {
        action->setIcon(QIcon(account->accountIcon()));
    }
}

void KopeteWindow::slotAccountUnregistered(const Kopete::Account *account)
{
    QList<Kopete::Account *> accounts = Kopete::AccountManager::self()->accounts();
    if (accounts.isEmpty()) {
//		actionConnect->setEnabled(false);
        d->actionDisconnect->setEnabled(false);
    }

    disconnect(account, SIGNAL(colorChanged(QColor)), this, SLOT(slotAccountColorChanged()));

    // update add contact actionmenu
    QString s = QStringLiteral("actionAdd%1Contact").arg(account->accountId());
    QAction *action = actionCollection()->action(s);
    if (action) {
        kDebug(14000) << " found QAction " << action << " with name: " << action->objectName();
        d->addContactMapper->removeMappings(action);
        d->actionAddContact->removeAction(action);
    }

    if (!d->showIdentityIcons) {
        KopeteAccountStatusBarIcon *sbIcon = d->accountStatusBarIcons.value(account, 0);
        if (sbIcon) {
            d->accountStatusBarIcons.remove(account);
            delete sbIcon;
        }
    }
}

void KopeteWindow::slotTrayAboutToShowMenu(QMenu *popup)
{
    popup->clear();
    popup->addSection(qApp->windowIcon(), KGlobal::caption());

    QList<Kopete::Account *> accountList = Kopete::AccountManager::self()->accounts();
    qSort(accountList.begin(), accountList.end(), invertedCompareOnlineStatus);
    foreach (Kopete::Account *account, accountList) {
        KActionMenu *menu = new KActionMenu(account->accountId(), account);
        menu->setIcon(account->myself()->onlineStatus().iconFor(account));

        if (!account->hasCustomStatusMenu()) {
            Kopete::StatusRootAction::createAccountStatusActions(account, menu);
        }

        account->fillActionMenu(menu);
        popup->addAction(menu);

        connect(popup, SIGNAL(aboutToHide()), menu, SLOT(deleteLater()));
    }

    popup->addSeparator();
    popup->addAction(d->actionStatusMenu);
    popup->addSeparator();
    popup->addAction(d->actionPrefs);
    popup->addAction(d->actionAddContact);
    popup->addSeparator();
    popup->addAction(d->tray->action(QStringLiteral("minimizeRestore")));
    popup->addAction(d->tray->action(KStandardAction::name(KStandardAction::Quit)));
}

void KopeteWindow::showExportDialog()
{
    KabcExportWizard *wizard = new KabcExportWizard(this);
    wizard->setObjectName(QStringLiteral("export_contact_dialog"));
    wizard->show();
}

void KopeteWindow::leaveEvent(QEvent *)
{
    startAutoHideTimer();
}

void KopeteWindow::showEvent(QShowEvent *)
{
    startAutoHideTimer();
    slotStartAutoResizeTimer();
}

void KopeteWindow::hideEvent(QHideEvent *)
{
    d->autoResizeTimer->stop();
}

void KopeteWindow::slotAutoHide()
{
    if (this->geometry().contains(QCursor::pos()) == false) {
        /* The autohide-timer doesn't need to emit
        * timeouts when the window is hidden already. */
        d->autoHideTimer->stop();
        hide();
    }
}

void KopeteWindow::startAutoHideTimer()
{
    if (d->autoHideTimeout > 0 && d->autoHide == true && isVisible() && Kopete::BehaviorSettings::self()->showSystemTray()) {
        d->autoHideTimer->start(d->autoHideTimeout * 1000);
    }
}

void KopeteWindow::slotStartAutoResizeTimer()
{
    if (Kopete::AppearanceSettings::contactListAutoResize() == true) {
        if (!d->autoResizeTimer->isActive()) {
            d->autoResizeTimer->start(1000);
        }
    }
}

void KopeteWindow::setOnlineStatus(uint category, const Kopete::StatusMessage &statusMessage)
{
    Kopete::AccountManager::self()->setOnlineStatus(category, statusMessage, 0, true);
}

void KopeteWindow::setStatusMessage(const Kopete::StatusMessage &statusMessage)
{
    Kopete::StatusManager::self()->setGlobalStatusMessage(statusMessage);
}

void KopeteWindow::globalStatusChanged()
{
    QString statusTitle = Kopete::StatusManager::self()->globalStatusMessage().title();
    QString statusMessage = Kopete::StatusManager::self()->globalStatusMessage().message();
    d->globalStatusMessage->setText(statusTitle);

    QString toolTip;
    toolTip += i18nc("@label:textbox formatted status title", "<b>Status&nbsp;Title:</b>&nbsp;%1",
                     Kopete::Emoticons::parseEmoticons(Kopete::Message::escape(statusTitle)));

    toolTip += i18nc("@label:textbox formatted status message", "<br /><b>Status&nbsp;Message:</b>&nbsp;%1",
                     Kopete::Emoticons::parseEmoticons(Kopete::Message::escape(statusMessage)));

    d->globalStatusMessage->setToolTip(toolTip);
}

void KopeteWindow::slotGlobalStatusMessageIconClicked(const QPoint &position)
{
    QMenu *menu = new QMenu(this);

    menu->addSection(i18n("Status Message"));

    Kopete::UI::StatusEditAction *statusEditAction = new Kopete::UI::StatusEditAction(this);
    statusEditAction->setStatusMessage(Kopete::StatusManager::self()->globalStatusMessage());
    connect(statusEditAction, SIGNAL(statusChanged(Kopete::StatusMessage)),
            this, SLOT(setStatusMessage(Kopete::StatusMessage)));

    menu->addAction(statusEditAction);
    menu->exec(position);

    statusEditAction->deleteLater();
    delete menu;
}

void KopeteWindow::slotAddContactDialogInternal(const QString &accountIdentifier)
{
    QString protocolId = accountIdentifier.section(QChar(0xE000), 0, 0);
    QString accountId = accountIdentifier.section(QChar(0xE000), 1, 1);
    Kopete::Account *account = Kopete::AccountManager::self()->findAccount(protocolId, accountId);
    showAddContactDialog(account);
}

void KopeteWindow::updateStatusMenuMessage(Kopete::StatusRootAction *statusRootAction)
{
    statusRootAction->setCurrentMessage(Kopete::StatusManager::self()->globalStatusMessage());
}

void KopeteWindow::showAddContactDialog(Kopete::Account *account)
{
    if (!account) {
        kDebug(14000) << "no account given";
        return;
    }

    KDialog *addDialog = new KDialog(this);
    addDialog->setCaption(i18n("Add Contact"));
    addDialog->setButtons(KDialog::Ok | KDialog::Cancel);
    addDialog->setDefaultButton(KDialog::Ok);
    addDialog->showButtonSeparator(true);

    KVBox *mainWid = new KVBox(addDialog);

    AddContactPage *addContactPage
        = account->protocol()->createAddContactWidget(mainWid, account);

    QWidget *groupKABC = new QWidget(mainWid);
    groupKABC->setObjectName(QStringLiteral("groupkabcwidget"));
    Ui::GroupKABCSelectorWidget ui_groupKABC;
    ui_groupKABC.setupUi(groupKABC);

    // Populate the groups list
    Kopete::GroupList groups = Kopete::ContactList::self()->groups();
    QHash<QString, Kopete::Group *> groupItems;

    // Add top level group
    groupItems.insert(Kopete::Group::topLevel()->displayName(), Kopete::Group::topLevel());
    ui_groupKABC.groupCombo->addItem(Kopete::Group::topLevel()->displayName());

    foreach (Kopete::Group *group, groups) {
        if (group->type() != Kopete::Group::Normal) {
            continue;
        }
        QString groupname = group->displayName();
        if (!groupname.isEmpty()) {
            groupItems.insert(groupname, group);
            ui_groupKABC.groupCombo->addItem(groupname);
        }
    }

    if (!addContactPage) {
        kDebug(14000)
            <<"Error while creating addcontactpage" << endl;
    } else {
        addDialog->setMainWidget(mainWid);
        if (addDialog->exec() == QDialog::Accepted) {
            if (addContactPage->validateData()) {
                Kopete::MetaContact *metacontact = new Kopete::MetaContact();
                metacontact->addToGroup(groupItems[ ui_groupKABC.groupCombo->currentText() ]);
                metacontact->setKabcId(ui_groupKABC.widAddresseeLink->uid());
                if (addContactPage->apply(account, metacontact)) {
                    Kopete::ContactList::self()->addMetaContact(metacontact);
                } else {
                    delete metacontact;
                }
            }
        }
    }
    addDialog->deleteLater();
}

void KopeteWindow::slotUpdateSize()
{
    /* resize with rules:
    - never will be taller than maxTall
    - never shorter than minTall
    - never will resize if contactlist is empty
    - never will resize if cursor is in window
    */

    if (this->geometry().contains(QCursor::pos()) == true) {
        return; // don't do anything if cursor is inside window
    }
    const int amountWindowBiggerThanContactList = 200;
    const QRect workArea = KWindowSystem::workArea();
    const int minHeight = 400;
    QRect newGeometry = geometry();
    const QRect oldGeometry = geometry();
    const int topFrameWidth = -(frameGeometry().top() - oldGeometry.top());
    const int bottomFrameWidth = frameGeometry().bottom() - oldGeometry.bottom();

    // desired height is height of full contents of contact list tree, as well as
    // some buffer for other elements in the main window
    int height = d->contactlist->visibleContentHeight();
    newGeometry.setHeight(height + amountWindowBiggerThanContactList);

    if (height) {
        // if new size is too big or too small, bring inside limits
        if (newGeometry.height() > workArea.height()) {
            newGeometry.setHeight(workArea.height() - topFrameWidth - bottomFrameWidth);
        } else if (newGeometry.height() < minHeight) {
            newGeometry.setHeight(minHeight);
        }

        // set position of new geometry rectangle to same position as the old one
        if (Kopete::AppearanceSettings::contactListResizeAnchor()
            == Kopete::AppearanceSettings::EnumContactListResizeAnchor::Top) {
            newGeometry.moveTop(oldGeometry.top());
        } else {
            newGeometry.moveBottom(oldGeometry.bottom());
        }

        // if the window + its frame is out of the work area, bring it just inside
        if ((newGeometry.top() - topFrameWidth) < workArea.top()) {
            newGeometry.moveTop(workArea.top() + topFrameWidth);
        } else if ((newGeometry.bottom() + bottomFrameWidth) > workArea.bottom()) {
            newGeometry.moveBottom(workArea.bottom() - bottomFrameWidth);
        }

        // do it!
        setGeometry(newGeometry);
    }
}

void KopeteWindow::slotNewInfoEvent()
{
    if (!d->infoEventWidget->isVisible()) {
        if (d->identitywidget->isVisible()) {
            d->identitywidget->setIdentity(0);
            d->identitywidget->setVisible(false);
        }
        d->infoEventWidget->setVisible(true);
    }
}

// vim: set noet ts=4 sts=4 sw=4:
