Commit 5e9c3353 authored by Sandro Knauß's avatar Sandro Knauß
Browse files

Korganizer: Add quickview for person collections

KOLAB: #3054
(cherry picked from commit e252dae1d9ef3d07ebd3fee788142f3f6a85f89e)
parent 9c7b5f8a
......@@ -176,6 +176,7 @@ set(korganizerprivate_LIB_SRCS
views/collectionview/reparentingmodel.cpp
views/collectionview/controller.cpp
views/collectionview/calendardelegate.cpp
views/collectionview/quickview.cpp
calendarview.cpp
datechecker.cpp
datenavigator.cpp
......@@ -224,6 +225,7 @@ set(korganizerprivate_LIB_SRCS
publishdialog_base.ui
searchdialog_base.ui
timescaleedit_base.ui
views/collectionview/quickview.ui
)
qt4_add_resources(korganizerprivate_LIB_SRCS
......
......@@ -33,6 +33,8 @@
#include "koglobals.h"
#include "views/collectionview/reparentingmodel.h"
#include "views/collectionview/calendardelegate.h"
#include "views/collectionview/quickview.h"
#include <calendarsupport/kcalprefs.h>
#include <calendarsupport/utils.h>
......@@ -349,8 +351,8 @@ class CollectionFilter : public QSortFilterProxyModel
const Akonadi::Collection &col = sourceIndex.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
CollectionIdentificationAttribute *attr = col.attribute<CollectionIdentificationAttribute>();
//We filter the user folders because we insert person nodes for user folders.
if (attr && ((attr->collectionNamespace() == "usertoplevel") || (attr->collectionNamespace() == "usertoplevel"))
|| (col.name().contains(QLatin1String("Other Users")))) {
if ( (attr && attr->collectionNamespace().startsWith("user"))
|| col.name().contains(QLatin1String("Other Users"))) {
return false;
}
return true;
......@@ -1018,7 +1020,7 @@ void AkonadiCollectionView::onAction(const QModelIndex &index, int a)
const StyledCalendarDelegate::Action action = static_cast<StyledCalendarDelegate::Action>(a);
switch (action) {
case StyledCalendarDelegate::AddToList: {
const Akonadi::Collection col = index.data(CollectionRole).value<Akonadi::Collection>();
const Akonadi::Collection col = CalendarSupport::collectionFromIndex(index);
if (col.isValid()) {
mController->setCollectionState(col, Controller::Referenced);
} else {
......@@ -1053,6 +1055,25 @@ void AkonadiCollectionView::onAction(const QModelIndex &index, int a)
}
}
break;
case StyledCalendarDelegate::Quickview: {
QVariant person = index.data(PersonRole);
Akonadi::Collection col = CalendarSupport::collectionFromIndex(index);
QModelIndex i = index;
while (!person.isValid()) {
i = i.parent();
if (!i.isValid()) {
break;
}
person = i.data(PersonRole);
}
if (person.isValid()) {
Quickview *quickview = new Quickview(person.value<Person>(), col);
quickview->show();
} else {
kWarning() << "No valid person found for" << index;
}
}
break;
}
}
......
......@@ -28,6 +28,7 @@
#include <calendarsupport/utils.h>
#include <kohelper.h>
#include "controller.h"
#include <akonadi/collectionidentificationattribute.h>
StyledCalendarDelegate::StyledCalendarDelegate(QObject * parent)
: QStyledItemDelegate(parent)
......@@ -35,6 +36,7 @@ StyledCalendarDelegate::StyledCalendarDelegate(QObject * parent)
mPixmap.insert(Enable, KIconLoader().loadIcon(QLatin1String("bookmarks"), KIconLoader::Small));
mPixmap.insert(RemoveFromList, KIconLoader().loadIcon(QLatin1String("list-remove"), KIconLoader::Small));
mPixmap.insert(AddToList, KIconLoader().loadIcon(QLatin1String("list-add"), KIconLoader::Small));
mPixmap.insert(Quickview, KIconLoader().loadIcon(QLatin1String("quickview"), KIconLoader::Small));
}
StyledCalendarDelegate::~StyledCalendarDelegate()
......@@ -94,6 +96,11 @@ QList<StyledCalendarDelegate::Action> StyledCalendarDelegate::getActions(const Q
// kDebug() << index.data().toString() << enabled;
QList<Action> buttons;
CollectionIdentificationAttribute *attr = col.attribute<CollectionIdentificationAttribute>();
if (attr && attr->collectionNamespace().startsWith("user")) {
buttons << Quickview;
}
if (isSearchResult) {
buttons << AddToList;
} else {
......@@ -181,14 +188,11 @@ bool StyledCalendarDelegate::editorEvent(QEvent *event,
QMouseEvent *me = static_cast<QMouseEvent*>(event);
if (enableButtonRect(option.rect, 1).contains(me->pos())) {
button = 1;
}
if (enableButtonRect(option.rect, 2).contains(me->pos())) {
button = 2;
}
if (enableButtonRect(option.rect, 3).contains(me->pos())) {
button = 3;
for (int i = 1; i < 4; i++) {
if (enableButtonRect(option.rect, i).contains(me->pos())) {
button = i;
break;
}
}
if (me->button() != Qt::LeftButton || button < 0) {
return QStyledItemDelegate::editorEvent(event, model, option, index);
......@@ -206,11 +210,13 @@ bool StyledCalendarDelegate::editorEvent(QEvent *event,
QStyleOptionViewItem opt = option;
opt.state |= QStyle::State_MouseOver;
const Action a = getActions(opt, index).at(button - 1);
// kDebug() << "Button clicked: " << a;
emit action(index, a);
return true;
QList<StyledCalendarDelegate::Action> actions = getActions(opt, index);
if (actions.count() >= button) {
const Action a = actions.at(button - 1);
emit action(index, a);
return true;
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
QSize StyledCalendarDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
......
......@@ -35,7 +35,8 @@ public:
enum Action {
AddToList,
RemoveFromList,
Enable
Enable,
Quickview
};
Q_SIGNALS:
......
......@@ -111,15 +111,17 @@ bool CollectionNode::isDuplicateOf(const QModelIndex& sourceIndex)
}
PersonNode::PersonNode(ReparentingModel& personModel, const Person& person)
PersonNode::PersonNode(ReparentingModel& personModel, const Person& person, const QModelIndex &colIndex)
: Node(personModel),
mPerson(person),
mCheckState(Qt::Unchecked),
isSearchNode(false)
isSearchNode(false),
mCollectionIndex(colIndex)
{
}
PersonNode::~PersonNode()
{
......@@ -180,7 +182,12 @@ QVariant PersonNode::data(int role) const
if (role == NodeTypeRole) {
return PersonNodeRole;
}
return QVariant();
if (mCollectionIndex.isValid()) {
return mCollectionIndex.data(role);
} else {
return QVariant();
}
}
bool PersonNode::setData(const QVariant& value, int role)
......@@ -213,7 +220,7 @@ bool PersonNode::adopts(const QModelIndex& sourceIndex)
bool PersonNode::isDuplicateOf(const QModelIndex& sourceIndex)
{
return (sourceIndex.data(PersonRole).value<Person>().name == mPerson.name);
return (sourceIndex.data(PersonRole).value<Person>().uid == mPerson.uid);
}
void PersonNodeManager::checkSourceIndex(const QModelIndex &sourceIndex)
......@@ -231,7 +238,7 @@ void PersonNodeManager::checkSourceIndex(const QModelIndex &sourceIndex)
person.uid = col.name();
person.rootCollection = col.id();
model.addNode(ReparentingModel::Node::Ptr(new PersonNode(model, person)));
model.addNode(ReparentingModel::Node::Ptr(new PersonNode(model, person, sourceIndex)));
}
}
}
......@@ -250,7 +257,7 @@ void PersonNodeManager::checkSourceIndexRemoval(const QModelIndex &sourceIndex)
person.ou = QString::fromUtf8(attr->ou());
person.uid = col.name();
person.rootCollection = col.id();
model.removeNode(PersonNode(model, person));
model.removeNode(PersonNode(model, person, sourceIndex));
}
}
}
......@@ -649,7 +656,7 @@ void Controller::onCollectionsFound(KJob* job)
void Controller::onPersonsFound(const QList<Person> &persons)
{
Q_FOREACH(const Person &p, persons) {
PersonNode *personNode = new PersonNode(*mSearchModel, p);
PersonNode *personNode = new PersonNode(*mSearchModel, p, QModelIndex());
personNode->isSearchNode = true;
//toggled by the checkbox, results in person getting added to main model
// connect(&personNode->emitter, SIGNAL(enabled(bool, Person)), this, SLOT(onPersonEnabled(bool, Person)));
......@@ -659,7 +666,7 @@ void Controller::onPersonsFound(const QList<Person> &persons)
void Controller::onPersonUpdate(const Person &person)
{
PersonNode *personNode = new PersonNode(*mSearchModel, person);
PersonNode *personNode = new PersonNode(*mSearchModel, person, QModelIndex());
personNode->isSearchNode = true;
mSearchModel->updateNode(ReparentingModel::Node::Ptr(personNode));
}
......@@ -755,7 +762,7 @@ void Controller::addPerson(const Person &person)
//TODO: use the found collection and update attribute
}
PersonNode *personNode = new PersonNode(*mPersonModel, person);
PersonNode *personNode = new PersonNode(*mPersonModel, person, QModelIndex());
personNode->setChecked(true);
mPersonModel->addNode(ReparentingModel::Node::Ptr(personNode));
......@@ -770,7 +777,7 @@ void Controller::addPerson(const Person &person)
void Controller::removePerson(const Person &person)
{
kDebug() << person.uid << person.name << person.rootCollection;
mPersonModel->removeNode(PersonNode(*mPersonModel, person));
mPersonModel->removeNode(PersonNode(*mPersonModel, person, QModelIndex()));
if (person.rootCollection > -1) {
setCollectionState(Akonadi::Collection(person.rootCollection), Disabled, true);
......
......@@ -89,7 +89,7 @@ public:
class PersonNode : public ReparentingModel::Node
{
public:
PersonNode(ReparentingModel &personModel, const Person &person);
PersonNode(ReparentingModel &personModel, const Person &person, const QModelIndex &colIndex);
virtual ~PersonNode();
virtual bool operator==(const Node &) const;
......@@ -107,6 +107,7 @@ private:
Person mPerson;
Qt::CheckState mCheckState;
QModelIndex mCollectionIndex;
};
class CollectionNode : public ReparentingModel::Node
......
/*
* Copyright 2014 Sandro Knauß <knauss@kolabsys.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//http://stackoverflow.com/questions/18831242/qt-start-editing-of-cell-after-one-click
#include "quickview.h"
#include "ui_quickview.h"
#include <KCalCore/Event>
#include <KCalCore/FreeBusy>
#include <KCalCore/MemoryCalendar>
#include <akonadi/entitydisplayattribute.h>
#include <akonadi/changerecorder.h>
#include <akonadi/itemfetchscope.h>
#include <calendarviews/agenda/agendaview.h>
#include <calendarviews/agenda/viewcalendar.h>
#include <calendarsupport/calendarsingleton.h>
#include <freebusymodel/freebusycalendar.h>
#include <KDebug>
#include <KSystemTimeZones>
#include <KCheckableProxyModel>
class FreebusyViewCalendar : public EventViews::ViewCalendar
{
public:
virtual ~FreebusyViewCalendar() {};
virtual bool isValid(const KCalCore::Incidence::Ptr &incidence) const
{
return incidence->uid().startsWith(QLatin1String("fb-"));
}
virtual QString displayName(const KCalCore::Incidence::Ptr &incidence) const
{
Q_UNUSED(incidence);
return i18n("Freebusycalendar from %1").arg(name);
}
virtual QColor resourceColor(const KCalCore::Incidence::Ptr &incidence) const
{
bool ok = false;
int status = incidence->customProperty("FREEBUSY", "STATUS").toInt(&ok);
if (!ok) {
return QColor("#555");
}
switch (status) {
case KCalCore::FreeBusyPeriod::Busy:
return QColor("#f00");
case KCalCore::FreeBusyPeriod::BusyTentative:
case KCalCore::FreeBusyPeriod::BusyUnavailable:
return QColor("#f70");
case KCalCore::FreeBusyPeriod::Free:
return QColor("#0f0");
default:
return QColor("#555");
}
}
virtual QString iconForIncidence(const KCalCore::Incidence::Ptr &incidence) const
{
return QString();
}
virtual KCalCore::Calendar::Ptr getCalendar() const
{
return mCalendar;
}
KCalCore::Calendar::Ptr mCalendar;
QString name;
};
Quickview::Quickview(const Person &person, const Akonadi::Collection &col)
: mPerson(person)
, mCollection(col)
, mDayRange(7)
{
mUi = new Ui_quickview;
QWidget *w = new QWidget( this );
mUi->setupUi( w );
setMainWidget( w );
mAgendaView = new EventViews::AgendaView(QDate(), QDate(), false, false);
//show fbcalendar for person in quickview
if (!person.mail.isEmpty()) {
FreeBusyItemModel *model = new FreeBusyItemModel(this);
FreeBusyCalendar *fbCal = new FreeBusyCalendar(this);
FreebusyViewCalendar *fbCalendar = new FreebusyViewCalendar();
KCalCore::Attendee::Ptr attendee(new KCalCore::Attendee(person.name, person.mail));
FreeBusyItem::Ptr freebusy( new FreeBusyItem( attendee, this ));
fbCal->setModel(model);
model->addItem(freebusy);
fbCalendar->mCalendar = fbCal->calendar();
fbCalendar->name = attendee->fullName();
mAgendaView->addCalendar(EventViews::ViewCalendar::Ptr(fbCalendar));
}
if (mCollection.isValid()) {
//create etm for mCollection
Akonadi::ChangeRecorder *monitor = new Akonadi::ChangeRecorder(this);
Akonadi::ItemFetchScope scope;
QStringList allMimeTypes;
allMimeTypes << KCalCore::Event::eventMimeType() << KCalCore::Todo::todoMimeType()
<< KCalCore::Journal::journalMimeType();
scope.fetchFullPayload(true);
scope.fetchAttribute<Akonadi::EntityDisplayAttribute>();
monitor->setCollectionMonitored(mCollection);
monitor->fetchCollection(true);
monitor->setItemFetchScope(scope);
monitor->setAllMonitored(true);
foreach(const QString &mimetype, allMimeTypes) {
monitor->setMimeTypeMonitored(mimetype, true);
}
Akonadi::ETMCalendar::Ptr calendar = Akonadi::ETMCalendar::Ptr(new Akonadi::ETMCalendar(monitor));
calendar->setCollectionFilteringEnabled(false);
mAgendaView->setCalendar(calendar);
}
mUi->calender->addWidget( mAgendaView );
connect(mUi->mTodayBtn, SIGNAL(clicked(bool)), SLOT(onTodayClicked()));
connect(mUi->mNextBtn, SIGNAL(clicked(bool)), SLOT(onNextClicked()));
connect(mUi->mPreviousBtn, SIGNAL(clicked(bool)), SLOT(onPreviousClicked()));
onTodayClicked();
}
Quickview::~Quickview()
{
}
void Quickview::onNextClicked()
{
QDate start = mAgendaView->startDate().addDays(mDayRange);
mAgendaView->showDates(start, start.addDays(mDayRange-1));
}
void Quickview::onPreviousClicked()
{
QDate start = mAgendaView->startDate().addDays(-mDayRange);
mAgendaView->showDates(start, start.addDays(mDayRange-1));
}
void Quickview::onTodayClicked()
{
QDate start = QDate::currentDate();
start = start.addDays(-QDate::currentDate().dayOfWeek()+1);
mAgendaView->showDates(start, start.addDays(mDayRange-1));
}
#include "quickview.moc"
/*
* Copyright 2014 Sandro Knauß <knauss@kolabsys.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef KORG_QUICKVIEW_H
#define KORG_QUICKVIEW_H
#include "controller.h"
#include <calendarviews/agenda/viewcalendar.h>
#include <KCalCore/FreeBusy>
#include <KDialog>
#include <QStringList>
#include <QStringListModel>
class Ui_quickview;
namespace EventViews
{
class AgendaView;
}
class Quickview : public KDialog
{
Q_OBJECT
public:
Quickview(const Person &person, const Akonadi::Collection &col);
virtual ~Quickview();
private slots:
void onTodayClicked();
void onNextClicked();
void onPreviousClicked();
private:
Ui_quickview *mUi;
EventViews::AgendaView *mAgendaView;
Person mPerson;
Akonadi::Collection mCollection;
int mDayRange;
};
#endif // QUICKVIEW_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>quickview</class>
<widget class="QWidget" name="quickview">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>552</width>
<height>566</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="mDayBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Day</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mWeekBtn">
<property name="text">
<string>Week</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mMothBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Month</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mAgendaBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Agenda</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="mPreviousBtn">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>&lt;</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mTodayBtn">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Today</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mNextBtn">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="calender">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
</layout>