Warning, file /network/ktp-contact-list/main-widget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * This file is part of ktp-contact-list
0003  *
0004  * Copyright (C) 2009-2010 Collabora Ltd. <info@collabora.co.uk>
0005  *   @Author George Goldberg <george.goldberg@collabora.co.uk>
0006  * Copyright (C) 2011 Keith Rusler <xzekecomax@gmail.com>
0007  * Copyright (C) 2011-2013 Martin Klapetek <martin.klapetek@gmail.com>
0008  * Copyright (C) 2012-2012 David Edmundson <kde@davidedmundson.co.uk>
0009  *
0010  * This library is free software; you can redistribute it and/or
0011  * modify it under the terms of the GNU Lesser General Public
0012  * License as published by the Free Software Foundation; either
0013  * version 2.1 of the License, or (at your option) any later version.
0014  *
0015  * This library is distributed in the hope that it will be useful,
0016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0018  * Lesser General Public License for more details.
0019  *
0020  * You should have received a copy of the GNU Lesser General Public
0021  * License along with this library; if not, write to the Free Software
0022  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
0023  */
0024 
0025 #include "main-widget.h"
0026 #include "ktp-contactlist-debug.h"
0027 
0028 #include <QSortFilterProxyModel>
0029 #include <QPainter>
0030 #include <QMenu>
0031 #include <QToolButton>
0032 #include <QtCore/QWeakPointer>
0033 #include <QWidgetAction>
0034 #include <QCloseEvent>
0035 #include <QDialog>
0036 #include <QMenu>
0037 #include <QMenuBar>
0038 #include <QDebug>
0039 
0040 #include <TelepathyQt/PendingChannelRequest>
0041 #include <TelepathyQt/PendingContacts>
0042 #include <TelepathyQt/ContactManager>
0043 #include <TelepathyQt/PendingReady>
0044 #include <TelepathyQt/TextChannel>
0045 
0046 #include <KTp/actions.h>
0047 #include <KTp/contact-factory.h>
0048 #include <KTp/types.h>
0049 #include <KTp/global-presence.h>
0050 #include <KTp/Widgets/add-contact-dialog.h>
0051 #include <KTp/Widgets/join-chat-room-dialog.h>
0052 #include <KTp/Widgets/start-chat-dialog.h>
0053 
0054 #include <KIO/Job>
0055 #include <KMessageBox>
0056 #include <KProtocolInfo>
0057 #include <ksettings/Dialog>
0058 #include <KSharedConfig>
0059 #include <KStandardShortcut>
0060 #include <KNotification>
0061 #include <KToolInvocation>
0062 #include <KStandardAction>
0063 #include <KHelpMenu>
0064 #include <KAboutData>
0065 #include <KWindowSystem>
0066 #include <KLocalizedString>
0067 
0068 #ifdef HAVE_KPEOPLE
0069 // #include <kpeople/widgets/mergedialog.h> //Disable for now, re-enable when kpeople re-enables it
0070 #include <KPeople/PersonsModel>
0071 #endif
0072 
0073 #include "ui_main-widget.h"
0074 #include "tooltips/tooltipmanager.h"
0075 #include "context-menu.h"
0076 #include "filter-bar.h"
0077 
0078 bool kde_tp_filter_contacts_by_publication_status(const Tp::ContactPtr &contact)
0079 {
0080     return contact->publishState() == Tp::Contact::PresenceStateAsk;
0081 }
0082 
0083 MainWidget::MainWidget(QWidget *parent)
0084     : KMainWindow(parent),
0085       m_globalMenu(NULL),
0086       m_settingsDialog(NULL),
0087       m_joinChatRoom(NULL),
0088       m_makeCall(NULL),
0089       m_mergeContacts(NULL),
0090       m_contactListTypeGroup(NULL),
0091       m_blockedFilterGroup(NULL),
0092       m_quitAction(NULL)
0093 {
0094     setupUi(this);
0095 
0096     m_filterBar->hide();
0097     setAutoSaveSettings();
0098     setupTelepathy();
0099 
0100     KSharedConfigPtr config = KSharedConfig::openConfig();
0101     KConfigGroup guiConfigGroup(config, "GUI");
0102     setupActions(guiConfigGroup);
0103     setupToolBar();
0104     setupGlobalMenu();
0105 
0106     // Restore window geometry, global/by-account presence, search widget state
0107     restoreGeometry(guiConfigGroup.readEntry("window_geometry", QByteArray()));
0108     toggleSearchWidget(guiConfigGroup.readEntry("pin_filterbar", true));
0109     m_presenceChooser->show();
0110 
0111     m_contextMenu = new ContextMenu(m_contactsListView);
0112     new ToolTipManager(m_contactsListView);
0113 
0114     m_messageWidget->setWordWrap(true);
0115     m_messageWidget->setCloseButtonVisible(true);
0116     m_messageWidget->hide();
0117 
0118     connect(m_contactsListView, SIGNAL(customContextMenuRequested(QPoint)),
0119             this, SLOT(onCustomContextMenuRequested(QPoint)));
0120     connect(m_contactsListView->contactsModel(), SIGNAL(modelInitialized(bool)),
0121             this, SLOT(onModelInitialized(bool)));
0122 
0123     connect(m_showOfflineAction, SIGNAL(toggled(bool)),
0124             m_contactsListView, SLOT(toggleOfflineContacts(bool)));
0125     connect(m_sortByPresenceAction, SIGNAL(activeChanged(bool)),
0126             m_contactsListView, SLOT(toggleSortByPresence(bool)));
0127     connect(m_metacontactToggleAction, SIGNAL(triggered(bool)),
0128             this, SLOT(onMetacontactToggleTriggered()));
0129 
0130     connect(m_filterBar, SIGNAL(filterChanged(QString)),
0131             m_contactsListView, SLOT(setFilterString(QString)));
0132     connect(m_filterBar, SIGNAL(closeRequest()),
0133             m_filterBar, SLOT(hide()));
0134     connect(m_filterBar, SIGNAL(closeRequest()),
0135             m_searchContactAction, SLOT(trigger()));
0136 
0137     connect(m_contactsListView, SIGNAL(genericOperationFinished(Tp::PendingOperation*)),
0138             this, SLOT(onGenericOperationFinished(Tp::PendingOperation*)));
0139 
0140     connect(m_contactsListView, SIGNAL(actionStarted()),
0141             this, SLOT(clearSearch()));
0142 
0143     connect(m_contactsListView, SIGNAL(contactSelectionChanged()),
0144             this, SLOT(onContactSelectionChanged()));
0145 
0146 
0147     bool showOffline = guiConfigGroup.readEntry("show_offline", false);
0148     m_showOfflineAction->setChecked(showOffline);
0149     m_showOfflineAction->setActive(showOffline);
0150 
0151     bool sortByPresence = guiConfigGroup.readEntry("sort_by_presence", true);
0152     m_sortByPresenceAction->setActive(sortByPresence);
0153 
0154     bool useGroups = guiConfigGroup.readEntry("use_groups", true);
0155     m_contactsListView->toggleGroups(useGroups);
0156     m_contactsListView->toggleOfflineContacts(showOffline);
0157     m_contactsListView->toggleSortByPresence(sortByPresence);
0158 }
0159 
0160 MainWidget::~MainWidget()
0161 {
0162     //save the state of the filter bar, pinned or not
0163     KSharedConfigPtr config = KSharedConfig::openConfig();
0164     KConfigGroup configGroup(config, "GUI");
0165     configGroup.writeEntry("pin_filterbar", m_searchContactAction->isChecked());
0166     configGroup.writeEntry("use_groups", m_groupContactsActionGroup->actions().first()->isChecked());
0167     configGroup.writeEntry("show_offline", m_showOfflineAction->isChecked());
0168     configGroup.writeEntry("sort_by_presence", m_sortByPresenceAction->isActive());
0169     configGroup.config()->sync();
0170 }
0171 
0172 void MainWidget::onAccountManagerReady(Tp::PendingOperation* op)
0173 {
0174     if (op && op->isError()) {
0175         qCDebug(KTP_CONTACTLIST_MODULE) << op->errorName();
0176         qCDebug(KTP_CONTACTLIST_MODULE) << op->errorMessage();
0177 
0178         KMessageBox::error(this,
0179                            i18n("Something unexpected happened to the core part of your Instant Messaging system "
0180                            "and it couldn't be initialized. Try restarting the Contact List."),
0181                            i18n("IM system failed to initialize"));
0182         return;
0183     }
0184 
0185     m_presenceChooser->globalPresence()->setAccountManager(m_accountManager);
0186     m_contactsListView->setAccountManager(m_accountManager);
0187     m_contextMenu->setAccountManager(m_accountManager);
0188 }
0189 
0190 void MainWidget::showMessageToUser(const QString& text, const MainWidget::SystemMessageType type)
0191 {
0192     //The pointer is automatically deleted when the event is closed
0193     KNotification *notification;
0194     if (type == MainWidget::SystemMessageError) {
0195         notification = new KNotification("telepathyError", this);
0196     } else {
0197         notification = new KNotification("telepathyInfo", this);
0198     }
0199 
0200     notification->setComponentName("ktelepathy");
0201 
0202     notification->setText(text);
0203     notification->sendEvent();
0204 }
0205 
0206 void MainWidget::onAddContactRequest()
0207 {
0208     if (m_accountManager->isReady()) {
0209         KTp::AddContactDialog *dialog = new KTp::AddContactDialog(m_accountManager, this);
0210         dialog->setAttribute(Qt::WA_DeleteOnClose);
0211         dialog->show();
0212     }
0213 }
0214 
0215 void MainWidget::onStartChatRequest()
0216 {
0217     if (m_accountManager->isReady()) {
0218         KTp::StartChatDialog *dialog = new KTp::StartChatDialog(m_accountManager, this);
0219         dialog->setAttribute(Qt::WA_DeleteOnClose);
0220         dialog->show();
0221     }
0222 }
0223 
0224 
0225 void MainWidget::onCustomContextMenuRequested(const QPoint &pos)
0226 {
0227     QModelIndex index = m_contactsListView->indexAt(pos);
0228 
0229     if (!index.isValid()) {
0230         return;
0231     }
0232 
0233     KTp::RowType type = (KTp::RowType)index.data(KTp::RowTypeRole).toInt();
0234 
0235     QMenu *menu = 0;
0236 
0237     if (type == KTp::ContactRowType || type == KTp::PersonRowType) {
0238         menu = m_contextMenu->contactContextMenu(index);
0239     } else if (type == KTp::GroupRowType) {
0240         menu = m_contextMenu->groupContextMenu(index);
0241     }
0242 
0243     if (menu) {
0244         menu->exec(QCursor::pos());
0245         menu->deleteLater();
0246     }
0247 }
0248 
0249 void MainWidget::onGenericOperationFinished(Tp::PendingOperation* operation)
0250 {
0251     if (operation->isError()) {
0252         QString errorMsg(operation->errorName() + ": " + operation->errorMessage());
0253         showMessageToUser(errorMsg, SystemMessageError);
0254     }
0255 }
0256 
0257 void MainWidget::onJoinChatRoomRequested()
0258 {
0259     if (m_accountManager->isReady()) {
0260         KTp::JoinChatRoomDialog *dialog = new KTp::JoinChatRoomDialog(m_accountManager);
0261         dialog->setAttribute(Qt::WA_DeleteOnClose);
0262 
0263         dialog->show();
0264     }
0265 }
0266 
0267 void MainWidget::onMakeCallRequested()
0268 {
0269     KToolInvocation::kdeinitExec(QLatin1String("ktp-dialout-ui"));
0270 }
0271 
0272 void MainWidget::onMergeContactsDialogRequested()
0273 {
0274     /*
0275 #ifdef HAVE_KPEOPLE
0276     KPeople::MergeDialog* mergeDialog = new KPeople::MergeDialog(this);
0277     //create a new model that queries all the data otherwise we will only show IM contacts
0278     KPeople::PersonsModel* model = new KPeople::PersonsModel(mergeDialog);
0279     mergeDialog->setPersonsModel(model);
0280     model->startQuery(KPeople::PersonsModelFeature::allFeatures());
0281     mergeDialog->show();
0282     mergeDialog->setAttribute(Qt::WA_DeleteOnClose);
0283 #endif
0284 */
0285 }
0286 
0287 void MainWidget::closeEvent(QCloseEvent* e)
0288 {
0289     KSharedConfigPtr config = KSharedConfig::openConfig();
0290     KConfigGroup generalConfigGroup(config, "General");
0291     KConfigGroup notifyConigGroup(config, "Notification Messages");
0292     KConfigGroup guiConfigGroup(config, "GUI");
0293 
0294     if (qApp->closingDown()) {
0295         //the standard KMessageBox control saves "true" if you select the checkbox, therefore the reversed var name
0296         bool dontCheckForPlasmoid = notifyConigGroup.readEntry("dont_check_for_plasmoid", false);
0297         bool onlineAccounts = !m_presenceChooser->globalPresence()->onlineAccounts()->accounts().isEmpty();
0298 
0299         if (onlineAccounts && !dontCheckForPlasmoid) {
0300             if (!isPresencePlasmoidPresent()) {
0301                 switch (KMessageBox::warningYesNoCancel(this,
0302                         i18n("You do not have any other presence controls active (a Presence widget for example).\n"
0303                             "Do you want to stay online or would you rather go offline?"),
0304                         i18n("No Other Presence Controls Found"),
0305                         KGuiItem(i18n("Stay Online"), QIcon::fromTheme("user-online")),
0306                         KGuiItem(i18n("Go Offline"), QIcon::fromTheme("user-offline")),
0307                         KStandardGuiItem::cancel(),
0308                         QString("dont_check_for_plasmoid"))) {
0309 
0310                     case KMessageBox::No:
0311                         generalConfigGroup.writeEntry("go_offline_when_closing", true);
0312                         m_presenceChooser->globalPresence()->setPresence(KTp::Presence::offline(), KTp::GlobalPresence::Session);
0313                         break;
0314                     case KMessageBox::Cancel:
0315                     default:
0316                         e->ignore();
0317                         return;
0318                 }
0319             }
0320         } else if (onlineAccounts && dontCheckForPlasmoid) {
0321             bool shouldGoOffline = generalConfigGroup.readEntry("go_offline_when_closing", false);
0322             if (shouldGoOffline) {
0323                 m_presenceChooser->globalPresence()->setPresence(KTp::Presence::offline(), KTp::GlobalPresence::Session);
0324             }
0325         }
0326 
0327         generalConfigGroup.config()->sync();
0328     }
0329 
0330     // Save window geometry
0331     guiConfigGroup.writeEntry("window_geometry", saveGeometry());
0332     guiConfigGroup.config()->sync();
0333 
0334     KMainWindow::closeEvent(e);
0335 }
0336 
0337 bool MainWidget::isPresencePlasmoidPresent() const
0338 {
0339     QDBusReply<bool> serviceRegistered = QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.Telepathy.PresenceApplet");
0340 
0341     if (serviceRegistered.isValid() && serviceRegistered.value()) {
0342         return true;
0343     } else {
0344         return false;
0345     }
0346 }
0347 
0348 void MainWidget::toggleSearchWidget(bool show)
0349 {
0350     m_searchContactAction->setChecked(show);
0351     if(show) {
0352         m_filterBar->show();
0353     } else {
0354         m_contactsListView->setFilterString(QString());
0355         m_filterBar->clear();
0356         m_filterBar->hide();
0357     }
0358 }
0359 
0360 void MainWidget::clearSearch()
0361 {
0362     m_filterBar->clear();
0363 }
0364 
0365 void MainWidget::setupGlobalMenu()
0366 {
0367     // Since our menu is hidden, its shortcuts do not work.
0368     // Here's a workarond: we must assign global menu's unique
0369     // items to main window. Since it's always active when an
0370     // application is active, shortcuts now will work properly.
0371 
0372     m_globalMenu = new QMenuBar(this);
0373     m_globalMenu->setVisible(false);
0374 
0375     QMenu *contacts = new QMenu(i18n("Contacts"), m_globalMenu);
0376     contacts->addAction(m_addContactAction);
0377     contacts->addAction(m_joinChatRoom);
0378     if (!QStandardPaths::findExecutable("ktp-dialout-ui").isEmpty()) {
0379         contacts->addAction(m_makeCall);
0380     }
0381     //temporarily disable until funcationality is restored in libkpeople
0382     if (KTp::kpeopleEnabled()) {
0383 //         contacts->addAction(m_mergeContacts);
0384     }
0385     contacts->addAction(m_settingsDialog);
0386     contacts->addSeparator();
0387     contacts->addAction(m_quitAction);
0388     this->addAction(m_quitAction); // Shortcuts workaround.
0389     m_globalMenu->addMenu(contacts);
0390 
0391     QMenu *view = new QMenu(i18n("View"), m_globalMenu);
0392     view->addAction(m_showOfflineAction);
0393     view->addAction(m_sortByPresenceAction);
0394     view->addSeparator();
0395     QMenu *view_contactListTypeMenu = new QMenu(i18n("Contact List Type"), view);
0396     view_contactListTypeMenu->addActions(m_contactListTypeGroup->actions());
0397     view->addMenu(view_contactListTypeMenu);
0398     QMenu *view_blockedFilterMenu = new QMenu(i18n("Shown Contacts"), view);
0399     view_blockedFilterMenu->addActions(m_blockedFilterGroup->actions());
0400     view->addMenu(view_blockedFilterMenu);
0401     QMenu *view_showGroupedMenu = new QMenu(i18n("Contact Grouping"), view);
0402     view_showGroupedMenu->addActions(m_groupContactsActionGroup->actions());
0403     view->addMenu(view_showGroupedMenu);
0404     m_globalMenu->addMenu(view);
0405     KHelpMenu *helpMenu = new KHelpMenu(this, KAboutData::applicationData());
0406     m_globalMenu->addMenu(helpMenu->menu());
0407 }
0408 
0409 void MainWidget::setupToolBar()
0410 {
0411     m_toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
0412     if (KTp::kpeopleEnabled()) {
0413         m_toolBar->addAction(m_metacontactToggleAction);
0414     }
0415     m_toolBar->addAction(m_addContactAction);
0416     m_toolBar->addAction(m_searchContactAction);
0417     m_toolBar->addAction(m_showOfflineAction);
0418     m_toolBar->addAction(m_sortByPresenceAction);
0419     m_toolBar->addSeparator();
0420     m_toolBar->addAction(m_startChatAction);
0421     m_toolBar->addAction(m_joinChatRoom);
0422 
0423     QWidget *toolBarSpacer = new QWidget(this);
0424     toolBarSpacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
0425     m_toolBar->addWidget(toolBarSpacer);
0426 
0427     QToolButton *settingsButton = new QToolButton(this);
0428     settingsButton->setIcon(QIcon::fromTheme("configure"));
0429     settingsButton->setPopupMode(QToolButton::InstantPopup);
0430 
0431     QMenu *settingsButtonMenu = new QMenu(settingsButton);
0432     settingsButtonMenu->addAction(m_settingsDialog);
0433 
0434     QActionGroup *delegateTypeGroup = new QActionGroup(this);
0435     delegateTypeGroup->setExclusive(true);
0436 
0437     QMenu *setDelegateTypeMenu = new QMenu(settingsButtonMenu);
0438     setDelegateTypeMenu->setTitle(i18n("Contact List Type"));
0439     setDelegateTypeMenu->addActions(m_contactListTypeGroup->actions());
0440     settingsButtonMenu->addMenu(setDelegateTypeMenu);
0441 
0442     QMenu *setBlockedFilterMenu = new QMenu(settingsButtonMenu);
0443     setBlockedFilterMenu->setTitle(i18n("Shown Contacts"));
0444     setBlockedFilterMenu->addActions(m_blockedFilterGroup->actions());
0445     settingsButtonMenu->addMenu(setBlockedFilterMenu);
0446 
0447     QMenu *showGroupedMenu = new QMenu(settingsButton);
0448     showGroupedMenu->setTitle(i18n("Contact Grouping"));
0449     showGroupedMenu->addActions(m_groupContactsActionGroup->actions());
0450     settingsButtonMenu->addMenu(showGroupedMenu);
0451 
0452     if (!QStandardPaths::findExecutable("ktp-dialout-ui").isEmpty()) {
0453         settingsButtonMenu->addAction(m_makeCall);
0454     }
0455 
0456     if (KTp::kpeopleEnabled()) {
0457 //         settingsButtonMenu->addAction(m_mergeContacts);
0458     }
0459 
0460     settingsButtonMenu->addSeparator();
0461 
0462     KHelpMenu *helpMenu = new KHelpMenu(this, KAboutData::applicationData());
0463     settingsButtonMenu->addMenu(helpMenu->menu());
0464 
0465     settingsButton->setMenu(settingsButtonMenu);
0466 
0467     m_toolBar->addWidget(settingsButton);
0468 }
0469 
0470 void MainWidget::setupTelepathy()
0471 {
0472     Tp::registerTypes();
0473 
0474     if (KTp::kpeopleEnabled()) {
0475         m_accountManager = KTp::accountManager();
0476     } else {
0477 
0478         Tp::AccountFactoryPtr  accountFactory = Tp::AccountFactory::create(QDBusConnection::sessionBus(),
0479                                                                         Tp::Features() << Tp::Account::FeatureCore
0480                                                                         << Tp::Account::FeatureAvatar
0481                                                                         << Tp::Account::FeatureCapabilities
0482                                                                         << Tp::Account::FeatureProtocolInfo
0483                                                                         << Tp::Account::FeatureProfile);
0484 
0485         Tp::ConnectionFactoryPtr connectionFactory = Tp::ConnectionFactory::create(QDBusConnection::sessionBus(),
0486                                                                                 Tp::Features() << Tp::Connection::FeatureCore
0487                                                                                 << Tp::Connection::FeatureSelfContact);
0488 
0489         Tp::ContactFactoryPtr contactFactory = KTp::ContactFactory::create(Tp::Features()  << Tp::Contact::FeatureAlias
0490                                                                         << Tp::Contact::FeatureAvatarToken
0491                                                                         << Tp::Contact::FeatureAvatarData
0492                                                                         << Tp::Contact::FeatureSimplePresence
0493                                                                         << Tp::Contact::FeatureCapabilities
0494                                                                         << Tp::Contact::FeatureClientTypes);
0495 
0496         Tp::ChannelFactoryPtr channelFactory = Tp::ChannelFactory::create(QDBusConnection::sessionBus());
0497 
0498         m_accountManager = Tp::AccountManager::create(QDBusConnection::sessionBus(),
0499                                                     accountFactory,
0500                                                     connectionFactory,
0501                                                     channelFactory,
0502                                                     contactFactory);
0503     }
0504 
0505     connect(m_accountManager->becomeReady(), SIGNAL(finished(Tp::PendingOperation*)),
0506             this, SLOT(onAccountManagerReady(Tp::PendingOperation*)));
0507 }
0508 
0509 QAction *MainWidget::createAction(const QString &text, QObject *signalReceiver, const char *slot, const QIcon &icon = QIcon())
0510 {
0511     QAction *action = new QAction(icon, text, this);
0512     action->setToolTip(text);
0513     connect(action, SIGNAL(triggered(bool)), signalReceiver, slot);
0514     return action;
0515 }
0516 
0517 QAction *MainWidget::createAction(const QString& text, QObject *signalReceiver, const char* slot, bool isChecked, const QIcon &icon = QIcon())
0518 {
0519     QAction *action = createAction(text, signalReceiver, slot, icon);
0520     action->setCheckable(true);
0521     action->setChecked(isChecked);
0522     return action;
0523 }
0524 
0525 void MainWidget::setupActions(const KConfigGroup& guiConfigGroup)
0526 {
0527     m_settingsDialog = KStandardAction::preferences(m_contactsListView, SLOT(showSettingsKCM()),this);
0528     m_settingsDialog->setText(i18n("Instant Messaging Settings...")); // We set text manually since standard name is too long
0529 
0530     m_quitAction = KStandardAction::quit(this, SLOT(close()), this);
0531     m_quitAction->setMenuRole(QAction::QuitRole);
0532 
0533     m_joinChatRoom = createAction(i18n("Join Chat Room..."), this, SLOT(onJoinChatRoomRequested()));
0534     m_joinChatRoom->setIcon(QIcon::fromTheme("im-irc"));
0535     m_makeCall = createAction(i18n("Make a Call..."), this, SLOT(onMakeCallRequested()));
0536     m_mergeContacts = createAction(i18n("Merge Contacts..."), this, SLOT(onMergeContactsDialogRequested()));
0537     m_addContactAction = createAction(i18n("Add New Contacts..."), this, SLOT(onAddContactRequest()), QIcon::fromTheme("list-add-user"));
0538     m_searchContactAction = createAction(i18n("Find Contact"), this, SLOT(toggleSearchWidget(bool)),
0539                                          guiConfigGroup.readEntry("pin_filterbar", true), QIcon::fromTheme("edit-find"));
0540     m_searchContactAction->setShortcuts(KStandardShortcut::find());
0541     m_startChatAction = createAction(i18n("Start a Chat..."), this, SLOT(onStartChatRequest()), QIcon::fromTheme("telepathy-kde"));
0542 
0543     // Dual actions
0544     m_metacontactToggleAction = new KDualAction(i18n("Split Selected Contacts"),
0545                                                 i18n("Merge Selected Contacts"),
0546                                                 this);
0547     m_metacontactToggleAction->setActiveIcon(QIcon::fromTheme("user-group-new"));
0548     m_metacontactToggleAction->setInactiveIcon(QIcon::fromTheme("user-group-delete"));
0549     m_metacontactToggleAction->setActive(true);
0550     m_metacontactToggleAction->setDisabled(true);
0551     m_metacontactToggleAction->setAutoToggle(false);
0552 
0553     m_groupContactsActionGroup = new QActionGroup(this);
0554     m_groupContactsActionGroup->setExclusive(true);
0555     m_groupContactsActionGroup->addAction(createAction(i18n("Show Contacts by Groups"), m_contactsListView, SLOT(showGrouped()),
0556                                           guiConfigGroup.readEntry("use_groups", true)));
0557     QString useGroupsDisabledText;
0558     if (KTp::kpeopleEnabled()) {
0559         useGroupsDisabledText = i18n("Do Not Group");
0560     }
0561     else {
0562         useGroupsDisabledText = i18n("Show Contacts by Accounts");
0563     }
0564     m_groupContactsActionGroup->addAction(createAction(useGroupsDisabledText, m_contactsListView, SLOT(showUngrouped()),
0565                                           ! guiConfigGroup.readEntry("use_groups", true)));
0566 
0567     m_showOfflineAction = new KDualAction(i18n("Show Offline Contacts"),
0568                                           i18n("Hide Offline Contacts"),
0569                                           this);
0570     m_showOfflineAction->setActiveIcon(QIcon::fromTheme("show-offline"));
0571     m_showOfflineAction->setInactiveIcon(QIcon::fromTheme("show-offline"));
0572     m_showOfflineAction->setCheckable(true);
0573     m_showOfflineAction->setChecked(false);
0574     m_showOfflineAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
0575 
0576     m_sortByPresenceAction = new KDualAction(i18n("Sort by Presence"),
0577                                              i18n("Sort by Name"),
0578                                              this);
0579     m_sortByPresenceAction->setActiveIcon(QIcon::fromTheme("sort-presence"));
0580     m_sortByPresenceAction->setInactiveIcon(QIcon::fromTheme("sort-name"));
0581 
0582     // Setup contact list appearance
0583     m_contactListTypeGroup = new QActionGroup(this);
0584     m_contactListTypeGroup->setExclusive(true);
0585     m_contactListTypeGroup->addAction(createAction(i18n("Use Full List"), m_contactsListView, SLOT(onSwitchToFullView()),
0586                                                    guiConfigGroup.readEntry("selected_delegate", "normal") == QLatin1String("full")));
0587     m_contactListTypeGroup->addAction(createAction(i18n("Use Normal List"), m_contactsListView, SLOT(onSwitchToCompactView()),
0588                                                    guiConfigGroup.readEntry("selected_delegate", "normal") == QLatin1String("normal")
0589                                                    || guiConfigGroup.readEntry("selected_delegate", "normal") == QLatin1String("compact"))); //needed for backwards compatibility
0590 
0591     m_contactListTypeGroup->addAction(createAction(i18n("Use Minimalistic List"), m_contactsListView, SLOT(onSwitchToMiniView()),
0592                                                    guiConfigGroup.readEntry("selected_delegate", "normal") == QLatin1String("mini")));
0593 
0594     // Setup blocked contacts filtering
0595     QString shownContacts = guiConfigGroup.readEntry("shown_contacts", "unblocked");
0596     m_blockedFilterGroup = new QActionGroup(this);
0597     m_blockedFilterGroup->setExclusive(true);
0598     m_blockedFilterGroup->addAction(createAction(i18n("Show All Contacts"), m_contactsListView, SLOT(onShowAllContacts()),
0599                                                 shownContacts == QLatin1String("all")));
0600     m_blockedFilterGroup->addAction(createAction(i18n("Show Unblocked Contacts"), m_contactsListView, SLOT(onShowUnblockedContacts()),
0601                                                 shownContacts == QLatin1String("unblocked")));
0602     m_blockedFilterGroup->addAction(createAction(i18n("Show Blocked Contacts"), m_contactsListView, SLOT(onShowBlockedContacts()),
0603                                                 shownContacts == QLatin1String("blocked")));
0604 }
0605 
0606 void MainWidget::toggleWindowVisibility()
0607 {
0608     if (isActiveWindow()) {
0609         close();
0610     } else {
0611         KWindowSystem::forceActiveWindow(this->effectiveWinId());
0612     }
0613 }
0614 
0615 void MainWidget::onContactSelectionChanged()
0616 {
0617     QModelIndexList selection = m_contactsListView->selectionModel()->selectedIndexes();
0618     if (selection.size() == 0) {
0619         //if nothing is selected, disable the button
0620         m_metacontactToggleAction->setActive(true);
0621         m_metacontactToggleAction->setDisabled(true);
0622         return;
0623     } else if (selection.size() == 1) {
0624         QModelIndex index = selection.first();
0625         if (index.data(KTp::RowTypeRole).toInt() == KTp::PersonRowType ||
0626             (index.parent().isValid() && index.parent().data(KTp::RowTypeRole).toInt() == KTp::PersonRowType)) {
0627             //if a person is selected or a subcontact is selected, switch to unlink action and enable
0628             m_metacontactToggleAction->setActive(false);
0629             m_metacontactToggleAction->setEnabled(true);
0630             return;
0631         }
0632     } else if (selection.size() > 1) {
0633         bool invalid = false;
0634         //we cannot merge child contact of a person with anything else
0635         Q_FOREACH (const QModelIndex &index, selection) {
0636             if (index.parent().isValid() && index.parent().data(KTp::RowTypeRole).toInt() == KTp::PersonRowType) {
0637                 invalid = true;
0638                 break;
0639             }
0640         }
0641 
0642         if (!invalid) {
0643             m_metacontactToggleAction->setActive(true);
0644             m_metacontactToggleAction->setEnabled(true);
0645             return;
0646         }
0647     }
0648 
0649     m_metacontactToggleAction->setActive(true);
0650     m_metacontactToggleAction->setDisabled(true);
0651 }
0652 
0653 void MainWidget::onMetacontactToggleTriggered()
0654 {
0655 #ifdef HAVE_KPEOPLE
0656     const QModelIndexList selection = m_contactsListView->selectionModel()->selectedIndexes();
0657 
0658     Q_ASSERT(!selection.isEmpty());
0659     if (m_metacontactToggleAction->isActive()) {
0660         //we're merging contacts
0661         bool invalid = false;
0662         QModelIndex person;
0663         QStringList uris;
0664 
0665         Q_FOREACH (const QModelIndex &index, selection) {
0666             if (index.parent().isValid()
0667                     && index.parent().data(KTp::RowTypeRole).toInt() == KTp::PersonRowType) {
0668                 //we can merge only standalone contacts, not a contact that's already part of a person
0669                 invalid = true;
0670                 qCDebug(KTP_CONTACTLIST_MODULE) << "Found selected subcontact, aborting";
0671                 break;
0672             }
0673 
0674             //the selection can have at most one person, so if we encounter second person
0675             //we break and do nothing
0676             if (index.data(KTp::RowTypeRole).toInt() == KTp::PersonRowType) {
0677                 if (person.isValid()) {
0678                     invalid = true;
0679                     qCDebug(KTP_CONTACTLIST_MODULE) << "Found second person, aborting";
0680                     break;
0681                 } else {
0682                     qCDebug(KTP_CONTACTLIST_MODULE) << "Found a person, adding";
0683                     person = index;
0684                 }
0685             }
0686 
0687             //if we're dealing with contact that's a child of selected person
0688             //(we should never get here)
0689             if (index.parent().isValid() && index.parent() == person) {
0690                 invalid = true;
0691                 qCDebug(KTP_CONTACTLIST_MODULE) << "Found subcontact of selected person, aborting";
0692                 break;
0693             }
0694 
0695             uris << index.data(KTp::PersonIdRole).toString();
0696         }
0697 
0698         if (!invalid) {
0699             KPeople::mergeContacts(uris);
0700         }
0701     } else {
0702         //we're removing contacts from person
0703         QStringList contacts;
0704         const QModelIndex &index = selection.first();
0705         QString uri;
0706 
0707         if (index.parent().isValid() && index.parent().data(KTp::RowTypeRole).toInt() == KTp::PersonRowType) {
0708             uri = index.data(KTp::ContactUriRole).toString();
0709         } else {
0710             uri = index.data(KTp::PersonIdRole).toString();
0711         }
0712         KPeople::unmergeContact(uri);
0713     }
0714 #endif
0715 }
0716 
0717 void MainWidget::onModelInitialized(bool success)
0718 {
0719     if (!success) {
0720         m_messageWidget->setMessageType(KMessageWidget::Warning);
0721         m_messageWidget->setText(i18n("Some data sources failed to initialize properly, your contact list might be incomplete."));
0722         m_messageWidget->animatedShow();
0723     }
0724 
0725     m_contactsListView->contactsModel()->setTrackUnreadMessages(true);
0726 }