diff -Nru zanshin-0.1+svn1006410/CMakeLists.txt zanshin-0.2.0/CMakeLists.txt --- zanshin-0.1+svn1006410/CMakeLists.txt 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/CMakeLists.txt 2011-11-25 15:00:52.000000000 +0000 @@ -10,7 +10,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}") add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) -include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES} ${Boost_INCLUDE_DIR}) +add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) +include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES} ${KDEPIMLIBS_INCLUDE_DIRS} ${Boost_INCLUDE_DIR}) add_subdirectory(src) +add_subdirectory(tests) diff -Nru zanshin-0.1+svn1006410/debian/changelog zanshin-0.2.0/debian/changelog --- zanshin-0.1+svn1006410/debian/changelog 2012-01-06 23:36:45.000000000 +0000 +++ zanshin-0.2.0/debian/changelog 2012-01-01 20:22:30.000000000 +0000 @@ -1,35 +1,5 @@ -zanshin (0.1+svn1006410-0ubuntu6) oneiric; urgency=low +zanshin (0.2.0-1) unstable; urgency=low - * Rebuild against libboost1.46-dev + * Initial release. (Closes: #653943) - -- Scott Kitterman Fri, 05 Aug 2011 19:23:34 -0400 - -zanshin (0.1+svn1006410-0ubuntu5) maverick; urgency=low - - * Bump Boost build-dep to libboost1.42-dev - - -- Scott Kitterman Wed, 09 Jun 2010 00:22:37 -0400 - -zanshin (0.1+svn1006410-0ubuntu4) lucid; urgency=low - - * Bump Boost build-dep to libboost1.40-dev - - -- Scott Kitterman Thu, 17 Dec 2009 09:44:40 -0500 - -zanshin (0.1+svn1006410-0ubuntu3) karmic; urgency=low - - * Fix copyright-should-refer-to-common-license-file-for-gpl - - -- Jonathan Riddell Tue, 18 Aug 2009 16:22:08 +0100 - -zanshin (0.1+svn1006410-0ubuntu2) karmic; urgency=low - - * Add build-dep on libboost1.38-dev - - -- Jonathan Riddell Tue, 18 Aug 2009 13:36:13 +0100 - -zanshin (0.1+svn1006410-0ubuntu1) karmic; urgency=low - - * Initial release - - -- Jonathan Riddell Thu, 13 Aug 2009 23:19:32 +0100 + -- Fathi Boudra Sun, 01 Jan 2012 20:48:41 +0200 diff -Nru zanshin-0.1+svn1006410/debian/compat zanshin-0.2.0/debian/compat --- zanshin-0.1+svn1006410/debian/compat 2012-01-06 23:36:45.000000000 +0000 +++ zanshin-0.2.0/debian/compat 2012-01-01 19:30:12.000000000 +0000 @@ -1 +1 @@ -7 +8 diff -Nru zanshin-0.1+svn1006410/debian/control zanshin-0.2.0/debian/control --- zanshin-0.1+svn1006410/debian/control 2012-01-06 23:36:45.000000000 +0000 +++ zanshin-0.2.0/debian/control 2012-01-01 19:37:49.000000000 +0000 @@ -1,15 +1,21 @@ Source: zanshin Section: kde Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Jonathan Riddell -Build-Depends: debhelper (>= 7), cdbs, cmake, pkg-kde-tools, - kdelibs5-dev, kdepimlibs5-dev, libboost1.46-dev -Standards-Version: 3.8.2 +Maintainer: Debian KDE Extras Team +Uploaders: Fathi Boudra +Build-Depends: cmake, + debhelper (>= 8.0.0), + kdepimlibs5-dev, + libboost-dev, + pkg-kde-tools +Standards-Version: 3.9.2 +Homepage: http://zanshin.kde.org/ Package: zanshin Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} -Description: Todo List Manager - A Getting Things Done application which aims at getting your mind like water. - +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: to-do list manager + Zanshin is a powerful yet simple application for managing your day to day + actions and notes. It helps you organize and reduce the cognitive pressure of + what one has to do in his job and personal life. You'll never forget anything + anymore, getting your mind like water. diff -Nru zanshin-0.1+svn1006410/debian/copyright zanshin-0.2.0/debian/copyright --- zanshin-0.1+svn1006410/debian/copyright 2012-01-06 23:36:45.000000000 +0000 +++ zanshin-0.2.0/debian/copyright 2012-01-01 20:19:15.000000000 +0000 @@ -1,72 +1,80 @@ -Name: zanshin -Maintainer: Jonathan Riddell -Source: http://websvn.kde.org/branches/work/~ervin/zanshin/ +Format: http://dep.debian.net/deps/dep5 +Upstream-Name: zanshin +Source: http://zanshin.kde.org/ Files: * -Copyright: 2008-2009 Kevin Ottens - 2008-2009 Mario Bensi -License: GPL-2 or GPL-3 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. - - On Debian systems this can be found in /usr/share/common-licenses/GPL - -Files: src/kdateedit.cpp: LGPL (v2 or later) - src/kdatepickerpopup.h: LGPL (v2 or later) - src/kdateedit.h: LGPL (v2 or later) - src/kdatepickerpopup.cpp: LGPL (v2 or later) +Copyright: 2008-2011 Kevin Ottens + 2008-2011 Mario Bensi +License: GPL-2+ + +Files: src/actionduedatedelegate.* +Copyright: 2008 Thomas Thrainer +License: GPL-2+ + +Files: src/kdateedit.* Copyright: 2002 Cornelius Schumacher 2002 David Jarvie 2003-2004 Reinhold Kainhofer 2004 Tobias Koenig - 2004 Bram Schoenmakers License: LGPL-2+ - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - On Debian systems this can be found in /usr/share/common-licenses/LGPL +Files: src/kdatepickerpopup.* +Copyright: 2004 Bram Schoenmakers +License: LGPL-2+ + +Files: tests/testlib/modeltest.* +Copyright: 2011 Nokia Corporation and/or its subsidiary(-ies) +License: LGPL2.1 with exception Files: debian/* -Copyright: 2009 Canonical Ltd +Copyright: 2012 Fathi Boudra License: GPL-2+ - 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) any later version. - - 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 . +License: GPL-2+ + This package 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) any later version. + . + This package 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 + . + + On Debian systems, the complete text of the GNU General Public License + can be found in "/usr/share/common-licenses/GPL-2" and + "/usr/share/common-licenses/GPL-3". - On Debian systems this can be found in /usr/share/common-licenses/GPL +License: LGPL-2+ + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + . + This package 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 + Lesser 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 . + . + On Debian systems, the complete text of the GNU Lesser General Public License + can be found in "/usr/share/common-licenses/LGPL-2" and + "/usr/share/common-licenses/LGPL-3". + +License: LGPL2.1 with exception + Alternatively, this file may be used under the terms of the GNU Lesser + General Public License version 2.1 as published by the Free Software + Foundation and appearing in the file LICENSE.LGPL included in the + packaging of this file. Please review the following information to + ensure the GNU Lesser General Public License version 2.1 requirements + will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + . + In addition, as a special exception, Nokia gives you certain additional + rights. These rights are described in the Nokia Qt LGPL Exception + version 1.1, included in the file LGPL_EXCEPTION.txt in this package. diff -Nru zanshin-0.1+svn1006410/debian/rules zanshin-0.2.0/debian/rules --- zanshin-0.1+svn1006410/debian/rules 2012-01-06 23:36:45.000000000 +0000 +++ zanshin-0.2.0/debian/rules 2012-01-01 20:07:56.000000000 +0000 @@ -1,4 +1,9 @@ #!/usr/bin/make -f -include /usr/share/cdbs/1/rules/debhelper.mk -include /usr/share/pkg-kde-tools/makefiles/1/cdbs/kde.mk +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ --parallel --with kde + +override_dh_auto_test: diff -Nru zanshin-0.1+svn1006410/debian/source/format zanshin-0.2.0/debian/source/format --- zanshin-0.1+svn1006410/debian/source/format 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/debian/source/format 2012-01-06 23:36:46.000000000 +0000 @@ -0,0 +1 @@ +3.0 (quilt) diff -Nru zanshin-0.1+svn1006410/Messages.sh zanshin-0.2.0/Messages.sh --- zanshin-0.1+svn1006410/Messages.sh 2009-08-13 23:02:32.000000000 +0000 +++ zanshin-0.2.0/Messages.sh 2011-11-25 15:00:52.000000000 +0000 @@ -1,2 +1,4 @@ #! /usr/bin/env bash +$EXTRACTRC `find . -name \*.rc` >> rc.cpp $XGETTEXT `find . -name \*.h -o -name \*.cpp` -o $podir/zanshin.pot +rm -f rc.cpp diff -Nru zanshin-0.1+svn1006410/src/aboutdata.cpp zanshin-0.2.0/src/aboutdata.cpp --- zanshin-0.1+svn1006410/src/aboutdata.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/aboutdata.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,44 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "aboutdata.h" + +KAboutData Zanshin::getAboutData() +{ + KAboutData about("zanshin", "zanshin", + ki18n("Zanshin Todo"), "0.2", + ki18n("A Getting Things Done application which aims at getting your mind like water"), + KAboutData::License_GPL_V3, + ki18n("Copyright 2008-2010, Kevin Ottens ")); + + about.addAuthor(ki18n("Kevin Ottens"), + ki18n("Lead Developer"), + "ervin@kde.org"); + + about.addAuthor(ki18n("Mario Bensi"), + ki18n("Developer"), + "nef@ipsquad.net"); + + return about; +} + diff -Nru zanshin-0.1+svn1006410/src/aboutdata.h zanshin-0.2.0/src/aboutdata.h --- zanshin-0.1+svn1006410/src/aboutdata.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/aboutdata.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,35 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_ABOUTDATA_H +#define ZANSHIN_ABOUTDATA_H + +#include + +namespace Zanshin +{ + KAboutData getAboutData(); +} + +#endif + diff -Nru zanshin-0.1+svn1006410/src/actionduedatedelegate.cpp zanshin-0.2.0/src/actionduedatedelegate.cpp --- zanshin-0.1+svn1006410/src/actionduedatedelegate.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/actionduedatedelegate.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -26,19 +26,15 @@ #include #include -#include +#include #include "kdateedit.h" #include -#include "actionlistmodel.h" - using namespace KPIM; -typedef boost::shared_ptr IncidencePtr; - -ActionDueDateDelegate::ActionDueDateDelegate(QObject *parent) - : ActionListDelegate(parent) +ActionDueDateDelegate::ActionDueDateDelegate(ModelStack *models, QObject *parent) + : ActionListDelegate(models, parent) { } diff -Nru zanshin-0.1+svn1006410/src/actionduedatedelegate.h zanshin-0.2.0/src/actionduedatedelegate.h --- zanshin-0.1+svn1006410/src/actionduedatedelegate.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/actionduedatedelegate.h 2011-11-25 15:00:52.000000000 +0000 @@ -26,14 +26,13 @@ #define ZANSHIN_ACTIONDUEDATEDELEGATE_H #include "actionlistdelegate.h" -#include "todoflatmodel.h" class ActionDueDateDelegate : public ActionListDelegate { Q_OBJECT public: - ActionDueDateDelegate(QObject *parent = 0); + ActionDueDateDelegate(ModelStack *models, QObject *parent = 0); virtual ~ActionDueDateDelegate(); QWidget *createEditor( QWidget *parent, const QStyleOptionViewItem &option, diff -Nru zanshin-0.1+svn1006410/src/actionlistcheckablemodel.cpp zanshin-0.2.0/src/actionlistcheckablemodel.cpp --- zanshin-0.1+svn1006410/src/actionlistcheckablemodel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/actionlistcheckablemodel.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,79 @@ +/* This file is part of Zanshin Todo. + + Copyright 2010 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include +#include +#include +#include + +ActionListCheckableModel::ActionListCheckableModel(QObject *parent) + : KCheckableProxyModel(parent) +{ +} + +QVariant ActionListCheckableModel::data(const QModelIndex& id, int role) const +{ + if (role == Qt::EditRole) { + QStringList categories; + QModelIndexList indexes = selectionModel()->selectedIndexes(); + foreach (const QModelIndex &index, indexes) { + QString category = index.data(Zanshin::CategoryPathRole).toString(); + categories << category; + } + return categories.join(", "); + } else if (role==Qt::DisplayRole) { + QModelIndex sourceChild = sourceModel()->index(id.row(), 0, id.parent()); + QString category = sourceChild.data().toString(); + category = category.mid(category.indexOf(" / ") + 3); + return category; + } + + QVariant var = KCheckableProxyModel::data(id, role); + return var; +} + +Qt::ItemFlags ActionListCheckableModel::flags(const QModelIndex &index) const +{ + if (!sourceModel()) { + return Qt::NoItemFlags; + } + + QString category = index.data().toString(); + category = category.split(" / ").last(); + Qt::ItemFlags flags = KCheckableProxyModel::flags(index); + if (m_disabledCategories.contains(category)) { + flags&= ~Qt::ItemIsEnabled; + return flags; + } + return flags; +} + +void ActionListCheckableModel::setDisabledCategories(const QStringList categories) +{ + m_disabledCategories = categories; +} + +const QStringList ActionListCheckableModel::disabledCategories() +{ + return m_disabledCategories; +} diff -Nru zanshin-0.1+svn1006410/src/actionlistcheckablemodel.h zanshin-0.2.0/src/actionlistcheckablemodel.h --- zanshin-0.1+svn1006410/src/actionlistcheckablemodel.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/actionlistcheckablemodel.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,45 @@ +/* This file is part of Zanshin Todo. + + Copyright 2010 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_ACTIONLISTCHECKALBEMODEL_H +#define ZANSHIN_ACTIONLISTCHECKABLEMODEL_H + +#include +#include + +class ActionListCheckableModel : public KCheckableProxyModel +{ + Q_OBJECT +public: + ActionListCheckableModel(QObject *parent = 0); + + QVariant data(const QModelIndex& index, int role) const; + + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + void setDisabledCategories(const QStringList categories); + const QStringList disabledCategories(); +private: + QStringList m_disabledCategories; +}; + +#endif //ZANSHIN_ACTIONLISTCOMBOBOX_H diff -Nru zanshin-0.1+svn1006410/src/actionlistcombobox.cpp zanshin-0.2.0/src/actionlistcombobox.cpp --- zanshin-0.1+svn1006410/src/actionlistcombobox.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/actionlistcombobox.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,139 @@ +/* This file is part of Zanshin Todo. + + Copyright 2010 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "actionlistcombobox.h" +#include "combomodel.h" + +#include +#include +#include +#include +#include +#include +#include + +ActionListComboBox::ActionListComboBox(QWidget *parent) + : QComboBox(parent), m_autoHidePopupEnabled(false) +{ +} + +void ActionListComboBox::setAutoHidePopupEnabled(bool autoHidePopupEnabled) +{ + if (autoHidePopupEnabled == m_autoHidePopupEnabled) { + return; + } + if (autoHidePopupEnabled) { + view()->removeEventFilter(view()->parentWidget()); + view()->viewport()->removeEventFilter(view()->parentWidget()); + view()->viewport()->installEventFilter(this); + } else { + view()->viewport()->removeEventFilter(this); + view()->installEventFilter(view()->parentWidget()); + view()->viewport()->installEventFilter(view()->parentWidget()); + } +} +bool ActionListComboBox::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::MouseButtonRelease && object==view()->viewport()) { + QMouseEvent *ev = static_cast(event); + QModelIndex index = view()->indexAt(ev->pos()); + setCurrentIndex(index.row()); + } + return QComboBox::eventFilter(object,event); +} + +QRect ActionListComboBox::finalizePopupGeometry(const QRect &geometry) const +{ + QRect result = geometry; + + int width = 0; + const int itemCount = count(); + const int iconWidth = iconSize().width() + 4; + const QFontMetrics &fm = fontMetrics(); + + for (int i = 0; i < itemCount; ++i) { + QModelIndex index = model()->index(i, 0); + if (index.isValid()) { + const int textWidth = fm.width(index.data().toString()); + if (itemIcon(i).isNull()) { + width = (qMax(width, textWidth)); + } else { + width = (qMax(width, textWidth + iconWidth)); + } + } + } + + QStyleOptionComboBox opt; + initStyleOption(&opt); + QSize tmp(width, 0); + tmp = style()->sizeFromContents(QStyle::CT_ComboBox, &opt, tmp, this); + width = tmp.width() + 2 * iconWidth; + + const int screenWidth = QApplication::desktop()->width(); + if ( width>screenWidth/2 ) { + width = screenWidth/2; + } + + if (width>geometry.width()) { + QSize size = geometry.size(); + size.setWidth(width + 10); + result.setSize(size); + } + + const int dx = screenWidth - result.right(); + + if (dx<0) { + const int x = geometry.x() + dx; + const int y = geometry.y(); + result.moveTopLeft(QPoint(x, y)); + } + + return result; +} + +void ActionListComboBox::showPopup() +{ + QComboBox::showPopup(); + + QRect geometry = finalizePopupGeometry(view()->parentWidget()->geometry()); + view()->parentWidget()->setGeometry(geometry); +} + +void ActionListComboBox::childEvent(QChildEvent *event) +{ + if (event->polished() && qobject_cast(event->child())) { + QTimer::singleShot(0, this, SLOT(onLineEditPolished())); + } +} + +void ActionListComboBox::onLineEditPolished() +{ + /*disconnect signal editingFinished and returnPressed in + actionListComboBox to fix the currentIndex, by default the combobox + call QAbstractItemModel::match to find the good index and here it can't + work because we set the lineEdit with the item name without the path and the + item.data(DisplayRole) return the path + the name of item.*/ + + disconnect(lineEdit(), SIGNAL(returnPressed()), this, 0); + disconnect(lineEdit(), SIGNAL(editingFinished()), this, 0); +} diff -Nru zanshin-0.1+svn1006410/src/actionlistcombobox.h zanshin-0.2.0/src/actionlistcombobox.h --- zanshin-0.1+svn1006410/src/actionlistcombobox.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/actionlistcombobox.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,54 @@ +/* This file is part of Zanshin Todo. + + Copyright 2010 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_ACTIONLISTCOMBOBOX_H +#define ZANSHIN_ACTIONLISTCOMBOBOX_H + +#include + +class ActionListComboBox : public QComboBox +{ + Q_OBJECT +public: + ActionListComboBox(QWidget *parent = 0); + + void setAutoHidePopupEnabled(bool autoHidePopupEnabled); + + bool eventFilter(QObject *object, QEvent *event); + + QRect finalizePopupGeometry(const QRect &geometry) const; + +public slots: + virtual void showPopup(); + +protected: + virtual void childEvent(QChildEvent *event); + +private slots: + void onLineEditPolished(); + +private: + bool m_autoHidePopupEnabled; +}; + +#endif //ZANSHIN_ACTIONLISTCOMBOBOX_H diff -Nru zanshin-0.1+svn1006410/src/actionlistcompletermodel.cpp zanshin-0.2.0/src/actionlistcompletermodel.cpp --- zanshin-0.1+svn1006410/src/actionlistcompletermodel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/actionlistcompletermodel.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,75 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "actionlistcompletermodel.h" +#include "globaldefs.h" + +#include +#include +#include + +ActionListCompleterModel::ActionListCompleterModel(QItemSelectionModel *selectionModel, QObject *parent) + : QSortFilterProxyModel(parent), m_selectionModel(selectionModel), m_mapper(0) +{ + setDynamicSortFilter(true); +} + +ActionListCompleterModel::~ActionListCompleterModel() +{ +} + +bool ActionListCompleterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + if (!m_mapper) { + return false; + } + QModelIndex sourceChild = sourceModel()->index(sourceRow, 0, sourceParent); + QModelIndex selectionIndex = m_mapper->mapRightToLeft(sourceChild); + return !m_selectionModel->selectedIndexes().contains(selectionIndex); +} + +QVariant ActionListCompleterModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::EditRole) { + QStringList indexList; + QModelIndexList indexes = m_selectionModel->selectedIndexes(); + foreach (const QModelIndex &index, indexes) { + indexList << index.data(Zanshin::CategoryPathRole).toString(); + } + if (indexList.isEmpty()) { + return index.data(Zanshin::CategoryPathRole).toString(); + } else { + return QString(indexList.join(", ") + ", " + index.data(Zanshin::CategoryPathRole).toString()); + } + } + + return QSortFilterProxyModel::data(index, role); +} + +void ActionListCompleterModel::setSourceModel(QAbstractItemModel *sourceModel) +{ + delete m_mapper; + m_mapper = new KModelIndexProxyMapper(m_selectionModel->model(), sourceModel, this); + QSortFilterProxyModel::setSourceModel(sourceModel); +} diff -Nru zanshin-0.1+svn1006410/src/actionlistcompletermodel.h zanshin-0.2.0/src/actionlistcompletermodel.h --- zanshin-0.1+svn1006410/src/actionlistcompletermodel.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/actionlistcompletermodel.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,50 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_ACTIONLISTCOMPLETERMODEL_H +#define ZANSHIN_ACTIONLISTCOMPLETERMODEL_H + +#include + +class KModelIndexProxyMapper; +class QItemSelectionModel; +class ActionListCompleterModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + ActionListCompleterModel(QItemSelectionModel *selection, QObject *parent = 0); + virtual ~ActionListCompleterModel(); + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + QVariant data(const QModelIndex& index, int role) const; + + virtual void setSourceModel(QAbstractItemModel *sourceModel); +private: + QItemSelectionModel *m_selectionModel; + KModelIndexProxyMapper *m_mapper; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/actionlistcompleterview.cpp zanshin-0.2.0/src/actionlistcompleterview.cpp --- zanshin-0.1+svn1006410/src/actionlistcompleterview.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/actionlistcompleterview.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,62 @@ +/* This file is part of Zanshin Todo. + + Copyright 2010 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "actionlistcompleterview.h" + +#include +#include + +#include "actionlistcombobox.h" + +ActionListCompleterView::ActionListCompleterView(ActionListComboBox *parent) + : QListView(parent), + m_combo(parent) +{ +} + +void ActionListCompleterView::mouseReleaseEvent(QMouseEvent *e) +{ + QModelIndex index = indexAt(e->pos()); + QVariant value = index.data(Qt::CheckStateRole); + if (!value.isValid()) { + return; + } + Qt::CheckState state = (static_cast(value.toInt()) == Qt::Checked + ? Qt::Unchecked : Qt::Checked); + model()->setData(index, state, Qt::CheckStateRole); + hide(); +} + +void ActionListCompleterView::resizeEvent(QResizeEvent *e) +{ + QListView::resizeEvent(e); + + static bool forceGeometry = false; + + if (!forceGeometry) { + forceGeometry = true; + QRect geo = m_combo->finalizePopupGeometry(geometry()); + setGeometry(geo); + forceGeometry = false; + } +} diff -Nru zanshin-0.1+svn1006410/src/actionlistcompleterview.h zanshin-0.2.0/src/actionlistcompleterview.h --- zanshin-0.1+svn1006410/src/actionlistcompleterview.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/actionlistcompleterview.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,45 @@ +/* This file is part of Zanshin Todo. + + Copyright 2010 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_ACTIONLISTCOMPLETERVIEW_H +#define ZANSHIN_ACTIONLISTCOMPLETERVIEW_H + +#include + +class ActionListComboBox; + +class ActionListCompleterView : public QListView +{ + Q_OBJECT +public: + ActionListCompleterView(ActionListComboBox *parent = 0); + +protected: + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void resizeEvent(QResizeEvent *e); + +private: + ActionListComboBox *m_combo; +}; + +#endif //ZANSHIN_ACTIONLISTCOMPLETERVIEW_H diff -Nru zanshin-0.1+svn1006410/src/actionlistdelegate.cpp zanshin-0.2.0/src/actionlistdelegate.cpp --- zanshin-0.1+svn1006410/src/actionlistdelegate.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/actionlistdelegate.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -23,18 +23,29 @@ #include "actionlistdelegate.h" -#include -#include -#include - -#include "actionlistmodel.h" - -typedef boost::shared_ptr IncidencePtr; - -ActionListDelegate::ActionListDelegate(QObject *parent) - : QStyledItemDelegate(parent), m_dragModeCount(0) +#include +#include +#include +#include + +#include "actionlistcheckablemodel.h" +#include "actionlistcompleterview.h" +#include "actionlistcompletermodel.h" +#include "actionlistcombobox.h" +#include "combomodel.h" +#include "globaldefs.h" +#include "kdescendantsproxymodel.h" +#include "kdateedit.h" +#include +#include "modelstack.h" + +using namespace KPIM; +Q_DECLARE_METATYPE(QItemSelectionModel*) + +ActionListDelegate::ActionListDelegate(ModelStack *models, QObject *parent) + : QStyledItemDelegate(parent) + , m_models(models) { - } ActionListDelegate::~ActionListDelegate() @@ -45,22 +56,11 @@ QSize ActionListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { - if (m_dragModeCount>0) { - if (index.column()==0) { - return QStyledItemDelegate::sizeHint(option, index); - } else { - return QSize(); - } - } - QSize res = QStyledItemDelegate::sizeHint(option, index); - TodoFlatModel::ItemType type = rowType(index); - - if (type==TodoFlatModel::FolderTodo || !isInFocus(index)) { - res.setHeight(32); + Zanshin::ItemType type = (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); - } else if (type!=TodoFlatModel::StandardTodo) { + if (type!=Zanshin::StandardTodo) { res.setHeight(24); } @@ -71,38 +71,22 @@ const QStyleOptionViewItem &option, const QModelIndex &index) const { - if (m_dragModeCount>0) { - QStyleOptionViewItemV4 opt = option; - opt.rect.setHeight(sizeHint(option, index).height()); - m_dragModeCount--; - if (index.column()==0) { - return QStyledItemDelegate::paint(painter, opt, index); - } else { - return; - } - } - - TodoFlatModel::ItemType type = rowType(index); + Zanshin::ItemType type = (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); QStyleOptionViewItemV4 opt = option; - if (type==TodoFlatModel::FolderTodo || !isInFocus(index)) { - opt.decorationSize = QSize(1, 1); - opt.displayAlignment = Qt::AlignHCenter|Qt::AlignBottom; - opt.font.setItalic(true); - opt.font.setPointSizeF(opt.font.pointSizeF()*0.75); - - } else if (type!=TodoFlatModel::StandardTodo) { + if (type!=Zanshin::StandardTodo) { opt.decorationSize = QSize(22, 22); opt.font.setWeight(QFont::Bold); - } else if (index.parent().isValid()) { - if (index.column()==0) { - opt.rect.adjust(40, 0, 0, 0); - } + } else { if (index.row()%2==0) { opt.features|= QStyleOptionViewItemV4::Alternate; } + + if (index.column()==0) { + opt.rect.setLeft(opt.rect.left()+32); + } } if (isCompleted(index)) { @@ -115,97 +99,193 @@ QStyledItemDelegate::paint(painter, opt, index); } -void ActionListDelegate::updateEditorGeometry(QWidget *editor, - const QStyleOptionViewItem &option, - const QModelIndex &index) const +KCalCore::Todo::Ptr ActionListDelegate::todoFromIndex(const QModelIndex &index) const { - QStyledItemDelegate::updateEditorGeometry(editor, option, index); + Zanshin::ItemType type = (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); - TodoFlatModel::ItemType type = rowType(index); + if (type!=Zanshin::StandardTodo) { + return KCalCore::Todo::Ptr(); + } - if (type==TodoFlatModel::StandardTodo - && index.column()==0 - && index.parent().isValid()) { - QRect r = editor->geometry(); - r.adjust(40, 0, 0, 0); - editor->setGeometry(r); + Akonadi::Item item = index.data(Akonadi::EntityTreeModel::ItemRole).value(); + if (!item.isValid() || !item.hasPayload()) { + return KCalCore::Todo::Ptr(); } + + return item.payload(); } -bool ActionListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, - const QStyleOptionViewItem &option, - const QModelIndex &index) +bool ActionListDelegate::isCompleted(const QModelIndex &index) const { - QStyleOptionViewItemV4 opt = option; - - TodoFlatModel::ItemType type = rowType(index); + KCalCore::Todo::Ptr todo = todoFromIndex(index); - if (type==TodoFlatModel::StandardTodo - && index.column()==0 - && index.parent().isValid()) { - opt.rect.adjust(40, 0, 0, 0); + if (todo) { + return todo->isCompleted(); + } else { + return false; } - - return QStyledItemDelegate::editorEvent(event, model, opt, index); } -TodoFlatModel::ItemType ActionListDelegate::rowType(const QModelIndex &index) +bool ActionListDelegate::isOverdue(const QModelIndex &index) const { - const ActionListModel *model = qobject_cast(index.model()); + KCalCore::Todo::Ptr todo = todoFromIndex(index); - if (model==0) { - return TodoFlatModel::StandardTodo; + if (todo) { + return todo->isOverdue(); + } else { + return false; } +} - QModelIndex sourceIndex = model->mapToSource(index); - QModelIndex rowTypeIndex = sourceIndex.sibling(sourceIndex.row(), TodoFlatModel::RowType); - - QVariant value = model->sourceModel()->data(rowTypeIndex); - if (!value.isValid()) { - return TodoFlatModel::StandardTodo; +QWidget *ActionListDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (index.data(Qt::EditRole).type()==QVariant::Date) { + return new KDateEdit(parent); + } else if (index.data(Zanshin::DataTypeRole).toInt() == Zanshin::CategoryType) { + return createComboBox(m_models->categoriesComboModel(), parent, index, true); + } else if (index.data(Zanshin::DataTypeRole).toInt() == Zanshin::ProjectType) { + return createComboBox(m_models->treeComboModel(), parent, index, false); + } else { + return QStyledItemDelegate::createEditor(parent, option, index); } - - return (TodoFlatModel::ItemType)value.toInt(); } -bool ActionListDelegate::isInFocus(const QModelIndex &index) const -{ - const ActionListModel *model = qobject_cast(index.model()); +QWidget *ActionListDelegate::createComboBox(QAbstractItemModel *model, QWidget *parent, const QModelIndex &selectedIndex, bool isCategory) const +{ + ActionListComboBox *comboBox = new ActionListComboBox(parent); + comboBox->setEditable(true); + comboBox->view()->setTextElideMode(Qt::ElideNone); + + QCompleter *completer = new QCompleter(comboBox); + completer->setCompletionMode(QCompleter::PopupCompletion); + completer->setCaseSensitivity(Qt::CaseInsensitive); + + if (isCategory) { + comboBox->setAutoHidePopupEnabled(true); + QItemSelectionModel *checkModel = new QItemSelectionModel(model, comboBox); + ActionListCheckableModel *checkable = new ActionListCheckableModel(comboBox); + QStringList ancestorsCategories = selectedIndex.data(Zanshin::AncestorsCategoriesRole).value(); + checkable->setDisabledCategories(ancestorsCategories); + checkable->setSourceModel(model); + checkable->setSelectionModel(checkModel); + + QStringList categories = selectedIndex.data(Zanshin::CategoriesRole).value(); + Q_ASSERT(checkable->rowCount() == model->rowCount()); + for (int i = 0; i < checkable->rowCount(); ++i) { + QModelIndex checkIndex = checkable->index(i, 0); + QModelIndex index = model->index(i, 0); + foreach (QString item, categories) { + if (index.data(Zanshin::CategoryPathRole).toString() == item && checkIndex.flags() & Qt::ItemIsEnabled) { + checkModel->select(index, QItemSelectionModel::Toggle); + } + } + } + comboBox->setModel(checkable); + ActionListCompleterModel *completerModel = new ActionListCompleterModel(checkModel, completer); + completerModel->setSourceModel(checkable); + completer->setModel(completerModel); + ActionListCompleterView *listView = new ActionListCompleterView(comboBox); + completer->setPopup(listView); + } else { + comboBox->setModel(model); + completer->setModel(model); + comboBox->setEditText(selectedIndex.data().toString()); + } + connect(completer, SIGNAL(activated(QModelIndex)), this, SLOT(onCompleterActivated(QModelIndex))); + comboBox->setCompleter(completer); + + return comboBox; +} + +void ActionListDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + KDateEdit *dateEdit = qobject_cast(editor); + + if (dateEdit) { + dateEdit->setDate(index.data(Qt::EditRole).toDate()); + if (dateEdit->lineEdit()->text().isEmpty()) { + dateEdit->setDate(QDate::currentDate()); + } + dateEdit->lineEdit()->selectAll(); - if (model==0) { - return true; + } else { + QStyledItemDelegate::setEditorData(editor, index); } - - return model->isInFocus(index); } -bool ActionListDelegate::isCompleted(const QModelIndex &index) const +void ActionListDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const { - return index.model()->data(index.sibling(index.row(), 0), Qt::CheckStateRole).toInt()==Qt::Checked; + if (index.data(Qt::EditRole).type()==QVariant::Date) { + KDateEdit *dateEdit = static_cast(editor); + model->setData(index, dateEdit->date()); + } else if (index.data(Zanshin::DataTypeRole).toInt() == Zanshin::CategoryType) { + QComboBox *comboBox = static_cast(editor); + QStringList currentCategories = comboBox->currentText().split(", "); + model->setData(index, currentCategories); + + } else if (index.data(Zanshin::DataTypeRole).toInt() == Zanshin::ProjectType) { + QComboBox *comboBox = static_cast(editor); + if (comboBox->currentIndex() == -1) { + return; + } + QModelIndex idx = comboBox->model()->index(comboBox->currentIndex(), 0); + if (idx.isValid()) { + model->setData(index, idx.data(Zanshin::UidRole)); + } + } else { + QStyledItemDelegate::setModelData(editor, model, index); + } } -bool ActionListDelegate::isOverdue(const QModelIndex &index) const +void ActionListDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, + const QModelIndex &index) const { - const ActionListModel *model = qobject_cast(index.model()); + QStyleOptionViewItemV4 opt = option; - if (model==0) { - return false; + if (index.column()==0) { + Zanshin::ItemType type = (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); + + if (type == Zanshin::StandardTodo && index.column()==0) { + opt.rect.setLeft(opt.rect.left()+32); + } } - TodoFlatModel::ItemType type = rowType(index); + QStyledItemDelegate::updateEditorGeometry(editor, opt, index); +} - if (type==TodoFlatModel::Category) { - return false; - } +bool ActionListDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + QStyleOptionViewItemV4 opt = option; - Akonadi::Item item = model->itemForIndex(index); - const IncidencePtr incidence = item.payload(); - KCal::Todo *todo = dynamic_cast(incidence.get()); + if (index.column()==0) { + Zanshin::ItemType type = (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); - return todo->isOverdue(); + if (type == Zanshin::StandardTodo && index.column()==0) { + opt.rect.setLeft(opt.rect.left()+32); + } + } + + return QStyledItemDelegate::editorEvent(event, model, opt, index); } -void ActionListDelegate::setDragModeCount(int count) +void ActionListDelegate::onCompleterActivated(const QModelIndex &index) { - m_dragModeCount = count; + QCompleter *completer = static_cast(sender()); + QComboBox *comboBox = static_cast(completer->widget()); + + KModelIndexProxyMapper *mapper = new KModelIndexProxyMapper(comboBox->model(), index.model(), this); + QModelIndex mapperIndex = mapper->mapRightToLeft(index); + + comboBox->setCurrentIndex(mapperIndex.row()); + QVariant value = mapperIndex.data(Qt::CheckStateRole); + if (!value.isValid()) { + return; + } + Qt::CheckState state = (static_cast(value.toInt()) == Qt::Checked + ? Qt::Unchecked : Qt::Checked); + comboBox->model()->setData(mapperIndex, state, Qt::CheckStateRole); } diff -Nru zanshin-0.1+svn1006410/src/actionlistdelegate.h zanshin-0.2.0/src/actionlistdelegate.h --- zanshin-0.1+svn1006410/src/actionlistdelegate.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/actionlistdelegate.h 2011-11-25 15:00:52.000000000 +0000 @@ -26,18 +26,18 @@ #include -#include "todoflatmodel.h" +#include + +class ModelStack; class ActionListDelegate : public QStyledItemDelegate { Q_OBJECT public: - ActionListDelegate(QObject *parent = 0); + ActionListDelegate(ModelStack *models, QObject *parent = 0); virtual ~ActionListDelegate(); - void setDragModeCount(int count); - virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; @@ -45,21 +45,29 @@ const QStyleOptionViewItem &option, const QModelIndex &index) const; - virtual void updateEditorGeometry(QWidget *editor, - const QStyleOptionViewItem &option, - const QModelIndex &index) const; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; +protected: virtual bool editorEvent(QEvent *event, QAbstractItemModel *model, - const QStyleOptionViewItem &option, - const QModelIndex &index); + const QStyleOptionViewItem &option, const QModelIndex &index); + + KCalCore::Todo::Ptr todoFromIndex(const QModelIndex &index) const; + +private slots: + void onCompleterActivated(const QModelIndex &index); - static TodoFlatModel::ItemType rowType(const QModelIndex &index); private: - bool isInFocus(const QModelIndex &index) const; bool isCompleted(const QModelIndex &index) const; bool isOverdue(const QModelIndex &index) const; + QWidget *createComboBox(QAbstractItemModel *model, QWidget *parent, const QModelIndex &selectedIndex, bool isFiltered) const; - mutable int m_dragModeCount; + ModelStack *m_models; }; #endif diff -Nru zanshin-0.1+svn1006410/src/actionlisteditor.cpp zanshin-0.2.0/src/actionlisteditor.cpp --- zanshin-0.1+svn1006410/src/actionlisteditor.cpp 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/actionlisteditor.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Zanshin Todo. - Copyright 2008-2009 Kevin Ottens + Copyright 2008-2010 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -23,16 +23,14 @@ #include "actionlisteditor.h" -#include -#include -#include - -#include - -#include +#include #include #include +#include +#include +#include +#include #include #include #include @@ -43,41 +41,67 @@ #include #include #include +#include +#include +#include "actionlistcombobox.h" #include "actionlistdelegate.h" -#include "actionlistmodel.h" -#include "actionlistview.h" -#include "contextsmodel.h" -#include "globalmodel.h" -#include "projectsmodel.h" +#include "actionlisteditorpage.h" +#include "categorymanager.h" +#include "globaldefs.h" +#include "modelstack.h" #include "quickselectdialog.h" -#include "todocategoriesmodel.h" -#include "todoflatmodel.h" -#include "todotreemodel.h" +#include "todohelpers.h" -typedef boost::shared_ptr IncidencePtr; - -ActionListEditor::ActionListEditor(QWidget *parent, KActionCollection *ac) - : QWidget(parent) +class TodoCollectionsProxyModel : public QSortFilterProxyModel +{ +public: + TodoCollectionsProxyModel(QObject *parent = 0) + : QSortFilterProxyModel(parent) + { + setDynamicSortFilter(true); + } + + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const + { + QModelIndex sourceChild = sourceModel()->index(sourceRow, 0, sourceParent); + Akonadi::Collection col = sourceChild.data(Akonadi::EntityTreeModel::CollectionRole).value(); + + return col.isValid() + && col.contentMimeTypes().contains("application/x-vnd.akonadi.calendar.todo") + && (col.rights() & (Akonadi::Collection::CanChangeItem|Akonadi::Collection::CanCreateItem)); + } +}; + +ActionListEditor::ActionListEditor(ModelStack *models, + QItemSelectionModel *projectSelection, + QItemSelectionModel *categoriesSelection, + KActionCollection *ac, + QWidget *parent) + : QWidget(parent), + m_projectSelection(projectSelection), + m_categoriesSelection(categoriesSelection), + m_models(models), + m_defaultCollectionId(-1) { setLayout(new QVBoxLayout(this)); - m_view = new ActionListView(this); - layout()->addWidget(m_view); - m_model = new ActionListModel(this); - m_view->setModel(m_model); - m_model->setSourceModel(GlobalModel::todoFlat()); - - connect(m_view->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), - this, SLOT(updateActions(QModelIndex))); - - m_view->header()->setSortIndicatorShown(true); - m_view->setSortingEnabled(true); - m_view->sortByColumn(0, Qt::AscendingOrder); + m_stack = new QStackedWidget(this); + layout()->addWidget(m_stack); + layout()->setContentsMargins(0, 0, 0, 0); + + connect(projectSelection, SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(onSideBarSelectionChanged(QModelIndex))); + connect(categoriesSelection, SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(onSideBarSelectionChanged(QModelIndex))); + + models->setItemTreeSelectionModel(projectSelection); + models->setItemCategorySelectionModel(categoriesSelection); QWidget *bottomBar = new QWidget(this); layout()->addWidget(bottomBar); bottomBar->setLayout(new QHBoxLayout(bottomBar)); + bottomBar->layout()->setContentsMargins(0, 0, 0, 0); m_addActionEdit = new KLineEdit(bottomBar); m_addActionEdit->installEventFilter(this); @@ -89,18 +113,131 @@ setupActions(ac); + createPage(models->treeSelectionModel(), models, Zanshin::ProjectMode); + createPage(models->categoriesSelectionModel(), models, Zanshin::CategoriesMode); + + m_comboBox = new ActionListComboBox(bottomBar); + m_comboBox->view()->setTextElideMode(Qt::ElideLeft); + m_comboBox->setMinimumContentsLength(20); + m_comboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + + connect(m_comboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(onComboBoxChanged())); + + KDescendantsProxyModel *descendantProxyModel = new KDescendantsProxyModel(m_comboBox); + descendantProxyModel->setSourceModel(models->collectionsModel()); + descendantProxyModel->setDisplayAncestorData(true); + + TodoCollectionsProxyModel *todoColsModel = new TodoCollectionsProxyModel(m_comboBox); + todoColsModel->setSourceModel(descendantProxyModel); + + KConfigGroup config(KGlobal::config(), "General"); + m_defaultCollectionId = config.readEntry("defaultCollection", -1); + + if (m_defaultCollectionId > 0) { + if (!selectDefaultCollection(todoColsModel, QModelIndex(), + 0, todoColsModel->rowCount()-1)) { + connect(todoColsModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(onRowInsertedInComboBox(QModelIndex,int,int))); + } + } + + m_comboBox->setModel(todoColsModel); + bottomBar->layout()->addWidget(m_comboBox); + QToolBar *toolBar = new QToolBar(bottomBar); toolBar->setIconSize(QSize(16, 16)); bottomBar->layout()->addWidget(toolBar); toolBar->addAction(m_cancelAdd); m_cancelAdd->setEnabled(false); - updateActions(QModelIndex()); + + updateActions(); + setMode(Zanshin::ProjectMode); + onComboBoxChanged(); +} + +void ActionListEditor::setMode(Zanshin::ApplicationMode mode) +{ + switch (mode) { + case Zanshin::ProjectMode: + m_stack->setCurrentIndex(0); + onSideBarSelectionChanged(m_projectSelection->currentIndex()); + break; + case Zanshin::CategoriesMode: + m_stack->setCurrentIndex(1); + onSideBarSelectionChanged(m_categoriesSelection->currentIndex()); + break; + } } -ActionListView *ActionListEditor::view() const +void ActionListEditor::onSideBarSelectionChanged(const QModelIndex &index) { - return m_view; + int type = index.data(Zanshin::ItemTypeRole).toInt(); + + m_comboBox->setVisible(type == Zanshin::Inbox + || type == Zanshin::Category + || type == Zanshin::CategoryRoot); + + currentPage()->setCollectionColumnHidden(type!=Zanshin::Inbox); + + currentPage()->selectFirstIndex(); +} + +void ActionListEditor::onComboBoxChanged() +{ + QModelIndex collectionIndex = m_comboBox->model()->index( m_comboBox->currentIndex(), 0 ); + Akonadi::Collection collection = collectionIndex.data(Akonadi::EntityTreeModel::CollectionRole).value(); + + KConfigGroup config(KGlobal::config(), "General"); + config.writeEntry("defaultCollection", QString::number(collection.id())); + config.sync(); + + for (int i=0; icount(); i++) { + page(i)->setDefaultCollection(collection); + } +} + +bool ActionListEditor::selectDefaultCollection(QAbstractItemModel *model, const QModelIndex &parent, int begin, int end) +{ + for (int i = begin; i <= end; i++) { + QModelIndex collectionIndex = model->index(i, 0, parent); + Akonadi::Collection collection = collectionIndex.data(Akonadi::EntityTreeModel::CollectionRole).value(); + if (collection.id() == m_defaultCollectionId) { + m_comboBox->setCurrentIndex(i); + m_defaultCollectionId = -1; + return true; + } + } + return false; +} + +void ActionListEditor::onRowInsertedInComboBox(const QModelIndex &parent, int begin, int end) +{ + QAbstractItemModel *model = static_cast(sender()); + if (selectDefaultCollection(model, parent, begin, end)) { + disconnect(this, SLOT(onRowInsertedInComboBox(QModelIndex,int,int))); + } +} + +void ActionListEditor::createPage(QAbstractItemModel *model, ModelStack *models, Zanshin::ApplicationMode mode) +{ + QList contextActions; + contextActions << m_add + << m_remove + << m_move + << m_promote; + + if (mode==Zanshin::CategoriesMode) { + contextActions << m_dissociate; + } + + ActionListEditorPage *page = new ActionListEditorPage(model, models, mode, contextActions, m_stack); + + connect(page->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(updateActions())); + + m_stack->addWidget(page); } void ActionListEditor::setupActions(KActionCollection *ac) @@ -108,10 +245,12 @@ m_add = ac->addAction("editor_add_action", this, SLOT(focusActionEdit())); m_add->setText(i18n("New Action")); m_add->setIcon(KIcon("list-add")); - m_add->setShortcut(Qt::CTRL | Qt::Key_N); + if (qgetenv("ZANSHIN_KONTACT_PLUGIN").isEmpty()) { + m_add->setShortcut(Qt::CTRL | Qt::Key_N); + } - m_cancelAdd = ac->addAction("editor_cancel_action", m_view, SLOT(setFocus())); - connect(m_cancelAdd, SIGNAL(activated()), m_addActionEdit, SLOT(clear())); + m_cancelAdd = ac->addAction("editor_cancel_action", m_stack, SLOT(setFocus())); + connect(m_cancelAdd, SIGNAL(triggered()), m_addActionEdit, SLOT(clear())); m_cancelAdd->setText(i18n("Cancel New Action")); m_cancelAdd->setIcon(KIcon("edit-undo")); m_cancelAdd->setShortcut(Qt::Key_Escape); @@ -124,17 +263,78 @@ m_move = ac->addAction("editor_move_action", this, SLOT(onMoveAction())); m_move->setText(i18n("Move Action...")); m_move->setShortcut(Qt::Key_M); -} -void ActionListEditor::updateActions(const QModelIndex &index) -{ - if (!index.isValid()) { - m_remove->setEnabled(false); - m_move->setEnabled(false); + m_promote = ac->addAction("editor_promote_action", this, SLOT(onPromoteAction())); + m_promote->setText(i18n("Promote Action as Project")); + m_promote->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_P); + + m_dissociate = ac->addAction("editor_dissociate_action", this, SLOT(onDissociateAction())); + m_dissociate->setText(i18n("Dissociate Action from Context")); + m_dissociate->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_D); +} + +void ActionListEditor::updateActions() +{ + const QItemSelectionModel * const itemSelectionModel = currentPage()->selectionModel(); + const QModelIndex index = itemSelectionModel->currentIndex(); + int type = index.data(Zanshin::ItemTypeRole).toInt(); + + Akonadi::Collection collection; + if ( type==Zanshin::Collection ) { + collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value(); + } else if (type==Zanshin::Category) { + QModelIndex collectionIndex = m_comboBox->model()->index( m_comboBox->currentIndex(), 0 ); + collection = collectionIndex.data(Akonadi::EntityTreeModel::CollectionRole).value(); + } else if (type==Zanshin::StandardTodo) { + QModelIndex parent = index; + int parentType = type; + while (parent.isValid() && parentType==Zanshin::StandardTodo) { + parent = parent.sibling(parent.row()-1, parent.column()); + parentType = parent.data(Zanshin::ItemTypeRole).toInt(); + } + + if (parentType!=Zanshin::ProjectTodo) { + QModelIndex collectionIndex = m_comboBox->model()->index( m_comboBox->currentIndex(), 0 ); + collection = collectionIndex.data(Akonadi::EntityTreeModel::CollectionRole).value(); + } else { + collection = index.data(Akonadi::EntityTreeModel::ParentCollectionRole).value(); + } + } else { - m_remove->setEnabled(true); - m_move->setEnabled(true); + // We use ParentCollectionRole instead of Akonadi::Item::parentCollection() because the + // information about the rights is not valid on retrieved items. + collection = index.data(Akonadi::EntityTreeModel::ParentCollectionRole).value(); } + + + m_add->setEnabled(index.isValid() + && (collection.rights() & Akonadi::Collection::CanCreateItem) + && (type==Zanshin::ProjectTodo + || type==Zanshin::Category + || type==Zanshin::Inbox + || type==Zanshin::StandardTodo)); + + m_addActionEdit->setEnabled(m_add->isEnabled()); + + m_remove->setEnabled(index.isValid() + && (collection.rights() & Akonadi::Collection::CanDeleteItem) + && ((type==Zanshin::StandardTodo) + || type==Zanshin::ProjectTodo + || type==Zanshin::Category)); + m_move->setEnabled(index.isValid() + && (collection.rights() & Akonadi::Collection::CanDeleteItem) + && (type==Zanshin::StandardTodo + || type==Zanshin::Category + || type==Zanshin::ProjectTodo)); + + m_promote->setEnabled(index.isValid() + && (collection.rights() & Akonadi::Collection::CanChangeItem) + && type==Zanshin::StandardTodo + && itemSelectionModel->selectedRows().size() == 1); + + m_dissociate->setEnabled(index.isValid() + && (collection.rights() & Akonadi::Collection::CanDeleteItem) + && type==Zanshin::StandardTodo); } void ActionListEditor::onAddActionRequested() @@ -142,198 +342,154 @@ QString summary = m_addActionEdit->text().trimmed(); m_addActionEdit->setText(QString()); - if (summary.isEmpty()) return; - - QModelIndex current = m_model->mapToSource(m_view->currentIndex()); - int type = current.sibling(current.row(), TodoFlatModel::RowType).data().toInt(); - if (type==TodoFlatModel::StandardTodo) { - current = current.parent(); - } - - QString parentRemoteId; - if (m_model->sourceModel()==GlobalModel::todoTree()) { - QModelIndex parentIndex = current.sibling(current.row(), TodoFlatModel::RemoteId); - parentRemoteId = GlobalModel::todoTree()->data(parentIndex).toString(); - } - - QString category; - if (m_model->sourceModel()==GlobalModel::todoCategories()) { - QModelIndex categoryIndex = current.sibling(current.row(), TodoFlatModel::Summary); - category = GlobalModel::todoCategories()->data(categoryIndex).toString(); - } - - KCal::Todo *todo = new KCal::Todo(); - todo->setSummary(summary); - if (!parentRemoteId.isEmpty()) { - todo->setRelatedToUid(parentRemoteId); - } - if (!category.isEmpty()) { - todo->setCategories(category); - } - - IncidencePtr incidence(todo); - Akonadi::Item item; - item.setMimeType("application/x-vnd.akonadi.calendar.todo"); - item.setPayload(incidence); - - Akonadi::Collection collection = GlobalModel::todoFlat()->collection(); - Akonadi::ItemCreateJob *job = new Akonadi::ItemCreateJob(item, collection); - job->start(); + currentPage()->addNewTodo(summary); } void ActionListEditor::onRemoveAction() { - QModelIndex current = m_view->currentIndex(); + QModelIndexList currentIndexes = currentPage()->selectionModel()->selectedRows(); - if (m_model->rowCount(current)>0) { + if (currentIndexes.isEmpty()) { return; } - Akonadi::Item item; - QAbstractItemModel *source = m_model->sourceModel(); - - TodoFlatModel *flat = dynamic_cast(source); - if (flat != 0) { - item = flat->itemForIndex(m_model->mapToSource(current)); + QModelIndexList currentProjects; + QModelIndexList currentCategories; + QModelIndexList currentTodos; + + foreach (const QModelIndex &index, currentIndexes) { + const int type = index.data(Zanshin::ItemTypeRole).toInt(); + if (type==Zanshin::ProjectTodo) { + currentProjects << index; + } else if (type==Zanshin::Category) { + currentCategories << index; + } else if (type==Zanshin::StandardTodo) { + currentTodos << index; + } } - TodoTreeModel *tree = dynamic_cast(source); - if (tree != 0) { - item = tree->itemForIndex(m_model->mapToSource(current)); - } + // Remove todos and projects already present in selected projects + if (!currentProjects.isEmpty()) { + QStringList projectUidList; + foreach (const QModelIndex project, currentProjects) { + projectUidList << project.data(Zanshin::UidRole).toString(); + } - TodoCategoriesModel *categories = dynamic_cast(source); - if (categories != 0) { - item = categories->itemForIndex(m_model->mapToSource(current)); - } + QSet projects = QSet::fromList(projectUidList); - if (!item.isValid()) { - return; + foreach (const QModelIndex project, currentProjects) { + QSet ancestors = QSet::fromList(project.data(Zanshin::AncestorsUidRole).toStringList()); + if (!ancestors.intersect(projects).isEmpty()) { + currentProjects.removeOne(project); + } + } + foreach (const QModelIndex todo, currentTodos) { + QSet ancestors = QSet::fromList(todo.data(Zanshin::AncestorsUidRole).toStringList()); + if (!ancestors.intersect(projects).isEmpty()) { + currentTodos.removeOne(todo); + } + } } - new Akonadi::ItemDeleteJob(item, this); -} - -void ActionListEditor::onMoveAction() -{ - QModelIndex current = m_view->currentIndex(); - - if (m_model->rowCount(current)>0) { - return; - } + // Remove categories if the parent is also in the list + if (!currentCategories.isEmpty()) { + QStringList categoryList; + foreach (const QModelIndex project, currentCategories) { + categoryList << project.data(Qt::EditRole).toString(); + } - QAbstractItemModel *source = m_model->sourceModel(); - QModelIndex movedIndex; + QSet categories = QSet::fromList(categoryList); - TodoFlatModel *flat = dynamic_cast(source); - if (flat != 0) { - movedIndex = m_model->mapToSource(current); + foreach (const QModelIndex category, currentCategories) { + QStringList pathList = category.data(Zanshin::CategoryPathRole).toString().split(CategoryManager::pathSeparator()); + pathList.removeLast(); + QSet ancestors = QSet::fromList(pathList); + if (!ancestors.intersect(categories).isEmpty()) { + currentCategories.removeOne(category); + } + } } - TodoTreeModel *tree = dynamic_cast(source); - if (tree != 0) { - movedIndex = m_model->mapToSource(current); + if (!currentProjects.isEmpty()) { + TodoHelpers::removeProjects(this, currentProjects); } - TodoCategoriesModel *categories = dynamic_cast(source); - if (categories != 0) { - movedIndex = m_model->mapToSource(current); + if (!currentCategories.isEmpty()) { + CategoryManager::instance().removeCategories(this, currentCategories); } - if (!movedIndex.isValid()) { - return; + if (!currentTodos.isEmpty()) { + foreach (QModelIndex index, currentTodos) { + currentPage()->removeTodo(index); + } } +} - QuickSelectDialog::Mode mode = QuickSelectDialog::ProjectMode; - if (m_model->mode()==ActionListModel::NoContextMode - || m_model->mode()==ActionListModel::ContextMode) { - mode = QuickSelectDialog::ContextMode; +void ActionListEditor::onMoveAction() +{ + QAbstractItemModel *model; + QModelIndex currentSelection; + if (currentPage()->mode()==Zanshin::ProjectMode) { + model = m_models->treeSideBarModel(); + currentSelection = m_projectSelection->currentIndex(); + } else { + model = m_models->categoriesSideBarModel(); + currentSelection = m_categoriesSelection->currentIndex(); } - QuickSelectDialog dlg(this, mode, + QuickSelectDialog dlg(this, model, currentPage()->mode(), QuickSelectDialog::MoveAction); if (dlg.exec()==QDialog::Accepted) { QString selectedId = dlg.selectedId(); - if (mode==QuickSelectDialog::ProjectMode) { - QModelIndex index = movedIndex.sibling(movedIndex.row(), TodoFlatModel::ParentRemoteId); - source->setData(index, selectedId); - } else { - QModelIndex index = movedIndex.sibling(movedIndex.row(), TodoFlatModel::Categories); - source->setData(index, selectedId); + QModelIndex index = dlg.selectedIndex(); + + QModelIndexList list = currentPage()->selectionModel()->selectedRows(); + if (currentSelection.isValid() && !list.isEmpty()) { + KModelIndexProxyMapper mapper(currentSelection.model(), list.first().model()); + foreach (QModelIndex current, list) { + if (!current.isValid()) { + return; + } + + if (currentPage()->mode()==Zanshin::ProjectMode) { + TodoHelpers::moveTodoToProject(current, selectedId, dlg.selectedType(), dlg.collection()); + } else { + int type = current.data(Zanshin::ItemTypeRole).toInt(); + QString categoryPath = current.data(Zanshin::CategoryPathRole).toString(); + if (type==Zanshin::Category) { + CategoryManager::instance().moveCategory(categoryPath, selectedId, dlg.selectedType()); + } else { + CategoryManager::instance().moveTodoToCategory(current, selectedId, dlg.selectedType()); + } + } + } } } } -void ActionListEditor::showNoProjectInbox() +void ActionListEditor::onPromoteAction() { - delete m_model; - m_model = new ActionListModel(this); - - m_model->setSourceModel(GlobalModel::todoFlat()); - m_model->setMode(ActionListModel::NoProjectMode); - - m_view->setModel(m_model); - m_view->setCurrentIndex(m_model->index(0, 0)); - updateActions(QModelIndex()); - connect(m_view->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), - this, SLOT(updateActions(QModelIndex))); -} - -void ActionListEditor::focusOnProject(const QModelIndex &index) -{ - delete m_model; - m_model = new ActionListModel(this); - - m_model->setSourceModel(GlobalModel::todoTree()); - m_model->setMode(ActionListModel::ProjectMode); - QModelIndex focusIndex = GlobalModel::projects()->mapToSource(index); - m_model->setSourceFocusIndex(focusIndex); + QModelIndex currentIndex = currentPage()->selectionModel()->currentIndex(); - m_view->setModel(m_model); - if (focusIndex.isValid()) { - m_view->setCurrentIndex(m_model->mapFromSource(focusIndex)); - } else { - m_view->setCurrentIndex(m_model->index(0, 0)); + if (!currentIndex.isValid()) { + return; } - updateActions(QModelIndex()); - connect(m_view->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), - this, SLOT(updateActions(QModelIndex))); -} -void ActionListEditor::showNoContextInbox() -{ - delete m_model; - m_model = new ActionListModel(this); + int type = currentIndex.data(Zanshin::ItemTypeRole).toInt(); - m_model->setSourceModel(GlobalModel::todoFlat()); - m_model->setMode(ActionListModel::NoContextMode); + if (type!=Zanshin::StandardTodo) { + return; + } - m_view->setModel(m_model); - m_view->setCurrentIndex(m_model->index(0, 0)); - updateActions(QModelIndex()); - connect(m_view->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), - this, SLOT(updateActions(QModelIndex))); + TodoHelpers::promoteTodo(currentIndex); } -void ActionListEditor::focusOnContext(const QModelIndex &index) +void ActionListEditor::onDissociateAction() { - delete m_model; - m_model = new ActionListModel(this); - - m_model->setSourceModel(GlobalModel::todoCategories()); - m_model->setMode(ActionListModel::ContextMode); - QModelIndex focusIndex = GlobalModel::contexts()->mapToSource(index); - m_model->setSourceFocusIndex(focusIndex); - - m_view->setModel(m_model); - if (focusIndex.isValid()) { - m_view->setCurrentIndex(m_model->mapFromSource(focusIndex)); - } else { - m_view->setCurrentIndex(m_model->index(0, 0)); + QModelIndexList currentIndexes = currentPage()->selectionModel()->selectedRows(); + foreach (QModelIndex index, currentIndexes) { + currentPage()->dissociateTodo(index); } - updateActions(QModelIndex()); - connect(m_view->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), - this, SLOT(updateActions(QModelIndex))); } void ActionListEditor::focusActionEdit() @@ -358,3 +514,25 @@ return QWidget::eventFilter(watched, event); } + +void ActionListEditor::saveColumnsState(KConfigGroup &config) const +{ + page(0)->saveColumnsState(config, "ProjectHeaderState"); + page(1)->saveColumnsState(config, "CategoriesHeaderState"); +} + +void ActionListEditor::restoreColumnsState(const KConfigGroup &config) +{ + page(0)->restoreColumnsState(config, "ProjectHeaderState"); + page(1)->restoreColumnsState(config, "CategoriesHeaderState"); +} + +ActionListEditorPage *ActionListEditor::currentPage() const +{ + return static_cast(m_stack->currentWidget()); +} + +ActionListEditorPage *ActionListEditor::page(int idx) const +{ + return static_cast(m_stack->widget(idx)); +} diff -Nru zanshin-0.1+svn1006410/src/actionlisteditor.h zanshin-0.2.0/src/actionlisteditor.h --- zanshin-0.1+svn1006410/src/actionlisteditor.h 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/actionlisteditor.h 2011-11-25 15:00:52.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Zanshin Todo. - Copyright 2008-2009 Kevin Ottens + Copyright 2008-2010 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -27,50 +27,74 @@ #include #include -class ActionListModel; -class ActionListView; +#include "globaldefs.h" + +class ActionListEditorPage; class KAction; class KActionCollection; +class KConfigGroup; class KLineEdit; +class QAbstractItemModel; +class QComboBox; +class QItemSelectionModel; +class QStackedWidget; +class ModelStack; class ActionListEditor : public QWidget { Q_OBJECT public: - ActionListEditor(QWidget *parent, KActionCollection *ac); - - ActionListView *view() const; + ActionListEditor(ModelStack *models, + QItemSelectionModel *projectSelection, + QItemSelectionModel *categoriesSelection, + KActionCollection *ac, QWidget *parent=0); -public slots: - void showNoProjectInbox(); - void focusOnProject(const QModelIndex &index); + void setMode(Zanshin::ApplicationMode mode); - void showNoContextInbox(); - void focusOnContext(const QModelIndex &index); + void saveColumnsState(KConfigGroup &config) const; + void restoreColumnsState(const KConfigGroup &config); protected: virtual bool eventFilter(QObject *watched, QEvent *event); private slots: - void updateActions(const QModelIndex &index); + void updateActions(); void onAddActionRequested(); void onRemoveAction(); void onMoveAction(); + void onPromoteAction(); + void onDissociateAction(); void focusActionEdit(); + void onSideBarSelectionChanged(const QModelIndex &index); + void onComboBoxChanged(); + void onRowInsertedInComboBox(const QModelIndex &index, int start, int end); private: + void createPage(QAbstractItemModel *model, ModelStack *models, Zanshin::ApplicationMode); void setupActions(KActionCollection *ac); + bool selectDefaultCollection(QAbstractItemModel *model, const QModelIndex &parent, int begin, int end); - ActionListView *m_view; - KLineEdit *m_addActionEdit; + ActionListEditorPage *currentPage() const; + ActionListEditorPage *page(int idx) const; - ActionListModel *m_model; + QStackedWidget *m_stack; + QItemSelectionModel *m_projectSelection; + QItemSelectionModel *m_categoriesSelection; + + KLineEdit *m_addActionEdit; + QComboBox *m_comboBox; KAction *m_add; KAction *m_cancelAdd; KAction *m_remove; KAction *m_move; + KAction *m_promote; + KAction *m_dissociate; + + ModelStack *m_models; + + qint64 m_defaultCollectionId; }; #endif diff -Nru zanshin-0.1+svn1006410/src/actionlisteditorpage.cpp zanshin-0.2.0/src/actionlisteditorpage.cpp --- zanshin-0.1+svn1006410/src/actionlisteditorpage.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/actionlisteditorpage.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,445 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "actionlisteditorpage.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "actionlistdelegate.h" +#include "categorymanager.h" +#include "globaldefs.h" +#include "todotreeview.h" +#include "todohelpers.h" + +static const char *_z_defaultColumnStateCache = "AAAA/wAAAAAAAAABAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAvAAAAAFAQEAAQAAAAAAAAAAAAAAAGT/////AAAAgQAAAAAAAAAFAAABNgAAAAEAAAAAAAAAlAAAAAEAAAAAAAAAjQAAAAEAAAAAAAAAcgAAAAEAAAAAAAAAJwAAAAEAAAAA"; + +class GroupLabellingProxyModel : public QSortFilterProxyModel +{ +public: + GroupLabellingProxyModel(QObject *parent = 0) + : QSortFilterProxyModel(parent) + { + setDynamicSortFilter(true); + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const + { + if (role!=Qt::DisplayRole || index.column()!=0) { + return QSortFilterProxyModel::data(index, role); + } else { + int type = index.data(Zanshin::ItemTypeRole).toInt(); + + if (type!=Zanshin::ProjectTodo + && type!=Zanshin::Category) { + return QSortFilterProxyModel::data(index, role); + + } else { + QString display = QSortFilterProxyModel::data(index, role).toString(); + + QModelIndex currentIndex = mapToSource(index.parent()); + type = currentIndex.data(Zanshin::ItemTypeRole).toInt(); + + while (type==Zanshin::ProjectTodo + || type==Zanshin::Category) { + display = currentIndex.data().toString() + ": " + display; + + currentIndex = currentIndex.parent(); + type = currentIndex.data(Zanshin::ItemTypeRole).toInt(); + } + + return display; + } + } + } +}; + +class GroupSortingProxyModel : public QSortFilterProxyModel +{ +public: + GroupSortingProxyModel(QObject *parent = 0) + : QSortFilterProxyModel(parent) + { + setDynamicSortFilter(true); + setFilterCaseSensitivity(Qt::CaseInsensitive); + } + + bool lessThan(const QModelIndex &left, const QModelIndex &right) const + { + int leftType = left.data(Zanshin::ItemTypeRole).toInt(); + int rightType = right.data(Zanshin::ItemTypeRole).toInt(); + + return leftType==Zanshin::Inbox + || (leftType==Zanshin::CategoryRoot && rightType!=Zanshin::Inbox) + || (leftType==Zanshin::Collection && rightType!=Zanshin::Inbox) + || (leftType==Zanshin::StandardTodo && rightType!=Zanshin::StandardTodo) + || (leftType==Zanshin::ProjectTodo && rightType==Zanshin::Collection) + || (leftType == rightType && QSortFilterProxyModel::lessThan(left, right)); + } +}; + +class TypeFilterProxyModel : public QSortFilterProxyModel +{ +public: + TypeFilterProxyModel(GroupSortingProxyModel *sorting, QObject *parent = 0) + : QSortFilterProxyModel(parent), m_sorting(sorting) + { + setDynamicSortFilter(true); + setFilterCaseSensitivity(Qt::CaseInsensitive); + } + + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const + { + QModelIndex sourceChild = sourceModel()->index(sourceRow, 0, sourceParent); + int type = sourceChild.data(Zanshin::ItemTypeRole).toInt(); + + QSize sizeHint = sourceChild.data(Qt::SizeHintRole).toSize(); + + return type!=Zanshin::Collection + && type!=Zanshin::CategoryRoot + && !sizeHint.isNull(); // SelectionProxyModel uses the null size for items we shouldn't display + } + + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) + { + m_sorting->sort(column, order); + } + +private: + GroupSortingProxyModel *m_sorting; +}; + + +class ActionListEditorView : public TodoTreeView +{ +public: + ActionListEditorView(QWidget *parent = 0) + : TodoTreeView(parent) { } + +protected: + virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) + { + QModelIndex index = currentIndex(); + + if (index.isValid() && modifiers==Qt::NoModifier) { + QModelIndex newIndex; + int newColumn = index.column(); + + switch (cursorAction) { + case MoveLeft: + do { + newColumn--; + } while (isColumnHidden(newColumn) + && newColumn>=0); + break; + + case MoveRight: + do { + newColumn++; + } while (isColumnHidden(newColumn) + && newColumncount()); + break; + + default: + return Akonadi::EntityTreeView::moveCursor(cursorAction, modifiers); + } + + newIndex = index.sibling(index.row(), newColumn); + + if (newIndex.isValid()) { + return newIndex; + } + } + + return Akonadi::EntityTreeView::moveCursor(cursorAction, modifiers); + } +}; + +class ActionListEditorModel : public KDescendantsProxyModel +{ +public: + ActionListEditorModel(QObject *parent = 0) + : KDescendantsProxyModel(parent) + { + } + + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) + { + if (!sourceModel()) { + return QAbstractProxyModel::dropMimeData(data, action, row, column, parent); + } + QModelIndex sourceParent = mapToSource(parent); + return sourceModel()->dropMimeData(data, action, row, column, sourceParent); + } +}; + +ActionListEditorPage::ActionListEditorPage(QAbstractItemModel *model, + ModelStack *models, + Zanshin::ApplicationMode mode, + const QList &contextActions, + QWidget *parent) + : QWidget(parent), m_mode(mode) +{ + setLayout(new QVBoxLayout(this)); + layout()->setContentsMargins(0, 0, 0, 0); + + m_treeView = new ActionListEditorView(this); + + GroupLabellingProxyModel *labelling = new GroupLabellingProxyModel(this); + labelling->setSourceModel(model); + + GroupSortingProxyModel *sorting = new GroupSortingProxyModel(this); + sorting->setSourceModel(labelling); + + ActionListEditorModel *descendants = new ActionListEditorModel(this); + descendants->setSourceModel(sorting); + + TypeFilterProxyModel *filter = new TypeFilterProxyModel(sorting, this); + filter->setSourceModel(descendants); + + m_treeView->setModel(filter); + m_treeView->setItemDelegate(new ActionListDelegate(models, m_treeView)); + + m_treeView->header()->setSortIndicatorShown(true); + m_treeView->setSortingEnabled(true); + m_treeView->sortByColumn(0, Qt::AscendingOrder); + + m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_treeView->setItemsExpandable(false); + m_treeView->setRootIsDecorated(false); + m_treeView->setEditTriggers(m_treeView->editTriggers() | QAbstractItemView::DoubleClicked); + + connect(m_treeView->model(), SIGNAL(modelReset()), + m_treeView, SLOT(expandAll())); + connect(m_treeView->model(), SIGNAL(layoutChanged()), + m_treeView, SLOT(expandAll())); + connect(m_treeView->model(), SIGNAL(rowsInserted(QModelIndex,int,int)), + m_treeView, SLOT(expandAll())); + + layout()->addWidget(m_treeView); + + QTimer::singleShot(0, this, SLOT(onAutoHideColumns())); + + connect(m_treeView->header(), SIGNAL(sectionResized(int,int,int)), + this, SLOT(onColumnsGeometryChanged())); + + m_treeView->setContextMenuPolicy(Qt::ActionsContextMenu); + m_treeView->addActions(contextActions); +} + +QItemSelectionModel *ActionListEditorPage::selectionModel() const +{ + return m_treeView->selectionModel(); +} + +void ActionListEditorPage::saveColumnsState(KConfigGroup &config, const QString &key) const +{ + config.writeEntry(key+"/Normal", m_normalStateCache.toBase64()); + config.writeEntry(key+"/NoCollection", m_noCollectionStateCache.toBase64()); +} + +void ActionListEditorPage::restoreColumnsState(const KConfigGroup &config, const QString &key) +{ + if (config.hasKey(key+"/Normal")) { + m_normalStateCache = QByteArray::fromBase64(config.readEntry(key+"/Normal", QByteArray())); + } else { + m_normalStateCache = QByteArray::fromBase64(_z_defaultColumnStateCache); + } + + if (config.hasKey(key+"/NoCollection")) { + m_noCollectionStateCache = QByteArray::fromBase64(config.readEntry(key+"/NoCollection", QByteArray())); + } + + if (!m_treeView->isColumnHidden(4)) { + m_treeView->header()->restoreState(m_normalStateCache); + } else { + m_treeView->header()->restoreState(m_noCollectionStateCache); + } +} + +void ActionListEditorPage::addNewTodo(const QString &summary) +{ + if (summary.isEmpty()) return; + + QModelIndex current = m_treeView->selectionModel()->currentIndex(); + + if (!current.isValid()) { + kWarning() << "Oops, nothing selected in the list!"; + return; + } + + int type = current.data(Zanshin::ItemTypeRole).toInt(); + + while (current.isValid() && type==Zanshin::StandardTodo) { + current = current.sibling(current.row()-1, current.column()); + type = current.data(Zanshin::ItemTypeRole).toInt(); + } + + Akonadi::Collection collection; + QString parentUid; + QString category; + + switch (type) { + case Zanshin::StandardTodo: + kFatal() << "Can't possibly happen!"; + break; + + case Zanshin::ProjectTodo: + parentUid = current.data(Zanshin::UidRole).toString(); + collection = current.data(Akonadi::EntityTreeModel::ParentCollectionRole).value(); + break; + + case Zanshin::Collection: + collection = current.data(Akonadi::EntityTreeModel::CollectionRole).value(); + break; + + case Zanshin::Category: + category = current.data(Zanshin::CategoryPathRole).toString(); + // fallthrough + case Zanshin::Inbox: + case Zanshin::CategoryRoot: + collection = m_defaultCollection; + break; + } + + TodoHelpers::addTodo(summary, parentUid, category, collection); +} + +void ActionListEditorPage::removeCurrentTodo() +{ + QModelIndex current = m_treeView->selectionModel()->currentIndex(); + removeTodo(current); +} + +void ActionListEditorPage::removeTodo(const QModelIndex ¤t) +{ + int type = current.data(Zanshin::ItemTypeRole).toInt(); + + if (!current.isValid() || type!=Zanshin::StandardTodo) { + return; + } + + TodoHelpers::removeProject(this, current); +} + +void ActionListEditorPage::dissociateTodo(const QModelIndex ¤t) +{ + int type = current.data(Zanshin::ItemTypeRole).toInt(); + + if (!current.isValid() + || type!=Zanshin::StandardTodo + || m_mode==Zanshin::ProjectMode) { + return; + } + + for (int i=current.row(); i>=0; --i) { + QModelIndex index = m_treeView->model()->index(i, 0); + int type = index.data(Zanshin::ItemTypeRole).toInt(); + if (type==Zanshin::Category) { + QString category = index.data(Zanshin::CategoryPathRole).toString(); + if (CategoryManager::instance().dissociateTodoFromCategory(current, category)) { + break; + } + } + } +} + +Zanshin::ApplicationMode ActionListEditorPage::mode() +{ + return m_mode; +} + +void ActionListEditorPage::onAutoHideColumns() +{ + switch (m_mode) { + case Zanshin::ProjectMode: + m_treeView->hideColumn(1); + m_treeView->showColumn(2); + break; + case Zanshin::CategoriesMode: + m_treeView->showColumn(1); + m_treeView->hideColumn(2); + break; + } +} + +void ActionListEditorPage::setCollectionColumnHidden(bool hidden) +{ + QByteArray state = hidden ? m_noCollectionStateCache : m_normalStateCache; + + if (!state.isEmpty()) { + m_treeView->header()->restoreState(state); + } else { + m_treeView->setColumnHidden(4, hidden); + } +} + +void ActionListEditorPage::onColumnsGeometryChanged() +{ + if (!m_treeView->isColumnHidden(4)) { + m_normalStateCache = m_treeView->header()->saveState(); + } else { + m_noCollectionStateCache = m_treeView->header()->saveState(); + } +} + +void ActionListEditorPage::setDefaultCollection(const Akonadi::Collection &collection) +{ + m_defaultCollection = collection; +} + +bool ActionListEditorPage::selectSiblingIndex(const QModelIndex &index) +{ + QModelIndex sibling = m_treeView->indexBelow(index); + if (!sibling.isValid()) { + sibling = m_treeView->indexAbove(index); + } + if (sibling.isValid()) { + m_treeView->selectionModel()->setCurrentIndex(sibling, QItemSelectionModel::Select|QItemSelectionModel::Rows); + return true; + } + return false; +} + +void ActionListEditorPage::selectFirstIndex() +{ + QTimer::singleShot(0, this, SLOT(onSelectFirstIndex())); +} + +void ActionListEditorPage::onSelectFirstIndex() +{ + // Clear selection to avoid multiple selections when a widget is in edit mode. + m_treeView->selectionModel()->clearSelection(); + QModelIndex root = m_treeView->model()->index(0, 0); + if (root.isValid()) { + m_treeView->selectionModel()->setCurrentIndex(root, QItemSelectionModel::Select|QItemSelectionModel::Rows); + } +} diff -Nru zanshin-0.1+svn1006410/src/actionlisteditorpage.h zanshin-0.2.0/src/actionlisteditorpage.h --- zanshin-0.1+svn1006410/src/actionlisteditorpage.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/actionlisteditorpage.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,91 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_ACTIONLISTEDITORPAGE_H +#define ZANSHIN_ACTIONLISTEDITORPAGE_H + +#include + +#include + +#include "globaldefs.h" + +class KConfigGroup; +class QAbstractItemModel; +class QItemSelectionModel; +class QModelIndex; +class ModelStack; + +namespace Akonadi +{ + class EntityTreeView; +} + +class ActionListEditorPage : public QWidget +{ + Q_OBJECT + +public: + ActionListEditorPage(QAbstractItemModel *model, + ModelStack *models, + Zanshin::ApplicationMode mode, + const QList &contextActions, + QWidget *parent=0); + + QItemSelectionModel *selectionModel() const; + + void saveColumnsState(KConfigGroup &config, const QString &key) const; + void restoreColumnsState(const KConfigGroup &config, const QString &key); + + Zanshin::ApplicationMode mode(); + + void setCollectionColumnHidden(bool hidden); + + void setDefaultCollection(const Akonadi::Collection &collection); + + bool selectSiblingIndex(const QModelIndex &index); + void selectFirstIndex(); + +public slots: + void addNewTodo(const QString &summary); + void removeCurrentTodo(); + void removeTodo(const QModelIndex ¤t); + void dissociateTodo(const QModelIndex ¤t); + +private slots: + void onAutoHideColumns(); + void onColumnsGeometryChanged(); + void onSelectFirstIndex(); + +private: + Akonadi::EntityTreeView *m_treeView; + Zanshin::ApplicationMode m_mode; + + QByteArray m_normalStateCache; + QByteArray m_noCollectionStateCache; + + Akonadi::Collection m_defaultCollection; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/actionlistmodel.cpp zanshin-0.2.0/src/actionlistmodel.cpp --- zanshin-0.1+svn1006410/src/actionlistmodel.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/actionlistmodel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,223 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "actionlistmodel.h" - -#include - -#include - -#include - -#include "todocategoriesmodel.h" -#include "todoflatmodel.h" -#include "todotreemodel.h" - -ActionListModel::ActionListModel(QObject *parent) - : QSortFilterProxyModel(parent), m_mode(StandardMode) -{ - setDynamicSortFilter(true); -} - -ActionListModel::~ActionListModel() -{ -} - -Akonadi::Item ActionListModel::itemForIndex(const QModelIndex &index) const -{ - QAbstractItemModel *source = sourceModel(); - - if (TodoFlatModel *flat = qobject_cast(source)) { - return flat->itemForIndex(mapToSource(index)); - } else if (TodoTreeModel *tree = qobject_cast(source)) { - return tree->itemForIndex(mapToSource(index)); - } else if (TodoCategoriesModel *categories = qobject_cast(source)) { - return categories->itemForIndex(mapToSource(index)); - } - - return Akonadi::Item(); -} - -Qt::ItemFlags ActionListModel::flags(const QModelIndex &index) const -{ - QModelIndex sourceIndex = mapToSource(index); - QModelIndex rowTypeIndex = sourceIndex.sibling(sourceIndex.row(), TodoFlatModel::RowType); - int rowType = sourceModel()->data(rowTypeIndex).toInt(); - - if (rowType==TodoFlatModel::FolderTodo || !isInFocus(index)) { - return 0; - } else { - return QSortFilterProxyModel::flags(index); - } -} - -QVariant ActionListModel::data(const QModelIndex &index, int role) const -{ - QModelIndex sourceIndex = mapToSource(index); - if (sourceIndex.column()==TodoFlatModel::Categories && role==Qt::DisplayRole) { - return sourceModel()->data(sourceIndex, Qt::EditRole); - } - - return sourceModel()->data(sourceIndex, role); -} - -QVariant ActionListModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role==Qt::DisplayRole && orientation == Qt::Horizontal) { - QModelIndex sourceIndex = mapToSource(index(0, section)); - - switch(sourceIndex.column()) { - case TodoFlatModel::Categories: - return i18n("Contexts"); - case TodoFlatModel::ParentSummary: - return i18n("Project"); - default: - break; - } - } - - return QSortFilterProxyModel::headerData(section, orientation, role); -} - -void ActionListModel::setMode(Mode mode) -{ - m_mode = mode; - invalidate(); -} - -ActionListModel::Mode ActionListModel::mode() const -{ - return m_mode; -} - -void ActionListModel::setSourceFocusIndex(const QModelIndex &sourceIndex) -{ - m_sourceFocusIndex = sourceIndex; - invalidate(); -} - -QModelIndex ActionListModel::sourceFocusIndex() const -{ - return m_sourceFocusIndex; -} - -bool ActionListModel::filterAcceptsColumn(int sourceColumn, const QModelIndex &/*sourceParent*/) const -{ - return sourceColumn!=TodoFlatModel::RemoteId - && sourceColumn!=TodoFlatModel::ParentRemoteId - && sourceColumn!=TodoFlatModel::RowType - && (sourceColumn!=TodoFlatModel::Categories || (m_mode!=ContextMode && m_mode!=NoContextMode)) - && (sourceColumn!=TodoFlatModel::ParentSummary || (m_mode!=ProjectMode && m_mode!=NoProjectMode)); -} - -bool ActionListModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent); - - switch (m_mode) { - case StandardMode: - case ContextMode: - case ProjectMode: - sourceIndex = sourceModel()->index(sourceRow, TodoFlatModel::RowType, sourceParent); - if (!sourceParent.isValid() && sourceModel()->data(sourceIndex).toInt()==TodoFlatModel::StandardTodo) { - return false; - } - break; - case NoProjectMode: - sourceIndex = sourceModel()->index(sourceRow, TodoFlatModel::RowType, sourceParent); - if (sourceModel()->data(sourceIndex).toInt()!=TodoFlatModel::StandardTodo) { - return false; - } - sourceIndex = sourceModel()->index(sourceRow, TodoFlatModel::ParentRemoteId, sourceParent); - if (!sourceModel()->data(sourceIndex).toString().isEmpty()) { - return false; - } - break; - case NoContextMode: - sourceIndex = sourceModel()->index(sourceRow, TodoFlatModel::RowType, sourceParent); - if (sourceModel()->data(sourceIndex).toInt()!=TodoFlatModel::StandardTodo) { - return false; - } - sourceIndex = sourceModel()->index(sourceRow, TodoFlatModel::Categories, sourceParent); - if (!sourceModel()->data(sourceIndex).toStringList().isEmpty()) { - return false; - } - break; - } - - sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent); - - QModelIndex i = sourceIndex; - while (i.isValid()) { - if (m_sourceFocusIndex == i) { - return true; - } - i = i.parent(); - } - - i = m_sourceFocusIndex; - while (i.isValid()) { - if (sourceIndex == i) { - return true; - } - i = i.parent(); - } - - return !m_sourceFocusIndex.isValid(); -} - -bool ActionListModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - if (left.column()==0 && right.column()==0) { - QModelIndex leftRowType = left.sibling(left.row(), TodoFlatModel::RowType); - QModelIndex rightRowType = right.sibling(right.row(), TodoFlatModel::RowType); - - if (sourceModel()->data(rightRowType).toInt()==TodoFlatModel::FolderTodo - && sourceModel()->data(leftRowType).toInt()!=TodoFlatModel::FolderTodo) { - return true; - } else { - return QSortFilterProxyModel::lessThan(left, right); - } - } - - return QSortFilterProxyModel::lessThan(left, right); -} - -bool ActionListModel::isInFocus(const QModelIndex &index) const -{ - if (!m_sourceFocusIndex.isValid()) { - return true; - } - - QModelIndex sourceIndex = mapToSource(index); - sourceIndex = sourceIndex.sibling(sourceIndex.row(), 0); - - while (sourceIndex.isValid()) { - if (m_sourceFocusIndex==sourceIndex) { - return true; - } - sourceIndex = sourceIndex.parent(); - } - - return false; -} diff -Nru zanshin-0.1+svn1006410/src/actionlistmodel.h zanshin-0.2.0/src/actionlistmodel.h --- zanshin-0.1+svn1006410/src/actionlistmodel.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/actionlistmodel.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#ifndef ZANSHIN_ACTIONLISTMODEL_H -#define ZANSHIN_ACTIONLISTMODEL_H - -#include - -namespace Akonadi -{ - class Item; -} - -class ActionListModel : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - enum Mode { - StandardMode = 0, - ProjectMode, - ContextMode, - NoProjectMode, - NoContextMode - }; - - ActionListModel(QObject *parent = 0); - virtual ~ActionListModel(); - - Akonadi::Item itemForIndex(const QModelIndex &index) const; - - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - - void setMode(Mode mode); - Mode mode() const; - - void setSourceFocusIndex(const QModelIndex &sourceIndex); - QModelIndex sourceFocusIndex() const; - - bool isInFocus(const QModelIndex &index) const; - -protected: - virtual bool filterAcceptsColumn(int sourceColumn, const QModelIndex &sourceParent) const; - virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; - virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; - -private: - QPersistentModelIndex m_sourceFocusIndex; - Mode m_mode; -}; - -#endif - diff -Nru zanshin-0.1+svn1006410/src/actionlistview.cpp zanshin-0.2.0/src/actionlistview.cpp --- zanshin-0.1+svn1006410/src/actionlistview.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/actionlistview.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - Copyright 2008, 2009 Mario Bensi - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "actionlistview.h" - -#include - -#include "actionlistdelegate.h" -#include "actionduedatedelegate.h" - -ActionListView::ActionListView(QWidget *parent) - : Akonadi::ItemView(parent) -{ - setRootIsDecorated(false); - setItemDelegate(new ActionListDelegate(this)); - setItemDelegateForColumn(2, new ActionDueDateDelegate(this)); - setAnimated(true); - setSelectionMode(QAbstractItemView::ExtendedSelection); - setDragEnabled(true); - viewport()->setAcceptDrops(true); - setDropIndicatorShown(true); - setIndentation(0); - setStyleSheet("QTreeView::branch { background: palette(base) }"); -} - -ActionListView::~ActionListView() -{ - -} - -void ActionListView::setModel(QAbstractItemModel *model) -{ - QByteArray headerState = header()->saveState(); - - Akonadi::ItemView::setModel(model); - expandAll(); - connectModel(model); - - header()->restoreState(headerState); -} - -void ActionListView::setRootIndex(const QModelIndex &index) -{ - Akonadi::ItemView::setRootIndex(index); - expandAll(); -} - -void ActionListView::expandBranch(const QModelIndex& parent) -{ - QModelIndex index = parent; - - while (index.isValid()) { - expand(index); - index = index.parent(); - } -} - -void ActionListView::connectModel(QAbstractItemModel *model) const -{ - connect(model, SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(expandBranch(const QModelIndex&))); -} - -void ActionListView::startDrag(Qt::DropActions supportedActions) -{ - ActionListDelegate *delegate = qobject_cast(itemDelegate()); - if (delegate) { - delegate->setDragModeCount(selectedIndexes().size()); - } - - Akonadi::ItemView::startDrag(supportedActions); -} - -QModelIndex ActionListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) -{ - QModelIndex index = currentIndex(); - QModelIndex newIndex; - - if (!index.isValid()) { - index = model()->index(0, 0); - - while (index.isValid() && (model()->flags(index) & Qt::ItemIsEnabled) == 0) { - index = indexBelow(index); - } - - return index; - } - - switch (cursorAction) { - case MoveLeft: - if (index.column()==0) { - return index; - } - - return index.sibling(index.row(), index.column()-1); - - case MoveRight: - if (index.column()==model()->columnCount(index)-1) { - return index; - } - - return index.sibling(index.row(), index.column()+1); - - case MoveUp: - case MoveDown: - newIndex = Akonadi::ItemView::moveCursor(cursorAction, modifiers); - return newIndex.sibling(newIndex.row(), index.column()); - - default: - return Akonadi::ItemView::moveCursor(cursorAction, modifiers); - } -} diff -Nru zanshin-0.1+svn1006410/src/actionlistview.h zanshin-0.2.0/src/actionlistview.h --- zanshin-0.1+svn1006410/src/actionlistview.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/actionlistview.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#ifndef ZANSHIN_ACTIONLISTVIEW_H -#define ZANSHIN_ACTIONLISTVIEW_H - -#include - -class ActionListView : public Akonadi::ItemView -{ - Q_OBJECT - -public: - ActionListView(QWidget *parent = 0); - virtual ~ActionListView(); - - virtual void setModel(QAbstractItemModel *model); - -public slots: - virtual void setRootIndex(const QModelIndex &index); - -protected: - virtual void startDrag(Qt::DropActions supportedActions); - virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers); - -private slots: - void expandBranch(const QModelIndex& parent); - -private: - void connectModel(QAbstractItemModel *model) const; -}; - -#endif diff -Nru zanshin-0.1+svn1006410/src/categorymanager.cpp zanshin-0.2.0/src/categorymanager.cpp --- zanshin-0.1+svn1006410/src/categorymanager.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/categorymanager.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,311 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "categorymanager.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "globaldefs.h" +#include "todohelpers.h" + +K_GLOBAL_STATIC(CategoryManager, s_categoryManager) + +CategoryManager &CategoryManager::instance() +{ + return *s_categoryManager; +} + + +CategoryManager::CategoryManager(QObject *parent) + : QObject(parent) + , m_model(0) +{ +} + +CategoryManager::~CategoryManager() +{ +} + +void CategoryManager::setModel(QAbstractItemModel *model) +{ + if (m_model) { + disconnect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int))); + disconnect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + } + + if (model) { + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(onSourceInsertRows(QModelIndex,int,int))); + connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(onSourceDataChanged(QModelIndex,QModelIndex))); + } + + m_categories.clear(); + m_model = model; +} + +QStringList CategoryManager::categories() +{ + return m_categories; +} + +const QChar CategoryManager::pathSeparator() +{ + return QChar(0x2044); +} + +void CategoryManager::addCategory(const QString &category, const QString &parentCategory) +{ + QString categoryPath; + if (parentCategory.isEmpty()) { + categoryPath = category; + } else { + categoryPath = parentCategory + CategoryManager::pathSeparator() + category; + } + addCategory(categoryPath); +} + +void CategoryManager::addCategory(const QString &categoryPath) +{ + if (!m_categories.contains(categoryPath)) { + m_categories << categoryPath; + emit categoryAdded(categoryPath); + } +} + +bool CategoryManager::removeCategory(QWidget *parent, const QModelIndex &categoryIndex) +{ + QModelIndexList categories; + categories << categoryIndex; + return removeCategories(parent, categories); +} + +bool CategoryManager::removeCategories(QWidget *parent, const QModelIndexList &categories) +{ + if (categories.isEmpty()) { + return false; + } + + QStringList categoryList; + foreach (QModelIndex category, categories) { + categoryList << category.data().toString(); + } + QString categoryName = categoryList.join(", "); + + QString title; + QString text; + + if (categories.size() > 1) { + text = i18n("Do you really want to delete the context '%1'? All actions won't be associated to this context anymore.", categoryName); + title = i18n("Delete Context"); + } else { + text = i18n("Do you really want to delete the contexts '%1'? All actions won't be associated to those contexts anymore.", categoryName); + title = i18n("Delete Contexts"); + } + + int button = KMessageBox::questionYesNo(parent, text, title); + bool canRemove = (button==KMessageBox::Yes); + + if (!canRemove) return false; + + foreach (QModelIndex category, categories) { + QString categoryPath = category.data(Zanshin::CategoryPathRole).toString(); + if (!removeCategory(categoryPath)) { + return false; + } + } + return true; +} + +bool CategoryManager::removeCategory(const QString &categoryPath) +{ + int pos = m_categories.indexOf(categoryPath); + if (pos != -1) { + removeCategoryFromTodo(QModelIndex(), categoryPath); + m_categories.removeAt(pos); + emit categoryRemoved(categoryPath); + return true; + } + return false; +} + +void CategoryManager::onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end) +{ + for (int i=begin; i<=end; ++i) { + QModelIndex sourceChildIndex = m_model->index(i, 0, sourceIndex); + if (!sourceChildIndex.isValid()) { + continue; + } + Zanshin::ItemType type = (Zanshin::ItemType) sourceChildIndex.data(Zanshin::ItemTypeRole).toInt(); + if (type==Zanshin::StandardTodo) { + QStringList categories = m_model->data(sourceChildIndex, Zanshin::CategoriesRole).toStringList(); + foreach (QString category, categories) { + addCategory(category); + } + } else if (type==Zanshin::Collection) { + onSourceInsertRows(sourceChildIndex, 0, m_model->rowCount(sourceChildIndex)-1); + } + } +} + +void CategoryManager::onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end) +{ + for (int row=begin.row(); row<=end.row(); ++row) { + QModelIndex sourceIndex = begin.sibling(row, 0); + QSet newCategories = QSet::fromList(sourceIndex.data(Zanshin::CategoriesRole).toStringList()); + + QSet oldCategories = QSet::fromList(m_categories); + QSet interCategories = newCategories; + interCategories.intersect(oldCategories); + newCategories-= interCategories; + + foreach (const QString &newCategory, newCategories) { + addCategory(newCategory); + } + } +} + +void CategoryManager::removeCategoryFromTodo(const QModelIndex &sourceIndex, const QString &categoryPath) +{ + for (int i=0; i < m_model->rowCount(sourceIndex); ++i) { + QModelIndex child = m_model->index(i, 0, sourceIndex); + dissociateTodoFromCategory(child, categoryPath); + removeCategoryFromTodo(child, categoryPath); + } +} + +bool CategoryManager::dissociateTodoFromCategory(const QModelIndex &index, const QString &categoryPath) +{ + if (!index.isValid()) { + return false; + } + + const Akonadi::Item item = index.data(Akonadi::EntityTreeModel::ItemRole).value(); + if (!item.isValid()) { + return false; + } + + KCalCore::Todo::Ptr todo = item.payload(); + + if (!todo) { + return false; + } + + QStringList categories = todo->categories(); + if (categories.contains(categoryPath)) { + categories.removeAll(categoryPath); + todo->setCategories(categories); + new Akonadi::ItemModifyJob(item); + return true; + } + return false; +} + +void CategoryManager::renameCategory(const QString &oldCategoryPath, const QString &newCategoryPath) +{ + if (oldCategoryPath == newCategoryPath) { + return; + } + + emit categoryRenamed(oldCategoryPath, newCategoryPath); + + m_categories.removeAll(oldCategoryPath); + m_categories << newCategoryPath; + + renameCategory(QModelIndex(), oldCategoryPath, newCategoryPath); +} + +void CategoryManager::renameCategory(const QModelIndex &sourceIndex, const QString &oldCategoryPath, const QString &newCategoryPath) +{ + for (int i=0; i < m_model->rowCount(sourceIndex); ++i) { + QModelIndex child = m_model->index(i, 0, sourceIndex); + if (child.isValid()) { + const Akonadi::Item item = child.data(Akonadi::EntityTreeModel::ItemRole).value(); + if (item.isValid()) { + KCalCore::Todo::Ptr todo = item.payload(); + if (todo) { + QStringList categories = todo->categories(); + if (categories.contains(oldCategoryPath)) { + categories = categories.replaceInStrings(oldCategoryPath, newCategoryPath); + todo->setCategories(categories); + new Akonadi::ItemModifyJob(item); + } + } + } + } + renameCategory(child, oldCategoryPath, newCategoryPath); + } +} + +void CategoryManager::moveCategory(const QString &oldCategoryPath, const QString &parentCategoryPath, Zanshin::ItemType parentType) +{ + if (parentType!=Zanshin::Category && parentType!=Zanshin::CategoryRoot) { + return; + } + + QString categoryName = oldCategoryPath.split(CategoryManager::pathSeparator()).last(); + QString newCategoryPath; + if (parentType==Zanshin::Category) { + newCategoryPath = parentCategoryPath + CategoryManager::pathSeparator() + categoryName; + } else { + newCategoryPath = categoryName; + } + + if (oldCategoryPath == newCategoryPath) { + return; + } + addCategory(newCategoryPath); + emit categoryMoved(oldCategoryPath, newCategoryPath); + removeCategory(oldCategoryPath); +} + +bool CategoryManager::moveTodoToCategory(const QModelIndex &index, const QString &categoryPath, const Zanshin::ItemType parentType) +{ + const Akonadi::Item item = index.data(Akonadi::EntityTreeModel::ItemRole).value(); + return moveTodoToCategory(item, categoryPath, parentType); +} + +bool CategoryManager::moveTodoToCategory(const Akonadi::Item &item, const QString &categoryPath, const Zanshin::ItemType parentType) +{ + KCalCore::Todo::Ptr todo = item.payload(); + if (!todo) { + return false; + } + QStringList categories; + if (parentType != Zanshin::Inbox && parentType != Zanshin::CategoryRoot) { + categories= todo->categories(); + if (!categories.contains(categoryPath)) { + categories << categoryPath; + } + } + todo->setCategories(categories); + new Akonadi::ItemModifyJob(item); + return true; +} diff -Nru zanshin-0.1+svn1006410/src/categorymanager.h zanshin-0.2.0/src/categorymanager.h --- zanshin-0.1+svn1006410/src/categorymanager.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/categorymanager.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,85 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_CATEGORYMANAGER_H +#define ZANSHIN_CATEGORYMANAGER_H + +#include +#include +#include +#include "globaldefs.h" + +class QAbstractItemModel; +class QModelIndex; +class TodoCategoriesModel; + +class CategoryManager : public QObject +{ + Q_OBJECT + +public: + static CategoryManager &instance(); + + CategoryManager(QObject *parent = 0); + virtual ~CategoryManager(); + + void setModel(QAbstractItemModel *model); + QAbstractItemModel *model() { return m_model; } + + void addCategory(const QString &category, const QString &parentCategory); + void addCategory(const QString &categoryPath); + bool removeCategory(QWidget *parent, const QModelIndex &categoryIndex); + bool removeCategories(QWidget *parent, const QModelIndexList &categoryIndex); + bool dissociateTodoFromCategory(const QModelIndex &index, const QString &categoryPath); + void renameCategory(const QString &oldCategoryPath, const QString &newCategoryPath); + void moveCategory(const QString &oldCategoryPath, const QString &parentCategoryPath, Zanshin::ItemType parentType); + bool moveTodoToCategory(const QModelIndex &index, const QString &categoryPath, const Zanshin::ItemType parentType); + bool moveTodoToCategory(const Akonadi::Item &item, const QString &categoryPath, const Zanshin::ItemType parentType); + + QStringList categories(); + + static const QChar pathSeparator(); + +Q_SIGNALS: + void categoryAdded(const QString &categoryPath); + void categoryRemoved(const QString &categoryPath); + void categoryRenamed(const QString &oldCategoryPath, const QString &newCategoryPath); + void categoryMoved(const QString &oldCategoryPath, const QString &newCategoryPath); + +private slots: + void onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end); + void onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end); + +private: + friend class TodoCategoriesModel; + + bool removeCategory(const QString &categoryPath); + void removeCategoryFromTodo(const QModelIndex &sourceIndex, const QString &categoryPath); + void renameCategory(const QModelIndex &sourceIndex, const QString &oldCategoryPath, const QString &newCategoryPath); + + QStringList m_categories; + QPointer m_model; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/CMakeLists.txt zanshin-0.2.0/src/CMakeLists.txt --- zanshin-0.1+svn1006410/src/CMakeLists.txt 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/CMakeLists.txt 2011-11-25 15:00:52.000000000 +0000 @@ -1,39 +1,93 @@ -add_subdirectory(tests) +add_subdirectory( icons ) +add_subdirectory( runner ) -########### zanshin ############### +########### zanshin core ############### -set(zanshin_SRCS +set(zanshin_core_SRCS + actionlistcheckablemodel.cpp + actionlistcompletermodel.cpp + categorymanager.cpp + combomodel.cpp + modelstack.cpp + selectionproxymodel.cpp + sidebarmodel.cpp + todocategoriesmodel.cpp + todohelpers.cpp + todoproxymodelbase.cpp + todotreemodel.cpp + todometadatamodel.cpp + todomodel.cpp + todonode.cpp + todonodemanager.cpp +) + +if(KDE4_BUILD_TESTS) + kde4_add_library(zanshin_static STATIC ${zanshin_core_SRCS}) + target_link_libraries(zanshin_static ${KDE4_KDEUI_LIBS} ${KDEPIMLIBS_AKONADI_LIBS} ${KDEPIMLIBS_KCALCORE_LIBS}) +endif(KDE4_BUILD_TESTS) + +########### zanshin gui ############### + +set(zanshin_gui_SRCS + aboutdata.cpp actionduedatedelegate.cpp + actionlistcombobox.cpp + actionlistcompleterview.cpp actionlistdelegate.cpp actionlisteditor.cpp - actionlistmodel.cpp - actionlistview.cpp + actionlisteditorpage.cpp configdialog.cpp - contextsmodel.cpp - globalmodel.cpp kdateedit.cpp kdatepickerpopup.cpp - librarymodel.cpp - main.cpp - mainwindow.cpp - projectsmodel.cpp + maincomponent.cpp quickselectdialog.cpp - todocategoriesattribute.cpp - todocategoriesmodel.cpp - todoflatmodel.cpp - todotreemodel.cpp sidebar.cpp + sidebarpage.cpp + todotreeview.cpp ) -kde4_add_kcfg_files(zanshin_SRCS globalsettings.kcfgc) +########### zanshin app ############### -kde4_add_executable(zanshin ${zanshin_SRCS}) - -target_link_libraries(zanshin ${KDE4_KDEUI_LIBS} ${KDE4_AKONADI_LIBS} ${KDE4_KCAL_LIBS}) +set(zanshin_SRCS + debugwindow.cpp + main.cpp + mainwindow.cpp + ${zanshin_core_SRCS} + ${zanshin_gui_SRCS} +) -########### install files ############### +kde4_add_app_icon(zanshin_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/hi*-apps-zanshin.png") +kde4_add_executable(zanshin ${zanshin_SRCS}) +target_link_libraries(zanshin ${KDE4_KDEUI_LIBS} ${KDEPIMLIBS_AKONADI_LIBS} ${KDEPIMLIBS_KCALCORE_LIBS}) install(TARGETS zanshin DESTINATION ${BIN_INSTALL_DIR}) install(FILES zanshinui.rc DESTINATION ${DATA_INSTALL_DIR}/zanshin) install(FILES zanshin.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) +########### zanshin part ############### + +set(zanshin_part_SRCS + part.cpp + ${zanshin_core_SRCS} + ${zanshin_gui_SRCS} +) + +kde4_add_plugin(zanshin_part ${zanshin_part_SRCS}) +target_link_libraries(zanshin_part ${KDE4_KPARTS_LIBS} ${KDEPIMLIBS_AKONADI_LIBS} ${KDEPIMLIBS_KCALCORE_LIBS}) + +install(TARGETS zanshin_part DESTINATION ${PLUGIN_INSTALL_DIR}) +install(FILES zanshin_part.rc DESTINATION ${DATA_INSTALL_DIR}/zanshin) +install(FILES zanshin_part.desktop DESTINATION ${SERVICES_INSTALL_DIR}) + +########### kontact zanshin plugin ############### + +set(kontact_zanshinplugin_SRCS + kontact_plugin.cpp +) + +kde4_add_plugin(kontact_zanshinplugin ${kontact_zanshinplugin_SRCS}) +target_link_libraries(kontact_zanshinplugin ${KDE4_KPARTS_LIBS} ${KDEPIMLIBS_KONTACTINTERFACE_LIBS}) + +install(TARGETS kontact_zanshinplugin DESTINATION ${PLUGIN_INSTALL_DIR}) +install(FILES zanshin_plugin.desktop DESTINATION ${SERVICES_INSTALL_DIR}/kontact) + diff -Nru zanshin-0.1+svn1006410/src/combomodel.cpp zanshin-0.2.0/src/combomodel.cpp --- zanshin-0.1+svn1006410/src/combomodel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/combomodel.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,50 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "combomodel.h" + +#include + +#include +#include + +#include "globaldefs.h" + +ComboModel::ComboModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ + setDynamicSortFilter(true); +} + +ComboModel::~ComboModel() +{ +} + +bool ComboModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex sourceChild = sourceModel()->index(sourceRow, 0, sourceParent); + return sourceChild.data(Zanshin::ItemTypeRole).toInt() != Zanshin::Inbox + && sourceChild.data(Zanshin::ItemTypeRole).toInt() != Zanshin::Collection + && sourceChild.data(Zanshin::ItemTypeRole).toInt() != Zanshin::CategoryRoot; +} diff -Nru zanshin-0.1+svn1006410/src/combomodel.h zanshin-0.2.0/src/combomodel.h --- zanshin-0.1+svn1006410/src/combomodel.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/combomodel.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,42 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_COMBOMODEL_H +#define ZANSHIN_COMBOMODEL_H + +#include + +class ComboModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + ComboModel(QObject *parent = 0); + virtual ~ComboModel(); + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/configdialog.cpp zanshin-0.2.0/src/configdialog.cpp --- zanshin-0.1+svn1006410/src/configdialog.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/configdialog.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Zanshin Todo. - Copyright 2009 Kevin Ottens + Copyright 2011 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -41,15 +42,14 @@ #include #include -#include "globalmodel.h" -#include "globalsettings.h" - -ConfigDialog::ConfigDialog(QWidget *parent, const QString &name, GlobalSettings *settings) - : KConfigDialog(parent, name, settings), m_settings(settings) +ConfigDialog::ConfigDialog(QWidget *parent) + : KDialog(parent) { - setFaceType(Plain); + setCaption(i18n("Settings...")); + setButtons(KDialog::Close); + resize(500, 450); - QWidget *page = new QWidget(this); + QWidget *page = mainWidget(); page->setLayout(new QVBoxLayout(page)); QLabel *description = new QLabel(page); @@ -57,14 +57,9 @@ description->setWordWrap(true); description->setText(i18n("Please select or create a resource which will be used by the application to store and query its TODOs.")); - m_collectionList = new Akonadi::CollectionView(page); - page->layout()->addWidget(m_collectionList); - m_collectionList->setObjectName("kcfg_collectionId"); - connect(m_collectionList, SIGNAL(clicked(const Akonadi::Collection &)), - this, SLOT(_k_updateButtons())); - - m_collectionList->setModel(GlobalModel::todoCollections()); - m_collectionList->expandAll(); + m_agentInstanceWidget = new Akonadi::AgentInstanceWidget(page); + m_agentInstanceWidget->agentFilterProxyModel()->addMimeTypeFilter("application/x-vnd.akonadi.calendar.todo"); + page->layout()->addWidget(m_agentInstanceWidget); QHBoxLayout *toolbarLayout = new QHBoxLayout; toolbarLayout->setAlignment(Qt::AlignRight); @@ -93,64 +88,6 @@ toolbar->addAction(configure); page->layout()->addItem(toolbarLayout); - - addPage(page, i18n("Resources"), QString(), QString(), false); - - updateWidgets(); -} - -void ConfigDialog::updateSettings() -{ - m_settings->setCollectionId(selectedCollection()); - m_settings->writeConfig(); - emit settingsChanged(objectName()); -} - -void ConfigDialog::updateWidgets() -{ - Akonadi::Collection::Id id = m_settings->collectionId(); - selectCollection(id); -} - -void ConfigDialog::updateWidgetsDefault() -{ - QModelIndex index = m_collectionList->model()->index(0, 0); - m_collectionList->setCurrentIndex(index); -} - -bool ConfigDialog::selectCollection(Akonadi::Collection::Id id, const QModelIndex &parentIndex) -{ - const QAbstractItemModel *model = m_collectionList->model(); - - for (int row=0; rowrowCount(parentIndex); row++) { - QModelIndex index = model->index(row, 0, parentIndex); - if (model->data(index, Akonadi::CollectionModel::CollectionIdRole).toInt()==id) { - m_collectionList->setCurrentIndex(index); - return true; - } else if (model->rowCount(index)>0 - && selectCollection(id, index)) { - return true; - } - } - - return false; -} - -Akonadi::Collection::Id ConfigDialog::selectedCollection() -{ - QModelIndex current = m_collectionList->currentIndex(); - return m_collectionList->model()->data(current, Akonadi::CollectionModel::CollectionIdRole).toInt(); -} - -bool ConfigDialog::hasChanged() -{ - return m_settings->collectionId()!=selectedCollection(); -} - -bool ConfigDialog::isDefault() -{ - QModelIndex current = m_collectionList->currentIndex(); - return current.isValid() && (current.row()==0); } void ConfigDialog::addResource() @@ -170,31 +107,31 @@ void ConfigDialog::removeResource() { - const QModelIndex current = m_collectionList->currentIndex(); - const Akonadi::Collection collection = m_collectionList->model()->data(current, Akonadi::CollectionModel::CollectionRole).value(); - Akonadi::AgentInstance agent = Akonadi::AgentManager::self()->instance(collection.resource()); - - if ( agent.isValid() ) { - if ( KMessageBox::questionYesNo( this, - i18n( "Do you really want to delete agent instance %1?", agent.name() ), - i18n( "Agent Deletion" ), - KStandardGuiItem::del(), - KStandardGuiItem::cancel(), - QString(), - KMessageBox::Dangerous ) - == KMessageBox::Yes ) + QList list = m_agentInstanceWidget->selectedAgentInstances(); + if (!list.isEmpty()) { + if (KMessageBox::questionYesNo( this, + i18np( "Do you really want to delete the selected agent instance?", + "Do you really want to delete these %1 agent instances?", + list.size() ), + i18n( "Multiple Agent Deletion" ), + KStandardGuiItem::del(), + KStandardGuiItem::cancel(), + QString(), + KMessageBox::Dangerous ) + == KMessageBox::Yes ) { - Akonadi::AgentManager::self()->removeInstance( agent ); + foreach( const Akonadi::AgentInstance &agent, list ) { + Akonadi::AgentManager::self()->removeInstance( agent ); + } } } } void ConfigDialog::configureResource() { - const QModelIndex current = m_collectionList->currentIndex(); - const Akonadi::Collection collection = m_collectionList->model()->data(current, Akonadi::CollectionModel::CollectionRole).value(); - Akonadi::AgentInstance agent = Akonadi::AgentManager::self()->instance(collection.resource()); - - if ( agent.isValid() ) + Akonadi::AgentInstance agent = m_agentInstanceWidget->currentAgentInstance(); + if (agent.isValid()) { agent.configure(this); + } } + diff -Nru zanshin-0.1+svn1006410/src/configdialog.h zanshin-0.2.0/src/configdialog.h --- zanshin-0.1+svn1006410/src/configdialog.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/configdialog.h 2011-11-25 15:00:52.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Zanshin Todo. - Copyright 2009 Kevin Ottens + Copyright 2011 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -24,32 +24,19 @@ #ifndef ZANSHIN_CONFIGDIALOG_H #define ZANSHIN_CONFIGDIALOG_H -#include - -#include +#include namespace Akonadi { - class CollectionView; + class AgentInstanceWidget; } -class GlobalSettings; - -class ConfigDialog : public KConfigDialog +class ConfigDialog : public KDialog { Q_OBJECT public: - ConfigDialog(QWidget *parent, const QString &name, GlobalSettings *settings); - -protected slots: - virtual void updateSettings(); - virtual void updateWidgets(); - virtual void updateWidgetsDefault(); - -protected: - virtual bool hasChanged(); - virtual bool isDefault(); + ConfigDialog(QWidget *parent = 0); private slots: void addResource(); @@ -57,10 +44,7 @@ void configureResource(); private: - bool selectCollection(Akonadi::Collection::Id id, const QModelIndex &parentIndex=QModelIndex()); - Akonadi::Collection::Id selectedCollection(); - Akonadi::CollectionView *m_collectionList; - GlobalSettings *m_settings; + Akonadi::AgentInstanceWidget *m_agentInstanceWidget; }; #endif diff -Nru zanshin-0.1+svn1006410/src/contextsmodel.cpp zanshin-0.2.0/src/contextsmodel.cpp --- zanshin-0.1+svn1006410/src/contextsmodel.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/contextsmodel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "contextsmodel.h" - -#include "todocategoriesmodel.h" -#include "todoflatmodel.h" - -ContextsModel::ContextsModel(QObject *parent) - : QSortFilterProxyModel(parent) -{ - setDynamicSortFilter(true); -} - -ContextsModel::~ContextsModel() -{ -} - -void ContextsModel::setSourceModel(QAbstractItemModel *sourceModel) -{ - Q_ASSERT(sourceModel==0 || qobject_cast(sourceModel)!=0); - QSortFilterProxyModel::setSourceModel(sourceModel); - - connect(categoriesModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(invalidate())); - connect(categoriesModel(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)), - this, SLOT(invalidate())); - connect(categoriesModel(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(invalidate())); -} - -TodoCategoriesModel *ContextsModel::categoriesModel() const -{ - return qobject_cast(sourceModel()); -} - -bool ContextsModel::filterAcceptsColumn(int sourceColumn, const QModelIndex &/*sourceParent*/) const -{ - return sourceColumn==TodoFlatModel::Summary; -} - -bool ContextsModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - QModelIndex index = categoriesModel()->index(sourceRow, - TodoFlatModel::RemoteId, - sourceParent); - - return categoriesModel()->data(index).toString().isEmpty(); -} - diff -Nru zanshin-0.1+svn1006410/src/contextsmodel.h zanshin-0.2.0/src/contextsmodel.h --- zanshin-0.1+svn1006410/src/contextsmodel.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/contextsmodel.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#ifndef ZANSHIN_CONTEXTSMODEL_H -#define ZANSHIN_CONTEXTSMODEL_H - -#include - -class TodoCategoriesModel; - -class ContextsModel : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - ContextsModel(QObject *parent = 0); - virtual ~ContextsModel(); - - virtual void setSourceModel(QAbstractItemModel *sourceModel); - -protected: - virtual bool filterAcceptsColumn(int sourceColumn, const QModelIndex &sourceParent) const; - virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; - -private: - TodoCategoriesModel *categoriesModel() const; -}; - -#endif - diff -Nru zanshin-0.1+svn1006410/src/debugwindow.cpp zanshin-0.2.0/src/debugwindow.cpp --- zanshin-0.1+svn1006410/src/debugwindow.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/debugwindow.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,55 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "debugwindow.h" + +#include + +#include "modelstack.h" + +DebugWindow::DebugWindow(ModelStack *models, QWidget *parent) + : QTabWidget(parent) +{ + Akonadi::EntityTreeView *view = new Akonadi::EntityTreeView(this); + view->setModel(models->baseModel()); + view->setSelectionMode(QAbstractItemView::ExtendedSelection); + addTab(view, "TodoModel"); + + view = new Akonadi::EntityTreeView(this); + view->setModel(models->categoriesModel()); + view->setSelectionMode(QAbstractItemView::ExtendedSelection); + addTab(view, "TodoCategoriesModel"); + + view = new Akonadi::EntityTreeView(this); + view->setModel(models->treeModel()); + view->setSelectionMode(QAbstractItemView::ExtendedSelection); + addTab(view, "TodoTreeModel"); + + view = new Akonadi::EntityTreeView(this); + view->setModel(models->collectionsModel()); + view->setSelectionMode(QAbstractItemView::ExtendedSelection); + addTab(view, "Collections Model"); + + resize(800, 600); +} diff -Nru zanshin-0.1+svn1006410/src/debugwindow.h zanshin-0.2.0/src/debugwindow.h --- zanshin-0.1+svn1006410/src/debugwindow.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/debugwindow.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,40 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_DEBUGWINDOW_H +#define ZANSHIN_DEBUGWINDOW_H + +#include + +class ModelStack; + +class DebugWindow : public QTabWidget +{ + Q_OBJECT + +public: + DebugWindow(ModelStack *models, QWidget *parent = 0); +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/globaldefs.h zanshin-0.2.0/src/globaldefs.h --- zanshin-0.1+svn1006410/src/globaldefs.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/globaldefs.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,71 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_GLOBALDEFS_H +#define ZANSHIN_GLOBALDEFS_H + +#include +#include + +namespace Zanshin +{ + enum ApplicationMode { + ProjectMode = 0, + CategoriesMode = 1 + }; + + enum ItemType + { + StandardTodo = 0, + ProjectTodo, + Category, + Collection, + Inbox, + CategoryRoot + }; + + enum Roles { + UidRole = Akonadi::EntityTreeModel::UserRole + 1, + ParentUidRole, + AncestorsUidRole, + CategoriesRole, + AncestorsCategoriesRole, + ItemTypeRole, + DataTypeRole, + ChildUidsRole, + ChildIndexesRole, + CategoryPathRole, + UserRole = Akonadi::EntityTreeModel::UserRole + 100 + }; + + enum DataType { + StandardType = 0, + CategoryType, + ProjectType + }; + +} +Q_DECLARE_METATYPE(QModelIndexList) + +#endif + diff -Nru zanshin-0.1+svn1006410/src/globalmodel.cpp zanshin-0.2.0/src/globalmodel.cpp --- zanshin-0.1+svn1006410/src/globalmodel.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/globalmodel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,139 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "globalmodel.h" - -#include -#include -#include - -#include - -#include "contextsmodel.h" -#include "librarymodel.h" -#include "projectsmodel.h" -#include "todocategoriesattribute.h" -#include "todocategoriesmodel.h" -#include "todoflatmodel.h" -#include "todotreemodel.h" - -class GlobalModelPrivate -{ -public: - GlobalModelPrivate() - { - Akonadi::AttributeFactory::registerAttribute(); - - todoFlat = new TodoFlatModel(); - - todoTree = new TodoTreeModel(); - todoTree->setSourceModel(todoFlat); - - todoCategories = new TodoCategoriesModel(); - todoCategories->setSourceModel(todoFlat); - - contexts = new ContextsModel(); - contexts->setSourceModel(todoCategories); - - projects = new ProjectsModel(); - projects->setSourceModel(todoTree); - - contextsLibrary = new LibraryModel(); - contextsLibrary->setSourceModel(contexts); - contextsLibrary->setType(LibraryModel::Contexts); - - projectsLibrary = new LibraryModel(); - projectsLibrary->setSourceModel(projects); - - todoCollections = new Akonadi::CollectionFilterProxyModel(); - Akonadi::CollectionModel *collectionModel = new Akonadi::CollectionModel(todoCollections); - todoCollections->setSourceModel(collectionModel); - todoCollections->addMimeTypeFilter("application/x-vnd.akonadi.calendar.todo"); - } - - ~GlobalModelPrivate() - { - delete projectsLibrary; - delete contextsLibrary; - delete projects; - delete contexts; - delete todoCategories; - delete todoTree; - delete todoFlat; - delete todoCollections; - } - - TodoFlatModel *todoFlat; - TodoTreeModel *todoTree; - TodoCategoriesModel *todoCategories; - - ContextsModel *contexts; - ProjectsModel *projects; - - LibraryModel *contextsLibrary; - LibraryModel *projectsLibrary; - - Akonadi::CollectionFilterProxyModel *todoCollections; -}; - -K_GLOBAL_STATIC(GlobalModelPrivate, globalModel) - -TodoFlatModel *GlobalModel::todoFlat() -{ - return globalModel->todoFlat; -} - -TodoTreeModel *GlobalModel::todoTree() -{ - return globalModel->todoTree; -} - -TodoCategoriesModel *GlobalModel::todoCategories() -{ - return globalModel->todoCategories; -} - -QAbstractItemModel *GlobalModel::todoCollections() -{ - return globalModel->todoCollections; -} - -ContextsModel *GlobalModel::contexts() -{ - return globalModel->contexts; -} - -ProjectsModel *GlobalModel::projects() -{ - return globalModel->projects; -} - -LibraryModel *GlobalModel::contextsLibrary() -{ - return globalModel->contextsLibrary; -} - -LibraryModel *GlobalModel::projectsLibrary() -{ - return globalModel->projectsLibrary; -} diff -Nru zanshin-0.1+svn1006410/src/globalmodel.h zanshin-0.2.0/src/globalmodel.h --- zanshin-0.1+svn1006410/src/globalmodel.h 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/globalmodel.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#ifndef ZANSHIN_GLOBALMODEL_H -#define ZANSHIN_GLOBALMODEL_H - -class TodoFlatModel; -class TodoTreeModel; -class TodoCategoriesModel; -class ContextsModel; -class LibraryModel; -class ProjectsModel; -class QAbstractItemModel; - -namespace GlobalModel -{ - // Internal models - TodoFlatModel *todoFlat(); - TodoTreeModel *todoTree(); - TodoCategoriesModel *todoCategories(); - QAbstractItemModel *todoCollections(); - - // User oriented models - ContextsModel *contexts(); - ProjectsModel *projects(); - - LibraryModel *contextsLibrary(); - LibraryModel *projectsLibrary(); -} - -#endif - diff -Nru zanshin-0.1+svn1006410/src/globalsettings.kcfg zanshin-0.2.0/src/globalsettings.kcfg --- zanshin-0.1+svn1006410/src/globalsettings.kcfg 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/globalsettings.kcfg 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ - - - - - - - - - -1 - - - diff -Nru zanshin-0.1+svn1006410/src/globalsettings.kcfgc zanshin-0.2.0/src/globalsettings.kcfgc --- zanshin-0.1+svn1006410/src/globalsettings.kcfgc 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/globalsettings.kcfgc 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -File=globalsettings.kcfg -ClassName=GlobalSettings -Singleton=true -Mutators=true diff -Nru zanshin-0.1+svn1006410/src/icons/CMakeLists.txt zanshin-0.2.0/src/icons/CMakeLists.txt --- zanshin-0.1+svn1006410/src/icons/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/icons/CMakeLists.txt 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1 @@ +kde4_install_icons( ${ICON_INSTALL_DIR} ) Binary files /tmp/vtxcpo5a2Y/zanshin-0.1+svn1006410/src/icons/hi128-apps-zanshin.png and /tmp/8zFakMKUF3/zanshin-0.2.0/src/icons/hi128-apps-zanshin.png differ Binary files /tmp/vtxcpo5a2Y/zanshin-0.1+svn1006410/src/icons/hi256-apps-zanshin.png and /tmp/8zFakMKUF3/zanshin-0.2.0/src/icons/hi256-apps-zanshin.png differ Binary files /tmp/vtxcpo5a2Y/zanshin-0.1+svn1006410/src/icons/hi32-apps-zanshin.png and /tmp/8zFakMKUF3/zanshin-0.2.0/src/icons/hi32-apps-zanshin.png differ Binary files /tmp/vtxcpo5a2Y/zanshin-0.1+svn1006410/src/icons/hi64-apps-zanshin.png and /tmp/8zFakMKUF3/zanshin-0.2.0/src/icons/hi64-apps-zanshin.png differ Binary files /tmp/vtxcpo5a2Y/zanshin-0.1+svn1006410/src/icons/hisc-apps-zanshin.svgz and /tmp/8zFakMKUF3/zanshin-0.2.0/src/icons/hisc-apps-zanshin.svgz differ diff -Nru zanshin-0.1+svn1006410/src/kdateedit.cpp zanshin-0.2.0/src/kdateedit.cpp --- zanshin-0.1+svn1006410/src/kdateedit.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/kdateedit.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -75,10 +76,9 @@ QStringList mKeywords; }; -KDateEdit::KDateEdit( QWidget *parent, const char *name ) +KDateEdit::KDateEdit( QWidget *parent ) : QComboBox( parent ), mReadOnly( false ), mDiscardNextMousePress( false ) { - setObjectName( name ); // need at least one entry for popup to work setMaxCount( 1 ); setEditable( true ); @@ -89,18 +89,18 @@ addItem( today ); setCurrentIndex( 0 ); - connect( lineEdit(), SIGNAL( returnPressed() ), - this, SLOT( lineEnterPressed() ) ); - connect( this, SIGNAL( textChanged( const QString& ) ), - SLOT( slotTextChanged( const QString& ) ) ); + connect( lineEdit(), SIGNAL(returnPressed()), + this, SLOT(lineEnterPressed()) ); + connect( this, SIGNAL(textChanged(QString)), + SLOT(slotTextChanged(QString)) ); mPopup = new KDatePickerPopup( KDatePickerPopup::DatePicker | KDatePickerPopup::Words, QDate::currentDate(), this ); mPopup->hide(); mPopup->installEventFilter( this ); - connect( mPopup, SIGNAL( dateChanged( const QDate& ) ), - SLOT( dateSelected( const QDate& ) ) ); + connect( mPopup, SIGNAL(dateChanged(QDate)), + SLOT(dateSelected(QDate)) ); // handle keyword entry setupKeywords(); @@ -301,12 +301,23 @@ if (!date.isValid()) break; date = date.addMonths( -1 ); break; + case Qt::Key_Equal: + date = QDate::currentDate(); + break; + case Qt::Key_Return: + case Qt::Key_Enter: + // When the date is selected and the Key_Return/Key_Enter pressed, the value in the + // QLineEdit is set to the value of the first item in the QCompleter, here "friday". + lineEdit()->deselect(); + break; } if ( date.isValid() && assignDate( date ) ) { + e->accept(); updateView(); emit dateChanged( date ); emit dateEntered( date ); + return; } } @@ -325,7 +336,7 @@ // Up and down arrow keys step the date QKeyEvent *keyEvent = (QKeyEvent *)event; - if ( keyEvent->key() == Qt::Key_Return ) { + if ( keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter ) { lineEnterPressed(); return true; } @@ -392,6 +403,11 @@ dayName = KGlobal::locale()->calendar()->weekDayName( i ).toLower(); mKeywordMap.insert( dayName, i + 100 ); } + + QCompleter *comp = new QCompleter( mKeywordMap.keys(), this ); + comp->setCaseSensitivity( Qt::CaseInsensitive ); + comp->setCompletionMode( QCompleter::InlineCompletion ); + setCompleter( comp ); } bool KDateEdit::assignDate( const QDate &date ) diff -Nru zanshin-0.1+svn1006410/src/kdateedit.h zanshin-0.2.0/src/kdateedit.h --- zanshin-0.1+svn1006410/src/kdateedit.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/kdateedit.h 2011-11-25 15:00:52.000000000 +0000 @@ -56,9 +56,10 @@ class KDateEdit : public QComboBox { Q_OBJECT + Q_PROPERTY( QDate date READ date WRITE setDate ) public: - explicit KDateEdit( QWidget *parent = 0, const char *name = 0 ); + explicit KDateEdit( QWidget *parent = 0 ); virtual ~KDateEdit(); /** diff -Nru zanshin-0.1+svn1006410/src/kdatepickerpopup.cpp zanshin-0.2.0/src/kdatepickerpopup.cpp --- zanshin-0.1+svn1006410/src/kdatepickerpopup.cpp 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/kdatepickerpopup.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -67,10 +67,10 @@ mDatePicker = new KDatePicker( this ); mDatePicker->setCloseButton( false ); - connect( mDatePicker, SIGNAL( dateEntered( const QDate& ) ), - SLOT( slotDateChanged( const QDate& ) ) ); - connect( mDatePicker, SIGNAL( dateSelected( const QDate& ) ), - SLOT( slotDateChanged( const QDate& ) ) ); + connect( mDatePicker, SIGNAL(dateEntered(QDate)), + SLOT(slotDateChanged(QDate)) ); + connect( mDatePicker, SIGNAL(dateSelected(QDate)), + SLOT(slotDateChanged(QDate)) ); mDatePicker->setDate( date ); @@ -93,10 +93,10 @@ } if ( mItems & Words ) { - addAction( i18nc( "@option today", "&Today" ), this, SLOT( slotToday() ) ); - addAction( i18nc( "@option tomorrow", "To&morrow" ), this, SLOT( slotTomorrow() ) ); - addAction( i18nc( "@option next week", "Next &Week" ), this, SLOT( slotNextWeek() ) ); - addAction( i18nc( "@option next month", "Next M&onth" ), this, SLOT( slotNextMonth() ) ); + addAction( i18nc( "@option today", "&Today" ), this, SLOT(slotToday()) ); + addAction( i18nc( "@option tomorrow", "To&morrow" ), this, SLOT(slotTomorrow()) ); + addAction( i18nc( "@option next week", "Next &Week" ), this, SLOT(slotNextWeek()) ); + addAction( i18nc( "@option next month", "Next M&onth" ), this, SLOT(slotNextMonth()) ); if ( mItems & NoDate ) { addSeparator(); @@ -104,7 +104,7 @@ } if ( mItems & NoDate ) { - addAction( i18nc( "@option do not specify a date", "No Date" ), this, SLOT( slotNoDate() ) ); + addAction( i18nc( "@option do not specify a date", "No Date" ), this, SLOT(slotNoDate()) ); } } diff -Nru zanshin-0.1+svn1006410/src/kontact_plugin.cpp zanshin-0.2.0/src/kontact_plugin.cpp --- zanshin-0.1+svn1006410/src/kontact_plugin.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/kontact_plugin.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,41 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "kontact_plugin.h" + +#include + +EXPORT_KONTACT_PLUGIN(Plugin, zanshin) + +Plugin::Plugin(KontactInterface::Core *core, const QVariantList&) + : KontactInterface::Plugin(core, core, "zanshin") +{ + qputenv("ZANSHIN_KONTACT_PLUGIN", "1"); + setComponentData(KontactPluginFactory::componentData()); +} + +KParts::ReadOnlyPart *Plugin::createPart() +{ + return loadPart(); +} + diff -Nru zanshin-0.1+svn1006410/src/kontact_plugin.h zanshin-0.2.0/src/kontact_plugin.h --- zanshin-0.1+svn1006410/src/kontact_plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/kontact_plugin.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,43 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_KONTACT_PLUGIN_H +#define ZANSHIN_KONTACT_PLUGIN_H + +#include + +class Plugin : public KontactInterface::Plugin +{ + Q_OBJECT + +public: + Plugin(KontactInterface::Core *core, const QVariantList &); + + int weight() const { return 449; } + +protected: + KParts::ReadOnlyPart *createPart(); +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/librarymodel.cpp zanshin-0.2.0/src/librarymodel.cpp --- zanshin-0.1+svn1006410/src/librarymodel.cpp 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/librarymodel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,295 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - Copyright 2008, 2009 Mario Bensi - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "librarymodel.h" - -#include -#include - -LibraryModel::LibraryModel(QObject *parent) - : QAbstractProxyModel(parent), m_inboxToken(1), m_libraryToken(2), - m_tokenShift(m_libraryToken+1), m_type(Projects) -{ -} - -LibraryModel::~LibraryModel() -{ - -} - -LibraryModel::LibraryType LibraryModel::type() const -{ - return m_type; -} - -void LibraryModel::setType(LibraryType type) -{ - m_type = type; -} - -QModelIndex LibraryModel::index(int row, int column, const QModelIndex &parent) const -{ - if (column!=0) return QModelIndex(); - - if (parent==QModelIndex()) { - switch (row) { - case 0: - return createIndex(row, column, (void*)m_inboxToken); - case 1: - return createIndex(row, column, (void*)m_libraryToken); - default: - return QModelIndex(); - } - } - - return mapFromSource(sourceModel()->index(row, column, mapToSource(parent))); -} - -QModelIndex LibraryModel::parent(const QModelIndex &index) const -{ - if (index.column()!=0 || !index.isValid() - || isInbox(index) || isLibraryRoot(index)) { - return QModelIndex(); - } - - return mapFromSource(sourceModel()->parent(mapToSource(index))); -} - -int LibraryModel::rowCount(const QModelIndex &parent) const -{ - if (parent==QModelIndex()) { - return 2; - } - - if (parent.column()!=0) return -1; - - if (isInbox(parent)) { - return 0; - } - - if (isLibraryRoot(parent)) { - return sourceModel()->rowCount(); - } - - return sourceModel()->rowCount(mapToSource(parent)); -} - -int LibraryModel::columnCount(const QModelIndex &/*parent*/) const -{ - return 1; -} - -QStringList LibraryModel::mimeTypes() const -{ - return sourceModel()->mimeTypes(); -} - -Qt::DropActions LibraryModel::supportedDropActions() const -{ - return sourceModel()->supportedDropActions(); -} - -Qt::ItemFlags LibraryModel::flags(const QModelIndex &index) const -{ - if (isInbox(index) || isLibraryRoot(index)) { - return Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsDropEnabled; - } - - return QAbstractProxyModel::flags(index); -} - -QMimeData *LibraryModel::mimeData(const QModelIndexList &indexes) const -{ - QModelIndexList sourceIndexes; - foreach (const QModelIndex &proxyIndex, indexes) { - sourceIndexes << mapToSource(proxyIndex); - } - - return sourceModel()->mimeData(sourceIndexes); -} - -bool LibraryModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, - int row, int column, const QModelIndex &parent) -{ - QModelIndex sourceParent = mapToSource(parent); - return sourceModel()->dropMimeData(mimeData, action, row, column, sourceParent); -} - -QVariant LibraryModel::data(const QModelIndex &index, int role) const -{ - if (index.column()!=0 || !index.isValid()) return QVariant(); - - if (isInbox(index)) { - switch (role) { - case Qt::DisplayRole: - switch (m_type) { - case Contexts: - return i18n("No Context"); - default: - return i18n("Inbox"); - } - case Qt::DecorationRole: - return KIcon("mail-folder-inbox"); - default: - return QVariant(); - } - } - - if (isLibraryRoot(index)) { - switch (role) { - case Qt::DisplayRole: - switch (m_type) { - case Contexts: - return i18n("Contexts"); - default: - return i18n("Library"); - } - case Qt::DecorationRole: - return KIcon("document-multiple"); - default: - return QVariant(); - } - } - - return QAbstractProxyModel::data(index, role); -} - -QVariant LibraryModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - return sourceModel()->headerData(section, orientation, role); -} - -bool LibraryModel::isInbox(const QModelIndex &index) const -{ - return index.internalId()==m_inboxToken; -} - -bool LibraryModel::isLibraryRoot(const QModelIndex &index) const -{ - return index.internalId()==m_libraryToken; -} - -QModelIndex LibraryModel::mapToSource(const QModelIndex &proxyIndex) const -{ - if (proxyIndex.column()!=0 - || isInbox(proxyIndex) - || isLibraryRoot(proxyIndex)) { - return QModelIndex(); - } - - int pos = proxyIndex.internalId()-m_tokenShift; - - if (pos>=m_sourceIndexesList.size()) { - return QModelIndex(); - } - - return m_sourceIndexesList[pos]; -} - -QModelIndex LibraryModel::mapFromSource(const QModelIndex &sourceIndex) const -{ - if (!sourceIndex.isValid()) { - return index(1, 0); // Index of the library item - } else { - qint64 indexOf = m_sourceIndexesList.indexOf(sourceIndex); - if (indexOf==-1) { - indexOf = m_sourceIndexesList.size(); - m_sourceIndexesList << sourceIndex; - } - - return createIndex(sourceIndex.row(), sourceIndex.column(), (void*)(indexOf+m_tokenShift)); - } -} - -void LibraryModel::setSourceModel(QAbstractItemModel *source) -{ - if (sourceModel()) { - disconnect(sourceModel()); - } - - QAbstractProxyModel::setSourceModel(source); - - connect(sourceModel(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(onSourceDataChanged(const QModelIndex&, const QModelIndex&))); - - connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)), - this, SLOT(onSourceRowsAboutToBeInserted(const QModelIndex&, int, int))); - connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(onSourceRowsInserted(const QModelIndex&, int, int))); - - connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), - this, SLOT(onSourceRowsAboutToBeRemoved(const QModelIndex&, int, int))); - connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)), - this, SLOT(onSourceRowsRemoved(const QModelIndex&, int, int))); - - connect(sourceModel(), SIGNAL(layoutAboutToBeChanged()), - this, SIGNAL(layoutAboutToBeChanged())); - connect(sourceModel(), SIGNAL(layoutChanged()), - this, SLOT(onSourceLayoutChanged())); -} - -void LibraryModel::onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end) -{ - emit dataChanged(mapFromSource(begin), mapFromSource(end)); -} - -void LibraryModel::onSourceRowsAboutToBeInserted(const QModelIndex &sourceIndex, int begin, int end) -{ - m_sourceIndexesList << sourceIndex; - beginInsertRows(mapFromSource(sourceIndex), begin, end); -} - -void LibraryModel::onSourceRowsInserted(const QModelIndex &sourceIndex, int begin, int end) -{ - for (int i=begin; i<=end; i++) { - QModelIndex child = sourceModel()->index(i, 0, sourceIndex); - m_sourceIndexesList << child; - } - endInsertRows(); -} - -void LibraryModel::onSourceRowsAboutToBeRemoved(const QModelIndex &sourceIndex, int begin, int end) -{ - beginRemoveRows(mapFromSource(sourceIndex), begin, end); - for (int i=begin; i<=end; i++) { - QModelIndex child = sourceModel()->index(i, 0, sourceIndex); - m_sourceIndexesList.removeAll(child); - } -} - -void LibraryModel::onSourceRowsRemoved(const QModelIndex &/*sourceIndex*/, int /*begin*/, int /*end*/) -{ - endRemoveRows(); -} - -void LibraryModel::onSourceLayoutChanged() -{ - m_sourceIndexesList.clear(); - emit layoutChanged(); -} - -void LibraryModel::sort(int column, Qt::SortOrder order) -{ - sourceModel()->sort(column, order); -} diff -Nru zanshin-0.1+svn1006410/src/librarymodel.h zanshin-0.2.0/src/librarymodel.h --- zanshin-0.1+svn1006410/src/librarymodel.h 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/librarymodel.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - Copyright 2008, 2009 Mario Bensi - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#ifndef ZANSHIN_LIBRARYMODEL_H -#define ZANSHIN_LIBRARYMODEL_H - -#include -#include - -class LibraryModel : public QAbstractProxyModel -{ - Q_OBJECT - -public: - enum LibraryType { - Projects = 0, - Contexts - }; - - - LibraryModel(QObject *parent = 0); - virtual ~LibraryModel(); - - LibraryType type() const; - void setType(LibraryType type); - - virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - virtual QModelIndex parent(const QModelIndex &index) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; - - Qt::DropActions supportedDropActions() const; - virtual QStringList mimeTypes() const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - virtual QMimeData *mimeData(const QModelIndexList &indexes) const; - virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - - bool isInbox(const QModelIndex &index) const; - bool isLibraryRoot(const QModelIndex &index) const; - - virtual void setSourceModel(QAbstractItemModel *sourceModel); - - virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const; - virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; - - virtual void sort(int column, Qt::SortOrder order=Qt::AscendingOrder); - -private slots: - void onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end); - void onSourceRowsAboutToBeInserted(const QModelIndex &sourceIndex, int begin, int end); - void onSourceRowsInserted(const QModelIndex &sourceIndex, int begin, int end); - void onSourceRowsAboutToBeRemoved(const QModelIndex &sourceIndex, int begin, int end); - void onSourceRowsRemoved(const QModelIndex &sourceIndex, int begin, int end); - void onSourceLayoutChanged(); - -private: - const qint64 m_inboxToken; - const qint64 m_libraryToken; - const qint64 m_tokenShift; - mutable QList m_sourceIndexesList; - LibraryType m_type; -}; - -#endif - diff -Nru zanshin-0.1+svn1006410/src/maincomponent.cpp zanshin-0.2.0/src/maincomponent.cpp --- zanshin-0.1+svn1006410/src/maincomponent.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/maincomponent.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,131 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008,2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "maincomponent.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "actionlisteditor.h" +#include "configdialog.h" +#include "globaldefs.h" +#include "sidebar.h" + +MainComponent::MainComponent(ModelStack *models, QWidget *parent, KXMLGUIClient *client) + : QObject(parent) +{ + KActionCollection *ac = client->actionCollection(); + + m_sidebar = new SideBar(models, ac, parent); + m_editor = new ActionListEditor(models, + m_sidebar->projectSelection(), + m_sidebar->categoriesSelection(), + ac, parent); + setupActions(ac); + + ac->action("project_mode")->trigger(); +} + +SideBar *MainComponent::sideBar() const +{ + return m_sidebar; +} + +ActionListEditor *MainComponent::editor() const +{ + return m_editor; +} + +void MainComponent::setupActions(KActionCollection *ac) +{ + QActionGroup *modeGroup = new QActionGroup(this); + modeGroup->setExclusive(true); + + KAction *action = ac->addAction("project_mode", this, SLOT(onModeSwitch())); + action->setText(i18n("Project View")); + action->setIcon(KIcon("view-pim-tasks")); + action->setShortcut(Qt::CTRL | Qt::Key_P); + action->setCheckable(true); + action->setData(Zanshin::ProjectMode); + modeGroup->addAction(action); + + action = ac->addAction("categories_mode", this, SLOT(onModeSwitch())); + action->setText(i18n("Context View")); + action->setIcon(KIcon("view-pim-notes")); + action->setShortcut(Qt::CTRL | Qt::Key_O); + action->setCheckable(true); + action->setData(Zanshin::CategoriesMode); + modeGroup->addAction(action); + + action = ac->addAction("synchronize_all", this, SLOT(onSynchronizeAll())); + action->setText(i18n("Synchronize All")); + action->setIcon(KIcon("view-refresh")); + action->setShortcut(Qt::CTRL | Qt::Key_L); +} + +void MainComponent::saveColumnsState(KConfigGroup &cg) const +{ + m_editor->saveColumnsState(cg); +} + +void MainComponent::restoreColumnsState(const KConfigGroup &cg) +{ + m_editor->restoreColumnsState(cg); +} + +void MainComponent::onModeSwitch() +{ + KAction *action = static_cast(sender()); + m_editor->setMode((Zanshin::ApplicationMode)action->data().toInt()); + m_sidebar->setMode((Zanshin::ApplicationMode)action->data().toInt()); +} + +void MainComponent::onSynchronizeAll() +{ + Akonadi::AgentInstance::List agents = Akonadi::AgentManager::self()->instances(); + while (!agents.isEmpty()) { + Akonadi::AgentInstance agent = agents.takeFirst(); + + if (agent.type().mimeTypes().contains("application/x-vnd.akonadi.calendar.todo")) { + agent.synchronize(); + } + } +} + +void MainComponent::showConfigDialog() +{ + ConfigDialog dialog(static_cast(parent())); + dialog.exec(); +} diff -Nru zanshin-0.1+svn1006410/src/maincomponent.h zanshin-0.2.0/src/maincomponent.h --- zanshin-0.1+svn1006410/src/maincomponent.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/maincomponent.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,65 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_MAINCOMPONENT_H +#define ZANSHIN_MAINCOMPONENT_H + +#include + +class KActionCollection; +class KConfigGroup; +class KXMLGUIClient; + +class ActionListEditor; +class ModelStack; +class SideBar; + +class MainComponent : public QObject +{ + Q_OBJECT + +public: + MainComponent(ModelStack *models, QWidget *parent, KXMLGUIClient *client); + + SideBar *sideBar() const; + ActionListEditor *editor() const; + + void saveColumnsState(KConfigGroup &group) const; + void restoreColumnsState(const KConfigGroup &group); + +public slots: + void showConfigDialog(); + +private slots: + void onModeSwitch(); + void onSynchronizeAll(); + +private: + void setupActions(KActionCollection *ac); + + SideBar *m_sidebar; + ActionListEditor *m_editor; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/main.cpp zanshin-0.2.0/src/main.cpp --- zanshin-0.1+svn1006410/src/main.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/main.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Zanshin Todo. - Copyright 2008 Kevin Ottens + Copyright 2008-2010 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -26,33 +26,84 @@ #include #include +#include "aboutdata.h" +#include "debugwindow.h" #include "mainwindow.h" +#include "modelstack.h" -int main(int argc, char **argv) +/* +template +void createViews(TodoModel *baseModel) { - KAboutData about("zanshin", "zanshin", - ki18n("Zanshin Todo"), "0.1", - ki18n("A Getting Things Done application which aims at getting your mind like water"), - KAboutData::License_GPL_V3, - ki18n("Copyright 2008, Kevin Ottens ")); - - about.addAuthor(ki18n("Kevin Ottens"), - ki18n("Lead Developer"), - "ervin@kde.org"); - - about.addAuthor(ki18n("Mario Bensi"), - ki18n("Developer"), - "nef@ipsquad.net"); + ProxyModel *proxy = new ProxyModel; + proxy->setSourceModel(baseModel); + + SideBarModel *sidebarModel = new SideBarModel; + sidebarModel->setSourceModel(proxy); + + QString className = proxy->metaObject()->className(); - //TODO: Remove once we have a proper icon - about.setProgramIconName("office-calendar"); + Akonadi::EntityTreeView *sidebar = new Akonadi::EntityTreeView; + sidebar->setWindowTitle(className+"/SideBar"); + sidebar->setModel(sidebarModel); + sidebar->setSelectionMode(QAbstractItemView::ExtendedSelection); + sidebar->show(); + + SelectionProxyModel *selectionProxy = new SelectionProxyModel(sidebar->selectionModel()); + selectionProxy->setSourceModel(proxy); + + Akonadi::EntityTreeView *mainView = new Akonadi::EntityTreeView; + mainView->setItemsExpandable(false); + mainView->setWindowTitle(className); + mainView->setModel(selectionProxy); + + QObject::connect(selectionProxy, SIGNAL(modelReset()), + mainView, SLOT(expandAll())); + QObject::connect(selectionProxy, SIGNAL(layoutChanged()), + mainView, SLOT(expandAll())); + QObject::connect(selectionProxy, SIGNAL(rowsInserted(QModelIndex,int,int)), + mainView, SLOT(expandAll())); + + mainView->setSelectionMode(QAbstractItemView::ExtendedSelection); + mainView->setItemDelegate(new ActionListDelegate(mainView)); + + mainView->show(); +} +*/ +int main(int argc, char **argv) +{ + KAboutData about = Zanshin::getAboutData(); KCmdLineArgs::init(argc, argv, &about); + KCmdLineOptions options; + options.add("debug", ki18n("Show the debug window")); + + KCmdLineArgs::addCmdLineOptions(options); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + KApplication app; - QWidget *widget = new MainWindow; - widget->show(); + ModelStack models; + + if (args->isSet("debug")) { + DebugWindow *debugWindow = new DebugWindow(&models); + debugWindow->show(); + } + + MainWindow *mainWindow = new MainWindow(&models); + mainWindow->show(); + + /* + TodoModel *todoModel = new TodoModel( changeRecorder ); + Akonadi::EntityTreeView *view = new Akonadi::EntityTreeView; + view->setWindowTitle("TodoModel"); + view->setModel(todoModel); + view->show(); + + createViews(todoModel); + createViews(todoModel); + */ return app.exec(); } diff -Nru zanshin-0.1+svn1006410/src/mainwindow.cpp zanshin-0.2.0/src/mainwindow.cpp --- zanshin-0.1+svn1006410/src/mainwindow.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/mainwindow.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -1,7 +1,7 @@ /* This file is part of Zanshin Todo. - Copyright 2008 Kevin Ottens - Copyright 2008, 2009 Mario Bensi + Copyright 2008-2010 Kevin Ottens + Copyright 2008,2009 Mario Bensi This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -24,68 +24,53 @@ #include "mainwindow.h" -#include - -#include - #include #include #include #include #include +#include +#include #include #include #include "actionlisteditor.h" -#include "actionlistview.h" #include "configdialog.h" -#include "globalmodel.h" -#include "globalsettings.h" -#include "todoflatmodel.h" +#include "globaldefs.h" +#include "maincomponent.h" #include "sidebar.h" -MainWindow::MainWindow(QWidget *parent) - : KXmlGuiWindow(parent) +MainWindow::MainWindow(ModelStack *models, QWidget *parent) + : KXmlGuiWindow(parent), + m_component(new MainComponent(models, this, this)) { - Akonadi::Control::start(); - setupSideBar(); setupCentralWidget(); setupActions(); - setupGUI(); + setupGUI(QSize(1024, 600), ToolBar | Keys | Save | Create); - restoreColumnState(); - applySettings(); + restoreColumnsState(); - actionCollection()->action("project_mode")->trigger(); + KConfigGroup config(KGlobal::config(), "General"); + if (config.readEntry("firstRun", true)) { + QTimer::singleShot(0, m_component, SLOT(showConfigDialog())); + config.writeEntry("firstRun", false); + } } void MainWindow::setupCentralWidget() { - m_editor = new ActionListEditor(this, actionCollection()); - - connect(m_sidebar, SIGNAL(noProjectInboxActivated()), - m_editor, SLOT(showNoProjectInbox())); - connect(m_sidebar, SIGNAL(projectActivated(QModelIndex)), - m_editor, SLOT(focusOnProject(QModelIndex))); - connect(m_sidebar, SIGNAL(noContextInboxActivated()), - m_editor, SLOT(showNoContextInbox())); - connect(m_sidebar, SIGNAL(contextActivated(QModelIndex)), - m_editor, SLOT(focusOnContext(QModelIndex))); - - setCentralWidget(m_editor); + setCentralWidget(m_component->editor()); } void MainWindow::setupSideBar() { - m_sidebar = new SideBar(this, actionCollection()); - QDockWidget *dock = new QDockWidget(this); dock->setObjectName("SideBar"); dock->setFeatures(dock->features() & ~QDockWidget::DockWidgetClosable); - dock->setWidget(m_sidebar); + dock->setWidget(m_component->sideBar()); addDockWidget(Qt::LeftDockWidgetArea, dock); } @@ -93,24 +78,11 @@ { KActionCollection *ac = actionCollection(); - QActionGroup *modeGroup = new QActionGroup(this); - modeGroup->setExclusive(true); - - KAction *action = ac->addAction("project_mode", m_sidebar, SLOT(switchToProjectMode())); - action->setText(i18n("Project View")); - action->setIcon(KIcon("view-pim-tasks")); - action->setShortcut(Qt::CTRL | Qt::Key_P); - action->setCheckable(true); - modeGroup->addAction(action); - - action = ac->addAction("context_mode", m_sidebar, SLOT(switchToContextMode())); - action->setText(i18n("Context View")); - action->setIcon(KIcon("view-pim-notes")); - action->setShortcut(Qt::CTRL | Qt::Key_O); - action->setCheckable(true); - modeGroup->addAction(action); + KAction *action = ac->addAction(KStandardAction::ShowMenubar); + connect(action, SIGNAL(toggled(bool)), + menuBar(), SLOT(setVisible(bool))); - ac->addAction(KStandardAction::Preferences, this, SLOT(showConfigDialog())); + ac->addAction(KStandardAction::Preferences, m_component, SLOT(showConfigDialog())); ac->addAction(KStandardAction::Quit, this, SLOT(close())); } @@ -129,51 +101,12 @@ void MainWindow::saveColumnsState() { KConfigGroup cg = autoSaveConfigGroup(); - QByteArray state = m_editor->view()->header()->saveState(); - cg.writeEntry("MainHeaderState", state.toBase64()); + m_component->saveColumnsState(cg); } -void MainWindow::restoreColumnState() +void MainWindow::restoreColumnsState() { KConfigGroup cg = autoSaveConfigGroup(); - QByteArray state; - if (cg.hasKey("MainHeaderState")) { - state = cg.readEntry("MainHeaderState", state); - m_editor->view()->header()->restoreState(QByteArray::fromBase64(state)); - } -} - -void MainWindow::showConfigDialog() -{ - if (KConfigDialog::showDialog("settings")) { - return; - } - - ConfigDialog *dialog = new ConfigDialog(this, "settings", - GlobalSettings::self()); - - connect(dialog, SIGNAL(settingsChanged(const QString&)), - this, SLOT(applySettings())); - - dialog->show(); + m_component->restoreColumnsState(cg); } -void MainWindow::applySettings() -{ - Akonadi::Collection collection(GlobalSettings::collectionId()); - - if (!collection.isValid()) { - showConfigDialog(); - return; - } - - Akonadi::CollectionFetchJob *job = new Akonadi::CollectionFetchJob(collection, Akonadi::CollectionFetchJob::Base); - job->exec(); - - if (job->collections().isEmpty()) { - showConfigDialog(); - return; - } - - GlobalModel::todoFlat()->setCollection(job->collections().first()); -} diff -Nru zanshin-0.1+svn1006410/src/mainwindow.h zanshin-0.2.0/src/mainwindow.h --- zanshin-0.1+svn1006410/src/mainwindow.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/mainwindow.h 2011-11-25 15:00:52.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Zanshin Todo. - Copyright 2008 Kevin Ottens + Copyright 2008-2010 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -26,24 +26,18 @@ #include -#include - -#include - -class ActionListEditor; -class SideBar; +class ModelStack; +class MainComponent; class MainWindow : public KXmlGuiWindow { Q_OBJECT public: - MainWindow(QWidget *parent = 0); + MainWindow(ModelStack *models, QWidget *parent = 0); protected slots: void saveAutoSaveSettings(); - void showConfigDialog(); - void applySettings(); protected: virtual void closeEvent(QCloseEvent *event); @@ -54,10 +48,9 @@ void setupActions(); void saveColumnsState(); - void restoreColumnState(); + void restoreColumnsState(); - SideBar *m_sidebar; - ActionListEditor *m_editor; + MainComponent *m_component; }; #endif diff -Nru zanshin-0.1+svn1006410/src/modelstack.cpp zanshin-0.2.0/src/modelstack.cpp --- zanshin-0.1+svn1006410/src/modelstack.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/modelstack.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,207 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "modelstack.h" + +#include +#include +#include +#include +#include + +#include "categorymanager.h" +#include "combomodel.h" +#include "sidebarmodel.h" +#include "selectionproxymodel.h" +#include "todocategoriesmodel.h" +#include "todometadatamodel.h" +#include "todomodel.h" +#include "todotreemodel.h" + +#include "kdescendantsproxymodel.h" + +ModelStack::ModelStack(QObject *parent) + : QObject(parent), + m_entityModel(0), + m_baseModel(0), + m_collectionsModel(0), + m_treeModel(0), + m_treeSideBarModel(0), + m_treeSelectionModel(0), + m_treeComboModel(0), + m_categoriesModel(0), + m_categoriesSideBarModel(0), + m_categoriesSelectionModel(0), + m_categoriesComboModel(0), + m_treeSelection(0), + m_categorySelection(0) +{ +} + +QAbstractItemModel *ModelStack::baseModel() +{ + if (!m_baseModel) { + Akonadi::Session *session = new Akonadi::Session("zanshin", this); + + Akonadi::ItemFetchScope itemScope; + itemScope.fetchFullPayload(); + itemScope.setAncestorRetrieval(Akonadi::ItemFetchScope::All); + + Akonadi::CollectionFetchScope collectionScope; + collectionScope.setAncestorRetrieval(Akonadi::CollectionFetchScope::All); + + Akonadi::ChangeRecorder *changeRecorder = new Akonadi::ChangeRecorder(this); + changeRecorder->setCollectionMonitored(Akonadi::Collection::root()); + changeRecorder->setMimeTypeMonitored("application/x-vnd.akonadi.calendar.todo"); + changeRecorder->setCollectionFetchScope(collectionScope); + changeRecorder->setItemFetchScope(itemScope); + changeRecorder->setSession(session); + + m_entityModel = new TodoModel(changeRecorder, this); + TodoMetadataModel *metadataModel = new TodoMetadataModel(this); + metadataModel->setSourceModel(m_entityModel); + m_baseModel = metadataModel; + } + return m_baseModel; +} + +QAbstractItemModel *ModelStack::collectionsModel() +{ + if (!m_collectionsModel) { + Akonadi::EntityMimeTypeFilterModel *collectionsModel = new Akonadi::EntityMimeTypeFilterModel(this); + collectionsModel->addMimeTypeInclusionFilter( Akonadi::Collection::mimeType() ); + collectionsModel->setSourceModel(m_entityModel); + m_collectionsModel = collectionsModel; + } + return m_collectionsModel; +} + +QAbstractItemModel *ModelStack::treeModel() +{ + if (!m_treeModel) { + TodoTreeModel *treeModel = new TodoTreeModel(this); + treeModel->setSourceModel(baseModel()); + m_treeModel = treeModel; + } + return m_treeModel; +} + +QAbstractItemModel *ModelStack::treeSideBarModel() +{ + if (!m_treeSideBarModel) { + SideBarModel *treeSideBarModel = new SideBarModel(this); + treeSideBarModel->setSourceModel(treeModel()); + m_treeSideBarModel = treeSideBarModel; + } + return m_treeSideBarModel; +} + +QAbstractItemModel *ModelStack::treeSelectionModel() +{ + if (!m_treeSelectionModel) { + SelectionProxyModel *treeSelectionModel = new SelectionProxyModel(this); + treeSelectionModel->setSelectionModel(m_treeSelection); + treeSelectionModel->setSourceModel(treeModel()); + m_treeSelectionModel = treeSelectionModel; + } + return m_treeSelectionModel; +} + +QAbstractItemModel *ModelStack::treeComboModel() +{ + if (!m_treeComboModel) { + ComboModel *treeComboModel = new ComboModel(this); + + KDescendantsProxyModel *descendantProxyModel = new KDescendantsProxyModel(treeComboModel); + descendantProxyModel->setSourceModel(treeSideBarModel()); + descendantProxyModel->setDisplayAncestorData(true); + + treeComboModel->setSourceModel(descendantProxyModel); + m_treeComboModel = treeComboModel; + } + return m_treeComboModel; +} + +QAbstractItemModel *ModelStack::categoriesModel() +{ + if (!m_categoriesModel) { + CategoryManager::instance().setModel(baseModel()); + TodoCategoriesModel *categoriesModel = new TodoCategoriesModel(this); + categoriesModel->setSourceModel(baseModel()); + m_categoriesModel = categoriesModel; + } + return m_categoriesModel; +} + +QAbstractItemModel *ModelStack::categoriesSideBarModel() +{ + if (!m_categoriesSideBarModel) { + SideBarModel *categoriesSideBarModel = new SideBarModel(this); + categoriesSideBarModel->setSourceModel(categoriesModel()); + m_categoriesSideBarModel = categoriesSideBarModel; + } + return m_categoriesSideBarModel; +} + +QAbstractItemModel *ModelStack::categoriesSelectionModel() +{ + if (!m_categoriesSelectionModel) { + SelectionProxyModel *categoriesSelectionModel = new SelectionProxyModel(this); + categoriesSelectionModel->setSelectionModel(m_categorySelection); + categoriesSelectionModel->setSourceModel(categoriesModel()); + m_categoriesSelectionModel = categoriesSelectionModel; + } + return m_categoriesSelectionModel; +} + +QAbstractItemModel *ModelStack::categoriesComboModel() +{ + if (!m_categoriesComboModel) { + ComboModel *categoriesComboModel = new ComboModel(this); + + KDescendantsProxyModel *descendantProxyModel = new KDescendantsProxyModel(categoriesComboModel); + descendantProxyModel->setSourceModel(categoriesSideBarModel()); + descendantProxyModel->setDisplayAncestorData(true); + + categoriesComboModel->setSourceModel(descendantProxyModel); + m_categoriesComboModel = categoriesComboModel; + } + return m_categoriesComboModel; +} + +void ModelStack::setItemTreeSelectionModel(QItemSelectionModel *selection) +{ + m_treeSelection = selection; + if (m_treeSelectionModel) { + static_cast(m_treeSelectionModel)->setSelectionModel(m_treeSelection); + } +} + +void ModelStack::setItemCategorySelectionModel(QItemSelectionModel *selection) +{ + m_categorySelection = selection; + if (m_categoriesSelectionModel) { + static_cast(m_categoriesSelectionModel)->setSelectionModel(m_categorySelection); + } +} diff -Nru zanshin-0.1+svn1006410/src/modelstack.h zanshin-0.2.0/src/modelstack.h --- zanshin-0.1+svn1006410/src/modelstack.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/modelstack.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,75 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_MODELSTACK_H +#define ZANSHIN_MODELSTACK_H + +#include + +class QItemSelectionModel; +class QAbstractItemModel; + +class ModelStack : public QObject +{ + Q_OBJECT + +public: + explicit ModelStack(QObject *parent = 0); + + QAbstractItemModel *baseModel(); + + QAbstractItemModel *collectionsModel(); + + QAbstractItemModel *treeModel(); + QAbstractItemModel *treeSideBarModel(); + QAbstractItemModel *treeSelectionModel(); + QAbstractItemModel *treeComboModel(); + + QAbstractItemModel *categoriesModel(); + QAbstractItemModel *categoriesSideBarModel(); + QAbstractItemModel *categoriesSelectionModel(); + QAbstractItemModel *categoriesComboModel(); + + void setItemTreeSelectionModel(QItemSelectionModel *selection); + void setItemCategorySelectionModel(QItemSelectionModel *selection); + +private: + QAbstractItemModel *m_entityModel; + QAbstractItemModel *m_baseModel; + QAbstractItemModel *m_collectionsModel; + + QAbstractItemModel *m_treeModel; + QAbstractItemModel *m_treeSideBarModel; + QAbstractItemModel *m_treeSelectionModel; + QAbstractItemModel *m_treeComboModel; + + QAbstractItemModel *m_categoriesModel; + QAbstractItemModel *m_categoriesSideBarModel; + QAbstractItemModel *m_categoriesSelectionModel; + QAbstractItemModel *m_categoriesComboModel; + QItemSelectionModel *m_treeSelection; + QItemSelectionModel *m_categorySelection; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/part.cpp zanshin-0.2.0/src/part.cpp --- zanshin-0.1+svn1006410/src/part.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/part.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,58 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "part.h" + +#include +#include + +#include + +#include "aboutdata.h" +#include "actionlisteditor.h" +#include "maincomponent.h" +#include "modelstack.h" +#include "sidebar.h" + +K_PLUGIN_FACTORY(PartFactory, registerPlugin();) +K_EXPORT_PLUGIN(PartFactory(Zanshin::getAboutData())) + +Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList &) + : KParts::ReadOnlyPart(parent), + m_models(new ModelStack(this)), + m_splitter(new QSplitter(parentWidget)), + m_component(new MainComponent(m_models, m_splitter, this)) +{ + m_splitter->addWidget(m_component->sideBar()); + m_splitter->addWidget(m_component->editor()); + + setComponentData(PartFactory::componentData()); + setWidget(m_splitter); + setXMLFile(KStandardDirs::locate("data", "zanshin/zanshin_part.rc")); +} + +bool Part::openFile() +{ + return false; +} + diff -Nru zanshin-0.1+svn1006410/src/part.h zanshin-0.2.0/src/part.h --- zanshin-0.1+svn1006410/src/part.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/part.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,51 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_PART_H +#define ZANSHIN_PART_H + +#include + +class QSplitter; + +class ModelStack; +class MainComponent; + +class Part : public KParts::ReadOnlyPart +{ + Q_OBJECT + +public: + Part(QWidget *parentWidget, QObject *parent, const QVariantList &); + +protected: + virtual bool openFile(); + +private: + ModelStack *m_models; + QSplitter *m_splitter; + MainComponent *m_component; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/projectsmodel.cpp zanshin-0.2.0/src/projectsmodel.cpp --- zanshin-0.1+svn1006410/src/projectsmodel.cpp 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/projectsmodel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "projectsmodel.h" - -#include "todotreemodel.h" -#include "todoflatmodel.h" - -ProjectsModel::ProjectsModel(QObject *parent) - : QSortFilterProxyModel(parent) -{ - setDynamicSortFilter(true); -} - -ProjectsModel::~ProjectsModel() -{ -} - -void ProjectsModel::setSourceModel(QAbstractItemModel *sourceModel) -{ - if (treeModel()) { - disconnect(treeModel()); - } - - Q_ASSERT(sourceModel==0 || qobject_cast(sourceModel)!=0); - QSortFilterProxyModel::setSourceModel(sourceModel); - - connect(treeModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(invalidate())); - connect(treeModel(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)), - this, SLOT(invalidate())); - connect(treeModel(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(invalidate())); -} - -TodoTreeModel *ProjectsModel::treeModel() const -{ - return qobject_cast(sourceModel()); -} - -bool ProjectsModel::filterAcceptsColumn(int sourceColumn, const QModelIndex &/*sourceParent*/) const -{ - return sourceColumn==TodoFlatModel::Summary; -} - -bool ProjectsModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - QModelIndex index = treeModel()->index(sourceRow, TodoFlatModel::RowType, sourceParent); - return treeModel()->data(index).toInt()!=TodoFlatModel::StandardTodo; -} - diff -Nru zanshin-0.1+svn1006410/src/projectsmodel.h zanshin-0.2.0/src/projectsmodel.h --- zanshin-0.1+svn1006410/src/projectsmodel.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/projectsmodel.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#ifndef ZANSHIN_PROJECTSMODEL_H -#define ZANSHIN_PROJECTSMODEL_H - -#include - -class TodoTreeModel; - -class ProjectsModel : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - ProjectsModel(QObject *parent = 0); - virtual ~ProjectsModel(); - - virtual void setSourceModel(QAbstractItemModel *sourceModel); - -protected: - virtual bool filterAcceptsColumn(int sourceColumn, const QModelIndex &sourceParent) const; - virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; - -private: - TodoTreeModel *treeModel() const; -}; - -#endif - diff -Nru zanshin-0.1+svn1006410/src/quickselectdialog.cpp zanshin-0.2.0/src/quickselectdialog.cpp --- zanshin-0.1+svn1006410/src/quickselectdialog.cpp 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/quickselectdialog.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -25,23 +25,28 @@ #include #include +#include +#include +#include #include #include -#include "globalmodel.h" -#include "projectsmodel.h" -#include "contextsmodel.h" +#include "globaldefs.h" #include "todocategoriesmodel.h" -#include "todoflatmodel.h" #include "todotreemodel.h" -QuickSelectDialog::QuickSelectDialog(QWidget *parent, Mode mode, ActionType action) - : KDialog(parent), m_tree(0), m_mode(mode) +QuickSelectDialog::QuickSelectDialog(QWidget *parent, QAbstractItemModel *model, Zanshin::ApplicationMode mode, ActionType action) + : KDialog(parent), + m_label(0), + m_tree(0), + m_filter(new KRecursiveFilterProxyModel(this)), + m_model(model), + m_mode(mode) { QString caption; - if (mode==ContextMode) { + if (mode==Zanshin::CategoriesMode) { switch (action) { case MoveAction: caption = i18n("Move Actions to Context"); @@ -53,16 +58,16 @@ caption = i18n("Jump to Context"); break; } - } else if (mode==ProjectMode) { + } else if (mode==Zanshin::ProjectMode) { switch (action) { case MoveAction: - caption = i18n("Move Actions to Project or Folder"); + caption = i18n("Move Actions to Project"); break; case CopyAction: - caption = i18n("Copy Actions to Project or Folder"); + caption = i18n("Copy Actions to Project"); break; case JumpAction: - caption = i18n("Jump to Project or Folder"); + caption = i18n("Jump to Project"); break; } } else { @@ -75,47 +80,116 @@ QWidget *page = mainWidget(); page->setLayout(new QVBoxLayout(page)); + m_label = new QLabel(page); + page->layout()->addWidget(m_label); + m_tree = new QTreeView(page); m_tree->setSortingEnabled(true); m_tree->sortByColumn(0, Qt::AscendingOrder); page->layout()->addWidget(m_tree); - switch (mode) { - case ProjectMode: - m_tree->setModel(GlobalModel::projects()); - break; - case ContextMode: - m_tree->setModel(GlobalModel::contexts()); - break; - } + m_filter->setDynamicSortFilter(true); + m_filter->setFilterCaseSensitivity(Qt::CaseInsensitive); + m_filter->setSourceModel(m_model); + m_tree->setModel(m_filter); m_tree->setSelectionMode(QAbstractItemView::SingleSelection); - m_tree->setCurrentIndex(m_tree->model()->index(0, 0)); + m_tree->setCurrentIndex(m_filter->index(0, 0)); m_tree->expandAll(); m_tree->setFocus(Qt::OtherFocusReason); + + m_tree->installEventFilter(this); + applyPattern(QString()); } QString QuickSelectDialog::selectedId() const { - if (m_mode==ProjectMode) { + if (m_mode==Zanshin::ProjectMode) { return projectSelectedId(); } else { - return contextSelectedId(); + return categorySelectedId(); } } -QString QuickSelectDialog::contextSelectedId() const +Zanshin::ItemType QuickSelectDialog::selectedType() const { QModelIndex index = m_tree->selectionModel()->currentIndex(); - QModelIndex sourceIndex = GlobalModel::contexts()->mapToSource(index); + return (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); +} - return GlobalModel::todoCategories()->data(sourceIndex.sibling(sourceIndex.row(), 0)).toString(); +QString QuickSelectDialog::categorySelectedId() const +{ + QModelIndex index = m_tree->selectionModel()->currentIndex(); + return index.data(Zanshin::CategoryPathRole).toString(); } QString QuickSelectDialog::projectSelectedId() const { QModelIndex index = m_tree->selectionModel()->currentIndex(); - QModelIndex sourceIndex = GlobalModel::projects()->mapToSource(index); + return index.data(Zanshin::UidRole).toString(); +} - return GlobalModel::todoTree()->data(sourceIndex.sibling(sourceIndex.row(), TodoFlatModel::RemoteId)).toString(); +Akonadi::Collection QuickSelectDialog::collection() const +{ + QModelIndex index = m_tree->selectionModel()->currentIndex(); + Akonadi::Collection collection; + Zanshin::ItemType type = (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); + if (type == Zanshin::Collection) { + collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value(); + } else { + collection = index.data(Akonadi::EntityTreeModel::ParentCollectionRole).value(); + } + return collection; +} + +QModelIndex QuickSelectDialog::selectedIndex() const +{ + return m_tree->selectionModel()->currentIndex(); +} + +QString QuickSelectDialog::pattern() const +{ + return m_filter->filterRegExp().pattern(); +} + +void QuickSelectDialog::applyPattern(const QString &pattern) +{ + if (pattern.isEmpty()) { + QString type = i18n("projects"); + if (m_mode==Zanshin::CategoriesMode) { + type = i18n("contexts"); + } + + m_label->setText(i18n("You can start typing to filter the list of %1.", type)); + } else { + m_label->setText(i18n("Path: %1", pattern)); + } + + m_filter->setFilterFixedString(pattern); + m_tree->expandAll(); +} + +bool QuickSelectDialog::eventFilter(QObject *, QEvent *ev) +{ + if (ev->type() == QEvent::KeyPress) { + const QKeyEvent * const event = static_cast(ev); + QString p = pattern(); + + switch (event->key()) { + case Qt::Key_Backspace: + p.chop(1); + break; + case Qt::Key_Delete: + p = QString(); + break; + default: + if (event->text().contains(QRegExp("^(\\w| )+$"))) { + p+= event->text(); + } + break; + } + + applyPattern(p); + } + return false; } diff -Nru zanshin-0.1+svn1006410/src/quickselectdialog.h zanshin-0.2.0/src/quickselectdialog.h --- zanshin-0.1+svn1006410/src/quickselectdialog.h 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/quickselectdialog.h 2011-11-25 15:00:52.000000000 +0000 @@ -26,35 +26,48 @@ #include +#include "globaldefs.h" + +class QAbstractItemModel; +class QLabel; class QTreeView; +namespace Akonadi +{ + class Collection; +} +class KRecursiveFilterProxyModel; class QuickSelectDialog : public KDialog { Q_OBJECT - Q_ENUMS(Mode) public: - enum Mode { - ContextMode, - ProjectMode - }; - enum ActionType { MoveAction, CopyAction, JumpAction }; - QuickSelectDialog(QWidget *parent, Mode mode, ActionType action); + QuickSelectDialog(QWidget *parent, QAbstractItemModel *model, Zanshin::ApplicationMode mode, ActionType action); QString selectedId() const; + Zanshin::ItemType selectedType() const; + Akonadi::Collection collection() const; + QModelIndex selectedIndex() const; private: - QString contextSelectedId() const; + QString categorySelectedId() const; QString projectSelectedId() const; + QString pattern() const; + void applyPattern(const QString &pattern); + bool eventFilter(QObject *object, QEvent *ev); + + QLabel *m_label; QTreeView *m_tree; - Mode m_mode; + KRecursiveFilterProxyModel *m_filter; + QAbstractItemModel *m_model; + Zanshin::ApplicationMode m_mode; }; #endif diff -Nru zanshin-0.1+svn1006410/src/runner/CMakeLists.txt zanshin-0.2.0/src/runner/CMakeLists.txt --- zanshin-0.1+svn1006410/src/runner/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/runner/CMakeLists.txt 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,12 @@ +include_directories(..) + +set(krunner_zanshin_SRCS + zanshinrunner.cpp +) + +kde4_add_plugin(krunner_zanshin ${krunner_zanshin_SRCS}) +target_link_libraries(krunner_zanshin ${KDE4_PLASMA_LIBS} ${KDEPIMLIBS_AKONADI_LIBS} ${KDEPIMLIBS_KCALCORE_LIBS}) + +install(TARGETS krunner_zanshin DESTINATION ${PLUGIN_INSTALL_DIR}) +install(FILES plasma-runner-zanshin.desktop DESTINATION ${SERVICES_INSTALL_DIR}) + diff -Nru zanshin-0.1+svn1006410/src/runner/plasma-runner-zanshin.desktop zanshin-0.2.0/src/runner/plasma-runner-zanshin.desktop --- zanshin-0.1+svn1006410/src/runner/plasma-runner-zanshin.desktop 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/runner/plasma-runner-zanshin.desktop 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,40 @@ +[Desktop Entry] +Name=Zanshin Todo +Name[da]=Zanshin gøremål +Name[de]=Zanshin-Aufgaben +Name[es]=Tareas pendientes de Zanshin +Name[et]=Zanshini ülesanded +Name[fr]=Tâche Zanshin +Name[it]=Cose da fare Zanshin +Name[km]=ការ​ងារ​ត្រូវ​ធ្វើ​របស់ Zanshin +Name[nl]=Zanshin taken +Name[pl]=Do zrobienia Zanshin +Name[pt]=Itens Por-Fazer Zanshin +Name[pt_BR]=Lista de tarefas Zanshin +Name[sv]=Zanshin uppgifter +Name[uk]=Завдання Zanshin +Name[x-test]=xxZanshin Todoxx +Comment=Add tasks to your todo list +Comment[da]=Tilføjer opgaver til din gøremålsliste +Comment[de]=Aufgaben zu Ihrer Aufgabenliste hinzufügen +Comment[es]=Añadir tareas a su lista de pendientes +Comment[et]=Ülesannete lisamine sinu ülesannete nimekirja. +Comment[fr]=Ajoute des tâches à votre liste de tâches +Comment[it]=Aggiungi attività alla lista delle tue cose da fare +Comment[km]=បន្ថែម​ភារកិច្ច​ទៅ​​ក្នុង​បញ្ជី​ការងារ​ត្រូវ​ធ្វើ​របស់​អ្នក +Comment[nl]=Voeg taken toe aan uw lijst met taken +Comment[pl]=Dodaj zadania do swojej listy 'do zrobienia' +Comment[pt]=Adicionar tarefas à sua lista de itens por-fazer +Comment[pt_BR]=Adicionar tarefas à sua lista +Comment[sv]=Lägg till uppgifter i uppgiftslistan +Comment[uk]=Додавання завдань до вашого списку +Comment[x-test]=xxAdd tasks to your todo listxx +X-KDE-ServiceTypes=Plasma/Runner +Type=Service +Icon=zanshin +X-KDE-Library=krunner_zanshin +X-KDE-PluginInfo-Author=Zanshin Team +X-KDE-PluginInfo-Name=org.kde.zanshin +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-License=LGPL +X-KDE-PluginInfo-EnabledByDefault=true diff -Nru zanshin-0.1+svn1006410/src/runner/zanshinrunner.cpp zanshin-0.2.0/src/runner/zanshinrunner.cpp --- zanshin-0.1+svn1006410/src/runner/zanshinrunner.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/runner/zanshinrunner.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,121 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "zanshinrunner.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +ZanshinRunner::ZanshinRunner(QObject *parent, const QVariantList &args) + : Plasma::AbstractRunner(parent, args) +{ + setObjectName(QLatin1String("Zanshin")); + setIgnoredTypes(Plasma::RunnerContext::Directory | Plasma::RunnerContext::File | + Plasma::RunnerContext::NetworkLocation | Plasma::RunnerContext::Help); +} + +ZanshinRunner::~ZanshinRunner() +{ +} + +void ZanshinRunner::match(Plasma::RunnerContext &context) +{ + const QString command = context.query().trimmed(); + + if (!command.startsWith("todo:", Qt::CaseInsensitive)) { + return; + } + + const QString summary = command.mid(5).trimmed(); + + if (summary.isEmpty()) { + return; + } + + QList matches; + + Plasma::QueryMatch match(this); + match.setData(summary); + match.setType(Plasma::QueryMatch::ExactMatch); + match.setIcon(KIcon("zanshin")); + match.setText(i18n("Add \"%1\" to your todo list", summary)); + match.setRelevance(1.0); + + matches << match; + context.addMatches(context.query(), matches); +} + +void ZanshinRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match) +{ + Q_UNUSED(context) + + Akonadi::CollectionFetchJob *job = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), + Akonadi::CollectionFetchJob::Recursive); + job->fetchScope().setContentMimeTypes(QStringList() << "application/x-vnd.akonadi.calendar.todo"); + job->exec(); + + Akonadi::Collection::List cols = job->collections(); + + if (cols.isEmpty()) { + return; + } + + Akonadi::Collection collection; + + KConfig zanshin("zanshinrc"); + KConfigGroup config(&zanshin, "General"); + + qint64 defaultCollectionId = config.readEntry("defaultCollection", -1); + + if (defaultCollectionId > 0) { + foreach (Akonadi::Collection col, cols) { + if (col.id() == defaultCollectionId) { + collection = col; + break; + } + } + } + + if (!collection.isValid()) { + collection = cols.first(); + } + + KCalCore::Todo::Ptr todo(new KCalCore::Todo); + todo->setSummary(match.data().toString()); + + Akonadi::Item item; + item.setMimeType("application/x-vnd.akonadi.calendar.todo"); + item.setPayload(todo); + + new Akonadi::ItemCreateJob(item, collection); +} + +#include "zanshinrunner.moc" diff -Nru zanshin-0.1+svn1006410/src/runner/zanshinrunner.h zanshin-0.2.0/src/runner/zanshinrunner.h --- zanshin-0.1+svn1006410/src/runner/zanshinrunner.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/runner/zanshinrunner.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,43 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHINRUNNER_H +#define ZANSHINRUNNER_H + +#include + +class ZanshinRunner : public Plasma::AbstractRunner +{ + Q_OBJECT + + public: + ZanshinRunner(QObject *parent, const QVariantList &args); + ~ZanshinRunner(); + + void match(Plasma::RunnerContext &context); + void run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &action); +}; + +K_EXPORT_PLASMA_RUNNER(zanshin, ZanshinRunner) + +#endif diff -Nru zanshin-0.1+svn1006410/src/selectionproxymodel.cpp zanshin-0.2.0/src/selectionproxymodel.cpp --- zanshin-0.1+svn1006410/src/selectionproxymodel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/selectionproxymodel.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,232 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "selectionproxymodel.h" + +#include +#include + +#include + +#include + +class TodoModel; + +SelectionProxyModel::SelectionProxyModel(QObject *parent) + : KRecursiveFilterProxyModel(parent), + m_selectionModel(0) +{ + setDynamicSortFilter(true); +} + +SelectionProxyModel::~SelectionProxyModel() +{ +} + +void SelectionProxyModel::setSelectionModel(QItemSelectionModel *selectionModel) +{ + if (m_selectionModel == selectionModel) { + return; + } + + if (m_selectionModel) { + disconnect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); + disconnect(m_selectionModel->model(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + } + + m_selectionModel = selectionModel; + + if (selectionModel) { + connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(onSelectionChanged(QItemSelection,QItemSelection))); + connect(selectionModel->model(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(onSourceRemoveRows(QModelIndex,int,int))); + } + + initializeSelection(); + invalidate(); +} + +void SelectionProxyModel::setSourceModel(QAbstractItemModel *model) +{ + if (model == sourceModel()) { + return; + } + KRecursiveFilterProxyModel::setSourceModel(model); + initializeSelection(); +} + +void SelectionProxyModel::initializeSelection() +{ + m_selectionChain.clear(); + m_sourceChain.clear(); + m_selectedRows.clear(); + m_sourceSelectedRows.clear(); + + if (!m_selectionModel || !sourceModel()) { + return; + } + + QList selectionStack = buildModelStack(const_cast(m_selectionModel->model())); + QList sourceStack = buildModelStack(sourceModel()); + QAbstractItemModel *commonModel = findCommonModel(selectionStack, sourceStack); + + Q_ASSERT(commonModel!=0); + + m_selectionChain = createProxyChain(selectionStack, commonModel, false); + m_sourceChain = createProxyChain(sourceStack, commonModel, true); + + onSelectionChanged(QItemSelection(), QItemSelection()); +} + +QVariant SelectionProxyModel::data(const QModelIndex &index, int role) const +{ + if (role==Qt::SizeHintRole) { + QModelIndex sourceRowIndex = mapToSource(index.sibling(index.row(), 0)); + while (sourceRowIndex.isValid()) { + if (m_sourceSelectedRows.contains(sourceRowIndex)) { + return KRecursiveFilterProxyModel::data(index, role); + } + sourceRowIndex = sourceRowIndex.parent(); + } + + return QSize(0, 0); // Ultimately we don't want to display those + + } else { + return KRecursiveFilterProxyModel::data(index, role); + } +} + +bool SelectionProxyModel::acceptRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent); + + while (sourceIndex.isValid()) { + if (m_sourceSelectedRows.contains(sourceIndex)) { + return true; + } + sourceIndex = sourceIndex.parent(); + } + + return false; +} + +void SelectionProxyModel::onSelectionChanged(const QItemSelection &/*selected*/, const QItemSelection &deselected) +{ +#if QT_VERSION < 0x040800 + // The QItemSelectionModel sometimes doesn't remove deselected items from its selection + // Fixed in Qt 4.8 : http://qt.gitorious.org/qt/qt/merge_requests/2403 + QItemSelection selection = m_selectionModel->selection(); + selection.merge(deselected, QItemSelectionModel::Deselect); +#else + QItemSelection selection = m_selectionModel->selection(); +#endif + + m_selectedRows.clear(); + m_sourceSelectedRows.clear(); + + foreach (const QModelIndex &index, selection.indexes()) { + if (index.column()==0) { + kDebug() << "Added index:" << index; + m_selectedRows << index; + m_sourceSelectedRows << mapFromSelectionToSource(index); + } + } + + kDebug() << "m_selectedRows" << m_selectedRows; + kDebug() << "m_sourceSelectedRows" << m_sourceSelectedRows; + if (!m_selectedRows.isEmpty()) + invalidate(); +} + +QModelIndex SelectionProxyModel::mapFromSelectionToSource(const QModelIndex &index) const +{ + QModelIndex result = index; + Q_ASSERT(result.model()==m_selectionModel->model()); + + foreach (QAbstractProxyModel *proxy, m_selectionChain) { + result = proxy->mapToSource(result); + } + + foreach (QAbstractProxyModel *proxy, m_sourceChain) { + result = proxy->mapFromSource(result); + } + + Q_ASSERT(result.model()==sourceModel()); + return result; +} + +QList SelectionProxyModel::buildModelStack(QAbstractItemModel *topModel) const +{ + QList result; + + QAbstractItemModel *currentModel = topModel; + result << currentModel; + + while (QAbstractProxyModel *proxy = qobject_cast(currentModel)) { + currentModel = proxy->sourceModel(); + result << currentModel; + } + + return result; +} + +QAbstractItemModel *SelectionProxyModel::findCommonModel(const QList &leftStack, + const QList &rightStack) const +{ + foreach (QAbstractItemModel *model, leftStack) { + if (rightStack.contains(model)) { + return model; + } + } + + return 0; +} + +QList SelectionProxyModel::createProxyChain(const QList &modelStack, + QAbstractItemModel *commonModel, bool isBackward) +{ + QList result; + + foreach (QAbstractItemModel *model, modelStack) { + if (model==commonModel) { + break; + } + QAbstractProxyModel *proxy = qobject_cast(model); + Q_ASSERT(proxy!=0); + if (isBackward) { + result.prepend(proxy); + } else { + result.append(proxy); + } + } + + return result; +} + +void SelectionProxyModel::onSourceRemoveRows(const QModelIndex &sourceIndex, int /*begin*/, int /*end*/) +{ + if (m_selectedRows.isEmpty()) { + m_selectionModel->select(sourceIndex, QItemSelectionModel::Select); + } +} diff -Nru zanshin-0.1+svn1006410/src/selectionproxymodel.h zanshin-0.2.0/src/selectionproxymodel.h --- zanshin-0.1+svn1006410/src/selectionproxymodel.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/selectionproxymodel.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,70 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_SELECTIONPROXYMODEL_H +#define ZANSHIN_SELECTIONPROXYMODEL_H + +#include + +class QItemSelectionModel; + +class SelectionProxyModel : public KRecursiveFilterProxyModel +{ + Q_OBJECT + +public: + SelectionProxyModel(QObject *parent = 0); + virtual ~SelectionProxyModel(); + + void setSelectionModel(QItemSelectionModel *selectionModel); + virtual void setSourceModel(QAbstractItemModel *model); + + QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const; + +protected: + virtual bool acceptRow(int sourceRow, const QModelIndex &sourceParent) const; + +private slots: + void onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void onSourceRemoveRows(const QModelIndex &sourceIndex, int begin, int end); + +private: + QModelIndex mapFromSelectionToSource(const QModelIndex &index) const; + QList buildModelStack(QAbstractItemModel *topModel) const; + QAbstractItemModel *findCommonModel(const QList &leftStack, + const QList &rightStack) const; + QList createProxyChain(const QList &modelStack, + QAbstractItemModel *commonModel, bool isBackward); + void initializeSelection(); + + QItemSelectionModel *m_selectionModel; + + QList m_selectionChain; + QList m_sourceChain; + + QList m_selectedRows; + QList m_sourceSelectedRows; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/sidebar.cpp zanshin-0.2.0/src/sidebar.cpp --- zanshin-0.1+svn1006410/src/sidebar.cpp 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/sidebar.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -23,6 +23,7 @@ #include "sidebar.h" +#if 0 #include #include #include @@ -30,33 +31,29 @@ #include -#include +#include +#endif +#include +#include +#include +#include +#include #include #include #include -#include #include -#include -#include #include #include -#include #include -#include -#include "contextsmodel.h" -#include "globalmodel.h" -#include "librarymodel.h" -#include "projectsmodel.h" -#include "todocategoriesmodel.h" -#include "todoflatmodel.h" -#include "todotreemodel.h" +#include "globaldefs.h" +#include "modelstack.h" +#include "sidebarpage.h" -typedef boost::shared_ptr IncidencePtr; -SideBar::SideBar(QWidget *parent, KActionCollection *ac) +SideBar::SideBar(ModelStack *models, KActionCollection *ac, QWidget *parent) : QWidget(parent) { setupActions(ac); @@ -66,111 +63,48 @@ layout()->addWidget(m_stack); layout()->setContentsMargins(0, 0, 0, 0); - setupProjectPage(); - setupContextPage(); + createPage(models->treeSideBarModel()); + createPage(models->categoriesSideBarModel()); + + setupToolBar(); } -void SideBar::setupProjectPage() +void SideBar::createPage(QAbstractItemModel *model) { - QWidget *projectPage = new QWidget(m_stack); - projectPage->setLayout(new QVBoxLayout(projectPage)); + QAction *separator = new QAction(this); + separator->setSeparator(true); - m_projectTree = new QTreeView(projectPage); - m_projectTree->setFocusPolicy(Qt::NoFocus); - m_projectTree->header()->hide(); - m_projectTree->setSortingEnabled(true); - m_projectTree->sortByColumn(0, Qt::AscendingOrder); - projectPage->layout()->addWidget(m_projectTree); - m_projectTree->setAnimated(true); - m_projectTree->setModel(GlobalModel::projectsLibrary()); - m_projectTree->setSelectionMode(QAbstractItemView::SingleSelection); - m_projectTree->setDragEnabled(true); - m_projectTree->viewport()->setAcceptDrops(true); - m_projectTree->setDropIndicatorShown(true); - m_projectTree->setCurrentIndex(m_projectTree->model()->index(0, 0)); - m_projectTree->setRootIsDecorated(false); - m_projectTree->setStyleSheet("QTreeView { background: transparent; border-style: none; }"); - connect(m_projectTree->model(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - m_projectTree, SLOT(expand(const QModelIndex&))); - connect(m_projectTree->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), - this, SLOT(onCurrentProjectChangeDetected())); - connect(m_projectTree->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), - this, SLOT(updateActions(QModelIndex))); + QList contextActions; + contextActions << m_add + << m_remove + << separator + << m_rename; - QHBoxLayout *toolbarLayout = new QHBoxLayout; - toolbarLayout->setAlignment(Qt::AlignRight); - - QToolBar *projectBar = new QToolBar(projectPage); - projectBar->setIconSize(QSize(16, 16)); - toolbarLayout->addWidget(projectBar); - projectPage->layout()->addItem(toolbarLayout); - projectBar->addAction(m_addFolder); - projectBar->addAction(m_add); - projectBar->addAction(m_remove); + SideBarPage *page = new SideBarPage(model, contextActions, m_stack); - m_projectTree->setContextMenuPolicy(Qt::ActionsContextMenu); - m_projectTree->addActions(projectBar->actions()); - QAction *separator = new QAction(m_projectTree); - separator->setSeparator(true); - m_projectTree->addAction(separator); - m_projectTree->addAction(m_rename); + connect(page->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(updateActions(QModelIndex))); - m_stack->addWidget(projectPage); + m_stack->addWidget(page); } -void SideBar::setupContextPage() +void SideBar::setupToolBar() { - QWidget *contextPage = new QWidget(m_stack); - contextPage->setLayout(new QVBoxLayout(contextPage)); - - m_contextTree = new QTreeView(contextPage); - m_contextTree->setFocusPolicy(Qt::NoFocus); - m_contextTree->header()->hide(); - m_contextTree->setSortingEnabled(true); - m_contextTree->sortByColumn(0, Qt::AscendingOrder); - contextPage->layout()->addWidget(m_contextTree); - m_contextTree->setAnimated(true); - m_contextTree->setModel(GlobalModel::contextsLibrary()); - m_contextTree->setSelectionMode(QAbstractItemView::SingleSelection); - m_contextTree->setDragEnabled(true); - m_contextTree->viewport()->setAcceptDrops(true); - m_contextTree->setDropIndicatorShown(true); - m_contextTree->setCurrentIndex(m_contextTree->model()->index(0, 0)); - m_contextTree->setRootIsDecorated(false); - m_contextTree->setStyleSheet("QTreeView { background: transparent; border-style: none; }"); - connect(m_contextTree->model(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - m_contextTree, SLOT(expand(const QModelIndex&))); - connect(m_contextTree->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), - this, SLOT(onCurrentContextChangeDetected())); - connect(m_contextTree->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), - this, SLOT(updateActions(QModelIndex))); - QHBoxLayout *toolbarLayout = new QHBoxLayout; toolbarLayout->setAlignment(Qt::AlignRight); + static_cast(layout())->addLayout(toolbarLayout); - QToolBar *contextBar = new QToolBar(contextPage); - contextBar->setIconSize(QSize(16, 16)); - toolbarLayout->addWidget(contextBar); - contextPage->layout()->addItem(toolbarLayout); - contextBar->addAction(m_add); - contextBar->addAction(m_remove); - - m_contextTree->setContextMenuPolicy(Qt::ActionsContextMenu); - m_contextTree->addActions(contextBar->actions()); - QAction *separator = new QAction(m_contextTree); - separator->setSeparator(true); - m_contextTree->addAction(separator); - m_contextTree->addAction(m_rename); + QToolBar *projectBar = new QToolBar(this); + projectBar->setIconSize(QSize(16, 16)); + projectBar->addAction(m_synchronize); + projectBar->addAction(m_add); + projectBar->addAction(m_remove); - m_stack->addWidget(contextPage); + toolbarLayout->addWidget(projectBar); } void SideBar::setupActions(KActionCollection *ac) { - m_addFolder = ac->addAction("sidebar_new_folder", this, SLOT(onAddFolder())); - m_addFolder->setText(i18n("New Folder")); - m_addFolder->setIcon(KIcon("folder-new")); - m_add = ac->addAction("sidebar_new", this, SLOT(onAddItem())); m_add->setText(i18n("New")); m_add->setIcon(KIcon("list-add")); @@ -192,192 +126,148 @@ m_next->setText(i18n("Next")); m_next->setIcon(KIcon("go-next")); m_next->setShortcut(Qt::ALT | Qt::Key_Down); + + m_synchronize = ac->addAction("sidebar_synchronize", this, SLOT(onSynchronize())); + m_synchronize->setText(i18n("Synchronize")); + m_synchronize->setIcon(KIcon("view-refresh")); + m_synchronize->setShortcut(Qt::Key_F5); +} + +void SideBar::setMode(Zanshin::ApplicationMode mode) +{ + switch (mode) { + case Zanshin::ProjectMode: + m_stack->setCurrentIndex(0); + m_add->setText(i18n("New Project")); + m_remove->setText(i18n("Remove Project")); + m_rename->setText(i18n("Rename Project")); + m_previous->setText(i18n("Previous Project")); + m_next->setText(i18n("Next Project")); + break; + + case Zanshin::CategoriesMode: + m_stack->setCurrentIndex(1); + m_add->setText(i18n("New Context")); + m_remove->setText(i18n("Remove Context")); + m_rename->setText(i18n("Rename Context")); + m_previous->setText(i18n("Previous Context")); + m_next->setText(i18n("Next Context")); + break; + } + + updateActions(currentPage()->selectionModel()->currentIndex()); } -void SideBar::switchToProjectMode() +QItemSelectionModel *SideBar::projectSelection() const { - m_stack->setCurrentIndex(ProjectPageIndex); - m_add->setText("New Project"); - m_remove->setText("Remove Project/Folder"); - m_rename->setText("Rename Project/Folder"); - m_previous->setText("Previous Project"); - m_next->setText("Next Project"); - updateActions(m_projectTree->currentIndex()); - - applyCurrentProjectChange(); -} - -void SideBar::switchToContextMode() -{ - m_stack->setCurrentIndex(ContextPageIndex); - m_add->setText("New Context"); - m_remove->setText("Remove Context"); - m_rename->setText("Rename Context"); - m_previous->setText("Previous Context"); - m_next->setText("Next Context"); - updateActions(m_contextTree->currentIndex()); + return static_cast(m_stack->widget(0))->selectionModel(); +} - applyCurrentContextChange(); +QItemSelectionModel *SideBar::categoriesSelection() const +{ + return static_cast(m_stack->widget(1))->selectionModel(); } void SideBar::updateActions(const QModelIndex &index) { - const LibraryModel *model = qobject_cast(index.model());; - - if (model==GlobalModel::projectsLibrary()) { - m_addFolder->setEnabled(true); + Zanshin::ItemType type = (Zanshin::ItemType) index.data(Zanshin::ItemTypeRole).toInt(); + Akonadi::Collection col; + if ( type==Zanshin::Collection ) { + col = index.data(Akonadi::EntityTreeModel::CollectionRole).value(); } else { - m_addFolder->setEnabled(false); + // We use ParentCollectionRole instead of Akonadi::Item::parentCollection() because the + // information about the rights is not valid on retrieved items. + col = index.data(Akonadi::EntityTreeModel::ParentCollectionRole).value(); } - if (model->isInbox(index)) { - m_addFolder->setEnabled(false); - m_add->setEnabled(false); - m_remove->setEnabled(false); - m_rename->setEnabled(false); - } else { - m_add->setEnabled(true); - m_remove->setEnabled(!model->isLibraryRoot(index)); - m_rename->setEnabled(!model->isLibraryRoot(index)); - - QModelIndex sourceIndex = model->mapToSource(index); // into "projects" - if (m_addFolder->isEnabled() && sourceIndex.isValid()) { // Shouldn't be enabled on projects - sourceIndex = GlobalModel::projects()->mapToSource(sourceIndex); // into "todoTree" - sourceIndex = sourceIndex.sibling(sourceIndex.row(), TodoFlatModel::RowType); // we want the row type - if (GlobalModel::todoTree()->data(sourceIndex).toInt()!=TodoFlatModel::FolderTodo) { - m_addFolder->setEnabled(false); - m_add->setEnabled(false); - } - } - } -} + m_add->setEnabled((col.rights() & Akonadi::Collection::CanCreateItem) + && ( type == Zanshin::Collection + || type == Zanshin::ProjectTodo + || type == Zanshin::CategoryRoot + || type == Zanshin::Category)); -void SideBar::onAddFolder() -{ - bool ok; - QString summary = KInputDialog::getText(i18n("New Folder"), - i18n("Enter folder name:"), - QString(), &ok, this); - summary = summary.trimmed(); + m_remove->setEnabled((col.rights() & Akonadi::Collection::CanDeleteItem) + && (type == Zanshin::ProjectTodo + || type == Zanshin::Category )); - if (!ok || summary.isEmpty()) return; + m_rename->setEnabled((col.rights() & Akonadi::Collection::CanChangeItem) + && (type == Zanshin::ProjectTodo + || type == Zanshin::Category )); - QModelIndex parent = m_projectTree->currentIndex(); - parent = GlobalModel::projectsLibrary()->mapToSource(parent); - parent = GlobalModel::projects()->mapToSource(parent); - parent = parent.sibling(parent.row(), TodoFlatModel::RemoteId); - - QString parentRemoteId = GlobalModel::todoTree()->data(parent).toString(); + QString name; - KCal::Todo *todo = new KCal::Todo(); - todo->setSummary(summary); - todo->addComment("X-Zanshin-Folder"); - - if (!parentRemoteId.isEmpty()) { - todo->setRelatedToUid(parentRemoteId); + if ( !col.isValid() ) { + name = index.data().toString(); + } else { + if ( col.hasAttribute() && + !col.attribute()->displayName().isEmpty() ) { + name = col.attribute()->displayName(); + } else { + name = col.name(); + } } - IncidencePtr incidence(todo); - Akonadi::Item item; - item.setMimeType("application/x-vnd.akonadi.calendar.todo"); - item.setPayload(incidence); - - Akonadi::Collection collection = GlobalModel::todoFlat()->collection(); - Akonadi::ItemCreateJob *job = new Akonadi::ItemCreateJob(item, collection); - job->start(); + m_synchronize->setData(QVariant::fromValue(col)); + m_synchronize->setText(i18n("Synchronize \"%1\"", name)); } void SideBar::onAddItem() { - switch (m_stack->currentIndex()) { - case ProjectPageIndex: - addNewProject(); - break; - case ContextPageIndex: - addNewContext(); - break; - default: - Q_ASSERT(false); - } + currentPage()->addNewItem(); } void SideBar::onRemoveItem() { - switch (m_stack->currentIndex()) { - case ProjectPageIndex: - removeCurrentProject(); - break; - case ContextPageIndex: - removeCurrentContext(); - break; - default: - Q_ASSERT(false); - } + currentPage()->removeCurrentItem(); } void SideBar::onRenameItem() { - switch (m_stack->currentIndex()) { - case ProjectPageIndex: - m_projectTree->edit(m_projectTree->currentIndex()); - break; - case ContextPageIndex: - m_contextTree->edit(m_contextTree->currentIndex()); - break; - default: - Q_ASSERT(false); - } + currentPage()->renameCurrentItem(); } void SideBar::onPreviousItem() { - QTreeView *tree; - - switch (m_stack->currentIndex()) { - case ProjectPageIndex: - tree = m_projectTree; - break; - case ContextPageIndex: - tree = m_contextTree; - break; - default: - Q_ASSERT(false); - } - - QModelIndex index = tree->currentIndex(); - index = tree->indexAbove(index); - - if (index.isValid()) { - tree->setCurrentIndex(index); - } + currentPage()->selectPreviousItem(); } void SideBar::onNextItem() { - QTreeView *tree; + currentPage()->selectNextItem(); +} - switch (m_stack->currentIndex()) { - case ProjectPageIndex: - tree = m_projectTree; - break; - case ContextPageIndex: - tree = m_contextTree; - break; - default: - Q_ASSERT(false); - } +void SideBar::onSynchronize() +{ + KAction *action = static_cast(sender()); + Akonadi::Collection col = action->data().value(); - QModelIndex index = tree->currentIndex(); - tree->expand(index); - index = tree->indexBelow(index); + if (col.isValid()) { + Akonadi::AgentManager::self()->synchronizeCollection(col); + } else { + Akonadi::AgentInstance::List agents = Akonadi::AgentManager::self()->instances(); + while (!agents.isEmpty()) { + Akonadi::AgentInstance agent = agents.takeFirst(); - if (index.isValid()) { - tree->setCurrentIndex(index); + if (agent.type().mimeTypes().contains("application/x-vnd.akonadi.calendar.todo")) { + agent.synchronize(); + } + } } } +SideBarPage *SideBar::currentPage() const +{ + return static_cast(m_stack->currentWidget()); +} + +SideBarPage *SideBar::page(int idx) const +{ + return static_cast(m_stack->widget(idx)); +} + void SideBar::addNewProject() { +#if 0 bool ok; QString summary = KInputDialog::getText(i18n("New Project"), i18n("Enter project name:"), @@ -393,12 +283,12 @@ QString parentRemoteId = GlobalModel::todoTree()->data(parent).toString(); - KCal::Todo *todo = new KCal::Todo(); + KCalCore::Todo *todo = new KCalCore::Todo(); todo->setSummary(summary); todo->addComment("X-Zanshin-Project"); if (!parentRemoteId.isEmpty()) { - todo->setRelatedToUid(parentRemoteId); + todo->setRelatedTo(parentRemoteId); } IncidencePtr incidence(todo); @@ -409,8 +299,10 @@ Akonadi::Collection collection = GlobalModel::todoFlat()->collection(); Akonadi::ItemCreateJob *job = new Akonadi::ItemCreateJob(item, collection); job->start(); +#endif } +#if 0 void addDeleteTodoJobsHelper(const QModelIndex &index, Akonadi::TransactionSequence *transaction) { for (int i=0; irowCount(index); i++) { @@ -420,9 +312,11 @@ Akonadi::Item item = GlobalModel::todoTree()->itemForIndex(index); new Akonadi::ItemDeleteJob(item, transaction); } +#endif void SideBar::removeCurrentProject() { +#if 0 QModelIndex current = m_projectTree->currentIndex(); current = GlobalModel::projectsLibrary()->mapToSource(current); current = GlobalModel::projects()->mapToSource(current); @@ -452,10 +346,12 @@ Akonadi::TransactionSequence *transaction = new Akonadi::TransactionSequence(); addDeleteTodoJobsHelper(current, transaction); transaction->start(); +#endif } void SideBar::addNewContext() { +#if 0 bool ok; QString summary = KInputDialog::getText(i18n("New Context"), i18n("Enter context name:"), @@ -470,10 +366,12 @@ parent = parent.sibling(parent.row(), TodoFlatModel::Summary); GlobalModel::todoCategories()->addCategory(summary, parent); +#endif } void SideBar::removeCurrentContext() { +#if 0 QModelIndex current = m_contextTree->currentIndex(); current = GlobalModel::contextsLibrary()->mapToSource(current); current = GlobalModel::contexts()->mapToSource(current); @@ -494,38 +392,5 @@ if (!canRemove) return; GlobalModel::todoCategories()->removeCategory(summary); -} - -void SideBar::onCurrentProjectChangeDetected() -{ - QTimer::singleShot(0, this, SLOT(applyCurrentProjectChange())); -} - -void SideBar::applyCurrentProjectChange() -{ - QModelIndex index = m_projectTree->currentIndex(); - const LibraryModel *model = qobject_cast(index.model()); - - if (model->isInbox(index)) { - emit noProjectInboxActivated(); - } else { - emit projectActivated(model->mapToSource(index)); - } -} - -void SideBar::onCurrentContextChangeDetected() -{ - QTimer::singleShot(0, this, SLOT(applyCurrentContextChange())); -} - -void SideBar::applyCurrentContextChange() -{ - QModelIndex index = m_contextTree->currentIndex(); - const LibraryModel *model = qobject_cast(index.model()); - - if (model->isInbox(index)) { - emit noContextInboxActivated(); - } else { - emit contextActivated(model->mapToSource(index)); - } +#endif } diff -Nru zanshin-0.1+svn1006410/src/sidebar.h zanshin-0.2.0/src/sidebar.h --- zanshin-0.1+svn1006410/src/sidebar.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/sidebar.h 2011-11-25 15:00:52.000000000 +0000 @@ -27,67 +27,59 @@ #include #include +#include "globaldefs.h" + class KAction; class KActionCollection; +class QAbstractItemModel; +class QItemSelectionModel; class QStackedWidget; -class QTreeView; +class ModelStack; +class SideBarPage; + class SideBar : public QWidget { Q_OBJECT public: - SideBar(QWidget *parent, KActionCollection *ac); + SideBar(ModelStack *models, KActionCollection *ac, QWidget *parent=0); -public slots: - void switchToProjectMode(); - void switchToContextMode(); - -signals: - void noProjectInboxActivated(); - void noContextInboxActivated(); + void setMode(Zanshin::ApplicationMode mode); - void projectActivated(const QModelIndex &index); - void contextActivated(const QModelIndex &index); + QItemSelectionModel *projectSelection() const; + QItemSelectionModel *categoriesSelection() const; private slots: void updateActions(const QModelIndex &index); - void onAddFolder(); void onAddItem(); void onRemoveItem(); void onRenameItem(); void onPreviousItem(); void onNextItem(); - void onCurrentProjectChangeDetected(); - void applyCurrentProjectChange(); - void onCurrentContextChangeDetected(); - void applyCurrentContextChange(); + void onSynchronize(); private: - void setupProjectPage(); - void setupContextPage(); + void createPage(QAbstractItemModel *model); + void setupToolBar(); void setupActions(KActionCollection *ac); + SideBarPage *currentPage() const; + SideBarPage *page(int idx) const; + void addNewProject(); void removeCurrentProject(); void addNewContext(); void removeCurrentContext(); - enum { - ProjectPageIndex = 0, - ContextPageIndex = 1 - }; - QStackedWidget *m_stack; - QTreeView *m_projectTree; - QTreeView *m_contextTree; KAction *m_add; - KAction *m_addFolder; KAction *m_remove; KAction *m_rename; KAction *m_previous; KAction *m_next; + KAction *m_synchronize; }; #endif diff -Nru zanshin-0.1+svn1006410/src/sidebarmodel.cpp zanshin-0.2.0/src/sidebarmodel.cpp --- zanshin-0.1+svn1006410/src/sidebarmodel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/sidebarmodel.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,75 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "sidebarmodel.h" + +#include + +#include +#include + +#include "globaldefs.h" + +SideBarModel::SideBarModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ + setDynamicSortFilter(true); +} + +SideBarModel::~SideBarModel() +{ +} + +bool SideBarModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + if (left.data(Zanshin::ItemTypeRole).toInt()==Zanshin::Inbox) { + return true; + + } else if (right.data(Zanshin::ItemTypeRole).toInt()==Zanshin::Inbox) { + return false; + + } else { + return QSortFilterProxyModel::lessThan(left, right); + } +} + +bool SideBarModel::filterAcceptsColumn(int sourceColumn, const QModelIndex &/*sourceParent*/) const +{ + return sourceColumn==0; +} + +bool SideBarModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex sourceChild = sourceModel()->index(sourceRow, 0, sourceParent); + return sourceChild.data(Zanshin::ItemTypeRole).toInt()!=Zanshin::StandardTodo; +} + +bool SideBarModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + if (!sourceModel()) { + return QAbstractProxyModel::dropMimeData(data, action, row, column, parent); + } + return sourceModel()->dropMimeData(data, action, row, column, parent); +} + diff -Nru zanshin-0.1+svn1006410/src/sidebarmodel.h zanshin-0.2.0/src/sidebarmodel.h --- zanshin-0.1+svn1006410/src/sidebarmodel.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/sidebarmodel.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,49 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_SIDEBARMODEL_H +#define ZANSHIN_SIDEBARMODEL_H + +#include + +#include +#include + +class SideBarModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + SideBarModel(QObject *parent = 0); + virtual ~SideBarModel(); + + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + virtual bool filterAcceptsColumn(int sourceColumn, const QModelIndex &sourceParent) const; + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/sidebarpage.cpp zanshin-0.2.0/src/sidebarpage.cpp --- zanshin-0.1+svn1006410/src/sidebarpage.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/sidebarpage.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,165 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2009 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "sidebarpage.h" + +#include +#include +#include +#include + +#include +#include + +#include "categorymanager.h" +#include "globaldefs.h" +#include "todohelpers.h" +#include "todotreeview.h" + +SideBarPage::SideBarPage(QAbstractItemModel *model, + const QList &contextActions, + QWidget *parent) + : QWidget(parent) +{ + setLayout(new QVBoxLayout(this)); + m_treeView = new TodoTreeView(this); + layout()->addWidget(m_treeView); + layout()->setContentsMargins(0, 0, 0, 0); + + m_treeView->setFocusPolicy(Qt::NoFocus); + m_treeView->header()->hide(); + m_treeView->setSortingEnabled(true); + m_treeView->sortByColumn(0, Qt::AscendingOrder); + m_treeView->setAnimated(true); + m_treeView->setModel(model); + m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_treeView->setDragEnabled(true); + m_treeView->viewport()->setAcceptDrops(true); + m_treeView->setDropIndicatorShown(true); + m_treeView->setRootIsDecorated(false); + m_treeView->setStyleSheet("QTreeView { background: transparent; border-style: none; }"); + + m_treeView->setCurrentIndex(m_treeView->model()->index(0, 0)); + + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), + m_treeView, SLOT(expand(QModelIndex))); + + m_treeView->setContextMenuPolicy(Qt::ActionsContextMenu); + m_treeView->addActions(contextActions); +} + +QItemSelectionModel *SideBarPage::selectionModel() const +{ + return m_treeView->selectionModel(); +} + +void SideBarPage::addNewItem() +{ + QModelIndex parentItem = selectionModel()->currentIndex(); + Zanshin::ItemType type = (Zanshin::ItemType) parentItem.data(Zanshin::ItemTypeRole).toInt(); + + QString title; + QString text; + + if (type==Zanshin::Collection + || type==Zanshin::ProjectTodo) { + title = i18n("New Project"); + text = i18n("Enter project name:"); + + } else if (type==Zanshin::CategoryRoot + || type==Zanshin::Category) { + title = i18n("New Context"); + text = i18n("Enter context name:"); + + } else { + kFatal() << "We should never, ever, get in this case..."; + } + + + bool ok; + QString summary = KInputDialog::getText(title, text, + QString(), &ok, this); + summary = summary.trimmed(); + + if (!ok || summary.isEmpty()) return; + + + if (type==Zanshin::Collection) { + Akonadi::Collection collection = parentItem.data(Akonadi::EntityTreeModel::CollectionRole).value(); + TodoHelpers::addProject(summary, collection); + + } else if (type==Zanshin::ProjectTodo) { + TodoHelpers::addProject(summary, parentItem); + + } else if (type==Zanshin::CategoryRoot) { + CategoryManager::instance().addCategory(summary); + } else if (type==Zanshin::Category) { + CategoryManager::instance().addCategory(summary, parentItem.data(Zanshin::CategoryPathRole).toString()); + } else { + kFatal() << "We should never, ever, get in this case..."; + } +} + +void SideBarPage::removeCurrentItem() +{ + QModelIndex current = selectionModel()->currentIndex(); + Zanshin::ItemType type = (Zanshin::ItemType) current.data(Zanshin::ItemTypeRole).toInt(); + + if (type==Zanshin::ProjectTodo) { + if (TodoHelpers::removeProject(this, current)) { + m_treeView->setCurrentIndex(current.parent()); + } + } else if (type==Zanshin::Category) { + if (CategoryManager::instance().removeCategory(this, current)) { + m_treeView->setCurrentIndex(current.parent()); + } + } else { + kFatal() << "We should never, ever, get in this case..."; + } +} + +void SideBarPage::renameCurrentItem() +{ + m_treeView->edit(selectionModel()->currentIndex()); +} + +void SideBarPage::selectPreviousItem() +{ + QModelIndex index = m_treeView->currentIndex(); + index = m_treeView->indexAbove(index); + + if (index.isValid()) { + m_treeView->setCurrentIndex(index); + } +} + +void SideBarPage::selectNextItem() +{ + QModelIndex index = m_treeView->currentIndex(); + m_treeView->expand(index); + index = m_treeView->indexBelow(index); + + if (index.isValid()) { + m_treeView->setCurrentIndex(index); + } +} diff -Nru zanshin-0.1+svn1006410/src/sidebarpage.h zanshin-0.2.0/src/sidebarpage.h --- zanshin-0.1+svn1006410/src/sidebarpage.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/sidebarpage.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,60 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2009 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_SIDEBARPAGE_H +#define ZANSHIN_SIDEBARPAGE_H + +#include + +class QItemSelectionModel; +class QAbstractItemModel; + +namespace Akonadi +{ + class EntityTreeView; +} + +class SideBarPage : public QWidget +{ + Q_OBJECT + +public: + SideBarPage(QAbstractItemModel *model, + const QList &contextActions, + QWidget *parent=0); + + QItemSelectionModel *selectionModel() const; + +public slots: + void addNewItem(); + void removeCurrentItem(); + void renameCurrentItem(); + void selectPreviousItem(); + void selectNextItem(); + +private: + Akonadi::EntityTreeView *m_treeView; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/tests/CMakeLists.txt zanshin-0.2.0/src/tests/CMakeLists.txt --- zanshin-0.1+svn1006410/src/tests/CMakeLists.txt 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/tests/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/..) - -macro(zanshin_add_unit_test _testname _extra_sources) - kde4_add_executable(${_testname} TEST ${_testname}.cpp ${_extra_sources} ${ARGN}) - target_link_libraries(${_testname} ${KDE4_KDEUI_LIBS} ${KDE4_AKONADI_LIBS} ${KDE4_KCAL_LIBS} ${QT_QTTEST_LIBRARY}) - - # based on kde4_add_unit_test - if (WIN32) - get_target_property( _loc ${_testname} LOCATION ) - set(_executable ${_loc}.bat) - else (WIN32) - set(_executable ${EXECUTABLE_OUTPUT_PATH}/${_testname}) - endif (WIN32) - if (UNIX) - set(_executable ${_executable}.shell) - endif (UNIX) - - find_program(_testrunner akonaditest) - - add_test( zanshin-${_testname} - ${_testrunner} -c - ${CMAKE_CURRENT_SOURCE_DIR}/unittestenv/config.xml - ${_executable} - ) -endmacro(zanshin_add_unit_test) - -zanshin_add_unit_test(librarymodeltest modeltestbase.cpp ../librarymodel.cpp ../todotreemodel.cpp ../todoflatmodel.cpp) -zanshin_add_unit_test(todoflatmodeltest modeltestbase.cpp ../todoflatmodel.cpp) -zanshin_add_unit_test(todotreemodeltest modeltestbase.cpp ../todotreemodel.cpp ../todoflatmodel.cpp) -zanshin_add_unit_test(todocategoriesmodeltest modeltestbase.cpp ../todocategoriesmodel.cpp ../todocategoriesattribute.cpp ../todoflatmodel.cpp) - diff -Nru zanshin-0.1+svn1006410/src/tests/librarymodeltest.cpp zanshin-0.2.0/src/tests/librarymodeltest.cpp --- zanshin-0.1+svn1006410/src/tests/librarymodeltest.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/tests/librarymodeltest.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,146 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "modeltestbase.h" - -#include -#include -#include -#include - -#include - -#include "librarymodel.h" -#include "todoflatmodel.h" -#include "todotreemodel.h" - -class LibraryModelTest : public ModelTestBase -{ - Q_OBJECT - -private slots: - void initTestCase(); - void testInitialState(); - -private: - TodoFlatModel m_flatModel; - QSortFilterProxyModel m_flatSortedModel; - TodoTreeModel m_treeModel; - QSortFilterProxyModel m_treeSortedModel; - LibraryModel m_model; -}; - -QTEST_AKONADIMAIN(LibraryModelTest, GUI) - -void LibraryModelTest::initTestCase() -{ - ModelTestBase::initTestCase(); - m_treeModel.setSourceModel(&m_flatModel); - m_treeModel.setCollection(m_collection); - - m_flatSortedModel.setSourceModel(&m_flatModel); - m_flatSortedModel.sort(TodoFlatModel::RemoteId); - - m_treeSortedModel.setSourceModel(&m_treeModel); - m_treeSortedModel.sort(TodoFlatModel::Summary); - - m_model.setSourceModel(&m_treeSortedModel); - flushNotifications(); -} - -class TreeNode -{ -public: - TreeNode(const QString &s) - : summary(s) { } - - TreeNode &operator<<(const TreeNode &child) - { - children << child; - return *this; - } - - QString summary; - QList children; -}; - -#if 0 -static void dumpTree(const QList &tree, int indent = 0) -{ - QString prefix; - for (int i=0; i &tree, - const QModelIndex &root = QModelIndex()) -{ - int row = 0; - foreach (const TreeNode &node, tree) { - QCOMPARE(model->data(model->index(row, TodoFlatModel::Summary, root)).toString(), node.summary); - - QCOMPARE(model->rowCount(model->index(row, 0, root)), node.children.size()); - compareTrees(model, node.children, model->index(row, 0, root)); - row++; - } -} - -void LibraryModelTest::testInitialState() -{ - QList tree; - - tree << TreeNode("Inbox") - << (TreeNode("Library") - << TreeNode("Choose a kitty") - << (TreeNode("First Folder") - << (TreeNode("Becoming Astronaut") - << TreeNode("Learn the constellations") - << TreeNode("Look at the stars") - ) - << (TreeNode("Becoming more relaxed") - << TreeNode("Listen new age album 2") - << TreeNode("Read magazine") - ) - ) - - << (TreeNode("Second Folder") - << (TreeNode("Pet Project") - << TreeNode("Choose a puppy") - << TreeNode("Feed the dog") - << TreeNode("Walk around with the dog") - ) - ) - ); - - compareTrees(&m_model, tree); -} - -#include "todolibrarymodeltest.moc" diff -Nru zanshin-0.1+svn1006410/src/tests/modeltestbase.cpp zanshin-0.2.0/src/tests/modeltestbase.cpp --- zanshin-0.1+svn1006410/src/tests/modeltestbase.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/tests/modeltestbase.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "modeltestbase.h" - -#include -#include - -#include -#include - -#include - -ModelTestBase::ModelTestBase() - : QObject() -{ -} - -void ModelTestBase::flushNotifications() -{ - QDBusInterface notifications("org.freedesktop.Akonadi", - "/notifications/debug", - "org.freedesktop.Akonadi.NotificationManager"); - notifications.call("emitPendingNotifications"); - QTest::qWait(250); -} - -void ModelTestBase::initTestCase() -{ - qRegisterMetaType(); - - Akonadi::CollectionFetchJob *colJob = new Akonadi::CollectionFetchJob(Akonadi::Collection::root()); - colJob->setResource("akonadi_ical_resource_0"); - QVERIFY2(colJob->exec(), colJob->errorString().toLatin1().data()); - QCOMPARE(colJob->collections().size(), 1); - m_collection = colJob->collections()[0]; -} - -#include "modeltestbase.moc" diff -Nru zanshin-0.1+svn1006410/src/tests/modeltestbase.h zanshin-0.2.0/src/tests/modeltestbase.h --- zanshin-0.1+svn1006410/src/tests/modeltestbase.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/tests/modeltestbase.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#ifndef MODELTESTBASE_H -#define MODELTESTBASE_H - -#include -#include -#include - -#include -#include - -class ModelTestBase : public QObject -{ - Q_OBJECT - -public: - ModelTestBase(); - -protected: - void flushNotifications(); - Akonadi::Collection m_collection; - -protected slots: - virtual void initTestCase(); - -private: - Akonadi::AgentInstance m_agentInstance; - QFile m_testFile; -}; - -Q_DECLARE_METATYPE(QModelIndex) - - -#endif - diff -Nru zanshin-0.1+svn1006410/src/tests/todocategoriesmodeltest.cpp zanshin-0.2.0/src/tests/todocategoriesmodeltest.cpp --- zanshin-0.1+svn1006410/src/tests/todocategoriesmodeltest.cpp 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/tests/todocategoriesmodeltest.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,683 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - Copyright 2008, 2009 Mario Bensi - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "modeltestbase.h" - -#include -#include -#include -#include - -#include - -#include "todoflatmodel.h" -#include "todocategoriesmodel.h" - -class TodoCategoriesModelTest : public ModelTestBase -{ - Q_OBJECT - -private slots: - void initTestCase(); - void testInitialState(); - void testSingleModification(); - void testReparentModification(); - void testSeveralReparentModification(); - void testSingleRemoved(); - void testMultipleRemoved(); - void testDragAndDrop(); - void testAddCategory_data(); - void testAddCategory(); - void testCategoryRename_data(); - void testCategoryRename(); - void testRemoveCategory_data(); - void testRemoveCategory(); - void testDnDCategory_data(); - void testDnDCategory(); - -private: - TodoFlatModel m_flatModel; - QSortFilterProxyModel m_flatSortedModel; - TodoCategoriesModel m_model; - QSortFilterProxyModel m_sortedModel; -}; - -QTEST_AKONADIMAIN(TodoCategoriesModelTest, GUI) - -void TodoCategoriesModelTest::initTestCase() -{ - ModelTestBase::initTestCase(); - m_model.setSourceModel(&m_flatModel); - m_model.setCollection(m_collection); - flushNotifications(); - - m_flatSortedModel.setSourceModel(&m_flatModel); - m_flatSortedModel.sort(TodoFlatModel::RemoteId); - - m_sortedModel.setSourceModel(&m_model); - m_sortedModel.sort(TodoFlatModel::Summary); -} - -class TreeNode -{ -public: - TreeNode(const QString &i, const QString &s) - : id(i), summary(s) { } - - TreeNode &operator<<(const TreeNode &child) - { - const_cast(child).parent = summary; - children << child; - return *this; - } - - QString id; - QString parent; - QString summary; - QList children; -}; - -#if 0 -static void dumpTree(const QList &tree, int indent = 0) -{ - QString prefix; - for (int i=0; i &tree, - const QModelIndex &root = QModelIndex()) -{ - int row = 0; - foreach (const TreeNode &node, tree) { - QCOMPARE(model->data(model->index(row, TodoFlatModel::RemoteId, root)).toString(), node.id); - QCOMPARE(model->data(model->index(row, TodoFlatModel::Summary, root)).toString(), node.summary); - if (node.parent.isEmpty()) { - QVERIFY(model->data(model->index(row, TodoFlatModel::Categories, root)).toStringList().isEmpty()); - } else { - QVERIFY(model->data(model->index(row, TodoFlatModel::Categories, root)).toStringList().contains(node.parent)); - } - - QCOMPARE(model->rowCount(model->index(row, 0, root)), node.children.size()); - compareTrees(model, node.children, model->index(row, 0, root)); - row++; - } -} - -void TodoCategoriesModelTest::testInitialState() -{ - QList tree; - - tree << (TreeNode(QString(), "Computer") - << TreeNode(QString(), "Online") - << TreeNode("fake-06", "Read magazine") - ) - - << (TreeNode(QString(), "Errands") - << TreeNode("fake-03", "Walk around with the dog") - ) - - << (TreeNode(QString(), "Home") - << TreeNode("fake-05", "Feed the dog") - ) - - << (TreeNode(QString(), "Office") - << TreeNode("fake-06", "Read magazine") - ) - - << TreeNode(QString(), "Phone"); - - compareTrees(&m_sortedModel, tree); -} - -void TodoCategoriesModelTest::testSingleModification() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(5, 0))); - QModelIndexList indexes = m_model.indexesForItem(item, TodoFlatModel::Summary); - - QSignalSpy spy(&m_model, SIGNAL(dataChanged(QModelIndex, QModelIndex))); - - QVERIFY(m_model.setData(indexes.first(), "Feed the cat")); - - flushNotifications(); - - foreach (const QModelIndex &index, indexes) { - QCOMPARE(m_model.data(index).toString(), QString("Feed the cat")); - } - - indexes = m_model.indexesForItem(item, 0); - QCOMPARE(spy.count(), indexes.size()); - - while (!spy.isEmpty()) { - QVariantList signal = spy.takeFirst(); - - QCOMPARE(signal.count(), 2); - QModelIndex begin = signal.at(0).value(); - QModelIndex end = signal.at(1).value(); - - QVERIFY(begin.row()==end.row()); - QVERIFY(begin.parent()==end.parent()); - QCOMPARE(begin.column(), 0); - QCOMPARE(end.column(), (int)TodoFlatModel::LastColumn); - - QVERIFY(indexes.contains(begin)); - } -} - -void TodoCategoriesModelTest::testReparentModification() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(5, 0))); - QModelIndexList indexes = m_model.indexesForItem(item, TodoFlatModel::Categories); - - QModelIndexList oldParents; - QList oldParentsRow; - foreach (const QModelIndex &index, indexes) { - oldParents << index.parent(); - oldParentsRow << index.row(); - } - - QModelIndexList newParents; - QList newParentsRow; - newParents << m_model.indexForCategory("Errands") - << m_model.indexForCategory("Home"); - foreach (const QModelIndex &newParent, newParents) { - newParentsRow << m_model.rowCount(newParent); - } - - QSignalSpy rowsInserted(&m_model, SIGNAL(rowsInserted(QModelIndex, int, int))); - QSignalSpy rowsRemoved(&m_model, SIGNAL(rowsRemoved(QModelIndex, int, int))); - - // Note: It includes a category "Foo" which wasn't in the model previously, - // we should get an insert event for it too! - QVERIFY(m_model.setData(indexes.first(), "Home, Errands, Foo")); - - flushNotifications(); - - QCOMPARE(rowsRemoved.count(), 2); - while (!rowsRemoved.isEmpty()) { - QVariantList signal = rowsRemoved.takeFirst(); - QCOMPARE(signal.count(), 3); - - QModelIndex parent = signal.at(0).value(); - QVERIFY(oldParents.contains(parent)); - - int expectedRow = oldParentsRow[oldParents.indexOf(parent)]; - QCOMPARE(signal.at(1).toInt(), expectedRow); - QCOMPARE(signal.at(2).toInt(), expectedRow); - } - - QCOMPARE(rowsInserted.count(), 4); - - // first insert event should be for "Foo": - { - QVariantList signal = rowsInserted.takeFirst(); - QCOMPARE(signal.count(), 3); - - QModelIndex parent = signal.at(0).value(); - QVERIFY(!parent.isValid()); - - int expectedRow = m_model.rowCount()-1; - QCOMPARE(signal.at(1).toInt(), expectedRow); - QCOMPARE(signal.at(2).toInt(), expectedRow); - } - - // Adjust newParents and newParentsRow to include the new "Foo" category - newParents << m_model.index(m_model.rowCount()-1, 0); - newParentsRow << 0; - - // now check the other events for the item itself - while (!rowsInserted.isEmpty()) { - QVariantList signal = rowsInserted.takeFirst(); - QCOMPARE(signal.count(), 3); - - QModelIndex parent = signal.at(0).value(); - QVERIFY(newParents.contains(parent)); - - int expectedRow = newParentsRow[newParents.indexOf(parent)]; - QCOMPARE(signal.at(1).toInt(), expectedRow); - QCOMPARE(signal.at(2).toInt(), expectedRow); - } -} - -void TodoCategoriesModelTest::testSeveralReparentModification() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(4, 0))); - QModelIndexList indexes = m_model.indexesForItem(item, TodoFlatModel::Categories); - QCOMPARE(indexes.size(), 1); - QModelIndex index = indexes.takeFirst(); - - QModelIndex oldParent = index.parent(); - QCOMPARE(oldParent, m_model.indexForCategory("Home")); - - QModelIndex newParent = m_model.indexForCategory("Errands"); - - QSignalSpy rowsInserted(&m_model, SIGNAL(rowsInserted(QModelIndex, int, int))); - QSignalSpy rowsRemoved(&m_model, SIGNAL(rowsRemoved(QModelIndex, int, int))); - - - QVERIFY(m_model.setData(index, "Errands")); - - flushNotifications(); - - QCOMPARE(rowsRemoved.count(), 1); - QVariantList signal = rowsRemoved.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), oldParent); - QCOMPARE(signal.at(1).toInt(), 0); - QCOMPARE(signal.at(2).toInt(), 0); - - QCOMPARE(rowsInserted.count(), 1); - signal = rowsInserted.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), newParent); - QCOMPARE(signal.at(1).toInt(), 2); - QCOMPARE(signal.at(2).toInt(), 2); - - - indexes = m_model.indexesForItem(item, TodoFlatModel::Categories); - QCOMPARE(indexes.size(), 1); - index = indexes.takeFirst(); - - QVERIFY(m_model.setData(index, "Home")); - - flushNotifications(); - - QCOMPARE(rowsRemoved.count(), 1); - signal = rowsRemoved.takeFirst(); - QCOMPARE(signal.count(), 3); - qDebug() << signal.at(0).value() << newParent; - QCOMPARE(signal.at(0).value(), newParent); - QCOMPARE(signal.at(1).toInt(), 2); - QCOMPARE(signal.at(2).toInt(), 2); - - QCOMPARE(rowsInserted.count(), 1); - signal = rowsInserted.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), oldParent); - QCOMPARE(signal.at(1).toInt(), 1); - QCOMPARE(signal.at(2).toInt(), 1); -} - -void TodoCategoriesModelTest::testSingleRemoved() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(4, 0))); - QModelIndexList indexes = m_model.indexesForItem(item, TodoFlatModel::Categories); - - QCOMPARE(indexes.size(), 1); - QModelIndex index = indexes.takeFirst(); - QModelIndex parent = index.parent(); - int count = m_model.rowCount(parent); - - QSignalSpy spy(&m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int))); - - Akonadi::ItemDeleteJob *job = new Akonadi::ItemDeleteJob(item); - QVERIFY(job->exec()); - - flushNotifications(); - - QCOMPARE(m_model.rowCount(parent), count - 1); - - QCOMPARE(spy.count(), 1); - QVariantList signal = spy.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), parent); - QCOMPARE(signal.at(1).toInt(), 1); - QCOMPARE(signal.at(1).toInt(), 1); -} - -void TodoCategoriesModelTest::testMultipleRemoved() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(4, 0))); - QModelIndexList indexes = m_model.indexesForItem(item, TodoFlatModel::Categories); - - QCOMPARE(indexes.size(), 3); - QModelIndex index = indexes.takeFirst(); - QModelIndex index2 = indexes.takeFirst(); - QModelIndex index3 = indexes.takeFirst(); - - QModelIndex parent = index.parent(); - QModelIndex parent2 = index2.parent(); - QModelIndex parent3 = index3.parent(); - int count = m_model.rowCount(parent); - - QSignalSpy spy(&m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int))); - - Akonadi::ItemDeleteJob *job = new Akonadi::ItemDeleteJob(item); - QVERIFY(job->exec()); - - flushNotifications(); - - QCOMPARE(m_model.rowCount(parent), count - 1); - - QCOMPARE(spy.count(), 3); - QVariantList signal = spy.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), parent); - QCOMPARE(signal.at(1).toInt(), 0); - QCOMPARE(signal.at(1).toInt(), 0); - - QCOMPARE(spy.count(), 2); - signal = spy.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), parent2); - QCOMPARE(signal.at(1).toInt(), 1); - QCOMPARE(signal.at(1).toInt(), 1); - - QCOMPARE(spy.count(), 1); - signal = spy.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), parent3); - QCOMPARE(signal.at(1).toInt(), 0); - QCOMPARE(signal.at(1).toInt(), 0); -} - -void TodoCategoriesModelTest::testDragAndDrop() -{ - // move 1 item - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(2, 0))); - QModelIndexList indexes = m_model.indexesForItem(item); - QModelIndexList catIndexes = m_model.indexesForItem(item, TodoFlatModel::Categories); - - QCOMPARE(indexes.size(), 1); - QModelIndex index = indexes.first(); - QModelIndex catIndex = catIndexes.first(); - QCOMPARE(m_model.data(catIndex).toString(), QString("Errands")); - QModelIndex parent = m_model.indexForCategory("Phone"); - - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, parent)); - - catIndexes = m_model.indexesForItem(item, TodoFlatModel::Categories); - QCOMPARE(m_model.data(catIndexes.first()).toString(), QString("Phone")); - - - //move 2 new items - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(4, 0))); - index = m_flatModel.indexForItem(item, 0); - catIndex = m_flatModel.indexForItem(item, TodoFlatModel::Categories); - QCOMPARE(m_flatModel.data(catIndex).toString(), QString("")); - - Akonadi::Item item2 = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(5, 0))); - QModelIndex index2 = m_flatModel.indexForItem(item2, 0); - QModelIndex catIndex2 = m_flatModel.indexForItem(item2, TodoFlatModel::Categories); - QCOMPARE(m_flatModel.data(catIndex2).toString(), QString("")); - - indexes.clear(); - - indexes << index; - indexes << index2; - - parent = m_model.indexForCategory("Office"); - - mimeData = m_flatModel.mimeData(indexes); - QVERIFY(m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, parent)); - - flushNotifications(); - - catIndexes = m_model.indexesForItem(item, TodoFlatModel::Categories); - QCOMPARE(m_model.data(catIndexes.first()).toString(), QString("Office")); - - catIndexes = m_model.indexesForItem(item2, TodoFlatModel::Categories); - QCOMPARE(m_model.data(catIndexes.first()).toString(), QString("Office")); -} - -void TodoCategoriesModelTest::testAddCategory_data() -{ - QTest::addColumn("name"); - QTest::addColumn("parentName"); - QTest::addColumn("expected"); - - QTest::newRow("Adding root category") << "Baz" << QString() << true; - QTest::newRow("Adding sub-category") << "FooBar" << "Baz" << true; - QTest::newRow("Adding existing root category") << "Foo" << QString() << false; - QTest::newRow("Adding existing category, deeper") << "Foo" << "Baz" << false; -} - -void TodoCategoriesModelTest::testAddCategory() -{ - QFETCH(QString, name); - QFETCH(QString, parentName); - QFETCH(bool, expected); - - QModelIndex parent = m_model.indexForCategory(parentName); - - int row = m_model.rowCount(parent); - QSignalSpy spy(&m_model, SIGNAL(rowsInserted(QModelIndex, int, int))); - - bool result = m_model.addCategory(name, parent); - QCOMPARE(result, expected); - - flushNotifications(); - - if (!expected) { - QCOMPARE(spy.count(), 0); - return; - } - - QCOMPARE(spy.count(), 1); - QVariantList signal = spy.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), parent); - QCOMPARE(signal.at(1).toInt(), row); - QCOMPARE(signal.at(2).toInt(), row); - - QCOMPARE(m_model.rowCount(parent), row+1); - - QModelIndex index = m_model.index(row, 0, parent); - QCOMPARE(m_model.data(index).toString(), name); - QCOMPARE(m_model.rowCount(index), 0); -} - -void TodoCategoriesModelTest::testCategoryRename_data() -{ - QTest::addColumn("oldName"); - QTest::addColumn("newName"); - QTest::addColumn("expected"); - - QTest::newRow("Nominal case") << "Office" << "Office renamed" << true; - QTest::newRow("Go back to original name") << "Office renamed" << "Office" << true; - QTest::newRow("Invalid name") << "Office" << " " << false; - QTest::newRow("Already exists") << "Office" << "Home" << false; -} - -void TodoCategoriesModelTest::testCategoryRename() -{ - QFETCH(QString, oldName); - QFETCH(QString, newName); - QFETCH(bool, expected); - - QModelIndex index = m_model.indexForCategory(oldName); - - QCOMPARE(m_model.data(index).toString(), oldName); - int rowCount = m_model.rowCount(index); - QSignalSpy spy(&m_model, SIGNAL(dataChanged(QModelIndex, QModelIndex))); - - bool result = m_model.setData(index, newName); - QCOMPARE(result, expected); - flushNotifications(); - - if (!expected) { - QCOMPARE(spy.count(), 0); - return; - } - - QCOMPARE(m_model.data(index).toString(), newName); - QCOMPARE(m_model.rowCount(index), rowCount); - - for (int row = 0; row(); - QModelIndex end = signal.last().value(); - QCOMPARE(begin, index); - QCOMPARE(begin.row(), end.row()); - QCOMPARE(begin.column(), 0); - QCOMPARE(end.column(), (int)TodoFlatModel::LastColumn); - - for (int row = 0; row(); - end = signal.last().value(); - QCOMPARE(begin.parent(), index); - QCOMPARE(begin.row(), row); - - QCOMPARE(begin.parent(), end.parent()); - QCOMPARE(begin.row(), end.row()); - QCOMPARE(begin.column(), 0); - QCOMPARE(end.column(), (int)TodoFlatModel::LastColumn); - } -} - -void TodoCategoriesModelTest::testRemoveCategory_data() -{ - QTest::addColumn("name"); - QTest::addColumn("expected"); - - QTest::newRow("Removing isolated category") << "Home" << true; - QTest::newRow("Removing non existant category") << "Plop" << false; - QTest::newRow("Removing category with sub-category") << "Computer" << true; - QTest::newRow("Removing category with actions") << "Office" << true; -} - -void TodoCategoriesModelTest::testRemoveCategory() -{ - QFETCH(QString, name); - QFETCH(bool, expected); - - QModelIndex index = m_model.indexForCategory(name); - QModelIndex parent = index.parent(); - int row = index.row(); - - if (expected) { - QCOMPARE(m_model.data(index).toString(), name); - } - int rowCount = m_model.rowCount(index); - QSignalSpy spy(&m_model, SIGNAL(rowsRemoved(QModelIndex, int, int))); - QSignalSpy flatSpy(&m_flatModel, SIGNAL(rowsRemoved(QModelIndex, int, int))); - - bool result = m_model.removeCategory(name); - QCOMPARE(result, expected); - flushNotifications(); - - QCOMPARE(flatSpy.count(), 0); - if (!expected) { - QCOMPARE(spy.count(), 0); - return; - } - - QVERIFY(!m_model.indexForCategory(name).isValid()); - - QCOMPARE(spy.count(), rowCount+1); - - for (int i=0; i(), index); - QCOMPARE(signal.at(1).toInt(), 0); - QCOMPARE(signal.at(2).toInt(), 0); - } - - QVariantList signal = spy.takeFirst(); - QCOMPARE(signal.size(), 3); - QCOMPARE(signal.at(0).value(), parent); - QCOMPARE(signal.at(1).toInt(), row); - QCOMPARE(signal.at(2).toInt(), row); - -} - -void TodoCategoriesModelTest::testDnDCategory_data() -{ - QTest::addColumn("category"); - QTest::addColumn("oldParent"); - QTest::addColumn("newParent"); - QTest::addColumn("expected"); - - QTest::newRow("Moving root category to sub-category") << "Phone" << QString() << "Errands" << true; - QTest::newRow("Moving sub-category to root") << "Phone" << "Errands" << QString() << true; -} -void TodoCategoriesModelTest::testDnDCategory() -{ - QFETCH(QString, category); - QFETCH(QString, oldParent); - QFETCH(QString, newParent); - QFETCH(bool, expected); - - QModelIndex index = m_model.indexForCategory(category); - QModelIndex oldParentIndex = m_model.indexForCategory(oldParent); - QModelIndex newParentIndex = m_model.indexForCategory(newParent); - - int oldRow = index.row(); - int newRow = m_model.rowCount(newParentIndex); - - QSignalSpy rowsInserted(&m_model, SIGNAL(rowsInserted(QModelIndex, int, int))); - QSignalSpy rowsRemoved(&m_model, SIGNAL(rowsRemoved(QModelIndex, int, int))); - - QMimeData *mimeData = m_model.mimeData(QModelIndexList() << index); - - bool result = m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, newParentIndex); - QCOMPARE(result, expected); - - flushNotifications(); - - if (!expected) { - QCOMPARE(rowsRemoved.count(), 0); - QCOMPARE(rowsInserted.count(), 0); - return; - } - - QCOMPARE(rowsRemoved.count(), 1); - QVariantList signal = rowsRemoved.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), oldParentIndex); - QCOMPARE(signal.at(1).toInt(), oldRow); - QCOMPARE(signal.at(2).toInt(), oldRow); - - - QCOMPARE(rowsInserted.count(), 1); - signal = rowsInserted.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), newParentIndex); - QCOMPARE(signal.at(1).toInt(), newRow); - QCOMPARE(signal.at(2).toInt(), newRow); -} - -#include "todocategoriesmodeltest.moc" diff -Nru zanshin-0.1+svn1006410/src/tests/todoflatmodeltest.cpp zanshin-0.2.0/src/tests/todoflatmodeltest.cpp --- zanshin-0.1+svn1006410/src/tests/todoflatmodeltest.cpp 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/tests/todoflatmodeltest.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,248 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - Copyright 2008, 2009 Mario Bensi - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "modeltestbase.h" - -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include "todoflatmodel.h" - -typedef boost::shared_ptr IncidencePtr; - -class TodoFlatModelTest : public ModelTestBase -{ - Q_OBJECT - -private slots: - void initTestCase(); - void testInitialState_data(); - void testInitialState(); - void testItemModification_data(); - void testItemModification(); - void testSingleRemoved(); - void testRowTypeDuringInsert(); - - void onInsertRows(const QModelIndex &parent, int begin, int end); -private: - TodoFlatModel m_model; - QSortFilterProxyModel m_sortedModel; -}; - -QTEST_AKONADIMAIN(TodoFlatModelTest, GUI) - -void TodoFlatModelTest::initTestCase() -{ - ModelTestBase::initTestCase(); - m_model.setCollection(m_collection); - flushNotifications(); - - m_sortedModel.setSourceModel(&m_model); - m_sortedModel.sort(TodoFlatModel::RemoteId); -} - -void TodoFlatModelTest::testInitialState_data() -{ - QTest::addColumn("row"); - QTest::addColumn("remoteId"); - QTest::addColumn("summary"); - QTest::addColumn("categories"); - QTest::addColumn("parentRemoteId"); - QTest::addColumn("dueDate"); - QTest::addColumn("isImportant"); - - QTest::newRow("0") << 0 << "fake-01" << "Becoming Astronaut" << QStringList() << "fake-12" << "" << false; - QTest::newRow("1") << 1 << "fake-02" << "Look at the stars" << QStringList() << "fake-01" << "" << false; - QTest::newRow("2") << 2 << "fake-03" << "Walk around with the dog" - << (QStringList() << "Errands") << "fake-10" << "2008-12-25" << false; - QTest::newRow("3") << 3 << "fake-04" << "Second Folder" << QStringList() << "" << "" << false; - QTest::newRow("4") << 4 << "fake-05" << "Feed the dog" - << (QStringList() << "Home") << "fake-10" << "" << false; - QTest::newRow("5") << 5 << "fake-06" << "Read magazine" - << (QStringList() << "Office" << "Computer") << "fake-11" << "" << false; - QTest::newRow("6") << 6 << "fake-07" << "Learn the constellations" << QStringList() << "fake-01" << "" << false; - QTest::newRow("7") << 7 << "fake-08" << "Choose a puppy" << QStringList() << "fake-10" << "" << false; - QTest::newRow("8") << 8 << "fake-09" << "Listen new age album 2" << QStringList() << "fake-11" << "" << false; - QTest::newRow("9") << 9 << "fake-10" << "Pet Project" << QStringList() << "fake-04" << "" << false; - QTest::newRow("10") << 10 << "fake-11" << "Becoming more relaxed" << QStringList() << "fake-12" << "" << false; - QTest::newRow("11") << 11 << "fake-12" << "First Folder" << QStringList() << "" << "" << false; - QTest::newRow("12") << 12 << "fake-14" << "Choose a kitty" << QStringList() << "" << "" << false; -} - -void TodoFlatModelTest::testInitialState() -{ - QFETCH(int, row); - QFETCH(QString, remoteId); - QFETCH(QString, summary); - QFETCH(QStringList, categories); - QFETCH(QString, parentRemoteId); - QFETCH(QString, dueDate); - QFETCH(bool, isImportant); - - QCOMPARE(m_sortedModel.rowCount(), 13); - QCOMPARE(m_sortedModel.columnCount(), 7); - - QModelIndex index = m_sortedModel.index(row, TodoFlatModel::RemoteId); - QCOMPARE(m_sortedModel.data(index).toString(), remoteId); - QCOMPARE(m_sortedModel.rowCount(index), 0); - QCOMPARE(m_sortedModel.columnCount(index), 0); - - index = m_sortedModel.index(row, TodoFlatModel::Summary); - QCOMPARE(m_sortedModel.data(index).toString(), summary); - QCOMPARE(m_sortedModel.rowCount(index), 0); - QCOMPARE(m_sortedModel.columnCount(index), 0); - - index = m_sortedModel.index(row, TodoFlatModel::Categories); - QCOMPARE(m_sortedModel.data(index).toStringList(), categories); - QCOMPARE(m_sortedModel.rowCount(index), 0); - QCOMPARE(m_sortedModel.columnCount(index), 0); - - index = m_sortedModel.index(row, TodoFlatModel::ParentRemoteId); - QCOMPARE(m_sortedModel.data(index).toString(), parentRemoteId); - QCOMPARE(m_sortedModel.rowCount(index), 0); - QCOMPARE(m_sortedModel.columnCount(index), 0); - - index = m_sortedModel.index(row, TodoFlatModel::DueDate); - QCOMPARE(m_sortedModel.data(index).toString(), dueDate); - QCOMPARE(m_sortedModel.rowCount(index), 0); - QCOMPARE(m_sortedModel.columnCount(index), 0); -} - -void TodoFlatModelTest::testItemModification_data() -{ - QTest::addColumn("row"); - QTest::addColumn("column"); - QTest::addColumn("newData"); - QTest::addColumn("expected"); - - QTest::newRow("Changing Summary") << 2 << (int)TodoFlatModel::Summary << "Walk around with the CAT" << true; - QTest::newRow("Changing Summary (bis)") << 2 << (int)TodoFlatModel::Summary << "Walk around with the dog" << true; - QTest::newRow("New Parent") << 1 << (int)TodoFlatModel::ParentRemoteId << "fake-10" << true; - QTest::newRow("Wrong Parent") << 1 << (int)TodoFlatModel::ParentRemoteId << "zonzog" << false; - QTest::newRow("New Categories") << 3 << (int)TodoFlatModel::Categories << "Computer, Office" << true; - QTest::newRow("New Due Date") << 6 << (int)TodoFlatModel::DueDate << "2009-03-14" << true; - QTest::newRow("Wrong Due Date") << 6 << (int)TodoFlatModel::DueDate << "2009-42-21" << false; - QTest::newRow("Cycle Prevention") << 11 << (int)TodoFlatModel::ParentRemoteId << "fake-02" << false; -} - -void TodoFlatModelTest::testItemModification() -{ - QFETCH(int, row); - QFETCH(int, column); - QFETCH(QString, newData); - QFETCH(bool, expected); - - QSignalSpy spy(&m_sortedModel, SIGNAL(dataChanged(QModelIndex, QModelIndex))); - - QModelIndex index = m_sortedModel.index(row, column); - bool result = m_sortedModel.setData(index, newData); - QCOMPARE(result, expected); - - flushNotifications(); - - if (!expected) { - QCOMPARE(spy.count(), 0); - return; - } - - QCOMPARE(m_sortedModel.data(index).toStringList().join(", "), newData); - - QCOMPARE(spy.count(), 1); - QVariantList signal = spy.takeFirst(); - QCOMPARE(signal.count(), 2); - QCOMPARE(signal.at(0).value(), m_sortedModel.index(row, 0)); - QCOMPARE(signal.at(1).value(), m_sortedModel.index(row, TodoFlatModel::LastColumn)); -} - -void TodoFlatModelTest::testSingleRemoved() -{ - Akonadi::Item item = m_model.itemForIndex(m_sortedModel.mapToSource(m_sortedModel.index(1, 0))); - - int count = m_model.rowCount(); - - QSignalSpy spy(&m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int))); - - Akonadi::ItemDeleteJob *job = new Akonadi::ItemDeleteJob(item); - QVERIFY(job->exec()); - - flushNotifications(); - - QCOMPARE(m_model.rowCount(), count - 1); - - QCOMPARE(spy.count(), 1); - QVariantList signal = spy.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), QModelIndex()); - QCOMPARE(signal.at(1).toInt(), 1); - QCOMPARE(signal.at(1).toInt(), 1); - -} - -void TodoFlatModelTest::testRowTypeDuringInsert() -{ - int count = m_model.rowCount(); - - connect(&m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(onInsertRows(QModelIndex, int, int))); - - KCal::Todo *todo = new KCal::Todo(); - todo->setSummary("foo folder"); - todo->addComment("X-Zanshin-Folder"); - IncidencePtr incidence(todo); - - Akonadi::Item item; - item.setMimeType("application/x-vnd.akonadi.calendar.todo"); - item.setPayload(incidence); - - Akonadi::Collection collection = m_model.collection(); - Akonadi::ItemCreateJob *job = new Akonadi::ItemCreateJob(item, collection); - QVERIFY(job->exec()); - - flushNotifications(); - - QCOMPARE(m_model.rowCount(), count+1); - - disconnect(&m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(onInsertRows(QModelIndex, int, int))); -} - -void TodoFlatModelTest::onInsertRows(const QModelIndex &parent, int begin, int end) -{ - QVERIFY(!parent.isValid()); - QVERIFY(begin==end); - - QModelIndex index = m_model.index(begin, TodoFlatModel::RowType); - QCOMPARE(m_model.data(index).toInt(), (int)TodoFlatModel::FolderTodo); -} - -#include "todoflatmodeltest.moc" diff -Nru zanshin-0.1+svn1006410/src/tests/todotreemodeltest.cpp zanshin-0.2.0/src/tests/todotreemodeltest.cpp --- zanshin-0.1+svn1006410/src/tests/todotreemodeltest.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/tests/todotreemodeltest.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,550 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - Copyright 2008, 2009 Mario Bensi - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "modeltestbase.h" - -#include -#include -#include -#include - -#include - -#include "todoflatmodel.h" -#include "todotreemodel.h" - -class TodoTreeModelTest : public ModelTestBase -{ - Q_OBJECT - -private slots: - void initTestCase(); - void testInitialState(); - void testSingleModification(); - void testReparentModification(); - void testSingleRemoved(); - void testDragAndDropSimpleItemMove(); - void testDragAndDropMoveTwoItem(); - void testDragAndDropCycleTest(); - void testDragAndDropAddNewItem(); - void testDragAndDropMoveItemOnItem(); - void testDragAndDropMoveProjectInProject(); - void testDragAndDropMoveProjectInItem(); - void testDragAndDropMoveItemInFolder(); - void testDragAndDropMoveFolderInProject(); - void testDragAndDropMoveFolderInItem(); - void testDragAndDropMoveItemOnNullParent(); - -private: - TodoFlatModel m_flatModel; - QSortFilterProxyModel m_flatSortedModel; - TodoTreeModel m_model; - QSortFilterProxyModel m_sortedModel; -}; - -QTEST_AKONADIMAIN(TodoTreeModelTest, GUI) - -void TodoTreeModelTest::initTestCase() -{ - ModelTestBase::initTestCase(); - m_model.setSourceModel(&m_flatModel); - m_model.setCollection(m_collection); - flushNotifications(); - - m_flatSortedModel.setSourceModel(&m_flatModel); - m_flatSortedModel.sort(TodoFlatModel::RemoteId); - - m_sortedModel.setSourceModel(&m_model); - m_sortedModel.sort(TodoFlatModel::Summary); -} - -class TreeNode -{ -public: - TreeNode(const QString &i, const QString &s) - : id(i), summary(s) { } - - TreeNode &operator<<(const TreeNode &child) - { - children << child; - return *this; - } - - QString id; - QString summary; - QList children; -}; - -#if 0 -static void dumpTree(const QList &tree, int indent = 0) -{ - QString prefix; - for (int i=0; i &tree, - const QModelIndex &root = QModelIndex()) -{ - int row = 0; - foreach (const TreeNode &node, tree) { - QCOMPARE(model->data(model->index(row, TodoFlatModel::RemoteId, root)).toString(), node.id); - QCOMPARE(model->data(model->index(row, TodoFlatModel::Summary, root)).toString(), node.summary); - - QCOMPARE(model->rowCount(model->index(row, 0, root)), node.children.size()); - compareTrees(model, node.children, model->index(row, 0, root)); - row++; - } -} - -void TodoTreeModelTest::testInitialState() -{ - QList tree; - - tree << TreeNode("fake-14", "Choose a kitty") - << (TreeNode("fake-12", "First Folder") - << (TreeNode("fake-01", "Becoming Astronaut") - << TreeNode("fake-07", "Learn the constellations") - << TreeNode("fake-02", "Look at the stars") - ) - << (TreeNode("fake-11", "Becoming more relaxed") - << TreeNode("fake-09", "Listen new age album 2") - << TreeNode("fake-06", "Read magazine") - ) - ) - - << (TreeNode("fake-04", "Second Folder") - << (TreeNode("fake-10", "Pet Project") - << TreeNode("fake-08", "Choose a puppy") - << TreeNode("fake-05", "Feed the dog") - << TreeNode("fake-03", "Walk around with the dog") - ) - ); - - compareTrees(&m_sortedModel, tree); -} - -void TodoTreeModelTest::testSingleModification() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(6, 0))); - QModelIndex index = m_model.indexForItem(item, TodoFlatModel::Summary); - - QSignalSpy spy(&m_model, SIGNAL(dataChanged(QModelIndex, QModelIndex))); - - QVERIFY(m_model.setData(index, "Learn something")); - - flushNotifications(); - - QCOMPARE(m_model.data(index).toString(), QString("Learn something")); - - QCOMPARE(spy.count(), 1); - QVariantList signal = spy.takeFirst(); - QCOMPARE(signal.count(), 2); - QCOMPARE(signal.at(0).value(), m_model.index(index.row(), 0, index.parent())); - QCOMPARE(signal.at(1).value(), m_model.index(index.row(), TodoFlatModel::LastColumn, index.parent())); -} - -void TodoTreeModelTest::testReparentModification() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(1, 0))); - QModelIndex index = m_model.indexForItem(item, TodoFlatModel::ParentRemoteId); - - QModelIndex oldParent = index.parent(); - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(9, 0))); - QModelIndex newParent = m_model.indexForItem(item, 0); - - QSignalSpy rowsInserted(&m_model, SIGNAL(rowsInserted(QModelIndex, int, int))); - QSignalSpy rowsRemoved(&m_model, SIGNAL(rowsRemoved(QModelIndex, int, int))); - - - - QVERIFY(m_model.setData(index, "fake-10")); - - flushNotifications(); - - QCOMPARE(rowsRemoved.count(), 1); - QVariantList signal = rowsRemoved.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), oldParent); - QCOMPARE(signal.at(1).toInt(), 1); - QCOMPARE(signal.at(2).toInt(), 1); - - QCOMPARE(rowsInserted.count(), 1); - signal = rowsInserted.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), newParent); - QCOMPARE(signal.at(1).toInt(), 3); - QCOMPARE(signal.at(2).toInt(), 3); - - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(1, 0))); - index = m_model.indexForItem(item, TodoFlatModel::ParentRemoteId); - QVERIFY(m_model.setData(index, "fake-01")); - - flushNotifications(); - - QCOMPARE(rowsRemoved.count(), 1); - signal = rowsRemoved.takeFirst(); - QCOMPARE(signal.count(), 3); - qDebug() << signal.at(0).value() << newParent; - QCOMPARE(signal.at(0).value(), newParent); - QCOMPARE(signal.at(1).toInt(), 3); - QCOMPARE(signal.at(2).toInt(), 3); - - QCOMPARE(rowsInserted.count(), 1); - signal = rowsInserted.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), oldParent); - QCOMPARE(signal.at(1).toInt(), 1); - QCOMPARE(signal.at(2).toInt(), 1); -} - -void TodoTreeModelTest::testSingleRemoved() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(2, 0))); - QModelIndex index = m_model.indexForItem(item, TodoFlatModel::ParentRemoteId); - - QModelIndex parent = index.parent(); - int count = m_model.rowCount(parent); - - QSignalSpy spy(&m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int))); - - Akonadi::ItemDeleteJob *job = new Akonadi::ItemDeleteJob(item); - QVERIFY(job->exec()); - - flushNotifications(); - - QCOMPARE(m_model.rowCount(parent), count - 1); - - QCOMPARE(spy.count(), 1); - QVariantList signal = spy.takeFirst(); - QCOMPARE(signal.count(), 3); - QCOMPARE(signal.at(0).value(), parent); - QCOMPARE(signal.at(1).toInt(), 2); - QCOMPARE(signal.at(1).toInt(), 2); -} - -void TodoTreeModelTest::testDragAndDropSimpleItemMove() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(5, 0))); - QModelIndex index = m_model.indexForItem(item); - QModelIndex parentRemoteIndex = m_model.indexForItem(item, TodoFlatModel::ParentRemoteId); - - QCOMPARE(m_model.data(parentRemoteIndex).toString(), QString("fake-01")); - - QModelIndexList indexes; - indexes << index; - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, QModelIndex())); - - flushNotifications(); - - parentRemoteIndex = m_model.indexForItem(item, TodoFlatModel::ParentRemoteId); - QCOMPARE(m_model.data(parentRemoteIndex).toString(), QString()); - - indexes.clear(); -} - -void TodoTreeModelTest::testDragAndDropMoveTwoItem() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(3, 0))); - QModelIndex index = m_model.indexForItem(item); - QModelIndex parentRemoteIndex = m_model.indexForItem(item, TodoFlatModel::ParentRemoteId); - - QCOMPARE(m_model.data(parentRemoteIndex).toString(), QString("fake-10")); - - Akonadi::Item item2 = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(2, 0))); - QModelIndex index2 = m_model.indexForItem(item2); - QModelIndex parentRemoteIndex2 = m_model.indexForItem(item2, TodoFlatModel::ParentRemoteId); - - QCOMPARE(m_model.data(parentRemoteIndex2).toString(), QString("")); - - Akonadi::Item newParentItem = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(10, 0))); - QModelIndex newParentIndex = m_model.indexForItem(newParentItem); - QModelIndex newParentRemoteIndex = m_model.indexForItem(newParentItem, TodoFlatModel::RemoteId); - QString remoteId = m_model.data(newParentRemoteIndex).toString(); - - QModelIndexList indexes; - indexes << index; - indexes << index2; - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, newParentIndex)); - - flushNotifications(); - - parentRemoteIndex = m_model.indexForItem(item, TodoFlatModel::ParentRemoteId); - - QCOMPARE(m_model.data(parentRemoteIndex).toString(), remoteId); - - parentRemoteIndex = m_model.indexForItem(item2, TodoFlatModel::ParentRemoteId); - QCOMPARE(m_model.data(parentRemoteIndex).toString(), remoteId); - - indexes.clear(); -} - -void TodoTreeModelTest::testDragAndDropCycleTest() -{ - //test cycle - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(9, 0))); - QModelIndex index = m_model.indexForItem(item); - QModelIndex parentRemoteIndex = m_model.indexForItem(item, TodoFlatModel::ParentRemoteId); - - QCOMPARE(m_model.data(parentRemoteIndex).toString(), QString("fake-12")); - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(7, 0))); - QModelIndex remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-09")); - - QModelIndexList indexes; - indexes << index; - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(!m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, remoteIndex)); - - flushNotifications(); - indexes.clear(); -} - -void TodoTreeModelTest::testDragAndDropAddNewItem() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(11, 0))); - QModelIndex index = m_flatModel.indexForItem(item, 0); - QModelIndex parentRemoteIndex = m_flatModel.indexForItem(item, TodoFlatModel::ParentRemoteId); - QModelIndex remoteIndex = m_flatModel.indexForItem(item, TodoFlatModel::RemoteId); - - QCOMPARE(m_flatModel.data(parentRemoteIndex).toString(), QString("")); - - Akonadi::Item newParentItem = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(10, 0))); - QModelIndex newParentIndex = m_model.indexForItem(newParentItem); - QModelIndex newParentRemoteIndex = m_model.indexForItem(newParentItem, TodoFlatModel::RemoteId); - - QModelIndexList indexes; - indexes << index; - QMimeData *mimeData = m_flatModel.mimeData(indexes); - QVERIFY(m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, newParentIndex)); - - flushNotifications(); - - parentRemoteIndex = m_model.indexForItem(item, TodoFlatModel::ParentRemoteId); - - QCOMPARE(m_model.data(parentRemoteIndex).toString(), m_model.data(newParentRemoteIndex).toString()); - - indexes.clear(); -} - -void TodoTreeModelTest::testDragAndDropMoveProjectInProject() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(9, 0))); - QModelIndex index = m_model.indexForItem(item); - QModelIndex remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - QModelIndex typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::ProjectTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-11")); - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(0, 0))); - QModelIndex newParentIndex = m_model.indexForItem(item); - remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::ProjectTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-01")); - - QModelIndexList indexes; - indexes << index; - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(!m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, newParentIndex)); - - flushNotifications(); - indexes.clear(); -} - -void TodoTreeModelTest::testDragAndDropMoveProjectInItem() -{ - //move project in item - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(9, 0))); - QModelIndex index = m_model.indexForItem(item); - QModelIndex remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - QModelIndex typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::ProjectTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-11")); - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(1, 0))); - QModelIndex newParentIndex = m_model.indexForItem(item); - remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::StandardTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-02")); - - QModelIndexList indexes; - indexes << index; - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(!m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, newParentIndex)); - - flushNotifications(); - indexes.clear(); -} - -void TodoTreeModelTest::testDragAndDropMoveItemInFolder() -{ - //move item in folder - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(4, 0))); - QModelIndex index = m_model.indexForItem(item); - QModelIndex remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - QModelIndex typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::StandardTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-06")); - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(10, 0))); - QModelIndex newParentIndex = m_model.indexForItem(item); - remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::FolderTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-12")); - - QModelIndexList indexes; - indexes << index; - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, newParentIndex)); - - flushNotifications(); - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(4, 0))); - remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-06")); - typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::ProjectTodo)); - - indexes.clear(); -} - -void TodoTreeModelTest::testDragAndDropMoveFolderInProject() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(2, 0))); - QModelIndex index = m_model.indexForItem(item); - QModelIndex remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - QModelIndex typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::FolderTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-04")); - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(4, 0))); - QModelIndex newParentIndex = m_model.indexForItem(item); - remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::ProjectTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-06")); - - QModelIndexList indexes; - indexes << index; - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(!m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, newParentIndex)); - - flushNotifications(); - indexes.clear(); -} - -void TodoTreeModelTest::testDragAndDropMoveFolderInItem() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(2, 0))); - QModelIndex index = m_model.indexForItem(item); - QModelIndex remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - QModelIndex typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::FolderTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-04")); - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(7, 0))); - QModelIndex newParentIndex = m_model.indexForItem(item); - remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::StandardTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-09")); - - QModelIndexList indexes; - indexes << index; - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(!m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, newParentIndex)); - - flushNotifications(); - indexes.clear(); -} - -void TodoTreeModelTest::testDragAndDropMoveItemOnNullParent() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(1, 0))); - QModelIndex index = m_model.indexForItem(item); - QModelIndex remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - QModelIndex typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::StandardTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-02")); - - QModelIndex newParentIndex; - - QModelIndexList indexes; - indexes << index; - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, newParentIndex)); - - flushNotifications(); - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(1, 0))); - remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-02")); - typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::ProjectTodo)); - - indexes.clear(); -} - -void TodoTreeModelTest::testDragAndDropMoveItemOnItem() -{ - Akonadi::Item item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(1, 0))); - QModelIndex index = m_model.indexForItem(item); - QModelIndex remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - QModelIndex typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::StandardTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-02")); - - item = m_flatModel.itemForIndex(m_flatSortedModel.mapToSource(m_flatSortedModel.index(7, 0))); - QModelIndex newParentIndex = m_model.indexForItem(item); - remoteIndex = m_model.indexForItem(item, TodoFlatModel::RemoteId); - typeIndex = m_model.indexForItem(item, TodoFlatModel::RowType); - QCOMPARE(m_model.data(typeIndex).toString(), QString::number(TodoFlatModel::StandardTodo)); - QCOMPARE(m_model.data(remoteIndex).toString(), QString("fake-09")); - - QModelIndexList indexes; - indexes << index; - QMimeData *mimeData = m_model.mimeData(indexes); - QVERIFY(!m_model.dropMimeData(mimeData, Qt::MoveAction, 0, 0, newParentIndex)); - - flushNotifications(); - indexes.clear(); -} - -#include "todotreemodeltest.moc" diff -Nru zanshin-0.1+svn1006410/src/tests/unittestenv/config.xml zanshin-0.2.0/src/tests/unittestenv/config.xml --- zanshin-0.1+svn1006410/src/tests/unittestenv/config.xml 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/tests/unittestenv/config.xml 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ - - kdehome - xdgconfig - xdglocal - akonadi_ical_resource - diff -Nru zanshin-0.1+svn1006410/src/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc zanshin-0.2.0/src/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc --- zanshin-0.1+svn1006410/src/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/tests/unittestenv/kdehome/share/config/akonadi-firstrunrc 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -[ProcessedDefaults] -defaultaddressbook=done -defaultcalendar=done diff -Nru zanshin-0.1+svn1006410/src/tests/unittestenv/kdehome/share/config/akonadi_ical_resource_0rc zanshin-0.2.0/src/tests/unittestenv/kdehome/share/config/akonadi_ical_resource_0rc --- zanshin-0.1+svn1006410/src/tests/unittestenv/kdehome/share/config/akonadi_ical_resource_0rc 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/tests/unittestenv/kdehome/share/config/akonadi_ical_resource_0rc 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -[General] -Path[$e]=$KDEHOME/testdata.ics -FileWatchingEnabled=false - diff -Nru zanshin-0.1+svn1006410/src/tests/unittestenv/kdehome/share/config/kdebugrc zanshin-0.2.0/src/tests/unittestenv/kdehome/share/config/kdebugrc --- zanshin-0.1+svn1006410/src/tests/unittestenv/kdehome/share/config/kdebugrc 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/tests/unittestenv/kdehome/share/config/kdebugrc 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -[0] -AbortFatal=true -ErrorFilename[$e]=kdebug.dbg -ErrorOutput=2 -FatalFilename[$e]=kdebug.dbg -FatalOutput=2 -InfoFilename[$e]=kdebug.dbg -InfoOutput=2 -WarnFilename[$e]=kdebug.dbg -WarnOutput=2 - -[264] -AbortFatal=true -ErrorFilename[$e]=kdebug.dbg -ErrorOutput=4 -FatalFilename[$e]=kdebug.dbg -FatalOutput=4 -InfoFilename[$e]=kdebug.dbg -WarnFilename[$e]=kdebug.dbg -WarnOutput=4 - -[5250] -InfoOutput=2 - -[7009] -AbortFatal=true -ErrorFilename[$e]=kdebug.dbg -ErrorOutput=4 -FatalFilename[$e]=kdebug.dbg -FatalOutput=4 -InfoFilename[$e]=kdebug.dbg -InfoOutput=4 -WarnFilename[$e]=kdebug.dbg -WarnOutput=4 - -[7011] -AbortFatal=true -ErrorFilename[$e]=kdebug.dbg -ErrorOutput=4 -FatalFilename[$e]=kdebug.dbg -FatalOutput=4 -InfoFilename[$e]=kdebug.dbg -InfoOutput=4 -WarnFilename[$e]=kdebug.dbg -WarnOutput=4 - -[7012] -AbortFatal=true -ErrorFilename[$e]=kdebug.dbg -ErrorOutput=4 -FatalFilename[$e]=kdebug.dbg -FatalOutput=4 -InfoFilename[$e]=kdebug.dbg -InfoOutput=4 -WarnFilename[$e]=kdebug.dbg -WarnOutput=4 - -[7014] -AbortFatal=true -ErrorFilename[$e]=kdebug.dbg -ErrorOutput=0 -FatalFilename[$e]=kdebug.dbg -FatalOutput=0 -InfoFilename[$e]=kdebug.dbg -InfoOutput=0 -WarnFilename[$e]=kdebug.dbg -WarnOutput=0 - -[7021] -AbortFatal=true -ErrorFilename[$e]=kdebug.dbg -ErrorOutput=4 -FatalFilename[$e]=kdebug.dbg -FatalOutput=4 -InfoFilename[$e]=kdebug.dbg -InfoOutput=4 -WarnFilename[$e]=kdebug.dbg -WarnOutput=4 diff -Nru zanshin-0.1+svn1006410/src/tests/unittestenv/kdehome/share/config/kdedrc zanshin-0.2.0/src/tests/unittestenv/kdehome/share/config/kdedrc --- zanshin-0.1+svn1006410/src/tests/unittestenv/kdehome/share/config/kdedrc 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/tests/unittestenv/kdehome/share/config/kdedrc 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -[General] -CheckSycoca=false -CheckFileStamps=false diff -Nru zanshin-0.1+svn1006410/src/tests/unittestenv/kdehome/testdata.ics zanshin-0.2.0/src/tests/unittestenv/kdehome/testdata.ics --- zanshin-0.1+svn1006410/src/tests/unittestenv/kdehome/testdata.ics 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/tests/unittestenv/kdehome/testdata.ics 1970-01-01 00:00:00.000000000 +0000 @@ -1,317 +0,0 @@ -BEGIN:VCALENDAR -PRODID:-//K Desktop Environment//NONSGML libkcal 3.5//EN -VERSION:2.0 -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231219Z -UID:fake-01 -SEQUENCE:1 -LAST-MODIFIED:20081205T231427Z -SUMMARY:Becoming Astronaut -RELATED-TO:fake-12 -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231513Z -UID:fake-02 -LAST-MODIFIED:20081205T231513Z -SUMMARY:Look at the stars -PRIORITY:5 -RELATED-TO:fake-01 -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231407Z -UID:fake-03 -SEQUENCE:2 -LAST-MODIFIED:20081205T231615Z -SUMMARY:Walk around with the dog -CATEGORIES:Errands -RELATED-TO:fake-10 -DUE;VALUE=DATE:20081225 -DTSTART;VALUE=DATE:20081218 -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231157Z -UID:fake-04 -LAST-MODIFIED:20081205T231157Z -SUMMARY:Second Folder -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231358Z -UID:fake-05 -SEQUENCE:1 -LAST-MODIFIED:20081205T231449Z -SUMMARY:Feed the dog -CATEGORIES:Home -RELATED-TO:fake-10 -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231334Z -UID:fake-06 -SEQUENCE:1 -LAST-MODIFIED:20081205T231441Z -SUMMARY:Read magazine -CATEGORIES:Office -CATEGORIES:Computer -RELATED-TO:fake-11 -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231530Z -UID:fake-07 -LAST-MODIFIED:20081205T231530Z -SUMMARY:Learn the constellations -PRIORITY:5 -RELATED-TO:fake-01 -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231351Z -UID:fake-08 -SEQUENCE:1 -LAST-MODIFIED:20081205T231452Z -SUMMARY:Choose a puppy -RELATED-TO:fake-10 -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231321Z -UID:fake-09 -SEQUENCE:1 -LAST-MODIFIED:20081205T231438Z -SUMMARY:Listen new age album 2 -RELATED-TO:fake-11 -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231210Z -UID:fake-10 -SEQUENCE:1 -LAST-MODIFIED:20081205T231423Z -SUMMARY:Pet Project -RELATED-TO:fake-04 -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231257Z -UID:fake-11 -SEQUENCE:1 -LAST-MODIFIED:20081205T231430Z -SUMMARY:Becoming more relaxed -RELATED-TO:fake-12 -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231114Z -UID:fake-12 -LAST-MODIFIED:20081205T231114Z -SUMMARY:First Folder -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VEVENT -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231042Z -UID:fake-13 -LAST-MODIFIED:20081205T231042Z -SUMMARY:Fake Event -LOCATION:Somewhere -DTSTART;TZID=Europe/Paris:20081203T100000 -DTEND;TZID=Europe/Paris:20081203T120000 -TRANSP:OPAQUE -END:VEVENT - -BEGIN:VTODO -DTSTAMP:20081205T231615Z -ORGANIZER;CN=Kevin Ottens:MAILTO:ervin@kde.org -CREATED:20081205T231351Z -UID:fake-14 -SEQUENCE:1 -LAST-MODIFIED:20081205T231452Z -SUMMARY:Choose a kitty -PERCENT-COMPLETE:0 -END:VTODO - -BEGIN:VTIMEZONE -TZID:Europe/Paris -BEGIN:STANDARD -TZNAME:WET -TZOFFSETFROM:+000921 -TZOFFSETTO:+0000 -DTSTART:19110311T235139 -RDATE;VALUE=DATE-TIME:19110311T235139 -END:STANDARD - -BEGIN:DAYLIGHT -TZNAME:WEST -TZOFFSETFROM:+0000 -TZOFFSETTO:+0100 -DTSTART:19160615T230000 -RDATE;VALUE=DATE-TIME:19160615T230000 -RDATE;VALUE=DATE-TIME:19170325T230000 -RDATE;VALUE=DATE-TIME:19180310T230000 -RDATE;VALUE=DATE-TIME:19190302T230000 -RDATE;VALUE=DATE-TIME:19200215T230000 -RDATE;VALUE=DATE-TIME:19210315T230000 -RDATE;VALUE=DATE-TIME:19220326T230000 -RDATE;VALUE=DATE-TIME:19230527T230000 -RDATE;VALUE=DATE-TIME:19240330T230000 -RDATE;VALUE=DATE-TIME:19250405T230000 -RDATE;VALUE=DATE-TIME:19260418T230000 -RDATE;VALUE=DATE-TIME:19270410T230000 -RDATE;VALUE=DATE-TIME:19280415T230000 -RDATE;VALUE=DATE-TIME:19290421T230000 -RDATE;VALUE=DATE-TIME:19300413T230000 -RDATE;VALUE=DATE-TIME:19310419T230000 -RDATE;VALUE=DATE-TIME:19320403T230000 -RDATE;VALUE=DATE-TIME:19330326T230000 -RDATE;VALUE=DATE-TIME:19340408T230000 -RDATE;VALUE=DATE-TIME:19350331T230000 -RDATE;VALUE=DATE-TIME:19360419T230000 -RDATE;VALUE=DATE-TIME:19370404T230000 -RDATE;VALUE=DATE-TIME:19380327T230000 -RDATE;VALUE=DATE-TIME:19390416T230000 -RDATE;VALUE=DATE-TIME:19400226T020000 -END:DAYLIGHT - -BEGIN:STANDARD -TZNAME:WET -TZOFFSETFROM:+0100 -TZOFFSETTO:+0000 -DTSTART:19231008T000000 -RRULE:FREQ=YEARLY;COUNT=10;BYDAY=1SU;BYMONTH=10 -END:STANDARD - -BEGIN:STANDARD -TZNAME:WET -TZOFFSETFROM:+0100 -TZOFFSETTO:+0000 -DTSTART:19161003T000000 -RDATE;VALUE=DATE-TIME:19161003T000000 -RDATE;VALUE=DATE-TIME:19171009T000000 -RDATE;VALUE=DATE-TIME:19181008T000000 -RDATE;VALUE=DATE-TIME:19191007T000000 -RDATE;VALUE=DATE-TIME:19201025T000000 -RDATE;VALUE=DATE-TIME:19211027T000000 -RDATE;VALUE=DATE-TIME:19221009T000000 -RDATE;VALUE=DATE-TIME:19331009T000000 -RDATE;VALUE=DATE-TIME:19341008T000000 -RDATE;VALUE=DATE-TIME:19351007T000000 -RDATE;VALUE=DATE-TIME:19361005T000000 -RDATE;VALUE=DATE-TIME:19371004T000000 -RDATE;VALUE=DATE-TIME:19381003T000000 -RDATE;VALUE=DATE-TIME:19391120T000000 -END:STANDARD - -BEGIN:DAYLIGHT -TZNAME:CEST -TZOFFSETFROM:+0100 -TZOFFSETTO:+0200 -DTSTART:19810329T020000 -RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 -END:DAYLIGHT - -BEGIN:DAYLIGHT -TZNAME:CEST -TZOFFSETFROM:+0100 -TZOFFSETTO:+0200 -DTSTART:19400615T220000 -RDATE;VALUE=DATE-TIME:19400615T220000 -RDATE;VALUE=DATE-TIME:19430329T010000 -RDATE;VALUE=DATE-TIME:19440403T010000 -RDATE;VALUE=DATE-TIME:19760328T000000 -RDATE;VALUE=DATE-TIME:19770403T020000 -RDATE;VALUE=DATE-TIME:19780402T020000 -RDATE;VALUE=DATE-TIME:19790401T020000 -RDATE;VALUE=DATE-TIME:19800406T020000 -END:DAYLIGHT - -BEGIN:STANDARD -TZNAME:CET -TZOFFSETFROM:+0200 -TZOFFSETTO:+0100 -DTSTART:19790930T030000 -RRULE:FREQ=YEARLY;COUNT=17;BYDAY=-1SU;BYMONTH=9 -END:STANDARD - -BEGIN:STANDARD -TZNAME:CET -TZOFFSETFROM:+0200 -TZOFFSETTO:+0100 -DTSTART:19961027T030000 -RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 -END:STANDARD - -BEGIN:STANDARD -TZNAME:CET -TZOFFSETFROM:+0200 -TZOFFSETTO:+0100 -DTSTART:19421103T030000 -RDATE;VALUE=DATE-TIME:19421103T030000 -RDATE;VALUE=DATE-TIME:19431004T020000 -RDATE;VALUE=DATE-TIME:19450917T010000 -RDATE;VALUE=DATE-TIME:19760925T230000 -RDATE;VALUE=DATE-TIME:19770925T030000 -RDATE;VALUE=DATE-TIME:19781001T030000 -END:STANDARD - -BEGIN:DAYLIGHT -TZNAME:WEST -TZOFFSETFROM:+0200 -TZOFFSETTO:+0100 -DTSTART:19441008T230000 -RDATE;VALUE=DATE-TIME:19441008T230000 -END:DAYLIGHT - -BEGIN:DAYLIGHT -TZNAME:WEMT -TZOFFSETFROM:+0100 -TZOFFSETTO:+0200 -DTSTART:19450402T010000 -RDATE;VALUE=DATE-TIME:19450402T010000 -END:DAYLIGHT - -END:VTIMEZONE - -END:VCALENDAR - - diff -Nru zanshin-0.1+svn1006410/src/todocategoriesattribute.cpp zanshin-0.2.0/src/todocategoriesattribute.cpp --- zanshin-0.1+svn1006410/src/todocategoriesattribute.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/todocategoriesattribute.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "todocategoriesattribute.h" - -TodoCategoriesAttribute::TodoCategoriesAttribute() -{ - -} - -QStringList TodoCategoriesAttribute::parentList() const -{ - return m_parentList; -} - -void TodoCategoriesAttribute::setParentList(const QStringList &parentList) -{ - m_parentList = parentList; -} - -QByteArray TodoCategoriesAttribute::type() const -{ - return "zanshin-categories"; -} - -Akonadi::Attribute *TodoCategoriesAttribute::clone() const -{ - TodoCategoriesAttribute *newClone = new TodoCategoriesAttribute(); - newClone->m_parentList = m_parentList; - return newClone; -} - -QByteArray TodoCategoriesAttribute::serialized() const -{ - QString data = m_parentList.join(", "); - return data.toUtf8(); -} - -void TodoCategoriesAttribute::deserialize(const QByteArray &data) -{ - QString copy = QString::fromUtf8(data); - m_parentList = copy.split(QRegExp("\\s*,\\s*")); -} diff -Nru zanshin-0.1+svn1006410/src/todocategoriesattribute.h zanshin-0.2.0/src/todocategoriesattribute.h --- zanshin-0.1+svn1006410/src/todocategoriesattribute.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/todocategoriesattribute.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#ifndef ZANSHIN_TODOCATEGORIESATTRIBUTE_H -#define ZANSHIN_TODOCATEGORIESATTRIBUTE_H - -#include - -#include - -class TodoCategoriesAttribute : public Akonadi::Attribute -{ -public: - TodoCategoriesAttribute(); - - QStringList parentList() const; - void setParentList(const QStringList &parentList); - - virtual QByteArray type() const; - virtual Akonadi::Attribute *clone() const; - virtual QByteArray serialized() const; - virtual void deserialize(const QByteArray &data); - -private: - QStringList m_parentList; -}; - -#endif diff -Nru zanshin-0.1+svn1006410/src/todocategoriesmodel.cpp zanshin-0.2.0/src/todocategoriesmodel.cpp --- zanshin-0.1+svn1006410/src/todocategoriesmodel.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/todocategoriesmodel.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -24,741 +24,475 @@ #include "todocategoriesmodel.h" -#include -#include -#include - -#include - -#include -#include -#include - #include +#include -#include "todocategoriesattribute.h" -#include "todoflatmodel.h" - -class TodoCategoryTreeNode -{ -public: - TodoCategoryTreeNode(const QString &c, TodoCategoryTreeNode *p = 0) - : id(-1), category(c), parent(p) { } - - TodoCategoryTreeNode(Akonadi::Entity::Id i, TodoCategoryTreeNode *p = 0) - : id(i), parent(p) { } - - ~TodoCategoryTreeNode() { qDeleteAll(children); } - - Akonadi::Entity::Id id; - QString category; - - TodoCategoryTreeNode *parent; - QList children; -}; +#include +#include +#include +#include +#include + +#include "categorymanager.h" +#include "globaldefs.h" +#include "todohelpers.h" +#include "todonode.h" +#include "todonodemanager.h" TodoCategoriesModel::TodoCategoriesModel(QObject *parent) - : QAbstractProxyModel(parent) + : TodoProxyModelBase(MultiMapping, parent), m_categoryRootNode(0) { + connect(&CategoryManager::instance(), SIGNAL(categoryAdded(QString)), + this, SLOT(createCategoryNode(QString))); + connect(&CategoryManager::instance(), SIGNAL(categoryRemoved(QString)), + this, SLOT(removeCategoryNode(QString))); + connect(&CategoryManager::instance(), SIGNAL(categoryRenamed(QString,QString)), + this, SLOT(renameCategoryNode(QString,QString))); + connect(&CategoryManager::instance(), SIGNAL(categoryMoved(QString,QString)), + this, SLOT(moveCategoryNode(QString,QString))); } TodoCategoriesModel::~TodoCategoriesModel() { - qDeleteAll(m_roots); } -QModelIndex TodoCategoriesModel::index(int row, int column, const QModelIndex &parent) const +void TodoCategoriesModel::onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end) { - if (row < 0 || column < 0 - || row >= rowCount(parent) - || column >= columnCount(parent)) { - return QModelIndex(); - } + for (int i = begin; i <= end; i++) { + QModelIndex sourceChildIndex = sourceModel()->index(i, 0, sourceIndex); - return createIndex(row, column, nodeForIndex(parent)); -} + if (!sourceChildIndex.isValid()) { + continue; + } -QModelIndex TodoCategoriesModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) { - return QModelIndex(); - } + Zanshin::ItemType type = (Zanshin::ItemType) sourceChildIndex.data(Zanshin::ItemTypeRole).toInt(); + if (type==Zanshin::StandardTodo) { + QStringList categories = sourceModel()->data(sourceChildIndex, Zanshin::CategoriesRole).toStringList(); - TodoCategoryTreeNode *parent = nodeForIndex(index)->parent; + if (categories.isEmpty()) { + addChildNode(sourceChildIndex, m_inboxNode); - if (parent == 0) { - return QModelIndex(); - } else { - return indexForNode(parent); - } -} + } else { + QList nodes = m_manager->nodesForSourceIndex(sourceChildIndex); + QSet oldCategories; -int TodoCategoriesModel::rowCount(const QModelIndex &parent) const -{ - if (!parent.isValid()) { - return m_roots.size(); - } else if (parent.column() == 0) { // Only one set of children per row - TodoCategoryTreeNode *node = nodeForIndex(parent); - return node->children.size(); - } + foreach (TodoNode *node, nodes) { - return 0; -} + TodoNode *categoryNode = node->parent(); + if (categoryNode + && categoryNode->data(0, Zanshin::ItemTypeRole).toInt()!=Zanshin::Inbox) { + QString category = categoryNode->data(0, Zanshin::CategoryPathRole).toString(); + oldCategories << category; + } + } -int TodoCategoriesModel::columnCount(const QModelIndex &/*parent*/) const -{ - return TodoFlatModel::LastColumn+1; + QSet newCategories = QSet::fromList(categories); + QSet interCategories = newCategories; + interCategories.intersect(oldCategories); + newCategories-= interCategories; + + foreach (const QString &category, newCategories) { + TodoNode *parent = m_categoryMap[category]; + Q_ASSERT(parent); + addChildNode(sourceChildIndex, parent); + } + } + } else if (type==Zanshin::Collection) { + onSourceInsertRows(sourceChildIndex, 0, sourceModel()->rowCount(sourceChildIndex)-1); + } + } } -QVariant TodoCategoriesModel::data(const QModelIndex &index, int role) const +void TodoCategoriesModel::onSourceRemoveRows(const QModelIndex &sourceIndex, int begin, int end) { - TodoCategoryTreeNode *node = nodeForIndex(index); - - if (node == 0) { - return QVariant(); - } + for (int i = begin; i <= end; ++i) { + QModelIndex sourceChildIndex = sourceModel()->index(i, 0, sourceIndex); + Zanshin::ItemType type = (Zanshin::ItemType) sourceChildIndex.data(Zanshin::ItemTypeRole).toInt(); - if (node->id != -1) { - return QAbstractProxyModel::data(index, role); - } + if (type==Zanshin::Collection) { + onSourceRemoveRows(sourceChildIndex, + 0, sourceModel()->rowCount(sourceChildIndex)-1); - if (role == Qt::DisplayRole || role == Qt::EditRole) { - if (index.column() == TodoFlatModel::Summary) { - return node->category; - } else if (index.column() == TodoFlatModel::RowType) { - return TodoFlatModel::Category; - } else if (index.column() == TodoFlatModel::Categories && node->parent != 0) { - return QStringList() << node->parent->category; } else { - return QStringList(); + QModelIndexList proxyIndexes = mapFromSourceAll(sourceChildIndex); + + foreach (const QModelIndex &proxyIndex, proxyIndexes) { + TodoNode *node = m_manager->nodeForIndex(proxyIndex); + + beginRemoveRows(proxyIndex.parent(), proxyIndex.row(), proxyIndex.row()); + m_manager->removeNode(node); + delete node; + endRemoveRows(); + } } - } else if (role == Qt::DecorationRole && index.column() == TodoFlatModel::Summary) { - return KIcon("view-pim-notes"); } - - return QVariant(); } -bool TodoCategoriesModel::setData(const QModelIndex &index, const QVariant &value, int role) +void TodoCategoriesModel::onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end) { - TodoCategoryTreeNode *node = nodeForIndex(index); - - if (node == 0) { - return false; - } - - if (node->id != -1) { - return QAbstractProxyModel::setData(index, value, role); - } + for (int row=begin.row(); row<=end.row(); ++row) { + QModelIndex sourceIndex = begin.sibling(row, 0); + QList nodes = m_manager->nodesForSourceIndex(sourceIndex); - if (role == Qt::DisplayRole || role == Qt::EditRole) { - if (index.column() == TodoFlatModel::Summary) { - QString oldName = node->category; - QString newName = value.toString().trimmed(); + if (sourceIndex.data(Zanshin::ItemTypeRole).toInt()==Zanshin::Collection) { + return; + } - if (newName.isEmpty() || m_categoryMap.contains(newName)) { - return false; + if (sourceIndex.data(Zanshin::ItemTypeRole).toInt()==Zanshin::ProjectTodo) { + foreach (TodoNode *node, nodes) { + TodoNode *parentNode = node->parent(); + int oldRow = parentNode->children().indexOf(node); + beginRemoveRows(m_manager->indexForNode(parentNode, 0), oldRow, oldRow); + m_manager->removeNode(node); + delete node; + endRemoveRows(); } + return; + } - m_categoryMap.remove(oldName); - node->category = newName; - m_categoryMap[newName] = node; - - serializeCategories(); - emit dataChanged(index.sibling(index.row(), 0), - index.sibling(index.row(), TodoFlatModel::LastColumn)); - - foreach (TodoCategoryTreeNode *child, node->children) { - QModelIndex childCatIndex = indexForNode(child, TodoFlatModel::Categories); - QStringList categories = data(childCatIndex).toStringList(); - categories.removeAll(oldName); - categories << newName; - setData(childCatIndex, categories); + QSet oldCategories; + QHash nodeMap; + foreach (TodoNode *node, nodes) { + QModelIndex begin = m_manager->indexForNode(node, 0); + QModelIndex end = m_manager->indexForNode(node, qMax(begin.column(), end.column())); + emit dataChanged(begin, end); + + TodoNode *categoryNode = node->parent(); + if (categoryNode + && categoryNode->data(0, Zanshin::ItemTypeRole).toInt()!=Zanshin::Inbox) { + QString category = categoryNode->data(0, Zanshin::CategoryPathRole).toString(); + oldCategories << category; + nodeMap[category] = node; } + } - return true; - } else if (index.column() == TodoFlatModel::Categories) { - QModelIndex parentIndex; - TodoCategoryTreeNode *newParent = 0; - TodoCategoryTreeNode *oldParent = node->parent; - - beginRemoveRows(index.parent(), index.row(), index.row()); - if (oldParent) { - oldParent->children.removeAll(node); - } else { - m_roots.removeAll(node); - } + QSet newCategories = QSet::fromList(sourceIndex.data(Zanshin::CategoriesRole).toStringList()); + + QSet interCategories = newCategories; + interCategories.intersect(oldCategories); + newCategories-= interCategories; + oldCategories-= interCategories; + + foreach (const QString &oldCategory, oldCategories) { + TodoNode *parentNode = m_categoryMap[oldCategory]; + TodoNode *node = nodeMap[oldCategory]; + + int oldRow = parentNode->children().indexOf(node); + beginRemoveRows(m_manager->indexForNode(parentNode, 0), oldRow, oldRow); + m_manager->removeNode(node); + delete node; endRemoveRows(); + } - int row = 0; - if(!value.toString().isEmpty()) { - parentIndex = indexForCategory(value.toString()); - newParent = nodeForIndex(parentIndex); - row = newParent->children.size(); - } else { - row = m_roots.size(); + if (!oldCategories.isEmpty()) { + QStringList categories = sourceModel()->data(sourceIndex, Zanshin::CategoriesRole).toStringList(); + if (categories.isEmpty()) { + addChildNode(sourceIndex, m_inboxNode); } + } - beginInsertRows(parentIndex, row, row); - node->parent = newParent; - if (newParent) { - newParent->children.append(node); - } else { - m_roots.append(node); + if (!newCategories.isEmpty()) { + TodoNode *node = 0; + QList nodes = m_manager->nodesForSourceIndex(sourceIndex); + foreach (TodoNode *n, nodes) { + if (n->parent() == m_inboxNode) { + node = n; + break; + } + } + if (node) { + int oldRow = m_inboxNode->children().indexOf(node); + beginRemoveRows(m_manager->indexForNode(m_inboxNode, 0), oldRow, oldRow); + m_manager->removeNode(node); + delete node; + endRemoveRows(); } - endInsertRows(); - - serializeCategories(); - return true; + foreach (const QString &newCategory, newCategories) { + TodoNode *parent = m_categoryMap[newCategory]; + Q_ASSERT(parent); + addChildNode(sourceIndex, parent); + } } - } - - return false; } -QStringList TodoCategoriesModel::mimeTypes() const +void TodoCategoriesModel::init() { - QStringList types; - types << flatModel()->mimeTypes(); - types << "application/x-vnd.zanshin.category"; - return types; -} + TodoProxyModelBase::init(); -Qt::DropActions TodoCategoriesModel::supportedDropActions() const -{ - return flatModel()->supportedDropActions(); -} + if (!m_categoryRootNode) { + beginInsertRows(QModelIndex(), 1, 1); -QMimeData *TodoCategoriesModel::mimeData(const QModelIndexList &indexes) const -{ - QModelIndexList sourceIndexes; - QByteArray categoriesList; - foreach (const QModelIndex &proxyIndex, indexes) { - TodoCategoryTreeNode *node = nodeForIndex(proxyIndex); - QModelIndex index = indexForNode(node, TodoFlatModel::RowType); - if (data(index).toInt() == TodoFlatModel::StandardTodo) { - sourceIndexes << mapToSource(proxyIndex); - } else { - categoriesList.append(data(proxyIndex).toByteArray()); - } - } + TodoNode *node = new TodoNode; + node->setData(i18n("Contexts"), 0, Qt::DisplayRole); + node->setData(KIcon("document-multiple"), 0, Qt::DecorationRole); + node->setRowData(Zanshin::CategoryRoot, Zanshin::ItemTypeRole); - if (!sourceIndexes.isEmpty()) { - return flatModel()->mimeData(sourceIndexes); - } else { - QMimeData *mimeData = new QMimeData(); - mimeData->setData("application/x-vnd.zanshin.category", categoriesList); - return mimeData; - } -} + m_categoryRootNode = node; + m_manager->insertNode(m_categoryRootNode); -bool TodoCategoriesModel::dropMimeData(const QMimeData *data, Qt::DropAction action, - int /*row*/, int /*column*/, const QModelIndex &parent) -{ - if (action != Qt::MoveAction || (!KUrl::List::canDecode(data) && !data->hasFormat("application/x-vnd.zanshin.category"))) { - return false; + endInsertRows(); } - QString parentCategory = categoryForIndex(parent); - - if (KUrl::List::canDecode(data)) { - KUrl::List urls = KUrl::List::fromMimeData(data); - - foreach (const KUrl &url, urls) { - const Akonadi::Item item = Akonadi::Item::fromUrl(url); - if (item.isValid()) { - QModelIndex index = flatModel()->indexForItem(item, TodoFlatModel::Categories); - if (!flatModel()->setData(index, parentCategory)) { - return false; - } - } + foreach (const QString &category, CategoryManager::instance().categories()) { + if (!m_categoryMap.contains(category)) { + createCategoryNode(category); } - } else { - QByteArray categories = data->data("application/x-vnd.zanshin.category"); - QString category = categories.data(); - QModelIndex index = indexForCategory(category, TodoFlatModel::Categories); - setData(index, parentCategory); } - - return true; -} - -QVariant TodoCategoriesModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - return flatModel()->headerData(section, orientation, role); } -Qt::ItemFlags TodoCategoriesModel::flags(const QModelIndex &index) const +TodoNode *TodoCategoriesModel::createInbox() const { - TodoCategoryTreeNode *node = nodeForIndex(index); - if (node && node->id==-1) { - switch (index.column()) { - case TodoFlatModel::Summary: - case TodoFlatModel::Categories: - return Qt::ItemIsEnabled - | Qt::ItemIsSelectable - | Qt::ItemIsEditable - | Qt::ItemIsDragEnabled - | Qt::ItemIsDropEnabled; + TodoNode *node = new TodoNode; - default: - break; - } - } else if (node && node->id!=-1) - return QAbstractProxyModel::flags(index); + node->setData(i18n("No Context"), 0, Qt::DisplayRole); + node->setData(KIcon("mail-folder-inbox"), 0, Qt::DecorationRole); + node->setRowData(Zanshin::Inbox, Zanshin::ItemTypeRole); - return Qt::ItemIsEnabled|Qt::ItemIsSelectable; + return node; } -QModelIndex TodoCategoriesModel::mapToSource(const QModelIndex &proxyIndex) const +void TodoCategoriesModel::createCategoryNode(const QString &categoryPath) { - TodoCategoryTreeNode *node = nodeForIndex(proxyIndex); + //TODO: Order them along a tree + TodoNode* parentNode = m_categoryRootNode; + QString categoryName = categoryPath; + if (categoryPath.contains(CategoryManager::pathSeparator())) { + QString parentCategory = categoryPath.left(categoryPath.lastIndexOf(CategoryManager::pathSeparator())); + categoryName = categoryPath.split(CategoryManager::pathSeparator()).last(); - if (!node || node->id == -1) { - return QModelIndex(); + if (m_categoryMap.contains(parentCategory)) { + parentNode = m_categoryMap[parentCategory]; + } else { + CategoryManager::instance().addCategory(parentCategory); + Q_ASSERT(m_categoryMap.contains(parentCategory)); + parentNode = m_categoryMap[parentCategory]; + } } - return flatModel()->indexForItem(Akonadi::Item(node->id), - proxyIndex.column()); -} - -QList TodoCategoriesModel::mapFromSourceAll(const QModelIndex &sourceIndex) const -{ - Akonadi::Item item = flatModel()->itemForIndex(sourceIndex); - - Akonadi::Entity::Id id = item.id(); - - if (!m_itemMap.contains(id)) { - return QList(); - } + Q_ASSERT(parentNode); + int row = parentNode->children().size(); - QList nodes = m_itemMap[id]; - QList res; + beginInsertRows(m_manager->indexForNode(parentNode, 0), row, row); - foreach (TodoCategoryTreeNode *node, nodes) - res << indexForNode(node, sourceIndex.column()); + TodoNode *node = new TodoNode(parentNode); + node->setData(categoryName, 0, Qt::DisplayRole); + node->setData(categoryName, 0, Qt::EditRole); + node->setData(categoryPath, 0, Zanshin::CategoryPathRole); + node->setData(KIcon("view-pim-notes"), 0, Qt::DecorationRole); + node->setRowData(Zanshin::Category, Zanshin::ItemTypeRole); - return res; -} + m_categoryMap[categoryPath] = node; + m_manager->insertNode(node); -QModelIndex TodoCategoriesModel::mapFromSource(const QModelIndex &/*sourceIndex*/) const -{ - kError() << "Never call mapFromSource() on TodoCategoriesModel"; - return QModelIndex(); + endInsertRows(); } -void TodoCategoriesModel::setSourceModel(QAbstractItemModel *sourceModel) +void TodoCategoriesModel::removeCategoryNode(const QString &category) { - if (flatModel()) { - disconnect(flatModel()); + if (!m_categoryMap.contains(category)) { + return; } - Q_ASSERT(sourceModel == 0 || qobject_cast(sourceModel) != 0); - QAbstractProxyModel::setSourceModel(sourceModel); - m_collection = flatModel()->collection(); - - onSourceInsertRows(QModelIndex(), 0, flatModel()->rowCount() - 1); + TodoNode *node = m_categoryMap[category]; - connect(flatModel(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(onSourceDataChanged(const QModelIndex&, const QModelIndex&))); - connect(flatModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(onSourceInsertRows(const QModelIndex&, int, int))); - connect(flatModel(), SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), - this, SLOT(onSourceRemoveRows(const QModelIndex&, int, int))); - connect(flatModel(), SIGNAL(collectionChanged(const Akonadi::Collection&)), - this, SLOT(onSourceCollectionChanged(const Akonadi::Collection&))); -} - -void TodoCategoriesModel::onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end) -{ - for (int i = end; i >= begin; i--) { - QStringList categories = flatModel()->data( - flatModel()->index(i, TodoFlatModel::Categories, sourceIndex)).toStringList(); - - if (categories.isEmpty()) continue; - - foreach (const QString &category, categories) { - if (!m_categoryMap.contains(category)) { - TodoCategoryTreeNode *node = new TodoCategoryTreeNode(category); - loadCategory(node); + QList children = node->children(); + foreach (TodoNode* child, children) { + QModelIndex childIndex = m_manager->indexForNode(child, 0); + if (childIndex.data(Zanshin::ItemTypeRole).toInt() == Zanshin::Category) { + CategoryManager::instance().removeCategory(childIndex.data(Zanshin::CategoryPathRole).toString()); + } else { + QStringList categories = childIndex.data(Zanshin::CategoriesRole).toStringList(); + if (categories.empty()) { + child->setParent(m_inboxNode); + } else { + beginRemoveRows(childIndex.parent(), childIndex.row(), childIndex.row()); + m_manager->removeNode(child); + delete child; + endRemoveRows(); } - - TodoCategoryTreeNode *parent = m_categoryMap[category]; - QModelIndex proxyParentIndex = indexForNode(parent); - int row = parent->children.size(); - - beginInsertRows(proxyParentIndex, row, row); - - Akonadi::Item item = flatModel()->itemForIndex(flatModel()->index(i, 0)); - Akonadi::Entity::Id id = item.id(); - - TodoCategoryTreeNode *child = new TodoCategoryTreeNode(id, parent); - parent->children << child; - m_itemMap[id] << child; - - endInsertRows(); } } - serializeCategories(); + QModelIndex index = m_manager->indexForNode(node, 0); + beginRemoveRows(index.parent(), index.row(), index.row()); + m_manager->removeNode(node); + m_categoryMap.remove(category); + delete node; + endRemoveRows(); } -void TodoCategoriesModel::onSourceRemoveRows(const QModelIndex &/*sourceIndex*/, int begin, int end) +void TodoCategoriesModel::renameCategoryNode(const QString &oldCategoryPath, const QString &newCategoryPath) { - for (int i = begin; i <= end; ++i) { - QModelIndex sourceIndex = flatModel()->index(i, 0); - QModelIndexList proxyIndexes = mapFromSourceAll(sourceIndex); + TodoNode *node = m_categoryMap[oldCategoryPath]; + m_categoryMap[newCategoryPath] = node; + m_categoryMap.remove(oldCategoryPath); - foreach (const QModelIndex &proxyIndex, proxyIndexes) { - TodoCategoryTreeNode *node = nodeForIndex(proxyIndex); - TodoCategoryTreeNode *parentNode = nodeForIndex(proxyIndex.parent()); - - beginRemoveRows(proxyIndex.parent(), proxyIndex.row(), proxyIndex.row()); - m_itemMap.remove(itemForIndex(proxyIndex).id()); - parentNode->children.removeAll(node); - delete node; - endRemoveRows(); + QList children = node->children(); + foreach (TodoNode* child, children) { + QModelIndex childIndex = m_manager->indexForNode(child, 0); + if (childIndex.data(Zanshin::ItemTypeRole).toInt() == Zanshin::Category) { + QString childPath = childIndex.data(Zanshin::CategoryPathRole).toString(); + QString newChildPath = childPath; + newChildPath = newChildPath.replace(oldCategoryPath, newCategoryPath); + CategoryManager::instance().renameCategory(childPath, newChildPath); } } -} - -void TodoCategoriesModel::onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end) -{ - for (int row=begin.row(); row<=end.row(); ++row) { - QModelIndex sourceIndex = flatModel()->index(row, 0); - QModelIndexList proxyIndexes = mapFromSourceAll(sourceIndex); - Akonadi::Entity::Id id = flatModel()->itemForIndex(sourceIndex).id(); - - QSet oldCategories; - QHash indexMap; - foreach (const QModelIndex &proxyIndex, proxyIndexes) { - emit dataChanged(index(proxyIndex.row(), 0, proxyIndex.parent()), - index(proxyIndex.row(), TodoFlatModel::LastColumn, proxyIndex.parent())); - TodoCategoryTreeNode *node = nodeForIndex(proxyIndex.parent()); - if (node!=0 && node->id==-1) { - oldCategories << node->category; - indexMap[node->category] = proxyIndex; - } - } - - QModelIndex catIndex = flatModel()->index(row, TodoFlatModel::Categories); - QSet newCategories = QSet::fromList(flatModel()->data(catIndex).toStringList()); - - QSet interCategories = newCategories; - interCategories.intersect(oldCategories); - newCategories-= interCategories; - oldCategories-= interCategories; - - foreach (const QString &oldCategory, oldCategories) { - TodoCategoryTreeNode *parentNode = m_categoryMap[oldCategory]; - TodoCategoryTreeNode *node = nodeForIndex(indexMap[oldCategory]); - - int oldRow = parentNode->children.indexOf(node); - beginRemoveRows(indexForNode(parentNode), oldRow, oldRow); - parentNode->children.removeAll(node); - m_itemMap[id].removeAll(node); - delete node; - endRemoveRows(); - } - - foreach (const QString &newCategory, newCategories) { - if (!m_categoryMap.contains(newCategory)) { - TodoCategoryTreeNode *node = new TodoCategoryTreeNode(newCategory); - loadCategory(node); - } - - TodoCategoryTreeNode *parent = m_categoryMap[newCategory]; - QModelIndex proxyParentIndex = indexForNode(parent); - int row = parent->children.size(); - - beginInsertRows(proxyParentIndex, row, row); - - TodoCategoryTreeNode *child = new TodoCategoryTreeNode(id, parent); - parent->children << child; - m_itemMap[id] << child; - - endInsertRows(); - } - } + QString newCategory = newCategoryPath.split(CategoryManager::pathSeparator()).last(); + node->setData(newCategory, 0, Qt::DisplayRole); + node->setData(newCategory, 0, Qt::EditRole); + node->setData(newCategoryPath, 0, Zanshin::CategoryPathRole); - serializeCategories(); + QModelIndex index = m_manager->indexForNode(node, 0); + emit dataChanged(index, index); } -void TodoCategoriesModel::onSourceCollectionChanged(const Akonadi::Collection &collection) +void TodoCategoriesModel::moveCategoryNode(const QString &oldCategoryPath, const QString &newCategoryPath) { - m_itemMap.clear(); - m_categoryMap.clear(); - qDeleteAll(m_roots); - m_roots.clear(); - - reset(); + TodoNode *node = m_categoryMap[oldCategoryPath]; - m_collection = collection; - emit collectionChanged(collection); + QList children = node->children(); + foreach (TodoNode* child, children) { + QModelIndex childIndex = m_manager->indexForNode(child, 0); + if (childIndex.data(Zanshin::ItemTypeRole).toInt() == Zanshin::Category) { + QString childPath = childIndex.data(Zanshin::CategoryPathRole).toString(); + CategoryManager::instance().moveCategory(childPath, newCategoryPath, Zanshin::Category); + } else { + CategoryManager::instance().moveTodoToCategory(childIndex, newCategoryPath, Zanshin::Category); + } + } - deserializeCategories(); } -TodoCategoryTreeNode *TodoCategoriesModel::nodeForIndex(const QModelIndex &index) const +Qt::ItemFlags TodoCategoriesModel::flags(const QModelIndex &index) const { if (!index.isValid()) { - return 0; + return Qt::NoItemFlags; } - TodoCategoryTreeNode *parentNode = static_cast(index.internalPointer()); - if (parentNode != 0) { - return parentNode->children.at(index.row()); - } else { - return m_roots.at(index.row()); + Zanshin::ItemType type = (Zanshin::ItemType) index.data(Zanshin::ItemTypeRole).toInt(); + if (type == Zanshin::Inbox || type == Zanshin::CategoryRoot) { + return Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled; } + return TodoProxyModelBase::flags(index) | Qt::ItemIsDropEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled; } -QModelIndex TodoCategoriesModel::indexForNode(TodoCategoryTreeNode *node, int column) const +QMimeData *TodoCategoriesModel::mimeData(const QModelIndexList &indexes) const { - if (node == 0) { - return QModelIndex(); - } - - TodoCategoryTreeNode *parentNode = node->parent; - int row; - if (parentNode != 0) { - row = parentNode->children.indexOf(node); - } else { - row = m_roots.indexOf(node); + QModelIndexList sourceIndexes; + QStringList categoriesList; + foreach (const QModelIndex &proxyIndex, indexes) { + TodoNode *node = m_manager->nodeForIndex(proxyIndex); + QModelIndex index = m_manager->indexForNode(node, 0); + Zanshin::ItemType type = (Zanshin::ItemType) index.data(Zanshin::ItemTypeRole).toInt(); + if (type==Zanshin::StandardTodo) { + sourceIndexes << mapToSource(proxyIndex); + } else { + categoriesList << proxyIndex.data(Zanshin::CategoryPathRole).toString(); + } } - return createIndex(row, column, parentNode); -} - -Akonadi::Item TodoCategoriesModel::itemForIndex(const QModelIndex &index) const -{ - return flatModel()->itemForIndex(mapToSource(index)); -} - -QList TodoCategoriesModel::indexesForItem(const Akonadi::Item &item, int column) const -{ - return mapFromSourceAll(flatModel()->indexForItem(item, column)); -} - -QString TodoCategoriesModel::categoryForIndex(const QModelIndex &index) const -{ - TodoCategoryTreeNode *node = nodeForIndex(index); - if (node != 0 && node->id == -1) { - return node->category; + if (!sourceIndexes.isEmpty()) { + return sourceModel()->mimeData(sourceIndexes); } else { - return QString(); + QMimeData *mimeData = new QMimeData(); + QString sep = CategoryManager::pathSeparator(); + sep += CategoryManager::pathSeparator(); + QByteArray categories = categoriesList.join(sep).toUtf8(); + mimeData->setData("application/x-vnd.zanshin.category", categories); + return mimeData; } } -QModelIndex TodoCategoriesModel::indexForCategory(const QString &category, int column) const +QStringList TodoCategoriesModel::mimeTypes() const { - if (m_categoryMap.contains(category)) { - TodoCategoryTreeNode *node = m_categoryMap[category]; - return indexForNode(node, column); + QStringList types; + if (sourceModel()) { + types << sourceModel()->mimeTypes(); } - - return QModelIndex(); -} - -void TodoCategoriesModel::setCollection(const Akonadi::Collection &collection) -{ - flatModel()->setCollection(collection); -} - -Akonadi::Collection TodoCategoriesModel::collection() const -{ - return flatModel()->collection(); -} - -TodoFlatModel *TodoCategoriesModel::flatModel() const -{ - return qobject_cast(sourceModel()); + types << "application/x-vnd.zanshin.category"; + return types; } -bool TodoCategoriesModel::addCategory(const QString &name, const QModelIndex &parent) -{ - if (m_categoryMap.contains(name)) { - return false; - } - - TodoCategoryTreeNode *parentNode = nodeForIndex(parent); - TodoCategoryTreeNode *node = new TodoCategoryTreeNode(name); - - loadCategory(node, parentNode); - serializeCategories(); - - return true; -} -bool TodoCategoriesModel::removeCategory(const QString &name) +bool TodoCategoriesModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, + int /*row*/, int /*column*/, const QModelIndex &parent) { - if (!m_categoryMap.contains(name)) { + if (action != Qt::MoveAction || (!KUrl::List::canDecode(mimeData) && !mimeData->hasFormat("application/x-vnd.zanshin.category"))) { return false; } - QModelIndex index = indexForCategory(name); + QString parentCategory = parent.data(Zanshin::CategoryPathRole).toString(); + Zanshin::ItemType parentType = (Zanshin::ItemType)parent.data(Zanshin::ItemTypeRole).toInt(); - while (rowCount(index)>0) { - TodoCategoryTreeNode *child = nodeForIndex(index.child(0, 0)); - - if (child->id==-1) { - removeCategory(child->category); - } else { - beginRemoveRows(index, 0, 0); + if (KUrl::List::canDecode(mimeData)) { + KUrl::List urls = KUrl::List::fromMimeData(mimeData); + foreach (const KUrl &url, urls) { + const Akonadi::Item urlItem = Akonadi::Item::fromUrl(url); + if (urlItem.isValid()) { + Akonadi::Item item = TodoHelpers::fetchFullItem(urlItem); - QModelIndex childCatIndex = index.child(0, TodoFlatModel::Categories); - QStringList categories = data(childCatIndex).toStringList(); - categories.removeAll(name); - setData(childCatIndex, categories); - - m_itemMap[child->id].removeAll(child); - child->parent->children.removeAll(child); - child->parent = 0; - delete child; + if (!item.isValid()) { + return false; + } - endRemoveRows(); + if (item.hasPayload()) { + CategoryManager::instance().moveTodoToCategory(item, parentCategory, parentType); + } + } } - } - - QModelIndex parent = index.parent(); - int row = index.row(); - - beginRemoveRows(parent, row, row); - TodoCategoryTreeNode *node = m_categoryMap.take(name); - - if (node->parent!=0) { - node->parent->children.removeAll(node); - node->parent = 0; } else { - m_roots.removeAll(node); + if (parentType!=Zanshin::Category && parentType!=Zanshin::CategoryRoot) { + return false; + } + QByteArray categories = mimeData->data("application/x-vnd.zanshin.category"); + QString sep = CategoryManager::pathSeparator(); + sep += CategoryManager::pathSeparator(); + QStringList categoriesPath = QString::fromUtf8(categories.data()).split(sep); + foreach (QString categoryPath, categoriesPath) { + CategoryManager::instance().moveCategory(categoryPath, parentCategory, parentType); + } } - - delete node; - endRemoveRows(); - - serializeCategories(); return true; } -void TodoCategoriesModel::loadDefaultCategories() +Qt::DropActions TodoCategoriesModel::supportedDropActions() const { - TodoCategoryTreeNode *errands = new TodoCategoryTreeNode("Errands"); - TodoCategoryTreeNode *home = new TodoCategoryTreeNode("Home"); - TodoCategoryTreeNode *office = new TodoCategoryTreeNode("Office"); - TodoCategoryTreeNode *computer = new TodoCategoryTreeNode("Computer"); - TodoCategoryTreeNode *online = new TodoCategoryTreeNode("Online"); - TodoCategoryTreeNode *phone = new TodoCategoryTreeNode("Phone"); - - loadCategory(home); - loadCategory(office); - loadCategory(errands); - loadCategory(computer); - loadCategory(online, computer); - loadCategory(phone); - - serializeCategories(); -} - -void TodoCategoriesModel::loadCategory(TodoCategoryTreeNode *node, TodoCategoryTreeNode *parent) -{ - int row; - if (parent != 0) { - row = parent->children.size(); - } else { - row = m_roots.size(); + if (!sourceModel()) { + return Qt::IgnoreAction; } - beginInsertRows(indexForNode(parent), row, row); - m_categoryMap[node->category] = node; - if (parent != 0) { - node->parent = parent; - parent->children << node; - } else { - m_roots << node; - } - endInsertRows(); + return sourceModel()->supportedDropActions(); } -void TodoCategoriesModel::serializeCategories() +bool TodoCategoriesModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (!m_collection.isValid()) return; - - QStringList parentList; - - foreach (TodoCategoryTreeNode *node, m_categoryMap.values()) { - QString key = node->category; - QString value; - if (node->parent != 0) { - value = node->parent->category; - } - parentList << key << value; + if (role!=Qt::EditRole || !index.isValid()) { + return TodoProxyModelBase::setData(index, value, role); } - TodoCategoriesAttribute *attribute = m_collection.attribute(Akonadi::Collection::AddIfMissing); - attribute->setParentList(parentList); + Zanshin::ItemType type = (Zanshin::ItemType) index.data(Zanshin::ItemTypeRole).toInt(); + if (index.column()==0 && type==Zanshin::Category) { + QString oldCategoryPath = index.data(Zanshin::CategoryPathRole).toString(); + QString newCategoryName = value.toString(); + QString newCategoryPath = oldCategoryPath.left(oldCategoryPath.lastIndexOf(CategoryManager::pathSeparator())+1) + newCategoryName; + CategoryManager::instance().renameCategory(oldCategoryPath, newCategoryPath); + return true; + } - new Akonadi::CollectionModifyJob(m_collection, this); + return TodoProxyModelBase::setData(index, value, role); } -void TodoCategoriesModel::deserializeCategories() +void TodoCategoriesModel::resetInternalData() { - if (!m_collection.isValid()) return; - - if (!m_collection.hasAttribute()) { - loadDefaultCategories(); - return; - } - - TodoCategoriesAttribute *attribute = m_collection.attribute(); - QStringList parentList = attribute->parentList(); - - QHash categoryMap; - QList roots; - QHash childrenMap; - QHash parentMap; - - for (int i = 0; i < parentList.size(); i += 2) { - QString child = parentList.at(i); - QString parent; - if (i + 1 < parentList.size()) { - parent = parentList.at(i + 1); - } - - if (!categoryMap.contains(child)) { - TodoCategoryTreeNode *node = new TodoCategoryTreeNode(child); - categoryMap[child] = node; - } - - if (!categoryMap.contains(parent)) { - TodoCategoryTreeNode *node = new TodoCategoryTreeNode(parent); - categoryMap[parent] = node; - } - - if (parent.isEmpty()) { - roots << child; - } else { - parentMap[child] = parent; - childrenMap[parent] << child; - } + m_categoryRootNode = 0; + m_categoryMap.clear(); + foreach(QString category, CategoryManager::instance().categories()) { + CategoryManager::instance().removeCategory(category); } - QStringList catToLoad = roots; - while (!catToLoad.isEmpty()) { - QString category = catToLoad.takeFirst(); - catToLoad+=childrenMap[category]; - - TodoCategoryTreeNode *node = categoryMap[category]; - - QString parent = parentMap[category]; - if (parent.isEmpty()) { - loadCategory(node); - } else { - TodoCategoryTreeNode *parentNode = categoryMap[parent]; - loadCategory(node, parentNode); - } - } + TodoProxyModelBase::resetInternalData(); } diff -Nru zanshin-0.1+svn1006410/src/todocategoriesmodel.h zanshin-0.2.0/src/todocategoriesmodel.h --- zanshin-0.1+svn1006410/src/todocategoriesmodel.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/todocategoriesmodel.h 2011-11-25 15:00:52.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Zanshin Todo. - Copyright 2008 Kevin Ottens + Copyright 2008-2010 Kevin Ottens Copyright 2008, 2009 Mario Bensi This program is free software; you can redistribute it and/or @@ -27,18 +27,10 @@ #include -#include -#include +#include "todonode.h" +#include "todoproxymodelbase.h" -namespace Akonadi -{ - class Item; -} - -class TodoFlatModel; -class TodoCategoryTreeNode; - -class TodoCategoriesModel : public QAbstractProxyModel +class TodoCategoriesModel : public TodoProxyModelBase { Q_OBJECT @@ -46,67 +38,30 @@ TodoCategoriesModel(QObject *parent = 0); virtual ~TodoCategoriesModel(); - virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - virtual QModelIndex parent(const QModelIndex &index) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; - - virtual QStringList mimeTypes() const; - Qt::DropActions supportedDropActions() const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual QMimeData *mimeData(const QModelIndexList &indexes) const; virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + virtual QStringList mimeTypes() const; + virtual Qt::DropActions supportedDropActions() const; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - - virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const; - QList mapFromSourceAll(const QModelIndex &sourceIndex) const; - - virtual void setSourceModel(QAbstractItemModel *sourceModel); - - Akonadi::Item itemForIndex (const QModelIndex &index) const; - QList indexesForItem (const Akonadi::Item &item, int column = 0) const; - - QString categoryForIndex(const QModelIndex &index) const; - QModelIndex indexForCategory(const QString &category, int column = 0) const; - - void setCollection(const Akonadi::Collection &collection); - Akonadi::Collection collection() const; - - bool addCategory(const QString &name, const QModelIndex &parent = QModelIndex()); - bool removeCategory(const QString &name); - -signals: - void collectionChanged(const Akonadi::Collection &collection); private slots: void onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end); void onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end); void onSourceRemoveRows(const QModelIndex &sourceIndex, int begin, int end); - void onSourceCollectionChanged(const Akonadi::Collection &collection); + void createCategoryNode(const QString &categoryPath); + void removeCategoryNode(const QString &categoryPath); + void renameCategoryNode(const QString &oldCategoryPath, const QString &newCategoryPath); + void moveCategoryNode(const QString &oldCategoryPath, const QString &newCategoryPath); private: - TodoFlatModel *flatModel() const; - - virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; - - void loadDefaultCategories(); - void loadCategory(TodoCategoryTreeNode *node, TodoCategoryTreeNode *parent = 0); - - void serializeCategories(); - void deserializeCategories(); - - Akonadi::Collection m_collection; - - TodoCategoryTreeNode *nodeForIndex(const QModelIndex &index) const; - QModelIndex indexForNode(TodoCategoryTreeNode *node, int column = 0) const; + virtual void init(); + virtual TodoNode *createInbox() const; + virtual void resetInternalData(); - QList m_roots; - QHash m_categoryMap; - QHash > m_itemMap; + TodoNode *m_categoryRootNode; + QMap m_categoryMap; }; #endif diff -Nru zanshin-0.1+svn1006410/src/todoflatmodel.cpp zanshin-0.2.0/src/todoflatmodel.cpp --- zanshin-0.1+svn1006410/src/todoflatmodel.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/todoflatmodel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,588 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - Copyright 2008, 2009 Mario Bensi - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#include "todoflatmodel.h" - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include - -typedef boost::shared_ptr IncidencePtr; - -class TodoFlatModelImpl : public Akonadi::ItemModel -{ -public: - TodoFlatModelImpl(QObject *parent = 0); - ~TodoFlatModelImpl(); - - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; -}; - -TodoFlatModelImpl::TodoFlatModelImpl(QObject *parent) - : Akonadi::ItemModel(parent) -{ -} - -TodoFlatModelImpl::~TodoFlatModelImpl() -{ -} - -int TodoFlatModelImpl::columnCount(const QModelIndex &parent) const -{ - if (parent.isValid()) { - return 0; - } else { - return TodoFlatModel::LastColumn + 1; - } -} - -int TodoFlatModelImpl::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) { - return 0; - } else { - return ItemModel::rowCount(parent); - } -} - -QVariant TodoFlatModelImpl::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || index.row() >= rowCount()) { - return QVariant(); - } - - const Akonadi::Item item = itemForIndex(index); - - if (!item.hasPayload()) { - return QVariant(); - } - - const IncidencePtr incidence = item.payload(); - - switch( role ) { - case Qt::DisplayRole: - case Qt::EditRole: - switch(index.column()) { - case TodoFlatModel::RemoteId: - return incidence->uid(); - case TodoFlatModel::Summary: - return incidence->summary(); - case TodoFlatModel::Categories: - if (role == Qt::EditRole) { - return incidence->categories().join(", "); - } else { - return incidence->categories(); - } - case TodoFlatModel::ParentRemoteId: - return incidence->relatedToUid(); - case TodoFlatModel::DueDate: { - KCal::Todo *todo = dynamic_cast(incidence.get()); - if (todo) { - return todo->dtDue().toString(); - } else { - return QString(); - } - } - } - break; - } - - return QVariant(); -} - -QVariant TodoFlatModelImpl::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { - switch(section) { - case TodoFlatModel::RemoteId: - return i18n("Remote Id"); - case TodoFlatModel::Summary: - return i18n("Summary"); - case TodoFlatModel::Categories: - return i18n("Categories"); - case TodoFlatModel::ParentSummary: - return i18n("Parent Summary"); - case TodoFlatModel::ParentRemoteId: - return i18n("Parent Id"); - case TodoFlatModel::DueDate: - return i18n("Due Date"); - case TodoFlatModel::RowType: - return i18n("Row Type"); - } - } - - return QVariant(); -} - - - -TodoFlatModel::TodoFlatModel(QObject *parent) - : QSortFilterProxyModel(parent) -{ - Akonadi::ItemModel *itemModel = new TodoFlatModelImpl(this); - itemModel->fetchScope().fetchFullPayload(); - setSourceModel(itemModel); -} - -TodoFlatModel::~TodoFlatModel() -{ - -} - -Akonadi::Item TodoFlatModel::itemForIndex(const QModelIndex &index) const -{ - return itemModel()->itemForIndex(mapToSource(index)); -} - -QModelIndex TodoFlatModel::indexForItem(const Akonadi::Item &item, const int column) const -{ - return mapFromSource(itemModel()->indexForItem(item, column)); -} - -void TodoFlatModel::setCollection(const Akonadi::Collection &collection) -{ - m_reverseRemoteIdMap.clear(); - m_remoteIdMap.clear(); - m_parentMap.clear(); - m_childrenMap.clear(); - itemModel()->setCollection(collection); -} - -Akonadi::Collection TodoFlatModel::collection() const -{ - return itemModel()->collection(); -} - -bool TodoFlatModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - Akonadi::ItemModel *source = itemModel(); - Q_ASSERT(source); - - const Akonadi::Item item = source->itemForIndex(source->index(sourceRow, 0, sourceParent)); - - return item.mimeType()=="application/x-vnd.akonadi.calendar.todo"; -} - -void TodoFlatModel::setSourceModel(QAbstractItemModel *sourceModel) -{ - Q_ASSERT(sourceModel == 0 || qobject_cast(sourceModel) != 0); - QSortFilterProxyModel::setSourceModel(sourceModel); - - // HACK PART 1: Disconnect the slot from QSortFilterProxyModel which - // is supposed to process rowsInserted() signals. It is done because - // we want to make sure our own slots is called first. - disconnect(itemModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(_q_sourceRowsInserted(const QModelIndex&, int, int))); - - connect(itemModel(), SIGNAL(collectionChanged(const Akonadi::Collection&)), - this, SIGNAL(collectionChanged(const Akonadi::Collection&))); - connect(itemModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(onSourceInsertRows(const QModelIndex&, int, int))); - connect(itemModel(), SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), - this, SLOT(onSourceRemoveRows(const QModelIndex&, int, int))); - connect(itemModel(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(onSourceDataChanged(const QModelIndex&, const QModelIndex&))); -} - -Akonadi::ItemModel *TodoFlatModel::itemModel() const -{ - return qobject_cast(sourceModel()); -} - -QStringList TodoFlatModel::mimeTypes() const -{ - QStringList list; - list << "text/uri-list"; - return list; -} - -Qt::DropActions TodoFlatModel::supportedDropActions() const -{ - return Qt::MoveAction; -} - -Qt::ItemFlags TodoFlatModel::flags(const QModelIndex &index) const -{ - if (!index.isValid() || index.row() >= rowCount()) { - return Qt::NoItemFlags; - } - - Qt::ItemFlags f = Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled; - - switch(index.column()) { - case TodoFlatModel::Summary: { - f |= Qt::ItemIsEditable; - - const Akonadi::Item item = itemForIndex(index); - - if (!item.hasPayload()) { - break; - } - - const IncidencePtr incidence = item.payload(); - ItemType type = todoType(incidence->uid()); - - if (type==StandardTodo) { - f |= Qt::ItemIsUserCheckable; - } - break; - } - case TodoFlatModel::Categories: - case TodoFlatModel::ParentRemoteId: - case TodoFlatModel::DueDate: - f |= Qt::ItemIsEditable; - break; - default: - break; - } - - return f; -} - -QVariant TodoFlatModel::data(const QModelIndex &index, int role) const -{ - if (role==Qt::DecorationRole && index.column()==Summary) { - const Akonadi::Item item = itemForIndex(index); - - if (!item.hasPayload()) { - return QVariant(); - } - - const IncidencePtr incidence = item.payload(); - ItemType type = todoType(incidence->uid()); - if (type==ProjectTodo) { - return KIcon("view-pim-tasks"); - } else if (type==FolderTodo) { - return KIcon("folder"); - } else { - return QVariant(); - } - } else if (role==Qt::CheckStateRole && index.column()==Summary) { - const Akonadi::Item item = itemForIndex(index); - - if (!item.hasPayload()) { - return QVariant(); - } - - const IncidencePtr incidence = item.payload(); - ItemType type = todoType(incidence->uid()); - - if (type==StandardTodo) { - KCal::Todo *todo = dynamic_cast(incidence.get()); - return (todo->isCompleted() ? Qt::Checked : Qt::Unchecked); - } - } else if (index.column()==ParentSummary) { - if (role!=Qt::DisplayRole) { - return QString(); - } - - const Akonadi::Item item = itemForIndex(index); - - if (!item.hasPayload()) { - return QVariant(); - } - - const IncidencePtr incidence = item.payload(); - - Akonadi::Item parentItem(m_reverseRemoteIdMap[incidence->relatedToUid()]); - QModelIndex parent = indexForItem(parentItem, TodoFlatModel::Summary); - return data(parent, role); - } else if (index.column()==RowType) { - if (role!=Qt::DisplayRole) { - return QString(); - } - - const Akonadi::Item item = itemForIndex(index); - - if (!item.hasPayload()) { - return QVariant(); - } - - const IncidencePtr incidence = item.payload(); - return todoType(incidence->uid()); - } - - return QSortFilterProxyModel::data(index, role); -} - -static bool modifyItemHelper(const Akonadi::Item &item) -{ - Akonadi::ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob(item); - modifyJob->disableRevisionCheck(); //FIXME: I don't get why this one is needed... - bool result = modifyJob->exec(); - - if (!result) { - kWarning() << "Couldn't apply modification to item" << item.id() - << "we got error:" << modifyJob->error() << modifyJob->errorString(); - } - - return result; -} - -bool TodoFlatModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid() || index.row() >= rowCount()) { - return false; - } - - const Akonadi::Item item = itemForIndex(index); - - if (!item.hasPayload()) { - return false; - } - - const IncidencePtr incidence = item.payload(); - KCal::Todo *todo = dynamic_cast(incidence.get()); - - if (!todo) { - return false; - } - - switch( role ) { - case Qt::EditRole: - switch(index.column()) { - case TodoFlatModel::Summary: - todo->setSummary(value.toString()); - return modifyItemHelper(item); - - case TodoFlatModel::Categories: { - QStringList categories = value.toString().split(QRegExp("\\s*,\\s*")); - todo->setCategories(categories); - return modifyItemHelper(item); - } - case TodoFlatModel::ParentRemoteId: { - // test existence and cycle - TodoFlatModel::ItemType itemType = todoType(incidence->uid()); - TodoFlatModel::ItemType parentType = StandardTodo; - if (!value.toString().isEmpty()) { - parentType = todoType(value.toString()); - } - if (!m_reverseRemoteIdMap.contains(value.toString()) - || isAncestorOf(incidence->uid(), value.toString()) - || (!value.toString().isEmpty() && itemType == StandardTodo && parentType == StandardTodo) - || (itemType == ProjectTodo && parentType == ProjectTodo) - || (itemType == ProjectTodo && parentType == StandardTodo) - || (itemType == FolderTodo && parentType == ProjectTodo) - || (itemType == FolderTodo && parentType == StandardTodo)) { - return false; - } - todo->setRelatedToUid(value.toString()); - - if (itemType == StandardTodo - && parentType == FolderTodo - && !todo->comments().contains("X-Zanshin-Project")) { - todo->addComment("X-Zanshin-Project"); - } - if (itemType == StandardTodo - && value.toString().isEmpty()) { - todo->addComment("X-Zanshin-Project"); - } - - return modifyItemHelper(item); - } - case TodoFlatModel::DueDate: { - QDate date = QDate::fromString(value.toString(), Qt::ISODate); - if (date.isValid()) { - todo->setDtDue(KDateTime(date)); - todo->setHasDueDate(true); - todo->setAllDay(true); - return modifyItemHelper(item); - } else { - if (value.toString().isEmpty()) { - todo->setDtDue(KDateTime()); - todo->setHasDueDate(false); - todo->setAllDay(false); - return modifyItemHelper(item); - } - return false; - } - } - default: - break; - } - break; - - case Qt::CheckStateRole: - switch(index.column()) { - case TodoFlatModel::Summary: { - bool completed = false; - if (value.toInt()==Qt::Checked) { - completed = true; - } - if (completed!=todo->isCompleted()) { - todo->setCompleted(completed); - return modifyItemHelper(item); - } - } - default: - break; - } - break; - } - - return false; -} - -void TodoFlatModel::onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end) -{ - for (int i = begin; i <= end; i++) { - // Retrieve the item from the source model - Akonadi::Item item = itemModel()->itemForIndex(itemModel()->index(i, 0)); - QString remoteId = itemModel()->data(itemModel()->index(i, TodoFlatModel::RemoteId)).toString(); - m_remoteIdMap[item.id()] = remoteId; - m_reverseRemoteIdMap[remoteId] = item.id(); - } - - // HACK PART 2: Invoke the slot from QSortFilterProxyModel by hand. - // This way we make sure that our internal maps got updated before - // we tell the outside world that a new row appeared (and this way - // we're sure the row type is immediately right, not only once we - // reenter the event loop. - QMetaObject::invokeMethod(this, "_q_sourceRowsInserted", - Q_ARG(QModelIndex, sourceIndex), - Q_ARG(int, begin), - Q_ARG(int, end)); - - for (int i = begin; i <= end; i++) { - Akonadi::Item item = itemModel()->itemForIndex(itemModel()->index(i, 0)); - - QString remoteId = m_remoteIdMap[item.id()]; - QString parentRemoteId = itemModel()->data(itemModel()->index(i, TodoFlatModel::ParentRemoteId)).toString(); - - bool shouldEmit = !m_childrenMap.contains(parentRemoteId); - - m_parentMap[remoteId] = parentRemoteId; - m_childrenMap[parentRemoteId] << remoteId; - - if (shouldEmit) { - Akonadi::Item item(m_reverseRemoteIdMap[parentRemoteId]); - QModelIndex start = indexForItem(item, 0); - QModelIndex end = indexForItem(item, LastColumn); - emit dataChanged(start, end); - } - } - -} - -void TodoFlatModel::onSourceRemoveRows(const QModelIndex&/*sourceIndex*/, int begin, int end) -{ - for (int i = begin; i <= end; ++i) { - QModelIndex sourceIndex = itemModel()->index(i, 0); - Akonadi::Item item = itemModel()->itemForIndex(sourceIndex); - - QString remoteId = m_remoteIdMap.take(item.id()); - QString parentRemoteId = itemModel()->data(itemModel()->index(i, TodoFlatModel::ParentRemoteId)).toString(); - - m_parentMap.remove(remoteId); - m_childrenMap[parentRemoteId].removeAll(remoteId); - m_reverseRemoteIdMap.remove(remoteId); - } -} - -void TodoFlatModel::onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end) -{ - for (int row = begin.row(); row <= end.row(); ++row) { - QModelIndex remoteIdIndex = itemModel()->index(row, TodoFlatModel::RemoteId); - QString remoteId = itemModel()->data(remoteIdIndex).toString(); - - QModelIndex parentRemoteIdIndex = itemModel()->index(row, TodoFlatModel::ParentRemoteId); - QString newParentRemoteId = itemModel()->data(parentRemoteIdIndex).toString(); - - QString oldParentRemoteId = m_parentMap[remoteId]; - - if (newParentRemoteId!=oldParentRemoteId) { - m_parentMap[remoteId] = newParentRemoteId; - m_childrenMap[newParentRemoteId] << remoteId; - m_childrenMap[oldParentRemoteId].removeAll(remoteId); - } - } -} - -bool TodoFlatModel::isAncestorOf(const QString &ancestor, const QString &child) -{ - QString parent = m_parentMap[child]; - if (parent.isEmpty()) - return false; - if (parent == ancestor) - return true; - return isAncestorOf(ancestor, parent); -} - -TodoFlatModel::ItemType TodoFlatModel::todoType(const QString &remoteId, bool examinateSiblings) const -{ - const QStringList children = m_childrenMap[remoteId]; - if (children.size()>0) { - foreach (const QString &child, children) { - if (todoType(child) == ProjectTodo) { - return FolderTodo; - } - } - - return ProjectTodo; - } - - if (m_reverseRemoteIdMap.contains(remoteId)) { - const Akonadi::Item it(m_reverseRemoteIdMap[remoteId]); - QModelIndex index = indexForItem(it, 0); - if (index != QModelIndex()) { - const Akonadi::Item item = itemForIndex(index); - const IncidencePtr incidence = item.payload(); - KCal::Todo *todo = dynamic_cast(incidence.get()); - QStringList comments = todo->comments(); - if (comments.contains("X-Zanshin-Project")) - return ProjectTodo; - else if (comments.contains("X-Zanshin-Folder")) - return FolderTodo; - } - } - - //FIXME: That's definitely expensive, optimize this one - if (examinateSiblings && !m_parentMap[remoteId].isEmpty()) { - const QStringList siblings = m_childrenMap[m_parentMap[remoteId]]; - foreach (const QString &sibling, siblings) { - if (sibling == remoteId) continue; - - if (todoType(sibling, false) != StandardTodo) { - return ProjectTodo; - } - } - } - - return StandardTodo; -} - diff -Nru zanshin-0.1+svn1006410/src/todoflatmodel.h zanshin-0.2.0/src/todoflatmodel.h --- zanshin-0.1+svn1006410/src/todoflatmodel.h 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/todoflatmodel.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,108 +0,0 @@ -/* This file is part of Zanshin Todo. - - Copyright 2008 Kevin Ottens - Copyright 2008, 2009 Mario Bensi - - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. -*/ - -#ifndef ZANSHIN_TODOFLATMODEL_H -#define ZANSHIN_TODOFLATMODEL_H - -#include -#include - -namespace Akonadi -{ - class Collection; - class Item; - class ItemModel; -} - -class TodoFlatModel : public QSortFilterProxyModel -{ - Q_OBJECT - Q_ENUMS(ItemType Column) - Q_FLAGS(ItemTypes) - -public: - enum ItemType - { - StandardTodo = 0, - ProjectTodo, - FolderTodo, - Category - }; - - Q_DECLARE_FLAGS(ItemTypes, ItemType) - - enum Column { - Summary = 0, - Categories, - ParentSummary, - DueDate, - RowType, - RemoteId, - ParentRemoteId, - LastColumn = ParentRemoteId - }; - - TodoFlatModel(QObject *parent = 0); - virtual ~TodoFlatModel(); - - virtual QStringList mimeTypes() const; - Qt::DropActions supportedDropActions() const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - - Akonadi::Item itemForIndex (const QModelIndex &index) const; - QModelIndex indexForItem (const Akonadi::Item &item, const int column) const; - - void setCollection(const Akonadi::Collection &collection); - Akonadi::Collection collection() const; - -signals: - void collectionChanged(const Akonadi::Collection &collection); - -private slots: - void onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end); - void onSourceRemoveRows(const QModelIndex &sourceIndex, int begin, int end); - void onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end); - -protected: - virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; - -private: - void setSourceModel(QAbstractItemModel *sourceModel); - Akonadi::ItemModel *itemModel() const; - - ItemType todoType(const QString &remoteId, bool examinateSiblings = true) const; - bool isAncestorOf(const QString &ancestor, const QString &child); - - QHash m_parentMap; - QHash m_childrenMap; - QHash m_remoteIdMap; - QHash m_reverseRemoteIdMap; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(TodoFlatModel::ItemTypes) - -#endif - diff -Nru zanshin-0.1+svn1006410/src/todohelpers.cpp zanshin-0.2.0/src/todohelpers.cpp --- zanshin-0.1+svn1006410/src/todohelpers.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todohelpers.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,352 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "todohelpers.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "categorymanager.h" +#include "globaldefs.h" + +Akonadi::Item TodoHelpers::fetchFullItem(const Akonadi::Item &item) +{ + Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item); + Akonadi::ItemFetchScope scope; + scope.setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); + scope.fetchFullPayload(); + job->setFetchScope(scope); + + if ( !job->exec() ) { + return Akonadi::Item(); + } + + Q_ASSERT(job->items().size()==1); + + return job->items().first(); +} + +void TodoHelpers::addTodo(const QString &summary, const QString &parentUid, const QString &category, const Akonadi::Collection &collection) +{ + if (!(collection.rights() & Akonadi::Collection::CanCreateItem)) { + return; + } + + KCalCore::Todo::Ptr todo(new KCalCore::Todo); + todo->setSummary(summary); + if (!parentUid.isEmpty()) { + todo->setRelatedTo(parentUid); + } + if (!category.isEmpty()) { + todo->setCategories(category); + } + + Akonadi::Item item; + item.setMimeType("application/x-vnd.akonadi.calendar.todo"); + item.setPayload(todo); + + new Akonadi::ItemCreateJob(item, collection); +} + +void TodoHelpers::addProject(const QString &summary, const Akonadi::Collection &collection) +{ + if (!(collection.rights() & Akonadi::Collection::CanCreateItem)) { + return; + } + + KCalCore::Todo::Ptr todo(new KCalCore::Todo()); + todo->setSummary(summary); + todo->addComment("X-Zanshin-Project"); + + Akonadi::Item item; + item.setMimeType("application/x-vnd.akonadi.calendar.todo"); + item.setPayload(todo); + + Akonadi::ItemCreateJob *job = new Akonadi::ItemCreateJob(item, collection); + job->start(); +} + +void TodoHelpers::addProject(const QString &summary, const QModelIndex &parentItem) +{ + // We use ParentCollectionRole instead of Akonadi::Item::parentCollection() because the + // information about the rights is not valid on retrieved items. + Akonadi::Collection collection = parentItem.data(Akonadi::EntityTreeModel::ParentCollectionRole).value(); + if (!(collection.rights() & Akonadi::Collection::CanCreateItem)) { + return; + } + + Akonadi::Item parentProject = parentItem.data(Akonadi::EntityTreeModel::ItemRole).value(); + + KCalCore::Todo::Ptr todo(new KCalCore::Todo()); + todo->setSummary(summary); + todo->addComment("X-Zanshin-Project"); + + KCalCore::Todo::Ptr parentTodo = parentProject.payload(); + todo->setRelatedTo(parentTodo->uid()); + + Akonadi::Item item; + item.setMimeType("application/x-vnd.akonadi.calendar.todo"); + item.setPayload(todo); + + Akonadi::ItemCreateJob *job = new Akonadi::ItemCreateJob(item, collection); + job->start(); +} + +void removeCurrentTodo(const QModelIndex &project, QModelIndexList children, Akonadi::TransactionSequence *sequence) +{ + // We use ParentCollectionRole instead of Akonadi::Item::parentCollection() because the + // information about the rights is not valid on retrieved items. + Akonadi::Collection collection = project.data(Akonadi::EntityTreeModel::ParentCollectionRole).value(); + if (!(collection.rights() & Akonadi::Collection::CanDeleteItem)) { + return; + } + + foreach (QModelIndex child, children) { + QModelIndexList childList = child.data(Zanshin::ChildIndexesRole).value(); + removeCurrentTodo(child, childList, sequence); + } + + Akonadi::Item item = project.data(Akonadi::EntityTreeModel::ItemRole).value(); + new Akonadi::ItemDeleteJob(item, sequence); +} + +bool TodoHelpers::removeProject(QWidget *parent, const QModelIndex &project) +{ + QModelIndexList projects; + projects << project; + return removeProjects(parent, projects); +} + +bool TodoHelpers::removeProjects(QWidget *parent, const QModelIndexList &projects) +{ + if (projects.isEmpty()) { + return false; + } + + bool canRemove = true; + QString summary; + if (projects.size() > 1) { + QStringList projectList; + foreach (QModelIndex project, projects) { + projectList << project.data().toString(); + } + summary = projectList.join(", "); + } else { + QModelIndexList children = projects[0].data(Zanshin::ChildIndexesRole).value(); + if (!children.isEmpty()) { + summary = projects[0].data().toString(); + } + } + + if (!summary.isEmpty()) { + QString title; + QString text; + + if (projects.size() > 1) { + title = i18n("Delete Projects"); + text = i18n("Do you really want to delete the projects '%1', with all its actions?", summary); + } else { + title = i18n("Delete Project"); + text = i18n("Do you really want to delete the project '%1', with all its actions?", summary); + } + + + int button = KMessageBox::questionYesNo(parent, text, title); + canRemove = (button==KMessageBox::Yes); + } + + if (!canRemove) return false; + + Akonadi::TransactionSequence *sequence = new Akonadi::TransactionSequence(); + foreach (QModelIndex project, projects) { + + // We use ParentCollectionRole instead of Akonadi::Item::parentCollection() because the + // information about the rights is not valid on retrieved items. + Akonadi::Collection collection = project.data(Akonadi::EntityTreeModel::ParentCollectionRole).value(); + if (!(collection.rights() & Akonadi::Collection::CanDeleteItem)) { + continue; + } + + QModelIndexList children = project.data(Zanshin::ChildIndexesRole).value(); + removeCurrentTodo(project, children, sequence); + } + sequence->start(); + return true; +} + +static Akonadi::Item::List collectChildItemsRecHelper(const Akonadi::Item &item, const Akonadi::Item::List &items) +{ + Akonadi::Item::List result; + + Akonadi::Item::List itemsToProcess = items; + Q_ASSERT(item.isValid() && item.hasPayload()); + KCalCore::Todo::Ptr todo = item.payload(); + + for (Akonadi::Item::List::Iterator it = itemsToProcess.begin(); + it!=itemsToProcess.end(); ++it) { + Akonadi::Item currentItem = *it; + + if (!currentItem.hasPayload() + || currentItem == item) { + it = itemsToProcess.erase(it); + --it; + + } else { + KCalCore::Todo::Ptr currentTodo = currentItem.payload(); + + if (currentTodo->relatedTo()==todo->uid()) { + it = itemsToProcess.erase(it); + --it; + + result << currentItem; + result+= collectChildItemsRecHelper(currentItem, itemsToProcess); + } + } + } + + return result; +} + +static Akonadi::Item::List collectChildItems(const Akonadi::Item &item) +{ + Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item.parentCollection()); + Akonadi::ItemFetchScope scope; + scope.setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); + scope.fetchFullPayload(); + job->setFetchScope(scope); + + if (!job->exec()) { + return Akonadi::Item::List(); + } + + return collectChildItemsRecHelper(item, job->items()); +} + +bool TodoHelpers::moveTodoToProject(const QModelIndex &index, const QString &parentUid, const Zanshin::ItemType parentType, const Akonadi::Collection &parentCollection) +{ + Zanshin::ItemType itemType = (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); + const Akonadi::Item item = index.data(Akonadi::EntityTreeModel::ItemRole).value(); + KCalCore::Todo::Ptr todo = item.payload(); + + if (!todo) { + return false; + } + + if ((itemType == Zanshin::StandardTodo && parentType == Zanshin::StandardTodo) + || (itemType == Zanshin::ProjectTodo && parentType == Zanshin::StandardTodo) + || (itemType == Zanshin::Collection && parentType == Zanshin::ProjectTodo) + || (itemType == Zanshin::Collection && parentType == Zanshin::StandardTodo)) { + return false; + } + + return moveTodoToProject(item, parentUid, parentType, parentCollection); +} + +bool TodoHelpers::moveTodoToProject(const Akonadi::Item &item, const QString &parentUid, const Zanshin::ItemType parentType, const Akonadi::Collection &parentCollection) +{ + if (!(parentCollection.rights() & Akonadi::Collection::CanCreateItem)) { + return false; + } + + KCalCore::Todo::Ptr todo = item.payload(); + + if (!todo) { + return false; + } + + if ((!parentUid.isEmpty() && todo->relatedTo()==parentUid) + || parentType == Zanshin::StandardTodo) { + return false; + } + + + if (parentType == Zanshin::Inbox || parentType == Zanshin::Collection) { + todo->setRelatedTo(""); + } else { + todo->setRelatedTo(parentUid); + } + + const int itemCollectonId = item.parentCollection().id(); + const int parentCollectionId = parentCollection.id(); + const bool shouldMoveItems = (parentType != Zanshin::Inbox) + && (itemCollectonId != parentCollectionId); + + Akonadi::Item::List childItems; + if (shouldMoveItems) { + // Collect first, as the inner fetch job has to be + // done before any transaction is created + childItems = collectChildItems(item); + } + + // Make the whole modification and move happen in a single transaction + Akonadi::TransactionSequence *transaction = new Akonadi::TransactionSequence(); + + new Akonadi::ItemModifyJob(item, transaction); + + if (shouldMoveItems) { + new Akonadi::ItemMoveJob(item, parentCollection, transaction); + + if (!childItems.isEmpty()) { + new Akonadi::ItemMoveJob(childItems, parentCollection, transaction); + } + } + return true; +} + +bool TodoHelpers::promoteTodo(const QModelIndex &index) +{ + // We use ParentCollectionRole instead of Akonadi::Item::parentCollection() because the + // information about the rights is not valid on retrieved items. + Akonadi::Collection collection = index.data(Akonadi::EntityTreeModel::ParentCollectionRole).value(); + if (!(collection.rights() & Akonadi::Collection::CanChangeItem)) { + return false; + } + + Zanshin::ItemType itemType = (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); + if (itemType!=Zanshin::StandardTodo) { + return false; + } + + const Akonadi::Item item = index.data(Akonadi::EntityTreeModel::ItemRole).value(); + KCalCore::Todo::Ptr todo = item.payload(); + + if (!todo) { + return false; + } + + todo->addComment("X-Zanshin-Project"); + new Akonadi::ItemModifyJob(item); + return true; +} diff -Nru zanshin-0.1+svn1006410/src/todohelpers.h zanshin-0.2.0/src/todohelpers.h --- zanshin-0.1+svn1006410/src/todohelpers.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todohelpers.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,51 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TODOHELPERS_H +#define ZANSHIN_TODOHELPERS_H + +#include +#include +#include + +#include "globaldefs.h" + +namespace TodoHelpers +{ + Akonadi::Item fetchFullItem(const Akonadi::Item &item); + + void addTodo(const QString &summary, const QString &parentUid, const QString &category, + const Akonadi::Collection &collection); + + void addProject(const QString &summary, const Akonadi::Collection &collection); + void addProject(const QString &summary, const QModelIndex &parentItem); + bool removeProject(QWidget *parent, const QModelIndex &project); + bool removeProjects(QWidget *parent, const QModelIndexList &projects); + bool moveTodoToProject(const QModelIndex &todo, const QString &parentUid, const Zanshin::ItemType parentType, const Akonadi::Collection &parentCollection); + bool moveTodoToProject(const Akonadi::Item &todo, const QString &parentUid, const Zanshin::ItemType parentType, const Akonadi::Collection &parentCollection); + bool promoteTodo(const QModelIndex &index); +} + +#endif + diff -Nru zanshin-0.1+svn1006410/src/todometadatamodel.cpp zanshin-0.2.0/src/todometadatamodel.cpp --- zanshin-0.1+svn1006410/src/todometadatamodel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todometadatamodel.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,403 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "todometadatamodel.h" + +#include + +#include +#include +#include + +#include "globaldefs.h" + +TodoMetadataModel::TodoMetadataModel(QObject *parent) + : KIdentityProxyModel(parent) +{ + connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(onSourceInsertRows(QModelIndex,int,int))); + connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(onSourceRemoveRows(QModelIndex,int,int))); + connect(this, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(onSourceDataChanged(QModelIndex,QModelIndex))); + connect(this, SIGNAL(modelReset()), + this, SLOT(onModelReset())); + + onSourceInsertRows(QModelIndex(), 0, rowCount()-1); +} + +TodoMetadataModel::~TodoMetadataModel() +{ +} + +Qt::ItemFlags TodoMetadataModel::flags(const QModelIndex &index) const +{ + if (!sourceModel()) { + return Qt::NoItemFlags; + } + + Qt::ItemFlags flags = Qt::NoItemFlags; + + if (index.isValid()) { + flags = sourceModel()->flags(mapToSource(index)); + Zanshin::ItemType type = (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); + if (index.column()==0) { + Akonadi::Item item = index.data(Akonadi::EntityTreeModel::ItemRole).value(); + if (item.isValid() && type==Zanshin::StandardTodo) { + flags|= Qt::ItemIsUserCheckable; + } + } else if (index.column()==4) { + flags&= ~Qt::ItemIsEditable; + } + + if (type==Zanshin::Collection) { + flags&= ~Qt::ItemIsEditable; + flags&= ~Qt::ItemIsDragEnabled; + } + } + + return flags; +} + +QVariant TodoMetadataModel::data(const QModelIndex &index, int role) const +{ + if (!sourceModel()) { + return QVariant(); + } + + Akonadi::Item item = sourceModel()->data(mapToSource(index), Akonadi::EntityTreeModel::ItemRole).value(); + + if (!item.isValid() || !item.hasPayload()) { + if (role==Zanshin::ItemTypeRole) { + Akonadi::Collection collection = sourceModel()->data(mapToSource(index), Akonadi::EntityTreeModel::CollectionRole).value(); + if (collection.isValid()) { + return Zanshin::Collection; + } + } + return KIdentityProxyModel::data(index, role); + } + + switch (role) { + case Qt::CheckStateRole: + if (index.column()==0 && itemTypeFromItem(item)==Zanshin::StandardTodo) { + return todoFromItem(item)->isCompleted() ? Qt::Checked : Qt::Unchecked; + } else { + return QVariant(); + } + case Qt::DecorationRole: + if (index.column()==0 && itemTypeFromItem(item)==Zanshin::ProjectTodo) { + return KIcon("view-pim-tasks"); + } else { + return KIdentityProxyModel::data(index, role); + } + case Zanshin::UidRole: + return uidFromItem(item); + case Zanshin::ParentUidRole: + return relatedUidFromItem(item); + case Zanshin::AncestorsUidRole: + return ancestorsUidFromItem(item); + case Zanshin::ItemTypeRole: + return itemTypeFromItem(item); + case Zanshin::CategoriesRole: + return categoriesFromItem(item); + case Zanshin::AncestorsCategoriesRole: + return ancestorsCategoriesFromItem(item); + case Zanshin::DataTypeRole: + switch (index.column()) { + case 1 : + return Zanshin::ProjectType; + case 2 : + return Zanshin::CategoryType; + default: + return Zanshin::StandardType; + } + case Zanshin::ChildUidsRole: + return childUidsFromItem(item); + case Zanshin::ChildIndexesRole: + return QVariant::fromValue(childIndexesFromIndex(index)); + default: + return KIdentityProxyModel::data(index, role); + } + return KIdentityProxyModel::data(index, role); +} + +void TodoMetadataModel::onSourceInsertRows(const QModelIndex &parent, int begin, int end) +{ + for (int i = begin; i <= end; i++) { + QModelIndex child = index(i, 0, parent); + onSourceInsertRows(child, 0, rowCount(child)-1); + + KCalCore::Todo::Ptr todo = todoFromIndex(child); + + if (!todo) { + continue; + } + + QString uid = todo->uid(); + + m_indexMap[uid] = child; + + QString relatedUid = todo->relatedTo(); + + if (relatedUid.isEmpty()) { + continue; + } + + m_parentMap[uid] = relatedUid; + m_childrenMap[relatedUid] << uid; + + + if (!m_indexMap.contains(relatedUid)) { + continue; + } + + // Emit dataChanged to notify that the todo is a project todo + QModelIndex parentIndex = m_indexMap[relatedUid]; + if (parentIndex.data(Zanshin::ItemTypeRole).toInt()==Zanshin::ProjectTodo + && m_childrenMap[relatedUid].size() == 1) { + emit dataChanged(parentIndex, parentIndex); + } + + } +} + +void TodoMetadataModel::onSourceRemoveRows(const QModelIndex &parent, int begin, int end) +{ + for (int i = begin; i <= end; ++i) { + QModelIndex child = index(i, 0, parent); + + cleanupDataForSourceIndex(child); + } +} + +void TodoMetadataModel::onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end) +{ + for (int row = begin.row(); row <= end.row(); ++row) { + for (int column = begin.column(); column <= end.column(); ++column) { + QModelIndex child = index(row, column, begin.parent()); + KCalCore::Todo::Ptr todo = todoFromIndex(child); + + if (!todo) { + continue; + } + + QString uid = todo->uid(); + + QString newRelatedUid = todo->relatedTo(); + QString oldRelatedUid; + if (m_parentMap.contains(uid)) { + oldRelatedUid = m_parentMap[uid]; + } + + + if (newRelatedUid!=oldRelatedUid) { + if (newRelatedUid.isEmpty()) { + m_parentMap.remove(uid); + } else { + m_parentMap[uid] = newRelatedUid; + m_childrenMap[newRelatedUid] << uid; + } + m_childrenMap[oldRelatedUid].removeAll(uid); + } + + if (child.data(Zanshin::ItemTypeRole).toInt()==Zanshin::ProjectTodo) { + foreach (const QModelIndex &index, childIndexesFromIndex(child)) { + emit dataChanged(index, index); + } + } + } + } +} + +void TodoMetadataModel::cleanupDataForSourceIndex(const QModelIndex &index) +{ + for (int row=0; rowindex(row, 0, index); + cleanupDataForSourceIndex(child); + } + + + KCalCore::Todo::Ptr todo = todoFromIndex(index); + + if (!todo) { + return; + } + + QString uid = todo->uid(); + + QString relatedUid = todo->relatedTo(); + + QModelIndex parentIndex = m_indexMap[relatedUid]; + Zanshin::ItemType parentType = (Zanshin::ItemType)parentIndex.data(Zanshin::ItemTypeRole).toInt(); + + m_parentMap.remove(uid); + m_childrenMap[relatedUid].removeAll(uid); + m_indexMap.remove(uid); + + if (parentType==Zanshin::ProjectTodo + && parentIndex.data(Zanshin::ItemTypeRole).toInt()!=parentType) { + emit dataChanged(parentIndex, parentIndex); + } +} + +KCalCore::Todo::Ptr TodoMetadataModel::todoFromIndex(const QModelIndex &index) const +{ + Akonadi::Item item = sourceModel()->data(mapToSource(index), Akonadi::EntityTreeModel::ItemRole).value(); + return todoFromItem(item); +} + +KCalCore::Todo::Ptr TodoMetadataModel::todoFromItem(const Akonadi::Item &item) const +{ + if (!item.isValid() || !item.hasPayload()) { + return KCalCore::Todo::Ptr(); + } else { + return item.payload(); + } +} + +Zanshin::ItemType TodoMetadataModel::itemTypeFromItem(const Akonadi::Item &item) const +{ + KCalCore::Todo::Ptr todo = todoFromItem(item); + + QStringList comments = todo->comments(); + const int childCount = m_childrenMap.contains(todo->uid()) ? m_childrenMap[todo->uid()].count() : 0; + if (comments.contains("X-Zanshin-Project") + || childCount>0) { + return Zanshin::ProjectTodo; + } else { + return Zanshin::StandardTodo; + } +} + +QString TodoMetadataModel::uidFromItem(const Akonadi::Item &item) const +{ + KCalCore::Todo::Ptr todo = todoFromItem(item); + if (todo) { + return todo->uid(); + } else { + return QString(); + } +} + +QString TodoMetadataModel::relatedUidFromItem(const Akonadi::Item &item) const +{ + KCalCore::Todo::Ptr todo = todoFromItem(item); + if (todo) { + return todo->relatedTo(); + } else { + return QString(); + } +} + +QStringList TodoMetadataModel::ancestorsUidFromItem(const Akonadi::Item &item) const +{ + QStringList result; + KCalCore::Todo::Ptr todo = todoFromItem(item); + + if (todo) { + QString id = todo->uid(); + while (m_parentMap.contains(id)) { + const QString parentId = m_parentMap[id]; + Q_ASSERT(!parentId.isEmpty()); + result << parentId; + id = parentId; + } + } + + return result; +} + +QStringList TodoMetadataModel::ancestorsCategoriesFromItem(const Akonadi::Item &item) const +{ + QStringList ancestors = ancestorsUidFromItem(item); + QStringList categories; + foreach (QString uid, ancestors) { + if (!m_indexMap.contains(uid)) { + continue; + } + const QModelIndex &index = m_indexMap[uid]; + KCalCore::Todo::Ptr todo = todoFromIndex(index); + if (todo) { + categories << todo->categories(); + } + } + categories.removeDuplicates(); + return categories; +} + +QStringList TodoMetadataModel::categoriesFromItem(const Akonadi::Item &item) const +{ + QStringList categories = ancestorsCategoriesFromItem(item); + KCalCore::Todo::Ptr todo = todoFromItem(item); + if (todo) { + categories << todo->categories(); + } + categories.removeDuplicates(); + return categories; +} + +QStringList TodoMetadataModel::childUidsFromItem(const Akonadi::Item &item) const +{ + KCalCore::Todo::Ptr todo = todoFromItem(item); + if (todo) { + return m_childrenMap[todo->uid()]; + } else { + return QStringList(); + } +} + +QModelIndexList TodoMetadataModel::childIndexesFromIndex(const QModelIndex &idx) const +{ + QModelIndexList indexes; + KCalCore::Todo::Ptr todo = todoFromIndex(idx); + if (!todo) { + return indexes; + } + QString parent = todo->uid(); + for (int i = 0; i < rowCount(idx.parent()); ++i) { + QModelIndex child = index(i, idx.column(), idx.parent()); + todo = todoFromIndex(child); + if (!todo) { + continue; + } + if (m_parentMap[todo->uid()] == parent) { + indexes << child; + } + } + return indexes; +} + +void TodoMetadataModel::setSourceModel(QAbstractItemModel *model) +{ + KIdentityProxyModel::setSourceModel(model); + + onSourceInsertRows(QModelIndex(), 0, rowCount() - 1); +} + +void TodoMetadataModel::onModelReset() +{ + m_parentMap.clear(); + m_childrenMap.clear(); + m_indexMap.clear(); +} + diff -Nru zanshin-0.1+svn1006410/src/todometadatamodel.h zanshin-0.2.0/src/todometadatamodel.h --- zanshin-0.1+svn1006410/src/todometadatamodel.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todometadatamodel.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,79 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TODOMETADATAMODEL_H +#define ZANSHIN_TODOMETADATAMODEL_H + +#include "kidentityproxymodel.h" +#include + +#include + +#include "globaldefs.h" + +namespace Akonadi +{ + class Item; +} + +class TodoMetadataModel : public KIdentityProxyModel +{ + Q_OBJECT + +public: + TodoMetadataModel(QObject *parent = 0); + virtual ~TodoMetadataModel(); + + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + virtual void setSourceModel(QAbstractItemModel *model); + +private slots: + void onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end); + void onSourceRemoveRows(const QModelIndex &sourceIndex, int begin, int end); + void onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end); + void onModelReset(); + +private: + KCalCore::Todo::Ptr todoFromIndex(const QModelIndex &index) const; + KCalCore::Todo::Ptr todoFromItem(const Akonadi::Item &item) const; + + void cleanupDataForSourceIndex(const QModelIndex &index); + Zanshin::ItemType itemTypeFromItem(const Akonadi::Item &item) const; + QString uidFromItem(const Akonadi::Item &item) const; + QString relatedUidFromItem(const Akonadi::Item &item) const; + QStringList ancestorsUidFromItem(const Akonadi::Item &item) const; + QStringList ancestorsCategoriesFromItem(const Akonadi::Item &item) const; + QStringList categoriesFromItem(const Akonadi::Item &item) const; + QStringList childUidsFromItem(const Akonadi::Item &item) const; + QModelIndexList childIndexesFromIndex(const QModelIndex &index) const; + + QHash m_parentMap; + QHash m_childrenMap; + QHash m_indexMap; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/todomodel.cpp zanshin-0.2.0/src/todomodel.cpp --- zanshin-0.1+svn1006410/src/todomodel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todomodel.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,261 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "todomodel.h" + +#include + +#include +#include +#include + +#include + +TodoModel::TodoModel(Akonadi::ChangeRecorder *monitor, QObject *parent) + : Akonadi::EntityTreeModel(monitor, parent) +{ + connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(onSourceInsertRows(QModelIndex,int,int))); + connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(onSourceRemoveRows(QModelIndex,int,int))); + connect(this, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(onSourceDataChanged(QModelIndex,QModelIndex))); + + onSourceInsertRows(QModelIndex(), 0, rowCount()-1); +} + +TodoModel::~TodoModel() +{ +} + +Qt::ItemFlags TodoModel::flags(const QModelIndex &index) const +{ + return Akonadi::EntityTreeModel::flags(index) | Qt::ItemIsEditable; +} + +int TodoModel::entityColumnCount(HeaderGroup headerGroup) const +{ + if (headerGroup == CollectionTreeHeaders) { + return 1; + } else { + return 5; + } +} + +QVariant TodoModel::entityHeaderData(int section, Qt::Orientation orientation, int role, HeaderGroup headerGroup) const +{ + if (orientation == Qt::Vertical) { + return EntityTreeModel::entityHeaderData(section, orientation, role, headerGroup); + } + + if (headerGroup == CollectionTreeHeaders) { + return i18n("Summary"); + } else if (role == Qt::DisplayRole) { + switch (section) { + case 0: + return i18n("Summary"); + case 1: + return i18n("Project"); + case 2: + return i18n("Contexts"); + case 3: + return i18n("Due Date"); + case 4: + return i18n("Collection"); + } + } + + return EntityTreeModel::entityHeaderData(section, orientation, role, headerGroup); +} + +QVariant TodoModel::entityData(const Akonadi::Item &item, int column, int role) const +{ + switch (role) { + case Qt::DisplayRole: + switch (column) { + case 0: + return todoFromItem(item)->summary(); + case 1: + return m_summaryMap[todoFromItem(item)->relatedTo()]; + case 2: + return todoFromItem(item)->categories().join(", "); + case 3: + return todoFromItem(item)->dtDue().date().toString(); + case 4: + return modelIndexForCollection(this, item.parentCollection()).data(); + } + case Qt::EditRole: + switch (column) { + case 0: + return todoFromItem(item)->summary(); + case 1: + return m_summaryMap[todoFromItem(item)->relatedTo()]; + case 2: + return todoFromItem(item)->categories(); + case 3: + return todoFromItem(item)->dtDue().date(); + case 4: + return modelIndexForCollection(this, item.parentCollection()).data(); + } + case Qt::DecorationRole: + if (column==4) { + return modelIndexForCollection(this, item.parentCollection()).data(Qt::DecorationRole); + } else { + return EntityTreeModel::entityData(item, column, role); + } + default: + return EntityTreeModel::entityData(item, column, role); + } +} + +bool TodoModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if ((role!=Qt::EditRole && role!=Qt::CheckStateRole) || !index.isValid()) { + return EntityTreeModel::setData(index, value, role); + } + + // We use ParentCollectionRole instead of Akonadi::Item::parentCollection() because the + // information about the rights is not valid on retrieved items. + Akonadi::Collection collection = data(index, Akonadi::EntityTreeModel::ParentCollectionRole).value(); + if (!(collection.rights() & Akonadi::Collection::CanChangeItem)) { + return false; + } + + Akonadi::Item item = data(index, ItemRole).value(); + + if (!item.isValid() || !item.hasPayload()) { + return EntityTreeModel::setData(index, value, role); + } + + bool shouldModifyItem = false; + + KCalCore::Todo::Ptr todo = todoFromItem(item); + + switch (index.column()) { + case 0: + if (role==Qt::EditRole) { + todo->setSummary(value.toString()); + shouldModifyItem = true; + } else if (role==Qt::CheckStateRole) { + todo->setCompleted(value.toInt()==Qt::Checked); + shouldModifyItem = true; + } + break; + case 1: + todo->setRelatedTo(value.toString()); + shouldModifyItem = true; + break; + case 2: + todoFromItem(item)->setCategories(value.toStringList()); + shouldModifyItem = true; + break; + case 3: + todo->setDtDue(KDateTime(value.toDate())); + todo->setHasDueDate(true); + todo->setAllDay(true); + shouldModifyItem = true; + break; + case 4: + break; + } + + if (shouldModifyItem) { + Akonadi::ItemModifyJob *itemModifyJob = new Akonadi::ItemModifyJob( item, this ); + connect(itemModifyJob, SIGNAL(result(KJob*)), + this, SLOT(updateJobDone(KJob*))); + } + + return false; +} + +void TodoModel::onSourceInsertRows(const QModelIndex &parent, int begin, int end) +{ + for (int i = begin; i <= end; i++) { + QModelIndex child = index(i, 0, parent); + onSourceInsertRows(child, 0, rowCount(child)-1); + + KCalCore::Todo::Ptr todo = todoFromIndex(child); + + if (!todo) { + continue; + } + + QString uid = todo->uid(); + + m_summaryMap[uid] = todo->summary(); + } +} + +void TodoModel::onSourceRemoveRows(const QModelIndex &parent, int begin, int end) +{ + for (int i = begin; i <= end; ++i) { + QModelIndex child = index(i, 0, parent); + KCalCore::Todo::Ptr todo = todoFromIndex(child); + + if (!todo) { + continue; + } + + QString uid = todo->uid(); + + m_summaryMap.remove(uid); + } +} + +void TodoModel::onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end) +{ + for (int row = begin.row(); row <= end.row(); ++row) { + for (int column = begin.column(); column <= end.column(); ++column) { + KCalCore::Todo::Ptr todo = todoFromIndex( index(row, column, begin.parent()) ); + + if (!todo) { + continue; + } + + QString uid = todo->uid(); + + m_summaryMap[uid] = todo->summary(); + } + } +} + +KCalCore::Todo::Ptr TodoModel::todoFromIndex(const QModelIndex &index) const +{ + Akonadi::Item item = data(index, ItemRole).value(); + return todoFromItem(item); +} + +KCalCore::Todo::Ptr TodoModel::todoFromItem(const Akonadi::Item &item) const +{ + if (!item.isValid() || !item.hasPayload()) { + return KCalCore::Todo::Ptr(); + } else { + return item.payload(); + } +} + +Qt::DropActions TodoModel::supportedDropActions() const +{ + return Qt::MoveAction; +} diff -Nru zanshin-0.1+svn1006410/src/todomodel.h zanshin-0.2.0/src/todomodel.h --- zanshin-0.1+svn1006410/src/todomodel.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todomodel.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,67 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TODOMODEL_H +#define ZANSHIN_TODOMODEL_H + +#include + +#include +#include +#include "globaldefs.h" + +class TodoModel : public Akonadi::EntityTreeModel +{ + Q_OBJECT + Q_ENUMS(Zanshin::ItemType Roles) + +public: + TodoModel(Akonadi::ChangeRecorder *monitor, QObject *parent = 0); + virtual ~TodoModel(); + + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + virtual int entityColumnCount(HeaderGroup headerGroup) const; + virtual QVariant entityHeaderData(int section, Qt::Orientation orientation, int role, HeaderGroup headerGroup) const; + virtual QVariant entityData(const Akonadi::Item &item, int column, int role) const; + using EntityTreeModel::entityData; + + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + virtual Qt::DropActions supportedDropActions() const; + +private slots: + void onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end); + void onSourceRemoveRows(const QModelIndex &sourceIndex, int begin, int end); + void onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end); + +private: + KCalCore::Todo::Ptr todoFromIndex(const QModelIndex &index) const; + KCalCore::Todo::Ptr todoFromItem(const Akonadi::Item &item) const; + + QHash m_summaryMap; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/todonode.cpp zanshin-0.2.0/src/todonode.cpp --- zanshin-0.1+svn1006410/src/todonode.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todonode.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,142 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "todonode.h" + +#include + +TodoNode::TodoNode(const QModelIndex &rowSourceIndex, TodoNode *parent) + : m_parent(parent), m_rowSourceIndex(rowSourceIndex) +{ + init(); +} + +TodoNode::TodoNode(TodoNode *parent) + : m_parent(parent) +{ + init(); +} + +TodoNode::~TodoNode() +{ + if (m_parent) { + m_parent->m_children.removeAll(this); + } + + QList children = m_children; + qDeleteAll(children); +} + +void TodoNode::init() +{ + if (m_parent) { + m_parent->m_children << this; + } + + // Setting default flags + if (!m_rowSourceIndex.isValid()) { + m_flags = Qt::ItemIsEnabled|Qt::ItemIsSelectable; + } +} + +TodoNode *TodoNode::parent() const +{ + return m_parent; +} + +void TodoNode::setParent(TodoNode *parent) +{ + if (m_parent) { + m_parent->m_children.removeAll(this); + } + + m_parent = parent; + + if (m_parent) { + m_parent->m_children << this; + } +} + +QList TodoNode::children() const +{ + return m_children; +} + +QModelIndex TodoNode::rowSourceIndex() const +{ + return m_rowSourceIndex; +} + +QVariant TodoNode::data(int column, int role) const +{ + if (m_rowSourceIndex.isValid()) { + return m_rowSourceIndex.sibling(m_rowSourceIndex.row(), column).data(role); + } else { + QPair key(-1, role); + if (!m_data.contains(key)) { // Row wide info has priority + key.first = column; + } + return m_data[key]; + } +} + +void TodoNode::setData(const QVariant &value, int column, int role) +{ + if (m_rowSourceIndex.isValid()) { + QAbstractItemModel *model = const_cast(m_rowSourceIndex.model()); + model->setData(m_rowSourceIndex.sibling(m_rowSourceIndex.row(), column), value, role); + } else { + m_data[QPair(column, role)] = value; + } +} + +void TodoNode::setRowData(const QVariant &value, int role) +{ + if (m_rowSourceIndex.isValid()) { + QAbstractItemModel *model = const_cast(m_rowSourceIndex.model()); + model->setData(m_rowSourceIndex, value, role); + } else { + m_data[QPair(-1, role)] = value; + } +} + +Qt::ItemFlags TodoNode::flags(int column) const +{ + if (m_rowSourceIndex.isValid()) { + QAbstractItemModel *model = const_cast(m_rowSourceIndex.model()); + return model->flags(m_rowSourceIndex.sibling(m_rowSourceIndex.row(), column)); + } else { + return m_flags; + } +} + +void TodoNode::setFlags(Qt::ItemFlags flags) +{ + if (m_rowSourceIndex.isValid()) { + kWarning() << "Trying to set flags on a non-virtual node."; + return; + } + + m_flags = flags; +} diff -Nru zanshin-0.1+svn1006410/src/todonode.h zanshin-0.2.0/src/todonode.h --- zanshin-0.1+svn1006410/src/todonode.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todonode.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,65 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TODONODE_H +#define ZANSHIN_TODONODE_H + +#include +#include +#include + +class TodoNode +{ +public: + explicit TodoNode(const QModelIndex &rowSourceIndex, TodoNode *parent = 0); + explicit TodoNode(TodoNode *parent = 0); + ~TodoNode(); + + TodoNode *parent() const; + void setParent(TodoNode *parent); + + QList children() const; + + QModelIndex rowSourceIndex() const; + + QVariant data(int column, int role) const; + void setData(const QVariant &value, int column, int role); + void setRowData(const QVariant &value, int role); + + Qt::ItemFlags flags(int column) const; + void setFlags(Qt::ItemFlags flags); + +private: + void init(); + + TodoNode *m_parent; + QList m_children; + + QPersistentModelIndex m_rowSourceIndex; + QHash< QPair, QVariant> m_data; + Qt::ItemFlags m_flags; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/todonodemanager.cpp zanshin-0.2.0/src/todonodemanager.cpp --- zanshin-0.1+svn1006410/src/todonodemanager.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todonodemanager.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,139 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "todonodemanager.h" + +#include "todonode.h" +#include "todoproxymodelbase.h" + + +TodoNodeManager::TodoNodeManager(TodoProxyModelBase *model, bool multiMapping) + : m_model(model), m_multiMapping(multiMapping) +{ + +} + +QModelIndex TodoNodeManager::index(int row, int column, TodoNode *parent) const +{ + if (row < 0 || column < 0 + || row >= m_model->rowCount(indexForNode(parent, 0)) + || column >= m_model->columnCount(indexForNode(parent, 0))) { + return QModelIndex(); + } + + return m_model->createIndex(row, column, parent); +} + +QModelIndex TodoNodeManager::index(int row, int column, const QModelIndex &parent) const +{ + return index(row, column, nodeForIndex(parent)); +} + +QModelIndex TodoNodeManager::indexForNode(TodoNode *node, int column) const +{ + if (!node) { + return QModelIndex(); + } else { + TodoNode *parent = node->parent(); + int row = 0; + if (parent) { + row = parent->children().indexOf(node); + } else { + row = m_roots.indexOf(node); + } + + return index(row, column, parent); + } +} + +TodoNode *TodoNodeManager::nodeForIndex(const QModelIndex &index) const +{ + if (!index.isValid()) { + return 0; + } + + TodoNode *parentNode = static_cast(index.internalPointer()); + if (parentNode != 0) { + return parentNode->children().at(index.row()); + } else { + return m_roots.at(index.row()); + } +} + +TodoNode *TodoNodeManager::nodeForSourceIndex(const QModelIndex &sourceIndex) const +{ + Q_ASSERT(!m_multiMapping); + + QModelIndex rowSourceIndex = sourceIndex.sibling(sourceIndex.row(), 0); + if (rowSourceIndex.isValid() && m_nodes.contains(rowSourceIndex)) { + return m_nodes.value(rowSourceIndex); + } else { + return 0; + } +} + +QList TodoNodeManager::nodesForSourceIndex(const QModelIndex &sourceIndex) const +{ + Q_ASSERT(m_multiMapping); + + QModelIndex rowSourceIndex = sourceIndex.sibling(sourceIndex.row(), 0); + if (rowSourceIndex.isValid() && m_nodes.contains(rowSourceIndex)) { + return m_nodes.values(sourceIndex); + } else { + return QList(); + } +} + +void TodoNodeManager::insertNode(TodoNode *node) +{ + if (node->rowSourceIndex().isValid()) { + Q_ASSERT( (!m_multiMapping && m_nodes.count(node->rowSourceIndex())==0) || m_multiMapping ); + m_nodes.insert(node->rowSourceIndex(), node); + } + + if (node->parent()==0) { + m_roots << node; + } +} + +void TodoNodeManager::removeNode(TodoNode *node) +{ + if (node->rowSourceIndex().isValid()) { + m_nodes.remove(node->rowSourceIndex(), node); + } + + if (node->parent()==0) { + m_roots.removeAll(node); + } +} + +QList TodoNodeManager::roots() const +{ + return m_roots; +} + +bool TodoNodeManager::isMultiMapping() const +{ + return m_multiMapping; +} diff -Nru zanshin-0.1+svn1006410/src/todonodemanager.h zanshin-0.2.0/src/todonodemanager.h --- zanshin-0.1+svn1006410/src/todonodemanager.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todonodemanager.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,62 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TODONODEMANAGER_H +#define ZANSHIN_TODONODEMANAGER_H + +#include + +class TodoNode; +class TodoProxyModelBase; + +class TodoNodeManager +{ +public: + explicit TodoNodeManager(TodoProxyModelBase *model, bool multiMapping = false); + + QModelIndex index(int row, int column, TodoNode *parent) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + + QModelIndex indexForNode(TodoNode *node, int column) const; + TodoNode *nodeForIndex(const QModelIndex &index) const; + + TodoNode *nodeForSourceIndex(const QModelIndex &sourceIndex) const; + QList nodesForSourceIndex(const QModelIndex &sourceIndex) const; + + void insertNode(TodoNode *node); + void removeNode(TodoNode *node); + + QList roots() const; + + bool isMultiMapping() const; + +protected: + TodoProxyModelBase *m_model; + bool m_multiMapping; + QList m_roots; + QMultiHash m_nodes; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/todoproxymodelbase.cpp zanshin-0.2.0/src/todoproxymodelbase.cpp --- zanshin-0.1+svn1006410/src/todoproxymodelbase.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todoproxymodelbase.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,285 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "todoproxymodelbase.h" + +#include + +#include +#include +#include + +#include "todonode.h" +#include "todonodemanager.h" + +TodoProxyModelBase::TodoProxyModelBase(MappingType type, QObject *parent) + : QAbstractProxyModel(parent), + m_manager(new TodoNodeManager(this, (type==MultiMapping ? true : false))), + m_inboxNode(0), m_mappingType(type) +{ +} + +TodoProxyModelBase::~TodoProxyModelBase() +{ + delete m_manager; +} + +void TodoProxyModelBase::init() +{ + if (!m_inboxNode) { + beginInsertRows(QModelIndex(), 0, 0); + m_inboxNode = createInbox(); + m_manager->insertNode(m_inboxNode); + endInsertRows(); + } +} + +QModelIndex TodoProxyModelBase::index(int row, int column, const QModelIndex &parent) const +{ + return m_manager->index(row, column, parent); +} + +QModelIndex TodoProxyModelBase::parent(const QModelIndex &index) const +{ + if (!index.isValid()) { + return QModelIndex(); + } + + TodoNode *parent = m_manager->nodeForIndex(index)->parent(); + + if (parent == 0) { + return QModelIndex(); + } else { + return m_manager->indexForNode(parent, 0); + } +} + +int TodoProxyModelBase::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return m_manager->roots().size(); + } else if (parent.column() == 0) { // Only one set of children per row + TodoNode *node = m_manager->nodeForIndex(parent); + return node->children().size(); + } + + return 0; +} + +int TodoProxyModelBase::columnCount(const QModelIndex &parent) const +{ + if (!sourceModel()) { + return 1; + } + + if (parent.isValid()) { + TodoNode *node = m_manager->nodeForIndex(parent); + if (node && node->children().size() > 0) { + return sourceModel()->columnCount(); + } else { + return 0; + } + } else { + return sourceModel()->columnCount(mapToSource(parent)); + } +} + +bool TodoProxyModelBase::hasChildren(const QModelIndex &parent) const +{ + // Since Qt 4.8, QAbstractProxyModel just forwards hasChildren to the source model + // so that's not based on the current model rowCount() and columnCount(), because + // of that it's easy to end up with inconsistent replies between rowCount() and + // hasChildren() for instance (resulting in empty views or crashes in proxies). + // + // So, here we just use the hasChildren() implementation inherited from QAbstractItemModel + // since it is based on rowCount() and columnCount(). + + return QAbstractItemModel::hasChildren(parent); +} + +QVariant TodoProxyModelBase::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Vertical) { + return QAbstractProxyModel::headerData(section, orientation, role); + } else { + if (!sourceModel()) { + return QVariant(); + } + + return sourceModel()->headerData(section, orientation, role); + } +} + +QVariant TodoProxyModelBase::data(const QModelIndex &index, int role) const +{ + TodoNode *node = m_manager->nodeForIndex(index); + + if (node == 0) { + return QVariant(); + } + + return node->data(index.column(), role); +} + +Qt::ItemFlags TodoProxyModelBase::flags(const QModelIndex &index) const +{ + TodoNode *node = m_manager->nodeForIndex(index); + + if (node == 0) { + return Qt::NoItemFlags; + } + + return node->flags(index.column()); +} + +QModelIndex TodoProxyModelBase::mapToSource(const QModelIndex &proxyIndex) const +{ + TodoNode *node = m_manager->nodeForIndex(proxyIndex); + + if (!node) { + return QModelIndex(); + } else { + QModelIndex rowSourceIndex = node->rowSourceIndex(); + return rowSourceIndex.sibling(rowSourceIndex.row(), proxyIndex.column()); + } +} + +QList TodoProxyModelBase::mapFromSourceAll(const QModelIndex &sourceIndex) const +{ + if (m_mappingType==SimpleMapping) { + kError() << "Never call mapFromSourceAll() for a SimpleMapping model"; + return QList(); + } + + QList nodes = m_manager->nodesForSourceIndex(sourceIndex); + QList indexes; + + foreach (TodoNode *node, nodes) { + indexes << m_manager->indexForNode(node, sourceIndex.column()); + } + + return indexes; +} + +QModelIndex TodoProxyModelBase::mapFromSource(const QModelIndex &sourceIndex) const +{ + if (m_mappingType==MultiMapping) { + kError() << "Never call mapFromSource() for a MultiMapping model"; + return QModelIndex(); + } + + TodoNode *node = m_manager->nodeForSourceIndex(sourceIndex); + return m_manager->indexForNode(node, sourceIndex.column()); +} + +void TodoProxyModelBase::setSourceModel(QAbstractItemModel *model) +{ + init(); + + if (sourceModel()) { + connect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(onSourceDataChanged(QModelIndex,QModelIndex))); + connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(onSourceInsertRows(QModelIndex,int,int))); + connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(onSourceRemoveRows(QModelIndex,int,int))); + connect(sourceModel(), SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(onRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + connect(sourceModel(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(onRowsMoved(QModelIndex,int,int,QModelIndex,int))); + connect(sourceModel(), SIGNAL(modelReset()), + this, SLOT(onModelReset())); + } + + if (model) { + connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(onSourceDataChanged(QModelIndex,QModelIndex))); + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(onSourceInsertRows(QModelIndex,int,int))); + connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(onSourceRemoveRows(QModelIndex,int,int))); + connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(onRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); + connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(onRowsMoved(QModelIndex,int,int,QModelIndex,int))); + connect(model, SIGNAL(modelReset()), + this, SLOT(onModelReset())); + } + + QAbstractProxyModel::setSourceModel(model); + + onSourceInsertRows(QModelIndex(), 0, sourceModel()->rowCount() - 1); +} + +TodoNode *TodoProxyModelBase::addChildNode(const QModelIndex &sourceIndex, TodoNode *parent) +{ + QModelIndex proxyParentIndex = m_manager->indexForNode(parent, 0); + int row = 0; + + if (parent) { + row = parent->children().size(); + } else { + row = m_manager->roots().size(); + } + + beginInsertRows(proxyParentIndex, row, row); + + TodoNode *child = new TodoNode(sourceIndex, parent); + m_manager->insertNode(child); + + endInsertRows(); + + return child; +} + +void TodoProxyModelBase::onRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &/*destinationParent*/, int /*destinationRow*/) +{ + onSourceRemoveRows(sourceParent, sourceStart, sourceEnd); +} + +void TodoProxyModelBase::onRowsMoved(const QModelIndex &/*parent*/, int start, int end, const QModelIndex &destination, int row) +{ + onSourceInsertRows(destination, row, row + end - start); +} + +void TodoProxyModelBase::onModelReset() +{ + beginResetModel(); + + resetInternalData(); + + endResetModel(); + + init(); +} + +void TodoProxyModelBase::resetInternalData() +{ + foreach(TodoNode* node, m_manager->roots()) { + m_manager->removeNode(node); + delete node; + } + + m_inboxNode = 0; +} diff -Nru zanshin-0.1+svn1006410/src/todoproxymodelbase.h zanshin-0.2.0/src/todoproxymodelbase.h --- zanshin-0.1+svn1006410/src/todoproxymodelbase.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todoproxymodelbase.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,89 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2010 Kevin Ottens + Copyright 2008, 2009 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TODOPROXYMODELBASE_H +#define ZANSHIN_TODOPROXYMODELBASE_H + +#include + +class TodoNode; +class TodoNodeManager; + +class TodoProxyModelBase : public QAbstractProxyModel +{ + Q_OBJECT + +public: + enum MappingType { + SimpleMapping, + MultiMapping + }; + + TodoProxyModelBase(MappingType type, QObject *parent = 0); + virtual ~TodoProxyModelBase(); + + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &index) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + Qt::ItemFlags flags(const QModelIndex &index) const; + + virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QList mapFromSourceAll(const QModelIndex &sourceIndex) const; + + virtual void setSourceModel(QAbstractItemModel *model); + +private slots: + virtual void onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end) = 0; + virtual void onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end) = 0; + virtual void onSourceRemoveRows(const QModelIndex &sourceIndex, int begin, int end) = 0; + virtual void onRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow); + virtual void onRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row); + +protected slots: + virtual void onModelReset(); + +protected: + virtual void init(); + virtual TodoNode *createInbox() const = 0; + virtual void resetInternalData(); + + TodoNode *addChildNode(const QModelIndex &sourceIndex, TodoNode *parent); + + friend class TodoNodeManager; + TodoNodeManager *m_manager; + TodoNode *m_inboxNode; + +private: + MappingType m_mappingType; +}; + +#endif + diff -Nru zanshin-0.1+svn1006410/src/todotreemodel.cpp zanshin-0.2.0/src/todotreemodel.cpp --- zanshin-0.1+svn1006410/src/todotreemodel.cpp 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/todotreemodel.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -24,352 +24,354 @@ #include "todotreemodel.h" -#include -#include - -#include - -#include #include -#include "todoflatmodel.h" +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include "kmodelindexproxymapper.h" +#include "globaldefs.h" +#include "todohelpers.h" +#include "todonode.h" +#include "todonodemanager.h" TodoTreeModel::TodoTreeModel(QObject *parent) - : QAbstractProxyModel(parent) + : TodoProxyModelBase(SimpleMapping, parent) { } TodoTreeModel::~TodoTreeModel() { - } -QModelIndex TodoTreeModel::index(int row, int column, const QModelIndex &parent) const +void TodoTreeModel::onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end) { - if (row < 0 || column < 0 - || row >= rowCount(parent) - || column >= columnCount(parent)) { - return QModelIndex(); - } + QList sourceChildIndexes; - Akonadi::Entity::Id parentId = idForIndex(parent); - Akonadi::Entity::Id id = m_childrenMap[parentId].at(row); - - return createIndex(row, column, (void*)id); -} - -QModelIndex TodoTreeModel::parent(const QModelIndex &index) const -{ - if (!index.isValid() - || !m_parentMap.contains(index.internalId())) { - return QModelIndex(); + // Walking through all the items to harvest the needed info for sorting + for (int i = begin; i <= end; i++) { + QModelIndex sourceChildIndex = sourceModel()->index(i, 0, sourceIndex); + if (sourceChildIndex.isValid()) { + sourceChildIndexes << sourceChildIndex; + } } - Akonadi::Entity::Id id = idForIndex(index); - Akonadi::Entity::Id parentId = m_parentMap[id]; - - if (parentId==-1) { - return QModelIndex(); - } + // Now we're sure to add them in the right order, so let's do that! + TodoNode *collectionNode = m_manager->nodeForSourceIndex(sourceIndex); + QHash uidHash = m_collectionToUidsHash[collectionNode]; + + foreach (const QModelIndex &sourceChildIndex, sourceChildIndexes) { + Zanshin::ItemType type = (Zanshin::ItemType) sourceChildIndex.data(Zanshin::ItemTypeRole).toInt(); + + if (type==Zanshin::Collection) { + //kDebug() << "Adding collection"; + addChildNode(sourceChildIndex, collectionNode); + onSourceInsertRows(sourceChildIndex, 0, sourceModel()->rowCount(sourceChildIndex)-1); + + } else { + QString parentUid = sourceChildIndex.data(Zanshin::ParentUidRole).toString(); + TodoNode *parentNode = 0; + + if (uidHash.contains(parentUid)) { + parentNode = uidHash[parentUid]; + + } else { + if (type==Zanshin::ProjectTodo) { + parentNode = collectionNode; + } else if (type==Zanshin::StandardTodo) { + parentNode = m_inboxNode; + } else { + kFatal() << "Shouldn't happen, we must get only collections or todos"; + } + } - return indexForId(parentId); -} + TodoNode *child = addChildNode(sourceChildIndex, parentNode); + QString uid = child->data(0, Zanshin::UidRole).toString(); + //kDebug() << "Adding node:" << uid << parentUid; + uidHash[uid] = child; + + if (type==Zanshin::ProjectTodo) { + m_collectionToUidsHash[collectionNode] = uidHash; + //find and reparent children if possible + QList children = findChildNodes(uid, m_inboxNode); + foreach (TodoNode *childNode, children) { + reparentTodo(childNode); + } + + children = findChildNodes(uid, collectionNode); + foreach (TodoNode *childNode, children) { + reparentTodo(childNode); + } -int TodoTreeModel::rowCount(const QModelIndex &parent) const -{ - if (!parent.isValid()) { - return m_childrenMap[-1].count(); - } else if (parent.column() == 0) { // Only one set of children per row - Akonadi::Entity::Id id = idForIndex(parent); - return m_childrenMap[id].count(); + uidHash = m_collectionToUidsHash[collectionNode]; + } + } } - return 0; -} - -int TodoTreeModel::columnCount(const QModelIndex &/*parent*/) const -{ - return TodoFlatModel::LastColumn + 1; + m_collectionToUidsHash[collectionNode] = uidHash; } -QStringList TodoTreeModel::mimeTypes() const +QList TodoTreeModel::collectChildrenNode(TodoNode *root) { - return flatModel()->mimeTypes(); -} - -Qt::DropActions TodoTreeModel::supportedDropActions() const -{ - return flatModel()->supportedDropActions(); -} - -Qt::ItemFlags TodoTreeModel::flags(const QModelIndex &index) const -{ - return flatModel()->flags(mapToSource(index)); + QList children; + foreach (TodoNode *child, root->children()) { + children << child; + children << collectChildrenNode(child); + } + return children; } -QMimeData *TodoTreeModel::mimeData(const QModelIndexList &indexes) const +QModelIndexList TodoTreeModel::mapNodesToSource(QList nodes) { - QModelIndexList sourceIndexes; - foreach (const QModelIndex &proxyIndex, indexes) { - sourceIndexes << mapToSource(proxyIndex); + QModelIndexList indexes; + foreach (TodoNode* node, nodes) { + indexes << node->rowSourceIndex(); } - - return flatModel()->mimeData(sourceIndexes); + return indexes; } -bool TodoTreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, - int /*row*/, int /*column*/, const QModelIndex &parent) +QList TodoTreeModel::findChildNodes(const QString &parentUid, const TodoNode *root) { - if (action != Qt::MoveAction || !KUrl::List::canDecode(mimeData)) { - return false; + QList children; + if (!root) { + return children; } - KUrl::List urls = KUrl::List::fromMimeData(mimeData); - - QString parentRemoteId = data( - parent.sibling(parent.row(), TodoFlatModel::RemoteId)).toString(); - - foreach (const KUrl &url, urls) { - const Akonadi::Item item = Akonadi::Item::fromUrl(url); - - if (item.isValid()) { - QModelIndex index = flatModel()->indexForItem(item, TodoFlatModel::ParentRemoteId); - if (!flatModel()->setData(index, parentRemoteId)) { - return false; - } + foreach (TodoNode *node, root->children()) { + if (node->data(0, Zanshin::ParentUidRole).toString()==parentUid) { + children << node; } } - - return true; + return children; } -QVariant TodoTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +void TodoTreeModel::reparentTodo(TodoNode *node) { - return flatModel()->headerData(section, orientation, role); -} + QList nodes; + nodes << node; + nodes << collectChildrenNode(node); -QModelIndex TodoTreeModel::mapToSource(const QModelIndex &proxyIndex) const -{ - return flatModel()->indexForItem(Akonadi::Item(proxyIndex.internalId()), - proxyIndex.column()); -} + QModelIndexList indexes; + indexes << mapNodesToSource(nodes); -QModelIndex TodoTreeModel::mapFromSource(const QModelIndex &sourceIndex) const -{ - Akonadi::Item item = flatModel()->itemForIndex(sourceIndex); - return indexForId(item.id(), sourceIndex.column()); + destroyBranch(node); + foreach (const QModelIndex &index, indexes) { + onSourceInsertRows(index.parent(), index.row(), index.row()); + } } -void TodoTreeModel::setSourceModel(QAbstractItemModel *sourceModel) +void TodoTreeModel::onSourceRemoveRows(const QModelIndex &sourceIndex, int begin, int end) { - if (flatModel()) { - disconnect(flatModel()); + for (int i = begin; i <= end; ++i) { + QModelIndex sourceChildIndex = sourceModel()->index(i, 0, sourceIndex); + TodoNode *node = m_manager->nodeForSourceIndex(sourceChildIndex); + if (node) { + destroyBranch(node); + } } - - Q_ASSERT(sourceModel == 0 || qobject_cast(sourceModel) != 0); - QAbstractProxyModel::setSourceModel(sourceModel); - - onSourceInsertRows(QModelIndex(), 0, flatModel()->rowCount() - 1); - - connect(flatModel(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), - this, SLOT(onSourceDataChanged(const QModelIndex&, const QModelIndex&))); - connect(flatModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(onSourceInsertRows(const QModelIndex&, int, int))); - connect(flatModel(), SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), - this, SLOT(onSourceRemoveRows(const QModelIndex&, int, int))); - connect(flatModel(), SIGNAL(collectionChanged(const Akonadi::Collection&)), - this, SLOT(onSourceCollectionChanged(const Akonadi::Collection&))); } -void TodoTreeModel::onSourceInsertRows(const QModelIndex &/*sourceIndex*/, int begin, int end) +void TodoTreeModel::onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end) { - QList idToEmit; + for (int row = begin.row(); row <= end.row(); ++row) { + QModelIndex sourceChildIndex = sourceModel()->index(row, 0, begin.parent()); - // Storing the akonadi id vs remote id mapping - for (int i = begin; i <= end; i++) { - // Retrieve the item from the source model - Akonadi::Item item = flatModel()->itemForIndex(flatModel()->index(i, 0)); - QString remoteId = flatModel()->data(flatModel()->index(i, TodoFlatModel::RemoteId)).toString(); - m_remoteIdMap[item.id()] = remoteId; - m_remoteIdReverseMap[remoteId] = item.id(); - idToEmit << item.id(); - } + if (!sourceChildIndex.isValid()) { + continue; + } + + TodoNode *node = m_manager->nodeForSourceIndex(sourceChildIndex); - // Filling the global tree maps, also store a partial one only for this bunch of updates - QHash > partialChildrenMap; - QHash partialParentMap; - QList partialRoots; - for (int i = end; i >= begin; i--) { - Akonadi::Item item = flatModel()->itemForIndex(flatModel()->index(i, 0)); - QString parentRemoteId = flatModel()->data(flatModel()->index(i, TodoFlatModel::ParentRemoteId)).toString(); - Akonadi::Entity::Id parentId = -1; - if (!parentRemoteId.isEmpty()) { - parentId = m_remoteIdReverseMap[parentRemoteId]; + if (!node) { + continue; } - partialChildrenMap[parentId] << item.id(); - partialParentMap[item.id()] = parentId; - if (parentId==-1 || !idToEmit.contains(parentId)) { - partialRoots << item.id(); + + // Collections are just reemited + int nodeType = node->data(0, Zanshin::ItemTypeRole).toInt(); + if (nodeType==Zanshin::Collection) { + emit dataChanged(mapFromSource(sourceChildIndex), + mapFromSource(sourceChildIndex)); + continue; } - } - // Use the partial map and roots list to emit the insertions in the correct order - idToEmit = partialRoots; - while (!idToEmit.isEmpty()) { - Akonadi::Entity::Id id = idToEmit.takeFirst(); - idToEmit+=partialChildrenMap[id]; + if (nodeType==Zanshin::StandardTodo + && node->parent() == m_manager->nodeForSourceIndex(sourceChildIndex.parent())) { + reparentTodo(node); + continue; + } - Akonadi::Entity::Id parentId = partialParentMap[id]; - int row = m_childrenMap[parentId].count(); - QModelIndex proxyParentIndex; - if (parentId!=-1) { - proxyParentIndex = indexForId(parentId); + if (nodeType==Zanshin::ProjectTodo && node->parent()==m_inboxNode) { + reparentTodo(node); + continue; } - beginInsertRows(proxyParentIndex, row, row); - m_parentMap[id] = parentId; - m_childrenMap[parentId] << id; - endInsertRows(); + QString oldParentUid = node->parent()->data(0, Zanshin::UidRole).toString(); + QString newParentUid = sourceChildIndex.data(Zanshin::ParentUidRole).toString(); + + // If the parent didn't change we just reemit + if (oldParentUid==newParentUid) { + emit dataChanged(mapFromSource(sourceChildIndex), mapFromSource(sourceChildIndex)); + } else { + reparentTodo(node); + } } } -void TodoTreeModel::onSourceRemoveRows(const QModelIndex &/*sourceIndex*/, int begin, int end) +TodoNode *TodoTreeModel::createInbox() const { - for (int i = begin; i <= end; ++i) { - QModelIndex sourceIndex = flatModel()->index(i, 0); - QModelIndex proxyIndex = mapFromSource(sourceIndex); - Akonadi::Item item = flatModel()->itemForIndex(sourceIndex); - QString remoteId = flatModel()->data(flatModel()->index(i, TodoFlatModel::RemoteId)).toString(); - - QHash >::iterator it = m_childrenMap.find(item.id()); - if (it != m_childrenMap.end()) { - Akonadi::Entity::Id idKey = it.key(); - QList idList = it.value(); - while (!idList.isEmpty()) { - beginRemoveRows(proxyIndex.child(0, 0), 0, 0); - Akonadi::Entity::Id id = idList.takeFirst(); - endRemoveRows(); - - beginInsertRows(QModelIndex(), 0, 0); - QList idEmpty; - m_childrenMap[id] = idEmpty; - endInsertRows(); - } - m_childrenMap[idKey] = idList; - } + TodoNode *node = new TodoNode; - beginRemoveRows(proxyIndex.parent(), proxyIndex.row(), proxyIndex.row()); + node->setData(i18n("Inbox"), 0, Qt::DisplayRole); + node->setData(KIcon("mail-folder-inbox"), 0, Qt::DecorationRole); + node->setRowData(Zanshin::Inbox, Zanshin::ItemTypeRole); + + return node; +} - m_remoteIdMap.remove(item.id()); - m_remoteIdReverseMap.remove(remoteId); +void TodoTreeModel::destroyBranch(TodoNode *root) +{ + foreach (TodoNode *child, root->children()) { + destroyBranch(child); + } - Akonadi::Entity::Id parent = m_parentMap[item.id()]; - QList idList = m_childrenMap[parent]; - idList.removeOne(item.id()); - m_childrenMap[parent] = idList; + // Remove the uid item from all the maps + QString uid = root->data(0, Zanshin::UidRole).toString(); + foreach (TodoNode* node, m_collectionToUidsHash.keys()) { + m_collectionToUidsHash[node].remove(uid); + } - m_childrenMap.remove(item.id()); - m_parentMap.remove(item.id()); + QModelIndex proxyParentIndex = m_manager->indexForNode(root->parent(), 0); + int row = 0; - endRemoveRows(); + if (root->parent()) { + row = root->parent()->children().indexOf(root); + } else { + row = m_manager->roots().indexOf(root); } + + beginRemoveRows(proxyParentIndex, row, row); + m_manager->removeNode(root); + delete root; + endRemoveRows(); } -void TodoTreeModel::onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end) +Qt::ItemFlags TodoTreeModel::flags(const QModelIndex &index) const { - for (int row = begin.row(); row <= end.row(); ++row) { - QModelIndex sourceIndex = flatModel()->index(row, TodoFlatModel::RemoteId); + if (!index.isValid()) { + return Qt::NoItemFlags; + } - QModelIndex proxyIndex = mapFromSource(sourceIndex); - emit dataChanged(index(proxyIndex.row(), 0, proxyIndex.parent()), - index(proxyIndex.row(), TodoFlatModel::LastColumn, proxyIndex.parent())); + Zanshin::ItemType type = (Zanshin::ItemType)index.data(Zanshin::ItemTypeRole).toInt(); - QModelIndex parentRemoteIdIndex = flatModel()->index(row, TodoFlatModel::ParentRemoteId); - QString parentRemoteId = flatModel()->data(parentRemoteIdIndex).toString(); - Akonadi::Entity::Id newParentId = -1; - if (!parentRemoteId.isEmpty()) { - newParentId = m_remoteIdReverseMap[parentRemoteId]; - } + if (type == Zanshin::Inbox) { + return Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled; + } - QString itemRemoteId = flatModel()->data(sourceIndex).toString(); - Akonadi::Entity::Id itemId = -1; - if (!itemRemoteId.isEmpty()) { - itemId = m_remoteIdReverseMap[itemRemoteId]; - } + Qt::ItemFlags flags = sourceModel()->flags(mapToSource(index)); + Akonadi::Collection collection; - Akonadi::Entity::Id oldParentId = -1; - if (m_parentMap.contains(itemId)) { - oldParentId = m_parentMap[itemId]; - } + if (type==Zanshin::Collection) { + collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value(); - if (oldParentId != newParentId) { - int oldRow = m_childrenMap[oldParentId].indexOf(itemId); - beginRemoveRows(indexForId(oldParentId), oldRow, oldRow); - m_childrenMap[oldParentId].removeAll(itemId); - m_parentMap.remove(itemId); - endRemoveRows(); + } else if (type==Zanshin::ProjectTodo) { + // We use ParentCollectionRole instead of Akonadi::Item::parentCollection() because the + // information about the rights is not valid on retrieved items. + collection = index.data(Akonadi::EntityTreeModel::ParentCollectionRole).value(); + } - int newRow = m_childrenMap[newParentId].size(); - beginInsertRows(indexForId(newParentId), newRow, newRow); - m_childrenMap[newParentId] << itemId; - m_parentMap[itemId] = newParentId; - endInsertRows(); - } + if (!(collection.rights() & Akonadi::Collection::CanCreateItem)) { + flags&= ~Qt::ItemIsDropEnabled; + } else { + flags|= Qt::ItemIsDropEnabled; } + + return flags; } -void TodoTreeModel::onSourceCollectionChanged(const Akonadi::Collection &collection) +QMimeData *TodoTreeModel::mimeData(const QModelIndexList &indexes) const { - m_childrenMap.clear(); - m_parentMap.clear(); - m_remoteIdMap.clear(); - m_remoteIdReverseMap.clear(); - - reset(); + QModelIndexList sourceIndexes; + foreach (const QModelIndex &proxyIndex, indexes) { + sourceIndexes << mapToSource(proxyIndex); + } - emit collectionChanged(collection); + return sourceModel()->mimeData(sourceIndexes); } -Akonadi::Entity::Id TodoTreeModel::idForIndex(const QModelIndex &index) const +QStringList TodoTreeModel::mimeTypes() const { - return index.isValid() ? index.internalId() : -1; + QStringList types; + if (sourceModel()) { + types << sourceModel()->mimeTypes(); + } + return types; } -QModelIndex TodoTreeModel::indexForId(Akonadi::Entity::Id id, int column) const +void TodoTreeModel::createChild(const QModelIndex &child, const QModelIndex &parent, int row) { - if (id==-1) { - return QModelIndex(); + if (!child.isValid() || !parent.isValid()) { + return; } - Q_ASSERT(m_parentMap.contains(id)); - Akonadi::Entity::Id parentId = m_parentMap[id]; - int row = m_childrenMap[parentId].indexOf(id); - Q_ASSERT(row>=0); + onSourceInsertRows(parent, row, row); - return createIndex(row, column, (void*)id); + QModelIndexList children = child.data(Zanshin::ChildIndexesRole).value(); + foreach (const QModelIndex &index, children) { + Q_ASSERT(index.model()==sourceModel()); + createChild(index, child.parent(), index.row()); + } } -Akonadi::Item TodoTreeModel::itemForIndex(const QModelIndex &index) const +bool TodoTreeModel::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, + int /*row*/, int /*column*/, const QModelIndex &parent) { - return flatModel()->itemForIndex(mapToSource(index)); -} + if (action != Qt::MoveAction || !KUrl::List::canDecode(mimeData)) { + return false; + } -QModelIndex TodoTreeModel::indexForItem(const Akonadi::Item &item, const int column) const -{ - return mapFromSource(flatModel()->indexForItem(item, column)); -} + KUrl::List urls = KUrl::List::fromMimeData(mimeData); -void TodoTreeModel::setCollection(const Akonadi::Collection &collection) -{ - flatModel()->setCollection(collection); -} + Akonadi::Collection collection; + Zanshin::ItemType parentType = (Zanshin::ItemType)parent.data(Zanshin::ItemTypeRole).toInt(); + if (parentType == Zanshin::Collection) { + collection = parent.data(Akonadi::EntityTreeModel::CollectionRole).value(); + } else { + const Akonadi::Item parentItem = parent.data(Akonadi::EntityTreeModel::ItemRole).value(); + collection = parentItem.parentCollection(); + } -Akonadi::Collection TodoTreeModel::collection() const -{ - return flatModel()->collection(); + QString parentUid = parent.data(Zanshin::UidRole).toString(); + + foreach (const KUrl &url, urls) { + const Akonadi::Item urlItem = Akonadi::Item::fromUrl(url); + if (urlItem.isValid()) { + Akonadi::Item item = TodoHelpers::fetchFullItem(urlItem); + + if (!item.isValid()) { + return false; + } + + if (item.hasPayload()) { + TodoHelpers::moveTodoToProject(item, parentUid, parentType, collection); + } + } + } + + return true; } -TodoFlatModel *TodoTreeModel::flatModel() const +Qt::DropActions TodoTreeModel::supportedDropActions() const { - return qobject_cast(sourceModel()); + if (!sourceModel()) { + return Qt::IgnoreAction; + } + return sourceModel()->supportedDropActions(); } diff -Nru zanshin-0.1+svn1006410/src/todotreemodel.h zanshin-0.2.0/src/todotreemodel.h --- zanshin-0.1+svn1006410/src/todotreemodel.h 2009-08-13 22:09:33.000000000 +0000 +++ zanshin-0.2.0/src/todotreemodel.h 2011-11-25 15:00:52.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Zanshin Todo. - Copyright 2008 Kevin Ottens + Copyright 2008-2010 Kevin Ottens Copyright 2008, 2009 Mario Bensi This program is free software; you can redistribute it and/or @@ -25,19 +25,10 @@ #ifndef ZANSHIN_TODOTREEMODEL_H #define ZANSHIN_TODOTREEMODEL_H -#include +#include "todonode.h" +#include "todoproxymodelbase.h" -#include - -namespace Akonadi -{ - class Item; - class Collection; -} - -class TodoFlatModel; - -class TodoTreeModel : public QAbstractProxyModel +class TodoTreeModel : public TodoProxyModelBase { Q_OBJECT @@ -45,49 +36,27 @@ TodoTreeModel(QObject *parent = 0); virtual ~TodoTreeModel(); - virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - virtual QModelIndex parent(const QModelIndex &index) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; - - virtual QStringList mimeTypes() const; - Qt::DropActions supportedDropActions() const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual QMimeData *mimeData(const QModelIndexList &indexes) const; virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); - virtual QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const; - - virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const; - virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; - - virtual void setSourceModel(QAbstractItemModel *sourceModel); - - Akonadi::Item itemForIndex (const QModelIndex &index) const; - QModelIndex indexForItem (const Akonadi::Item &item, const int column = 0) const; - - void setCollection(const Akonadi::Collection &collection); - Akonadi::Collection collection() const; - -signals: - void collectionChanged(const Akonadi::Collection &collection); + virtual QStringList mimeTypes() const; + virtual Qt::DropActions supportedDropActions() const; private slots: - void onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end); - void onSourceRemoveRows(const QModelIndex &sourceIndex, int begin, int end); - void onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end); - void onSourceCollectionChanged(const Akonadi::Collection &collection); + virtual void onSourceDataChanged(const QModelIndex &begin, const QModelIndex &end); + virtual void onSourceInsertRows(const QModelIndex &sourceIndex, int begin, int end); + virtual void onSourceRemoveRows(const QModelIndex &sourceIndex, int begin, int end); private: - TodoFlatModel *flatModel() const; - Akonadi::Entity::Id idForIndex(const QModelIndex &index) const; - QModelIndex indexForId(Akonadi::Entity::Id id, int column = 0) const; - - QHash > m_childrenMap; - QHash m_parentMap; + virtual TodoNode *createInbox() const; + void destroyBranch(TodoNode *root); + void createChild(const QModelIndex &child, const QModelIndex &parent, int row); + QList collectChildrenNode(TodoNode *root); + QModelIndexList mapNodesToSource(QList nodes); + QList findChildNodes(const QString &parentUid, const TodoNode *root); + void reparentTodo(TodoNode *node); - QHash m_remoteIdMap; - QHash m_remoteIdReverseMap; + QHash > m_collectionToUidsHash; }; #endif diff -Nru zanshin-0.1+svn1006410/src/todotreeview.cpp zanshin-0.2.0/src/todotreeview.cpp --- zanshin-0.1+svn1006410/src/todotreeview.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todotreeview.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,51 @@ +/* This file is part of Zanshin Todo. + + Copyright 2010 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "todotreeview.h" +#include +#include + +TodoTreeView::TodoTreeView(QWidget *parent) + : Akonadi::EntityTreeView(parent) +{ +} + +void TodoTreeView::dragMoveEvent(QDragMoveEvent *event) +{ + QTreeView::dragMoveEvent(event); +} + +void TodoTreeView::dropEvent(QDropEvent *event) +{ + QTreeView::dropEvent(event); +} + +QItemSelectionModel::SelectionFlags TodoTreeView::selectionCommand(const QModelIndex &index, const QEvent *event) const +{ + if (!index.isValid()) { + return QItemSelectionModel::NoUpdate; + } else { + return Akonadi::EntityTreeView::selectionCommand(index, event); + } +} + diff -Nru zanshin-0.1+svn1006410/src/todotreeview.h zanshin-0.2.0/src/todotreeview.h --- zanshin-0.1+svn1006410/src/todotreeview.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/todotreeview.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,43 @@ +/* This file is part of Zanshin Todo. + + Copyright 2010 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TODOTREEVIEW_H +#define ZANSHIN_TODOTREEVIEW_H + +#include + +class QDragMoveEvent; +class QDropEvent; + +class TodoTreeView : public Akonadi::EntityTreeView +{ + public: + TodoTreeView(QWidget *parent = 0); + protected: + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, + const QEvent *event = 0 ) const; +}; + +#endif //ZANSHIN_TODOTREEVIEW_H diff -Nru zanshin-0.1+svn1006410/src/zanshin.desktop zanshin-0.2.0/src/zanshin.desktop --- zanshin-0.1+svn1006410/src/zanshin.desktop 2009-08-13 22:45:20.000000000 +0000 +++ zanshin-0.2.0/src/zanshin.desktop 2011-11-25 15:00:52.000000000 +0000 @@ -3,5 +3,36 @@ Categories=Qt;KDE;Utility;X-KDE-Utilities-PIM; Exec=zanshin Name=Zanshin -Icon=office-calendar +Name[cs]=Zanshin +Name[da]=Zanshin +Name[de]=Zanshin +Name[es]=Zanshin +Name[et]=Zanshin +Name[fr]=Zanshin +Name[it]=Zanshin +Name[km]=Zanshin +Name[nds]=Zanshin +Name[nl]=Zanshin +Name[pl]=Zanshin +Name[pt]=Zanshin +Name[pt_BR]=Zanshin +Name[sv]=Zanshin +Name[uk]=Zanshin +Name[x-test]=xxZanshinxx +Icon=zanshin GenericName=TODO Management Application +GenericName[da]=Program til håndtering af gøremål +GenericName[de]=Programm zur Aufgabenverwaltung +GenericName[es]=PARA HACER - Aplicación de gestión +GenericName[et]=Ülesannete haldamise rakendus +GenericName[fr]=Application de gestion de tâches à effectuer +GenericName[it]=Applicazione per la gestione delle COSE DA FARE +GenericName[km]=កម្មវិធី​គ្រប់គ្រង​ការងារ​ត្រូវ​ធ្វើ +GenericName[nds]=Programm för de Opgavenpleeg +GenericName[nl]=Beheertoepassing voor taken +GenericName[pl]=Program zarządzający zadaniami 'do zrobienia' +GenericName[pt]=Aplicação de Gestão de Tarefas +GenericName[pt_BR]=Aplicativo de gerenciamento de tarefas +GenericName[sv]=Hanteringsprogram för uppgifter +GenericName[uk]=Програма для керування записами завдань +GenericName[x-test]=xxTODO Management Applicationxx diff -Nru zanshin-0.1+svn1006410/src/zanshin_part.desktop zanshin-0.2.0/src/zanshin_part.desktop --- zanshin-0.1+svn1006410/src/zanshin_part.desktop 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/zanshin_part.desktop 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,21 @@ +[Desktop Entry] +Name=Zanshin Part +Name[da]=Zanshin-Part +Name[de]=Zanshin-Part +Name[es]=Zanshin Part +Name[et]=Zanshini komponent +Name[fr]=Composant Zanshin +Name[it]=Zanshin Part +Name[km]=ផ្នែក Zanshin +Name[nl]=Zanshin-onderdeel +Name[pl]=Moduł Zanshin +Name[pt]=Componente do Zanshin +Name[pt_BR]=Parte do Zanshin +Name[sv]=Zanshin-delprogram +Name[uk]=Модуль Zanshin +Name[x-test]=xxZanshin Partxx +X-KDE-ServiceTypes=KParts/ReadOnlyPart +X-KDE-Library=zanshin_part +Type=Service +Icon=view-pim-tasks + diff -Nru zanshin-0.1+svn1006410/src/zanshin_part.rc zanshin-0.2.0/src/zanshin_part.rc --- zanshin-0.1+svn1006410/src/zanshin_part.rc 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/zanshin_part.rc 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + &Go + + + + + + + + + Main Toolbar + + + + + + diff -Nru zanshin-0.1+svn1006410/src/zanshin_plugin.desktop zanshin-0.2.0/src/zanshin_plugin.desktop --- zanshin-0.1+svn1006410/src/zanshin_plugin.desktop 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/src/zanshin_plugin.desktop 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,48 @@ +[Desktop Entry] +Type=Service +Icon=view-pim-tasks +X-KDE-ServiceTypes=Kontact/Plugin,KPluginInfo + +X-KDE-Library=kontact_zanshinplugin +X-KDE-KontactPluginVersion=9 +X-KDE-KontactPartLibraryName=zanshin_part +X-KDE-KontactPartExecutableName=zanshin +X-KDE-KontactPartLoadOnStart=false +X-KDE-KontactPluginHasSummary=false + +X-KDE-PluginInfo-Name=kontact_zanshinplugin +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true +X-KDE-PluginInfo-AllowEmptySettings=true + +Comment=Kontact Zanshin Plugin +Comment[da]=Zanshin-plugin til Kontact +Comment[de]=Zanshin-Modul für Kontakt +Comment[es]=Complemento Kontact para Zanshin +Comment[et]=Kontacti Zanshini plugin +Comment[fr]=Module externe Zanshin pour Kontact +Comment[it]=Estensione per Kontact di Zanshin +Comment[km]=កម្មវិធី​ជំនួយ​របស់ Kontact Zanshin +Comment[nl]=Kontact-plugin voor Zanshin +Comment[pl]=Wtyczka kontaktów Zanshin +Comment[pt]='Plugin' do Zanshin para o Kontact +Comment[pt_BR]=Plugin do Zanshin para o Kontact +Comment[sv]=Zanshin-insticksprogram för Kontact +Comment[uk]=Додаток Zanshin до Kontact +Comment[x-test]=xxKontact Zanshin Pluginxx +Name=Todo Lists +Name[da]=Gøremålslister +Name[de]=Aufgabenlisten +Name[es]=Lista de tareas +Name[et]=Ülesannete nimekirjad +Name[fr]=Listes de tâches +Name[it]=Elenchi delle cose da fare +Name[km]=បញ្ជី​ការងារ​ត្រូវ​ធ្វើ +Name[nl]=Takenlijsten +Name[pl]=Lista zadań 'do zrobienia' +Name[pt]=Listas de Itens Por-Fazer +Name[pt_BR]=Listas de tarefas +Name[sv]=Uppgiftslistor +Name[uk]=Списки завдань +Name[x-test]=xxTodo Listsxx diff -Nru zanshin-0.1+svn1006410/src/zanshinui.rc zanshin-0.2.0/src/zanshinui.rc --- zanshin-0.1+svn1006410/src/zanshinui.rc 2009-08-13 22:09:32.000000000 +0000 +++ zanshin-0.2.0/src/zanshinui.rc 2011-11-25 15:00:52.000000000 +0000 @@ -2,18 +2,22 @@ - + + + + + &Go - + @@ -22,7 +26,7 @@ Main Toolbar - + diff -Nru zanshin-0.1+svn1006410/tests/CMakeLists.txt zanshin-0.2.0/tests/CMakeLists.txt --- zanshin-0.1+svn1006410/tests/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/CMakeLists.txt 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,27 @@ +add_subdirectory(testlib) + +include_directories( ${CMAKE_SOURCE_DIR}/src) + +MACRO(ZANSHIN_UNIT_TESTS) + FOREACH(_testname ${ARGN}) + kde4_add_unit_test(${_testname} TESTNAME zanshin-${_testname} ${_testname}.cpp) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}") + target_link_libraries(${_testname} ${KDE4_KDEUI_LIBS} ${KDEPIMLIBS_AKONADI_LIBS} + ${KDEPIMLIBS_KCALCORE_LIBS} ${QT_QTTEST_LIBRARY} + zanshin_static + zanshin_testlib + ) + ENDFOREACH(_testname) +ENDMACRO(ZANSHIN_UNIT_TESTS) + +########### automated tests ############### + +ZANSHIN_UNIT_TESTS( + modelstacktest + selectionproxymodeltest + sidebarmodelspec + todocategoriesmodelspec + todometadatamodelspec + todotreemodelspec +) + diff -Nru zanshin-0.1+svn1006410/tests/modelstacktest.cpp zanshin-0.2.0/tests/modelstacktest.cpp --- zanshin-0.1+svn1006410/tests/modelstacktest.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/modelstacktest.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,108 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include + +#include + +#include "modelstack.h" + +class ModelStackTest : public QObject +{ + Q_OBJECT +private slots: + void shouldEnsureModelsAreAvailable() + { + ModelStack stack; + + QVERIFY(stack.baseModel()!=0); + QVERIFY(stack.collectionsModel()!=0); + + QVERIFY(stack.treeModel()!=0); + QVERIFY(stack.treeSideBarModel()!=0); + QVERIFY(stack.treeComboModel()!=0); + + QVERIFY(stack.categoriesModel()!=0); + QVERIFY(stack.categoriesSideBarModel()!=0); + QVERIFY(stack.categoriesComboModel()!=0); + } + + void shouldLinkToExactlyOneSelectionModel() + { + ModelStack stack; + + QItemSelectionModel selectionModel1(stack.baseModel()); + QItemSelectionModel selectionModel2(stack.baseModel()); + + stack.setItemTreeSelectionModel(&selectionModel1); + stack.setItemCategorySelectionModel(&selectionModel2); + + QVERIFY(stack.treeSelectionModel()!=0); + QVERIFY(stack.categoriesSelectionModel()!=0); + + QVERIFY(stack.treeSelectionModel()!=0); + QVERIFY(stack.categoriesSelectionModel()!=0); + + // It should raise asserts in the other cases, but since + // we don't have a QEXPECT_ASSERT... + } + + void shouldEnsureModelsAreConstant() + { + ModelStack stack; + + QItemSelectionModel selectionModel(stack.baseModel()); + stack.setItemTreeSelectionModel(&selectionModel); + stack.setItemCategorySelectionModel(&selectionModel); + + QList models; + models << stack.baseModel() + << stack.collectionsModel() + << stack.treeModel() + << stack.treeSideBarModel() + << stack.treeSelectionModel() + << stack.treeComboModel() + << stack.categoriesModel() + << stack.categoriesSideBarModel() + << stack.categoriesSelectionModel() + << stack.categoriesComboModel(); + + for (int i=0; i<3; i++) { + QList list = models; + QCOMPARE(stack.baseModel(), list.takeFirst()); + QCOMPARE(stack.collectionsModel(), list.takeFirst()); + QCOMPARE(stack.treeModel(), list.takeFirst()); + QCOMPARE(stack.treeSideBarModel(), list.takeFirst()); + QCOMPARE(stack.treeSelectionModel(), list.takeFirst()); + QCOMPARE(stack.treeComboModel(), list.takeFirst()); + QCOMPARE(stack.categoriesModel(), list.takeFirst()); + QCOMPARE(stack.categoriesSideBarModel(), list.takeFirst()); + QCOMPARE(stack.categoriesSelectionModel(), list.takeFirst()); + QCOMPARE(stack.categoriesComboModel(), list.takeFirst()); + } + } +}; + +QTEST_KDEMAIN(ModelStackTest, GUI) + +#include "modelstacktest.moc" diff -Nru zanshin-0.1+svn1006410/tests/selectionproxymodeltest.cpp zanshin-0.2.0/tests/selectionproxymodeltest.cpp --- zanshin-0.1+svn1006410/tests/selectionproxymodeltest.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/selectionproxymodeltest.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,190 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include + +#include +#include + +#include "selectionproxymodel.h" +#include "testlib/testlib.h" + +using namespace Zanshin::Test; + +class SelectionProxyModelTest : public QObject +{ + Q_OBJECT +private slots: + void initTestCase() + { + QList roles; + roles << Qt::DisplayRole + << Akonadi::EntityTreeModel::ItemRole + << Akonadi::EntityTreeModel::CollectionRole; + + QTest::setEvaluatedItemRoles(roles); + } + + void shouldRememberItsSourceModel() + { + //GIVEN + QStandardItemModel baseModel; + QItemSelectionModel selectionModel(&baseModel); + SelectionProxyModel proxyModel; + ModelTest mt(&proxyModel); + + //WHEN + proxyModel.setSelectionModel(&selectionModel); + proxyModel.setSourceModel(&baseModel); + + //THEN + QVERIFY(proxyModel.sourceModel() == &baseModel); + } + + void shouldNotCrashWithAnEmptySelectionModel() + { + //GIVEN + QStandardItemModel treeModel; + SelectionProxyModel proxyModel; + ModelTest mt(&proxyModel); + + //WHEN + proxyModel.setSourceModel(&treeModel); + + //THEN + QVERIFY(proxyModel.sourceModel() == &treeModel); + } + + void shouldCallSetSourceModelBeforeSetSelectionModel() + { + //GIVEN + QStandardItemModel baseModel; + QItemSelectionModel selectionModel(&baseModel); + SelectionProxyModel proxyModel; + ModelTest mt(&proxyModel); + + //WHEN + proxyModel.setSourceModel(&baseModel); + proxyModel.setSelectionModel(&selectionModel); + + //THEN + QVERIFY(proxyModel.sourceModel() == &baseModel); + } + + void shouldKeepAscendantAndDescendantOfSelectedItems_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "itemsToSelect" ); + QTest::addColumn( "outputStructure" ); + + // Base items + C c1(1, 0, "c1"); + C c2(2, 0, "c2"); + C c3(3, 2, "c3"); + C c4(4, 3, "c4"); + C c5(5, 3, "c5"); + T t1(1, 3, "t1", QString(), "t1"); + T t2(2, 3, "t2", QString(), "t2"); + T t3(3, 4, "t3", QString(), "t3"); + T t4(4, 4, "t4", QString(), "t4"); + T t5(5, 5, "t5", QString(), "t5"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << c2 + << _+c3 + << __+t1 + << __+t2 + << __+c4 + << ___+t3 + << ___+t4 + << __+c5 + << ___+t5; + + + ModelPath::List itemsToSelect; + ModelStructure outputStructure; + + QTest::newRow( "empty selection" ) << sourceStructure << itemsToSelect << outputStructure; + + itemsToSelect << c2 % c3 % c4; + outputStructure << c2 + << _+c3 + << __+c4 + << ___+t3 + << ___+t4; + + QTest::newRow( "one item selected" ) << sourceStructure << itemsToSelect << outputStructure; + + itemsToSelect.clear(); + itemsToSelect << c2 % c3 % c4 + << c2 % c3 % c5; + outputStructure.clear(); + outputStructure << c2 + << _+c3 + << __+c4 + << ___+t3 + << ___+t4 + << __+c5 + << ___+t5; + + QTest::newRow( "two items selected" ) << sourceStructure << itemsToSelect << outputStructure; + } + + void shouldKeepAscendantAndDescendantOfSelectedItems() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + ModelUtils::create(&source, sourceStructure); + + //Selection proxy + QItemSelectionModel selection(&source); + SelectionProxyModel proxyModel; + ModelTest mt(&proxyModel); + proxyModel.setSelectionModel(&selection); + proxyModel.setSourceModel(&source); + + //WHEN + QFETCH(ModelPath::List, itemsToSelect); + + // We change the selection + foreach (const ModelPath &path, itemsToSelect) { + QModelIndex index = ModelUtils::locateItem(&source, path); + selection.select(index, QItemSelectionModel::Select); + } + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + QCOMPARE(proxyModel, output); + } +}; + +QTEST_KDEMAIN(SelectionProxyModelTest, GUI) + +#include "selectionproxymodeltest.moc" diff -Nru zanshin-0.1+svn1006410/tests/sidebarmodelspec.cpp zanshin-0.2.0/tests/sidebarmodelspec.cpp --- zanshin-0.1+svn1006410/tests/sidebarmodelspec.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/sidebarmodelspec.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,135 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include + +#include + +#include "sidebarmodel.h" +#include "todometadatamodel.h" +#include "todotreemodel.h" +#include "testlib/testlib.h" +#include "testlib/modelbuilderbehavior.h" + +using namespace Zanshin::Test; + +class SideBarModelTest : public QObject +{ + Q_OBJECT +private slots: + void shouldReactToSourceRowRemovals_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "itemsToRemove" ); + QTest::addColumn( "outputStructure" ); + + V inbox(Inbox); + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1"); + T t2(2, 1, "t2", "t1", "t2"); + T t3(3, 1, "t3", QString(), "t3", InProgress, ProjectTag); + T t4(4, 3, "t4", "t3", "t4"); + + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2; + + ModelPath::List itemsToRemove; + itemsToRemove << c1 % t2; + + ModelStructure outputStructure; + outputStructure << inbox + << c1; + + QTest::newRow( "Remove all children under a project" ) << sourceStructure + << itemsToRemove + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t3 + << _+t4; + + itemsToRemove.clear(); + itemsToRemove << c1 % t4; + + outputStructure.clear(); + outputStructure << inbox + << c1 + << _+t3; + + QTest::newRow( "Remove all children under a project with tag" ) << sourceStructure + << itemsToRemove + << outputStructure; + + } + + void shouldReactToSourceRowRemovals() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel metadataModel; + ModelTest t1(&metadataModel); + + metadataModel.setSourceModel(&source); + + //create todoTreeModel + TodoTreeModel todoTreeModel; + ModelTest t2(&todoTreeModel); + + todoTreeModel.setSourceModel(&metadataModel); + + // create sidebarmodel + SideBarModel treeSideBarModel; + ModelTest t3(&treeSideBarModel); + + treeSideBarModel.setSourceModel(&todoTreeModel); + + //WHEN + QFETCH(ModelPath::List, itemsToRemove); + + ModelUtils::destroy(&source, itemsToRemove); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + StandardModelBuilderBehavior outputBehavior; + outputBehavior.setSingleColumnEnabled(true); + ModelUtils::create(&output, outputStructure, ModelPath(), &outputBehavior); + + QCOMPARE(treeSideBarModel, output); + } +}; + +QTEST_KDEMAIN(SideBarModelTest, GUI) + +#include "sidebarmodelspec.moc" diff -Nru zanshin-0.1+svn1006410/tests/testlib/cat.cpp zanshin-0.2.0/tests/testlib/cat.cpp --- zanshin-0.1+svn1006410/tests/testlib/cat.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/cat.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,49 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "cat.h" + +#include + +using namespace Zanshin::Test; + +Cat::Cat() +{ +} + +Cat::Cat(const QString &n) + : name(n) +{ +} + +Cat::Cat(const QString &pP, const QString &n) + : parentPath(pP), name(n) +{ +} + +bool Cat::operator==(const Cat &other) const +{ + return parentPath==other.parentPath + && name==other.name; +} + diff -Nru zanshin-0.1+svn1006410/tests/testlib/cat.h zanshin-0.2.0/tests/testlib/cat.h --- zanshin-0.1+svn1006410/tests/testlib/cat.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/cat.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,57 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_CAT_H +#define ZANSHIN_TESTLIB_CAT_H + +#include +#include + +namespace Zanshin +{ +namespace Test +{ + +struct Cat // Stands for category +{ +public: + typedef QList List; + + Cat(); + explicit Cat(const QString &name); + Cat(const QString &parentPath, const QString &name); + + bool operator==(const Cat &other) const; + + QString parentPath; + QString name; +}; + +} // namespace Test +} // namespace Zanshin + +Q_DECLARE_METATYPE(Zanshin::Test::Cat) +Q_DECLARE_METATYPE(Zanshin::Test::Cat::List) + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/c.cpp zanshin-0.2.0/tests/testlib/c.cpp --- zanshin-0.1+svn1006410/tests/testlib/c.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/c.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,45 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "c.h" + +#include + +using namespace Zanshin::Test; + +C::C() +{ +} + +C::C(qint64 i, qint64 pI, const QString &n) + : id(i), parentId(pI), name(n) +{ +} + +bool C::operator==(const C &other) const +{ + return id==other.id + && parentId==other.parentId + && name==other.name; +} + diff -Nru zanshin-0.1+svn1006410/tests/testlib/c.h zanshin-0.2.0/tests/testlib/c.h --- zanshin-0.1+svn1006410/tests/testlib/c.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/c.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,57 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_C_H +#define ZANSHIN_TESTLIB_C_H + +#include +#include + +namespace Zanshin +{ +namespace Test +{ + +struct C // Stands for collection +{ +public: + typedef QList List; + + C(); + explicit C(qint64 id, qint64 parentId, const QString &name); + + bool operator==(const C &other) const; + + qint64 id; + qint64 parentId; + QString name; +}; + +} // namespace Test +} // namespace Zanshin + +Q_DECLARE_METATYPE(Zanshin::Test::C) +Q_DECLARE_METATYPE(Zanshin::Test::C::List) + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/CMakeLists.txt zanshin-0.2.0/tests/testlib/CMakeLists.txt --- zanshin-0.1+svn1006410/tests/testlib/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/CMakeLists.txt 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,25 @@ +# Even the test lib needs to be tested. ;-) +add_subdirectory(tests) + +########### testlib ############### + +set(zanshin_testlib_SRCS + c.cpp + cat.cpp + indent.cpp + mockmodel.cpp + modelbuilderbehavior.cpp + modelnode.cpp + modelpath.cpp + modelstructure.cpp + modeltest.cpp + modelutils.cpp + qcompare.cpp + t.cpp + v.cpp +) + +include_directories(${CMAKE_SOURCE_DIR}/tests) +kde4_add_library(zanshin_testlib STATIC ${zanshin_testlib_SRCS}) +target_link_libraries(zanshin_testlib ${KDE4_KDEUI_LIBS} ${KDEPIMLIBS_AKONADI_LIBS} ${KDEPIMLIBS_KCALCORE_LIBS} ${QT_QTTEST_LIBRARY}) + diff -Nru zanshin-0.1+svn1006410/tests/testlib/dsl.h zanshin-0.2.0/tests/testlib/dsl.h --- zanshin-0.1+svn1006410/tests/testlib/dsl.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/dsl.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,37 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_DSL_H +#define ZANSHIN_TESTLIB_DSL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/indent.cpp zanshin-0.2.0/tests/testlib/indent.cpp --- zanshin-0.1+svn1006410/tests/testlib/indent.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/indent.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,34 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "indent.h" + +#include + +using namespace Zanshin::Test; + +Indent::Indent(quint64 s) + : size(s) +{ +} + diff -Nru zanshin-0.1+svn1006410/tests/testlib/indent.h zanshin-0.2.0/tests/testlib/indent.h --- zanshin-0.1+svn1006410/tests/testlib/indent.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/indent.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,59 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_INDENT_H +#define ZANSHIN_TESTLIB_INDENT_H + +#include + +namespace Zanshin +{ +namespace Test +{ + +struct Indent +{ +public: + explicit Indent(quint64 size = 0); + + quint64 size; +}; + +const Indent _(1); +const Indent __(2); +const Indent ___(3); +const Indent ____(4); +const Indent _____(5); +const Indent ______(6); +const Indent _______(7); +const Indent ________(8); +const Indent _________(9); +const Indent __________(10); + +} // namespace Test +} // namespace Zanshin + +Q_DECLARE_METATYPE(Zanshin::Test::Indent) + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/mockmodel.cpp zanshin-0.2.0/tests/testlib/mockmodel.cpp --- zanshin-0.1+svn1006410/tests/testlib/mockmodel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/mockmodel.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,45 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + Copyright 2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "mockmodel.h" + +using namespace Zanshin::Test; + +static const int COLUMNCOUNT = 5; + +MockModel::MockModel(QObject* parent) + : QStandardItemModel(parent) +{ + setColumnCount(COLUMNCOUNT); +} + +void MockModel::clearData() +{ + bool blocked = blockSignals(true); + clear(); + setColumnCount(COLUMNCOUNT); + blockSignals(blocked); + reset(); +} + diff -Nru zanshin-0.1+svn1006410/tests/testlib/mockmodel.h zanshin-0.2.0/tests/testlib/mockmodel.h --- zanshin-0.1+svn1006410/tests/testlib/mockmodel.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/mockmodel.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,55 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + Copyright 2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_MOCKMODEL_H +#define ZANSHIN_TESTLIB_MOCKMODEL_H + +#include + +namespace Zanshin +{ +namespace Test +{ + +// We have added this class to have the correct number of column after clearData was called. +// QStandardItemModel recreate the root node when clear is called. +// The number of column on this root node is 0 by default. +// To fix that we added clearData to clear the model, set the column number and after that send the +// signal resetModel. + +class MockModel : public QStandardItemModel +{ +public: + MockModel(QObject* parent = 0); + + void clearData(); +private: + using QStandardItemModel::clear; +}; + +} // namespace Test +} // namespace Zanshin + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/modelbuilderbehavior.cpp zanshin-0.2.0/tests/testlib/modelbuilderbehavior.cpp --- zanshin-0.1+svn1006410/tests/testlib/modelbuilderbehavior.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modelbuilderbehavior.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,241 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "modelbuilderbehavior.h" + +#include +#include +#include "../../src/globaldefs.h" + +using namespace Zanshin::Test; + +ModelBuilderBehaviorBase::ModelBuilderBehaviorBase() +{ + +} + +ModelBuilderBehaviorBase::~ModelBuilderBehaviorBase() +{ + +} + +StandardModelBuilderBehavior::StandardModelBuilderBehavior() + : ModelBuilderBehaviorBase() + , m_metadataCreationEnabled(true) + , m_singleColumnEnabled(false) +{ + +} + +StandardModelBuilderBehavior::~StandardModelBuilderBehavior() +{ + +} + +QList Zanshin::Test::StandardModelBuilderBehavior::expandTodo(const T &t) +{ + KCalCore::Todo::Ptr todo(new KCalCore::Todo); + todo->setUid(t.uid); + todo->setRelatedTo(t.parentUid); + todo->setSummary(t.summary); + todo->setCategories(t.categories); + todo->setCompleted(t.state==Done); + if (t.dueDate.isValid()) { + todo->setDtDue(t.dueDate); + todo->setHasDueDate(true); + todo->setAllDay(true); + } + + if (t.todoTag==ProjectTag) { + todo->addComment("X-Zanshin-Project"); + } + + Akonadi::Item it(t.id); + it.setParentCollection(Akonadi::Collection(t.parentId)); + it.setMimeType("application/x-vnd.akonadi.calendar.todo"); + it.setPayload(todo); + + + QList row; + + QStandardItem *item = new QStandardItem(t.summary); + item->setData(QVariant::fromValue(it), Akonadi::EntityTreeModel::ItemRole); + addTodoMetadata(item, t); + row << item; + + if (m_singleColumnEnabled) { + return row; + } + + item = new QStandardItem(t.parentUid); + item->setData(QVariant::fromValue(it), Akonadi::EntityTreeModel::ItemRole); + addTodoMetadata(item, t); + row << item; + + item = new QStandardItem(t.categories.join(", ")); + item->setData(QVariant::fromValue(it), Akonadi::EntityTreeModel::ItemRole); + addTodoMetadata(item, t); + row << item; + + item = new QStandardItem(t.dueDate.toString()); + item->setData(QVariant::fromValue(it), Akonadi::EntityTreeModel::ItemRole); + addTodoMetadata(item, t); + row << item; + + item = new QStandardItem; + item->setData(QVariant::fromValue(it), Akonadi::EntityTreeModel::ItemRole); + addTodoMetadata(item, t); + row << item; + + return row; +} + +QList Zanshin::Test::StandardModelBuilderBehavior::expandCategory(const Cat &c) +{ + QList row; + + QStandardItem *item = new QStandardItem(c.name); + addCategoryMetadata(item); + row << item; + + if (m_singleColumnEnabled) { + return row; + } + + for (int i=0; i<4; i++) { + item = new QStandardItem; + addCategoryMetadata(item); + row << item; + } + + return row; +} + +QList Zanshin::Test::StandardModelBuilderBehavior::expandCollection(const C &c) +{ + Akonadi::Collection col(c.id); + col.setName(c.name); + + QList row; + + QStandardItem *item = new QStandardItem(c.name); + item->setData(QVariant::fromValue(col), Akonadi::EntityTreeModel::CollectionRole); + addCollectionMetadata(item); + row << item; + + if (m_singleColumnEnabled) { + return row; + } + + for (int i=0; i<4; i++) { + item = new QStandardItem; + item->setData(QVariant::fromValue(col), Akonadi::EntityTreeModel::CollectionRole); + row << item; + } + + return row; +} + +QList Zanshin::Test::StandardModelBuilderBehavior::expandVirtual(const V &virt) +{ + QList row; + + QStandardItem *item = new QStandardItem(virt.name); + item->setData(QVariant::fromValue(virt.name), Qt::DisplayRole); + + if (m_metadataCreationEnabled) { + int type = 0; + switch (virt.type) { + case Inbox: + case NoCategory: + type = Zanshin::Inbox; + break; + case Categories: + type = Zanshin::CategoryRoot; + break; + } + + item->setData(QVariant::fromValue(type), Zanshin::ItemTypeRole); + } + + row << item; + + if (m_singleColumnEnabled) { + return row; + } + + for (int i=0; i<4; i++) { + item = new QStandardItem; + row << item; + } + + return row; +} + +void Zanshin::Test::StandardModelBuilderBehavior::setMetadataCreationEnabled(bool enabled) +{ + m_metadataCreationEnabled = enabled; +} + +bool Zanshin::Test::StandardModelBuilderBehavior::isMetadataCreationEnabled() +{ + return m_metadataCreationEnabled; +} + +void Zanshin::Test::StandardModelBuilderBehavior::addTodoMetadata(QStandardItem *item, const T &todo) +{ + if (m_metadataCreationEnabled) { + item->setData(todo.parentUid, Zanshin::ParentUidRole); + item->setData(todo.uid, Zanshin::UidRole); + item->setData(todo.categories, Zanshin::CategoriesRole); + if (todo.todoTag==ProjectTag || todo.todoTag==ReferencedTag) { + item->setData(QVariant::fromValue((int)Zanshin::ProjectTodo), Zanshin::ItemTypeRole); + } else { + item->setData(QVariant::fromValue((int)Zanshin::StandardTodo), Zanshin::ItemTypeRole); + } + } +} + +void Zanshin::Test::StandardModelBuilderBehavior::addCategoryMetadata(QStandardItem *item) +{ + if (m_metadataCreationEnabled) { + item->setData(QVariant::fromValue((int)Zanshin::Category), Zanshin::ItemTypeRole); + } +} + +void Zanshin::Test::StandardModelBuilderBehavior::addCollectionMetadata(QStandardItem *item) +{ + if (m_metadataCreationEnabled) { + item->setData(QVariant::fromValue((int)Zanshin::Collection), Zanshin::ItemTypeRole); + } +} + +void Zanshin::Test::StandardModelBuilderBehavior::setSingleColumnEnabled(bool singleColumnEnabled) +{ + m_singleColumnEnabled = singleColumnEnabled; +} + +bool Zanshin::Test::StandardModelBuilderBehavior::isSingleColumnEnabled() +{ + return m_singleColumnEnabled; +} diff -Nru zanshin-0.1+svn1006410/tests/testlib/modelbuilderbehavior.h zanshin-0.2.0/tests/testlib/modelbuilderbehavior.h --- zanshin-0.1+svn1006410/tests/testlib/modelbuilderbehavior.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modelbuilderbehavior.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,82 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_MODELBUILDERBEHAVIOR_H +#define ZANSHIN_TESTLIB_MODELBUILDERBEHAVIOR_H + +#include +#include + +#include +#include +#include +#include + +namespace Zanshin +{ +namespace Test +{ + + +class ModelBuilderBehaviorBase +{ +public: + ModelBuilderBehaviorBase(); + virtual ~ModelBuilderBehaviorBase(); + + virtual QList expandTodo(const T &todo) = 0; + virtual QList expandCollection(const C &collection) = 0; + virtual QList expandCategory(const Cat &category) = 0; + virtual QList expandVirtual(const V &virt) = 0; +}; + +class StandardModelBuilderBehavior : public ModelBuilderBehaviorBase +{ +public: + StandardModelBuilderBehavior(); + virtual ~StandardModelBuilderBehavior(); + + virtual QList expandTodo(const T &todo); + virtual QList expandCollection(const C &collection); + virtual QList expandCategory(const Cat &category); + virtual QList expandVirtual(const V &virt); + + void setMetadataCreationEnabled(bool enabled); + bool isMetadataCreationEnabled(); + + void setSingleColumnEnabled(bool singleColumnEnabled); + bool isSingleColumnEnabled(); + +private: + void addTodoMetadata(QStandardItem*, const T &todo); + void addCategoryMetadata(QStandardItem*); + void addCollectionMetadata(QStandardItem*); + bool m_metadataCreationEnabled; + bool m_singleColumnEnabled; +}; + +} // namespace Test +} // namespace Zanshin + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/modelnode.cpp zanshin-0.2.0/tests/testlib/modelnode.cpp --- zanshin-0.1+svn1006410/tests/testlib/modelnode.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modelnode.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,87 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "modelnode.h" + +#include + +using namespace Zanshin::Test; + +ModelNode::ModelNode() +{ +} + +ModelNode::ModelNode(const C &collection, const Indent &indent) + : m_entity(QVariant::fromValue(collection)), + m_indent(indent) +{ +} + +ModelNode::ModelNode(const T &todo, const Indent &indent) + : m_entity(QVariant::fromValue(todo)), + m_indent(indent) +{ +} + +ModelNode::ModelNode(const Cat &category, const Indent &indent) + : m_entity(QVariant::fromValue(category)), + m_indent(indent) +{ +} + +ModelNode::ModelNode(const V &virt, const Indent &indent) + : m_entity(QVariant::fromValue(virt)), + m_indent(indent) +{ +} + +quint64 ModelNode::indent() const +{ + return m_indent.size; +} + +QVariant ModelNode::entity() const +{ + return m_entity; +} + +ModelNode operator+(const Indent& indent, const C &collection) +{ + return ModelNode(collection, indent); +} + +ModelNode operator+(const Indent& indent, const T &todo) +{ + return ModelNode(todo, indent); +} + +ModelNode operator+(const Indent& indent, const Cat &category) +{ + return ModelNode(category, indent); +} + +ModelNode operator+(const Indent& indent, const V &virt) +{ + return ModelNode(virt, indent); +} + diff -Nru zanshin-0.1+svn1006410/tests/testlib/modelnode.h zanshin-0.2.0/tests/testlib/modelnode.h --- zanshin-0.1+svn1006410/tests/testlib/modelnode.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modelnode.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,68 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_MODELNODE_H +#define ZANSHIN_TESTLIB_MODELNODE_H + +#include + +#include +#include +#include +#include +#include + +namespace Zanshin +{ +namespace Test +{ + +class ModelNode +{ +public: + ModelNode(); + ModelNode(const C &collection, const Indent &indent = Indent()); + ModelNode(const T &todo, const Indent &indent = Indent()); + ModelNode(const Cat &category, const Indent &indent = Indent()); + ModelNode(const V &virt, const Indent &indent = Indent()); + + quint64 indent() const; + QVariant entity() const; + +private: + QVariant m_entity; + Indent m_indent; +}; + +} // namespace Test +} // namespace Zanshin + +Zanshin::Test::ModelNode operator+(const Zanshin::Test::Indent& indent, const Zanshin::Test::C &collection); +Zanshin::Test::ModelNode operator+(const Zanshin::Test::Indent& indent, const Zanshin::Test::T &todo); +Zanshin::Test::ModelNode operator+(const Zanshin::Test::Indent& indent, const Zanshin::Test::Cat &category); +Zanshin::Test::ModelNode operator+(const Zanshin::Test::Indent& indent, const Zanshin::Test::V &virt); + +Q_DECLARE_METATYPE(Zanshin::Test::ModelNode) + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/modelpath.cpp zanshin-0.2.0/tests/testlib/modelpath.cpp --- zanshin-0.1+svn1006410/tests/testlib/modelpath.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modelpath.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,140 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "modelpath.h" + +#include + +using namespace Zanshin::Test; + +ModelPath::ModelPath() +{ +} + +ModelPath::ModelPath(const C &collection) +{ + m_path << QVariant::fromValue(collection); +} + +ModelPath::ModelPath(const T &todo) +{ + m_path << QVariant::fromValue(todo); +} + +ModelPath::ModelPath(const Cat &category) +{ + m_path << QVariant::fromValue(category); +} + +ModelPath::ModelPath(const V &virt) +{ + m_path << QVariant::fromValue(virt); +} + +ModelPath::ModelPath(const C &collection1, const C &collection2) +{ + m_path << QVariant::fromValue(collection1) + << QVariant::fromValue(collection2); +} + +ModelPath::ModelPath(const C &collection, const T &todo) +{ + m_path << QVariant::fromValue(collection) + << QVariant::fromValue(todo); +} + +ModelPath::ModelPath(const Cat &category1, const Cat &category2) +{ + m_path << QVariant::fromValue(category1) + << QVariant::fromValue(category2); +} + +ModelPath::ModelPath(const Cat &category, const T &todo) +{ + m_path << QVariant::fromValue(category) + << QVariant::fromValue(todo); +} + +ModelPath::ModelPath(const ModelPath &path, const C &collection) +{ + m_path = path.m_path; + m_path << QVariant::fromValue(collection); +} + +ModelPath::ModelPath(const ModelPath &path, const T &todo) +{ + m_path = path.m_path; + m_path << QVariant::fromValue(todo); +} + +ModelPath::ModelPath(const ModelPath &path, const Cat &category) +{ + m_path = path.m_path; + m_path << QVariant::fromValue(category); +} + +ModelPath::ModelPath(const ModelPath &path1, const ModelPath &path2) +{ + m_path = path1.m_path + path2.m_path; +} + +ModelPath operator%(const C &collection1, const C &collection2) +{ + return ModelPath(collection1, collection2); +} + +ModelPath operator%(const C &collection, const T &todo) +{ + return ModelPath(collection, todo); +} + +ModelPath operator%(const Cat &category1, const Cat &category2) +{ + return ModelPath(category1, category2); +} + +ModelPath operator%(const Cat &category, const T &todo) +{ + return ModelPath(category, todo); +} + +ModelPath operator%(const ModelPath &path, const C &collection) +{ + return ModelPath(path, collection); +} + +ModelPath operator%(const ModelPath &path, const T &todo) +{ + return ModelPath(path, todo); +} + +ModelPath operator%(const ModelPath &path, const Cat &category) +{ + return ModelPath(path, category); +} + +ModelPath operator%(const ModelPath &path1, const ModelPath &path2) +{ + return ModelPath(path1, path2); +} + diff -Nru zanshin-0.1+svn1006410/tests/testlib/modelpath.h zanshin-0.2.0/tests/testlib/modelpath.h --- zanshin-0.1+svn1006410/tests/testlib/modelpath.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modelpath.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,83 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_MODELPATH_H +#define ZANSHIN_TESTLIB_MODELPATH_H + +#include + +#include +#include +#include +#include + +namespace Zanshin +{ +namespace Test +{ + +class ModelUtils; + +class ModelPath +{ +public: + typedef QList List; + + ModelPath(); + + ModelPath(const C &collection); + ModelPath(const T &todo); + ModelPath(const Cat &category); + ModelPath(const V &virt); + ModelPath(const C &collection1, const C &collection2); + ModelPath(const C &collection, const T &todo); + ModelPath(const Cat &category1, const Cat &category2); + ModelPath(const Cat &category, const T &todo); + ModelPath(const ModelPath &path, const C &collection); + ModelPath(const ModelPath &path, const T &todo); + ModelPath(const ModelPath &path, const Cat &category); + ModelPath(const ModelPath &path1, const ModelPath &path2); + +private: + friend class ModelUtils; + + QVariantList m_path; +}; + +} // namespace Test +} // namespace Zanshin + +Zanshin::Test::ModelPath operator%(const Zanshin::Test::C &collection1, const Zanshin::Test::C &collection2); +Zanshin::Test::ModelPath operator%(const Zanshin::Test::C &collection, const Zanshin::Test::T &todo); +Zanshin::Test::ModelPath operator%(const Zanshin::Test::Cat &category1, const Zanshin::Test::Cat &category2); +Zanshin::Test::ModelPath operator%(const Zanshin::Test::Cat &category, const Zanshin::Test::T &todo); +Zanshin::Test::ModelPath operator%(const Zanshin::Test::ModelPath &path, const Zanshin::Test::C &collection); +Zanshin::Test::ModelPath operator%(const Zanshin::Test::ModelPath &path, const Zanshin::Test::T &todo); +Zanshin::Test::ModelPath operator%(const Zanshin::Test::ModelPath &path, const Zanshin::Test::Cat &category); +Zanshin::Test::ModelPath operator%(const Zanshin::Test::ModelPath &path1, const Zanshin::Test::ModelPath &path2); + +Q_DECLARE_METATYPE(Zanshin::Test::ModelPath) +Q_DECLARE_METATYPE(Zanshin::Test::ModelPath::List) + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/modelstructure.cpp zanshin-0.2.0/tests/testlib/modelstructure.cpp --- zanshin-0.1+svn1006410/tests/testlib/modelstructure.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modelstructure.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,158 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "modelstructure.h" + +#include + +using namespace Zanshin::Test; + +ModelStructure::ModelStructure() + : m_latestNode(0), + m_latestIndent(0) +{ +} + +ModelStructure::~ModelStructure() +{ + qDeleteAll(m_roots); +} + +ModelStructure::ModelStructure(const ModelNode &node) + : m_latestNode(new ModelStructureTreeNode(node)), + m_latestIndent(0) +{ + Q_ASSERT(node.indent()==0); + m_roots << m_latestNode; +} + +static void _structureRecursiveReplay(ModelStructure &target, const QList nodes) +{ + foreach (ModelStructureTreeNode *node, nodes) { + target << node->modelNode(); + _structureRecursiveReplay(target, node->children()); + } +} + +ModelStructure::ModelStructure(const ModelStructure &other) + : m_latestNode(0), + m_latestIndent(0) +{ + _structureRecursiveReplay(*this, other.m_roots); +} + +ModelStructure &ModelStructure::operator=(const ModelStructure &other) +{ + ModelStructure s(other); + std::swap(m_latestNode, s.m_latestNode); + std::swap(m_latestIndent, s.m_latestIndent); + return *this; +} + +ModelStructure &ModelStructure::operator<<(const ModelNode &node) +{ + Q_ASSERT((node.indent()<=m_latestIndent) + || (node.indent()==m_latestIndent+1)); + + if (node.indent()==m_latestIndent) { + if (m_latestNode) { + m_latestNode = new ModelStructureTreeNode(node, m_latestNode->parent()); + } else { + m_latestNode = new ModelStructureTreeNode(node, 0); + } + + if (m_latestNode->parent()==0) { + m_roots << m_latestNode; + } + + } else if (node.indent()==m_latestIndent+1) { + m_latestNode = new ModelStructureTreeNode(node, m_latestNode); + + } else /*if (node.indent()<=m_latestIndent-1)*/ { + quint64 tmpIndent = m_latestIndent; + ModelStructureTreeNode *parent = m_latestNode->parent(); + while (tmpIndent>node.indent()) { + tmpIndent--; + parent = parent->parent(); + } + + m_latestNode = new ModelStructureTreeNode(node, parent); + + if (parent==0) { // We created a root + m_roots << m_latestNode; + } + } + + m_latestIndent = node.indent(); + + return *this; +} + +void ModelStructure::clear() +{ + qDeleteAll(m_roots); + m_roots.clear(); + m_latestIndent = 0; + m_latestNode = 0; +} + +ModelStructureTreeNode::ModelStructureTreeNode() + : m_modelNode(ModelNode()), + m_parent(0) +{ +} + +ModelStructureTreeNode::ModelStructureTreeNode(const ModelNode &node, ModelStructureTreeNode *parent) + : m_modelNode(node), + m_parent(parent) +{ + if (m_parent) { + m_parent->m_children << this; + } +} + +ModelStructureTreeNode::~ModelStructureTreeNode() +{ + QList children = m_children; + + if (m_parent) { + m_parent->m_children.removeAll(this); + } + + qDeleteAll(children); +} + +ModelNode ModelStructureTreeNode::modelNode() const +{ + return m_modelNode; +} + +ModelStructureTreeNode *ModelStructureTreeNode::parent() const +{ + return m_parent; +} + +QList ModelStructureTreeNode::children() const +{ + return m_children; +} diff -Nru zanshin-0.1+svn1006410/tests/testlib/modelstructure.h zanshin-0.2.0/tests/testlib/modelstructure.h --- zanshin-0.1+svn1006410/tests/testlib/modelstructure.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modelstructure.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,91 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_MODELSTRUCTURE_H +#define ZANSHIN_TESTLIB_MODELSTRUCTURE_H + +#include + +#include + +namespace Zanshin +{ +namespace Test +{ + +class ModelUtils; +class ModelStructureTreeNode; + +class ModelStructure +{ +public: + typedef QList List; + + ModelStructure(); + ~ModelStructure(); + ModelStructure(const ModelNode &node); + + ModelStructure(const ModelStructure &other); + ModelStructure &operator=(const ModelStructure &other); + + ModelStructure &operator<<(const ModelNode &node); + + void clear(); + +private: + friend class ModelUtils; + + QList m_roots; + + ModelStructureTreeNode *m_latestNode; + quint64 m_latestIndent; +}; + +class ModelStructureTreeNode +{ +public: + ModelStructureTreeNode(const ModelNode &node, ModelStructureTreeNode *parent = 0); + ~ModelStructureTreeNode(); + + ModelNode modelNode() const; + + ModelStructureTreeNode *parent() const; + QList children() const; + +private: + ModelStructureTreeNode(); + ModelStructureTreeNode(const ModelStructureTreeNode &); + + ModelNode m_modelNode; + ModelStructureTreeNode *m_parent; + QList m_children; +}; + +} // namespace Test +} // namespace Zanshin + +Q_DECLARE_METATYPE(Zanshin::Test::ModelStructure) +Q_DECLARE_METATYPE(Zanshin::Test::ModelStructure::List) + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/modeltest.cpp zanshin-0.2.0/tests/testlib/modeltest.cpp --- zanshin-0.1+svn1006410/tests/testlib/modeltest.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modeltest.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,564 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include "modeltest.h" + +#include +#undef Q_ASSERT +#define Q_ASSERT QVERIFY + +Q_DECLARE_METATYPE ( QModelIndex ) + +/*! + Connect to all of the models signals. Whenever anything happens recheck everything. +*/ +ModelTest::ModelTest ( QAbstractItemModel *_model, QObject *parent ) : QObject ( parent ), model ( _model ), fetchingMore ( false ) +{ + Q_ASSERT ( model ); + + connect ( model, SIGNAL (columnsAboutToBeInserted(QModelIndex,int,int)), + this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (columnsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (columnsInserted(QModelIndex,int,int)), + this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (columnsRemoved(QModelIndex,int,int)), + this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (dataChanged(QModelIndex,QModelIndex)), + this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (headerDataChanged(Qt::Orientation,int,int)), + this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (layoutAboutToBeChanged()), this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (layoutChanged()), this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (modelReset()), this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (rowsAboutToBeInserted(QModelIndex,int,int)), + this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (rowsInserted(QModelIndex,int,int)), + this, SLOT (runAllTests()) ); + connect ( model, SIGNAL (rowsRemoved(QModelIndex,int,int)), + this, SLOT (runAllTests()) ); + + // Special checks for inserting/removing + connect ( model, SIGNAL (layoutAboutToBeChanged()), + this, SLOT (layoutAboutToBeChanged()) ); + connect ( model, SIGNAL (layoutChanged()), + this, SLOT (layoutChanged()) ); + + connect ( model, SIGNAL (rowsAboutToBeInserted(QModelIndex,int,int)), + this, SLOT (rowsAboutToBeInserted(QModelIndex,int,int)) ); + connect ( model, SIGNAL (rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT (rowsAboutToBeRemoved(QModelIndex,int,int)) ); + connect ( model, SIGNAL (rowsInserted(QModelIndex,int,int)), + this, SLOT (rowsInserted(QModelIndex,int,int)) ); + connect ( model, SIGNAL (rowsRemoved(QModelIndex,int,int)), + this, SLOT (rowsRemoved(QModelIndex,int,int)) ); + + runAllTests(); +} + +void ModelTest::runAllTests() +{ + if ( fetchingMore ) + return; + nonDestructiveBasicTest(); + rowCount(); + columnCount(); + hasIndex(); + index(); + parent(); + data(); +} + +/*! + nonDestructiveBasicTest tries to call a number of the basic functions (not all) + to make sure the model doesn't outright segfault, testing the functions that makes sense. +*/ +void ModelTest::nonDestructiveBasicTest() +{ + Q_ASSERT ( model->buddy ( QModelIndex() ) == QModelIndex() ); + model->canFetchMore ( QModelIndex() ); + Q_ASSERT ( model->columnCount ( QModelIndex() ) >= 0 ); + Q_ASSERT ( model->data ( QModelIndex() ) == QVariant() ); + fetchingMore = true; + model->fetchMore ( QModelIndex() ); + fetchingMore = false; + Qt::ItemFlags flags = model->flags ( QModelIndex() ); + Q_ASSERT ( flags == Qt::ItemIsDropEnabled || flags == 0 ); + model->hasChildren ( QModelIndex() ); + model->hasIndex ( 0, 0 ); + model->headerData ( 0, Qt::Horizontal ); + model->index ( 0, 0 ); + model->itemData ( QModelIndex() ); + QVariant cache; + model->match ( QModelIndex(), -1, cache ); + model->mimeTypes(); + Q_ASSERT ( model->parent ( QModelIndex() ) == QModelIndex() ); + Q_ASSERT ( model->rowCount() >= 0 ); + QVariant variant; + model->setData ( QModelIndex(), variant, -1 ); + model->setHeaderData ( -1, Qt::Horizontal, QVariant() ); + model->setHeaderData ( 999999, Qt::Horizontal, QVariant() ); + QMap roles; + model->sibling ( 0, 0, QModelIndex() ); + model->span ( QModelIndex() ); + model->supportedDropActions(); +} + +/*! + Tests model's implementation of QAbstractItemModel::rowCount() and hasChildren() + + Models that are dynamically populated are not as fully tested here. + */ +void ModelTest::rowCount() +{ +// qDebug() << "rc"; + // check top row + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + int rows = model->rowCount ( topIndex ); + Q_ASSERT ( rows >= 0 ); + if ( rows > 0 ) + Q_ASSERT ( model->hasChildren ( topIndex ) == true ); + + QModelIndex secondLevelIndex = model->index ( 0, 0, topIndex ); + if ( secondLevelIndex.isValid() ) { // not the top level + // check a row count where parent is valid + rows = model->rowCount ( secondLevelIndex ); + Q_ASSERT ( rows >= 0 ); + if ( rows > 0 ) + Q_ASSERT ( model->hasChildren ( secondLevelIndex ) == true ); + } + + // The models rowCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::columnCount() and hasChildren() + */ +void ModelTest::columnCount() +{ + // check top row + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + Q_ASSERT ( model->columnCount ( topIndex ) >= 0 ); + + // check a column count where parent is valid + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + if ( childIndex.isValid() ) + Q_ASSERT ( model->columnCount ( childIndex ) >= 0 ); + + // columnCount() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::hasIndex() + */ +void ModelTest::hasIndex() +{ +// qDebug() << "hi"; + // Make sure that invalid values returns an invalid index + Q_ASSERT ( model->hasIndex ( -2, -2 ) == false ); + Q_ASSERT ( model->hasIndex ( -2, 0 ) == false ); + Q_ASSERT ( model->hasIndex ( 0, -2 ) == false ); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + // check out of bounds + Q_ASSERT ( model->hasIndex ( rows, columns ) == false ); + Q_ASSERT ( model->hasIndex ( rows + 1, columns + 1 ) == false ); + + if ( rows > 0 ) + Q_ASSERT ( model->hasIndex ( 0, 0 ) == true ); + + // hasIndex() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::index() + */ +void ModelTest::index() +{ +// qDebug() << "i"; + // Make sure that invalid values returns an invalid index + Q_ASSERT ( model->index ( -2, -2 ) == QModelIndex() ); + Q_ASSERT ( model->index ( -2, 0 ) == QModelIndex() ); + Q_ASSERT ( model->index ( 0, -2 ) == QModelIndex() ); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + if ( rows == 0 ) + return; + + // Catch off by one errors + Q_ASSERT ( model->index ( rows, columns ) == QModelIndex() ); + Q_ASSERT ( model->index ( 0, 0 ).isValid() == true ); + + // Make sure that the same index is *always* returned + QModelIndex a = model->index ( 0, 0 ); + QModelIndex b = model->index ( 0, 0 ); + Q_ASSERT ( a == b ); + + // index() is tested more extensively in checkChildren(), + // but this catches the big mistakes +} + +/*! + Tests model's implementation of QAbstractItemModel::parent() + */ +void ModelTest::parent() +{ +// qDebug() << "p"; + // Make sure the model wont crash and will return an invalid QModelIndex + // when asked for the parent of an invalid index. + Q_ASSERT ( model->parent ( QModelIndex() ) == QModelIndex() ); + + if ( model->rowCount() == 0 ) + return; + + // Column 0 | Column 1 | + // QModelIndex() | | + // \- topIndex | topIndex1 | + // \- childIndex | childIndex1 | + + // Common error test #1, make sure that a top level index has a parent + // that is a invalid QModelIndex. + QModelIndex topIndex = model->index ( 0, 0, QModelIndex() ); + Q_ASSERT ( model->parent ( topIndex ) == QModelIndex() ); + + // Common error test #2, make sure that a second level index has a parent + // that is the first level index. + if ( model->rowCount ( topIndex ) > 0 ) { + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + Q_ASSERT ( model->parent ( childIndex ) == topIndex ); + } + + // Common error test #3, the second column should NOT have the same children + // as the first column in a row. + // Usually the second column shouldn't have children. + QModelIndex topIndex1 = model->index ( 0, 1, QModelIndex() ); + if ( model->rowCount ( topIndex1 ) > 0 ) { + QModelIndex childIndex = model->index ( 0, 0, topIndex ); + QModelIndex childIndex1 = model->index ( 0, 0, topIndex1 ); + Q_ASSERT ( childIndex != childIndex1 ); + } + + // Full test, walk n levels deep through the model making sure that all + // parent's children correctly specify their parent. + checkChildren ( QModelIndex() ); +} + +/*! + Called from the parent() test. + + A model that returns an index of parent X should also return X when asking + for the parent of the index. + + This recursive function does pretty extensive testing on the whole model in an + effort to catch edge cases. + + This function assumes that rowCount(), columnCount() and index() already work. + If they have a bug it will point it out, but the above tests should have already + found the basic bugs because it is easier to figure out the problem in + those tests then this one. + */ +void ModelTest::checkChildren ( const QModelIndex &parent, int currentDepth ) +{ + // First just try walking back up the tree. + QModelIndex p = parent; + while ( p.isValid() ) + p = p.parent(); + + // For models that are dynamically populated + if ( model->canFetchMore ( parent ) ) { + fetchingMore = true; + model->fetchMore ( parent ); + fetchingMore = false; + } + + int rows = model->rowCount ( parent ); + int columns = model->columnCount ( parent ); + + if ( rows > 0 ) + Q_ASSERT ( model->hasChildren ( parent ) ); + + // Some further testing against rows(), columns(), and hasChildren() + Q_ASSERT ( rows >= 0 ); + Q_ASSERT ( columns >= 0 ); + if ( rows > 0 ) + Q_ASSERT ( model->hasChildren ( parent ) == true ); + + //qDebug() << "parent:" << model->data(parent).toString() << "rows:" << rows + // << "columns:" << columns << "parent column:" << parent.column(); + + Q_ASSERT ( model->hasIndex ( rows + 1, 0, parent ) == false ); + for ( int r = 0; r < rows; ++r ) { + if ( model->canFetchMore ( parent ) ) { + fetchingMore = true; + model->fetchMore ( parent ); + fetchingMore = false; + } + Q_ASSERT ( model->hasIndex ( r, columns + 1, parent ) == false ); + for ( int c = 0; c < columns; ++c ) { + Q_ASSERT ( model->hasIndex ( r, c, parent ) == true ); + QModelIndex index = model->index ( r, c, parent ); + // rowCount() and columnCount() said that it existed... + Q_ASSERT ( index.isValid() == true ); + + // index() should always return the same index when called twice in a row + QModelIndex modifiedIndex = model->index ( r, c, parent ); + Q_ASSERT ( index == modifiedIndex ); + + // Make sure we get the same index if we request it twice in a row + QModelIndex a = model->index ( r, c, parent ); + QModelIndex b = model->index ( r, c, parent ); + Q_ASSERT ( a == b ); + + // Some basic checking on the index that is returned + Q_ASSERT ( index.model() == model ); + Q_ASSERT ( index.row() == r ); + Q_ASSERT ( index.column() == c ); + // While you can technically return a QVariant usually this is a sign + // of an bug in data() Disable if this really is ok in your model. +// Q_ASSERT ( model->data ( index, Qt::DisplayRole ).isValid() == true ); + + // If the next test fails here is some somewhat useful debug you play with. + + if (model->parent(index) != parent) { + qDebug() << r << c << currentDepth << model->data(index).toString() + << model->data(parent).toString(); + qDebug() << index << parent << model->parent(index); +// And a view that you can even use to show the model. +// QTreeView view; +// view.setModel(model); +// view.show(); + } + + // Check that we can get back our real parent. +// qDebug() << model->parent ( index ) << parent ; + Q_ASSERT ( model->parent ( index ) == parent ); + + // recursively go down the children + if ( model->hasChildren ( index ) && currentDepth < 10 ) { + //qDebug() << r << c << "has children" << model->rowCount(index); + checkChildren ( index, ++currentDepth ); + }/* else { if (currentDepth >= 10) qDebug() << "checked 10 deep"; };*/ + + // make sure that after testing the children that the index doesn't change. + QModelIndex newerIndex = model->index ( r, c, parent ); + Q_ASSERT ( index == newerIndex ); + } + } +} + +/*! + Tests model's implementation of QAbstractItemModel::data() + */ +void ModelTest::data() +{ + // Invalid index should return an invalid qvariant + Q_ASSERT ( !model->data ( QModelIndex() ).isValid() ); + + if ( model->rowCount() == 0 ) + return; + + // A valid index should have a valid QVariant data + Q_ASSERT ( model->index ( 0, 0 ).isValid() ); + + // shouldn't be able to set data on an invalid index + Q_ASSERT ( model->setData ( QModelIndex(), QLatin1String ( "foo" ), Qt::DisplayRole ) == false ); + + // General Purpose roles that should return a QString + QVariant variant = model->data ( model->index ( 0, 0 ), Qt::ToolTipRole ); + if ( variant.isValid() ) { + Q_ASSERT ( qVariantCanConvert ( variant ) ); + } + variant = model->data ( model->index ( 0, 0 ), Qt::StatusTipRole ); + if ( variant.isValid() ) { + Q_ASSERT ( qVariantCanConvert ( variant ) ); + } + variant = model->data ( model->index ( 0, 0 ), Qt::WhatsThisRole ); + if ( variant.isValid() ) { + Q_ASSERT ( qVariantCanConvert ( variant ) ); + } + + // General Purpose roles that should return a QSize + variant = model->data ( model->index ( 0, 0 ), Qt::SizeHintRole ); + if ( variant.isValid() ) { + Q_ASSERT ( qVariantCanConvert ( variant ) ); + } + + // General Purpose roles that should return a QFont + QVariant fontVariant = model->data ( model->index ( 0, 0 ), Qt::FontRole ); + if ( fontVariant.isValid() ) { + Q_ASSERT ( qVariantCanConvert ( fontVariant ) ); + } + + // Check that the alignment is one we know about + QVariant textAlignmentVariant = model->data ( model->index ( 0, 0 ), Qt::TextAlignmentRole ); + if ( textAlignmentVariant.isValid() ) { + int alignment = textAlignmentVariant.toInt(); + Q_ASSERT ( alignment == ( alignment & ( Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask ) ) ); + } + + // General Purpose roles that should return a QColor + QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole ); + if ( colorVariant.isValid() ) { + Q_ASSERT ( qVariantCanConvert ( colorVariant ) ); + } + + colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); + if ( colorVariant.isValid() ) { + Q_ASSERT ( qVariantCanConvert ( colorVariant ) ); + } + + // Check that the "check state" is one we know about. + QVariant checkStateVariant = model->data ( model->index ( 0, 0 ), Qt::CheckStateRole ); + if ( checkStateVariant.isValid() ) { + int state = checkStateVariant.toInt(); + Q_ASSERT ( state == Qt::Unchecked || + state == Qt::PartiallyChecked || + state == Qt::Checked ); + } +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsInserted() + */ +void ModelTest::rowsAboutToBeInserted ( const QModelIndex &parent, int start, int end ) +{ + Q_UNUSED(end); +// qDebug() << "rowsAboutToBeInserted" << "start=" << start << "end=" << end << "parent=" << model->data ( parent ).toString() +// << "current count of parent=" << model->rowCount ( parent ); // << "display of last=" << model->data( model->index(start-1, 0, parent) ); +// qDebug() << model->index(start-1, 0, parent) << model->data( model->index(start-1, 0, parent) ); + Changing c; + c.parent = parent; + c.oldSize = model->rowCount ( parent ); + c.last = model->data ( model->index ( start - 1, 0, parent ) ); + c.next = model->data ( model->index ( start, 0, parent ) ); + insert.push ( c ); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeInserted() + */ +void ModelTest::rowsInserted ( const QModelIndex & parent, int start, int end ) +{ + Changing c = insert.pop(); + Q_ASSERT ( c.parent == parent ); +// qDebug() << "rowsInserted" << "start=" << start << "end=" << end << "oldsize=" << c.oldSize +// << "parent=" << model->data ( parent ).toString() << "current rowcount of parent=" << model->rowCount ( parent ); + +// for (int ii=start; ii <= end; ii++) +// { +// qDebug() << "itemWasInserted:" << ii << model->data ( model->index ( ii, 0, parent )); +// } +// qDebug(); + + Q_ASSERT ( c.oldSize + ( end - start + 1 ) == model->rowCount ( parent ) ); + Q_ASSERT ( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); + + if (c.next != model->data(model->index(end + 1, 0, c.parent))) { + qDebug() << start << end; + for (int i=0; i < model->rowCount(); ++i) + qDebug() << model->index(i, 0).data().toString(); + qDebug() << c.next << model->data(model->index(end + 1, 0, c.parent)); + } + + Q_ASSERT ( c.next == model->data ( model->index ( end + 1, 0, c.parent ) ) ); +} + +void ModelTest::layoutAboutToBeChanged() +{ + for ( int i = 0; i < qBound ( 0, model->rowCount(), 100 ); ++i ) + changing.append ( QPersistentModelIndex ( model->index ( i, 0 ) ) ); +} + +void ModelTest::layoutChanged() +{ + for ( int i = 0; i < changing.count(); ++i ) { + QPersistentModelIndex p = changing[i]; + Q_ASSERT ( p == model->index ( p.row(), p.column(), p.parent() ) ); + } + changing.clear(); +} + +/*! + Store what is about to be inserted to make sure it actually happens + + \sa rowsRemoved() + */ +void ModelTest::rowsAboutToBeRemoved ( const QModelIndex &parent, int start, int end ) +{ + Changing c; + c.parent = parent; + c.oldSize = model->rowCount ( parent ); + c.last = model->data ( model->index ( start - 1, 0, parent ) ); + c.next = model->data ( model->index ( end + 1, 0, parent ) ); + remove.push ( c ); +} + +/*! + Confirm that what was said was going to happen actually did + + \sa rowsAboutToBeRemoved() + */ +void ModelTest::rowsRemoved ( const QModelIndex & parent, int start, int end ) +{ + Changing c = remove.pop(); + Q_ASSERT ( c.parent == parent ); + Q_ASSERT ( c.oldSize - ( end - start + 1 ) == model->rowCount ( parent ) ); + Q_ASSERT ( c.last == model->data ( model->index ( start - 1, 0, c.parent ) ) ); + Q_ASSERT ( c.next == model->data ( model->index ( start, 0, c.parent ) ) ); +} + + diff -Nru zanshin-0.1+svn1006410/tests/testlib/modeltest.h zanshin-0.2.0/tests/testlib/modeltest.h --- zanshin-0.1+svn1006410/tests/testlib/modeltest.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modeltest.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef MODELTEST_H +#define MODELTEST_H + +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT + +public: + ModelTest( QAbstractItemModel *model, QObject *parent = 0 ); + +private Q_SLOTS: + void nonDestructiveBasicTest(); + void rowCount(); + void columnCount(); + void hasIndex(); + void index(); + void parent(); + void data(); + +protected Q_SLOTS: + void runAllTests(); + void layoutAboutToBeChanged(); + void layoutChanged(); + void rowsAboutToBeInserted( const QModelIndex &parent, int start, int end ); + void rowsInserted( const QModelIndex & parent, int start, int end ); + void rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ); + void rowsRemoved( const QModelIndex & parent, int start, int end ); + +private: + void checkChildren( const QModelIndex &parent, int currentDepth = 0 ); + + QAbstractItemModel *model; + + struct Changing { + QModelIndex parent; + int oldSize; + QVariant last; + QVariant next; + }; + QStack insert; + QStack remove; + + bool fetchingMore; + + QList changing; +}; + +#endif diff -Nru zanshin-0.1+svn1006410/tests/testlib/modelutils.cpp zanshin-0.2.0/tests/testlib/modelutils.cpp --- zanshin-0.1+svn1006410/tests/testlib/modelutils.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modelutils.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,183 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "../../src/globaldefs.h" +#include "modelutils.h" + +#include "modelbuilderbehavior.h" + +using namespace Zanshin::Test; + +QModelIndex ModelUtils::locateItem(QAbstractItemModel *model, const ModelPath &root) +{ + QVariantList path = root.m_path; + QModelIndex index; + + foreach (const QVariant &pathPart, path) { + bool found = false; + + for (int row=0; rowrowCount(index); row++) { + QModelIndex childIndex = model->index(row, 0, index); + QVariant variant = model->data(childIndex, TestDslRole); + + if (!variant.isValid()) { + variant = model->data(childIndex, Zanshin::ItemTypeRole); + } else if (variant.userType()!=pathPart.userType()) { + continue; + } + + if (variant.canConvert()) { + T t1 = variant.value(); + T t2 = pathPart.value(); + if (t1==t2) { + found = true; + } + } else if (variant.canConvert()) { + C c1 = variant.value(); + C c2 = pathPart.value(); + if (c1==c2) { + found = true; + } + } else if (variant.canConvert()) { + Cat cat1 = variant.value(); + Cat cat2 = pathPart.value(); + if (cat1==cat2) { + found = true; + } + } else if (variant.canConvert() && pathPart.canConvert()) { + Zanshin::ItemType type = (Zanshin::ItemType)variant.toInt(); + V v2 = pathPart.value(); + if ((type==Zanshin::Inbox && (v2.type==Inbox || v2.type==NoCategory)) + || (type==Zanshin::CategoryRoot && v2.type==Categories)) { + found = true; + } + } else if (variant.canConvert()) { + V v1 = variant.value(); + V v2 = pathPart.value(); + if (v1==v2) { + found = true; + } + } + + if (found) { + index = childIndex; + break; + } + } + Q_ASSERT(found); + } + + return index; +} + + + +void ModelUtils::create(QStandardItemModel *model, + const ModelStructure &structure, + const ModelPath &root, + ModelBuilderBehaviorBase *behavior) +{ + QModelIndex rootIndex = locateItem(model, root); + QStandardItem *rootItem = model->itemFromIndex(rootIndex); + + bool mustDeleteBehavior = false; + if (behavior==0) { + behavior = new StandardModelBuilderBehavior; + mustDeleteBehavior = true; + } + + QList< QList > rows = createItems(structure, behavior); + + if (mustDeleteBehavior) { + delete behavior; + behavior = 0; + } + + foreach (const QList &row, rows) { + if (rootItem) { + rootItem->appendRow(row); + } else { + model->appendRow(row); + } + } +} + +QList< QList > ModelUtils::createItems(const ModelStructure &structure, ModelBuilderBehaviorBase *behavior) +{ + QList< QList > items; + + foreach (ModelStructureTreeNode* node, structure.m_roots) { + items << createItem(node, behavior); + } + + return items; +} + +QList ModelUtils::createItem(const ModelStructureTreeNode *node, ModelBuilderBehaviorBase *behavior) +{ + QList row; + + ModelNode modelNode = node->modelNode(); + QVariant variant = modelNode.entity(); + + if (variant.canConvert()) { + C c = variant.value(); + row = behavior->expandCollection(c); + } else if (variant.canConvert()) { + T t = variant.value(); + row = behavior->expandTodo(t); + } else if (variant.canConvert()) { + Cat cat = variant.value(); + row = behavior->expandCategory(cat); + } else { + V v = variant.value(); + row = behavior->expandVirtual(v); + } + + foreach (QStandardItem *item, row) { + item->setData(variant, TestDslRole); + } + + foreach (ModelStructureTreeNode* child, node->children()) { + row.first()->appendRow(createItem(child, behavior)); + } + + return row; +} + +bool ModelUtils::destroy(QAbstractItemModel *model, const ModelPath::List &paths) +{ + foreach (ModelPath path, paths) { + if (!destroy(model, path)) { + return false; + } + } + return true; +} + +bool ModelUtils::destroy(QAbstractItemModel *model, const ModelPath &path) +{ + QModelIndex index = ModelUtils::locateItem(model, path); + return model->removeRow(index.row(), index.parent()); +} + diff -Nru zanshin-0.1+svn1006410/tests/testlib/modelutils.h zanshin-0.2.0/tests/testlib/modelutils.h --- zanshin-0.1+svn1006410/tests/testlib/modelutils.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/modelutils.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,68 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_MODELUTILS_H +#define ZANSHIN_TESTLIB_MODELUTILS_H + +#include +#include + +#include +#include + +#include + +namespace Zanshin +{ +namespace Test +{ + +enum Roles { + TestDslRole = Akonadi::EntityTreeModel::UserRole + 101, + UserRole = Akonadi::EntityTreeModel::UserRole + 200 +}; + +class ModelBuilderBehaviorBase; + +class ModelUtils +{ +public: + static void create(QStandardItemModel *model, + const ModelStructure &structure, + const ModelPath &root = ModelPath(), + ModelBuilderBehaviorBase *behavior = 0); + + static QModelIndex locateItem(QAbstractItemModel *model, const ModelPath &root); + static bool destroy(QAbstractItemModel *model, const ModelPath::List &paths); + static bool destroy(QAbstractItemModel *model, const ModelPath &path); + +private: + static QList< QList > createItems(const ModelStructure &structure, ModelBuilderBehaviorBase *behavior); + static QList createItem(const ModelStructureTreeNode *node, ModelBuilderBehaviorBase *behavior); +}; + +} // namespace Test +} // namespace Zanshin + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/qcompare.cpp zanshin-0.2.0/tests/testlib/qcompare.cpp --- zanshin-0.1+svn1006410/tests/testlib/qcompare.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/qcompare.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,254 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "qcompare.h" + +#include + +#include "modelbuilderbehavior.h" + +#include +#include +#include + +Q_DECLARE_METATYPE(QModelIndexList) + +static QList s_itemRoles; + +QList QTest::getEvaluatedItemRoles() +{ + return s_itemRoles; +} + +void QTest::setEvaluatedItemRoles(const QList &roles) +{ + s_itemRoles = roles; +} + +template +QString toString(const T &t) +{ + return t.toString(); +} + +template<> +QString toString(const QString &s) +{ + return s; +} + +QString toString(const char *s) +{ + return QString::fromUtf8(s); +} + +template<> +QString toString(const Akonadi::Item &item) +{ + return QString("ID: %1, RemoteID: %2, UID: %3, \"%4\"") + .arg(item.id()) + .arg(item.remoteId()) + .arg("uid?") + .arg("summary?"); +} + +template<> +QString toString(const Akonadi::Collection &collection) +{ + return QString("ID: %1, RemoteID: %2, \"%3\"") + .arg(collection.id()) + .arg(collection.remoteId()) + .arg(collection.name()); +} + +template<> +QString toString(const QModelIndexList &list) +{ + QString result = "("; + + foreach (const QModelIndex &index, list) { + result+= "[0x" + QString::number(index.internalId(), 16) + ": " + + QString::number(index.row()) + ", " + + QString::number(index.column()) + ", " + + "0x" + QString::number(index.parent().internalId(), 16) + "], "; + } + result.chop(2); + + result+= ')'; + return result; +} + +template +static bool dumpError(const QString &message, + const T1 &actualValue, const T2 &expectedValue, + const char *actual, const char *expected, + const char *file, int line) +{ + QString str1 = toString(actualValue); + char *data1 = new char[str1.toLocal8Bit().size() + 1]; + strcpy(data1, str1.toLocal8Bit().data()); + + QString str2 = toString(expectedValue); + char *data2 = new char[str2.toLocal8Bit().size() + 1]; + strcpy(data2, str2.toLocal8Bit().data()); + + return QTest::compare_helper(false, message.toLocal8Bit().constData(), + data1, data2, + actual, expected, file, line); +} + +template +bool compareVariantsAs(const QVariant &value1, const QVariant &value2, int depth, int role, + const char *actual, const char *expected, + const char *file, int line) +{ + if (!value1.canConvert()) { + return true; + } + + T t1 = value1.value(); + T t2 = value2.value(); + + if (t1==t2) { + return true; + } + + return dumpError(QString("Different values found at depth %1 for role %2").arg(depth).arg(role), + t1, t2, actual, expected, file, line); +} + +template<> +bool compareVariantsAs(const QVariant &value1, const QVariant &value2, int depth, int role, + const char *actual, const char *expected, + const char *file, int line) +{ + if (value1.type()==QVariant::UserType) { + return true; + } + + if (value1==value2) { + return true; + } + + return dumpError(QString("Different values found at depth %1 for role %2").arg(depth).arg(role), + value1, value2, actual, expected, file, line); +} + +static bool compareItems(const QModelIndex &index1, const QModelIndex &index2, int depth, + const char *actual, const char *expected, + const char *file, int line) +{ + foreach (int role, s_itemRoles) { + QVariant value1 = index1.data(role); + QVariant value2 = index2.data(role); + + if (value1.userType()!=value2.userType()) { + return dumpError(QString("Different types found at depth %1 for role %2").arg(depth).arg(role), + value1.typeName(), + value2.typeName(), + actual, expected, file, line); + } + + if (!compareVariantsAs(value1, value2, depth, role, actual, expected, file, line) + || !compareVariantsAs(value1, value2, depth, role, actual, expected, file, line) + || !compareVariantsAs(value1, value2, depth, role, actual, expected, file, line) + || !compareVariantsAs(value1, value2, depth, role, actual, expected, file, line)) { + return false; + } + } + + return true; +} + +static bool compareBranches(const QAbstractItemModel *model1, const QAbstractItemModel *model2, + const QModelIndex &root1, const QModelIndex &root2, int depth, + const char *actual, const char *expected, + const char *file, int line) +{ + if (root1.isValid()==root2.isValid()) { + if (root1.isValid() // No need to compare invalid indexes + && !compareItems(root1, root2, depth, actual, expected, file, line)) { + return false; + } + } else { // One valid, not the other! + return dumpError(QString("One branch valid while the other is invalid at depth ")+QString::number(depth), + root1.isValid() ? "true" : "false", + root2.isValid() ? "true" : "false", + actual, expected, file, line); + } + + int rowCount1 = model1->rowCount(root1); + int rowCount2 = model2->rowCount(root2); + + if (rowCount1!=rowCount2) { + return dumpError(QString("Different row counts at depth ")+QString::number(depth), + QString::number(rowCount1), + QString::number(rowCount2), + actual, expected, file, line); + } + + if (rowCount1==0) { + // Nothing to see, we're done + return true; + } + + int columnCount1 = model1->columnCount(root1); + int columnCount2 = model2->columnCount(root2); + + if (columnCount1!=columnCount2) { + return dumpError(QString("Different column counts at depth ")+QString::number(depth), + QString::number(columnCount1), + QString::number(columnCount2), + actual, expected, file, line); + } + + for (int row = 0; rowindex(row, col, root1); + QModelIndex child2 = model2->index(row, col, root2); + + if (!compareBranches(model1, model2, child1, child2, depth+1, + actual, expected, file, line)) { + return false; + } + } + } + + return true; +} + +bool QTest::qCompare(const QAbstractItemModel &model1, const QAbstractItemModel &model2, + const char *actual, const char *expected, + const char *file, int line) +{ + bool result = compareBranches(&model1, &model2, + QModelIndex(), QModelIndex(), 0, + actual, expected, file, line); + + if (result) { + return compare_helper(true, "COMPARE()", file, line); + } else { + return false; + } +} + diff -Nru zanshin-0.1+svn1006410/tests/testlib/qcompare.h zanshin-0.2.0/tests/testlib/qcompare.h --- zanshin-0.1+svn1006410/tests/testlib/qcompare.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/qcompare.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,56 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_QCOMPARE_H +#define ZANSHIN_TESTLIB_QCOMPARE_H + +#include +#include +#include + + +namespace QTest +{ + +QList getEvaluatedItemRoles(); +void setEvaluatedItemRoles(const QList &roles); + +bool qCompare(const QAbstractItemModel &model1, const QAbstractItemModel &model2, + const char *actual, const char *expected, + const char *file, int line); + +template +bool qCompare(const T1 &t1, const T2 &t2, + const char *actual, const char *expected, + const char *file, int line) +{ + const QAbstractItemModel *model1 = &t1; + const QAbstractItemModel *model2 = &t2; + + return qCompare(*model1, *model2, actual, expected, file, line); +} + +} + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/t.cpp zanshin-0.2.0/tests/testlib/t.cpp --- zanshin-0.1+svn1006410/tests/testlib/t.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/t.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,71 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "t.h" + +#include + +using namespace Zanshin::Test; + +T::T() +{ +} + +T::T(qint64 i, + qint64 pI, + const QString &u, + const QString &pU, + const QString &s, + TodoState st, + TodoTag tag, + const QString &d, + const QString &c) + : id(i), + parentId(pI), + uid(u), + parentUid(pU), + state(st), + todoTag(tag), + summary(s) +{ + if (!d.isEmpty()) { + dueDate = KDateTime::fromString(d); + Q_ASSERT(dueDate.isValid()); + } + + categories = c.split(QRegExp("\\s*,\\s*"), QString::SkipEmptyParts); +} + +bool T::operator==(const T &other) const +{ + return id==other.id + && parentId==other.parentId + && uid==other.uid + && parentUid==other.parentUid + && state==other.state + && todoTag==other.todoTag + && summary==other.summary + && dueDate==other.dueDate + && categories==other.categories; +} + diff -Nru zanshin-0.1+svn1006410/tests/testlib/testlib.h zanshin-0.2.0/tests/testlib/testlib.h --- zanshin-0.1+svn1006410/tests/testlib/testlib.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/testlib.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,33 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_TESTLIB_H +#define ZANSHIN_TESTLIB_TESTLIB_H + +#include +#include +#include +#include + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/tests/CMakeLists.txt zanshin-0.2.0/tests/testlib/tests/CMakeLists.txt --- zanshin-0.1+svn1006410/tests/testlib/tests/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/tests/CMakeLists.txt 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,19 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..) + +MACRO(ZANSHIN_TESTLIB_UNIT_TESTS) + FOREACH(_testname ${ARGN}) + kde4_add_unit_test(${_testname} TESTNAME zanshin-testlib-${_testname} ${_testname}.cpp) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}") + target_link_libraries(${_testname} ${KDE4_KDEUI_LIBS} ${KDEPIMLIBS_AKONADI_LIBS} + ${KDEPIMLIBS_KCALCORE_LIBS} ${QT_QTTEST_LIBRARY} + zanshin_testlib + ) + ENDFOREACH(_testname) +ENDMACRO(ZANSHIN_TESTLIB_UNIT_TESTS) + +########### automated tests ############### + +ZANSHIN_TESTLIB_UNIT_TESTS( + modelstructuretest +) + diff -Nru zanshin-0.1+svn1006410/tests/testlib/tests/modelstructuretest.cpp zanshin-0.2.0/tests/testlib/tests/modelstructuretest.cpp --- zanshin-0.1+svn1006410/tests/testlib/tests/modelstructuretest.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/tests/modelstructuretest.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,124 @@ +/* This file is part of Zanshin Todo. + + Copyright 2008-2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include + +#include +#include + +using namespace Zanshin::Test; + +class ModelStructureTest : public QObject +{ + Q_OBJECT +private slots: + void shouldCreateEmptyModels() + { + //GIVEN + QStandardItemModel model; + ModelStructure structure; + + //WHEN + ModelUtils::create(&model, structure); + + //THEN + QCOMPARE(model.rowCount(), 0); + QCOMPARE(model.columnCount(), 0); + } + + void shouldCreateTreeStructures() + { + //GIVEN + QStandardItemModel model; + ModelStructure structure; + + C c1(1, 0, "c1"); + C c2(2, 0, "c2"); + C c3(3, 2, "c3"); + C c4(4, 3, "c4"); + Cat c5("category"); + T t1(1, 3, "t1", QString(), "t1"); + T t2(2, 3, "t2", QString(), "t2"); + T t3(3, 4, "t3", QString(), "t3"); + T t4(4, 4, "t4", QString(), "t4"); + T t5(5, 5, "t5", QString(), "t5"); + + //WHEN + structure << c1 + << c2 // Make sure we can have several roots + << _+c3 + << __+t1 + << ___+t2 // To force c2 to be grand-grand-parent + << _+c4 // Make sure we can chain a second branch at same depth + << __+t3 + << __+t4 + << t5 // Make sure we can have roots after branches + << c5; + + ModelUtils::create(&model, structure); + + //THEN + // Verify the roots + QCOMPARE(model.rowCount(), 4); + QCOMPARE(model.data(model.index(0, 0)).toString(), QString("c1")); + QCOMPARE(model.data(model.index(1, 0)).toString(), QString("c2")); + QCOMPARE(model.data(model.index(2, 0)).toString(), QString("t5")); + QCOMPARE(model.data(model.index(3, 0)).toString(), QString("category")); + + // Verify c1 leaf + QModelIndex c1Idx = model.index(0, 0); + QCOMPARE(model.rowCount(c1Idx), 0); + + // Verify c2 branch + QModelIndex c2Idx = model.index(1, 0); + QCOMPARE(model.rowCount(c2Idx), 2); + QCOMPARE(model.data(model.index(0, 0, c2Idx)).toString(), QString("c3")); + QCOMPARE(model.data(model.index(1, 0, c2Idx)).toString(), QString("c4")); + + // Verify c3 branch + QModelIndex c3Idx = model.index(0, 0, c2Idx); + QCOMPARE(model.rowCount(c3Idx), 1); + QCOMPARE(model.data(model.index(0, 0, c3Idx)).toString(), QString("t1")); + + QModelIndex t1Idx = model.index(0, 0, c3Idx); + QCOMPARE(model.rowCount(t1Idx), 1); + QCOMPARE(model.data(model.index(0, 0, t1Idx)).toString(), QString("t2")); + QCOMPARE(model.rowCount(model.index(0, 0, t1Idx)), 0); // t2 is a leaf + + // Verify c4 branch + QModelIndex c4Idx = model.index(1, 0, c2Idx); + QCOMPARE(model.rowCount(c4Idx), 2); + QCOMPARE(model.data(model.index(0, 0, c4Idx)).toString(), QString("t3")); + QCOMPARE(model.rowCount(model.index(0, 0, c4Idx)), 0); // t3 is a leaf + QCOMPARE(model.data(model.index(1, 0, c4Idx)).toString(), QString("t4")); + QCOMPARE(model.rowCount(model.index(1, 0, c4Idx)), 0); // t4 is a leaf + + // Verify t5 leaf + QModelIndex t5Idx = model.index(2, 0); + QCOMPARE(model.rowCount(t5Idx), 0); + } +}; + +QTEST_KDEMAIN(ModelStructureTest, GUI) + +#include "modelstructuretest.moc" diff -Nru zanshin-0.1+svn1006410/tests/testlib/t.h zanshin-0.2.0/tests/testlib/t.h --- zanshin-0.1+svn1006410/tests/testlib/t.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/t.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,84 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_T_H +#define ZANSHIN_TESTLIB_T_H + +#include +#include +#include + +namespace Zanshin +{ +namespace Test +{ + +enum TodoState { + InProgress = 0, + Done +}; + +enum TodoTag { + NoTag = 0, + ProjectTag, + ReferencedTag +}; + +struct T // Stands for todo +{ +public: + typedef QList List; + + T(); + T(qint64 id, + qint64 parentId, + const QString &uid, + const QString &parentUid, + const QString &summary, + TodoState state = Done, + TodoTag todoTag = NoTag, + const QString &date = QString(), + const QString &categories = QString()); + + bool operator==(const T &other) const; + + qint64 id; + qint64 parentId; + QString uid; + QString parentUid; + TodoState state; + TodoTag todoTag; + QString summary; + KDateTime dueDate; + QStringList categories; +}; + +} // namespace Test +} // namespace Zanshin + +Q_DECLARE_METATYPE(Zanshin::Test::T) +Q_DECLARE_METATYPE(Zanshin::Test::T::List) +Q_DECLARE_METATYPE(Zanshin::Test::TodoState) + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/testlib/v.cpp zanshin-0.2.0/tests/testlib/v.cpp --- zanshin-0.1+svn1006410/tests/testlib/v.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/v.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,54 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include "v.h" + +#include + +using namespace Zanshin::Test; + +V::V() +{ +} + +V::V(VirtualType t) + : type(t) +{ + switch (t) { + case Inbox: + name = "Inbox"; + break; + case NoCategory: + name = "No Context"; + break; + case Categories: + name = "Contexts"; + break; + } +} + +bool V::operator==(const V &other) const +{ + return type==other.type; +} + diff -Nru zanshin-0.1+svn1006410/tests/testlib/v.h zanshin-0.2.0/tests/testlib/v.h --- zanshin-0.1+svn1006410/tests/testlib/v.h 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/testlib/v.h 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,63 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#ifndef ZANSHIN_TESTLIB_V_H +#define ZANSHIN_TESTLIB_V_H + +#include +#include + +namespace Zanshin +{ +namespace Test +{ + +enum VirtualType { + Inbox=0, + NoCategory, + Categories +}; + +struct V // Stands for collection +{ +public: + typedef QList List; + + + V(); + explicit V(VirtualType t); + + bool operator==(const V &other) const; + + VirtualType type; + QString name; +}; + +} // namespace Test +} // namespace Zanshin + +Q_DECLARE_METATYPE(Zanshin::Test::V) +Q_DECLARE_METATYPE(Zanshin::Test::V::List) + +#endif + diff -Nru zanshin-0.1+svn1006410/tests/todocategoriesmodelspec.cpp zanshin-0.2.0/tests/todocategoriesmodelspec.cpp --- zanshin-0.1+svn1006410/tests/todocategoriesmodelspec.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/todocategoriesmodelspec.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,677 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Kevin Ottens + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include + +#include "categorymanager.h" +#include "todocategoriesmodel.h" +#include "todometadatamodel.h" +#include "testlib/testlib.h" +#include "testlib/mockmodel.h" +#include "testlib/modelbuilderbehavior.h" + +#include +#include + +using namespace Zanshin::Test; + +Q_DECLARE_METATYPE(QModelIndex) + +class TodoCategoriesModelSpec : public QObject +{ + Q_OBJECT +private slots: + void initTestCase() + { + qRegisterMetaType(); + + QList roles; + roles << Qt::DisplayRole + << Akonadi::EntityTreeModel::ItemRole + << Akonadi::EntityTreeModel::CollectionRole; + + QTest::setEvaluatedItemRoles(roles); + } + + void shouldRememberItsSourceModel() + { + //GIVEN + QStandardItemModel baseModel; + TodoCategoriesModel proxyModel; + ModelTest mt(&proxyModel); + + //WHEN + proxyModel.setSourceModel(&baseModel); + + //THEN + QVERIFY(proxyModel.sourceModel() == &baseModel); + } + + void shouldReactToSourceRowRemovals_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "itemsToRemove" ); + QTest::addColumn( "outputStructure" ); + + // Base items + V nocat(NoCategory); + V cats(Categories); + C c1(1, 0, "c1"); + C c2(2, 0, "c2"); + Cat cat1("cat1"); + Cat cat2("cat2"); + T t1(3, 1, "t1", QString(), "t1", InProgress, ProjectTag, QString(), "cat1"); + T t2(4, 1, "t2", "t1", "t2", InProgress, NoTag, QString(), "cat1, cat2"); + T t3(5, 2, "t3", QString(), "t3", InProgress, ProjectTag, QString()); + T t4(6, 2, "t4", QString(), "t4", InProgress, NoTag, QString(), "cat1"); + T t5(6, 2, "t5", QString(), "t5", InProgress, NoTag); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2 + << c2 + << _+t3 + << _+t4 + << _+t5; + + + ModelPath::List itemsToRemove; + itemsToRemove << c1 % t2; + + ModelStructure outputStructure; + outputStructure << nocat + << _+t5 + << cats + << _+cat1 + << __+t4 + << _+cat2; + + QTest::newRow( "delete todo" ) << sourceStructure << itemsToRemove << outputStructure; + + + itemsToRemove.clear(); + itemsToRemove << c1; + + QTest::newRow( "delete collection" ) << sourceStructure << itemsToRemove << outputStructure; + } + + void shouldReactToSourceRowRemovals() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + //Kick up category manager + CategoryManager::instance().setModel(&source); + + ModelUtils::create(&source, sourceStructure); + + //create categoriesModel + TodoCategoriesModel categoriesModel; + ModelTest t1(&categoriesModel); + + categoriesModel.setSourceModel(&source); + + //WHEN + QFETCH(ModelPath::List, itemsToRemove); + ModelUtils::destroy(&source, itemsToRemove); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(categoriesModel, output); + } + + void shouldReparentBasedOnCategories_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "outputStructure" ); + + // Base items + V nocat(NoCategory); + V cats(Categories); + C c1(1, 0, "c1"); + C c2(2, 0, "c2"); + Cat cat1("cat1"); + Cat cat2("cat2"); + T t1(3, 1, "t1", QString(), "t1", InProgress, ProjectTag, QString(), "cat1"); + T t2(4, 1, "t2", "t1", "t2", InProgress, NoTag, QString(), "cat1, cat2"); + T t3(5, 2, "t3", QString(), "t3", InProgress, ProjectTag, QString()); + T t4(6, 2, "t4", QString(), "t4", InProgress, NoTag, QString(), "cat1"); + T t5(6, 2, "t5", QString(), "t5", InProgress, NoTag); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2 + << c2 + << _+t3 + << _+t4 + << _+t5; + + + ModelPath::List itemsToRemove; + itemsToRemove << c1 % t2; + + ModelStructure outputStructure; + outputStructure << nocat + << _+t5 + << cats + << _+cat1 + << __+t2 + << __+t4 + << _+cat2 + << __+t2; + + QTest::newRow( "nominal case" ) << sourceStructure << outputStructure; + } + + void shouldReparentBasedOnCategories() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + //Kick up category manager + CategoryManager::instance().setModel(&source); + ModelUtils::create(&source, sourceStructure); + + //WHEN + //create categoriesModel + TodoCategoriesModel categoriesModel; + ModelTest t1(&categoriesModel); + + categoriesModel.setSourceModel(&source); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(categoriesModel, output); + } + + void shouldReactToSourceRowInserts_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "sourceParentPath" ); + QTest::addColumn( "sourceSiblingPaths" ); + QTest::addColumn( "insertedStructure" ); + QTest::addColumn( "outputStructure" ); + + // Base items + V nocat(NoCategory); + V cats(Categories); + C c1(1, 0, "c1"); + C c2(2, 0, "c2"); + Cat cat1("cat1"); + Cat cat2("cat2"); + T t1(3, 1, "t1", QString(), "t1", InProgress, ProjectTag, QString(), "cat1"); + T t2(4, 1, "t2", "t1", "t2", InProgress, NoTag, QString(), "cat1, cat2"); + T t3(5, 2, "t3", QString(), "t3", InProgress, ProjectTag, QString()); + T t4(6, 2, "t4", QString(), "t4", InProgress, NoTag, QString(), "cat1"); + T t5(6, 2, "t5", QString(), "t5", InProgress, NoTag); + T t6(7, 1, "t6", QString(), "t6", InProgress, NoTag, QString(), "cat2"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t6 + << c2 + << _+t3 + << _+t4 + << _+t5; + + + ModelPath sourceParentPath = c1; + ModelPath::List sourceSiblingPaths; + sourceSiblingPaths << c1 % t6 << c2 % t4; + + ModelStructure insertedStructure; + insertedStructure << t2; + + ModelStructure outputStructure; + outputStructure << nocat + << _+t5 + << cats + << _+cat2 + << __+t6 + << __+t2 + << _+cat1 + << __+t4 + << __+t2; + + QTest::newRow( "add todo with several categories" ) << sourceStructure << sourceParentPath + << sourceSiblingPaths << insertedStructure + << outputStructure; + } + + void shouldReactToSourceRowInserts() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + //Kick up category manager + CategoryManager::instance().setModel(&source); + ModelUtils::create(&source, sourceStructure); + + //create treeModel + TodoCategoriesModel categoriesModel; + ModelTest t1(&categoriesModel); + + categoriesModel.setSourceModel(&source); + + // What row number will we expect? + QFETCH(ModelPath::List, sourceSiblingPaths); + + QModelIndexList parentIndexes; + QList expectedRows; + + foreach (const ModelPath &sourceSiblingPath, sourceSiblingPaths) { + QModelIndex sourceSibling = ModelUtils::locateItem(&source, sourceSiblingPath); + QModelIndexList proxySiblings = categoriesModel.mapFromSourceAll(sourceSibling); + Q_ASSERT(proxySiblings.size()==1); + parentIndexes << proxySiblings.first().parent(); + expectedRows << categoriesModel.rowCount(parentIndexes.last()); + } + + //WHEN + QFETCH(ModelPath, sourceParentPath); + QFETCH(ModelStructure, insertedStructure); + + // Collect data to ensure we signalled the outside properly + QSignalSpy aboutToInsertSpy(&categoriesModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy insertSpy(&categoriesModel, SIGNAL(rowsInserted(QModelIndex,int,int))); + + ModelUtils::create(&source, insertedStructure, sourceParentPath); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(categoriesModel, output); + + QCOMPARE(aboutToInsertSpy.size(), parentIndexes.count()); + QCOMPARE(insertSpy.size(), parentIndexes.count()); + + for (int i=0; i(), parentIndex); + QCOMPARE(aboutToInsertSpy.at(i).at(1).toInt(), expectedRow); + QCOMPARE(aboutToInsertSpy.at(i).at(2).toInt(), expectedRow); + + QCOMPARE(insertSpy.at(i).at(0).value(), parentIndex); + QCOMPARE(insertSpy.at(i).at(1).toInt(), expectedRow); + QCOMPARE(insertSpy.at(i).at(2).toInt(), expectedRow); + } + } + + void shouldReactToSourceDataChanges_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "itemToChange" ); + QTest::addColumn( "value" ); + QTest::addColumn( "role" ); + QTest::addColumn( "outputStructure" ); + + // Base items + V nocat(NoCategory); + V cats(Categories); + C c1(1, 0, "c1"); + Cat cat1("cat1"); + Cat cat2("cat2"); + T t1(3, 1, "t1", QString(), "t1", InProgress, NoTag, QString(), "cat1"); + T t2(4, 1, "t2", QString(), "t2", InProgress, NoTag, QString(), "cat1"); + T t3(5, 1, "t3", QString(), "t3"); + T t4(6, 1, "t4", QString(), "t4", InProgress, NoTag, QString(), "cat1, cat2"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2; + + ModelPath itemToChange = c1 % t1; + QVariant value = Zanshin::ProjectTodo; + int role = Zanshin::ItemTypeRole; + + ModelStructure outputStructure; + outputStructure << nocat + << cats + << _+cat1 + << __+t2; + + QTest::newRow( "promote item" ) << sourceStructure << itemToChange + << value << role + << outputStructure; + + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t3; + + itemToChange = c1 % t3; + QStringList categoriesAdded; + categoriesAdded << "cat1"; + + value = categoriesAdded; + role = Zanshin::CategoriesRole; + + outputStructure.clear(); + outputStructure << nocat + << cats + << _+cat1 + << __+t1 + << __+t3; + + QTest::newRow( "Todo with no category which gets a new category" ) << sourceStructure << itemToChange + << value << role + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t4; + + itemToChange = c1 % t1; + categoriesAdded.clear(); + + value = categoriesAdded; + role = Zanshin::CategoriesRole; + + outputStructure.clear(); + outputStructure << nocat + << _+t1 + << cats + << _+cat1 + << __+t4 + << _+cat2 + << __+t4; + + QTest::newRow( "Todo with categories which looses all its categories" ) << sourceStructure << itemToChange + << value << role + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t4; + + itemToChange = c1 % t1; + categoriesAdded.clear(); + categoriesAdded << "cat1"; + categoriesAdded << "cat2"; + + value = categoriesAdded; + role = Zanshin::CategoriesRole; + + outputStructure.clear(); + outputStructure << nocat + << cats + << _+cat1 + << __+t1 + << __+t4 + << _+cat2 + << __+t4 + << __+t1; + + QTest::newRow( "Todo with categories which gets a new category" ) << sourceStructure << itemToChange + << value << role + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t4; + + itemToChange = c1 % t4; + categoriesAdded.clear(); + categoriesAdded << "cat1"; + + value = categoriesAdded; + role = Zanshin::CategoriesRole; + + outputStructure.clear(); + outputStructure << nocat + << cats + << _+cat1 + << __+t1 + << __+t4 + << _+cat2; + + QTest::newRow( "Todo with categories which looses one category" ) << sourceStructure << itemToChange + << value << role + << outputStructure; + } + + void shouldReactToSourceDataChanges() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + //Kick up category manager + CategoryManager::instance().setModel(&source); + ModelUtils::create(&source, sourceStructure); + + //create categoriesModel + TodoCategoriesModel categoriesModel; + ModelTest t1(&categoriesModel); + + categoriesModel.setSourceModel(&source); + + //WHEN + QFETCH(ModelPath, itemToChange); + QModelIndex index = ModelUtils::locateItem(&source, itemToChange); + + QFETCH(QVariant, value); + QFETCH(int, role); + + source.setData(index, value, role); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(categoriesModel, output); + } + + + void shouldNotDuplicateTodo_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "sourceParentPath" ); + QTest::addColumn( "insertedStructure" ); + QTest::addColumn( "outputStructure" ); + + // Base items + V nocat(NoCategory); + V cats(Categories); + C c1(1, 0, "c1"); + Cat cat1("cat1"); + Cat cat2("cat2"); + T t1(3, 1, "t1", QString(), "t1", InProgress, ProjectTag, QString(), "cat1"); + T t2(4, 1, "t2", "t1", "t2"); + T t3(5, 1, "t3", "t1", "t3", InProgress, ProjectTag, QString(), "cat2"); + T t4(6, 1, "t4", "t3", "t4"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1; + + ModelPath sourceParentPath = c1; + + ModelStructure insertedStructure; + insertedStructure << t2; + + ModelStructure outputStructure; + outputStructure << nocat + << cats + << _+cat1 + << __+t2; + + QTest::newRow( "add one todo" ) << sourceStructure + << sourceParentPath + << insertedStructure + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1; + + sourceParentPath = c1; + + insertedStructure.clear(); + insertedStructure << t2 + << t3 + << t4; + + outputStructure.clear(); + outputStructure << nocat + << cats + << _+cat1 + << __+t2 + << __+t4 + << _+cat2 + << __+t4; + + QTest::newRow( "add complexe structure" ) << sourceStructure + << sourceParentPath + << insertedStructure + << outputStructure; + } + void shouldNotDuplicateTodo() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel metadataModel; + ModelTest t1(&metadataModel); + + //Kick up category manager + CategoryManager::instance().setModel(&metadataModel); + + metadataModel.setSourceModel(&source); + + //create categoriesModel + TodoCategoriesModel categoriesModel; + ModelTest t2(&categoriesModel); + + categoriesModel.setSourceModel(&metadataModel); + + //WHEN + QFETCH(ModelPath, sourceParentPath); + QFETCH(ModelStructure, insertedStructure); + + ModelUtils::create(&source, insertedStructure, sourceParentPath, &behavior); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(categoriesModel, output); + } + + void shouldReactToModelReset_data() + { + QTest::addColumn("sourceStructure"); + + // Base items + V inbox(Inbox); + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag, QString(), "cat1"); + T t2(2, 1, "t2", "t1", "t2", InProgress, NoTag, QString(), "cat1, cat2"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2; + + QTest::newRow("clear model") << sourceStructure; + } + + + void shouldReactToModelReset() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + MockModel source; + + //Kick up category manager + CategoryManager::instance().setModel(&source); + + ModelUtils::create(&source, sourceStructure); + + //create categoriesModel + TodoCategoriesModel categoriesModel; + ModelTest t2(&categoriesModel); + + categoriesModel.setSourceModel(&source); + + //WHEN + source.clearData(); + + //THEN + MockModel output; + TodoCategoriesModel* categoriesModelOutput = new TodoCategoriesModel(); + + categoriesModelOutput->setSourceModel(&output); + + QAbstractItemModel* abstractModel = categoriesModelOutput; + QCOMPARE(categoriesModel, *abstractModel); + delete categoriesModelOutput; + } +}; + +QTEST_KDEMAIN(TodoCategoriesModelSpec, GUI) + +#include "todocategoriesmodelspec.moc" diff -Nru zanshin-0.1+svn1006410/tests/todometadatamodelspec.cpp zanshin-0.2.0/tests/todometadatamodelspec.cpp --- zanshin-0.1+svn1006410/tests/todometadatamodelspec.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/todometadatamodelspec.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,1514 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include + +#include + +#include "todometadatamodel.h" +#include "testlib/testlib.h" +#include "testlib/modelbuilderbehavior.h" + +#include + +using namespace Zanshin::Test; + +Q_DECLARE_METATYPE(QModelIndex) + +class TodoMetadataModelTest : public QObject +{ + Q_OBJECT +private: + static QModelIndexList collectChildren(QAbstractItemModel *model, const QModelIndex &root) + { + QModelIndexList result; + + for (int row=0; rowrowCount(root); row++) { + QModelIndex child = model->index(row, 0, root); + + if (!child.isValid()) { + return result; + } + + result+= collectChildren(model, child); + result << child; + } + + return result; + } + +private slots: + void initTestCase() + { + qRegisterMetaType(); + QList roles; + roles << Qt::DisplayRole + << Akonadi::EntityTreeModel::ItemRole + << Akonadi::EntityTreeModel::CollectionRole; + + QTest::setEvaluatedItemRoles(roles); + } + + void shouldRememberItsSourceModel() + { + //GIVEN + QStandardItemModel baseModel; + TodoMetadataModel todoMetadataModel(&baseModel); + ModelTest mt(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&baseModel); + + //THEN + QVERIFY(todoMetadataModel.sourceModel() == &baseModel); + } + + void shouldRetrieveItemState_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "state" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress); + T t2(2, 1, "t2", QString(), "t2", Done); + T t3(3, 1, "t3", QString(), "t3", Done, ProjectTag); + + ModelStructure sourceStructure; + sourceStructure << t1; + + QVariant state = Qt::Unchecked; + + QTest::newRow( "check state role in progress" ) << sourceStructure << state; + + sourceStructure.clear(); + sourceStructure << t2; + + state = Qt::Checked; + QTest::newRow( "check state role done" ) << sourceStructure << state; + + sourceStructure.clear(); + sourceStructure << t3; + + state = QVariant(); + QTest::newRow( "check state role on project" ) << sourceStructure << state; + + sourceStructure.clear(); + sourceStructure << c1; + + state = QVariant(); + QTest::newRow( "check state role on collection" ) << sourceStructure << state; + } + + void shouldRetrieveItemState() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(QVariant, state); + QModelIndex index = todoMetadataModel.index(0,0); + + QCOMPARE(index.data(Qt::CheckStateRole), state); + } + + void shouldRetrieveItemUid_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "uid" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1"); + + ModelStructure sourceStructure; + sourceStructure << c1; + + QString uid; + QTest::newRow( "check uid of a collection" ) << sourceStructure << uid; + + sourceStructure.clear(); + sourceStructure << t1; + + uid = "t1"; + QTest::newRow( "check uid for a todo" ) << sourceStructure << uid; + } + + void shouldRetrieveItemUid() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(QString, uid); + QModelIndex index = todoMetadataModel.index(0,0); + + QCOMPARE(index.data(Zanshin::UidRole).toString(), uid); + } + + void shouldRetrieveItemParentUid_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "parentUid" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1"); + T t2(2, 1, "t2", "t1", "t2"); + + ModelStructure sourceStructure; + sourceStructure << c1; + + QString parentUid; + QTest::newRow( "check without parent" ) << sourceStructure << parentUid; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1; + + QTest::newRow( "check parentUid with collection as parent" ) << sourceStructure << parentUid; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2; + + parentUid = "t1"; + + QTest::newRow( "check parentUid with todo as parent" ) << sourceStructure << parentUid; + } + + void shouldRetrieveItemParentUid() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(QString, parentUid); + QModelIndex index = todoMetadataModel.index(0,0); + while (todoMetadataModel.rowCount(index) > 0) { + index = todoMetadataModel.index(todoMetadataModel.rowCount(index)-1, 0, index); + } + + QCOMPARE(index.data(Zanshin::ParentUidRole).toString(), parentUid); + } + + void shouldRetrieveItemAncestors_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "ancestors" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1"); + T t2(2, 1, "t2", "t1", "t2"); + T t3(3, 2, "t3", "t2", "t3"); + T t4(4, 3, "t4", "t3", "t4"); + T t5(5, 4, "t5", "t4", "t5"); + T t6(6, 5, "t6", "t5", "t6"); + T t7(7, 6, "t7", "t6", "t7"); + T t8(8, 7, "t8", "t7", "t8"); + T t9(9, 8, "t9", "t8", "t9"); + + ModelStructure sourceStructure; + sourceStructure << c1; + + QStringList ancestors; + QTest::newRow( "check ancestor on collection" ) << sourceStructure << ancestors; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1; + + QTest::newRow( "check ancestor with collection as parent" ) << sourceStructure << ancestors; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2; + + ancestors << "t1"; + + QTest::newRow( "check ancestor with one level" ) << sourceStructure << ancestors; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3; + + ancestors.clear(); + ancestors << "t2" + << "t1"; + + QTest::newRow( "check ancestor with two level" ) << sourceStructure << ancestors; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3 + << _+t4 + << _+t5 + << _+t6 + << _+t7 + << _+t8 + << _+t9; + + ancestors.clear(); + ancestors << "t8" + << "t7" + << "t6" + << "t5" + << "t4" + << "t3" + << "t2" + << "t1"; + + QTest::newRow( "check ancestor with height level" ) << sourceStructure << ancestors; + } + + void shouldRetrieveItemAncestors() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(QStringList, ancestors); + QModelIndex index = todoMetadataModel.index(0,0); + while (todoMetadataModel.rowCount(index) > 0) { + index = todoMetadataModel.index(todoMetadataModel.rowCount(index)-1, 0, index); + } + + QCOMPARE(index.data(Zanshin::AncestorsUidRole).toStringList(), ancestors); + } + + void shouldRetrieveItemType_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "type" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + T t3(3, 2, "t3", "t3", "t3"); + V inbox(Inbox); + V categoryRoot(Categories); + V nocategory(NoCategory); + Cat category("cat"); + + ModelStructure sourceStructure; + sourceStructure << c1; + + int type = Zanshin::Collection; + + QTest::newRow( "check collection type" ) << sourceStructure << type; + + sourceStructure.clear(); + sourceStructure << t1; + + type = Zanshin::ProjectTodo; + + QTest::newRow( "check project type" ) << sourceStructure << type; + + sourceStructure.clear(); + sourceStructure << t2; + + type = Zanshin::StandardTodo; + + QTest::newRow( "check todo type" ) << sourceStructure << type; + + sourceStructure.clear(); + sourceStructure << t1 + << t2; + + type = Zanshin::ProjectTodo; + + QTest::newRow( "check todo type when it has a child" ) << sourceStructure << type; + + sourceStructure.clear(); + sourceStructure << inbox; + + type = 0; + + QTest::newRow( "check inbox type" ) << sourceStructure << type; + + sourceStructure.clear(); + sourceStructure << categoryRoot; + + type = 0; + + QTest::newRow( "check category root type" ) << sourceStructure << type; + + sourceStructure.clear(); + sourceStructure << nocategory; + + type = 0; + + QTest::newRow( "check no category type" ) << sourceStructure << type; + + sourceStructure.clear(); + sourceStructure << category; + + type = 0; + + QTest::newRow( "check category type" ) << sourceStructure << type; + } + + void shouldRetrieveItemType() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(int, type); + QModelIndex index = todoMetadataModel.index(0,0); + + QCOMPARE(index.data(Zanshin::ItemTypeRole).toInt(), type); + } + + void shouldRetrieveItemCategories_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "categories" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1"); + T t2(2, 1, "t2", "t1", "t2", Done, NoTag, QString(), "cat1"); + T t3(3, 2, "t3", "t2", "t3", Done, NoTag, QString(), "cat2"); + T t4(4, 3, "t4", "t3", "t4", Done, NoTag, QString(), "cat2"); + T t5(5, 4, "t5", "t4", "t5", Done, NoTag, QString(), "cat3"); + T t6(6, 5, "t6", "t5", "t6", Done, NoTag, QString(), "cat3"); + T t7(7, 6, "t7", "t6", "t7", Done, NoTag, QString(), "cat4"); + T t8(8, 7, "t8", "t7", "t8", Done, NoTag, QString(), "cat5"); + T t9(9, 8, "t9", "t8", "t9"); + + ModelStructure sourceStructure; + sourceStructure << c1; + + QStringList categories; + QTest::newRow( "get category list on collection" ) << sourceStructure << categories; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1; + + QTest::newRow( "check todo without category set" ) << sourceStructure << categories; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2; + + categories << "cat1"; + + QTest::newRow( "check todo with category set" ) << sourceStructure << categories; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3; + + categories.clear(); + categories << "cat1" + << "cat2"; + + QTest::newRow( "check todo with the different category set" ) << sourceStructure << categories; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3 + << _+t4; + + categories.clear(); + categories << "cat2" + << "cat1"; + + QTest::newRow( "check todo with the same category set" ) << sourceStructure << categories; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3 + << _+t4 + << _+t5 + << _+t6 + << _+t7 + << _+t8 + << _+t9; + + categories.clear(); + categories << "cat5" + << "cat4" + << "cat3" + << "cat2" + << "cat1"; + + QTest::newRow( "check todo with some category duplicate" ) << sourceStructure << categories; + } + + void shouldRetrieveItemCategories() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(QStringList, categories); + QModelIndex index = todoMetadataModel.index(0,0); + while (todoMetadataModel.rowCount(index) > 0) { + index = todoMetadataModel.index(todoMetadataModel.rowCount(index)-1, 0, index); + } + + QCOMPARE(index.data(Zanshin::CategoriesRole).toStringList(), categories); + } + + void shouldRetrieveItemAncestorsCategories_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "categories" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1"); + T t2(2, 1, "t2", "t1", "t2", Done, NoTag, QString(), "cat1"); + T t3(3, 2, "t3", "t2", "t3", Done, NoTag, QString(), "cat2"); + T t4(4, 3, "t4", "t3", "t4", Done, NoTag, QString(), "cat2"); + T t5(5, 4, "t5", "t4", "t5", Done, NoTag, QString(), "cat3"); + T t6(6, 5, "t6", "t5", "t6", Done, NoTag, QString(), "cat3"); + T t7(7, 6, "t7", "t6", "t7", Done, NoTag, QString(), "cat4"); + T t8(8, 7, "t8", "t7", "t8", Done, NoTag, QString(), "cat5"); + T t9(9, 8, "t9", "t8", "t9"); + + ModelStructure sourceStructure; + sourceStructure << c1; + + QStringList categories; + QTest::newRow( "get category list on collection" ) << sourceStructure << categories; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1; + + QTest::newRow( "check todo without category set" ) << sourceStructure << categories; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2; + + QTest::newRow( "check todo with category set" ) << sourceStructure << categories; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3; + + categories << "cat1"; + + QTest::newRow( "check todo with the different category set" ) << sourceStructure << categories; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3 + << _+t4; + + categories.clear(); + categories << "cat2" + << "cat1"; + + QTest::newRow( "check todo with the same category set" ) << sourceStructure << categories; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3 + << _+t4 + << _+t5 + << _+t6 + << _+t7 + << _+t8 + << _+t9; + + categories.clear(); + categories << "cat5" + << "cat4" + << "cat3" + << "cat2" + << "cat1"; + + QTest::newRow( "check todo with some category duplicate" ) << sourceStructure << categories; + } + + void shouldRetrieveItemAncestorsCategories() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(QStringList, categories); + QModelIndex index = todoMetadataModel.index(0,0); + while (todoMetadataModel.rowCount(index) > 0) { + index = todoMetadataModel.index(todoMetadataModel.rowCount(index)-1, 0, index); + } + + QCOMPARE(index.data(Zanshin::AncestorsCategoriesRole).toStringList(), categories); + } + + void shouldRetrieveItemDataType_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "column" ); + QTest::addColumn( "type" ); + + T t1(1, 0, "t1", QString(), "t1"); + + ModelStructure sourceStructure; + sourceStructure << t1; + + int column = 1; + int type = Zanshin::ProjectType; + + QTest::newRow( "check data type for column 1" ) << sourceStructure << column << type; + + column = 2; + type = Zanshin::CategoryType; + + QTest::newRow( "check data type for column 2" ) << sourceStructure << column << type; + + column = 0; + type = Zanshin::StandardType; + + QTest::newRow( "check data type for column 0" ) << sourceStructure << column << type; + + column = 3; + type = Zanshin::StandardType; + + QTest::newRow( "check data type for column 3" ) << sourceStructure << column << type; + } + + void shouldRetrieveItemDataType() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(int, column); + QFETCH(int, type); + QModelIndex index = todoMetadataModel.index(0, column); + QCOMPARE(index.data(Zanshin::DataTypeRole).toInt(), type); + } + + void shouldRetrieveItemChildUids_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "childUids" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1"); + T t2(2, 1, "t2", "t1", "t2"); + T t3(3, 1, "t3", "t1", "t3"); + T t4(4, 1, "t4", "t1", "t4"); + T t5(5, 4, "t5", "t4", "t5"); + + ModelStructure sourceStructure; + sourceStructure << c1; + + QStringList childUids; + QTest::newRow( "get child list on empty collection" ) << sourceStructure << childUids; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1; + + QTest::newRow( "get child list on collection with one child" ) << sourceStructure << childUids; + + sourceStructure.clear(); + sourceStructure << t1 + << t2; + + childUids << "t2"; + + QTest::newRow( "get child list on todo with one child" ) << sourceStructure << childUids; + + sourceStructure.clear(); + sourceStructure << t1 + << t2 + << t3 + << t4; + + childUids.clear(); + childUids << "t2" + << "t3" + << "t4"; + + QTest::newRow( "get child list on todo with several children" ) << sourceStructure << childUids; + + sourceStructure.clear(); + sourceStructure << t1 + << t2 + << t3 + << t4 + << t5; + + childUids.clear(); + childUids << "t2" + << "t3" + << "t4"; + + QTest::newRow( "check if the list is recursive" ) << sourceStructure << childUids; + + } + + void shouldRetrieveItemChildUids() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(QStringList, childUids); + QModelIndex index = todoMetadataModel.index(0,0); + + QCOMPARE(index.data(Zanshin::ChildUidsRole).toStringList(), childUids); + } + + void shouldRetrieveItemChildIndexes_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "childUids" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1"); + T t2(2, 1, "t2", "t1", "t2"); + T t3(3, 1, "t3", "t1", "t3"); + T t4(4, 1, "t4", "t1", "t4"); + T t5(5, 4, "t5", "t4", "t5"); + + ModelStructure sourceStructure; + sourceStructure << c1; + + QStringList childUids; + QTest::newRow( "get child list on empty collection" ) << sourceStructure << childUids; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1; + + QTest::newRow( "get child list on collection with one child" ) << sourceStructure << childUids; + + sourceStructure.clear(); + sourceStructure << t1 + << t2; + + childUids << "t2"; + + QTest::newRow( "get child list on todo with one child" ) << sourceStructure << childUids; + + sourceStructure.clear(); + sourceStructure << t1 + << t2 + << t3 + << t4; + + childUids.clear(); + childUids << "t2" + << "t3" + << "t4"; + + QTest::newRow( "get child list on todo with several children" ) << sourceStructure << childUids; + + sourceStructure.clear(); + sourceStructure << t1 + << t2 + << t3 + << t4 + << t5; + + childUids.clear(); + childUids << "t2" + << "t3" + << "t4"; + + QTest::newRow( "check if the list is recursive" ) << sourceStructure << childUids; + + } + + void shouldRetrieveItemChildIndexes() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(QStringList, childUids); + QModelIndex index = todoMetadataModel.index(0,0); + QModelIndexList children = index.data(Zanshin::ChildIndexesRole).value(); + + QCOMPARE(childUids.size(), children.size()); + + for (int i=0; i( "sourceStructure" ); + QTest::addColumn( "column" ); + QTest::addColumn( "flags" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + V inbox(Inbox); + V categoryRoot(Categories); + V nocategory(NoCategory); + Cat category("cat"); + + ModelStructure sourceStructure; + sourceStructure << c1; + + int flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + int column = 0; + QTest::newRow( "get flags on collection" ) << sourceStructure << column << flags; + + sourceStructure.clear(); + sourceStructure << t2; + + flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled; + + QTest::newRow( "get flags on todo" ) << sourceStructure << column << flags; + + flags = Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + column = 4; + + QTest::newRow( "get flags on todo on column 4" ) << sourceStructure << column << flags; + + sourceStructure.clear(); + sourceStructure << t1; + + column = 0; + flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + + QTest::newRow( "get flags on project" ) << sourceStructure << column << flags; + + sourceStructure.clear(); + sourceStructure << inbox; + + flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + + QTest::newRow( "get flags on inbox" ) << sourceStructure << column << flags; + + sourceStructure.clear(); + sourceStructure << category; + + flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + + QTest::newRow( "get flags on category" ) << sourceStructure << column << flags; + + sourceStructure.clear(); + sourceStructure << categoryRoot; + + flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + + QTest::newRow( "get flags on category root" ) << sourceStructure << column << flags; + + sourceStructure.clear(); + sourceStructure << nocategory; + + flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + + QTest::newRow( "get flags on NoCategory" ) << sourceStructure << column << flags; + } + + void shouldRetrieveItemFlags() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel todoMetadataModel; + ModelTest t1(&todoMetadataModel); + + //WHEN + todoMetadataModel.setSourceModel(&source); + + //THEN + QFETCH(int, column); + QFETCH(int, flags); + + QModelIndex index = todoMetadataModel.index(0,column); + + QCOMPARE(todoMetadataModel.flags(index), flags); + } + + void shouldReactToSourceRowInserts_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "sourceParentPath" ); + QTest::addColumn( "insertedStructure" ); + QTest::addColumn( "outputStructure" ); + + // Base items + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + T t3(3, 1, "t3", "t2", "t3"); + T t4(4, 1, "t4", QString(), "t4"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1; + + ModelPath sourceParentPath = c1; + ModelStructure insertedStructure; + insertedStructure << t1; + + ModelStructure outputStructure; + outputStructure << c1 + << _+t1; + + QTest::newRow( "simple insertion" ) << sourceStructure << sourceParentPath + << insertedStructure + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1; + + sourceParentPath = c1 % t1; + + insertedStructure.clear(); + insertedStructure << t2; + + outputStructure.clear(); + outputStructure << c1 + << _+t1 + << __+t2; + + QTest::newRow( "add todo to project" ) << sourceStructure << sourceParentPath + << insertedStructure + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t2; + + sourceParentPath = c1 % t2; + + insertedStructure.clear(); + insertedStructure << t3; + + outputStructure.clear(); + outputStructure << c1 + << _+t2 + << __+t3; + + QTest::newRow( "add todo to project without project tag" ) << sourceStructure << sourceParentPath + << insertedStructure + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t3; + + sourceParentPath = c1 % t3; + + insertedStructure.clear(); + insertedStructure << t4; + + outputStructure.clear(); + outputStructure << c1 + << _+t3 + << __+t4; + + QTest::newRow( "add todo to todo" ) << sourceStructure << sourceParentPath + << insertedStructure + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1; + + sourceParentPath = c1; + + insertedStructure.clear(); + insertedStructure << t2; + + outputStructure.clear(); + outputStructure << c1 + << _+t1 + << _+t2; + + QTest::newRow( "add todo to collection with a todo" ) << sourceStructure << sourceParentPath + << insertedStructure + << outputStructure; + } + + void shouldReactToSourceRowInserts() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel metadataModel; + ModelTest t1(&metadataModel); + + metadataModel.setSourceModel(&source); + + // What row number will we expect? + QFETCH(ModelPath, sourceParentPath); + QModelIndex parentIndex = ModelUtils::locateItem(&metadataModel, sourceParentPath); + const int expectedRow = metadataModel.rowCount(parentIndex); + + //WHEN + QFETCH(ModelStructure, insertedStructure); + + // Collect data to ensure we signalled the outside properly + QSignalSpy aboutToInsertSpy(&metadataModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy insertSpy(&metadataModel, SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy changeSpy(&metadataModel, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + ModelUtils::create(&source, insertedStructure, sourceParentPath, &behavior); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(metadataModel, output); + + QCOMPARE(aboutToInsertSpy.first().at(0).value(), parentIndex); + QCOMPARE(aboutToInsertSpy.first().at(1).toInt(), expectedRow); + QCOMPARE(aboutToInsertSpy.first().at(2).toInt(), expectedRow); + + QCOMPARE(insertSpy.first().at(0).value(), parentIndex); + QCOMPARE(insertSpy.first().at(1).toInt(), expectedRow); + QCOMPARE(insertSpy.first().at(2).toInt(), expectedRow); + + if (parentIndex.data(Zanshin::ItemTypeRole).toInt()==Zanshin::ProjectTodo) { + QCOMPARE(changeSpy.first().at(0).value(), parentIndex); + QCOMPARE(changeSpy.first().at(1).value(), parentIndex); + } + } + + void shouldReactToSourceRowRemovals_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "itemsToRemove" ); + QTest::addColumn( "outputStructure" ); + + // Base items + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + T t3(3, 1, "t3", "t2", "t3"); + T t4(4, 1, "t4", QString(), "t4"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1; + + ModelPath::List itemsToRemove; + itemsToRemove << c1; + + ModelStructure outputStructure; + + QTest::newRow( "remove empty collection" ) << sourceStructure + << itemsToRemove + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3 + << _+t4; + + + QTest::newRow( "remove collection with items" ) << sourceStructure + << itemsToRemove + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2; + + itemsToRemove.clear(); + itemsToRemove << c1 % t1; + + outputStructure << c1 + << _+t2; + + QTest::newRow( "remove todo" ) << sourceStructure + << itemsToRemove + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2; + + itemsToRemove.clear(); + itemsToRemove << c1 % t2 + << c1 % t1; + + outputStructure.clear(); + outputStructure << c1; + + QTest::newRow( "remove all todos" ) << sourceStructure + << itemsToRemove + << outputStructure; + + } + + void shouldReactToSourceRowRemovals() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel metadataModel; + ModelTest t1(&metadataModel); + + metadataModel.setSourceModel(&source); + + //WHEN + QFETCH(ModelPath::List, itemsToRemove); + + // Collect data to ensure we signalled the outside properly + QSignalSpy aboutToRemoveSpy(&metadataModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy removeSpy(&metadataModel, SIGNAL(rowsRemoved(QModelIndex,int,int))); + + QList parents; + QList rows; + + foreach (const ModelPath &path, itemsToRemove) { + QModelIndex sourceIndex = ModelUtils::locateItem(&source, path); + QModelIndex index = metadataModel.mapFromSource(sourceIndex); + + parents << index.parent(); + rows << index.row(); + } + + ModelUtils::destroy(&source, itemsToRemove); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(metadataModel, output); + + while (aboutToRemoveSpy.size()>0) { + QModelIndex expectedParent = parents.takeFirst(); + int expectedRow = rows.takeFirst(); + + QVariantList signalPayload = aboutToRemoveSpy.takeFirst(); + + QCOMPARE(signalPayload.at(0).value(), expectedParent); + QCOMPARE(signalPayload.at(1).toInt(), expectedRow); + QCOMPARE(signalPayload.at(2).toInt(), expectedRow); + + signalPayload = removeSpy.takeFirst(); + QCOMPARE(signalPayload.at(0).value(), expectedParent); + QCOMPARE(signalPayload.at(1).toInt(), expectedRow); + QCOMPARE(signalPayload.at(2).toInt(), expectedRow); + } + } + + void shouldReactToSourceDataChanges_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "changePath" ); + QTest::addColumn( "notifyList" ); + + // Base items + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + T t3(3, 1, "t3", "t2", "t3"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1; + + ModelPath changePath = c1; + ModelPath::List notifyList; + notifyList << c1; + + QTest::newRow( "change collection name" ) << sourceStructure + << changePath + << notifyList; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t2; + + changePath = c1 % t2; + + notifyList.clear(); + notifyList << c1 % t2; + + QTest::newRow( "change todo name" ) << sourceStructure + << changePath + << notifyList; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2; + + changePath = c1 % t1; + + notifyList.clear(); + notifyList << c1 % t2 + << c1 % t1; + + QTest::newRow( "change project name" ) << sourceStructure + << changePath + << notifyList; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t2 + << _+t3; + + changePath = c1 % t2; + + notifyList.clear(); + notifyList << c1 % t3 + << c1 % t2; + + QTest::newRow( "change name of todo with a child" ) << sourceStructure + << changePath + << notifyList; + + } + + void shouldReactToSourceDataChanges() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel metadataModel; + ModelTest t1(&metadataModel); + + metadataModel.setSourceModel(&source); + + //WHEN + QFETCH(ModelPath, changePath); + QModelIndex index = ModelUtils::locateItem(&source, changePath); + + QFETCH(ModelPath::List, notifyList); + QModelIndexList indexes; + + foreach (ModelPath path, notifyList) { + indexes << ModelUtils::locateItem(&metadataModel, path); + } + + // Collect data to ensure we signalled the outside properly + QSignalSpy changeSpy(&metadataModel, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + + source.setData(index, "test", Qt::DisplayRole); + + //THEN + QCOMPARE(index.data(Qt::DisplayRole).toString(), QString("test")); + + int i = 0; + foreach (QModelIndex changeIndex, indexes) { + QCOMPARE(changeSpy[i].at(0).value(), changeIndex); + QCOMPARE(changeSpy[i].at(1).value(), changeIndex); + i++; + } + } + + void shouldNotCrashWhenParentBecomesEmpty_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "itemToTest" ); + QTest::addColumn( "itemToRemove" ); + //QTest::addColumn( "outputStructure" ); + + // Base items + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2; + + ModelPath itemToRemove = c1 % t1; + + ModelPath itemToTest = c1 % t2; + + QTest::newRow( "remove empty collection" ) << sourceStructure + << itemToTest + << itemToRemove; + } + + void shouldNotCrashWhenParentBecomesEmpty() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + ModelUtils::create(&source, sourceStructure); + + //create metadataModel + TodoMetadataModel metadataModel; + ModelTest t1(&metadataModel); + + metadataModel.setSourceModel(&source); + + //WHEN + QFETCH(ModelPath, itemToTest); + QModelIndex sourceIndex = ModelUtils::locateItem(&source, itemToTest); + + Akonadi::Item item = sourceIndex.data(Akonadi::EntityTreeModel::ItemRole).value(); + KCalCore::Todo::Ptr todo = item.payload(); + todo->setRelatedTo(QString()); + + //modify something + source.setData(sourceIndex, QString(), Zanshin::CategoriesRole); + + QModelIndex metadataIndex = ModelUtils::locateItem(&metadataModel, itemToTest); + + //THEN + QStringList categories = metadataIndex.data(Zanshin::CategoriesRole).toStringList(); + QVERIFY(categories.empty()); + } + + void shouldReactToModelReset_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "outputStructure" ); + + // Base items + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2; + + ModelStructure outputStructure; + + QTest::newRow( "clear model" ) << sourceStructure + << outputStructure; + } + + + void shouldReactToModelReset() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel metadataModel; + ModelTest t1(&metadataModel); + + metadataModel.setSourceModel(&source); + + //WHEN + source.clear(); + + //THEN + QStandardItemModel output; + QCOMPARE(metadataModel, output); + } +}; + +QTEST_KDEMAIN(TodoMetadataModelTest, GUI) + +#include "todometadatamodeltest.moc" diff -Nru zanshin-0.1+svn1006410/tests/todotreemodelspec.cpp zanshin-0.2.0/tests/todotreemodelspec.cpp --- zanshin-0.1+svn1006410/tests/todotreemodelspec.cpp 1970-01-01 00:00:00.000000000 +0000 +++ zanshin-0.2.0/tests/todotreemodelspec.cpp 2011-11-25 15:00:52.000000000 +0000 @@ -0,0 +1,926 @@ +/* This file is part of Zanshin Todo. + + Copyright 2011 Mario Bensi + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + USA. +*/ + +#include + +#include +#include +#include + +#include "todotreemodel.h" +#include "todometadatamodel.h" +#include "testlib/testlib.h" +#include "testlib/mockmodel.h" +#include "testlib/modeltest.h" +#include "testlib/modelbuilderbehavior.h" + +using namespace Zanshin::Test; + +Q_DECLARE_METATYPE(QModelIndex) +Q_DECLARE_METATYPE(QList) + +class TodoTreeModelSpec : public QObject +{ + Q_OBJECT + +private: + static QModelIndexList collectChildren(QAbstractItemModel *model, const QModelIndex &root) + { + QModelIndexList result; + + for (int row=0; rowrowCount(root); row++) { + QModelIndex child = model->index(row, 0, root); + + if (!child.isValid()) { + return result; + } + + result+= collectChildren(model, child); + result << child; + } + + return result; + } + +private slots: + void initTestCase() + { + qRegisterMetaType(); + + QList roles; + roles << Qt::DisplayRole + << Akonadi::EntityTreeModel::ItemRole + << Akonadi::EntityTreeModel::CollectionRole; + + QTest::setEvaluatedItemRoles(roles); + } + + void shouldRememberItsSourceModel() + { + //GIVEN + QStandardItemModel baseModel; + TodoTreeModel treeModel; + ModelTest t1(&treeModel); + + //WHEN + treeModel.setSourceModel(&baseModel); + + //THEN + QVERIFY(treeModel.sourceModel() == &baseModel); + } + + void shouldReactToSourceRowRemovals_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "itemsToRemove" ); + QTest::addColumn( "outputStructure" ); + + // Base items + V inbox(Inbox); + C c1(1, 0, "c1"); + C c2(2, 0, "c2"); + T t1(3, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(4, 1, "t2", "t1", "t2"); + T t3(5, 2, "t3", QString(), "t3", InProgress, ProjectTag); + T t4(6, 2, "t4", QString(), "t4", InProgress, ProjectTag); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2 + << c2 + << _+t3 + << _+t4; + + + ModelPath::List itemsToRemove; + itemsToRemove << c1; + + ModelStructure outputStructure; + outputStructure << inbox + << c2 + << _+t3 + << _+t4; + + + QTest::newRow( "delete collection" ) << sourceStructure << itemsToRemove << outputStructure; + } + + void shouldReactToSourceRowRemovals() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + ModelUtils::create(&source, sourceStructure); + + //create treeModel + TodoTreeModel treeModel; + ModelTest t1(&treeModel); + + treeModel.setSourceModel(&source); + + //WHEN + QFETCH(ModelPath::List, itemsToRemove); + + // Collect data to ensure we signalled the outside properly + QSignalSpy aboutToRemoveSpy(&treeModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int))); + QSignalSpy removeSpy(&treeModel, SIGNAL(rowsRemoved(QModelIndex,int,int))); + + QList parents; + QList rows; + + foreach (const ModelPath &path, itemsToRemove) { + QModelIndex sourceIndex = ModelUtils::locateItem(&source, path); + QModelIndex index = treeModel.mapFromSource(sourceIndex); + + foreach (const QModelIndex &child, collectChildren(&treeModel, index)) { + parents << child.parent(); + rows << child.row(); + } + + parents << index.parent(); + rows << index.row(); + } + + // destroy the item selected + ModelUtils::destroy(&source, itemsToRemove); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(treeModel, output); + + QCOMPARE(aboutToRemoveSpy.size(), parents.size()); + QCOMPARE(removeSpy.size(), parents.size()); + + while (aboutToRemoveSpy.size()>0) { + QModelIndex expectedParent = parents.takeFirst(); + int expectedRow = rows.takeFirst(); + + QVariantList signalPayload = aboutToRemoveSpy.takeFirst(); + QCOMPARE(signalPayload.at(0).value(), expectedParent); + QCOMPARE(signalPayload.at(1).toInt(), expectedRow); + QCOMPARE(signalPayload.at(2).toInt(), expectedRow); + + signalPayload = removeSpy.takeFirst(); + QCOMPARE(signalPayload.at(0).value(), expectedParent); + QCOMPARE(signalPayload.at(1).toInt(), expectedRow); + QCOMPARE(signalPayload.at(2).toInt(), expectedRow); + } + } + + void shouldReparentBasedOnUids_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "outputStructure" ); + + // Base items + V inbox(Inbox); + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2; + + ModelStructure outputStructure; + outputStructure << inbox + << c1 + << _+t1 + << __+t2; + + QTest::newRow( "add todo" ) << sourceStructure << outputStructure; + } + + void shouldReparentBasedOnUids() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + ModelUtils::create(&source, sourceStructure); + + //WHEN + //create treeModel + TodoTreeModel treeModel; + ModelTest t1(&treeModel); + + //treeModel.setSourceModel(static_cast(&source)); + treeModel.setSourceModel(&source); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(treeModel, output); + } + + void shouldReactToSourceRowInserts_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "sourceParentPath" ); + QTest::addColumn( "proxyParentPathList" ); + QTest::addColumn( "insertedStructure" ); + QTest::addColumn( "outputStructure" ); + QTest::addColumn >( "insertRows" ); + + // Base items + V inbox(Inbox); + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + T t3(3, 1, "t3", "t2", "t3"); + T t4(4, 1, "t4", "QString", "t4"); + T t5(5, 1, "t5", "t4", "t5"); + T t6(6, 1, "t6", "t5", "t6"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1; + + ModelPath::List proxyParentPathList; + + ModelPath sourceParentPath = c1; + proxyParentPathList << c1 % t1; + + ModelStructure insertedStructure; + insertedStructure << t2; + + ModelStructure outputStructure; + outputStructure << inbox + << c1 + << _+t1 + << __+t2; + + QList insertRows; + insertRows << 0; + + QTest::newRow( "add todo to project" ) << sourceStructure << sourceParentPath + << proxyParentPathList << insertedStructure + << outputStructure << insertRows; + + sourceStructure.clear(); + sourceStructure << c1; + + sourceParentPath = c1; + proxyParentPathList.clear(); + proxyParentPathList << c1 + << inbox + << inbox + << c1 % t1 + << c1 % t1 % t2; + + insertedStructure.clear(); + insertedStructure << t1 + << t3 + << t2; + + outputStructure.clear(); + outputStructure << inbox + << c1 + << _+t1 + << __+t2 + << ___+t3; + + insertRows.clear(); + insertRows << 0 + << 0 + << 0 + << 0 + << 0; + + QTest::newRow( "add todo before project" ) << sourceStructure << sourceParentPath + << proxyParentPathList << insertedStructure + << outputStructure << insertRows; + + sourceStructure.clear(); + sourceStructure << c1; + + sourceParentPath = c1; + + proxyParentPathList.clear(); + proxyParentPathList << inbox + << c1 + << c1 % t1 + << inbox + << c1 + << c1 % t4 + << c1 % t1 % t2 + << c1 + << c1 % t4 + << c1 % t4 % t5; + + insertedStructure.clear(); + insertedStructure << t2 + << t1 + << t5 + << t6 + << t3 + << t4; + + outputStructure.clear(); + outputStructure << inbox + << c1 + << _+t1 + << __+t2 + << ___+t3 + << _+t4 + << __+t5 + << ___+t6; + + insertRows.clear(); + insertRows << 0 + << 0 + << 0 + << 0 + << 1 + << 0 + << 0 + << 2 + << 0 + << 0; + + QTest::newRow( "add todos in random order" ) << sourceStructure << sourceParentPath + << proxyParentPathList << insertedStructure + << outputStructure << insertRows; + + } + + void shouldReactToSourceRowInserts() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + ModelUtils::create(&source, sourceStructure); + + //create metadataModel + TodoMetadataModel metadataModel; + ModelTest t1(&metadataModel); + + metadataModel.setSourceModel(&source); + + //create treeModel + TodoTreeModel treeModel; + ModelTest t2(&treeModel); + + treeModel.setSourceModel(&metadataModel); + + //WHEN + QFETCH(ModelPath, sourceParentPath); + QFETCH(ModelStructure, insertedStructure); + + // Collect data to ensure we signalled the outside properly + QSignalSpy aboutToInsertSpy(&treeModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int))); + QSignalSpy insertSpy(&treeModel, SIGNAL(rowsInserted(QModelIndex,int,int))); + + ModelUtils::create(&source, insertedStructure, sourceParentPath); + + // What row number will we expect? + QFETCH(ModelPath::List, proxyParentPathList); + QFETCH(QList, insertRows); + QModelIndexList parentIndexList; + foreach (ModelPath proxyParentPath, proxyParentPathList) { + parentIndexList << ModelUtils::locateItem(&treeModel, proxyParentPath); + } + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(treeModel, output); + + QCOMPARE(aboutToInsertSpy.size(), insertRows.size()); + QCOMPARE(insertSpy.size(), insertRows.size()); + + QCOMPARE(aboutToInsertSpy.size(), parentIndexList.size()); + QCOMPARE(insertSpy.size(), parentIndexList.size()); + + for (int i=0; i(), parentIndexList[i]); + QCOMPARE(aboutToInsertSpy[i].at(1).toInt(), insertRows[i]); + QCOMPARE(aboutToInsertSpy[i].at(2).toInt(), insertRows[i]); + + QCOMPARE(insertSpy[i].at(0).value(), parentIndexList[i]); + QCOMPARE(insertSpy[i].at(1).toInt(), insertRows[i]); + QCOMPARE(insertSpy[i].at(2).toInt(), insertRows[i]); + } + } + + void shouldHandleProjectMoves_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "itemToChange" ); + QTest::addColumn( "parentUid" ); + QTest::addColumn( "outputStructure" ); + + // Base items + V inbox(Inbox); + C c1(1, 0, "c1"); + T p1(1, 1, "p1", QString(), "p1", InProgress, ProjectTag); + T p2(2, 1, "p2", QString(), "p2", InProgress, ProjectTag); + T p3(3, 1, "p3", "p1", "p3", InProgress, ProjectTag); + T t1(11, 1, "t1", "p1", "t1"); + T t2(22, 1, "t2", "p2", "t2"); + T t3(22, 1, "t3", "p3", "t3"); + C c2(2, 0, "c2"); + T p4(4, 2, "p4", QString(), "p4", InProgress, ProjectTag); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3 + << _+p1 + << _+p2 + << _+p3 + << c2 + << _+p4; + + ModelPath itemToChange = c1 % p2; + + QString parentUid = "p1"; + + ModelStructure outputStructure; + outputStructure << inbox + << c1 + << _+p1 + << __+t1 + << __+p3 + << ___+t3 + << __+p2 + << ___+t2 + << c2 + << _+p4; + + QTest::newRow( "root project moved under another project of the same collection" ) + << sourceStructure << itemToChange + << parentUid << outputStructure; + + + itemToChange = c1 % p3; + parentUid = QString(); + outputStructure.clear(); + outputStructure << inbox + << c1 + << _+p1 + << __+t1 + << _+p2 + << __+t2 + << _+p3 + << __+t3 + << c2 + << _+p4; + + QTest::newRow( "sub-project moved as root in the same collection" ) + << sourceStructure << itemToChange + << parentUid << outputStructure; + + } + + void shouldHandleProjectMoves() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + ModelUtils::create(&source, sourceStructure); + + //create treeModel + TodoTreeModel treeModel; + ModelTest t1(&treeModel); + + treeModel.setSourceModel(&source); + + //WHEN + QFETCH(ModelPath, itemToChange); + QModelIndex index = ModelUtils::locateItem(&source, itemToChange); + + QFETCH(QString, parentUid); + source.setData(index, parentUid, Zanshin::ParentUidRole); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(treeModel, output); + } + + + void shouldKeepSourceOrder_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "outputStructure" ); + + // Base items + V inbox(Inbox); + C c1(1, 0, "c1"); + C c2(2, 0, "c2"); + T t1(1, 0, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + T t3(3, 1, "t3", "t1", "t3", InProgress, ProjectTag); + T t4(4, 1, "t4", "t1", "t4"); + T t5(5, 1, "t5", "t1", "t5", InProgress, ProjectTag); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << t1 + << c1; + + ModelStructure outputStructure; + outputStructure << inbox + << t1 + << c1; + + QTest::newRow( "collection and project order" ) << sourceStructure + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t2 + << _+t3; + + outputStructure.clear(); + outputStructure << inbox + << c1 + << _+t1 + << __+t2 + << __+t3; + + QTest::newRow( "project and todo order" ) << sourceStructure + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t4 + << _+t2; + + outputStructure.clear(); + outputStructure << inbox + << c1 + << _+t1 + << __+t4 + << __+t2; + + QTest::newRow( "order between two todo" ) << sourceStructure + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t1 + << _+t5 + << _+t3; + + outputStructure.clear(); + outputStructure << inbox + << c1 + << _+t1 + << __+t5 + << __+t3; + + QTest::newRow( "order between two project" ) << sourceStructure + << outputStructure; + + sourceStructure.clear(); + sourceStructure << c2 + << c1; + + outputStructure.clear(); + outputStructure << inbox + << c2 + << c1; + + QTest::newRow( "order between two collection" ) << sourceStructure + << outputStructure; + } + + void shouldKeepSourceOrder() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + ModelUtils::create(&source, sourceStructure); + + //create todoTreeModel + TodoTreeModel todoTreeModel; + ModelTest t1(&todoTreeModel); + + //WHEN + todoTreeModel.setSourceModel(&source); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(todoTreeModel, output); + } + + void shouldRetrieveItemFlags_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "itemPath" ); + QTest::addColumn( "column" ); + QTest::addColumn( "flags" ); + + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + V inbox(Inbox); + V categoryRoot(Categories); + V nocategory(NoCategory); + Cat category("cat"); + + ModelStructure sourceStructure; + sourceStructure << c1; + + int flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + int column = 0; + ModelPath itemPath = c1; + + QTest::newRow( "get flags on collection" ) << sourceStructure << itemPath << column << flags; + + sourceStructure.clear(); + sourceStructure << t2; + + flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + itemPath = inbox % t2; + + QTest::newRow( "get flags on todo" ) << sourceStructure << itemPath << column << flags; + + flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + column = 4; + + QTest::newRow( "get flags on todo on column 4" ) << sourceStructure << itemPath << column << flags; + + sourceStructure.clear(); + sourceStructure << t1; + + itemPath = t1; + + column = 0; + flags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + + QTest::newRow( "get flags on project" ) << sourceStructure << itemPath << column << flags; + + itemPath = inbox; + + flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; + + QTest::newRow( "get flags on inbox" ) << sourceStructure << itemPath << column << flags; + } + + void shouldRetrieveItemFlags() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + ModelUtils::create(&source, sourceStructure); + + //create metadataModel + TodoTreeModel todoTreeModel; + ModelTest t1(&todoTreeModel); + + //WHEN + todoTreeModel.setSourceModel(&source); + + //THEN + QFETCH(ModelPath, itemPath); + QFETCH(int, column); + QFETCH(int, flags); + + QModelIndex index = ModelUtils::locateItem(&todoTreeModel, itemPath); + index = index.sibling(index.row(), column); + + QCOMPARE(todoTreeModel.flags(index), flags); + } + + void shouldReactToSourceRowRemovalsWithMetadata_data() + { + QTest::addColumn( "sourceStructure" ); + QTest::addColumn( "itemsToRemove" ); + QTest::addColumn( "outputStructure" ); + + // Base items + V inbox(Inbox); + C c1(1, 0, "c1"); + C c2(2, 0, "c2"); + T t1(3, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(4, 1, "t2", "t1", "t2"); + T t3(5, 2, "t3", QString(), "t3", InProgress, ProjectTag); + T t4(6, 2, "t4", QString(), "t4", InProgress, ProjectTag); + T t5(6, 2, "t5", "", "t5"); + T t6(6, 2, "t6", "t5", "t6"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2 + << c2 + << _+t3 + << _+t4; + + + ModelPath::List itemsToRemove; + itemsToRemove << c1; + + ModelStructure outputStructure; + outputStructure << inbox + << c2 + << _+t3 + << _+t4; + + + QTest::newRow( "delete collection" ) << sourceStructure << itemsToRemove << outputStructure; + + sourceStructure.clear(); + sourceStructure << c1 + << _+t5 + << _+t6; + + itemsToRemove.clear(); + itemsToRemove << c1 % t6; + + outputStructure.clear(); + outputStructure << inbox + << _+t5 + << c1; + + QTest::newRow( "Root Project without tag should return to inbox when it looses its children" ) + << sourceStructure << itemsToRemove << outputStructure; + } + + void shouldReactToSourceRowRemovalsWithMetadata() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + QStandardItemModel source; + StandardModelBuilderBehavior behavior; + behavior.setMetadataCreationEnabled(false); + ModelUtils::create(&source, sourceStructure, ModelPath(), &behavior); + + //create metadataModel + TodoMetadataModel metadataModel; + ModelTest t1(&metadataModel); + + metadataModel.setSourceModel(&source); + + //create treeModel + TodoTreeModel treeModel; + ModelTest t2(&treeModel); + + treeModel.setSourceModel(&metadataModel); + + //WHEN + QFETCH(ModelPath::List, itemsToRemove); + + // destroy the item selected + ModelUtils::destroy(&source, itemsToRemove); + + //THEN + QFETCH(ModelStructure, outputStructure); + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(treeModel, output); + } + + void shouldReparentOnTodoPromotion() + { + //GIVEN + V inbox(Inbox); + C c1(1, 0, "c1"); + T t1(3, 1, "t1", QString(), "t1"); + T t2(4, 1, "t2", QString(), "t2"); + + // Source structure + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2; + + //Source model + QStandardItemModel source; + ModelUtils::create(&source, sourceStructure); + + //create treeModel + TodoTreeModel treeModel; + ModelTest modelTest(&treeModel); + + treeModel.setSourceModel(&source); + + //WHEN + ModelPath itemToPromote = c1 % t1; + QModelIndex index = ModelUtils::locateItem(&source, itemToPromote); + source.setData(index, Zanshin::ProjectTodo, Zanshin::ItemTypeRole); + + //THEN + ModelStructure outputStructure; + outputStructure << inbox + << _+t2 + << c1 + << _+t1; + + QStandardItemModel output; + ModelUtils::create(&output, outputStructure); + + QCOMPARE(treeModel, output); + } + + void shouldReactToModelReset_data() + { + QTest::addColumn("sourceStructure"); + + // Base items + C c1(1, 0, "c1"); + T t1(1, 1, "t1", QString(), "t1", InProgress, ProjectTag); + T t2(2, 1, "t2", "t1", "t2"); + + // Create the source structure once and for all + ModelStructure sourceStructure; + sourceStructure << c1 + << _+t1 + << _+t2; + + QTest::newRow("clear model") << sourceStructure; + } + + + void shouldReactToModelReset() + { + //GIVEN + QFETCH(ModelStructure, sourceStructure); + + //Source model + MockModel source; + ModelUtils::create(&source, sourceStructure); + + //create treeModel + TodoTreeModel treeModel; + ModelTest t2(&treeModel); + + treeModel.setSourceModel(&source); + + //WHEN + source.clearData(); + + //THEN + MockModel output; + TodoTreeModel* treeModelOutput = new TodoTreeModel(); + + treeModelOutput->setSourceModel(&output); + + QAbstractItemModel* abstractModel = treeModelOutput; + QCOMPARE(treeModel, *abstractModel); + delete treeModelOutput; + } +}; + +QTEST_KDEMAIN(TodoTreeModelSpec, GUI) + +#include "todotreemodelspec.moc"