Use qtpaths directly
[kdepim.git] / kaddressbook / mainwidget.cpp
blobead5e6ac04c1503694e015b5a69f180725f1e792
1 /*
2 This file is part of KAddressBook.
4 Copyright (c) 2007 Tobias Koenig <tokoe@kde.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "mainwidget.h"
22 #include "contactswitcher.h"
23 #include "globalcontactmodel.h"
24 #include "modelcolumnmanager.h"
25 #include "printing/printingwizard.h"
26 #include "merge/manualmerge/mergecontactsdialog.h"
27 #include "merge/searchduplicate/searchandmergecontactduplicatecontactdialog.h"
28 #include "quicksearchwidget.h"
29 #include "settings.h"
30 #include "xxport/xxportmanager.h"
31 #include "utils.h"
32 #include "kaddressbookadaptor.h"
33 #include "categoryselectwidget.h"
34 #include "categoryfilterproxymodel.h"
35 #include "kaddressbook_options.h"
37 #include "sendmail/mailsenderjob.h"
38 #include "sendvcards/sendvcardsjob.h"
39 #include "gravatar/widgets/gravatarupdatedialog.h"
41 #include "KaddressbookGrantlee/GrantleeContactFormatter"
42 #include "KaddressbookGrantlee/GrantleeContactGroupFormatter"
43 #include "grantleetheme/grantleethememanager.h"
45 #include "Libkdepim/UiStateSaver"
47 #include <PimCommon/CollectionAclPage>
48 #include <PimCommon/ImapAclAttribute>
50 #include <AkonadiWidgets/ETMViewStateSaver>
51 #include <AkonadiCore/CollectionFilterProxyModel>
52 #include <AkonadiWidgets/ControlGui>
53 #include <AkonadiCore/EntityMimeTypeFilterModel>
54 #include <AkonadiWidgets/EntityTreeView>
55 #include <AkonadiWidgets/ItemView>
56 #include <AkonadiCore/MimeTypeChecker>
57 #include <AkonadiCore/AttributeFactory>
58 #include <AkonadiWidgets/CollectionPropertiesDialog>
59 #include <AkonadiSearch/Debug/akonadisearchdebugdialog.h>
60 #include <KContacts/Addressee>
61 #include <QPointer>
62 #include "PimCommon/ManageServerSideSubscriptionJob"
63 #include "PimCommon/PimUtil"
65 #include <Akonadi/Contact/ContactDefaultActions>
66 #include <Akonadi/Contact/ContactGroupEditorDialog>
67 #include <Akonadi/Contact/ContactGroupViewer>
68 #include <Akonadi/Contact/ContactsFilterProxyModel>
69 #include <Akonadi/Contact/ContactsTreeModel>
70 #include <Akonadi/Contact/ContactViewer>
71 #include <Akonadi/Contact/StandardContactActionManager>
73 #include <KContacts/ContactGroup>
74 #include "kaddressbook_debug.h"
75 #include <QAction>
76 #include <QApplication>
77 #include <KActionCollection>
78 #include <KActionMenu>
79 #include <KCheckableProxyModel>
80 #include <KDescendantsProxyModel>
81 #include <KLocalizedString>
82 #include <KSelectionProxyModel>
83 #include <QTextBrowser>
84 #include <KToggleAction>
85 #include <KCMultiDialog>
86 #include <KPrintPreview>
87 #include <KXMLGUIClient>
88 #include <KIconLoader>
89 #include <KMessageBox>
91 #include <QActionGroup>
92 #include <QHBoxLayout>
93 #include <QHeaderView>
94 #include <QPrinter>
95 #include <QPrintDialog>
96 #include <QSplitter>
97 #include <QStackedWidget>
98 #include <QDBusConnection>
99 #include <QDesktopServices>
100 #include <ItemModifyJob>
102 namespace
104 static bool isStructuralCollection(const Akonadi::Collection &collection)
106 QStringList mimeTypes;
107 mimeTypes << KContacts::Addressee::mimeType() << KContacts::ContactGroup::mimeType();
108 const QStringList collectionMimeTypes = collection.contentMimeTypes();
109 foreach (const QString &mimeType, mimeTypes) {
110 if (collectionMimeTypes.contains(mimeType)) {
111 return false;
114 return true;
117 class StructuralCollectionsNotCheckableProxy : public KCheckableProxyModel
119 public:
120 explicit StructuralCollectionsNotCheckableProxy(QObject *parent)
121 : KCheckableProxyModel(parent)
125 QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE
127 if (!index.isValid()) {
128 return QVariant();
131 if (role == Qt::CheckStateRole) {
132 // Don't show the checkbox if the collection can't contain incidences
133 const Akonadi::Collection collection =
134 index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
135 if (collection.isValid() && isStructuralCollection(collection)) {
136 return QVariant();
139 return KCheckableProxyModel::data(index, role);
145 MainWidget::MainWidget(KXMLGUIClient *guiClient, QWidget *parent)
146 : QWidget(parent),
147 mAllContactsModel(Q_NULLPTR),
148 mXmlGuiClient(guiClient),
149 mGrantleeThemeManager(Q_NULLPTR),
150 mQuickSearchAction(Q_NULLPTR),
151 mServerSideSubscription(Q_NULLPTR),
152 mSearchGravatarAction(Q_NULLPTR),
153 mSendVcardAction(Q_NULLPTR),
154 mSendEmailAction(Q_NULLPTR)
157 (void) new KaddressbookAdaptor(this);
158 QDBusConnection::sessionBus().registerObject(QStringLiteral("/KAddressBook"), this);
160 mXXPortManager = new XXPortManager(this);
161 Akonadi::AttributeFactory::registerAttribute<PimCommon::ImapAclAttribute>();
163 setupGui();
164 setupActions(guiClient->actionCollection());
167 * The item models, proxies and views have the following structure:
169 * mItemView
172 * mContactsFilterModel
175 * mCategorySelectWidget --> mCategoryFilterModel
178 * mItemTree
181 * | mAllContactsModel
182 * | ^
183 * | |
184 * mCollectionView selectionProxyModel descendantsModel
185 * ^ ^ ^ ^
186 * | | | |
187 * | selectionModel | |
188 * | | | |
189 * proxyModel ---------' | |
190 * ^ | |
191 * | | |
192 * mCollectionTree | |
193 * ^ | |
194 * | | _______________/
195 * \ / /
196 * GlobalContactModel::instance()
199 * GlobalContactModel::instance(): The global contact model (contains collections and items)
200 * mCollectionTree: Filters out all items
201 * proxyModel: Allows the user to select collections by checkboxes
202 * selectionModel: Represents the selected collections that have been
203 * selected in proxyModel
204 * mCollectionView: Shows the collections (address books) in a view
205 * selectionProxyModel: Filters out all collections and items that are no children
206 * of the collections currently selected in selectionModel
207 * mItemTree: Filters out all collections
208 * mCategorySelectWidget: Selects a list of categories for filtering
209 * mCategoryFilterModel: Filters the contacts by the selected categories
210 * mContactsFilterModel: Filters the contacts by the content of mQuickSearchWidget
211 * mItemView: Shows the items (contacts and contact groups) in a view
213 * descendantsModel: Flattens the item/collection tree to a list
214 * mAllContactsModel: Provides a list of all available contacts from all
215 * address books
218 mCollectionTree = new Akonadi::EntityMimeTypeFilterModel(this);
219 mCollectionTree->setDynamicSortFilter(true);
220 mCollectionTree->setSortCaseSensitivity(Qt::CaseInsensitive);
221 mCollectionTree->setSourceModel(GlobalContactModel::instance()->model());
222 mCollectionTree->addMimeTypeInclusionFilter(Akonadi::Collection::mimeType());
223 mCollectionTree->setHeaderGroup(Akonadi::EntityTreeModel::CollectionTreeHeaders);
225 mCollectionSelectionModel = new QItemSelectionModel(mCollectionTree);
226 StructuralCollectionsNotCheckableProxy *checkableProxyModel =
227 new StructuralCollectionsNotCheckableProxy(this);
228 checkableProxyModel->setSelectionModel(mCollectionSelectionModel);
229 checkableProxyModel->setSourceModel(mCollectionTree);
231 mCollectionView->setModel(checkableProxyModel);
232 mCollectionView->setXmlGuiClient(guiClient);
233 mCollectionView->header()->setDefaultAlignment(Qt::AlignCenter);
234 mCollectionView->header()->setSortIndicatorShown(false);
236 connect(mCollectionView->model(), &QAbstractItemModel::rowsInserted,
237 this, &MainWidget::slotCheckNewCalendar);
239 connect(mCollectionView, SIGNAL(currentChanged(Akonadi::Collection)),
240 this, SLOT(slotCurrentCollectionChanged(Akonadi::Collection)));
242 KSelectionProxyModel *selectionProxyModel =
243 new KSelectionProxyModel(mCollectionSelectionModel, this);
244 selectionProxyModel->setSourceModel(GlobalContactModel::instance()->model());
245 selectionProxyModel->setFilterBehavior(KSelectionProxyModel::ChildrenOfExactSelection);
247 mItemTree = new Akonadi::EntityMimeTypeFilterModel(this);
248 mItemTree->setSourceModel(selectionProxyModel);
249 mItemTree->addMimeTypeExclusionFilter(Akonadi::Collection::mimeType());
250 mItemTree->setHeaderGroup(Akonadi::EntityTreeModel::ItemListHeaders);
252 mCategoryFilterModel = new CategoryFilterProxyModel(this);
253 mCategoryFilterModel->setSourceModel(mItemTree);
254 mCategoryFilterModel->setFilterCategories(mCategorySelectWidget->filterTags());
255 mCategoryFilterModel->setFilterEnabled(true);
257 connect(mCategorySelectWidget, &CategorySelectWidget::filterChanged,
258 mCategoryFilterModel, &CategoryFilterProxyModel::setFilterCategories);
260 mContactsFilterModel = new Akonadi::ContactsFilterProxyModel(this);
261 mContactsFilterModel->setSourceModel(mCategoryFilterModel);
263 connect(mQuickSearchWidget, &QuickSearchWidget::filterStringChanged,
264 mContactsFilterModel, &Akonadi::ContactsFilterProxyModel::setFilterString);
265 connect(mQuickSearchWidget, &QuickSearchWidget::filterStringChanged,
266 this, &MainWidget::selectFirstItem);
267 connect(mQuickSearchWidget, SIGNAL(arrowDownKeyPressed()),
268 mItemView, SLOT(setFocus()));
270 mItemView->setModel(mContactsFilterModel);
271 mItemView->setXmlGuiClient(guiClient);
272 mItemView->setSelectionMode(QAbstractItemView::ExtendedSelection);
273 mItemView->setRootIsDecorated(false);
274 mItemView->header()->setDefaultAlignment(Qt::AlignCenter);
276 mXXPortManager->setSelectionModel(mItemView->selectionModel());
278 mActionManager = new Akonadi::StandardContactActionManager(guiClient->actionCollection(), this);
279 mActionManager->setCollectionSelectionModel(mCollectionView->selectionModel());
280 mActionManager->setItemSelectionModel(mItemView->selectionModel());
282 QList<Akonadi::StandardActionManager::Type> standardActions;
283 standardActions << Akonadi::StandardActionManager::CreateCollection
284 << Akonadi::StandardActionManager::DeleteCollections
285 << Akonadi::StandardActionManager::SynchronizeCollections
286 << Akonadi::StandardActionManager::CollectionProperties
287 << Akonadi::StandardActionManager::CopyItems
288 << Akonadi::StandardActionManager::Paste
289 << Akonadi::StandardActionManager::DeleteItems
290 << Akonadi::StandardActionManager::CutItems
291 << Akonadi::StandardActionManager::CreateResource
292 << Akonadi::StandardActionManager::DeleteResources
293 << Akonadi::StandardActionManager::ResourceProperties
294 << Akonadi::StandardActionManager::SynchronizeResources
295 << Akonadi::StandardActionManager::SynchronizeCollectionsRecursive
296 << Akonadi::StandardActionManager::MoveItemToMenu
297 << Akonadi::StandardActionManager::CopyItemToMenu;
299 Q_FOREACH (Akonadi::StandardActionManager::Type standardAction, standardActions) {
300 mActionManager->createAction(standardAction);
302 guiClient->actionCollection()->setDefaultShortcut(mActionManager->action(Akonadi::StandardActionManager::DeleteItems), QKeySequence(Qt::Key_Delete));
303 QList<Akonadi::StandardContactActionManager::Type> contactActions;
304 contactActions << Akonadi::StandardContactActionManager::CreateContact
305 << Akonadi::StandardContactActionManager::CreateContactGroup
306 << Akonadi::StandardContactActionManager::EditItem;
308 Q_FOREACH (Akonadi::StandardContactActionManager::Type contactAction, contactActions) {
309 mActionManager->createAction(contactAction);
311 static bool pageRegistered = false;
313 if (!pageRegistered) {
314 Akonadi::CollectionPropertiesDialog::registerPage(new PimCommon::CollectionAclPageFactory);
315 pageRegistered = true;
318 const QStringList pages =
319 QStringList() << QStringLiteral("Akonadi::CollectionGeneralPropertiesPage")
320 << QStringLiteral("Akonadi::CachePolicyPage")
321 << QStringLiteral("PimCommon::CollectionAclPage");
323 mActionManager->setCollectionPropertiesPageNames(pages);
325 connect(mItemView, SIGNAL(currentChanged(Akonadi::Item)),
326 this, SLOT(itemSelected(Akonadi::Item)));
327 connect(mItemView, SIGNAL(doubleClicked(Akonadi::Item)),
328 mActionManager->action(Akonadi::StandardContactActionManager::EditItem),
329 SLOT(trigger()));
330 connect(mItemView->selectionModel(), &QItemSelectionModel::currentChanged,
331 this, &MainWidget::itemSelectionChanged);
332 connect(mItemView->selectionModel(), &QItemSelectionModel::selectionChanged,
333 this, &MainWidget::slotSelectionChanged);
335 // show the contact details view as default
336 mDetailsViewStack->setCurrentWidget(mContactDetails);
338 mContactSwitcher->setView(mItemView);
340 Akonadi::ControlGui::widgetNeedsAkonadi(this);
342 mModelColumnManager = new ModelColumnManager(GlobalContactModel::instance()->model(), this);
343 mModelColumnManager->setWidget(mItemView->header());
344 mModelColumnManager->load();
346 QMetaObject::invokeMethod(this, "delayedInit", Qt::QueuedConnection);
347 updateQuickSearchText();
348 slotSelectionChanged();
351 void MainWidget::configure()
353 KCMultiDialog dlg(this);
354 dlg.addModule(QStringLiteral("akonadicontact_actions.desktop"));
355 dlg.addModule(QStringLiteral("kcmldap.desktop"));
357 dlg.exec();
360 void MainWidget::handleCommandLine(const QStringList &arguments)
362 QCommandLineParser parser;
363 kaddressbook_options(&parser);
364 parser.process(arguments);
366 if (parser.isSet(QStringLiteral("import"))) {
367 for (const QString &url : parser.positionalArguments()) {
368 importManager()->importFile(QUrl::fromUserInput(url));
373 XXPortManager *MainWidget::importManager() const
375 return mXXPortManager;
378 void MainWidget::updateQuickSearchText()
380 mQuickSearchWidget->updateQuickSearchText(i18nc("@label Search contacts in list", "Search...<%1>", mQuickSearchAction->shortcut().toString()));
383 void MainWidget::delayedInit()
385 setViewMode(0); // get default from settings
387 const KConfigGroup group(Settings::self()->config(), "UiState_ContactView");
388 KPIM::UiStateSaver::restoreState(mItemView, group);
390 #if defined(HAVE_PRISON)
391 mXmlGuiClient->
392 actionCollection()->
393 action(QStringLiteral("options_show_qrcodes"))->setChecked(showQRCodes());
394 #endif
396 connect(GlobalContactModel::instance()->model(), &QAbstractItemModel::modelAboutToBeReset,
397 this, &MainWidget::saveState);
398 connect(GlobalContactModel::instance()->model(), &QAbstractItemModel::modelReset,
399 this, &MainWidget::restoreState);
400 connect(qApp, &QApplication::aboutToQuit, this, &MainWidget::saveState);
402 restoreState();
403 updateQuickSearchText();
406 MainWidget::~MainWidget()
408 mModelColumnManager->store();
409 saveSplitterStates();
411 KConfigGroup group(Settings::self()->config(), "UiState_ContactView");
412 KPIM::UiStateSaver::saveState(mItemView, group);
414 saveState();
415 delete mGrantleeThemeManager;
417 Settings::self()->save();
420 void MainWidget::restoreState()
422 // collection view
424 Akonadi::ETMViewStateSaver *saver = new Akonadi::ETMViewStateSaver;
425 saver->setView(mCollectionView);
427 const KConfigGroup group(Settings::self()->config(), "CollectionViewState");
428 saver->restoreState(group);
431 // collection view
433 Akonadi::ETMViewStateSaver *saver = new Akonadi::ETMViewStateSaver;
434 saver->setSelectionModel(mCollectionSelectionModel);
436 const KConfigGroup group(Settings::self()->config(), "CollectionViewCheckState");
437 saver->restoreState(group);
440 // item view
442 Akonadi::ETMViewStateSaver *saver = new Akonadi::ETMViewStateSaver;
443 saver->setView(mItemView);
444 saver->setSelectionModel(mItemView->selectionModel());
446 const KConfigGroup group(Settings::self()->config(), "ItemViewState");
447 saver->restoreState(group);
451 void MainWidget::saveState()
453 // collection view
455 Akonadi::ETMViewStateSaver saver;
456 saver.setView(mCollectionView);
458 KConfigGroup group(Settings::self()->config(), "CollectionViewState");
459 saver.saveState(group);
460 group.sync();
463 // collection view
465 Akonadi::ETMViewStateSaver saver;
466 saver.setSelectionModel(mCollectionSelectionModel);
468 KConfigGroup group(Settings::self()->config(), "CollectionViewCheckState");
469 saver.saveState(group);
470 group.sync();
473 // item view
475 Akonadi::ETMViewStateSaver saver;
476 saver.setView(mItemView);
477 saver.setSelectionModel(mItemView->selectionModel());
479 KConfigGroup group(Settings::self()->config(), "ItemViewState");
480 saver.saveState(group);
481 group.sync();
485 void MainWidget::setupGui()
487 // the horizontal main layout
488 QHBoxLayout *layout = new QHBoxLayout(this);
489 layout->setMargin(0);
491 // Splitter 1 contains the two main parts of the GUI:
492 // - collection and item view splitter 2 on the left (see below)
493 // - details pane on the right, that contains
494 // - details view stack on the top
495 // - contact switcher at the bottom
496 mMainWidgetSplitter1 = new QSplitter(Qt::Horizontal);
497 mMainWidgetSplitter1->setObjectName(QStringLiteral("MainWidgetSplitter1"));
498 layout->addWidget(mMainWidgetSplitter1);
500 // Splitter 2 contains the remaining parts of the GUI:
501 // - collection view on either the left or the top
502 // - item view on either the right or the bottom
503 // The orientation of this splitter is changed for either
504 // a three or two column view; in simple mode it is hidden.
505 mMainWidgetSplitter2 = new QSplitter(Qt::Vertical);
506 mMainWidgetSplitter2->setObjectName(QStringLiteral("MainWidgetSplitter2"));
507 mMainWidgetSplitter1->addWidget(mMainWidgetSplitter2);
509 // the collection view
510 mCollectionView = new Akonadi::EntityTreeView();
511 mMainWidgetSplitter2->addWidget(mCollectionView);
513 // the items view
514 mItemView = new Akonadi::EntityTreeView();
515 mItemView->setObjectName(QStringLiteral("ContactView"));
516 mItemView->setDefaultPopupMenu(QStringLiteral("akonadi_itemview_contextmenu"));
517 mItemView->setAlternatingRowColors(true);
518 mMainWidgetSplitter2->addWidget(mItemView);
520 // the details pane that contains the details view stack and contact switcher
521 mDetailsPane = new QWidget;
522 mMainWidgetSplitter1->addWidget(mDetailsPane);
524 mMainWidgetSplitter1->setStretchFactor(1, 9); // maximum width for detail
525 mMainWidgetSplitter2->setStretchFactor(1, 9); // for intuitive resizing
526 mMainWidgetSplitter2->setChildrenCollapsible(false);
527 mMainWidgetSplitter1->setChildrenCollapsible(false);
529 QVBoxLayout *detailsPaneLayout = new QVBoxLayout(mDetailsPane);
530 detailsPaneLayout->setMargin(0);
532 // the details view stack
533 mDetailsViewStack = new QStackedWidget();
534 detailsPaneLayout->addWidget(mDetailsViewStack);
536 // the details widget for contacts
537 mContactDetails = new Akonadi::ContactViewer(mDetailsViewStack);
538 mDetailsViewStack->addWidget(mContactDetails);
540 // the details widget for contact groups
541 mContactGroupDetails = new Akonadi::ContactGroupViewer(mDetailsViewStack);
542 mDetailsViewStack->addWidget(mContactGroupDetails);
544 // the details widget for empty items
545 mEmptyDetails = new QTextBrowser(mDetailsViewStack);
546 mDetailsViewStack->addWidget(mEmptyDetails);
548 // the contact switcher for the simple gui mode
549 mContactSwitcher = new ContactSwitcher;
550 detailsPaneLayout->addWidget(mContactSwitcher);
551 mContactSwitcher->setVisible(false);
553 // the quick search widget which is embedded in the toolbar action
554 mQuickSearchWidget = new QuickSearchWidget;
555 mQuickSearchWidget->setMaximumWidth(500);
557 // the category filter widget which is embedded in the toolbar action
558 mCategorySelectWidget = new CategorySelectWidget;
560 // setup the default actions
561 Akonadi::ContactDefaultActions *actions = new Akonadi::ContactDefaultActions(this);
562 actions->connectToView(mContactDetails);
563 actions->connectToView(mContactGroupDetails);
564 mFormatter = new KAddressBookGrantlee::GrantleeContactFormatter;
566 mContactDetails->setContactFormatter(mFormatter);
568 mGroupFormatter = new KAddressBookGrantlee::GrantleeContactGroupFormatter;
570 mContactGroupDetails->setContactGroupFormatter(mGroupFormatter);
573 void MainWidget::setupActions(KActionCollection *collection)
575 mGrantleeThemeManager = new GrantleeTheme::ThemeManager(QStringLiteral("addressbook"),
576 QStringLiteral("theme.desktop"),
577 collection,
578 QStringLiteral("kaddressbook/viewertemplates/"));
579 mGrantleeThemeManager->setDownloadNewStuffConfigFile(QStringLiteral("kaddressbook_themes.knsrc"));
580 connect(mGrantleeThemeManager, &GrantleeTheme::ThemeManager::grantleeThemeSelected, this, &MainWidget::slotGrantleeThemeSelected);
581 connect(mGrantleeThemeManager, &GrantleeTheme::ThemeManager::updateThemes, this, &MainWidget::slotGrantleeThemesUpdated);
583 KActionMenu *themeMenu = new KActionMenu(i18n("&Themes"), this);
584 collection->addAction(QStringLiteral("theme_menu"), themeMenu);
586 initGrantleeThemeName();
587 QActionGroup *group = new QActionGroup(this);
588 mGrantleeThemeManager->setThemeMenu(themeMenu);
589 mGrantleeThemeManager->setActionGroup(group);
591 QAction *action = KStandardAction::print(this, SLOT(print()), collection);
592 action->setWhatsThis(
593 i18nc("@info:whatsthis",
594 "Print the complete address book or a selected number of contacts."));
596 if (KPrintPreview::isAvailable()) {
597 KStandardAction::printPreview(this, SLOT(printPreview()), collection);
600 QWidgetAction *quicksearch = new QWidgetAction(this);
601 quicksearch->setText(i18n("Quick search"));
602 quicksearch->setDefaultWidget(mQuickSearchWidget);
603 collection->addAction(QStringLiteral("quick_search"), quicksearch);
605 QWidgetAction *categoryFilter = new QWidgetAction(this);
606 categoryFilter->setText(i18n("Category filter"));
607 categoryFilter->setDefaultWidget(mCategorySelectWidget);
608 collection->addAction(QStringLiteral("category_filter"), categoryFilter);
610 action = collection->addAction(QStringLiteral("select_all"));
611 action->setText(i18n("Select All"));
612 collection->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_A));
613 action->setWhatsThis(i18n("Select all contacts in the current address book view."));
614 connect(action, &QAction::triggered, mItemView, &Akonadi::EntityTreeView::selectAll);
616 #if defined(HAVE_PRISON)
617 KToggleAction *qrtoggleAction;
618 qrtoggleAction = collection->add<KToggleAction>(QStringLiteral("options_show_qrcodes"));
619 qrtoggleAction->setText(i18n("Show QR Codes"));
620 qrtoggleAction->setWhatsThis(i18n("Show QR Codes in the contact."));
621 connect(qrtoggleAction, &KToggleAction::toggled, this, &MainWidget::setQRCodeShow);
622 #endif
624 mViewModeGroup = new QActionGroup(this);
626 QAction *act = new QAction(i18nc("@action:inmenu", "Simple (one column)"), mViewModeGroup);
627 act->setCheckable(true);
628 act->setData(1);
629 collection->setDefaultShortcut(act, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_1));
630 act->setWhatsThis(i18n("Show a simple mode of the address book view."));
631 collection->addAction(QStringLiteral("view_mode_simple"), act);
633 act = new QAction(i18nc("@action:inmenu", "Two Columns"), mViewModeGroup);
634 act->setCheckable(true);
635 act->setData(2);
636 collection->addAction(QStringLiteral("view_mode_2columns"), act);
637 collection->setDefaultShortcut(act, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_2));
639 act = new QAction(i18nc("@action:inmenu", "Three Columns"), mViewModeGroup);
640 act->setCheckable(true);
641 act->setData(3);
642 collection->setDefaultShortcut(act, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_3));
643 collection->addAction(QStringLiteral("view_mode_3columns"), act);
645 connect(mViewModeGroup, SIGNAL(triggered(QAction*)), SLOT(setViewMode(QAction*)));
647 // import actions
648 action = collection->addAction(QStringLiteral("file_import_vcard"));
649 action->setText(i18n("Import vCard..."));
650 action->setWhatsThis(i18n("Import contacts from a vCard file."));
651 mXXPortManager->addImportAction(action, QStringLiteral("vcard30"));
653 action = collection->addAction(QStringLiteral("file_import_csv"));
654 action->setText(i18n("Import CSV file..."));
655 action->setWhatsThis(i18n("Import contacts from a file in comma separated value format."));
656 mXXPortManager->addImportAction(action, QStringLiteral("csv"));
658 action = collection->addAction(QStringLiteral("file_import_ldif"));
659 action->setText(i18n("Import LDIF file..."));
660 action->setWhatsThis(i18n("Import contacts from an LDIF file."));
661 mXXPortManager->addImportAction(action, QStringLiteral("ldif"));
663 action = collection->addAction(QStringLiteral("file_import_ldap"));
664 action->setText(i18n("Import From LDAP server..."));
665 action->setWhatsThis(i18n("Import contacts from an LDAP server."));
666 mXXPortManager->addImportAction(action, QStringLiteral("ldap"));
668 action = collection->addAction(QStringLiteral("file_import_gmx"));
669 action->setText(i18n("Import GMX file..."));
670 action->setWhatsThis(i18n("Import contacts from a GMX address book file."));
671 mXXPortManager->addImportAction(action, QStringLiteral("gmx"));
673 // export actions
674 action = collection->addAction(QStringLiteral("file_export_vcard40"));
675 action->setText(i18n("Export vCard 4.0..."));
676 action->setWhatsThis(i18n("Export contacts to a vCard 4.0 file."));
677 mXXPortManager->addExportAction(action, QStringLiteral("vcard40"));
679 action = collection->addAction(QStringLiteral("file_export_vcard30"));
680 action->setText(i18n("Export vCard 3.0..."));
681 action->setWhatsThis(i18n("Export contacts to a vCard 3.0 file."));
682 mXXPortManager->addExportAction(action, QStringLiteral("vcard30"));
684 action = collection->addAction(QStringLiteral("file_export_vcard21"));
685 action->setText(i18n("Export vCard 2.1..."));
686 action->setWhatsThis(i18n("Export contacts to a vCard 2.1 file."));
687 mXXPortManager->addExportAction(action, QStringLiteral("vcard21"));
689 action = collection->addAction(QStringLiteral("file_export_csv"));
690 action->setText(i18n("Export CSV file..."));
691 action->setWhatsThis(i18n("Export contacts to a file in comma separated value format."));
692 mXXPortManager->addExportAction(action, QStringLiteral("csv"));
694 action = collection->addAction(QStringLiteral("file_export_ldif"));
695 action->setText(i18n("Export LDIF file..."));
696 action->setWhatsThis(i18n("Export contacts to an LDIF file."));
697 mXXPortManager->addExportAction(action, QStringLiteral("ldif"));
699 action = collection->addAction(QStringLiteral("file_export_gmx"));
700 action->setText(i18n("Export GMX file..."));
701 action->setWhatsThis(i18n("Export contacts to a GMX address book file."));
702 mXXPortManager->addExportAction(action, QStringLiteral("gmx"));
704 KToggleAction *actTheme = mGrantleeThemeManager->actionForTheme();
705 if (actTheme) {
706 actTheme->setChecked(true);
709 action = collection->addAction(QStringLiteral("merge_contacts"));
710 action->setText(i18n("Merge Contacts..."));
711 connect(action, &QAction::triggered, this, &MainWidget::mergeContacts);
713 action = collection->addAction(QStringLiteral("search_duplicate_contacts"));
714 action->setText(i18n("Search Duplicate Contacts..."));
715 connect(action, &QAction::triggered, this, &MainWidget::slotSearchDuplicateContacts);
717 mQuickSearchAction = new QAction(i18n("Set Focus to Quick Search"), this);
718 //If change shortcut change in quicksearchwidget->lineedit->setPlaceholderText
719 collection->addAction(QStringLiteral("focus_to_quickseach"), mQuickSearchAction);
720 connect(mQuickSearchAction, &QAction::triggered, mQuickSearchWidget, &QuickSearchWidget::slotFocusQuickSearch);
721 collection->setDefaultShortcut(mQuickSearchAction, QKeySequence(Qt::ALT + Qt::Key_Q));
723 mSendEmailAction = collection->addAction(QStringLiteral("send_mail"));
724 mSendEmailAction->setText(i18n("Send an email..."));
725 mSendEmailAction->setIcon(KIconLoader::global()->loadIcon(QStringLiteral("mail-message-new"), KIconLoader::Small));
726 connect(mSendEmailAction, &QAction::triggered, this, &MainWidget::slotSendMail);
728 mSendVcardAction = collection->addAction(QStringLiteral("send_vcards"));
729 mSendVcardAction->setText(i18n("Send vCards..."));
730 mSendVcardAction->setIcon(KIconLoader::global()->loadIcon(QStringLiteral("mail-message-new"), KIconLoader::Small));
731 connect(mSendVcardAction, &QAction::triggered, this, &MainWidget::slotSendVcards);
733 if (!qEnvironmentVariableIsEmpty("KDEPIM_BALOO_DEBUG")) {
734 action = collection->addAction(QStringLiteral("debug_baloo"));
735 //Don't translate it. It's just for debug
736 action->setText(QStringLiteral("Debug baloo..."));
737 connect(action, &QAction::triggered, this, &MainWidget::slotDebugBaloo);
740 mServerSideSubscription = new QAction(QIcon::fromTheme(QStringLiteral("folder-bookmarks")), i18n("Serverside Subscription..."), this);
741 collection->addAction(QStringLiteral("serverside_subscription"), mServerSideSubscription);
742 connect(mServerSideSubscription, &QAction::triggered, this, &MainWidget::slotServerSideSubscription);
744 mSearchGravatarAction = collection->addAction(QStringLiteral("search_gravatar"));
745 mSearchGravatarAction->setText(i18n("Check Gravatar..."));
746 connect(mSearchGravatarAction, &QAction::triggered, this, &MainWidget::slotCheckGravatar);
749 void MainWidget::printPreview()
751 QPrinter printer;
752 printer.setDocName(i18n("Address Book"));
753 printer.setOutputFileName(Settings::self()->defaultFileName());
754 printer.setOutputFormat(QPrinter::PdfFormat);
755 printer.setCollateCopies(true);
757 KPrintPreview previewdlg(&printer, this);
758 KABPrinting::PrintingWizard wizard(&printer, mItemView->selectionModel(), this);
759 wizard.setDefaultAddressBook(currentAddressBook());
761 const int result = wizard.exec();
762 if (result) {
763 Settings::self()->setDefaultFileName(printer.outputFileName());
764 Settings::self()->setPrintingStyle(wizard.printingStyle());
765 Settings::self()->setSortOrder(wizard.sortOrder());
766 previewdlg.exec();
770 void MainWidget::print()
772 QPrinter printer;
773 printer.setDocName(i18n("Address Book"));
774 printer.setOutputFileName(Settings::self()->defaultFileName());
775 printer.setCollateCopies(true);
777 QPointer<QPrintDialog> printDialog = new QPrintDialog(&printer, this);
779 printDialog->setWindowTitle(i18n("Print Contacts"));
780 if (!printDialog->exec() || !printDialog) {
781 delete printDialog;
782 return;
784 KABPrinting::PrintingWizard wizard(&printer, mItemView->selectionModel(), this);
785 wizard.setDefaultAddressBook(currentAddressBook());
787 wizard.exec(); //krazy:exclude=crashy
789 Settings::self()->setDefaultFileName(printer.outputFileName());
790 Settings::self()->setPrintingStyle(wizard.printingStyle());
791 Settings::self()->setSortOrder(wizard.sortOrder());
794 void MainWidget::newContact()
796 mActionManager->action(Akonadi::StandardContactActionManager::CreateContact)->trigger();
799 void MainWidget::newGroup()
801 mActionManager->action(Akonadi::StandardContactActionManager::CreateContactGroup)->trigger();
805 * Depending on the mime type of the selected item, this method
806 * brings up the right view on the detail view stack and sets the
807 * selected item on it.
809 void MainWidget::itemSelected(const Akonadi::Item &item)
811 if (Akonadi::MimeTypeChecker::isWantedItem(item, KContacts::Addressee::mimeType())) {
812 mDetailsViewStack->setCurrentWidget(mContactDetails);
813 mContactDetails->setContact(item);
814 } else if (Akonadi::MimeTypeChecker::isWantedItem(item, KContacts::ContactGroup::mimeType())) {
815 mDetailsViewStack->setCurrentWidget(mContactGroupDetails);
816 mContactGroupDetails->setContactGroup(item);
821 * Catch when the selection has gone ( e.g. an empty address book has been selected )
822 * clear the details view in this case.
824 void MainWidget::itemSelectionChanged(const QModelIndex &current, const QModelIndex &)
826 if (!current.isValid()) {
827 mDetailsViewStack->setCurrentWidget(mEmptyDetails);
831 void MainWidget::selectFirstItem()
833 // Whenever the quick search has changed, we select the first item
834 // in the item view, so that the detailsview is updated
835 if (mItemView && mItemView->selectionModel()) {
836 mItemView->selectionModel()->setCurrentIndex(mItemView->model()->index(0, 0),
837 QItemSelectionModel::Rows |
838 QItemSelectionModel::ClearAndSelect);
842 bool MainWidget::showQRCodes()
844 #if defined(HAVE_PRISON)
845 KConfig config(QStringLiteral("akonadi_contactrc"));
846 KConfigGroup group(&config, QStringLiteral("View"));
847 return group.readEntry("QRCodes", true);
848 #else
849 return true;
850 #endif
853 void MainWidget::setQRCodeShow(bool on)
855 #if defined(HAVE_PRISON)
856 // must write the configuration setting first before updating the view.
857 KConfig config(QStringLiteral("akonadi_contactrc"));
858 KConfigGroup group(&config, QStringLiteral("View"));
859 group.writeEntry("QRCodes", on);
860 group.sync();
861 if (mDetailsViewStack->currentWidget() == mContactDetails) {
862 mContactDetails->updateView();
864 #else
865 Q_UNUSED(on);
866 #endif
869 Akonadi::Collection MainWidget::currentAddressBook() const
871 if (mCollectionView->selectionModel() && mCollectionView->selectionModel()->hasSelection()) {
872 const QModelIndex index = mCollectionView->selectionModel()->selectedIndexes().first();
873 const Akonadi::Collection collection =
874 index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
876 return collection;
879 return Akonadi::Collection();
882 QAbstractItemModel *MainWidget::allContactsModel()
884 if (!mAllContactsModel) {
885 KDescendantsProxyModel *descendantsModel = new KDescendantsProxyModel(this);
886 descendantsModel->setSourceModel(GlobalContactModel::instance()->model());
888 mAllContactsModel = new Akonadi::EntityMimeTypeFilterModel(this);
889 mAllContactsModel->setSourceModel(descendantsModel);
890 mAllContactsModel->addMimeTypeExclusionFilter(Akonadi::Collection::mimeType());
891 mAllContactsModel->setHeaderGroup(Akonadi::EntityTreeModel::ItemListHeaders);
894 return mAllContactsModel;
897 void MainWidget::setViewMode(QAction *action)
899 setViewMode(action->data().toInt());
902 void MainWidget::setViewMode(int mode)
904 int currentMode = Settings::self()->viewMode();
905 //qCDebug(KADDRESSBOOK_LOG) << "cur" << currentMode << "new" << mode;
906 if (mode == currentMode) {
907 return; // nothing to do
910 if (mode == 0) {
911 mode = currentMode;// initialisation, no save
912 } else {
913 saveSplitterStates(); // for 2- or 3-column mode
915 if (mode == 1) { // simple mode
916 mMainWidgetSplitter2->setVisible(false);
917 mDetailsPane->setVisible(true);
918 mContactSwitcher->setVisible(true);
919 } else {
920 mMainWidgetSplitter2->setVisible(true);
921 mContactSwitcher->setVisible(false);
923 if (mode == 2) { // 2 columns
924 mMainWidgetSplitter2->setOrientation(Qt::Vertical);
925 } else if (mode == 3) { // 3 columns
926 mMainWidgetSplitter2->setOrientation(Qt::Horizontal);
930 Settings::self()->setViewMode(mode); // save new mode in settings
931 restoreSplitterStates(); // restore state for new mode
932 mViewModeGroup->actions().at(mode - 1)->setChecked(true);
934 if (mItemView->model()) {
935 mItemView->setCurrentIndex(mItemView->model()->index(0, 0));
939 void MainWidget::saveSplitterStates() const
941 // The splitter states are saved separately for each column view mode,
942 // but only if not in simple mode (1 column).
943 int currentMode = Settings::self()->viewMode();
944 if (currentMode == 1) {
945 return;
948 const QString groupName = QStringLiteral("UiState_MainWidgetSplitter_%1").arg(currentMode);
949 //qCDebug(KADDRESSBOOK_LOG) << "saving to group" << groupName;
950 KConfigGroup group(Settings::self()->config(), groupName);
951 KPIM::UiStateSaver::saveState(mMainWidgetSplitter1, group);
952 KPIM::UiStateSaver::saveState(mMainWidgetSplitter2, group);
955 void MainWidget::restoreSplitterStates()
957 // The splitter states are restored as appropriate for the current
958 // column view mode, but not for simple mode (1 column).
959 int currentMode = Settings::self()->viewMode();
960 if (currentMode == 1) {
961 return;
964 const QString groupName = QStringLiteral("UiState_MainWidgetSplitter_%1").arg(currentMode);
965 //qCDebug(KADDRESSBOOK_LOG) << "restoring from group" << groupName;
966 KConfigGroup group(Settings::self()->config(), groupName);
967 KPIM::UiStateSaver::restoreState(mMainWidgetSplitter1, group);
968 KPIM::UiStateSaver::restoreState(mMainWidgetSplitter2, group);
971 void MainWidget::initGrantleeThemeName()
973 QString themeName = mGrantleeThemeManager->configuredThemeName();
974 if (themeName.isEmpty()) {
975 themeName = QStringLiteral("default");
977 mFormatter->setGrantleeTheme(mGrantleeThemeManager->theme(themeName));
978 mGroupFormatter->setGrantleeTheme(mGrantleeThemeManager->theme(themeName));
981 void MainWidget::slotGrantleeThemeSelected()
983 initGrantleeThemeName();
984 if (mItemView->model()) {
985 mItemView->setCurrentIndex(mItemView->model()->index(0, 0));
989 void MainWidget::slotGrantleeThemesUpdated()
991 if (mItemView->model()) {
992 mItemView->setCurrentIndex(mItemView->model()->index(0, 0));
996 void MainWidget::mergeContacts()
998 const Akonadi::Item::List lst = Utils::collectSelectedContactsItem(mItemView->selectionModel());
999 QPointer<KABMergeContacts::MergeContactsDialog> dlg = new KABMergeContacts::MergeContactsDialog(this);
1000 dlg->setContacts(lst);
1001 dlg->exec();
1002 delete dlg;
1005 void MainWidget::slotSearchDuplicateContacts()
1007 const Akonadi::Item::List lst = Utils::collectSelectedContactsItem(mItemView->selectionModel());
1008 QPointer<KABMergeContacts::SearchAndMergeContactDuplicateContactDialog> dlg = new KABMergeContacts::SearchAndMergeContactDuplicateContactDialog(this);
1009 dlg->searchPotentialDuplicateContacts(lst);
1010 dlg->exec();
1011 delete dlg;
1014 Akonadi::EntityTreeModel *MainWidget::entityTreeModel() const
1016 QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel *>(mCollectionView->model());
1017 while (proxy) {
1018 Akonadi::EntityTreeModel *etm = qobject_cast<Akonadi::EntityTreeModel *>(proxy->sourceModel());
1019 if (etm) {
1020 return etm;
1022 proxy = qobject_cast<QAbstractProxyModel *>(proxy->sourceModel());
1025 qCWarning(KADDRESSBOOK_LOG) << "Couldn't find EntityTreeModel";
1026 return Q_NULLPTR;
1029 void MainWidget::slotCheckNewCalendar(const QModelIndex &parent, int begin, int end)
1031 // HACK: Check newly created calendars
1033 if ( begin < end ) {
1034 return;
1037 Akonadi::EntityTreeModel *etm = entityTreeModel();
1038 QAbstractItemModel *model = mCollectionView->model();
1039 if (etm && etm->isCollectionTreeFetched()) {
1040 for (int row = begin; row <= end; ++row) {
1041 QModelIndex index = model->index( row, 0, parent );
1042 if ( index.isValid() ) {
1043 model->setData( index, Qt::Checked, Qt::CheckStateRole );
1044 slotCheckNewCalendar( index, 0, model->rowCount(index) - 1 );
1047 if (parent.isValid()) {
1048 mCollectionView->setExpanded(parent, true);
1053 void MainWidget::slotSendVcards()
1055 const Akonadi::Item::List lst = collectSelectedAllContactsItem(mItemView->selectionModel());
1056 if (!lst.isEmpty()) {
1057 KABSendVCards::SendVcardsJob *sendVcards = new KABSendVCards::SendVcardsJob(lst, this);
1058 connect(sendVcards, &KABSendVCards::SendVcardsJob::sendVCardsError, this, &MainWidget::slotSendVcardsError);
1059 sendVcards->start();
1063 void MainWidget::slotSendVcardsError(const QString &error)
1065 KMessageBox::error(this, error);
1068 void MainWidget::slotSendMail()
1070 const Akonadi::Item::List lst = collectSelectedAllContactsItem(mItemView->selectionModel());
1071 if (!lst.isEmpty()) {
1072 KABMailSender::MailSenderJob *mailSender = new KABMailSender::MailSenderJob(lst, this);
1073 connect(mailSender, &KABMailSender::MailSenderJob::sendMails, this, &MainWidget::slotSendMails);
1074 connect(mailSender, &KABMailSender::MailSenderJob::sendMailsError, this, &MainWidget::slotSendMailsError);
1075 mailSender->start();
1076 } else {
1077 KMessageBox::sorry(this, i18n("You have not selected any contacts."));
1081 void MainWidget::slotSendMailsError(const QString &error)
1083 KMessageBox::error(this, error);
1086 void MainWidget::slotSendMails(const QStringList &emails)
1088 if (!emails.isEmpty()) {
1089 QUrl url;
1090 url.setScheme(QStringLiteral("mailto"));
1091 url.setPath(emails.join(QStringLiteral(";")));
1092 QDesktopServices::openUrl(url);
1096 void MainWidget::slotDebugBaloo()
1098 const Akonadi::Item::List lst = collectSelectedAllContactsItem(mItemView->selectionModel());
1099 if (lst.isEmpty()) {
1100 return;
1102 QPointer<Akonadi::Search::AkonadiSearchDebugDialog> dlg = new Akonadi::Search::AkonadiSearchDebugDialog;
1103 dlg->setAkonadiId(lst.at(0).id());
1104 dlg->setAttribute(Qt::WA_DeleteOnClose);
1105 dlg->setSearchType(Akonadi::Search::AkonadiSearchDebugSearchPathComboBox::Contacts);
1106 dlg->doSearch();
1107 dlg->show();
1110 const Akonadi::Item::List MainWidget::collectSelectedAllContactsItem(QItemSelectionModel *model)
1112 Akonadi::Item::List lst;
1114 const QModelIndexList indexes = model->selectedRows(0);
1115 for (int i = 0; i < indexes.count(); ++i) {
1116 const QModelIndex index = indexes.at(i);
1117 if (index.isValid()) {
1118 const Akonadi::Item item =
1119 index.data(Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
1120 if (item.isValid()) {
1121 if (item.hasPayload<KContacts::Addressee>() || item.hasPayload<KContacts::ContactGroup>()) {
1122 lst.append(item);
1127 return lst;
1130 void MainWidget::slotServerSideSubscription()
1132 Akonadi::Collection collection = currentAddressBook();
1133 if (collection.isValid()) {
1134 PimCommon::ManageServerSideSubscriptionJob *job = new PimCommon::ManageServerSideSubscriptionJob(this);
1135 job->setCurrentCollection(collection);
1136 job->setParentWidget(this);
1137 job->start();
1141 void MainWidget::slotCurrentCollectionChanged(const Akonadi::Collection &col)
1143 mXXPortManager->setDefaultAddressBook(col);
1144 bool isOnline;
1145 mServerSideSubscription->setEnabled(PimCommon::Util::isImapFolder(col, isOnline));
1148 void MainWidget::slotCheckGravatar()
1150 const Akonadi::Item::List lst = collectSelectedAllContactsItem(mItemView->selectionModel());
1151 if (lst.count() == 1) {
1152 Akonadi::Item item = lst.first();
1153 if (item.hasPayload<KContacts::Addressee>()) {
1154 KContacts::Addressee address = item.payload<KContacts::Addressee>();
1155 const QString email = address.preferredEmail();
1156 if (email.isEmpty()) {
1157 KMessageBox::error(this, i18n("No email found for this contact."));
1158 return;
1160 QPointer<KABGravatar::GravatarUpdateDialog> dlg = new KABGravatar::GravatarUpdateDialog(this);
1161 dlg->setEmail(email);
1162 if (!address.photo().isEmpty()) {
1163 if (address.photo().isIntern()) {
1164 const QPixmap pix = QPixmap::fromImage(address.photo().data());
1165 dlg->setOriginalPixmap(pix);
1166 } else {
1167 dlg->setOriginalUrl(address.photo().url());
1170 if (dlg->exec()) {
1171 KContacts::Picture picture = address.photo();
1172 bool needToSave = false;
1173 if (dlg->saveUrl()) {
1174 const QUrl url = dlg->resolvedUrl();
1175 if (!url.isEmpty()) {
1176 picture.setUrl(url.toString());
1177 needToSave = true;
1179 } else {
1180 const QPixmap pix = dlg->pixmap();
1181 if (!pix.isNull()) {
1182 picture.setData(pix.toImage());
1183 needToSave = true;
1186 if (needToSave) {
1187 address.setPhoto(picture);
1188 item.setPayload<KContacts::Addressee>(address);
1190 Akonadi::ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob(item, this);
1191 connect(modifyJob, &Akonadi::ItemModifyJob::result, this, &MainWidget::slotModifyContactFinished);
1194 delete dlg;
1195 } else {
1196 KMessageBox::information(this, i18n("A contact group was selected."));
1201 void MainWidget::slotModifyContactFinished(KJob *job)
1203 if (job->error()) {
1204 qCDebug(KADDRESSBOOK_LOG) << "Error while modifying items. " << job->error() << job->errorString();
1208 void MainWidget::slotSelectionChanged()
1210 bool hasUniqSelection = false;
1211 bool hasSelection = false;
1212 if (mItemView->selectionModel()->selection().count() == 1) {
1213 hasUniqSelection = (mItemView->selectionModel()->selection().at(0).height() == 1);
1214 hasSelection = true;
1216 mSendVcardAction->setEnabled(hasSelection);
1217 mSendEmailAction->setEnabled(hasSelection);
1218 mSearchGravatarAction->setEnabled(hasUniqSelection);