Commit 0c502202 authored by Christian Mollekopf's avatar Christian Mollekopf
Browse files

CalendarSelection: only show tool buttons on hover and display multiple tool buttons.

parent d27f32c6
......@@ -248,41 +248,6 @@ static bool hasCompatibleMimeTypes( const Akonadi::Collection &collection )
return false;
}
class ColorDelegate : public QStyledItemDelegate
{
public:
explicit ColorDelegate( QObject * parent = 0 ) : QStyledItemDelegate( parent )
{
}
void paint ( QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
QStyledItemDelegate::paint( painter, option, index );
QStyleOptionViewItemV4 v4 = option;
initStyleOption( &v4, index );
if ( v4.checkState ) {
const Akonadi::Collection collection = CalendarSupport::collectionFromIndex( index );
QColor color = KOHelper::resourceColor( collection );
if ( color.isValid() ) {
QRect r = v4.rect;
const int h = r.height() - 4;
r.adjust( r.width() - h - 2, 2, - 2, -2 );
painter->save();
painter->setRenderHint( QPainter::Antialiasing );
QPen pen = painter->pen();
pen.setColor( color );
QPainterPath path;
path.addRoundedRect( r, 5, 5 );
color.setAlpha( 200 );
painter->fillPath( path, color );
painter->strokePath( path, pen );
painter->restore();
}
}
}
};
class SortProxyModel : public QSortFilterProxyModel
{
public:
......@@ -294,8 +259,8 @@ class SortProxyModel : public QSortFilterProxyModel
bool lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
QVariant leftPerson = left.data(PersonNode::PersonRole);
QVariant rightPerson = right.data(PersonNode::PersonRole);
QVariant leftPerson = left.data(PersonRole);
QVariant rightPerson = right.data(PersonRole);
if (leftPerson.isValid() && !rightPerson.isValid()) {
return true;
}
......@@ -452,7 +417,7 @@ AkonadiCollectionView::AkonadiCollectionView( CalendarView *view, bool hasContex
mCollectionView->setRootIsDecorated( true );
{
StyledCalendarDelegate *delegate = new StyledCalendarDelegate(mCollectionView);
connect(delegate, SIGNAL(enabled(QModelIndex, bool)), this, SLOT(onCalendarEnabled(QModelIndex, bool)));
connect(delegate, SIGNAL(action(QModelIndex, int)), this, SLOT(onAction(QModelIndex, int)));
mCollectionView->setItemDelegate( delegate );
}
mCollectionView->setModel( collectionFilter );
......@@ -478,7 +443,11 @@ AkonadiCollectionView::AkonadiCollectionView( CalendarView *view, bool hasContex
Akonadi::EntityTreeView *mSearchView = new Akonadi::EntityTreeView( this );
mSearchView->header()->hide();
mSearchView->setRootIsDecorated( true );
mSearchView->setItemDelegate( new ColorDelegate( this ) );
{
StyledCalendarDelegate *delegate = new StyledCalendarDelegate(mCollectionView);
connect(delegate, SIGNAL(action(QModelIndex, int)), this, SLOT(onAction(QModelIndex, int)));
mSearchView->setItemDelegate( delegate );
}
mSearchView->setModel( searchProxy );
new NewNodeExpander(mSearchView, true, QString());
......@@ -892,13 +861,23 @@ void AkonadiCollectionView::edit_enable()
}
}
void AkonadiCollectionView::onCalendarEnabled(const QModelIndex &index, bool enabled)
{
const Akonadi::Collection col = CalendarSupport::collectionFromIndex(index);
if (col.isValid()) {
mController->setCollection(col, enabled, false);
} else {
if (!enabled) {
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>();
if (col.isValid()) {
mController->setCollection(col, false, true);
} else {
const QVariant var = index.data(PersonRole);
if (var.isValid()) {
mController->setPersonEnabled(var.value<Person>(), true);
}
}
}
break;
case StyledCalendarDelegate::RemoveFromList: {
//Disable all child collections
const QAbstractItemModel *model = index.model();
for (int row = 0; row < model->rowCount(index); row++) {
......@@ -907,12 +886,24 @@ void AkonadiCollectionView::onCalendarEnabled(const QModelIndex &index, bool ena
mController->setCollection(col, false, false);
}
}
const Akonadi::Collection col = CalendarSupport::collectionFromIndex(index);
if (col.isValid()) {
mController->setCollection(col, false, false);
} else {
const QVariant var = index.data(PersonRole);
if (var.isValid()) {
mController->setPersonEnabled(var.value<Person>(), false);
}
}
}
const QVariant var = index.data(PersonNode::PersonRole);
if (var.isValid()) {
mController->setPersonDisabled(var.value<Person>());
break;
case StyledCalendarDelegate::Enable: {
const Akonadi::Collection col = CalendarSupport::collectionFromIndex(index);
if (col.isValid()) {
mController->setCollection(col, true, false);
}
}
break;
}
}
......
......@@ -109,7 +109,7 @@ class AkonadiCollectionView : public CalendarViewExtension
void disableColor();
void setDefaultCalendar();
void onSearchIsActive(bool);
void onCalendarEnabled(const QModelIndex &, bool);
void onAction(const QModelIndex &index, int action);
private:
Akonadi::EntityTreeModel *entityTreeModel() const;
......
......@@ -27,10 +27,14 @@
#include <calendarsupport/utils.h>
#include <kohelper.h>
#include "controller.h"
StyledCalendarDelegate::StyledCalendarDelegate(QObject * parent)
: QStyledItemDelegate(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));
}
StyledCalendarDelegate::~StyledCalendarDelegate()
......@@ -38,12 +42,13 @@ StyledCalendarDelegate::~StyledCalendarDelegate()
}
static QRect enableButtonRect(const QRect &rect)
static QRect enableButtonRect(const QRect &rect, int pos = 1)
{
QRect r = rect;
const int h = r.height()- 4;
r.adjust(r.width()- h*2 - 2*2, 2, -2 - h - 2, -2);
return r;
//2px border on each side of the icon
static int border = 2;
const int side = rect.height() - (2 * border);
const int offset = side * pos + border * (pos + 1);
return rect.adjusted(rect.width() - (offset + side), border, -offset, -border);
}
static QStyle *style(const QStyleOptionViewItem &option)
......@@ -54,38 +59,71 @@ static QStyle *style(const QStyleOptionViewItem &option)
}
QStyle *style = widget ? widget->style() : QApplication::style();
return style;
}
static QStyleOptionButton buttonOpt(const QStyleOptionViewItemV4 &opt, const QPixmap &pixmap, int pos = 1)
{
QStyleOptionButton buttonOpt;
buttonOpt.icon = pixmap;
QRect r = opt.rect;
const int h = r.height() - 4;
buttonOpt.rect = enableButtonRect(r, pos);
buttonOpt.state = QStyle::State_Active | QStyle::State_Enabled;
buttonOpt.iconSize = QSize(h, h);
return buttonOpt;
}
QList<StyledCalendarDelegate::Action> StyledCalendarDelegate::getActions(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
const bool isSearchResult = index.data(IsSearchResultRole).toBool();
const bool hover = option.state & QStyle::State_MouseOver;
const Akonadi::Collection col = CalendarSupport::collectionFromIndex(index);
const bool enabled = col.shouldList(Akonadi::Collection::ListDisplay);
const bool referenced = col.referenced();
QList<Action> buttons;
if (isSearchResult) {
buttons << AddToList;
} else {
//Folders that have been pulled in due to a subfolder
// if (!enabled && !referenced) {
// return QList<Action>() << RemoveFromList;
// }
if (hover) {
if (!enabled) {
buttons << Enable;
}
buttons << RemoveFromList;
} else {
if (enabled) {
buttons << Enable;
}
}
}
return buttons;
}
void StyledCalendarDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
Q_ASSERT(index.isValid());
QStyledItemDelegate::paint( painter, option, index );
const Akonadi::Collection col = CalendarSupport::collectionFromIndex(index);
QStyleOptionViewItemV4 opt = option;
initStyleOption(&opt, index);
QStyledItemDelegate::paint(painter, opt, index);
QStyle *s = style(option);
const Akonadi::Collection col = CalendarSupport::collectionFromIndex(index);
//Favorite button
//Buttons
{
static QPixmap enablePixmap = KIconLoader().loadIcon(QLatin1String("bookmarks"), KIconLoader::Small);
static QPixmap disablePixmap = KIconLoader().loadIcon(QLatin1String("window-close"), KIconLoader::Small);
QStyleOptionButton buttonOpt;
if (!col.shouldList(Akonadi::Collection::ListDisplay)) {
buttonOpt.icon = enablePixmap;
} else {
buttonOpt.icon = disablePixmap;
QList<Action> buttons;
int i = 1;
Q_FOREACH (Action action, getActions(option, index)) {
QStyleOptionButton buttonOption = buttonOpt(opt, mPixmap.value(action), i);
s->drawControl(QStyle::CE_PushButton, &buttonOption, painter, 0);
i++;
}
QRect r = opt.rect;
const int h = r.height()- 4;
buttonOpt.rect = enableButtonRect(r);
buttonOpt.state = QStyle::State_Active | QStyle::State_Enabled;
buttonOpt.iconSize = QSize(h, h);
s->drawControl(QStyle::CE_PushButton, &buttonOpt, painter, 0);
}
//Color indicator
......@@ -117,15 +155,24 @@ bool StyledCalendarDelegate::editorEvent(QEvent *event,
Q_ASSERT(event);
Q_ASSERT(model);
int button = -1;
// make sure that we have the right event type
if ((event->type() == QEvent::MouseButtonRelease)
|| (event->type() == QEvent::MouseButtonDblClick)
|| (event->type() == QEvent::MouseButtonPress)) {
QRect buttonRect = enableButtonRect(option.rect);
QMouseEvent *me = static_cast<QMouseEvent*>(event);
if (me->button() != Qt::LeftButton || !buttonRect.contains(me->pos())) {
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;
}
if (me->button() != Qt::LeftButton || button < 0) {
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
......@@ -137,14 +184,15 @@ bool StyledCalendarDelegate::editorEvent(QEvent *event,
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
onEnableButtonClicked(index);
return true;
}
Q_ASSERT(button > 0);
QStyleOptionViewItem opt = option;
opt.state |= QStyle::State_MouseOver;
void StyledCalendarDelegate::onEnableButtonClicked(const QModelIndex &index)
{
const Akonadi::Collection col = CalendarSupport::collectionFromIndex(index);
emit enabled(index, !col.enabled());
const Action a = getActions(opt, index).at(button - 1);
// kDebug() << "Button clicked: " << a;
emit action(index, a);
return true;
}
QSize StyledCalendarDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
......
......@@ -32,8 +32,14 @@ public:
void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const;
QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const;
enum Action {
AddToList,
RemoveFromList,
Enable
};
Q_SIGNALS:
void enabled(const QModelIndex &, bool);
void action(const QModelIndex &, int);
protected:
bool editorEvent(QEvent *event,
......@@ -42,7 +48,8 @@ protected:
const QModelIndex &index);
private:
void onEnableButtonClicked(const QModelIndex &index);
QList<Action> getActions(const QStyleOptionViewItem & option, const QModelIndex &index) const;
QHash<Action, QPixmap> mPixmap;
};
#endif
......
......@@ -37,7 +37,8 @@
CollectionNode::CollectionNode(ReparentingModel& personModel, const Akonadi::Collection& col)
: Node(personModel),
mCollection(col),
mCheckState(Qt::Unchecked)
mCheckState(Qt::Unchecked),
isSearchNode(false)
{
}
......@@ -73,11 +74,20 @@ QVariant CollectionNode::data(int role) const
return QVariant();
}
if (role == Qt::CheckStateRole) {
if (isSearchNode) {
return QVariant();
}
return mCheckState;
}
if (role == Qt::ToolTipRole) {
return QString(QLatin1String("Collection: ") + mCollection.name() + QString::number(mCollection.id()));
}
if (role == IsSearchResultRole) {
return isSearchNode;
}
if (role == CollectionRole) {
return QVariant::fromValue(mCollection);
}
return QVariant();
}
......@@ -100,7 +110,8 @@ bool CollectionNode::isDuplicateOf(const QModelIndex& sourceIndex)
PersonNode::PersonNode(ReparentingModel& personModel, const Person& person)
: Node(personModel),
mPerson(person),
mCheckState(Qt::Unchecked)
mCheckState(Qt::Unchecked),
isSearchNode(false)
{
}
......@@ -137,6 +148,9 @@ QVariant PersonNode::data(int role) const
return KIcon(QLatin1String("meeting-participant"));
}
if (role == Qt::CheckStateRole) {
if (isSearchNode) {
return QVariant();
}
return mCheckState;
}
if (role == Qt::ToolTipRole) {
......@@ -145,6 +159,9 @@ QVariant PersonNode::data(int role) const
if (role == PersonRole) {
return QVariant::fromValue(mPerson);
}
if (role == IsSearchResultRole) {
return isSearchNode;
}
return QVariant();
}
......@@ -184,11 +201,11 @@ bool PersonNode::isDuplicateOf(const QModelIndex& sourceIndex)
void PersonNodeManager::checkSourceIndex(const QModelIndex &sourceIndex)
{
const Akonadi::Collection col = sourceIndex.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
kDebug() << col.displayName() << col.enabled();
// kDebug() << col.displayName() << col.enabled();
if (col.isValid()) {
CollectionIdentificationAttribute *attr = col.attribute<CollectionIdentificationAttribute>();
if (attr && attr->collectionNamespace() == "usertoplevel") {
kDebug() << "Found user folder, creating person node";
kDebug() << "Found user folder, creating person node " << col.displayName();
Person person;
person.name = col.displayName();
person.rootCollection = col.id();
......@@ -198,6 +215,23 @@ void PersonNodeManager::checkSourceIndex(const QModelIndex &sourceIndex)
}
}
void PersonNodeManager::checkSourceIndexRemoval(const QModelIndex &sourceIndex)
{
const Akonadi::Collection col = sourceIndex.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>();
// kDebug() << col.displayName() << col.enabled();
if (col.isValid()) {
CollectionIdentificationAttribute *attr = col.attribute<CollectionIdentificationAttribute>();
if (attr && attr->collectionNamespace() == "usertoplevel") {
kDebug() << "Found user folder, removing person node " << col.displayName();
Person person;
person.name = col.displayName();
person.rootCollection = col.id();
model.removeNode(PersonNode(model, person));
}
}
}
CollectionSearchJob::CollectionSearchJob(const QString& searchString, QObject* parent)
: KJob(parent),
mSearchString(searchString)
......@@ -400,25 +434,7 @@ void Controller::setSearchString(const QString &searchString)
void Controller::onPersonEnabled(bool enabled, const Person& person)
{
// kDebug() << person.name << enabled;
if (enabled) {
PersonNode *personNode = new PersonNode(*mPersonModel, person);
personNode->setChecked(true);
mPersonModel->addNode(ReparentingModel::Node::Ptr(personNode));
Akonadi::Collection rootCollection(person.rootCollection);
if (rootCollection.isValid()) {
//Reference the persons collections if available
//We have to include all mimetypes since mimetypes are not available yet (they will be synced once the collectoins are referenced)
Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(rootCollection, Akonadi::CollectionFetchJob::Recursive, this);
fetchJob->setProperty("enable", enabled);
fetchJob->fetchScope().setListFilter(Akonadi::CollectionFetchScope::NoFilter);
connect(fetchJob, SIGNAL(result(KJob*)), this, SLOT(onPersonCollectionsFetched(KJob*)));
}
} else {
//If we accidentally added a person and want to remove it again
mPersonModel->removeNode(PersonNode(*mPersonModel, person));
//Dereference subcollections
}
setPersonEnabled(person, enabled);
}
......@@ -444,6 +460,7 @@ void Controller::onCollectionsFound(KJob* job)
Q_ASSERT(mCollectionSearchJob == static_cast<CollectionSearchJob*>(job));
Q_FOREACH(const Akonadi::Collection &col, mCollectionSearchJob->matchingCollections()) {
CollectionNode *collectionNode = new CollectionNode(*mSearchModel, col);
collectionNode->isSearchNode = true;
//toggled by the checkbox, results in collection getting monitored
connect(&collectionNode->emitter, SIGNAL(enabled(bool, Akonadi::Collection)), this, SLOT(onCollectionEnabled(bool, Akonadi::Collection)));
mSearchModel->addNode(ReparentingModel::Node::Ptr(collectionNode));
......@@ -461,6 +478,7 @@ void Controller::onPersonsFound(KJob* job)
Q_ASSERT(mPersonSearchJob == static_cast<PersonSearchJob*>(job));
Q_FOREACH(const Person &p, mPersonSearchJob->matches()) {
PersonNode *personNode = new PersonNode(*mSearchModel, p);
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)));
mSearchModel->addNode(ReparentingModel::Node::Ptr(personNode));
......@@ -521,8 +539,26 @@ void Controller::setCollection(const Akonadi::Collection &collection, bool enabl
etm->setCollectionReferenced(modifiedCollection, referenced);
}
void Controller::setPersonDisabled(const Person &person)
void Controller::setPersonEnabled(const Person &person, bool enabled)
{
mPersonModel->removeNode(PersonNode(*mPersonModel, person));
// kDebug() << person.name << enabled;
if (enabled) {
PersonNode *personNode = new PersonNode(*mPersonModel, person);
personNode->setChecked(true);
mPersonModel->addNode(ReparentingModel::Node::Ptr(personNode));
} else {
mPersonModel->removeNode(PersonNode(*mPersonModel, person));
}
Akonadi::Collection rootCollection(person.rootCollection);
// kDebug() << rootCollection.id();
if (rootCollection.isValid()) {
//Reference the persons collections if available
//We have to include all mimetypes since mimetypes are not available yet (they will be synced once the collectoins are referenced)
Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(rootCollection, Akonadi::CollectionFetchJob::Recursive, this);
fetchJob->setProperty("enable", enabled);
fetchJob->fetchScope().setListFilter(Akonadi::CollectionFetchScope::NoFilter);
connect(fetchJob, SIGNAL(result(KJob*)), this, SLOT(onPersonCollectionsFetched(KJob*)));
}
}
......@@ -29,6 +29,12 @@
#include <Akonadi/Collection>
#include "reparentingmodel.h"
enum DataRole {
PersonRole = Akonadi::EntityTreeModel::UserRole + 1,
IsSearchResultRole,
CollectionRole
};
struct Person
{
Person(): rootCollection(-1){};
......@@ -69,10 +75,6 @@ public:
class PersonNode : public ReparentingModel::Node
{
public:
enum DataRole {
PersonRole = Akonadi::EntityTreeModel::UserRole + 1
};
PersonNode(ReparentingModel &personModel, const Person &person);
virtual ~PersonNode();
virtual bool operator==(const Node &) const;
......@@ -82,6 +84,8 @@ public:
virtual QVariant data(int role) const;
Emitter emitter;
bool isSearchNode;
private:
virtual bool setData(const QVariant& variant, int role);
virtual bool adopts(const QModelIndex& sourceIndex);
......@@ -99,6 +103,7 @@ public:
virtual bool operator==(const Node &) const;
Emitter emitter;
bool isSearchNode;
private:
virtual QVariant data(int role) const;
......@@ -114,6 +119,7 @@ public:
PersonNodeManager(ReparentingModel &personModel) : ReparentingModel::NodeManager(personModel){};
private:
void checkSourceIndex(const QModelIndex &sourceIndex);
void checkSourceIndexRemoval(const QModelIndex &sourceIndex);
};
class CollectionSearchJob : public KJob
......@@ -172,7 +178,7 @@ public:
void setCollectionReferenced(bool enabled, const Akonadi::Collection &collection);
void setCollectionEnabled(bool enabled, const Akonadi::Collection &collection);
void setCollection(const Akonadi::Collection &collection, bool enabled, bool referenced);
void setPersonDisabled(const Person &person);
void setPersonEnabled(const Person &person, bool enabled);
Q_SIGNALS:
void searchIsActive(bool);
......@@ -184,7 +190,6 @@ private Q_SLOTS:
void onCollectionsFound(KJob *job);
void onPersonsFound(KJob *job);
void onPersonEnabled(bool enabled, const Person &person);
// void onPersonCollectionsReceived(Akonadi::Collection::List);
void onPersonCollectionsFetched(KJob *job);
void onCollectionEnabled(bool enabled, const Akonadi::Collection &collection);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment