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"
30 #include "xxport/xxportmanager.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>
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"
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>
95 #include <QPrintDialog>
97 #include <QStackedWidget>
98 #include <QDBusConnection>
99 #include <QDesktopServices>
100 #include <ItemModifyJob>
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
)) {
117 class StructuralCollectionsNotCheckableProxy
: public KCheckableProxyModel
120 explicit StructuralCollectionsNotCheckableProxy(QObject
*parent
)
121 : KCheckableProxyModel(parent
)
125 QVariant
data(const QModelIndex
&index
, int role
) const Q_DECL_OVERRIDE
127 if (!index
.isValid()) {
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
)) {
139 return KCheckableProxyModel::data(index
, role
);
145 MainWidget::MainWidget(KXMLGUIClient
*guiClient
, 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
>();
164 setupActions(guiClient
->actionCollection());
167 * The item models, proxies and views have the following structure:
172 * mContactsFilterModel
175 * mCategorySelectWidget --> mCategoryFilterModel
181 * | mAllContactsModel
184 * mCollectionView selectionProxyModel descendantsModel
187 * | selectionModel | |
189 * proxyModel ---------' | |
192 * mCollectionTree | |
194 * | | _______________/
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
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
),
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"));
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)
393 action(QStringLiteral("options_show_qrcodes"))->setChecked(showQRCodes());
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
);
403 updateQuickSearchText();
406 MainWidget::~MainWidget()
408 mModelColumnManager
->store();
409 saveSplitterStates();
411 KConfigGroup
group(Settings::self()->config(), "UiState_ContactView");
412 KPIM::UiStateSaver::saveState(mItemView
, group
);
415 delete mGrantleeThemeManager
;
417 Settings::self()->save();
420 void MainWidget::restoreState()
424 Akonadi::ETMViewStateSaver
*saver
= new Akonadi::ETMViewStateSaver
;
425 saver
->setView(mCollectionView
);
427 const KConfigGroup
group(Settings::self()->config(), "CollectionViewState");
428 saver
->restoreState(group
);
433 Akonadi::ETMViewStateSaver
*saver
= new Akonadi::ETMViewStateSaver
;
434 saver
->setSelectionModel(mCollectionSelectionModel
);
436 const KConfigGroup
group(Settings::self()->config(), "CollectionViewCheckState");
437 saver
->restoreState(group
);
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()
455 Akonadi::ETMViewStateSaver saver
;
456 saver
.setView(mCollectionView
);
458 KConfigGroup
group(Settings::self()->config(), "CollectionViewState");
459 saver
.saveState(group
);
465 Akonadi::ETMViewStateSaver saver
;
466 saver
.setSelectionModel(mCollectionSelectionModel
);
468 KConfigGroup
group(Settings::self()->config(), "CollectionViewCheckState");
469 saver
.saveState(group
);
475 Akonadi::ETMViewStateSaver saver
;
476 saver
.setView(mItemView
);
477 saver
.setSelectionModel(mItemView
->selectionModel());
479 KConfigGroup
group(Settings::self()->config(), "ItemViewState");
480 saver
.saveState(group
);
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
);
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"),
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
);
624 mViewModeGroup
= new QActionGroup(this);
626 QAction
*act
= new QAction(i18nc("@action:inmenu", "Simple (one column)"), mViewModeGroup
);
627 act
->setCheckable(true);
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);
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);
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
*)));
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"));
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();
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()
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();
763 Settings::self()->setDefaultFileName(printer
.outputFileName());
764 Settings::self()->setPrintingStyle(wizard
.printingStyle());
765 Settings::self()->setSortOrder(wizard
.sortOrder());
770 void MainWidget::print()
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
) {
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
¤t
, 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);
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
);
861 if (mDetailsViewStack
->currentWidget() == mContactDetails
) {
862 mContactDetails
->updateView();
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
>();
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
911 mode
= currentMode
;// initialisation, no save
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);
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) {
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) {
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
);
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
);
1014 Akonadi::EntityTreeModel
*MainWidget::entityTreeModel() const
1016 QAbstractProxyModel
*proxy
= qobject_cast
<QAbstractProxyModel
*>(mCollectionView
->model());
1018 Akonadi::EntityTreeModel
*etm
= qobject_cast
<Akonadi::EntityTreeModel
*>(proxy
->sourceModel());
1022 proxy
= qobject_cast
<QAbstractProxyModel
*>(proxy
->sourceModel());
1025 qCWarning(KADDRESSBOOK_LOG
) << "Couldn't find EntityTreeModel";
1029 void MainWidget::slotCheckNewCalendar(const QModelIndex
&parent
, int begin
, int end
)
1031 // HACK: Check newly created calendars
1033 if ( begin
< end
) {
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();
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()) {
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()) {
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
);
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
>()) {
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);
1141 void MainWidget::slotCurrentCollectionChanged(const Akonadi::Collection
&col
)
1143 mXXPortManager
->setDefaultAddressBook(col
);
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."));
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
);
1167 dlg
->setOriginalUrl(address
.photo().url());
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());
1180 const QPixmap pix
= dlg
->pixmap();
1181 if (!pix
.isNull()) {
1182 picture
.setData(pix
.toImage());
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
);
1196 KMessageBox::information(this, i18n("A contact group was selected."));
1201 void MainWidget::slotModifyContactFinished(KJob
*job
)
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
);