diff options
Diffstat (limited to 'akregator/src')
522 files changed, 84083 insertions, 0 deletions
diff --git a/akregator/src/Makefile.am b/akregator/src/Makefile.am new file mode 100644 index 000000000..f146b4861 --- /dev/null +++ b/akregator/src/Makefile.am @@ -0,0 +1,166 @@ +#set the include path for X, qt and KDE +INCLUDES = -I$(top_srcdir)/akregator/src/librss -I$(top_srcdir) $(all_includes) + +# let automoc handle all of the meta source files (moc). +METASOURCES = AUTO + +messages: rc.cpp + $(EXTRACTRC) `find . -name "*.rc" -o -name "*.ui" -o -name "*.kcfg"` >> rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/akregator.pot + +KDE_ICON = AUTO + +# this Makefile creates both a KPart application and a KPart +######################################################################### +# APPLICATION SECTION +######################################################################### +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = akregator + +# the application source, library search path, and link libraries +akregator_SOURCES = main.cpp mainwindow.cpp +akregator_LDFLAGS = $(KDE_RPATH) $(all_libraries) +akregator_LDADD = $(LIB_KPARTS) libakregatorprivate.la +#akregator_LDADD = $(LIB_KPARTS) libakregatorprivate.la $(top_builddir)/libkdepim/libkdepim.la + +# this is where the desktop file will go +xdg_apps_DATA = akregator.desktop + +# this is where the shell's XML-GUI resource file goes +shellrcdir = $(kde_datadir)/akregator +shellrc_DATA = akregator_shell.rc + +kdelnk_DATA = feed.protocol +kdelnkdir = $(kde_servicesdir) + +######################################################################### +# PRIVATE SECTION +######################################################################### + +# install headers (for storage plugins) +akregatorinclude_HEADERS = storage.h feedstorage.h storagefactory.h storagefactoryregistry.h \ + plugin.h articleinterceptor.h akregator_export.h +akregatorincludedir = $(includedir)/akregator + +# this is a lib shared by both the KPart and the main application +lib_LTLIBRARIES = libakregatorprivate.la +libakregatorprivate_la_SOURCES = akregatorconfig.kcfgc \ + articlefilter.cpp \ + aboutdata.cpp \ + trayicon.cpp \ + article.cpp \ + feed.cpp \ + treenode.cpp \ + treenodevisitor.cpp \ + tagnode.cpp \ + folder.cpp \ + tagfolder.cpp \ + nodelist.cpp \ + feedlist.cpp \ + tagnodelist.cpp \ + fetchqueue.cpp \ + feediconmanager.cpp \ + feediconmanager.skel \ + articleinterceptor.cpp \ + plugin.cpp \ + pluginmanager.cpp \ + storagefactoryregistry.cpp \ + storage.cpp \ + tag.cpp \ + tagset.cpp \ + storagedummyimpl.cpp \ + storagefactorydummyimpl.cpp \ + simplenodeselector.cpp \ + feedstoragedummyimpl.cpp \ + dragobjects.cpp \ + utils.cpp + +libakregatorprivate_la_LDFLAGS = $(all_libraries) -avoid-version -no-undefined +libakregatorprivate_la_LIBADD = $(top_builddir)/libkdepim/libkdepim.la \ + $(top_builddir)/akregator/src/librss/librsslocal.la $(LIB_KHTML) + +######################################################################### +# KPART SECTION +######################################################################### + +kde_kcfg_DATA = akregator.kcfg +kde_module_LTLIBRARIES = libakregatorpart.la + +mainwindow.lo: akregatorconfig.h +akregator_part.lo: akregatorconfig.h +akregator_view.lo: akregatorconfig.h +akregatorconfig.lo: akregatorconfig.h +articlelistview.lo: akregatorconfig.h +articleviewer.lo: akregatorconfig.h +feed.lo: akregatorconfig.h +fetchqueue.lo: akregatorconfig.h +trayicon.lo: akregatorconfig.h +viewer.lo: akregatorconfig.h +searchbar.lo: akregatorconfig.h + +kspeech_DIR = $(kde_includes) +kspeechsink_DIR = $(kde_includes) + +# the Part's source, library search path, and link libraries +libakregatorpart_la_SOURCES = \ + searchbar.cpp \ + akregator_run.cpp \ + articlelistview.cpp \ + actionmanager.cpp \ + actionmanagerimpl.cpp \ + frame.cpp \ + viewer.cpp \ + articleviewer.cpp \ + addfeeddialog.cpp \ + addfeedwidgetbase.ui \ + propertiesdialog.cpp \ + propertieswidgetbase.ui \ + pageviewer.cpp \ + tabwidget.cpp \ + feedlistview.cpp \ + treenodeitem.cpp \ + folderitem.cpp \ + feeditem.cpp \ + progressmanager.cpp \ + kernel.cpp \ + listtabwidget.cpp \ + settings_appearance.ui \ + settings_general.ui \ + settings_archive.ui \ + settings_browser.ui \ + settings_advancedbase.ui \ + tagnodeitem.cpp \ + configdialog.cpp \ + settings_advanced.cpp \ + akregator_partiface.skel \ + akregator_part.cpp \ + akregator_view.cpp \ + notificationmanager.cpp \ + tagaction.cpp \ + tagpropertieswidgetbase.ui \ + tagpropertiesdialog.cpp \ + tagfolderitem.cpp \ + speechclient.cpp \ + kspeechsink.skel \ + kspeech.stub + +libakregatorpart_la_LDFLAGS = $(KDE_RPATH) $(KDE_PLUGIN) $(LIB_KUTILS) -avoid-version -no-undefined $(all_libraries) +libakregatorpart_la_LIBADD = libakregatorprivate.la + +# this is where the desktop file will go +partdesktopdir = $(kde_servicesdir) +partdesktop_DATA = akregator_part.desktop + +# this is where the part's XML-GUI resource file goes +partrcdir = $(kde_datadir)/akregator +partrc_DATA = articleviewer.rc pageviewer.rc akregator_part.rc eventsrc + +kde_servicetypes_DATA = akregator_plugin.desktop + + +SUBDIRS = librss about . mk4storage pics + +DOXYGEN_REFERENCES = kdeui +include $(top_srcdir)/admin/Doxyfile.am + diff --git a/akregator/src/about/Makefile.am b/akregator/src/about/Makefile.am new file mode 100644 index 000000000..2025a97f8 --- /dev/null +++ b/akregator/src/about/Makefile.am @@ -0,0 +1,6 @@ +about_DATA = \ + top-right-akregator.png \ + main.html \ + akregator.css + +aboutdir = $(kde_datadir)/akregator/about diff --git a/akregator/src/about/akregator.css b/akregator/src/about/akregator.css new file mode 100644 index 000000000..9c31cfe35 --- /dev/null +++ b/akregator/src/about/akregator.css @@ -0,0 +1,20 @@ + +#headerR { + position: absolute; + right: 0px; + width: 430px; + height: 131px; + background-image: url(top-right-akregator.png); +} + +/* .. for when we actually have a box-center-akregator.png .. +#boxCenter { + background-image: url(box-center-akregator.png); + background-repeat: no-repeat; + background-color: #dfe7f3; + background-position: bottom right; +} +*/ + +/* vim:set sw=2 et nocindent smartindent: */ + diff --git a/akregator/src/about/main.html b/akregator/src/about/main.html new file mode 100644 index 000000000..c1cbd42df --- /dev/null +++ b/akregator/src/about/main.html @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <meta name="generator" content= + "HTML Tidy for Linux/x86 (vers 1st August 2004), see www.w3.org" /> + + <style type="text/css"> + /*<![CDATA[*/ + @import "%1"; /* kde_infopage.css */ + %1 /* maybe @import "kde_infopage_rtl.css"; */ + @import "akregator.css"; + body {font-size: %1px;} + /*]]>*/ + </style> + + <title>Akregator</title> +</head> + +<body> + <div id="header"> + <div id="headerL"/> + <div id="headerR"/> + + <div id="title"> + %2 <!-- Akregator --> + </div> + + <div id="tagline"> + %3 <!-- Catchphrase --> + </div> + </div> + + <!-- the bar --> + <div id="bar"> + <div id="barT"><div id="barTL"/><div id="barTR"/><div id="barTC"/></div> + <div id="barL"> + <div id="barR"> + <div id="barCenter" class="bar_text"> + %4<!-- Akregator is ... --> + </div> + </div> + </div> + <div id="barB"><div id="barBL"/><div id="barBR"/><div id="barBC"/></div> + </div> + + <!-- the main text box --> + <div id="box"> + <div id="boxT"><div id="boxTL"/><div id="boxTR"/><div id="boxTC"/></div> + <div id="boxL"> + <div id="boxR"> + <div id="boxCenter"> + <!--Welcome to Akregator--> + %5 + </div> + </div> + </div> + <div id="boxB"><div id="boxBL"/><div id="boxBR"/><div id="boxBC"/></div> + </div> + + <div id="footer"><div id="footerL"/><div id="footerR"/></div> +</body> +</html> +<!-- vim:set sw=2 et nocindent smartindent: --> diff --git a/akregator/src/about/top-right-akregator.png b/akregator/src/about/top-right-akregator.png Binary files differnew file mode 100644 index 000000000..fd539eeef --- /dev/null +++ b/akregator/src/about/top-right-akregator.png diff --git a/akregator/src/aboutdata.cpp b/akregator/src/aboutdata.cpp new file mode 100644 index 000000000..93be34a2a --- /dev/null +++ b/akregator/src/aboutdata.cpp @@ -0,0 +1,58 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Teemu Rytilahti <tpr@d5k.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qstring.h> + +#include "aboutdata.h" + +namespace Akregator { + +AboutData::AboutData() + : KAboutData("akregator", I18N_NOOP("Akregator"), AKREGATOR_VERSION, I18N_NOOP("A KDE Feed Aggregator"), + License_GPL, I18N_NOOP("(C) 2004, 2005 Akregator developers"), 0, + "http://akregator.kde.org/") +{ + addAuthor( "Frank Osterfeld", I18N_NOOP("Maintainer"), "frank.osterfeld@kdemail.net" ); + addAuthor( "Teemu Rytilahti", I18N_NOOP("Developer"), "tpr@d5k.net" ); + addAuthor( "Sashmit Bhaduri", I18N_NOOP("Developer"), "sashmit@vfemail.net" ); + addAuthor( "Pierre Habouzit", I18N_NOOP("Developer"), "pierre.habouzit@m4x.org" ); + addAuthor( "Stanislav Karchebny", I18N_NOOP("Developer"), "Stanislav.Karchebny@kdemail.net" ); + addAuthor( "Gary Cramblitt", I18N_NOOP("Contributor"), "garycramblitt@comcast.net"); + addAuthor( "Stephan Binner", I18N_NOOP("Contributor"), "binner@kde.org" ); + addAuthor( "Christof Musik", I18N_NOOP("Contributor"), "christof@freenet.de" ); + addCredit( "Anne-Marie Mahfouf", I18N_NOOP("Handbook"), "annma@kde.org" ); + addCredit( "Frerich Raabe", I18N_NOOP("Author of librss"), "raabe@kde.org" ); + addCredit( "Eckhart Woerner", I18N_NOOP("Bug tracker management, Usability improvements"), "kde@ewsoftware.de"); + addCredit( "Heinrich Wendel", I18N_NOOP("Tons of bug fixes"), "h_wendel@cojobo.net"); + addCredit( "Eike Hein", I18N_NOOP("'Delayed mark as read' feature"), "sho@eikehein.com" ); + addCredit( "Marcel Dierkes", I18N_NOOP("Icons"), "marcel.dierkes@gmx.de"); + addCredit( "George Staikos", I18N_NOOP("Insomnia"), "staikos@kde.org" ); + addCredit( "Philipp Droessler", I18N_NOOP("Gentoo Ebuild"), "kingmob@albert-unser.net"); +} + +AboutData::~AboutData() +{ +} + +} diff --git a/akregator/src/aboutdata.h b/akregator/src/aboutdata.h new file mode 100644 index 000000000..56223d15f --- /dev/null +++ b/akregator/src/aboutdata.h @@ -0,0 +1,46 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Teemu Rytilahti <tpr@d5k.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef ABOUTDATA_H +#define ABOUTDATA_H + +#include <kaboutdata.h> +#include <kdepimmacros.h> + +#define AKREGATOR_VERSION "1.2.9" + +namespace Akregator { +/** +@author Teemu Rytilahti +*/ +class KDE_EXPORT AboutData : public KAboutData +{ +public: + AboutData(); + ~AboutData(); +}; + +} + +#endif diff --git a/akregator/src/actionmanager.cpp b/akregator/src/actionmanager.cpp new file mode 100644 index 000000000..21d7f51eb --- /dev/null +++ b/akregator/src/actionmanager.cpp @@ -0,0 +1,55 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "actionmanager.h" + +namespace Akregator +{ + +class ActionManager::ActionManagerPrivate +{}; + +ActionManager* ActionManager::m_self = 0; + +ActionManager* ActionManager::getInstance() +{ + return m_self; +} + +void ActionManager::setInstance(ActionManager* manager) +{ + m_self = manager; +} + + +ActionManager::ActionManager(QObject* parent, const char* name) : QObject(parent, name), d(new ActionManagerPrivate) +{} + +ActionManager::~ActionManager() +{ + delete d; + d = 0; +} + +} // namespace Akregator diff --git a/akregator/src/actionmanager.h b/akregator/src/actionmanager.h new file mode 100644 index 000000000..cae5985ca --- /dev/null +++ b/akregator/src/actionmanager.h @@ -0,0 +1,62 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_ACTIONMANAGER_H +#define AKREGATOR_ACTIONMANAGER_H + +#include <qobject.h> + +class QWidget; + +class KAction; + +namespace Akregator { + +/** interface for accessing actions, popup menus etc. from widgets. + (Extracted from the implementation to avoid dependencies between widgets and Akregator::Part). + */ +class ActionManager : public QObject +{ + public: + + static ActionManager* getInstance(); + static void setInstance(ActionManager* manager); + + ActionManager(QObject* parent=0, const char* name=0); + virtual ~ActionManager(); + + virtual KAction* action(const char* name, const char* classname=0) = 0; + virtual QWidget* container(const char* name) = 0; + + private: + + static ActionManager* m_self; + + class ActionManagerPrivate; + ActionManagerPrivate* d; +}; + +} // namespace Akregator + +#endif // AKREGATOR_ACTIONMANAGER_H diff --git a/akregator/src/actionmanagerimpl.cpp b/akregator/src/actionmanagerimpl.cpp new file mode 100644 index 000000000..a8b50fc76 --- /dev/null +++ b/akregator/src/actionmanagerimpl.cpp @@ -0,0 +1,443 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qwidget.h> +#include <kaction.h> +#include <kactioncollection.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kshortcut.h> +#include <kxmlguifactory.h> + +#include <qmap.h> +#include <qstring.h> +#include <qvaluelist.h> + +#include "actionmanagerimpl.h" +#include "akregatorconfig.h" +#include "akregator_part.h" +#include "akregator_view.h" +#include "articlelistview.h" +#include "articleviewer.h" +#include "feed.h" +#include "feedlistview.h" +#include "fetchqueue.h" +#include "folder.h" +#include "listtabwidget.h" +#include "kernel.h" +#include "speechclient.h" +#include "tag.h" +#include "tagaction.h" +#include "tagnode.h" +#include "tagset.h" +#include "trayicon.h" +#include "treenode.h" +#include "treenodevisitor.h" +#include "tabwidget.h" +#include "kstdaccel.h" + + + +#include <kdebug.h> + +namespace Akregator +{ + +class ActionManagerImpl::NodeSelectVisitor : public TreeNodeVisitor +{ + public: + NodeSelectVisitor(ActionManagerImpl* manager) : m_manager(manager) {} + + virtual bool visitFeed(Feed* node) + { + KAction* remove = m_manager->action("feed_remove"); + if (remove) + remove->setEnabled(true); + KAction* hp = m_manager->action("feed_homepage"); + if (hp) + hp->setEnabled(!node->htmlUrl().isEmpty()); + m_manager->action("feed_fetch")->setText(i18n("&Fetch Feed")); + m_manager->action("feed_remove")->setText(i18n("&Delete Feed")); + m_manager->action("feed_modify")->setText(i18n("&Edit Feed...")); + m_manager->action("feed_mark_all_as_read")->setText(i18n("&Mark Feed as Read")); + + return true; + } + + virtual bool visitFolder(Folder* node) + { + KAction* remove = m_manager->action("feed_remove"); + if (remove) + remove->setEnabled(node->parent()); // root nodes must not be deleted + KAction* hp = m_manager->action("feed_homepage"); + if (hp) + hp->setEnabled(false); + + m_manager->action("feed_fetch")->setText(i18n("&Fetch Feeds")); + m_manager->action("feed_remove")->setText(i18n("&Delete Folder")); + m_manager->action("feed_modify")->setText(i18n("&Rename Folder")); + m_manager->action("feed_mark_all_as_read")->setText(i18n("&Mark Feeds as Read")); + + return true; + } + + virtual bool visitTagNode(TagNode* /*node*/) + { + KAction* remove = m_manager->action("feed_remove"); + if (remove) + remove->setEnabled(true); + KAction* hp = m_manager->action("feed_homepage"); + if (hp) + hp->setEnabled(false); + m_manager->action("feed_mark_all_as_read")->setText(i18n("&Mark Articles as Read")); + m_manager->action("feed_remove")->setText(i18n("&Delete Tag")); + m_manager->action("feed_modify")->setText(i18n("&Edit Tag...")); + + return true; + } + private: + ActionManagerImpl* m_manager; +}; + +class ActionManagerImpl::ActionManagerImplPrivate +{ +public: + + NodeSelectVisitor* nodeSelectVisitor; + ArticleListView* articleList; + ListTabWidget* listTabWidget; + View* view; + ArticleViewer* articleViewer; + Part* part; + TrayIcon* trayIcon; + KActionMenu* tagMenu; + KActionCollection* actionCollection; + TagSet* tagSet; + QMap<QString, TagAction*> tagActions; + TabWidget* tabWidget; + KAction* speakSelectedArticlesAction; +}; + +void ActionManagerImpl::slotUpdateTagActions(bool enabled, const QStringList& tagIds) +{ + if (Settings::showTaggingGUI() && d->tagMenu) + { + d->tagMenu->setEnabled(enabled); + QValueList<TagAction*> actions = d->tagActions.values(); + + for (QValueList<TagAction*>::ConstIterator it = actions.begin(); it != actions.end(); ++it) + { + (*it)->setChecked(tagIds.contains((*it)->tag().id())); + } + } +} + +void ActionManagerImpl::setTagSet(TagSet* tagSet) +{ + if (tagSet == d->tagSet) + return; + + if (d->tagSet != 0) + { + disconnect(d->tagSet, SIGNAL(signalTagAdded(const Tag&)), this, SLOT(slotTagAdded(const Tag&))); + disconnect(d->tagSet, SIGNAL(signalTagRemoved(const Tag&)), this, SLOT(slotTagRemoved(const Tag&))); + } + + d->tagSet = tagSet; + + if (tagSet != 0) + { + connect(d->tagSet, SIGNAL(signalTagAdded(const Tag&)), this, SLOT(slotTagAdded(const Tag&))); + connect(d->tagSet, SIGNAL(signalTagRemoved(const Tag&)), this, SLOT(slotTagRemoved(const Tag&))); + } + + QValueList<TagAction*> actions = d->tagActions.values(); + for (QValueList<TagAction*>::ConstIterator it = actions.begin(); it != actions.end(); ++it) + { + d->tagMenu->remove(*it); + delete *it; + } + + + d->tagActions.clear(); + + //TODO: remove actions from menus, delete actions, clear maps + + if (tagSet != 0L) + { + QValueList<Tag> list = tagSet->toMap().values(); + for (QValueList<Tag>::ConstIterator it = list.begin(); it != list.end(); ++it) + slotTagAdded(*it); + } +} + +void ActionManagerImpl::slotTagAdded(const Tag& tag) +{ + if (!Settings::showTaggingGUI()) + return; + + if (!d->tagActions.contains(tag.id())) + { + d->tagActions[tag.id()] = new TagAction(tag, d->view, SLOT(slotAssignTag(const Tag&, bool)), d->tagMenu); + d->tagMenu->insert(d->tagActions[tag.id()]); + } +} + +void ActionManagerImpl::slotTagRemoved(const Tag& tag) +{ + if (!Settings::showTaggingGUI()) + return; + + QString id = tag.id(); + TagAction* action = d->tagActions[id]; + d->tagMenu->remove(action); + d->tagActions.remove(id); + delete action; +} + +void ActionManagerImpl::slotNodeSelected(TreeNode* node) +{ + if (node != 0) + d->nodeSelectVisitor->visit(node); +} + +ActionManagerImpl::ActionManagerImpl(Part* part, QObject* parent, const char* name) : ActionManager(parent, name), d(new ActionManagerImplPrivate) +{ + d->nodeSelectVisitor = new NodeSelectVisitor(this); + d->part = part; + d->tagSet = 0; + d->listTabWidget = 0; + d->articleList = 0; + d->trayIcon = 0; + d->articleViewer = 0; + d->view = 0; + d->tabWidget = 0; + d->tagMenu = 0; + d->speakSelectedArticlesAction = 0; + d->actionCollection = part->actionCollection(); + initPart(); +} + +ActionManagerImpl::~ActionManagerImpl() +{ + delete d->nodeSelectVisitor; + delete d; + d = 0; +} + +void ActionManagerImpl::initTrayIcon(TrayIcon* trayIcon) +{ + if (d->trayIcon) + return; + else d->trayIcon = trayIcon; + + KPopupMenu* traypop = trayIcon->contextMenu(); + + if (actionCollection()->action("feed_fetch_all")) + actionCollection()->action("feed_fetch_all")->plug(traypop, 1); + if (actionCollection()->action("akregator_configure_akregator")) + actionCollection()->action("akregator_configure_akregator")->plug(traypop, 2); +} + +void ActionManagerImpl::initPart() +{ + new KAction(i18n("&Import Feeds..."), "", "", d->part, SLOT(fileImport()), d->actionCollection, "file_import"); + new KAction(i18n("&Export Feeds..."), "", "", d->part, SLOT(fileExport()), d->actionCollection, "file_export"); + //new KAction(i18n("&Get Feeds From Web..."), "", "", d->part, SLOT(fileGetFeeds()), d->actionCollection, "file_getfromweb"); + + new KAction(i18n("Send &Link Address..."), "mail_generic", "", d->part, SLOT(fileSendLink()), d->actionCollection, "file_sendlink"); + new KAction(i18n("Send &File..."), "mail_generic", "", d->part, SLOT(fileSendFile()), d->actionCollection, "file_sendfile"); + + KStdAction::configureNotifications(d->part, SLOT(showKNotifyOptions()), d->actionCollection); // options_configure_notifications + new KAction( i18n("Configure &Akregator..."), "configure", "", d->part, SLOT(showOptions()), d->actionCollection, "akregator_configure_akregator" ); +} + +void ActionManagerImpl::initView(View* view) +{ + if (d->view) + return; + else + d->view = view; + + // tag actions + new KAction(i18n("&New Tag..."), "", "", d->view, SLOT(slotNewTag()), actionCollection(), "tag_new"); + + // Feed/Feed Group popup menu + new KAction(i18n("&Open Homepage"), "", "Ctrl+H", d->view, SLOT(slotOpenHomepage()), actionCollection(), "feed_homepage"); + new KAction(i18n("&Add Feed..."), "bookmark_add", "Insert", d->view, SLOT(slotFeedAdd()), actionCollection(), "feed_add"); + new KAction(i18n("Ne&w Folder..."), "folder_new", "Shift+Insert", d->view, SLOT(slotFeedAddGroup()), actionCollection(), "feed_add_group"); + new KAction(i18n("&Delete Feed"), "editdelete", "Alt+Delete", d->view, SLOT(slotFeedRemove()), actionCollection(), "feed_remove"); + new KAction(i18n("&Edit Feed..."), "edit", "F2", d->view, SLOT(slotFeedModify()), actionCollection(), "feed_modify"); + KActionMenu* vm = new KActionMenu( i18n( "&View Mode" ), actionCollection(), "view_mode" ); + + KRadioAction *ra = new KRadioAction(i18n("&Normal View"), "view_top_bottom", "Ctrl+Shift+1", d->view, SLOT(slotNormalView()), actionCollection(), "normal_view"); + ra->setExclusiveGroup( "ViewMode" ); + vm->insert(ra); + + ra = new KRadioAction(i18n("&Widescreen View"), "view_left_right", "Ctrl+Shift+2", d->view, SLOT(slotWidescreenView()), actionCollection(), "widescreen_view"); + ra->setExclusiveGroup( "ViewMode" ); + vm->insert(ra); + + ra = new KRadioAction(i18n("C&ombined View"), "view_text", "Ctrl+Shift+3", d->view, SLOT(slotCombinedView()), actionCollection(), "combined_view"); + ra->setExclusiveGroup( "ViewMode" ); + vm->insert(ra); + + // toolbar / feed menu + new KAction(i18n("&Fetch Feed"), "down", KStdAccel::shortcut(KStdAccel::Reload), d->view, SLOT(slotFetchCurrentFeed()), actionCollection(), "feed_fetch"); + new KAction(i18n("Fe&tch All Feeds"), "bottom", "Ctrl+L", d->view, SLOT(slotFetchAllFeeds()), actionCollection(), "feed_fetch_all"); + + KAction* stopAction = new KAction(i18n( "&Abort Fetches" ), "stop", Key_Escape, Kernel::self()->fetchQueue(), SLOT(slotAbort()), actionCollection(), "feed_stop"); + stopAction->setEnabled(false); + + new KAction(i18n("&Mark Feed as Read"), "goto", "Ctrl+R", d->view, SLOT(slotMarkAllRead()), actionCollection(), "feed_mark_all_as_read"); + new KAction(i18n("Ma&rk All Feeds as Read"), "goto", "Ctrl+Shift+R", d->view, SLOT(slotMarkAllFeedsRead()), actionCollection(), "feed_mark_all_feeds_as_read"); + + // Settings menu + KToggleAction* sqf = new KToggleAction(i18n("Show Quick Filter"), QString::null, 0, d->view, SLOT(slotToggleShowQuickFilter()), actionCollection(), "show_quick_filter"); + sqf->setChecked( Settings::showQuickFilter() ); + + new KAction( i18n("Open in Tab"), "tab_new", "Shift+Return", d->view, SLOT(slotOpenCurrentArticle()), actionCollection(), "article_open" ); + new KAction( i18n("Open in Background Tab"), QString::null, "tab_new", d->view, SLOT(slotOpenCurrentArticleBackgroundTab()), actionCollection(), "article_open_background_tab" ); + new KAction( i18n("Open in External Browser"), "window_new", "Ctrl+Shift+Return", d->view, SLOT(slotOpenCurrentArticleExternal()), actionCollection(), "article_open_external" ); + new KAction( i18n("Copy Link Address"), QString::null, QString::null, d->view, SLOT(slotCopyLinkAddress()), actionCollection(), "article_copy_link_address" ); + + new KAction(i18n("Pre&vious Unread Article"), "", Key_Minus, d->view, SLOT(slotPrevUnreadArticle()),actionCollection(), "go_prev_unread_article"); + new KAction(i18n("Ne&xt Unread Article"), "", Key_Plus, d->view, SLOT(slotNextUnreadArticle()),actionCollection(), "go_next_unread_article"); + + new KAction(i18n("&Delete"), "editdelete", "Delete", d->view, SLOT(slotArticleDelete()), actionCollection(), "article_delete"); + + if (Settings::showTaggingGUI()) + { + d->tagMenu = new KActionMenu ( i18n( "&Set Tags" ), "rss_tag", actionCollection(), "article_tagmenu" ); + d->tagMenu->setEnabled(false); // only enabled when articles are selected + } + KActionMenu* statusMenu = new KActionMenu ( i18n( "&Mark As" ), + actionCollection(), "article_set_status" ); + + d->speakSelectedArticlesAction = new KAction(i18n("&Speak Selected Articles"), "kttsd", "", d->view, SLOT(slotTextToSpeechRequest()), actionCollection(), "akr_texttospeech"); + + KAction* abortTTS = new KAction(i18n( "&Stop Speaking" ), "player_stop", Key_Escape, SpeechClient::self(), SLOT(slotAbortJobs()), actionCollection(), "akr_aborttexttospeech"); + abortTTS->setEnabled(false); + + connect(SpeechClient::self(), SIGNAL(signalActivated(bool)), + abortTTS, SLOT(setEnabled(bool))); + + statusMenu->insert(new KAction(KGuiItem(i18n("as in: mark as read","&Read"), "", + i18n("Mark selected article as read")), + "Ctrl+E", d->view, SLOT(slotSetSelectedArticleRead()), + actionCollection(), "article_set_status_read")); + + statusMenu->insert(new KAction(KGuiItem(i18n("&New"), "", + i18n("Mark selected article as new")), + "Ctrl+N", d->view, SLOT(slotSetSelectedArticleNew()), + actionCollection(), "article_set_status_new" )); + + + statusMenu->insert(new KAction(KGuiItem(i18n("&Unread"), "", + i18n("Mark selected article as unread")), + "Ctrl+U", d->view, SLOT(slotSetSelectedArticleUnread()), + actionCollection(), "article_set_status_unread")); + + KToggleAction* importantAction = new KToggleAction(i18n("&Mark as Important"), "flag", "Ctrl+I", actionCollection(), "article_set_status_important"); + importantAction->setCheckedState(i18n("Remove &Important Mark")); + connect(importantAction, SIGNAL(toggled(bool)), d->view, SLOT(slotArticleToggleKeepFlag(bool))); + + + new KAction( i18n("Move Node Up"), QString::null, "Shift+Alt+Up", view, SLOT(slotMoveCurrentNodeUp()), d->actionCollection, "feedstree_move_up" ); + new KAction( i18n("Move Node Down"), QString::null, "Shift+Alt+Down", view, SLOT(slotMoveCurrentNodeDown()), d->actionCollection, "feedstree_move_down" ); + new KAction( i18n("Move Node Left"), QString::null, "Shift+Alt+Left", view, SLOT(slotMoveCurrentNodeLeft()), d->actionCollection, "feedstree_move_left" ); + new KAction( i18n("Move Node Right"), QString::null, "Shift+Alt+Right", view, SLOT(slotMoveCurrentNodeRight()), d->actionCollection, "feedstree_move_right"); +} + +void ActionManagerImpl::initArticleViewer(ArticleViewer* articleViewer) +{ + if (d->articleViewer) + return; + else + d->articleViewer = articleViewer; +} + +void ActionManagerImpl::initArticleListView(ArticleListView* articleList) +{ + if (d->articleList) + return; + else + d->articleList = articleList; + + new KAction( i18n("&Previous Article"), QString::null, "Left", articleList, SLOT(slotPreviousArticle()), actionCollection(), "go_previous_article" ); + new KAction( i18n("&Next Article"), QString::null, "Right", articleList, SLOT(slotNextArticle()), actionCollection(), "go_next_article" ); +} + +void ActionManagerImpl::initListTabWidget(ListTabWidget* listTabWidget) +{ + if (d->listTabWidget) + return; + else + d->listTabWidget = listTabWidget; + + new KAction(i18n("&Previous Feed"), "", "P", listTabWidget, SLOT(slotPrevFeed()),actionCollection(), "go_prev_feed"); + new KAction(i18n("&Next Feed"), "", "N", listTabWidget, SLOT(slotNextFeed()),actionCollection(), "go_next_feed"); + new KAction(i18n("N&ext Unread Feed"), "", "Alt+Plus", listTabWidget, SLOT(slotNextUnreadFeed()),actionCollection(), "go_next_unread_feed"); + new KAction(i18n("Prev&ious Unread Feed"), "", "Alt+Minus", listTabWidget, SLOT(slotPrevUnreadFeed()),actionCollection(), "go_prev_unread_feed"); + + new KAction( i18n("Go to Top of Tree"), QString::null, "Ctrl+Home", listTabWidget, SLOT(slotItemBegin()), d->actionCollection, "feedstree_home" ); + new KAction( i18n("Go to Bottom of Tree"), QString::null, "Ctrl+End", listTabWidget, SLOT(slotItemEnd()), d->actionCollection, "feedstree_end" ); + new KAction( i18n("Go Left in Tree"), QString::null, "Ctrl+Left", listTabWidget, SLOT(slotItemLeft()), d->actionCollection, "feedstree_left" ); + new KAction( i18n("Go Right in Tree"), QString::null, "Ctrl+Right", listTabWidget, SLOT(slotItemRight()), d->actionCollection, "feedstree_right" ); + new KAction( i18n("Go Up in Tree"), QString::null, "Ctrl+Up", listTabWidget, SLOT(slotItemUp()), d->actionCollection, "feedstree_up" ); + new KAction( i18n("Go Down in Tree"), QString::null, "Ctrl+Down", listTabWidget, SLOT(slotItemDown()), d->actionCollection, "feedstree_down" ); +} + +void ActionManagerImpl::initTabWidget(TabWidget* tabWidget) +{ + if (d->tabWidget) + return; + else + d->tabWidget = tabWidget; + + new KAction(i18n("Select Next Tab"), "", "Ctrl+Period", d->tabWidget, SLOT(slotNextTab()),actionCollection(), "select_next_tab"); + new KAction(i18n("Select Previous Tab"), "", "Ctrl+Comma", d->tabWidget, SLOT(slotPreviousTab()),actionCollection(), "select_previous_tab"); + new KAction( i18n("Detach Tab"), "tab_breakoff", CTRL+SHIFT+Key_B, d->tabWidget, SLOT(slotDetachTab()), actionCollection(), "tab_detach" ); + new KAction( i18n("Copy Link Address"), QString::null, QString::null, d->tabWidget, SLOT(slotCopyLinkAddress()), actionCollection(), "tab_copylinkaddress" ); + new KAction( i18n("&Close Tab"), "tab_remove", KStdAccel::close(), d->tabWidget, SLOT(slotCloseTab()), actionCollection(), "tab_remove" ); +} + +QWidget* ActionManagerImpl::container(const char* name) +{ + return d->part->factory()->container(name, d->part); +} + + +KActionCollection* ActionManagerImpl::actionCollection() +{ + return d->actionCollection; +} +KAction* ActionManagerImpl::action(const char* name, const char* classname) +{ + return d->actionCollection != 0 ? d->actionCollection->action(name, classname) : 0; +} + +} // namespace Akregator + +#include "actionmanagerimpl.moc" diff --git a/akregator/src/actionmanagerimpl.h b/akregator/src/actionmanagerimpl.h new file mode 100644 index 000000000..eefa3525e --- /dev/null +++ b/akregator/src/actionmanagerimpl.h @@ -0,0 +1,97 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_ACTIONMANAGERIMPL_H +#define AKREGATOR_ACTIONMANAGERIMPL_H + +#include "actionmanager.h" + +class QStringList; +class QWidget; +class KAction; +class KActionMenu; +class KActionCollection; + +namespace Akregator { + +class ArticleListView; +class ArticleViewer; +class ListTabWidget; +class Part; +class TrayIcon; +class Tag; +class TagSet; +class TreeNode; +class View; +class TabWidget; + +/** Akregator-specific implementation of the ActionManager interface */ +class ActionManagerImpl : public ActionManager +{ + Q_OBJECT + + public: + ActionManagerImpl(Part* part, QObject* parent=0, const char* name=0); + virtual ~ActionManagerImpl(); + + virtual KAction* action(const char* name, const char* classname=0); + virtual QWidget* container(const char* name); + + void initView(View* view); + void initTrayIcon(TrayIcon* trayIcon); + void initArticleViewer(ArticleViewer* articleViewer); + void initArticleListView(ArticleListView* articleList); + void initListTabWidget(ListTabWidget* listTabWidget); + void initTabWidget(TabWidget* tabWidget); + void setTagSet(TagSet* tagSet); + + public slots: + + /** fills the remove tag menu with the given tags + enables/disables tag menu action according to @c enabled */ + void slotUpdateTagActions(bool enabled, const QStringList& tagIds); + + void slotNodeSelected(TreeNode* node); + + void slotTagAdded(const Tag& tag); + void slotTagRemoved(const Tag& tag); + + protected: + + KActionCollection* actionCollection(); + + private: + + void initPart(); + + friend class NodeSelectVisitor; + class NodeSelectVisitor; + + class ActionManagerImplPrivate; + ActionManagerImplPrivate* d; +}; + +} // namespace Akregator + +#endif // AKREGATOR_ACTIONMANAGERIMPL_H diff --git a/akregator/src/addfeeddialog.cpp b/akregator/src/addfeeddialog.cpp new file mode 100644 index 000000000..c6fa6be18 --- /dev/null +++ b/akregator/src/addfeeddialog.cpp @@ -0,0 +1,123 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "feed.h" +#include "addfeeddialog.h" + +#include <qcheckbox.h> + +#include <kapplication.h> +#include <kurl.h> +#include <klocale.h> +#include <klineedit.h> +#include <kiconloader.h> +#include <kicontheme.h> +#include <kdebug.h> +#include <ksqueezedtextlabel.h> +#include <kmessagebox.h> + +namespace Akregator { + +AddFeedWidget::AddFeedWidget(QWidget *parent, const char *name) + : AddFeedWidgetBase(parent, name) +{ + pixmapLabel1->setPixmap(kapp->iconLoader()->loadIcon( "package_network",KIcon::Desktop,KIcon::SizeHuge, KIcon::DefaultState, 0, true)); + statusLabel->setText(QString::null); +} + +AddFeedWidget::~AddFeedWidget() +{} + +AddFeedDialog::AddFeedDialog(QWidget *parent, const char *name) + : KDialogBase(KDialogBase::Swallow, Qt::WStyle_DialogBorder, parent, name, true, i18n("Add Feed"), KDialogBase::Ok|KDialogBase::Cancel) +{ + widget = new AddFeedWidget(this); + connect(widget->urlEdit, SIGNAL(textChanged(const QString&)), this, SLOT(textChanged(const QString&))); + enableButtonOK(false); + setMainWidget(widget); +} + +AddFeedDialog::~AddFeedDialog() +{} + +void AddFeedDialog::setURL(const QString& t) +{ + widget->urlEdit->setText(t); +} + +void AddFeedDialog::slotOk( ) +{ + enableButtonOK(false); + feedURL = widget->urlEdit->text().stripWhiteSpace(); + + Feed *f=new Feed(); + + feed=f; + + // HACK: make weird wordpress links ("feed:http://foobar/rss") work + if (feedURL.startsWith("feed:")) + feedURL = feedURL.right( feedURL.length() - 5 ); + + if (feedURL.find(":/") == -1) + feedURL.prepend("http://"); + f->setXmlUrl(feedURL); + + widget->statusLabel->setText( i18n("Downloading %1").arg(feedURL) ); + + connect( feed, SIGNAL(fetched(Feed* )), + this, SLOT(fetchCompleted(Feed *)) ); + connect( feed, SIGNAL(fetchError(Feed* )), + this, SLOT(fetchError(Feed *)) ); + connect( feed, SIGNAL(fetchDiscovery(Feed* )), + this, SLOT(fetchDiscovery(Feed *)) ); + + f->fetch(true); +} + +void AddFeedDialog::fetchCompleted(Feed */*f*/) +{ + KDialogBase::slotOk(); +} + +void AddFeedDialog::fetchError(Feed *) +{ + KMessageBox::error(this, i18n("Feed not found from %1.").arg(feedURL)); + KDialogBase::slotCancel(); +} + +void AddFeedDialog::fetchDiscovery(Feed *f) +{ + widget->statusLabel->setText( i18n("Feed found, downloading...") ); + feedURL=f->xmlUrl(); +} + +void AddFeedDialog::textChanged(const QString& text) +{ + enableButtonOK(!text.isEmpty()); +} + +} // namespace Akregator + +#include "addfeeddialog.moc" +// vim: ts=4 sw=4 et diff --git a/akregator/src/addfeeddialog.h b/akregator/src/addfeeddialog.h new file mode 100644 index 000000000..e7e67a165 --- /dev/null +++ b/akregator/src/addfeeddialog.h @@ -0,0 +1,74 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATORADDFEEDDIALOG_H +#define AKREGATORADDFEEDDIALOG_H + +#include "addfeedwidgetbase.h" + +#include <kdialogbase.h> + +using namespace RSS; + + + +namespace Akregator +{ + + class Feed; + + class AddFeedWidget : public AddFeedWidgetBase + { + Q_OBJECT + public: + AddFeedWidget(QWidget *parent = 0, const char *name = 0); + ~AddFeedWidget(); + }; + + class AddFeedDialog : public KDialogBase + { + Q_OBJECT + public: + AddFeedDialog(QWidget *parent = 0, const char *name = 0); + ~AddFeedDialog(); + + void setURL(const QString& t); + Feed *feed; + + public slots: + void slotOk( ); + void fetchCompleted(Feed *); + void fetchDiscovery(Feed *); + void fetchError(Feed *); + + private slots: + void textChanged(const QString&); + + private: + AddFeedWidget *widget; + QString feedURL; + }; +} + +#endif diff --git a/akregator/src/addfeedwidgetbase.ui b/akregator/src/addfeedwidgetbase.ui new file mode 100644 index 000000000..6b55d5fb1 --- /dev/null +++ b/akregator/src/addfeedwidgetbase.ui @@ -0,0 +1,145 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>Akregator::AddFeedWidgetBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>AddFeedWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>567</width> + <height>176</height> + </rect> + </property> + <property name="caption"> + <string>Add Feed</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout16</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>pixmapLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout15</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>urlEdit</cstring> + </property> + <property name="minimumSize"> + <size> + <width>200</width> + <height>0</height> + </size> + </property> + </widget> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Add New Source</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Feed &URL:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>urlEdit</cstring> + </property> + </widget> + </grid> + </widget> + </hbox> + </widget> + <widget class="KSqueezedTextLabel"> + <property name="name"> + <cstring>statusLabel</cstring> + </property> + <property name="text"> + <string>Status</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>50</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>ksqueezedtextlabel.h</includehint> +</includehints> +</UI> diff --git a/akregator/src/akregator.desktop b/akregator/src/akregator.desktop new file mode 100644 index 000000000..394cf1860 --- /dev/null +++ b/akregator/src/akregator.desktop @@ -0,0 +1,111 @@ +[Desktop Entry] +Name=Akregator +Name[ne]=एक्रिगेटर +Exec=akregator %i %m -caption "%c" +Icon=akregator +Type=Application +DocPath=akregator/index.html +GenericName=RSS Feed Reader +GenericName[af]=RSS stroom leser +GenericName[bg]=Четец на новости RSS +GenericName[ca]=Lector d'enllaços RSS +GenericName[cs]=Čtení RSS kanálů +GenericName[da]=RSS-Kildelæser +GenericName[de]=Anzeige von RSS-Nachrichtenquellen +GenericName[el]=Αναγνώστης ροών RSS +GenericName[eo]=RSS-flulegilo por diskutrondoj +GenericName[es]=Lector de orígenes RSS +GenericName[et]=RSS-uudistevoogude lugemisvahend +GenericName[eu]=RSS iturri Irakurlea +GenericName[fa]=خوانندۀ خوراندن RSS +GenericName[fi]=RSS-syötelukija +GenericName[fr]=Lecteur de flux RSS +GenericName[fy]=RSS-nijsoanfierlêzer +GenericName[ga]=Léitheoir na bhFothaí RSS +GenericName[gl]=Lector de Novas por RSS +GenericName[he]=קורא חדשות (RSS) +GenericName[hu]=RSS hírolvasó +GenericName[is]=RSS fréttaforrit +GenericName[it]=Lettore Fonti RSS +GenericName[ja]=RSS ニュースリーダー +GenericName[ka]=RSS კვების წამკითხველი +GenericName[kk]=RSS жаңалықтарын оқу +GenericName[km]=កម្មវិធីអានមតិព័ត៌មាន RSS +GenericName[ko]=RSS 피드 리더 +GenericName[lt]=RSS kanalų skaitytuvė +GenericName[ms]= Pembaca Suapan RSS +GenericName[nb]=Leser for RSS-kanal +GenericName[nds]=Kieker för RSS-Mellenströöm +GenericName[ne]=आरएसएस फिड रिडर +GenericName[nl]=RSS-feedlezer +GenericName[nn]=RSS-lesar +GenericName[pa]=RSS ਫੀਡ ਰੀਡਰ +GenericName[pl]=Program do przeglądania kanałów RSS +GenericName[pt]=Leitor de Fontes RSS +GenericName[pt_BR]=Leitor de Fontes de Notícias RSS +GenericName[ru]=Чтение лент новостей +GenericName[se]=RSS-gáldu logan +GenericName[sk]=Prehliadač RSS kŕmitok +GenericName[sl]=Bralnik virov RSS +GenericName[sr]=Читач RSS довода +GenericName[sr@Latn]=Čitač RSS dovoda +GenericName[sv]=Läsare av RSS-kanaler +GenericName[ta]=RSS பீஃட் வாசிப்பான் +GenericName[tr]=RSS Haber Kaynağı Okuyucu +GenericName[uk]=Програма для читання подач RSS +GenericName[uz]=RSS yangiliklarni oʻquvchi +GenericName[uz@cyrillic]=RSS янгиликларни ўқувчи +GenericName[zh_CN]=RSS 种子阅读器 +GenericName[zh_TW]=RSS Feed 閱讀器 +Comment=An RSS Aggregator for KDE +Comment[af]='n RSS leser vir KDE +Comment[bg]=Четец на новости във формат RSS +Comment[ca]=Un lector RSS per KDE +Comment[cs]=RSS agregátor pro KDE +Comment[da]=En RSS Aggregator for KDE +Comment[de]=Ein RSS-Nachrichtensammler für KDE +Comment[el]=Ένας συσσωρευτής RSS για το KDE +Comment[eo]=RSS-akumulilo por KDE +Comment[es]=Un agregador de RSS para KDE +Comment[et]=KDE RSS-uudistevoogude lugemisvahend +Comment[eu]=KDE-ren RSS gehitzailea +Comment[fa]=انبوههساز RSSای برای KDE +Comment[fi]=RSS-syötelukija +Comment[fr]=Un lecteur de flux RSS pour KDE +Comment[fy]=In RSS-agregator foar KDE +Comment[ga]=Comhbhailitheoir RSS le haghaidh KDE +Comment[gl]=Un agregador de RSS para KDE +Comment[hu]=KDE-s hírolvasó RSS hírcsatornákhoz +Comment[is]=RSS fréttaforrit fyrir KDE +Comment[it]=Un concentratore KDE per RSS +Comment[ja]=KDE 用 RSS アグリゲータ +Comment[ka]= RSS აგრეგატი KDE-სთვის +Comment[kk]=KDE-нің RSS жаңалық агрегаторы +Comment[km]=កម្មវិធីអាន RSS សម្រាប់ KDE +Comment[ko]=KDE용 RSS 리더 +Comment[ms]=Pengagregat RSS untuk KDE +Comment[nb]=En RSS-oppsamler for KDE +Comment[nds]=Tosamensteller för RSS-Mellenströöm vun KDE +Comment[ne]=केडीई का लागि एउटा आरएसएस एक्रिगेटर +Comment[nl]=Een RSS-agregator voor KDE +Comment[nn]=Ein RSS-lesar for KDE +Comment[pl]=Program podsumowujący kanały RSS dla KDE +Comment[pt]=Um Agregador RSS do KDE +Comment[pt_BR]=Um Agregador RSS para o KDE +Comment[ru]=Чтение лент новостей RSS +Comment[se]=KDE:a RSS-logan +Comment[sk]=Zhromažďovač RSS pre KDE +Comment[sl]=Zbiralnik RSS za KDE +Comment[sr]=Сакупљач RSS довода за KDE +Comment[sr@Latn]=Sakupljač RSS dovoda za KDE +Comment[sv]=En RSS-samlare för KDE +Comment[ta]=கேடியிக்கான ஒரு RSS சேர்ப்பான் +Comment[tr]=Bir KDE RSS Okuyucusu +Comment[uk]=Агрегатор RSS для KDE +Comment[uz]=KDE uchun RSS yangiliklarni oʻquvchi +Comment[uz@cyrillic]=KDE учун RSS янгиликларни ўқувчи +Comment[zh_CN]=KDE RSS 新闻收集器 +Comment[zh_TW]=KDE 的 RSS 收集器 +Terminal=false +Categories=Qt;KDE;Network;News; +X-DCOP-ServiceType=Unique diff --git a/akregator/src/akregator.kcfg b/akregator/src/akregator.kcfg new file mode 100644 index 000000000..31dafeb37 --- /dev/null +++ b/akregator/src/akregator.kcfg @@ -0,0 +1,218 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="akregatorrc" /> + <group name="View" > + <entry key="Show Quick Filter" type="Bool" > + <label>Show Quick Filter</label> + <whatsthis>Show Quick Filter Bar</whatsthis> + <default>true</default> + </entry> + <entry key="Status Filter" type="Int" > + <label>Status Filter</label> + <whatsthis>Stores the last status filter setting</whatsthis> + <default>0</default> + </entry> + <entry key="Text Filter" type="String" > + <label>Text Filter</label> + <whatsthis>Stores the last search line text</whatsthis> + </entry> + <entry key="View Mode" type="Int" > + <label>View Mode</label> + <whatsthis>Article display mode.</whatsthis> + <default>0</default> + </entry> + <entry key="Splitter 1 Sizes" type="IntList" > + <label>Sizes for first splitter</label> + <whatsthis>First (usually vertical) splitter widget sizes.</whatsthis> + <default>225,650</default> + </entry> + <entry key="Splitter 2 Sizes" type="IntList" > + <label>Sizes for second splitter</label> + <whatsthis>Second (usually horizontal) splitter widget sizes.</whatsthis> + <default>50,350</default> + </entry> + </group> + <group name="Appearance"> + <entry key="Standard Font" type="String" /> + <entry key="Fixed Font" type="String" /> + <entry key="Serif Font" type="String" /> + <entry key="Sans Serif Font" type="String" /> + </group> + <group name="HTML Settings" > + <entry key="Fonts" type="StringList" /> + <entry key="MediumFontSize" type="Int"> + <default>12</default> + </entry> + <entry key="MinimumFontSize" type="Int"> + <default>8</default> + </entry> + <entry key="UnderlineLinks" type="Bool"> + </entry> + </group> + <group name="Archive"> + <entry key="ArchiveMode" type="Enum"> + <label>Archive Mode</label> + <default>keepAllArticles</default> + <choices> + <choice name="keepAllArticles"> + <label>Keep All Articles</label> + <whatsthis>Save an unlimited number of articles.</whatsthis> + </choice> + <choice name="limitArticleNumber"> + <label>Limit Number of Articles</label> + <whatsthis>Limit the number of articles in a feed</whatsthis> + </choice> + <choice name="limitArticleAge"> + <label>Delete Expired Articles</label> + <whatsthis>Delete expired articles</whatsthis> + </choice> + <choice name="disableArchiving"> + <label>Disable Archiving</label> + <whatsthis>Do not save any articles</whatsthis> + </choice> + </choices> + </entry> + <entry key="Max Article Age" type="Int" > + <label>Expiry Age</label> + <whatsthis>Default expiry age for articles in days.</whatsthis> + <default>60</default> + </entry> + <entry key="Max Article Number" type="Int" > + <label>Article Limit</label> + <whatsthis>Number of articles to keep per feed.</whatsthis> + <default>1000</default> + </entry> + <entry key="Do Not Expire Important Articles" type="Bool"> + <label>Do Not Expire Important Articles</label> + <whatsthis>When this option is enabled, articles you marked as important will not be removed when limit the archive size by either age or number of the articles.</whatsthis> + <default>true</default> + </entry> + </group> + <group name="Network" > + <entry key="Concurrent Fetches" type="Int" > + <label>Concurrent Fetches</label> + <whatsthis>Number of concurrent fetches</whatsthis> + <default>6</default> + </entry> + <entry key="Use HTML Cache" type="Bool" > + <label>Use HTML Cache</label> + <whatsthis>Use the KDE-wide HTML cache settings when downloading feeds, to avoid unnecessary traffic. Disable only when necessary.</whatsthis> + <default>true</default> + </entry> + </group> + <group name="General" > + <entry key="Fetch On Startup" type="Bool" > + <label>Fetch on startup</label> + <whatsthis>Fetch feedlist on startup.</whatsthis> + <default>false</default> + </entry> + <entry key="Mark All Feeds Read On Startup" type="Bool" > + <label>Mark all feeds as read on startup</label> + <whatsthis>Mark all feeds as read on startup.</whatsthis> + <default>false</default> + </entry> + <entry key="Use Interval Fetch" type="Bool" > + <label>Use interval fetching</label> + <whatsthis>Fetch all feeds every %1 minutes.</whatsthis> + <default>true</default> + </entry> + <entry key="Auto Fetch Interval" type="Int" > + <label>Interval for autofetching</label> + <whatsthis>Interval for autofetching in minutes.</whatsthis> + <default>30</default> + </entry> + <entry key="Use Notifications" type="Bool" > + <label>Use notifications</label> + <whatsthis>Specifies if the balloon notifications are used or not.</whatsthis> + <default>false</default> + </entry> + <entry key="Show Tray Icon" type="Bool" > + <label>Show tray icon</label> + <whatsthis>Specifies if the tray icon is shown or not.</whatsthis> + <default>true</default> + </entry> + </group> + <group name="Browser" > + <entry key="Close Button On Tabs" type="Bool"> + <label>Show close buttons on tabs</label> + <whatsthis>Show close buttons on tabs instead of icons</whatsthis> + <default>false</default> + </entry> + + <entry key="External Browser Use Kde Default" type="Bool" > + <label>Use default KDE web browser</label> + <whatsthis>Use KDE web browser when opening in external browser.</whatsthis> + <default>true</default> + </entry> + <entry key="External Browser Use Custom Command" type="Bool" > + <label>Use this command:</label> + <whatsthis>Use the specified command when opening in external browser.</whatsthis> + <default>false</default> + </entry> + <entry key="External Browser Custom Command" type="String" > + <whatsthis>Command to launch external browser. URL will substitute for %u.</whatsthis> + <default>firefox %u</default> + </entry> + <entry key="LMB Behaviour" type="Enum" > + <whatsthis>What the click with left mouse button should do.</whatsthis> + <default>OpenInInternalBrowser</default> + <choices> + <choice name="OpenInInternalBrowser" /> + <choice name="OpenInBackground" /> + <choice name="OpenInExternalBrowser" /> + </choices> + </entry> + <entry key="MMB Behaviour" type="Enum" > + <whatsthis>What the click with middle mouse button should do.</whatsthis> + <default>OpenInExternalBrowser</default> + <choices> + <choice name="OpenInInternalBrowser" /> + <choice name="OpenInBackground" /> + <choice name="OpenInExternalBrowser" /> + </choices> + </entry> + </group> + <group name="Viewer Columns" > + <entry key="Title Width" type="Int" > + <default>-1</default> + </entry> + <entry key="Feed Width" type="Int" > + <default>-1</default> + </entry> + <entry key="Date Width" type="Int" > + <default>-1</default> + </entry> + <entry key="Sort Column" type="Int" > + <default>2</default> + </entry> + <entry key="Sort Ascending" type="Bool" > + <default>false</default> + </entry> + </group> + <group name="Advanced" > + <entry key="Archive Backend" type="String" > + <label>Archive Backend</label> + <default>metakit</default> + </entry> + <entry key="Delay Mark Read" name="UseMarkReadDelay" type="Bool" > + <whatsthis>Whether to delay before marking an article as read upon selecting it.</whatsthis> + <default>true</default> + </entry> + <entry key="Mark Read Delay" name="MarkReadDelay" type="Int" > + <whatsthis>Configurable delay between selecting and article and it being marked as read.</whatsthis> + <default>0</default> + </entry> + <entry key="Reset Quick Filter On Node Change" type="Bool" > + <whatsthis>Resets the quick filter when changing feeds.</whatsthis> + <default>true</default> + </entry> + + <entry key="Show Tagging GUI" type="Bool" > + <label>Show Tagging GUI elements (unfinished)</label> + <default>false</default> + </entry> +</group> +</kcfg> diff --git a/akregator/src/akregator_export.h b/akregator/src/akregator_export.h new file mode 100644 index 000000000..9ac198266 --- /dev/null +++ b/akregator/src/akregator_export.h @@ -0,0 +1,28 @@ +/* + This file is part of Akregator. + + Copyright (C) 2006 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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. +*/ +#ifndef AKREGATOR_AKREGATOREXPORT_H +#define AKREGATOR_AKREGATOREXPORT_H + +#include <kdepimmacros.h> + +#define AKREGATOR_EXPORT KDE_EXPORT + +#endif diff --git a/akregator/src/akregator_options.h b/akregator/src/akregator_options.h new file mode 100644 index 000000000..2b4334baf --- /dev/null +++ b/akregator/src/akregator_options.h @@ -0,0 +1,45 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_OPTIONS_H +#define AKREGATOR_OPTIONS_H + +#include <kcmdlineargs.h> +#include <klocale.h> + +namespace Akregator { + +static KCmdLineOptions akregator_options[] = +{ + { "a", 0, 0 }, + { "addfeed <url>", I18N_NOOP( "Add a feed with the given URL" ), 0}, + { "g", 0, 0 }, + { "group <groupname>", I18N_NOOP( "When adding feeds, place them in this group" ), I18N_NOOP("Imported") }, + { "hide-mainwindow", I18N_NOOP( "Hide main window on startup" ), 0}, + KCmdLineLastOption +}; + +} + +#endif diff --git a/akregator/src/akregator_part.cpp b/akregator/src/akregator_part.cpp new file mode 100644 index 000000000..b5392c791 --- /dev/null +++ b/akregator/src/akregator_part.cpp @@ -0,0 +1,1035 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#include <dcopclient.h> +#include <kaboutdata.h> +#include <kaction.h> +#include <kactionclasses.h> +#include <kactioncollection.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kconfigdialog.h> +#include <kfiledialog.h> +#include <kglobalsettings.h> +#include <khtmldefaults.h> +#include <kinstance.h> +#include <kmainwindow.h> +#include <kmessagebox.h> +#include <knotifyclient.h> +#include <knotifydialog.h> +#include <kpopupmenu.h> +#include <kservice.h> +#include <kstandarddirs.h> +#include <kstdaction.h> +#include <ktempfile.h> +#include <ktrader.h> +#include <kio/netaccess.h> +#include <kparts/browserinterface.h> +#include <kparts/genericfactory.h> +#include <kparts/partmanager.h> + +#include <qfile.h> +#include <qobjectlist.h> +#include <qstringlist.h> +#include <qtimer.h> +#include <qwidgetlist.h> +#include <private/qucomextra_p.h> + +#include <cerrno> +#include <sys/types.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "aboutdata.h" +#include "actionmanagerimpl.h" +#include "akregator_part.h" +#include "akregator_view.h" +#include "akregatorconfig.h" +#include "articlefilter.h" +#include "articleinterceptor.h" +#include "configdialog.h" +#include "fetchqueue.h" +#include "frame.h" +#include "article.h" +#include "kernel.h" +#include "kcursorsaver.h" +#include "notificationmanager.h" +#include "pageviewer.h" +#include "plugin.h" +#include "pluginmanager.h" +#include "storage.h" +#include "storagefactory.h" +#include "storagefactorydummyimpl.h" +#include "storagefactoryregistry.h" +#include "speechclient.h" +#include "trayicon.h" +#include "tagset.h" +#include "tag.h" + +namespace Akregator { + +typedef KParts::GenericFactory<Part> AkregatorFactory; +K_EXPORT_COMPONENT_FACTORY( libakregatorpart, AkregatorFactory ) + +BrowserExtension::BrowserExtension(Part *p, const char *name) + : KParts::BrowserExtension( p, name ) +{ + m_part=p; +} + +void BrowserExtension::saveSettings() +{ + m_part->saveSettings(); +} + +class Part::ApplyFiltersInterceptor : public ArticleInterceptor +{ + public: + virtual void processArticle(Article& article) + { + Filters::ArticleFilterList list = Kernel::self()->articleFilterList(); + for (Filters::ArticleFilterList::ConstIterator it = list.begin(); it != list.end(); ++it) + (*it).applyTo(article); + } +}; + +Part::Part( QWidget *parentWidget, const char * /*widgetName*/, + QObject *parent, const char *name, const QStringList& ) + : DCOPObject("AkregatorIface") + , MyBasePart(parent, name) + , m_standardListLoaded(false) + , m_shuttingDown(false) + , m_mergedPart(0) + , m_view(0) + , m_backedUpList(false) + , m_storage(0) +{ + // we need an instance + setInstance( AkregatorFactory::instance() ); + + // start knotifyclient if not already started. makes it work for people who doesn't use full kde, according to kmail devels + KNotifyClient::startDaemon(); + + m_standardFeedList = KGlobal::dirs()->saveLocation("data", "akregator/data") + "/feeds.opml"; + + m_tagSetPath = KGlobal::dirs()->saveLocation("data", "akregator/data") + "/tagset.xml"; + + Backend::StorageFactoryDummyImpl* dummyFactory = new Backend::StorageFactoryDummyImpl(); + Backend::StorageFactoryRegistry::self()->registerFactory(dummyFactory, dummyFactory->key()); + loadPlugins(); // FIXME: also unload them! + + m_storage = 0; + Backend::StorageFactory* factory = Backend::StorageFactoryRegistry::self()->getFactory(Settings::archiveBackend()); + + QStringList storageParams; + + storageParams.append(QString("taggingEnabled=%1").arg(Settings::showTaggingGUI() ? "true" : "false")); + + if (factory != 0) + { + if (factory->allowsMultipleWriteAccess()) + { + m_storage = factory->createStorage(storageParams); + } + else + { + if (tryToLock(factory->name())) + m_storage = factory->createStorage(storageParams); + else + m_storage = dummyFactory->createStorage(storageParams); + } + } + + + if (!m_storage) // Houston, we have a problem + { + m_storage = Backend::StorageFactoryRegistry::self()->getFactory("dummy")->createStorage(storageParams); + + KMessageBox::error(parentWidget, i18n("Unable to load storage backend plugin \"%1\". No feeds are archived.").arg(Settings::archiveBackend()), i18n("Plugin error") ); + } + + Filters::ArticleFilterList list; + list.readConfig(Settings::self()->config()); + Kernel::self()->setArticleFilterList(list); + + m_applyFiltersInterceptor = new ApplyFiltersInterceptor(); + ArticleInterceptorManager::self()->addInterceptor(m_applyFiltersInterceptor); + + m_storage->open(true); + Kernel::self()->setStorage(m_storage); + Backend::Storage::setInstance(m_storage); // TODO: kill this one + + loadTagSet(m_tagSetPath); + + m_actionManager = new ActionManagerImpl(this); + ActionManager::setInstance(m_actionManager); + + m_view = new Akregator::View(this, parentWidget, m_actionManager, "akregator_view"); + m_actionManager->initView(m_view); + m_actionManager->setTagSet(Kernel::self()->tagSet()); + + m_extension = new BrowserExtension(this, "ak_extension"); + + connect(m_view, SIGNAL(setWindowCaption(const QString&)), this, SIGNAL(setWindowCaption(const QString&))); + connect(m_view, SIGNAL(setStatusBarText(const QString&)), this, SIGNAL(setStatusBarText(const QString&))); + connect(m_view, SIGNAL(setProgress(int)), m_extension, SIGNAL(loadingProgress(int))); + connect(m_view, SIGNAL(signalCanceled(const QString&)), this, SIGNAL(canceled(const QString&))); + connect(m_view, SIGNAL(signalStarted(KIO::Job*)), this, SIGNAL(started(KIO::Job*))); + connect(m_view, SIGNAL(signalCompleted()), this, SIGNAL(completed())); + + // notify the part that this is our internal widget + setWidget(m_view); + + TrayIcon* trayIcon = new TrayIcon( getMainWindow() ); + TrayIcon::setInstance(trayIcon); + m_actionManager->initTrayIcon(trayIcon); + + connect(trayIcon, SIGNAL(showPart()), this, SIGNAL(showPart())); + + if ( isTrayIconEnabled() ) + { + trayIcon->show(); + NotificationManager::self()->setWidget(trayIcon, instance()); + } + else + NotificationManager::self()->setWidget(getMainWindow(), instance()); + + connect( trayIcon, SIGNAL(quitSelected()), + kapp, SLOT(quit())) ; + + connect( m_view, SIGNAL(signalUnreadCountChanged(int)), trayIcon, SLOT(slotSetUnread(int)) ); + + connect(kapp, SIGNAL(shutDown()), this, SLOT(slotOnShutdown())); + + m_autosaveTimer = new QTimer(this); + connect(m_autosaveTimer, SIGNAL(timeout()), this, SLOT(slotSaveFeedList())); + m_autosaveTimer->start(5*60*1000); // 5 minutes + + setXMLFile("akregator_part.rc", true); + + initFonts(); + + RSS::FileRetriever::setUserAgent(QString("Akregator/%1; librss/remnants").arg(AKREGATOR_VERSION)); +} + +void Part::loadPlugins() +{ + // "[X-KDE-akregator-plugintype] == 'storage'" + KTrader::OfferList offers = PluginManager::query(); + + for( KTrader::OfferList::ConstIterator it = offers.begin(), end = offers.end(); it != end; ++it ) + { + Akregator::Plugin* plugin = PluginManager::createFromService(*it); + if (plugin) + plugin->init(); + } +} + +void Part::slotOnShutdown() +{ + m_shuttingDown = true; + + const QString lockLocation = locateLocal("data", "akregator/lock"); + KSimpleConfig config(lockLocation); + config.writeEntry("pid", -1); + config.sync(); + + m_autosaveTimer->stop(); + saveSettings(); + slotSaveFeedList(); + saveTagSet(m_tagSetPath); + m_view->slotOnShutdown(); + //delete m_view; + delete TrayIcon::getInstance(); + TrayIcon::setInstance(0L); + delete m_storage; + m_storage = 0; + //delete m_actionManager; +} + +void Part::slotSettingsChanged() +{ + NotificationManager::self()->setWidget(isTrayIconEnabled() ? TrayIcon::getInstance() : getMainWindow(), instance()); + + RSS::FileRetriever::setUseCache(Settings::useHTMLCache()); + + QStringList fonts; + fonts.append(Settings::standardFont()); + fonts.append(Settings::fixedFont()); + fonts.append(Settings::sansSerifFont()); + fonts.append(Settings::serifFont()); + fonts.append(Settings::standardFont()); + fonts.append(Settings::standardFont()); + fonts.append("0"); + Settings::setFonts(fonts); + + if (Settings::minimumFontSize() > Settings::mediumFontSize()) + Settings::setMediumFontSize(Settings::minimumFontSize()); + saveSettings(); + m_view->slotSettingsChanged(); + emit signalSettingsChanged(); +} +void Part::saveSettings() +{ + Kernel::self()->articleFilterList().writeConfig(Settings::self()->config()); + m_view->saveSettings(); +} + +Part::~Part() +{ + kdDebug() << "Part::~Part() enter" << endl; + if (!m_shuttingDown) + slotOnShutdown(); + kdDebug() << "Part::~Part(): leaving" << endl; + ArticleInterceptorManager::self()->removeInterceptor(m_applyFiltersInterceptor); + delete m_applyFiltersInterceptor; +} + +void Part::readProperties(KConfig* config) +{ + m_backedUpList = false; + openStandardFeedList(); + + if(m_view) + m_view->readProperties(config); +} + +void Part::saveProperties(KConfig* config) +{ + if (m_view) + { + slotSaveFeedList(); + m_view->saveProperties(config); + } +} + +bool Part::openURL(const KURL& url) +{ + m_file = url.path(); + return openFile(); +} + +void Part::openStandardFeedList() +{ + if ( !m_standardFeedList.isEmpty() && openURL(m_standardFeedList) ) + m_standardListLoaded = true; +} + +QDomDocument Part::createDefaultFeedList() +{ + QDomDocument doc; + QDomProcessingInstruction z = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\""); + doc.appendChild( z ); + + QDomElement root = doc.createElement( "opml" ); + root.setAttribute("version","1.0"); + doc.appendChild( root ); + + QDomElement head = doc.createElement( "head" ); + root.appendChild(head); + + QDomElement text = doc.createElement( "text" ); + text.appendChild(doc.createTextNode(i18n("Feeds"))); + head.appendChild(text); + + QDomElement body = doc.createElement( "body" ); + root.appendChild(body); + + QDomElement mainFolder = doc.createElement( "outline" ); + mainFolder.setAttribute("text","KDE"); + body.appendChild(mainFolder); + + QDomElement ak = doc.createElement( "outline" ); + ak.setAttribute("text",i18n("Akregator News")); + ak.setAttribute("xmlUrl","http://akregator.sf.net/rss2.php"); + mainFolder.appendChild(ak); + + QDomElement akb = doc.createElement( "outline" ); + akb.setAttribute("text",i18n("Akregator Blog")); + akb.setAttribute("xmlUrl","http://akregator.pwsp.net/blog/?feed=rss2"); + mainFolder.appendChild(akb); + + QDomElement dot = doc.createElement( "outline" ); + dot.setAttribute("text",i18n("KDE Dot News")); + dot.setAttribute("xmlUrl","http://www.kde.org/dotkdeorg.rdf"); + mainFolder.appendChild(dot); + + QDomElement plan = doc.createElement( "outline" ); + plan.setAttribute("text",i18n("Planet KDE")); + plan.setAttribute("xmlUrl","http://planetkde.org/rss20.xml"); + mainFolder.appendChild(plan); + + QDomElement apps = doc.createElement( "outline" ); + apps.setAttribute("text",i18n("KDE Apps")); + apps.setAttribute("xmlUrl","http://www.kde.org/dot/kde-apps-content.rdf"); + mainFolder.appendChild(apps); + + QDomElement look = doc.createElement( "outline" ); + look.setAttribute("text",i18n("KDE Look")); + look.setAttribute("xmlUrl","http://www.kde.org/kde-look-content.rdf"); + mainFolder.appendChild(look); + + return doc; +} + +bool Part::openFile() +{ + emit setStatusBarText(i18n("Opening Feed List...") ); + + QString str; + // m_file is always local so we can use QFile on it + QFile file(m_file); + + bool fileExists = file.exists(); + QString listBackup = m_storage->restoreFeedList(); + + QDomDocument doc; + + if (!fileExists) + { + doc = createDefaultFeedList(); + } + else + { + if (file.open(IO_ReadOnly)) + { + // Read OPML feeds list and build QDom tree. + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); // FIXME not all opmls are in utf8 + str = stream.read(); + file.close(); + } + + if (!doc.setContent(str)) + { + + if (file.size() > 0) // don't backup empty files + { + QString backup = m_file + "-backup." + QString::number(QDateTime::currentDateTime().toTime_t()); + + copyFile(backup); + + KMessageBox::error(m_view, i18n("<qt>The standard feed list is corrupted (invalid XML). A backup was created:<p><b>%2</b></p></qt>").arg(backup), i18n("XML Parsing Error") ); + } + + if (!doc.setContent(listBackup)) + doc = createDefaultFeedList(); + } + } + + if (!m_view->loadFeeds(doc)) + { + if (file.size() > 0) // don't backup empty files + { + QString backup = m_file + "-backup." + QString::number(QDateTime::currentDateTime().toTime_t()); + copyFile(backup); + + KMessageBox::error(m_view, i18n("<qt>The standard feed list is corrupted (no valid OPML). A backup was created:<p><b>%2</b></p></qt>").arg(backup), i18n("OPML Parsing Error") ); + } + m_view->loadFeeds(createDefaultFeedList()); + } + + emit setStatusBarText(QString::null); + + + if( Settings::markAllFeedsReadOnStartup() ) + m_view->slotMarkAllFeedsRead(); + + if (Settings::fetchOnStartup()) + m_view->slotFetchAllFeeds(); + + return true; +} + +void Part::slotSaveFeedList() +{ + // don't save to the standard feed list, when it wasn't completely loaded before + if (!m_standardListLoaded) + return; + + // the first time we overwrite the feed list, we create a backup + if (!m_backedUpList) + { + QString backup = m_file + "~"; + + if (copyFile(backup)) + m_backedUpList = true; + } + + QString xmlStr = m_view->feedListToOPML().toString(); + m_storage->storeFeedList(xmlStr); + + QFile file(m_file); + if (file.open(IO_WriteOnly) == false) + { + //FIXME: allow to save the feedlist into different location -tpr 20041118 + KMessageBox::error(m_view, i18n("Access denied: cannot save feed list (%1)").arg(m_file), i18n("Write error") ); + return; + } + + // use QTextStream to dump the text to the file + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + + // Write OPML data file. + // Archive data files are saved elsewhere. + + stream << xmlStr << endl; + + file.close(); +} + +bool Part::isTrayIconEnabled() const +{ + return Settings::showTrayIcon(); +} + +bool Part::mergePart(KParts::Part* part) +{ + if (part != m_mergedPart) + { + if (!factory()) + { + if (m_mergedPart) + removeChildClient(m_mergedPart); + else + insertChildClient(part); + } + else + { + if (m_mergedPart) { + factory()->removeClient(m_mergedPart); + if (childClients()->containsRef(m_mergedPart)) + removeChildClient(m_mergedPart); + } + if (part) + factory()->addClient(part); + } + + m_mergedPart = part; + } + return true; +} + +QWidget* Part::getMainWindow() +{ + // this is a dirty fix to get the main window used for the tray icon + + QWidgetList *l = kapp->topLevelWidgets(); + QWidgetListIt it( *l ); + QWidget *wid; + + // check if there is an akregator main window + while ( (wid = it.current()) != 0 ) + { + ++it; + //kdDebug() << "win name: " << wid->name() << endl; + if (QString(wid->name()) == "akregator_mainwindow") + { + delete l; + return wid; + } + } + // if not, check for kontact main window + QWidgetListIt it2( *l ); + while ( (wid = it2.current()) != 0 ) + { + ++it2; + if (QString(wid->name()).startsWith("kontact-mainwindow")) + { + delete l; + return wid; + } + } + delete l; + return 0; +} + +void Part::loadTagSet(const QString& path) +{ + QDomDocument doc; + + QFile file(path); + if (file.open(IO_ReadOnly)) + { + doc.setContent(file.readAll()); + file.close(); + } + // if we can't load the tagset from the xml file, check for the backup in the backend + if (doc.isNull()) + { + doc.setContent(m_storage->restoreTagSet()); + } + + if (!doc.isNull()) + { + Kernel::self()->tagSet()->readFromXML(doc); + } + else + { + Kernel::self()->tagSet()->insert(Tag("http://akregator.sf.net/tags/Interesting", i18n("Interesting"))); + } +} + +void Part::saveTagSet(const QString& path) +{ + QString xmlStr = Kernel::self()->tagSet()->toXML().toString(); + + m_storage->storeTagSet(xmlStr); + + QFile file(path); + + if ( file.open(IO_WriteOnly) ) + { + + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + stream << xmlStr << "\n"; + file.close(); + } +} + +void Part::importFile(const KURL& url) +{ + QString filename; + + bool isRemote = false; + + if (url.isLocalFile()) + filename = url.path(); + else + { + isRemote = true; + + if (!KIO::NetAccess::download(url, filename, m_view) ) + { + KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() ); + return; + } + } + + QFile file(filename); + if (file.open(IO_ReadOnly)) + { + // Read OPML feeds list and build QDom tree. + QDomDocument doc; + if (doc.setContent(file.readAll())) + m_view->importFeeds(doc); + else + KMessageBox::error(m_view, i18n("Could not import the file %1 (no valid OPML)").arg(filename), i18n("OPML Parsing Error") ); + } + else + KMessageBox::error(m_view, i18n("The file %1 could not be read, check if it exists or if it is readable for the current user.").arg(filename), i18n("Read Error")); + + if (isRemote) + KIO::NetAccess::removeTempFile(filename); +} + +void Part::exportFile(const KURL& url) +{ + if (url.isLocalFile()) + { + QFile file(url.path()); + + if ( file.exists() && + KMessageBox::questionYesNo(m_view, + i18n("The file %1 already exists; do you want to overwrite it?").arg(file.name()), + i18n("Export"), + i18n("Overwrite"), + KStdGuiItem::cancel()) == KMessageBox::No ) + return; + + if ( !file.open(IO_WriteOnly) ) + { + KMessageBox::error(m_view, i18n("Access denied: cannot write to file %1").arg(file.name()), i18n("Write Error") ); + return; + } + + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + + stream << m_view->feedListToOPML().toString() << "\n"; + file.close(); + } + else + { + KTempFile tmpfile; + tmpfile.setAutoDelete(true); + + QTextStream stream(tmpfile.file()); + stream.setEncoding(QTextStream::UnicodeUTF8); + + stream << m_view->feedListToOPML().toString() << "\n"; + tmpfile.close(); + + if (!KIO::NetAccess::upload(tmpfile.name(), url, m_view)) + KMessageBox::error(m_view, KIO::NetAccess::lastErrorString() ); + } +} + +void Part::fileImport() +{ + KURL url = KFileDialog::getOpenURL( QString::null, + "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)") + +"\n*|" + i18n("All Files") ); + + if (!url.isEmpty()) + importFile(url); +} + + void Part::fileExport() +{ + KURL url= KFileDialog::getSaveURL( QString::null, + "*.opml *.xml|" + i18n("OPML Outlines (*.opml, *.xml)") + +"\n*|" + i18n("All Files") ); + + if ( !url.isEmpty() ) + exportFile(url); +} + +void Part::fileGetFeeds() +{ + /*GetFeeds *gf = new GetFeeds(); + gf->show();*/ + //KNS::DownloadDialog::open("akregator/feeds", i18n("Get New Feeds")); +} + +void Part::fileSendArticle(bool attach) +{ + // FIXME: you have to open article to tab to be able to send... + QString title, text; + + text = m_view->currentFrame()->part()->url().prettyURL(); + if(text.isEmpty() || text.isNull()) + return; + + title = m_view->currentFrame()->title(); + + if(attach) { + kapp->invokeMailer("", + "", + "", + title, + text, + "", + text); + } + else { + kapp->invokeMailer("", + "", + "", + title, + text); + } +} + +void Part::fetchAllFeeds() +{ + m_view->slotFetchAllFeeds(); +} + +void Part::fetchFeedUrl(const QString&s) +{ + kdDebug() << "fetchFeedURL==" << s << endl; +} + +void Part::addFeedsToGroup(const QStringList& urls, const QString& group) +{ + for (QStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it) + { + kdDebug() << "Akregator::Part::addFeedToGroup adding feed with URL " << *it << " to group " << group << endl; + m_view->addFeedToGroup(*it, group); + } + NotificationManager::self()->slotNotifyFeeds(urls); +} + +void Part::addFeed() +{ + m_view->slotFeedAdd(); +} + +KAboutData *Part::createAboutData() +{ + return new Akregator::AboutData; +} + +void Part::showKNotifyOptions() +{ + KAboutData* about = new Akregator::AboutData; + KNotifyDialog::configure(m_view, "akregator_knotify_config", about); + delete about; +} + +void Part::showOptions() +{ + if ( KConfigDialog::showDialog( "settings" ) ) + return; + + KConfigDialog* dialog = new ConfigDialog( m_view, "settings", Settings::self() ); + + connect( dialog, SIGNAL(settingsChanged()), + this, SLOT(slotSettingsChanged()) ); + connect( dialog, SIGNAL(settingsChanged()), + TrayIcon::getInstance(), SLOT(settingsChanged()) ); + + dialog->show(); +} + +void Part::partActivateEvent(KParts::PartActivateEvent* event) +{ + if (factory() && m_mergedPart) + { + if (event->activated()) + factory()->addClient(m_mergedPart); + else + factory()->removeClient(m_mergedPart); + } + + MyBasePart::partActivateEvent(event); +} + +KParts::Part* Part::hitTest(QWidget *widget, const QPoint &globalPos) +{ + bool child = false; + QWidget *me = this->widget(); + while (widget) { + if (widget == me) { + child = true; + break; + } + if (!widget) { + break; + } + widget = widget->parentWidget(); + } + if (m_view && m_view->currentFrame() && child) { + return m_view->currentFrame()->part(); + } else { + return MyBasePart::hitTest(widget, globalPos); + } +} + +void Part::initFonts() +{ + QStringList fonts = Settings::fonts(); + if (fonts.isEmpty()) + { + fonts.append(KGlobalSettings::generalFont().family()); + fonts.append(KGlobalSettings::fixedFont().family()); + fonts.append(KGlobalSettings::generalFont().family()); + fonts.append(KGlobalSettings::generalFont().family()); + fonts.append("0"); + } + Settings::setFonts(fonts); + if (Settings::standardFont().isEmpty()) + Settings::setStandardFont(fonts[0]); + if (Settings::fixedFont().isEmpty()) + Settings::setFixedFont(fonts[1]); + if (Settings::sansSerifFont().isEmpty()) + Settings::setSansSerifFont(fonts[2]); + if (Settings::serifFont().isEmpty()) + Settings::setSerifFont(fonts[3]); + + KConfig* conf = Settings::self()->config(); + conf->setGroup("HTML Settings"); + + KConfig konq("konquerorrc", true, false); + konq.setGroup("HTML Settings"); + + if (!conf->hasKey("MinimumFontSize")) + { + int minfs; + if (konq.hasKey("MinimumFontSize")) + minfs = konq.readNumEntry("MinimumFontSize"); + else + minfs = KGlobalSettings::generalFont().pointSize(); + kdDebug() << "Part::initFonts(): set MinimumFontSize to " << minfs << endl; + Settings::setMinimumFontSize(minfs); + } + + if (!conf->hasKey("MediumFontSize")) + { + int medfs; + if (konq.hasKey("MediumFontSize")) + medfs = konq.readNumEntry("MediumFontSize"); + else + medfs = KGlobalSettings::generalFont().pointSize(); + kdDebug() << "Part::initFonts(): set MediumFontSize to " << medfs << endl; + Settings::setMediumFontSize(medfs); + } + + if (!conf->hasKey("UnderlineLinks")) + { + bool underline = true; + if (konq.hasKey("UnderlineLinks")) + underline = konq.readBoolEntry("UnderlineLinks"); + + kdDebug() << "Part::initFonts(): set UnderlineLinks to " << underline << endl; + Settings::setUnderlineLinks(underline); + } + +} + +bool Part::copyFile(const QString& backup) +{ + QFile file(m_file); + + if (file.open(IO_ReadOnly)) + { + QFile backupFile(backup); + if (backupFile.open(IO_WriteOnly)) + { + QTextStream in(&file); + QTextStream out(&backupFile); + while (!in.atEnd()) + out << in.readLine(); + backupFile.close(); + file.close(); + return true; + } + else + { + file.close(); + return false; + } + } + return false; +} + +static QString getMyHostName() +{ + char hostNameC[256]; + // null terminate this C string + hostNameC[255] = 0; + // set the string to 0 length if gethostname fails + if(gethostname(hostNameC, 255)) + hostNameC[0] = 0; + return QString::fromLocal8Bit(hostNameC); +} + +// taken from KMail +bool Part::tryToLock(const QString& backendName) +{ +// Check and create a lock file to prevent concurrent access to metakit archive + QString appName = kapp->instanceName(); + if ( appName.isEmpty() ) + appName = "akregator"; + + QString programName; + const KAboutData *about = kapp->aboutData(); + if ( about ) + programName = about->programName(); + if ( programName.isEmpty() ) + programName = i18n("Akregator"); + + QString lockLocation = locateLocal("data", "akregator/lock"); + KSimpleConfig config(lockLocation); + int oldPid = config.readNumEntry("pid", -1); + const QString oldHostName = config.readEntry("hostname"); + const QString oldAppName = config.readEntry( "appName", appName ); + const QString oldProgramName = config.readEntry( "programName", programName ); + const QString hostName = getMyHostName(); + bool first_instance = false; + if ( oldPid == -1 ) + first_instance = true; + // check if the lock file is stale by trying to see if + // the other pid is currently running. + // Not 100% correct but better safe than sorry + else if (hostName == oldHostName && oldPid != getpid()) { + if ( kill(oldPid, 0) == -1 ) + first_instance = ( errno == ESRCH ); + } + + if ( !first_instance ) + { + QString msg; + if ( oldHostName == hostName ) + { + // this can only happen if the user is running this application on + // different displays on the same machine. All other cases will be + // taken care of by KUniqueApplication() + if ( oldAppName == appName ) + msg = i18n("<qt>%1 already seems to be running on another display on " + "this machine. <b>Running %2 more than once is not supported " + "by the %3 backend and " + "can cause the loss of archived articles and crashes at startup.</b> " + "You should disable the archive for now " + "unless you are sure that %2 is not already running.</qt>") + .arg( programName, programName, backendName ); + // QString::arg( st ) only replaces the first occurrence of %1 + // with st while QString::arg( s1, s2 ) replacess all occurrences + // of %1 with s1 and all occurrences of %2 with s2. So don't + // even think about changing the above to .arg( programName ). + else + msg = i18n("<qt>%1 seems to be running on another display on this " + "machine. <b>Running %1 and %2 at the same " + "time is not supported by the %3 backend and can cause " + "the loss of archived articles and crashes at startup.</b> " + "You should disable the archive for now " + "unless you are sure that %2 is not already running.</qt>") + .arg( oldProgramName, programName, backendName ); + } + else + { + if ( oldAppName == appName ) + msg = i18n("<qt>%1 already seems to be running on %2. <b>Running %1 more " + "than once is not supported by the %3 backend and can cause " + "the loss of archived articles and crashes at startup.</b> " + "You should disable the archive for now " + "unless you are sure that it is " + "not already running on %2.</qt>") + .arg( programName, oldHostName, backendName ); + else + msg = i18n("<qt>%1 seems to be running on %3. <b>Running %1 and %2 at the " + "same time is not supported by the %4 backend and can cause " + "the loss of archived articles and crashes at startup.</b> " + "You should disable the archive for now " + "unless you are sure that %1 is " + "not running on %3.</qt>") + .arg( oldProgramName, programName, oldHostName, backendName ); + } + + KCursorSaver idle( KBusyPtr::idle() ); + if ( KMessageBox::No == + KMessageBox::warningYesNo( 0, msg, QString::null, + i18n("Force Access"), + i18n("Disable Archive")) ) + { + return false; + } + } + + config.writeEntry("pid", getpid()); + config.writeEntry("hostname", hostName); + config.writeEntry( "appName", appName ); + config.writeEntry( "programName", programName ); + config.sync(); + return true; +} + + +} // namespace Akregator +#include "akregator_part.moc" diff --git a/akregator/src/akregator_part.desktop b/akregator/src/akregator_part.desktop new file mode 100644 index 000000000..7dcd91f7d --- /dev/null +++ b/akregator/src/akregator_part.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Name=aKregatorPart +Name[cs]=Komponenta aKregator +Name[da]=AkregatorPart +Name[de]=aKregator-Komponente +Name[nds]=aKregator-Komponent +Name[pt_BR]=Componente do aKregator +Name[sv]=aKregator-delprogram +Name[ta]=aKregatorபகுதி +ServiceTypes=KParts/ReadOnlyPart +X-KDE-Library=libakregatorpart +Type=Service +Icon=akregator diff --git a/akregator/src/akregator_part.h b/akregator/src/akregator_part.h new file mode 100644 index 000000000..b241517f5 --- /dev/null +++ b/akregator/src/akregator_part.h @@ -0,0 +1,223 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef _AKREGATORPART_H_ +#define _AKREGATORPART_H_ + +#include <kparts/browserextension.h> +#include <kparts/part.h> +#include <kurl.h> + +#include "config.h" + +#include "akregator_partiface.h" + +class QDomDocument; +class QTimer; + +class KAboutData; +class KConfig; +class KURL; +class KParts::BrowserExtension; + +namespace Akregator +{ + namespace Backend + { + class Storage; + } + + typedef KParts::ReadOnlyPart MyBasePart; + + class ActionManagerImpl; + class View; + class Part; + class Feed; + class Article; + class TrayIcon; + + class BrowserExtension : public KParts::BrowserExtension + { + Q_OBJECT + + public: + BrowserExtension(Part *p, const char *name ); + public slots: + void saveSettings(); + private: + Part *m_part; + }; + + /** + This is a RSS Aggregator "Part". It does all the real work. + It is also embeddable into other applications (e.g. for use in Kontact). + */ + class Part : public MyBasePart, virtual public AkregatorPartIface + { + Q_OBJECT + public: + typedef MyBasePart inherited; + + /** Default constructor.*/ + Part(QWidget *parentWidget, const char *widgetName, + QObject *parent, const char *name, const QStringList&); + + /** Destructor. */ + virtual ~Part(); + + /** Create KAboutData for this KPart. */ + static KAboutData *createAboutData(); + + /** + Opens feedlist + @param url URL to feedlist + */ + virtual bool openURL(const KURL& url); + + /** Opens standard feedlist */ + virtual void openStandardFeedList(); + + virtual void fetchFeedUrl(const QString&); + + /** Fetch all feeds in the feed tree */ + virtual void fetchAllFeeds(); + + /** + Add a feed to a group. + @param urls The URL(s) of the feed(s) to add. + @param group The name of the folder into which the feed is added. + If the group does not exist, it is created. The feed is added as the last member + of the group. + */ + virtual void addFeedsToGroup(const QStringList& urls, const QString& group); + + virtual void addFeed(); + + /** + This method is called when this app is restored. The KConfig + object points to the session management config file that was saved + with @ref saveProperties + Calls AkregatorView's saveProperties. + */ + virtual void readProperties(KConfig* config); + + /** This method is called when it is time for the app to save its + properties for session management purposes. + Calls AkregatorView's readProperties. */ + virtual void saveProperties(KConfig* config); + + /** merges a nested part's GUI into the gui of this part + @return true iff merging was successful, i.e. the GUI factory was not NULL */ + virtual bool mergePart(KParts::Part*); + + void loadTagSet(const QString& path); + void saveTagSet(const QString& path); + + public slots: + /** Used to save settings after changing them from configuration dialog. Calls AkregatorPart's saveSettings. */ + virtual void saveSettings(); + + /** Saves the standard feed list to it's default location */ + void slotSaveFeedList(); + + void fileImport(); + void fileExport(); + void fileGetFeeds(); + + void fileSendLink() { fileSendArticle(); } + void fileSendFile() { fileSendArticle(true); } + void fileSendArticle(bool attach=false); + + /** Shows configuration dialog */ + void showOptions(); + void showKNotifyOptions(); + + signals: + void showPart(); + void signalSettingsChanged(); + + + protected: + + /** @return Whether the tray icon is enabled or not */ + virtual bool isTrayIconEnabled() const; + + /** loads all Akregator plugins */ + void loadPlugins(); + + /** This must be implemented by each part */ + virtual bool openFile(); + + void importFile(const KURL& url); + void exportFile(const KURL& url); + + /** FIXME: hack to get the tray icon working */ + QWidget* getMainWindow(); + + virtual KParts::Part *hitTest(QWidget *widget, const QPoint &globalPos); + + /** reimplemented to load/unload the merged parts on selection/deselection */ + virtual void partActivateEvent(KParts::PartActivateEvent* event); + + protected slots: + void slotOnShutdown(); + void slotSettingsChanged(); + + private: // methods + + bool copyFile(const QString& backup); + + /** fills the font settings with system fonts, if fonts are not set */ + void initFonts(); + + /** creates an OPML file containing the initial feeds (KDE feeds) */ + static QDomDocument createDefaultFeedList(); + + bool tryToLock(const QString& backendName); + + private: // attributes + + class ApplyFiltersInterceptor; + ApplyFiltersInterceptor* m_applyFiltersInterceptor; + QString m_standardFeedList; + QString m_tagSetPath; + bool m_standardListLoaded; + bool m_shuttingDown; + + KParts::BrowserExtension *m_extension; + KParts::Part* m_mergedPart; + View* m_view; + + QTimer* m_autosaveTimer; + /** did we backup the feed list already? */ + bool m_backedUpList; + Backend::Storage* m_storage; + ActionManagerImpl* m_actionManager; + }; +} + +#endif // _AKREGATORPART_H_ + +// vim: set et ts=4 sts=4 sw=4: diff --git a/akregator/src/akregator_part.rc b/akregator/src/akregator_part.rc new file mode 100644 index 000000000..ad3e10602 --- /dev/null +++ b/akregator/src/akregator_part.rc @@ -0,0 +1,175 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="akregator_part" version="78"> +<MenuBar> + <Menu name="file"> + <Action name="file_import"/> + <Action name="file_export"/> + <Action name="file_getfromweb"/> + <Separator/> + <Action name="file_print"/> + <Separator/> + </Menu> + + + + <Menu name="edit"> + <text>&Edit</text> + <Action name="feed_modify"/> + <Action name="feed_remove"/> + <Separator/> + </Menu> + + + <Menu name="view"> + <text>&View</text> + <Action name="view_mode"/> + </Menu> + + <Menu name="go"> + <text>&Go</text> + <Action name="go_previous_article"/> + <Action name="go_prev_unread_article"/> + <Action name="go_next_article"/> + <Action name="go_next_unread_article"/> + <Separator/> + <Action name="go_prev_feed"/> + <Action name="go_prev_unread_feed"/> + <Action name="go_next_feed"/> + <Action name="go_next_unread_feed"/> + </Menu> + + + <Menu name="feed"> + <text>F&eed</text> + <Action name="feed_add"/> + <Action name="feed_add_group"/> + <Separator/> + <Action name="feed_mark_all_as_read"/> + <Action name="feed_mark_all_feeds_as_read"/> + <Separator/> + <Action name="feed_fetch"/> + <Action name="feed_fetch_all"/> + <Action name="feed_stop"/> + </Menu> + + <Menu name ="article"> + <text>&Article</text> + <Action name="article_open"/> + <Action name="article_open_external" /> + <Separator/> + <Action name="article_set_status_important"/> + <Action name="article_set_status"/> + <Action name="article_delete"/> + <Separator/> + <Action name="article_assign_tag_menu"/> + <Action name="article_remove_tag_menu"/> + <Separator/> + <Action name="file_sendlink"/> + <Action name="file_sendfile"/> + </Menu> + + <Menu name="settings"> + <Action name="show_quick_filter"/> + <Separator/> + <Action name="options_configure_notifications" group="settings_configure"/> + <Action name="akregator_configure_akregator" group="settings_configure"/> + </Menu> + <Menu name="help"> + </Menu> +</MenuBar> + +<ToolBar name="mainToolBar"> + <Action name="feed_fetch"/> + <Action name="feed_fetch_all"/> + <Action name="feed_stop"/> + <Action name="feed_mark_all_as_read"/> + <Action name="article_tagmenu"/> + <DefineGroup name="pageviewer_operations" /> + <Merge/> +</ToolBar> + +<ToolBar hidden="true" name="textToSpeechToolBar"> + <Action name="akr_texttospeech"/> + <Action name="akr_aborttexttospeech"/> + <Merge/> +</ToolBar> + +<!-- this menu doesn't show up in the app, just groups the actions --> +<Menu name="various"> +<Action name="go_previous_article"/> +<Action name="go_next_article"/> +<Action name="feedstree_up"/> +<Action name="feedstree_down"/> +<Action name="feedstree_left"/> +<Action name="feedstree_right"/> +<Action name="feedstree_home"/> +<Action name="feedstree_end"/> +<Action name="feedstree_move_up"/> +<Action name="feedstree_move_down"/> +<Action name="feedstree_move_left"/> +<Action name="feedstree_move_right"/> +<Action name="article_open_in_tab"/> +<Action name="article_open_background_tab"/> +<Action name="article_open"/> +<Action name="select_previous_tab"/> +<Action name="select_next_tab"/> +</Menu> + +<Menu name="feeds_popup"> + <Action name="feed_mark_all_as_read"/> + <Separator/> + <Action name="feed_fetch"/> + <Separator/> + <Action name="feed_homepage"/> + <Separator/> + <Action name="feed_modify"/> + <Action name="feed_remove"/> +</Menu> + +<Menu name="feedgroup_popup"> + <Action name="feed_mark_all_as_read"/> + <Separator/> + <Action name="feed_fetch"/> + <Separator/> + <Action name="feed_add"/> + <Action name="feed_add_group"/> + <Action name="feed_modify"/> + <Action name="feed_remove"/> +</Menu> + +<Menu name="tagfolder_popup"> + <Action name="feed_mark_all_as_read"/> + <Separator/> + <Action name="tag_new"/> +<!-- <Action name="feed_add_group"/> --> + <Action name="feed_modify"/> + <Action name="feed_remove"/> +</Menu> + + +<Menu name="tagnode_popup"> + <Action name="feed_mark_all_as_read"/> + <Action name="feed_modify"/> + <Action name="feed_remove"/> +</Menu> + +<Menu name="article_popup"> + <Action name="article_open"/> + <Action name="article_open_external" /> + <Action name="article_copy_link_address" /> + <Separator/> + <Action name="article_set_status_important"/> + <Action name="article_set_status"/> + <Action name="article_toggle_keep"/> + <Action name="article_delete"/> + <Separator/> + <Action name="article_tagmenu"/> +</Menu> + +<Menu name="tab_popup"> + <Action name="tab_detach"/> + <Action name="tab_copylinkaddress"/> + <Action name="tab_remove"/> +</Menu> + +</kpartgui> diff --git a/akregator/src/akregator_partiface.h b/akregator/src/akregator_partiface.h new file mode 100644 index 000000000..2c4290b8a --- /dev/null +++ b/akregator/src/akregator_partiface.h @@ -0,0 +1,49 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATORPARTIFACE_H +#define AKREGATORPARTIFACE_H + +#include <dcopobject.h> +#include <qstringlist.h> +#include <kurl.h> + +namespace Akregator { + + class AkregatorPartIface : virtual public DCOPObject + { + K_DCOP + k_dcop: + virtual void openStandardFeedList() = 0; + virtual void fetchFeedUrl(const QString&) = 0; + virtual void fetchAllFeeds() = 0; + virtual void saveSettings() = 0; + virtual void addFeedsToGroup(const QStringList&, const QString&) = 0; + virtual void exportFile(const KURL& url) = 0; + virtual void addFeed() = 0; + }; + +} + +#endif diff --git a/akregator/src/akregator_plugin.desktop b/akregator/src/akregator_plugin.desktop new file mode 100644 index 000000000..986515c06 --- /dev/null +++ b/akregator/src/akregator_plugin.desktop @@ -0,0 +1,86 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Akregator/Plugin +Comment=Plugin for Akregator +Comment[af]=Inprop module vir Akregator +Comment[be]=Утулка для Akregator +Comment[bg]=Приставка за Akregator +Comment[br]=Lugent evit Akregator +Comment[ca]=Endollable per a l'Akregator +Comment[cs]=Modul pro Akregator +Comment[de]=Modul für Akregator +Comment[el]=Πρόσθετο για το Akregator +Comment[eo]=Kromprogramo por Akregator +Comment[es]=Extensión para Akregator +Comment[et]=Akregatori plugin +Comment[eu]=Akregator-en plugina +Comment[fa]=وصله برای Akregator +Comment[fi]=Liitännäinen Akregatoriin +Comment[fr]=Module pour Akregator +Comment[fy]=Plugin foar Akregator +Comment[ga]=Breiseán Akregator +Comment[gl]=Extensión para Akregator +Comment[he]=תוסף עבור Akregator +Comment[hu]=Akregator bővítőmodul +Comment[is]=Íforrit fyrir Akregator +Comment[it]=Plugin per Akregator +Comment[ja]=Akregator 用プラグイン +Comment[ka]=Akregator-ის მოდული +Comment[kk]=Akregator-дың плагин модулі +Comment[km]=កម្មវិធីជំនួយ Akregator +Comment[ko]=Akregator 플러그인 +Comment[lt]=Akregator skirtas priedas +Comment[mk]=Приклучок за Akregator +Comment[ms]=Plugin untuk Akregator +Comment[nb]=Programtillegg for Akregator +Comment[nds]=Moduul för Akregator +Comment[ne]=एक्रिगेटरका लागि प्लगइन +Comment[nl]=Plugin voor Akregator +Comment[nn]=Programtillegg til Akregator +Comment[pl]=Wtyczka dla Akregatora +Comment[pt]='Plugin' para o Akregator +Comment[pt_BR]=Plug-in para o Akregator +Comment[ru]=Модуль Akregator +Comment[se]=Lassemoduvla Akregatorii +Comment[sk]=Modul pre Akregator +Comment[sl]=Vstavek za Akregator +Comment[sr]=Прикључак за Akregator +Comment[sr@Latn]=Priključak za Akregator +Comment[sv]=Insticksprogram för Akregator +Comment[tr]=Akregator Eklentisi +Comment[uk]=Втулок для Akregator +Comment[uz]=Akregator uchun plagin +Comment[uz@cyrillic]=Akregator учун плагин +Comment[zh_CN]=Akregator 插件 +Comment[zh_TW]=Akregator 外掛程式 + + +# Type of plugin, e.g. "storage". +[PropertyDef::X-KDE-akregator-plugintype] +Type=QString + +# Internal name for identification, not translated. +[PropertyDef::X-KDE-akregator-name] +Type=QString + +# List of authors. +[PropertyDef::X-KDE-akregator-authors] +Type=QStringList + +# List of author's email addresses. +[PropertyDef::X-KDE-akregator-email] +Type=QStringList + +# Priority of the plugin. When KTrader returns multiple offers, the one with the highest rank is chosen. +# Range: 0 (disabled) - 255 (highest) +[PropertyDef::X-KDE-akregator-rank] +Type=int + +# Version of the plugin. +[PropertyDef::X-KDE-akregator-version] +Type=int + +# Version of the framework this plugin is compatible with. +# Must be bumped after making incompatible changes. +[PropertyDef::X-KDE-akregator-framework-version] +Type=int diff --git a/akregator/src/akregator_run.cpp b/akregator/src/akregator_run.cpp new file mode 100644 index 000000000..b78c92f7c --- /dev/null +++ b/akregator/src/akregator_run.cpp @@ -0,0 +1,72 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <kdebug.h> + +#include "viewer.h" +#include "akregator_run.h" + +namespace Akregator { + + +BrowserRun::BrowserRun(QWidget* mainWindow, Viewer* currentViewer, const KURL& url, const KParts::URLArgs& args, OpeningMode mode) + : KParts::BrowserRun(url, args, 0L, mainWindow, false, false, true) +{ + m_currentViewer = currentViewer; + m_openingMode = mode; + + if (mode == CURRENT_TAB) + { + connect(m_currentViewer, SIGNAL(destroyed()), this, SLOT(slotViewerDeleted())); + } + setEnableExternalBrowser(false); +} + +BrowserRun::~BrowserRun() +{ + kdDebug() << "BrowserRun::~BrowserRun()" << endl; +} + +void BrowserRun::foundMimeType( const QString & type ) +{ + if (type=="text/html" ||type=="text/xml" || type=="application/xhtml+xml") + emit signalOpenInViewer(url(), m_currentViewer, m_openingMode); + else + if ( handleNonEmbeddable(type) == KParts::BrowserRun::NotHandled ) + KRun::foundMimeType( type ); +} + +void BrowserRun::slotViewerDeleted() +{ + + // HACK: if the mode is to open the page in the current viewer, we set it to new tab (foreground) if the part gets deleted meanwhile + m_currentViewer = 0; + m_openingMode = NEW_TAB_FOREGROUND; +} + +} // namespace Akregator + +#include "akregator_run.moc" + +// vim: set et ts=4 sts=4 sw=4: diff --git a/akregator/src/akregator_run.h b/akregator/src/akregator_run.h new file mode 100644 index 000000000..ad549786c --- /dev/null +++ b/akregator/src/akregator_run.h @@ -0,0 +1,70 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_RUN_H +#define AKREGATOR_RUN_H + +#include <kparts/browserrun.h> + +namespace Akregator +{ + +class Viewer; + +class BrowserRun : public KParts::BrowserRun +{ + Q_OBJECT + public: + /** indicates how HTML pages should be opened. It is passed in the constructor and sent back via the openInViewer signal. This is a workaround to fix opening of non-HTML mimetypes in 3.5, which will be refactored for KDE4 anyway. For 3.5.x it's the easiest way to fix the problem without changing too much code TODO KDE4: refactor, remove this enum */ + enum OpeningMode + { + CURRENT_TAB, + NEW_TAB_FOREGROUND, + NEW_TAB_BACKGROUND, + EXTERNAL + }; + + BrowserRun(QWidget* mainWindow, Viewer* currentViewer, const KURL& url, const KParts::URLArgs& args, OpeningMode mode); + virtual ~BrowserRun(); + + signals: + + void signalOpenInViewer(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode); + + protected: + virtual void foundMimeType(const QString& type); + + private slots: + void slotViewerDeleted(); + + private: + OpeningMode m_openingMode; + Viewer* m_currentViewer; +}; + +} + +#endif + +// vim: set et ts=4 sts=4 sw=4: diff --git a/akregator/src/akregator_shell.rc b/akregator/src/akregator_shell.rc new file mode 100644 index 000000000..88e8b37cd --- /dev/null +++ b/akregator/src/akregator_shell.rc @@ -0,0 +1,55 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="akregator_shell" version="14"> +<MenuBar> + <Menu noMerge="1" name="file"><text>&File</text> + <Merge/> + <Separator/> + <Action name="file_quit"/> + </Menu> + + <Menu noMerge="1" name="edit"> + <text>&Edit</text> + <Merge/> + </Menu> + + <Menu noMerge="1" name="view"> + <text>&View</text> + <Merge/> + </Menu> + + <Menu noMerge="1" name="go"> + <text>&Go</text> + <Merge/> + </Menu> + + <Menu noMerge="1" name="feed"> + <text>&Feed</text> + <Merge/> + </Menu> + + <Menu noMerge="1" name="article"> + <text>&Article</text> + <Merge/> + </Menu> + + <Menu noMerge="1" name="settings"><text>&Settings</text> + <Merge name="StandardToolBarMenuHandler" /> + <Action name="options_show_statusbar"/> + <Merge name="show_merge"/> + <Separator/> + <Action name="fetch_on_startup"/> + <Separator/> + <Merge/> + <Action name="options_configure_keybinding"/> + <Action name="options_configure_toolbars"/> + <Merge name="configure_merge"/> + </Menu> +</MenuBar> +<ToolBar noMerge="1" name="mainToolBar"><text>Main Toolbar</text> + <Merge/> + <Action name="stop"/> +</ToolBar> +<ToolBar hidden="true" noMerge="1" name="textToSpeechToolBar"><text>Speech Toolbar</text> + <Merge/> +</ToolBar> +</kpartgui> diff --git a/akregator/src/akregator_view.cpp b/akregator/src/akregator_view.cpp new file mode 100644 index 000000000..f45f784ed --- /dev/null +++ b/akregator/src/akregator_view.cpp @@ -0,0 +1,1533 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2004 Sashmit Bhaduri <smt@vfemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "actionmanagerimpl.h" +#include "akregator_part.h" +#include "akregator_run.h" +#include "akregator_view.h" +#include "listtabwidget.h" +#include "addfeeddialog.h" +#include "propertiesdialog.h" +#include "frame.h" +#include "fetchqueue.h" +#include "feedlistview.h" +#include "articlelistview.h" +#include "articleviewer.h" +#include "viewer.h" +#include "feed.h" +#include "tagfolder.h" +#include "folder.h" +#include "feedlist.h" +#include "akregatorconfig.h" +#include "kernel.h" +#include "pageviewer.h" +#include "searchbar.h" +#include "speechclient.h" +#include "storage.h" +#include "tabwidget.h" +#include "tag.h" +#include "tagset.h" +#include "tagnode.h" +#include "tagnodelist.h" +#include "tagpropertiesdialog.h" +#include "treenode.h" +#include "progressmanager.h" +#include "treenodevisitor.h" +#include "notificationmanager.h" + +#include <kaction.h> +#include <kapplication.h> +#include <kcharsets.h> +#include <kcombobox.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kdialog.h> +#include <kfiledialog.h> +#include <kfileitem.h> +#include <khtml_part.h> +#include <khtmlview.h> +#include <kiconloader.h> +#include <kinputdialog.h> +#include <klineedit.h> +#include <klistview.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpassdlg.h> +#include <kprocess.h> +#include <krun.h> +#include <kshell.h> +#include <kstandarddirs.h> +#include <kurl.h> +#include <kxmlguifactory.h> +#include <kparts/partmanager.h> + +#include <qbuttongroup.h> +#include <qcheckbox.h> +#include <qdatetime.h> // for startup time measure +#include <qfile.h> +#include <qhbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qmultilineedit.h> +#include <qpopupmenu.h> +#include <qptrlist.h> +#include <qstylesheet.h> +#include <qtextstream.h> +#include <qtimer.h> +#include <qtoolbutton.h> +#include <qtooltip.h> +#include <qvaluevector.h> +#include <qwhatsthis.h> +#include <qclipboard.h> + +namespace Akregator { + +class View::EditNodePropertiesVisitor : public TreeNodeVisitor +{ + public: + EditNodePropertiesVisitor(View* view) : m_view(view) {} + + virtual bool visitTagNode(TagNode* node) + { + TagPropertiesDialog* dlg = new TagPropertiesDialog(m_view); + dlg->setTag(node->tag()); + dlg->exec(); + delete dlg; + return true; + } + + virtual bool visitFolder(Folder* node) + { + m_view->m_listTabWidget->activeView()->startNodeRenaming(node); + return true; + } + + virtual bool visitFeed(Feed* node) + { + FeedPropertiesDialog *dlg = new FeedPropertiesDialog( m_view, "edit_feed" ); + dlg->setFeed(node); + dlg->exec(); + delete dlg; + return true; + } + private: + + View* m_view; +}; + +class View::DeleteNodeVisitor : public TreeNodeVisitor +{ + public: + DeleteNodeVisitor(View* view) : m_view(view) {} + + virtual bool visitTagNode(TagNode* node) + { + QString msg = i18n("<qt>Are you sure you want to delete tag <b>%1</b>? The tag will be removed from all articles.</qt>").arg(node->title()); + if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Tag"), KStdGuiItem::del()) == KMessageBox::Continue) + { + Tag tag = node->tag(); + QValueList<Article> articles = m_view->m_feedList->rootNode()->articles(tag.id()); + node->setNotificationMode(false); + for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it) + (*it).removeTag(tag.id()); + node->setNotificationMode(true); + Kernel::self()->tagSet()->remove(tag); + m_view->m_listTabWidget->activeView()->setFocus(); + } + return true; + } + + virtual bool visitFolder(Folder* node) + { + QString msg; + if (node->title().isEmpty()) + msg = i18n("<qt>Are you sure you want to delete this folder and its feeds and subfolders?</qt>"); + else + msg = i18n("<qt>Are you sure you want to delete folder <b>%1</b> and its feeds and subfolders?</qt>").arg(node->title()); + + if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Folder"), KStdGuiItem::del()) == KMessageBox::Continue) + { + delete node; + m_view->m_listTabWidget->activeView()->setFocus(); + } + return true; + } + + virtual bool visitFeed(Feed* node) + { + QString msg; + if (node->title().isEmpty()) + msg = i18n("<qt>Are you sure you want to delete this feed?</qt>"); + else + msg = i18n("<qt>Are you sure you want to delete feed <b>%1</b>?</qt>").arg(node->title()); + + if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Feed"), KStdGuiItem::del()) == KMessageBox::Continue) + { + delete node; + m_view->m_listTabWidget->activeView()->setFocus(); + } + return true; + } + private: + + View* m_view; +}; + + +View::~View() +{ + // if m_shuttingDown is false, slotOnShutdown was not called. That + // means that not the whole app is shutdown, only the part. So it + // should be no risk to do the cleanups now + if (!m_shuttingDown) + { + kdDebug() << "View::~View(): slotOnShutdown() wasn't called. Calling it now." << endl; + slotOnShutdown(); + } + kdDebug() << "View::~View(): leaving" << endl; +} + +View::View( Part *part, QWidget *parent, ActionManagerImpl* actionManager, const char *name) + : QWidget(parent, name), m_viewMode(NormalView), m_actionManager(actionManager) +{ + m_editNodePropertiesVisitor = new EditNodePropertiesVisitor(this); + m_deleteNodeVisitor = new DeleteNodeVisitor(this); + m_keepFlagIcon = QPixmap(locate("data", "akregator/pics/akregator_flag.png")); + m_part = part; + m_feedList = new FeedList(); + m_tagNodeList = new TagNodeList(m_feedList, Kernel::self()->tagSet()); + m_shuttingDown = false; + m_displayingAboutPage = false; + m_currentFrame = 0L; + setFocusPolicy(QWidget::StrongFocus); + + QVBoxLayout *lt = new QVBoxLayout( this ); + + m_horizontalSplitter = new QSplitter(QSplitter::Horizontal, this); + + m_horizontalSplitter->setOpaqueResize(true); + lt->addWidget(m_horizontalSplitter); + + connect (Kernel::self()->fetchQueue(), SIGNAL(fetched(Feed*)), this, SLOT(slotFeedFetched(Feed*))); + connect (Kernel::self()->fetchQueue(), SIGNAL(signalStarted()), this, SLOT(slotFetchingStarted())); + connect (Kernel::self()->fetchQueue(), SIGNAL(signalStopped()), this, SLOT(slotFetchingStopped())); + + connect(Kernel::self()->tagSet(), SIGNAL(signalTagAdded(const Tag&)), this, SLOT(slotTagCreated(const Tag&))); + connect(Kernel::self()->tagSet(), SIGNAL(signalTagRemoved(const Tag&)), this, SLOT(slotTagRemoved(const Tag&))); + + m_listTabWidget = new ListTabWidget(m_horizontalSplitter); + m_actionManager->initListTabWidget(m_listTabWidget); + + connect(m_listTabWidget, SIGNAL(signalNodeSelected(TreeNode*)), this, SLOT(slotNodeSelected(TreeNode*))); + + if (!Settings::showTaggingGUI()) + m_listTabWidget->setViewMode(ListTabWidget::single); + + m_feedListView = new NodeListView( this, "feedtree" ); + m_listTabWidget->addView(m_feedListView, i18n("Feeds"), KGlobal::iconLoader()->loadIcon("folder", KIcon::Small)); + + connect(m_feedListView, SIGNAL(signalContextMenu(KListView*, TreeNode*, const QPoint&)), this, SLOT(slotFeedTreeContextMenu(KListView*, TreeNode*, const QPoint&))); + + connect(m_feedListView, SIGNAL(signalDropped (KURL::List &, TreeNode*, + Folder*)), this, SLOT(slotFeedURLDropped (KURL::List &, + TreeNode*, Folder*))); + + m_tagNodeListView = new NodeListView(this); + m_listTabWidget->addView(m_tagNodeListView, i18n("Tags"), KGlobal::iconLoader()->loadIcon("rss_tag", KIcon::Small)); + + connect(m_tagNodeListView, SIGNAL(signalContextMenu(KListView*, TreeNode*, const QPoint&)), this, SLOT(slotFeedTreeContextMenu(KListView*, TreeNode*, const QPoint&))); + + + ProgressManager::self()->setFeedList(m_feedList); + + m_tabs = new TabWidget(m_horizontalSplitter); + m_actionManager->initTabWidget(m_tabs); + + connect( m_part, SIGNAL(signalSettingsChanged()), m_tabs, SLOT(slotSettingsChanged())); + + connect( m_tabs, SIGNAL( currentFrameChanged(Frame *) ), this, + SLOT( slotFrameChanged(Frame *) ) ); + + QWhatsThis::add(m_tabs, i18n("You can view multiple articles in several open tabs.")); + + m_mainTab = new QWidget(this, "Article Tab"); + QVBoxLayout *mainTabLayout = new QVBoxLayout( m_mainTab, 0, 2, "mainTabLayout"); + + QWhatsThis::add(m_mainTab, i18n("Articles list.")); + + m_searchBar = new SearchBar(m_mainTab); + + if ( !Settings::showQuickFilter() ) + m_searchBar->hide(); + + mainTabLayout->addWidget(m_searchBar); + + m_articleSplitter = new QSplitter(QSplitter::Vertical, m_mainTab, "panner2"); + + m_articleList = new ArticleListView( m_articleSplitter, "articles" ); + m_actionManager->initArticleListView(m_articleList); + + connect( m_articleList, SIGNAL(signalMouseButtonPressed(int, const Article&, const QPoint &, int)), this, SLOT(slotMouseButtonPressed(int, const Article&, const QPoint &, int))); + + // use selectionChanged instead of clicked + connect( m_articleList, SIGNAL(signalArticleChosen(const Article&)), + this, SLOT( slotArticleSelected(const Article&)) ); + connect( m_articleList, SIGNAL(signalDoubleClicked(const Article&, const QPoint&, int)), + this, SLOT( slotOpenArticleExternal(const Article&, const QPoint&, int)) ); + + m_articleViewer = new ArticleViewer(m_articleSplitter, "article_viewer"); + m_articleViewer->setSafeMode(); // disable JS, Java, etc... + + m_actionManager->initArticleViewer(m_articleViewer); + + connect(m_searchBar, SIGNAL(signalSearch(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)), m_articleList, SLOT(slotSetFilter(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&))); + + connect(m_searchBar, SIGNAL(signalSearch(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&)), m_articleViewer, SLOT(slotSetFilter(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&))); + + connect( m_articleViewer, SIGNAL(urlClicked(const KURL&, Viewer*, bool, bool)), + this, SLOT(slotUrlClickedInViewer(const KURL&, Viewer*, bool, bool)) ); + + connect( m_articleViewer->browserExtension(), SIGNAL(mouseOverInfo(const KFileItem *)), + this, SLOT(slotMouseOverInfo(const KFileItem *)) ); + + connect( m_part, SIGNAL(signalSettingsChanged()), m_articleViewer, SLOT(slotPaletteOrFontChanged())); + QWhatsThis::add(m_articleViewer->widget(), i18n("Browsing area.")); + mainTabLayout->addWidget( m_articleSplitter ); + + m_mainFrame=new Frame(this, m_part, m_mainTab, i18n("Articles"), false); + connectFrame(m_mainFrame); + m_tabs->addFrame(m_mainFrame); + + m_horizontalSplitter->setSizes( Settings::splitter1Sizes() ); + m_articleSplitter->setSizes( Settings::splitter2Sizes() ); + + KConfig *conf = Settings::self()->config(); + conf->setGroup("General"); + if(!conf->readBoolEntry("Disable Introduction", false)) + { + m_articleList->hide(); + m_searchBar->hide(); + m_articleViewer->displayAboutPage(); + m_mainFrame->setTitle(i18n("About")); + m_displayingAboutPage = true; + } + + m_fetchTimer = new QTimer(this); + connect( m_fetchTimer, SIGNAL(timeout()), this, SLOT(slotDoIntervalFetches()) ); + m_fetchTimer->start(1000*60); + + // delete expired articles once per hour + m_expiryTimer = new QTimer(this); + connect(m_expiryTimer, SIGNAL(timeout()), this, + SLOT(slotDeleteExpiredArticles()) ); + m_expiryTimer->start(3600*1000); + + m_markReadTimer = new QTimer(this); + connect(m_markReadTimer, SIGNAL(timeout()), this, SLOT(slotSetCurrentArticleReadDelayed()) ); + + switch (Settings::viewMode()) + { + case CombinedView: + slotCombinedView(); + break; + case WidescreenView: + slotWidescreenView(); + break; + default: + slotNormalView(); + } + + if (!Settings::resetQuickFilterOnNodeChange()) + { + m_searchBar->slotSetStatus(Settings::statusFilter()); + m_searchBar->slotSetText(Settings::textFilter()); + } + + QTimer::singleShot(1000, this, SLOT(slotDeleteExpiredArticles()) ); + m_part->mergePart(m_articleViewer); +} + +void View::slotSettingsChanged() +{ + // if tagging is hidden, show only feed list + m_listTabWidget->setViewMode(Settings::showTaggingGUI() ? ListTabWidget::verticalTabs : ListTabWidget::single); + +} + +void View::slotOnShutdown() +{ + m_shuttingDown = true; // prevents slotFrameChanged from crashing + + m_articleList->slotShowNode(0); + m_articleViewer->slotShowNode(0); + + Kernel::self()->fetchQueue()->slotAbort(); + + m_feedListView->setNodeList(0); + ProgressManager::self()->setFeedList(0); + + delete m_feedList; + delete m_tagNodeList; + + // close all pageviewers in a controlled way + // fixes bug 91660, at least when no part loading data + m_tabs->setCurrentPage(m_tabs->count()-1); // select last page + while (m_tabs->count() > 1) // remove frames until only the main frame remains + m_tabs->slotRemoveCurrentFrame(); + + delete m_mainTab; + delete m_mainFrame; + delete m_editNodePropertiesVisitor; + delete m_deleteNodeVisitor; +} + +void View::saveSettings() +{ + Settings::setSplitter1Sizes( m_horizontalSplitter->sizes() ); + Settings::setSplitter2Sizes( m_articleSplitter->sizes() ); + Settings::setViewMode( m_viewMode ); + Settings::writeConfig(); +} + +void View::slotOpenNewTab(const KURL& url, bool background) +{ + PageViewer* page = new PageViewer(this, "page"); + + connect( m_part, SIGNAL(signalSettingsChanged()), page, SLOT(slotPaletteOrFontChanged())); + + connect( page, SIGNAL(setTabIcon(const QPixmap&)), + this, SLOT(setTabIcon(const QPixmap&))); + connect( page, SIGNAL(urlClicked(const KURL &, Viewer*, bool, bool)), + this, SLOT(slotUrlClickedInViewer(const KURL &, Viewer*, bool, bool)) ); + + Frame* frame = new Frame(this, page, page->widget(), i18n("Untitled")); + frame->setAutoDeletePart(true); // delete page viewer when removing the tab + + connect(page, SIGNAL(setWindowCaption (const QString &)), frame, SLOT(setTitle (const QString &))); + connectFrame(frame); + m_tabs->addFrame(frame); + + if(!background) + m_tabs->showPage(page->widget()); + else + setFocus(); + + page->openURL(url); +} + + +void View::setTabIcon(const QPixmap& icon) +{ + const PageViewer *s = dynamic_cast<const PageViewer*>(sender()); + if (s) { + m_tabs->setTabIconSet(const_cast<PageViewer*>(s)->widget(), icon); + } +} + +void View::connectFrame(Frame *f) +{ + connect(f, SIGNAL(statusText(const QString &)), this, SLOT(slotStatusText(const QString&))); + connect(f, SIGNAL(captionChanged (const QString &)), this, SLOT(slotCaptionChanged (const QString &))); + connect(f, SIGNAL(loadingProgress(int)), this, SLOT(slotLoadingProgress(int)) ); + connect(f, SIGNAL(started()), this, SLOT(slotStarted())); + connect(f, SIGNAL(completed()), this, SLOT(slotCompleted())); + connect(f, SIGNAL(canceled(const QString &)), this, SLOT(slotCanceled(const QString&))); +} + +void View::slotStatusText(const QString &c) +{ + if (sender() == m_currentFrame) + emit setStatusBarText(c); +} + +void View::slotCaptionChanged(const QString &c) +{ + if (sender() == m_currentFrame) + emit setWindowCaption(c); +} + +void View::slotStarted() +{ + if (sender() == m_currentFrame) + emit signalStarted(0); +} + +void View::slotCanceled(const QString &s) +{ + if (sender() == m_currentFrame) + emit signalCanceled(s); +} + +void View::slotCompleted() +{ + if (sender() == m_currentFrame) + emit signalCompleted(); +} + +void View::slotLoadingProgress(int percent) +{ + if (sender() == m_currentFrame) + emit setProgress(percent); +} + +bool View::importFeeds(const QDomDocument& doc) +{ + FeedList* feedList = new FeedList(); + bool parsed = feedList->readFromXML(doc); + + // FIXME: parsing error, print some message + if (!parsed) + { + delete feedList; + return false; + } + QString title = feedList->title(); + + if (title.isEmpty()) + title = i18n("Imported Folder"); + + bool ok; + title = KInputDialog::getText(i18n("Add Imported Folder"), i18n("Imported folder name:"), title, &ok); + + if (!ok) + { + delete feedList; + return false; + } + + Folder* fg = new Folder(title); + m_feedList->rootNode()->appendChild(fg); + m_feedList->append(feedList, fg); + + return true; +} + +bool View::loadFeeds(const QDomDocument& doc, Folder* parent) +{ + FeedList* feedList = new FeedList(); + bool parsed = feedList->readFromXML(doc); + + // parsing went wrong + if (!parsed) + { + delete feedList; + return false; + } + m_feedListView->setUpdatesEnabled(false); + m_tagNodeListView->setUpdatesEnabled(false); + if (!parent) + { + TagSet* tagSet = Kernel::self()->tagSet(); + + Kernel::self()->setFeedList(feedList); + ProgressManager::self()->setFeedList(feedList); + disconnectFromFeedList(m_feedList); + delete m_feedList; + delete m_tagNodeList; + m_feedList = feedList; + connectToFeedList(m_feedList); + + m_tagNodeList = new TagNodeList(m_feedList, tagSet); + m_feedListView->setNodeList(m_feedList); + m_tagNodeListView->setNodeList(m_tagNodeList); + + QStringList tagIDs = m_feedList->rootNode()->tags(); + QStringList::ConstIterator end = tagIDs.end(); + for (QStringList::ConstIterator it = tagIDs.begin(); it != end; ++it) + { + kdDebug() << *it << endl; + // create a tag for every tag ID in the archive that is not part of the tagset + // this is a fallback in case the tagset was corrupted, + // so the tagging information from archive does not get lost. + if (!tagSet->containsID(*it)) + { + Tag tag(*it, *it); + tagSet->insert(tag); + } + } + } + else + m_feedList->append(feedList, parent); + + m_feedListView->setUpdatesEnabled(true); + m_feedListView->triggerUpdate(); + m_tagNodeListView->setUpdatesEnabled(true); + m_tagNodeListView->triggerUpdate(); + return true; +} + +void View::slotDeleteExpiredArticles() +{ + TreeNode* rootNode = m_feedList->rootNode(); + if (rootNode) + rootNode->slotDeleteExpiredArticles(); +} + +QDomDocument View::feedListToOPML() +{ + return m_feedList->toXML(); +} + +void View::addFeedToGroup(const QString& url, const QString& groupName) +{ + + // Locate the group. + TreeNode* node = m_feedListView->findNodeByTitle(groupName); + + Folder* group = 0; + if (!node || !node->isGroup()) + { + Folder* g = new Folder( groupName ); + m_feedList->rootNode()->appendChild(g); + group = g; + } + else + group = static_cast<Folder*>(node); + + // Invoke the Add Feed dialog with url filled in. + if (group) + addFeed(url, 0, group, true); +} + +void View::slotNormalView() +{ + if (m_viewMode == NormalView) + return; + + if (m_viewMode == CombinedView) + { + m_articleList->slotShowNode(m_listTabWidget->activeView()->selectedNode()); + m_articleList->show(); + + Article article = m_articleList->currentArticle(); + + if (!article.isNull()) + m_articleViewer->slotShowArticle(article); + else + m_articleViewer->slotShowSummary(m_listTabWidget->activeView()->selectedNode()); + } + + m_articleSplitter->setOrientation(QSplitter::Vertical); + m_viewMode = NormalView; + + Settings::setViewMode( m_viewMode ); +} + +void View::slotWidescreenView() +{ + if (m_viewMode == WidescreenView) + return; + + if (m_viewMode == CombinedView) + { + m_articleList->slotShowNode(m_listTabWidget->activeView()->selectedNode()); + m_articleList->show(); + + Article article = m_articleList->currentArticle(); + + if (!article.isNull()) + m_articleViewer->slotShowArticle(article); + else + m_articleViewer->slotShowSummary(m_listTabWidget->activeView()->selectedNode()); + } + + m_articleSplitter->setOrientation(QSplitter::Horizontal); + m_viewMode = WidescreenView; + + Settings::setViewMode( m_viewMode ); +} + +void View::slotCombinedView() +{ + if (m_viewMode == CombinedView) + return; + + m_articleList->slotClear(); + m_articleList->hide(); + m_viewMode = CombinedView; + + slotNodeSelected(m_listTabWidget->activeView()->selectedNode()); + Settings::setViewMode( m_viewMode ); +} + +void View::slotFrameChanged(Frame *f) +{ + if (m_shuttingDown) + return; + + m_currentFrame=f; + + emit setWindowCaption(f->caption()); + emit setProgress(f->progress()); + emit setStatusBarText(f->statusText()); + + if (f->part() == m_part) + m_part->mergePart(m_articleViewer); + else + m_part->mergePart(f->part()); + + f->widget()->setFocus(); + + switch (f->state()) + { + case Frame::Started: + emit signalStarted(0); + break; + case Frame::Canceled: + emit signalCanceled(QString::null); + break; + case Frame::Idle: + case Frame::Completed: + default: + emit signalCompleted(); + } +} + +void View::slotFeedTreeContextMenu(KListView*, TreeNode* /*node*/, const QPoint& /*p*/) +{ + m_tabs->showPage(m_mainTab); +} + +void View::slotMoveCurrentNodeUp() +{ + TreeNode* current = m_listTabWidget->activeView()->selectedNode(); + if (!current) + return; + TreeNode* prev = current->prevSibling(); + Folder* parent = current->parent(); + + if (!prev || !parent) + return; + + parent->removeChild(prev); + parent->insertChild(prev, current); + m_listTabWidget->activeView()->ensureNodeVisible(current); +} + +void View::slotMoveCurrentNodeDown() +{ + TreeNode* current = m_listTabWidget->activeView()->selectedNode(); + if (!current) + return; + TreeNode* next = current->nextSibling(); + Folder* parent = current->parent(); + + if (!next || !parent) + return; + + parent->removeChild(current); + parent->insertChild(current, next); + m_listTabWidget->activeView()->ensureNodeVisible(current); +} + +void View::slotMoveCurrentNodeLeft() +{ + TreeNode* current = m_listTabWidget->activeView()->selectedNode(); + if (!current || !current->parent() || !current->parent()->parent()) + return; + + Folder* parent = current->parent(); + Folder* grandparent = current->parent()->parent(); + + parent->removeChild(current); + grandparent->insertChild(current, parent); + m_listTabWidget->activeView()->ensureNodeVisible(current); +} + +void View::slotMoveCurrentNodeRight() +{ + TreeNode* current = m_listTabWidget->activeView()->selectedNode(); + if (!current || !current->parent()) + return; + TreeNode* prev = current->prevSibling(); + + if ( prev && prev->isGroup() ) + { + Folder* fg = static_cast<Folder*>(prev); + current->parent()->removeChild(current); + fg->appendChild(current); + m_listTabWidget->activeView()->ensureNodeVisible(current); + } +} + +void View::slotNodeSelected(TreeNode* node) +{ + m_markReadTimer->stop(); + + if (node) + { + kdDebug() << "node selected: " << node->title() << endl; + kdDebug() << "unread: " << node->unread() << endl; + kdDebug() << "total: " << node->totalCount() << endl; + } + + if (m_displayingAboutPage) + { + m_mainFrame->setTitle(i18n("Articles")); + if (m_viewMode != CombinedView) + m_articleList->show(); + if (Settings::showQuickFilter()) + m_searchBar->show(); + m_displayingAboutPage = false; + } + + m_tabs->showPage(m_mainTab); + + if (Settings::resetQuickFilterOnNodeChange()) + m_searchBar->slotClearSearch(); + + if (m_viewMode == CombinedView) + m_articleViewer->slotShowNode(node); + else + { + m_articleList->slotShowNode(node); + m_articleViewer->slotShowSummary(node); + } + + if (node) + m_mainFrame->setCaption(node->title()); + + m_actionManager->slotNodeSelected(node); + + updateTagActions(); +} + +void View::slotOpenURL(const KURL& url, Viewer* currentViewer, BrowserRun::OpeningMode mode) +{ + if (mode == BrowserRun::EXTERNAL) + Viewer::displayInExternalBrowser(url); + else + { + KParts::URLArgs args = currentViewer ? currentViewer->browserExtension()->urlArgs() : KParts::URLArgs(); + + BrowserRun* r = new BrowserRun(this, currentViewer, url, args, mode); + connect(r, SIGNAL(signalOpenInViewer(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode)), + this, SLOT(slotOpenURLReply(const KURL&, Akregator::Viewer*, Akregator::BrowserRun::OpeningMode))); + } +} + +//TODO: KDE4 remove this ugly ugly hack +void View::slotUrlClickedInViewer(const KURL& url, Viewer* viewer, bool newTab, bool background) +{ + + if (!newTab) + { + slotOpenURL(url, viewer, BrowserRun::CURRENT_TAB); + } + else + { + slotOpenURL(url, 0L, background ? BrowserRun::NEW_TAB_BACKGROUND : BrowserRun::NEW_TAB_FOREGROUND); + } +} + +//TODO: KDE4 remove this ugly ugly hack +void View::slotOpenURLReply(const KURL& url, Viewer* currentViewer, BrowserRun::OpeningMode mode) +{ + switch (mode) + { + case BrowserRun::CURRENT_TAB: + currentViewer->openURL(url); + break; + case BrowserRun::NEW_TAB_FOREGROUND: + case BrowserRun::NEW_TAB_BACKGROUND: + slotOpenNewTab(url, mode == BrowserRun::NEW_TAB_BACKGROUND); + break; + case BrowserRun::EXTERNAL: + Viewer::displayInExternalBrowser(url); + break; + } +} + +void View::slotFeedAdd() +{ + Folder* group = 0; + if (!m_feedListView->selectedNode()) + group = m_feedList->rootNode(); // all feeds + else + { + //TODO: tag nodes need rework + if ( m_feedListView->selectedNode()->isGroup()) + group = static_cast<Folder*>(m_feedListView->selectedNode()); + else + group= m_feedListView->selectedNode()->parent(); + + } + + TreeNode* lastChild = group->children().last(); + + addFeed(QString::null, lastChild, group, false); +} + +void View::addFeed(const QString& url, TreeNode *after, Folder* parent, bool autoExec) +{ + + AddFeedDialog *afd = new AddFeedDialog( 0, "add_feed" ); + + afd->setURL(KURL::decode_string(url)); + + if (autoExec) + afd->slotOk(); + else + { + if (afd->exec() != QDialog::Accepted) + { + delete afd; + return; + } + } + + Feed* feed = afd->feed; + delete afd; + + FeedPropertiesDialog *dlg = new FeedPropertiesDialog( 0, "edit_feed" ); + dlg->setFeed(feed); + + dlg->selectFeedName(); + + if (!autoExec) + if (dlg->exec() != QDialog::Accepted) + { + delete feed; + delete dlg; + return; + } + + if (!parent) + parent = m_feedList->rootNode(); + + parent->insertChild(feed, after); + + m_feedListView->ensureNodeVisible(feed); + + + delete dlg; +} + +void View::slotFeedAddGroup() +{ + TreeNode* node = m_feedListView->selectedNode(); + TreeNode* after = 0; + + if (!node) + node = m_feedListView->rootNode(); + + // if a feed is selected, add group next to it + //TODO: tag nodes need rework + if (!node->isGroup()) + { + after = node; + node = node->parent(); + } + + Folder* currentGroup = static_cast<Folder*> (node); + + bool Ok; + + QString text = KInputDialog::getText(i18n("Add Folder"), i18n("Folder name:"), "", &Ok); + + if (Ok) + { + Folder* newGroup = new Folder(text); + if (!after) + currentGroup->appendChild(newGroup); + else + currentGroup->insertChild(newGroup, after); + + m_feedListView->ensureNodeVisible(newGroup); + } +} + +void View::slotFeedRemove() +{ + TreeNode* selectedNode = m_listTabWidget->activeView()->selectedNode(); + + // don't delete root element! (safety valve) + if (!selectedNode || selectedNode == m_feedList->rootNode()) + return; + + m_deleteNodeVisitor->visit(selectedNode); +} + +void View::slotFeedModify() +{ + TreeNode* node = m_listTabWidget->activeView()->selectedNode(); + if (node) + m_editNodePropertiesVisitor->visit(node); + +} + +void View::slotNextUnreadArticle() +{ + if (m_viewMode == CombinedView) + m_listTabWidget->activeView()->slotNextUnreadFeed(); + + TreeNode* sel = m_listTabWidget->activeView()->selectedNode(); + if (sel && sel->unread() > 0) + m_articleList->slotNextUnreadArticle(); + else + m_listTabWidget->activeView()->slotNextUnreadFeed(); +} + +void View::slotPrevUnreadArticle() +{ + if (m_viewMode == CombinedView) + m_listTabWidget->activeView()->slotPrevUnreadFeed(); + + TreeNode* sel = m_listTabWidget->activeView()->selectedNode(); + if (sel && sel->unread() > 0) + m_articleList->slotPreviousUnreadArticle(); + else + m_listTabWidget->activeView()->slotPrevUnreadFeed(); +} + +void View::slotMarkAllFeedsRead() +{ + m_feedList->rootNode()->slotMarkAllArticlesAsRead(); +} + +void View::slotMarkAllRead() +{ + if(!m_listTabWidget->activeView()->selectedNode()) return; + m_listTabWidget->activeView()->selectedNode()->slotMarkAllArticlesAsRead(); +} + +void View::slotOpenHomepage() +{ + Feed* feed = dynamic_cast<Feed *>(m_listTabWidget->activeView()->selectedNode()); + + if (!feed) + return; + + KURL url = KURL(feed->htmlUrl()) +; + switch (Settings::lMBBehaviour()) + { + case Settings::EnumLMBBehaviour::OpenInExternalBrowser: + slotOpenURL(url, 0, BrowserRun::EXTERNAL); + break; + case Settings::EnumLMBBehaviour::OpenInBackground: + slotOpenURL(url, 0, BrowserRun::NEW_TAB_BACKGROUND); + break; + default: + slotOpenURL(url, 0, BrowserRun::NEW_TAB_FOREGROUND); + } +} + +void View::slotSetTotalUnread() +{ + emit signalUnreadCountChanged( m_feedList->rootNode()->unread() ); +} + +void View::slotDoIntervalFetches() +{ + m_feedList->rootNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue(), true); +} + +void View::slotFetchCurrentFeed() +{ + if ( !m_listTabWidget->activeView()->selectedNode() ) + return; + m_listTabWidget->activeView()->selectedNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue()); +} + +void View::slotFetchAllFeeds() +{ + m_feedList->rootNode()->slotAddToFetchQueue(Kernel::self()->fetchQueue()); +} + +void View::slotFetchingStarted() +{ + m_mainFrame->setState(Frame::Started); + m_actionManager->action("feed_stop")->setEnabled(true); + m_mainFrame->setStatusText(i18n("Fetching Feeds...")); +} + +void View::slotFetchingStopped() +{ + m_mainFrame->setState(Frame::Completed); + m_actionManager->action("feed_stop")->setEnabled(false); + m_mainFrame->setStatusText(QString::null); +} + +void View::slotFeedFetched(Feed *feed) +{ + // iterate through the articles (once again) to do notifications properly + if (feed->articles().count() > 0) + { + QValueList<Article> articles = feed->articles(); + QValueList<Article>::ConstIterator it; + QValueList<Article>::ConstIterator end = articles.end(); + for (it = articles.begin(); it != end; ++it) + { + if ((*it).status()==Article::New && ((*it).feed()->useNotification() || Settings::useNotifications())) + { + NotificationManager::self()->slotNotifyArticle(*it); + } + } + } +} + +void View::slotMouseButtonPressed(int button, const Article& article, const QPoint &, int) +{ + if (button == Qt::MidButton) + { + KURL link = article.link(); + switch (Settings::mMBBehaviour()) + { + case Settings::EnumMMBBehaviour::OpenInExternalBrowser: + slotOpenURL(link, 0L, BrowserRun::EXTERNAL); + break; + case Settings::EnumMMBBehaviour::OpenInBackground: + slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND); + break; + default: + slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND); + } + } +} + +void View::slotAssignTag(const Tag& tag, bool assign) +{ + kdDebug() << (assign ? "assigned" : "removed") << " tag \"" << tag.id() << "\"" << endl; + QValueList<Article> selectedArticles = m_articleList->selectedArticles(); + for (QValueList<Article>::Iterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it) + { + if (assign) + (*it).addTag(tag.id()); + else + (*it).removeTag(tag.id()); + } + updateTagActions(); +} +/* +void View::slotRemoveTag(const Tag& tag) +{ + kdDebug() << "remove tag \"" << tag.id() << "\" from selected articles" << endl; + QValueList<Article> selectedArticles = m_articleList->selectedArticles(); + for (QValueList<Article>::Iterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it) + (*it).removeTag(tag.id()); + + updateTagActions(); +} +*/ +void View::slotNewTag() +{ + Tag tag(KApplication::randomString(8), "New Tag"); + Kernel::self()->tagSet()->insert(tag); + TagNode* node = m_tagNodeList->findByTagID(tag.id()); + if (node) + m_tagNodeListView->startNodeRenaming(node); +} + +void View::slotTagCreated(const Tag& tag) +{ + if (m_tagNodeList && !m_tagNodeList->containsTagId(tag.id())) + { + TagNode* tagNode = new TagNode(tag, m_feedList->rootNode()); + m_tagNodeList->rootNode()->appendChild(tagNode); + } +} + +void View::slotTagRemoved(const Tag& /*tag*/) +{ +} + +void View::slotArticleSelected(const Article& article) +{ + if (m_viewMode == CombinedView) + return; + + m_markReadTimer->stop(); + + Feed *feed = article.feed(); + if (!feed) + return; + + Article a(article); + if (a.status() != Article::Read) + { + int delay; + + if ( Settings::useMarkReadDelay() ) + { + delay = Settings::markReadDelay(); + + if (delay > 0) + m_markReadTimer->start( delay*1000, true ); + else + a.setStatus(Article::Read); + } + } + + KToggleAction* maai = dynamic_cast<KToggleAction*>(m_actionManager->action("article_set_status_important")); + maai->setChecked(a.keep()); + + kdDebug() << "selected: " << a.guid() << endl; + + updateTagActions(); + + m_articleViewer->slotShowArticle(a); +} + +void View::slotOpenArticleExternal(const Article& article, const QPoint&, int) +{ + if (!article.isNull()) + Viewer::displayInExternalBrowser(article.link()); +} + + +void View::slotOpenCurrentArticle() +{ + Article article = m_articleList->currentArticle(); + + if (article.isNull()) + return; + + KURL link; + if (article.link().isValid()) + link = article.link(); + else if (article.guidIsPermaLink()) + link = KURL(article.guid()); + + if (link.isValid()) + { + slotOpenURL(link, 0L, BrowserRun::NEW_TAB_FOREGROUND); + } +} + +void View::slotOpenCurrentArticleExternal() +{ + slotOpenArticleExternal(m_articleList->currentArticle(), QPoint(), 0); +} + +void View::slotOpenCurrentArticleBackgroundTab() +{ + Article article = m_articleList->currentArticle(); + + if (article.isNull()) + return; + + KURL link; + + if (article.link().isValid()) + link = article.link(); + else if (article.guidIsPermaLink()) + link = KURL(article.guid()); + + if (link.isValid()) + { + slotOpenURL(link, 0L, BrowserRun::NEW_TAB_BACKGROUND); + } +} + +void View::slotCopyLinkAddress() +{ + Article article = m_articleList->currentArticle(); + + if(article.isNull()) + return; + + QString link; + if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid())) + { + // in case link isn't valid, fall back to the guid permaLink. + if (article.link().isValid()) + link = article.link().url(); + else + link = article.guid(); + QClipboard *cb = QApplication::clipboard(); + cb->setText(link, QClipboard::Clipboard); + cb->setText(link, QClipboard::Selection); + } +} + +void View::slotFeedURLDropped(KURL::List &urls, TreeNode* after, Folder* parent) +{ + KURL::List::iterator it; + for ( it = urls.begin(); it != urls.end(); ++it ) + { + addFeed((*it).prettyURL(), after, parent, false); + } +} + +void View::slotToggleShowQuickFilter() +{ + if ( Settings::showQuickFilter() ) + { + Settings::setShowQuickFilter(false); + m_searchBar->slotClearSearch(); + m_searchBar->hide(); + } + else + { + Settings::setShowQuickFilter(true); + if (!m_displayingAboutPage) + m_searchBar->show(); + } + +} + +void View::slotArticleDelete() +{ + + if ( m_viewMode == CombinedView ) + return; + + QValueList<Article> articles = m_articleList->selectedArticles(); + + QString msg; + switch (articles.count()) + { + case 0: + return; + case 1: + msg = i18n("<qt>Are you sure you want to delete article <b>%1</b>?</qt>").arg(QStyleSheet::escape(articles.first().title())); + break; + default: + msg = i18n("<qt>Are you sure you want to delete the selected article?</qt>", + "<qt>Are you sure you want to delete the %n selected articles?</qt>", + articles.count()); + } + + if (KMessageBox::warningContinueCancel(0, msg, i18n("Delete Article"), KStdGuiItem::del()) == KMessageBox::Continue) + { + if (m_listTabWidget->activeView()->selectedNode()) + m_listTabWidget->activeView()->selectedNode()->setNotificationMode(false); + + QValueList<Feed*> feeds; + for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it) + { + Feed* feed = (*it).feed(); + if (!feeds.contains(feed)) + feeds.append(feed); + feed->setNotificationMode(false); + (*it).setDeleted(); + } + + for (QValueList<Feed*>::Iterator it = feeds.begin(); it != feeds.end(); ++it) + { + (*it)->setNotificationMode(true); + } + + if (m_listTabWidget->activeView()->selectedNode()) + m_listTabWidget->activeView()->selectedNode()->setNotificationMode(true); + } +} + + +void View::slotArticleToggleKeepFlag(bool /*enabled*/) +{ + QValueList<Article> articles = m_articleList->selectedArticles(); + + if (articles.isEmpty()) + return; + + bool allFlagsSet = true; + for (QValueList<Article>::Iterator it = articles.begin(); allFlagsSet && it != articles.end(); ++it) + if (!(*it).keep()) + allFlagsSet = false; + + for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it) + (*it).setKeep(!allFlagsSet); +} + +void View::slotSetSelectedArticleRead() +{ + QValueList<Article> articles = m_articleList->selectedArticles(); + + if (articles.isEmpty()) + return; + + for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it) + (*it).setStatus(Article::Read); +} + +void View::slotTextToSpeechRequest() +{ + if (m_currentFrame == m_mainFrame) + { + if (m_viewMode != CombinedView) + { + // in non-combined view, read selected articles + SpeechClient::self()->slotSpeak(m_articleList->selectedArticles()); + // TODO: if article viewer has a selection, read only the selected text? + } + else + { + if (m_listTabWidget->activeView()->selectedNode()) + { + //TODO: read articles in current node, respecting quick filter! + } + } + } + else + { + QString selectedText = static_cast<PageViewer *>(m_currentFrame->part())->selectedText(); + + if (!selectedText.isEmpty()) + SpeechClient::self()->slotSpeak(selectedText, "en"); + } +} + +void View::slotSetSelectedArticleUnread() +{ + QValueList<Article> articles = m_articleList->selectedArticles(); + + if (articles.isEmpty()) + return; + + for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it) + (*it).setStatus(Article::Unread); +} + +void View::slotSetSelectedArticleNew() +{ + QValueList<Article> articles = m_articleList->selectedArticles(); + + if (articles.isEmpty()) + return; + + for (QValueList<Article>::Iterator it = articles.begin(); it != articles.end(); ++it) + (*it).setStatus(Article::New); +} + +void View::slotSetCurrentArticleReadDelayed() +{ + Article article = m_articleList->currentArticle(); + + if (article.isNull()) + return; + + article.setStatus(Article::Read); +} + +void View::slotMouseOverInfo(const KFileItem *kifi) +{ + if (kifi) + { + KFileItem *k=(KFileItem*)kifi; + m_mainFrame->setStatusText(k->url().prettyURL());//getStatusBarInfo()); + } + else + { + m_mainFrame->setStatusText(QString::null); + } +} + +void View::readProperties(KConfig* config) +{ + + if (!Settings::resetQuickFilterOnNodeChange()) + { + m_searchBar->slotSetText(config->readEntry("searchLine")); + int statusfilter = config->readNumEntry("searchCombo", -1); + if (statusfilter != -1) + m_searchBar->slotSetStatus(statusfilter); + } + + int selectedID = config->readNumEntry("selectedNodeID", -1); + if (selectedID != -1) + { + TreeNode* selNode = m_feedList->findByID(selectedID); + if (selNode) + m_listTabWidget->activeView()->setSelectedNode(selNode); + } + + QStringList urls = config->readListEntry("FeedBrowserURLs"); + QStringList::ConstIterator it = urls.begin(); + for (; it != urls.end(); ++it) + { + KURL url = KURL::fromPathOrURL(*it); + if (url.isValid()) + slotOpenNewTab(url, true); // open in background + } +} + +void View::saveProperties(KConfig* config) +{ + // save filter settings + config->writeEntry("searchLine", m_searchBar->text()); + config->writeEntry("searchCombo", m_searchBar->status()); + + TreeNode* sel = m_listTabWidget->activeView()->selectedNode(); + + if (sel) + { + config->writeEntry("selectedNodeID", sel->id() ); + } + + // save browser URLs + QStringList urls; + QPtrList<Frame> frames = m_tabs->frames(); + QPtrList<Frame>::ConstIterator it = frames.begin(); + for (; it != frames.end(); ++it) + { + Frame *frame = *it; + KParts::ReadOnlyPart *part = frame->part(); + PageViewer *pageViewer = dynamic_cast<PageViewer*>(part); // don't save the ArticleViewer + if (pageViewer) + { + KURL url = pageViewer->url(); + if (url.isValid()) + urls.append(url.prettyURL()); + } + } + + config->writeEntry("FeedBrowserURLs", urls); +} + +void View::connectToFeedList(FeedList* feedList) +{ + connect(feedList->rootNode(), SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotSetTotalUnread())); + slotSetTotalUnread(); +} + +void View::disconnectFromFeedList(FeedList* feedList) +{ + disconnect(feedList->rootNode(), SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotSetTotalUnread())); +} + +void View::updateTagActions() +{ + QStringList tags; + + QValueList<Article> selectedArticles = m_articleList->selectedArticles(); + + for (QValueList<Article>::ConstIterator it = selectedArticles.begin(); it != selectedArticles.end(); ++it) + { + QStringList atags = (*it).tags(); + for (QStringList::ConstIterator it2 = atags.begin(); it2 != atags.end(); ++it2) + { + if (!tags.contains(*it2)) + tags += *it2; + } + } + m_actionManager->slotUpdateTagActions(!selectedArticles.isEmpty(), tags); +} + +} // namespace Akregator + +#include "akregator_view.moc" diff --git a/akregator/src/akregator_view.h b/akregator/src/akregator_view.h new file mode 100644 index 000000000..599f8cfdc --- /dev/null +++ b/akregator/src/akregator_view.h @@ -0,0 +1,343 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2004 Sashmit Bhaduri <smt@vfemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef _AKREGATORVIEW_H_ +#define _AKREGATORVIEW_H_ + +#include <qpixmap.h> +#include <qwidget.h> + +#include <kurl.h> + +#include "akregator_run.h" +#include "feed.h" + +class QSplitter; +class QDomDocument; +class QDomElement; +class QHBox; +class QToolButton; +class QListViewItem; +class KComboBox; +class KConfig; +class KFileItem; +class KLineEdit; +class KListView; +class KListViewItem; +class KTabWidget; +class Viewer; + +namespace KIO { + + class Job; +} + +namespace Akregator { + + class AboutPageViewer; + class ActionManagerImpl; + class ArticleMatcher; + class ArticleListView; + class ArticleViewer; + class BrowserRun; + class Folder; + class FeedList; + class Frame; + class NodeListView; + class ListTabWidget; + class Part; + class SearchBar; + class TabWidget; + class Tag; + class TagNodeList; + + /** + * This is the main widget of the view, containing tree view, article list, viewer etc. + */ + class View : public QWidget + { + Q_OBJECT + public: + + /** constructor + @param part the Akregator::Part which contains this widget + @param parent parent widget + @param Actionmanager for this view + @param name the name of the widget (@ref QWidget ) + */ + View(Akregator::Part *part, QWidget *parent, ActionManagerImpl* actionManager, const char* name); + + /** destructor. Note that cleanups should be done in + slotOnShutdown(), so we don't risk accessing self-deleting objects after deletion. */ + ~View(); + + /** saves settings. Make sure that the Settings singleton is not destroyed yet when saveSettings is called */ + void saveSettings(); + + void slotSettingsChanged(); + + /** Adds the feeds in @c doc to the "Imported Folder" + @param doc the DOM tree (OPML) of the feeds to import */ + bool importFeeds(const QDomDocument& doc); + + /** Parse OPML presentation of feeds and read in articles archive, if present. If @c parent is @c NULL, the current + feed list is replaced by the parsed one + @param doc QDomDocument generated from OPML + @param parent The parent group the new nodes */ + bool loadFeeds(const QDomDocument& doc, Folder* parent = 0); + + /** + @return the displayed Feed List in OPML format + */ + QDomDocument feedListToOPML(); + + /** + Add a feed to a group. + @param url The URL of the feed to add. + @param group The name of the folder into which the feed is added. + If the group does not exist, it is created. The feed is added as the last member of the group. + */ + void addFeedToGroup(const QString& url, const QString& group); + + /** session management **/ + virtual void readProperties(KConfig* config); + virtual void saveProperties(KConfig* config); + + Frame* currentFrame() const { return m_currentFrame; } + + signals: + /** emitted when the unread count of "All Feeds" was changed */ + void signalUnreadCountChanged(int); + + void setWindowCaption(const QString&); + void setStatusBarText(const QString&); + void setProgress(int); + void signalStarted(KIO::Job*); + void signalCompleted(); + void signalCanceled(const QString&); + + public slots: + + void slotOnShutdown(); + + /** selected tree node has changed */ + void slotNodeSelected(TreeNode* node); + + /** the article selection has changed */ + void slotArticleSelected(const Article&); + + /** Shows requested popup menu for feed tree */ + void slotFeedTreeContextMenu(KListView*, TreeNode*, const QPoint&); + + /** emits @ref signalUnreadCountChanged(int) */ + void slotSetTotalUnread(); + + /** special behaviour in article list view (TODO: move code there?) */ + void slotMouseButtonPressed(int button, const Article& article, const QPoint & pos, int c); + + /** opens article of @c item in external browser */ + void slotOpenArticleExternal(const Article& article, const QPoint&, int); + + /** opens the current article (currentItem) in external browser + TODO: use selected instead of current? */ + void slotOpenCurrentArticleExternal(); + + /** opens the current article (currentItem) in background tab + TODO: use selected instead of current? */ + void slotOpenCurrentArticleBackgroundTab(); + + /** opens current article in new tab, background/foreground depends on settings TODO: use selected instead of current? */ + void slotOpenCurrentArticle(); + + /** copies the link of current article to clipboard + */ + void slotCopyLinkAddress(); + + /** opens a page viewer in a new tab and loads an URL + @param url the url to load + @param background whether the tab should be opened in the background or in the foreground (activated after creation) */ + void slotOpenNewTab(const KURL& url, bool background = false); + + /** called when another part/frame is activated. Updates progress bar, caption etc. accordingly + @param f the activated frame */ + void slotFrameChanged(Frame *f); + + /** sets the window caption after a frame change */ + void slotCaptionChanged(const QString &); + + /** called when URLs are dropped into the tree view */ + void slotFeedURLDropped (KURL::List &urls, TreeNode* after, Folder *parent); + + /** displays a URL in the status bar when the user moves the mouse over a link */ + void slotMouseOverInfo(const KFileItem *kifi); + + /** sets the status bar text to a given string */ + void slotStatusText(const QString &); + + void slotStarted(); + void slotCanceled(const QString &); + void slotCompleted(); + void slotLoadingProgress(int); + + void slotFetchingStarted(); + void slotFetchingStopped(); + + + /** Feed has been fetched, populate article view if needed and update counters. */ + void slotFeedFetched(Feed *); + + /** adds a new feed to the feed tree */ + void slotFeedAdd(); + /** adds a feed group to the feed tree */ + void slotFeedAddGroup(); + /** removes the currently selected feed (ask for confirmation)*/ + void slotFeedRemove(); + /** calls the properties dialog for feeds, starts renaming for feed groups */ + void slotFeedModify(); + /** fetches the currently selected feed */ + void slotFetchCurrentFeed(); + /** starts fetching of all feeds in the tree */ + void slotFetchAllFeeds(); + /** marks all articles in the currently selected feed as read */ + void slotMarkAllRead(); + /** marks all articles in all feeds in the tree as read */ + void slotMarkAllFeedsRead(); + /** opens the homepage of the currently selected feed */ + void slotOpenHomepage(); + + /** toggles the keep flag of the currently selected article */ + void slotArticleToggleKeepFlag(bool enabled); + /** deletes the currently selected article */ + void slotArticleDelete(); + /** marks the currently selected article as read */ + void slotSetSelectedArticleRead(); + /** marks the currently selected article as unread */ + void slotSetSelectedArticleUnread(); + /** marks the currently selected article as new */ + void slotSetSelectedArticleNew(); + /** marks the currenctly selected article as read after a user-set delay */ + void slotSetCurrentArticleReadDelayed(); + + /** reads the currently selected articles using KTTSD */ + void slotTextToSpeechRequest(); + + void slotAssignTag(const Tag& tag, bool assign); + //void slotRemoveTag(const Tag& tag); + void slotNewTag(); + void slotTagCreated(const Tag& tag); + void slotTagRemoved(const Tag& tag); + + /** switches view mode to normal view */ + void slotNormalView(); + /** switches view mode to widescreen view */ + void slotWidescreenView(); + /** switches view mode to combined view */ + void slotCombinedView(); + /** toggles the visibility of the filter bar */ + void slotToggleShowQuickFilter(); + + /** selects the previous unread article in the article list */ + void slotPrevUnreadArticle(); + /** selects the next unread article in the article list */ + void slotNextUnreadArticle(); + + void slotMoveCurrentNodeUp(); + void slotMoveCurrentNodeDown(); + void slotMoveCurrentNodeLeft(); + void slotMoveCurrentNodeRight(); + + protected: + + void addFeed(const QString& url, TreeNode* after, Folder* parent, bool autoExec = true); + + void connectToFeedList(FeedList* feedList); + void disconnectFromFeedList(FeedList* feedList); + + void updateTagActions(); + + protected slots: + + void connectFrame(Frame *); + + void setTabIcon(const QPixmap&); + + void slotDoIntervalFetches(); + void slotDeleteExpiredArticles(); + + /** HACK: receives signal from browserrun when the browserrun detects an HTML mimetype and actually loads the page TODO: Remove for KDE 4.0 */ + void slotOpenURLReply(const KURL& url, Akregator::Viewer* currentViewer, Akregator::BrowserRun::OpeningMode mode); + + /** HACK: part of the url opening hack for 3.5. called when a viewer emits urlClicked(). TODO: Remove for KDE4 */ + void slotUrlClickedInViewer(const KURL& url, Viewer* viewer, bool newTab, bool background); + + void slotOpenURL(const KURL& url, Akregator::Viewer* currentViewer, Akregator::BrowserRun::OpeningMode mode); + + public: // compat with KDE-3.x assertions, remove for KDE 4 +// private: + + enum ViewMode { NormalView=0, WidescreenView, CombinedView }; + + FeedList* m_feedList; + TagNodeList* m_tagNodeList; + NodeListView* m_feedListView; + NodeListView* m_tagNodeListView; + ArticleListView *m_articleList; + ArticleViewer *m_articleViewer; + TabWidget *m_tabs; + + QWidget *m_mainTab; + Frame *m_mainFrame; + Frame *m_currentFrame; + + SearchBar* m_searchBar; + + QSplitter *m_articleSplitter; + QSplitter *m_horizontalSplitter; + + ListTabWidget* m_listTabWidget; + Akregator::Part *m_part; + ViewMode m_viewMode; + + QTimer *m_fetchTimer; + QTimer* m_expiryTimer; + QTimer *m_markReadTimer; + + bool m_shuttingDown; + bool m_displayingAboutPage; + + ActionManagerImpl* m_actionManager; + + QPixmap m_keepFlagIcon; + friend class EditNodePropertiesVisitor; + class EditNodePropertiesVisitor; + EditNodePropertiesVisitor* m_editNodePropertiesVisitor; + friend class DeleteNodeVisitor; + class DeleteNodeVisitor; + DeleteNodeVisitor* m_deleteNodeVisitor; + }; +} + +#endif // _AKREGATORVIEW_H_ diff --git a/akregator/src/akregatorconfig.kcfgc b/akregator/src/akregatorconfig.kcfgc new file mode 100644 index 000000000..5602bc561 --- /dev/null +++ b/akregator/src/akregatorconfig.kcfgc @@ -0,0 +1,8 @@ +# Code generation options for kconfig_compiler +File=akregator.kcfg +ClassName=Settings +NameSpace=Akregator +Singleton=true +Mutators=true +Visibility=KDE_EXPORT +MemberVariables=private diff --git a/akregator/src/article.cpp b/akregator/src/article.cpp new file mode 100644 index 000000000..eeab61dd5 --- /dev/null +++ b/akregator/src/article.cpp @@ -0,0 +1,479 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "article.h" +#include "feed.h" +#include "feedstorage.h" +#include "storage.h" +#include "librss/librss.h" +#include "shared.h" +#include "utils.h" + +#include <qdatetime.h> +#include <qdom.h> +#include <qregexp.h> +#include <qstringlist.h> +#include <qvaluelist.h> + +#include <krfcdate.h> +#include <kdebug.h> +#include <kurl.h> + + +namespace Akregator { + +struct Article::Private : public Shared +{ + /** The status of the article is stored in an int, the bits having the + following meaning: + + 0000 0001 Deleted + 0000 0010 Trash + 0000 0100 New + 0000 1000 Read + 0001 0000 Keep + */ + + enum Status {Deleted=0x01, Trash=0x02, New=0x04, Read=0x08, Keep=0x10}; + + QString guid; + Backend::FeedStorage* archive; + Feed* feed; + + // the variables below are initialized to null values in the Article constructor + // and then loaded on demand instead. + // + // to read their values, you should therefore use the accessor methods of the Article + // hash(), pubDate(), statusBits() rather than accessing them directly. + uint hash; + QDateTime pubDate; + int status; +}; + +Article::Article() : d(new Private) +{ + d->hash = 0; + d->status = 0; + d->feed = 0; + d->archive = 0; +} + +Article::Article(const QString& guid, Feed* feed) : d(new Private) +{ + // this constructor should be as cheap as possible, so avoid calls to + // read information from the archive in here if possible + // + // d->hash, d->pubDate and d->status are loaded on-demand by + // the hash(), pubDate() and statusBits() methods respectively + + d->feed = feed; + d->guid = guid; + d->archive = Backend::Storage::getInstance()->archiveFor(feed->xmlUrl()); + d->status = 0; +} + +void Article::initialize(RSS::Article article, Backend::FeedStorage* archive) +{ + d->archive = archive; + d->status = Private::New; + d->hash = Utils::calcHash(article.title() + article.description() + article.author() + article.link().url() + + article.commentsLink().url() ); + + d->guid = article.guid(); + + if (!d->archive->contains(d->guid)) + { + d->archive->addEntry(d->guid); + + if (article.meta("deleted") == "true") + { // if article is in deleted state, we just add the status and omit the rest + d->status = Private::Read | Private::Deleted; + d->archive->setStatus(d->guid, d->status); + } + else + { // article is not deleted, let's add it to the archive + + d->archive->setHash(d->guid, hash() ); + QString title = article.title().isEmpty() ? buildTitle(article.description()) : article.title(); + d->archive->setTitle(d->guid, title); + d->archive->setDescription(d->guid, article.description()); + d->archive->setLink(d->guid, article.link().url()); + d->archive->setComments(d->guid, article.comments()); + d->archive->setCommentsLink(d->guid, article.commentsLink().url()); + d->archive->setGuidIsPermaLink(d->guid, article.guidIsPermaLink()); + d->archive->setGuidIsHash(d->guid, article.meta("guidIsHash") == "true"); + d->pubDate = article.pubDate().isValid() ? article.pubDate() : QDateTime::currentDateTime(); + d->archive->setPubDate(d->guid, d->pubDate.toTime_t()); + d->archive->setAuthor(d->guid, article.author()); + + QValueList<RSS::Category> cats = article.categories(); + QValueList<RSS::Category>::ConstIterator end = cats.end(); + + for (QValueList<RSS::Category>::ConstIterator it = cats.begin(); it != end; ++it) + { + Backend::Category cat; + + cat.term = (*it).category(); + cat.scheme = (*it).domain(); + cat.name = (*it).category(); + + d->archive->addCategory(d->guid, cat); + } + + if (!article.enclosure().isNull()) + { + d->archive->setEnclosure(d->guid, article.enclosure().url(), article.enclosure().type(), article.enclosure().length()); + } + else + { + d->archive->removeEnclosure(d->guid); + } + + QString status = article.meta("status"); + + if (!status.isEmpty()) + { + int statusInt = status.toInt(); + if (statusInt == New) + statusInt = Unread; + setStatus(statusInt); + } + setKeep(article.meta("keep") == "true"); + } + } + else + { + // always update comments count, as it's not used for hash calculation + d->archive->setComments(d->guid, article.comments()); + if ( hash() != d->archive->hash(d->guid)) //article is in archive, was it modified? + { // if yes, update + d->pubDate.setTime_t(d->archive->pubDate(d->guid)); + d->archive->setHash(d->guid, hash() ); + QString title = article.title().isEmpty() ? buildTitle(article.description()) : article.title(); + d->archive->setTitle(d->guid, title); + d->archive->setDescription(d->guid, article.description()); + d->archive->setLink(d->guid, article.link().url()); + d->archive->setCommentsLink(d->guid, article.commentsLink().url()); + d->archive->setAuthor(d->guid, article.author()); + } + } +} + +Article::Article(RSS::Article article, Feed* feed) : d(new Private) +{ + //assert(feed) + d->feed = feed; + initialize(article, Backend::Storage::getInstance()->archiveFor(feed->xmlUrl())); +} + +Article::Article(RSS::Article article, Backend::FeedStorage* archive) : d(new Private) +{ + d->feed = 0; + initialize(article, archive); +} + +bool Article::isNull() const +{ + return d->archive == 0; // TODO: use proper null state +} + +void Article::offsetPubDate(int secs) +{ + d->pubDate = pubDate().addSecs(secs); + d->archive->setPubDate(d->guid, d->pubDate.toTime_t()); + +} + +void Article::setDeleted() +{ + if (isDeleted()) + return; + + setStatus(Read); + d->status = Private::Deleted | Private::Read; + d->archive->setStatus(d->guid, d->status); + d->archive->setDeleted(d->guid); + + if (d->feed) + d->feed->setArticleDeleted(*this); +} + +bool Article::isDeleted() const +{ + return (statusBits() & Private::Deleted) != 0; +} + +Article::Article(const Article &other) : d(new Private) +{ + *this = other; +} + +Article::~Article() +{ + if (d->deref()) + { + delete d; + d = 0; + } +} + +Article &Article::operator=(const Article &other) +{ + if (this != &other) { + other.d->ref(); + if (d && d->deref()) + delete d; + d = other.d; + } + return *this; +} + + +bool Article::operator<(const Article &other) const +{ + return pubDate() > other.pubDate() || + (pubDate() == other.pubDate() && guid() < other.guid() ); +} + +bool Article::operator<=(const Article &other) const +{ + return (pubDate() > other.pubDate() || *this == other); +} + +bool Article::operator>(const Article &other) const +{ + return pubDate() < other.pubDate() || + (pubDate() == other.pubDate() && guid() > other.guid() ); +} + +bool Article::operator>=(const Article &other) const +{ + return (pubDate() > other.pubDate() || *this == other); +} + +bool Article::operator==(const Article &other) const +{ + return d->guid == other.guid(); +} + +int Article::statusBits() const +{ + // delayed loading of status information from archive + if ( d->status == 0 ) + { + d->status = d->archive->status(d->guid); + } + + return d->status; +} + +int Article::status() const +{ + if ((statusBits() & Private::Read) != 0) + return Read; + + if ((statusBits() & Private::New) != 0) + return New; + else + return Unread; +} + +void Article::setStatus(int stat) +{ + // use status() rather than statusBits() here to filter out status flags that we are not + // interested in + int oldStatus = status(); + + if (oldStatus != stat) + { + switch (stat) + { + case Read: + d->status = ( d->status | Private::Read) & ~Private::New; + break; + case Unread: + d->status = ( d->status & ~Private::Read) & ~Private::New; + break; + case New: + d->status = ( d->status | Private::New) & ~Private::Read; + break; + } + d->archive->setStatus(d->guid, d->status); + if (d->feed) + d->feed->setArticleChanged(*this, oldStatus); + } +} + +QString Article::title() const +{ + return d->archive->title(d->guid); +} + +QString Article::author() const +{ + return d->archive->author(d->guid); +} + +KURL Article::link() const +{ + return d->archive->link(d->guid); +} + +QString Article::description() const +{ + return d->archive->description(d->guid); +} + +QString Article::guid() const +{ + return d->guid; +} + +KURL Article::commentsLink() const +{ + return d->archive->commentsLink(d->guid); +} + + +int Article::comments() const +{ + + return d->archive->comments(d->guid); +} + + +bool Article::guidIsPermaLink() const +{ + return d->archive->guidIsPermaLink(d->guid); +} + +bool Article::guidIsHash() const +{ + return d->archive->guidIsHash(d->guid); +} + +uint Article::hash() const +{ + // delayed loading of hash from archive + if ( d->hash == 0 ) + { + d->hash = d->archive->hash(d->guid); + } + + return d->hash; +} + +bool Article::keep() const +{ + return ( statusBits() & Private::Keep) != 0; +} + +RSS::Enclosure Article::enclosure() const +{ + bool hasEnc; + QString url, type; + int length; + d->archive->enclosure(d->guid, hasEnc, url, type, length); + return hasEnc ? RSS::Enclosure(url, length, type) : RSS::Enclosure(); + + +} + + +void Article::setKeep(bool keep) +{ + d->status = keep ? ( statusBits() | Private::Keep) : ( statusBits() & ~Private::Keep); + d->archive->setStatus(d->guid, d->status); + if (d->feed) + d->feed->setArticleChanged(*this); +} + +void Article::addTag(const QString& tag) +{ + d->archive->addTag(d->guid, tag); + if (d->feed) + d->feed->setArticleChanged(*this); +} + +void Article::removeTag(const QString& tag) +{ + d->archive->removeTag(d->guid, tag); + if (d->feed) + d->feed->setArticleChanged(*this); +} + +bool Article::hasTag(const QString& tag) const +{ + return d->archive->tags(d->guid).contains(tag); +} + +QStringList Article::tags() const +{ + return d->archive->tags(d->guid); +} + +Feed* Article::feed() const +{ return d->feed; } + +const QDateTime& Article::pubDate() const +{ + // delayed loading of publication date information from archive + if ( d->pubDate.isNull() ) + { + d->pubDate.setTime_t(d->archive->pubDate(d->guid)); + } + + return d->pubDate; +} + +QString Article::buildTitle(const QString& description) +{ + QString s = description; + if (description.stripWhiteSpace().isEmpty()) + return ""; + + int i = s.find('>',500); /*avoid processing too much */ + if (i != -1) + s = s.left(i+1); + QRegExp rx("(<([^\\s>]*)(?:[^>]*)>)[^<]*", false); + QString tagName, toReplace, replaceWith; + while (rx.search(s) != -1 ) + { + tagName=rx.cap(2); + if (tagName=="SCRIPT"||tagName=="script") + toReplace=rx.cap(0); // strip tag AND tag contents + else if (tagName.startsWith("br") || tagName.startsWith("BR")) + { + toReplace=rx.cap(1); + replaceWith=" "; + } + else + toReplace=rx.cap(1); // strip just tag + s=s.replace(s.find(toReplace),toReplace.length(),replaceWith); // do the deed + } + if (s.length()> 90) + s=s.left(90)+"..."; + return s.simplifyWhiteSpace(); +} +} // namespace Akregator diff --git a/akregator/src/article.h b/akregator/src/article.h new file mode 100644 index 000000000..a4dae19c6 --- /dev/null +++ b/akregator/src/article.h @@ -0,0 +1,147 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_ARTICLE_H +#define AKREGATOR_ARTICLE_H + +class QDateTime; +class QDomDocument; +class QDomElement; +class QString; +class QStringList; +class QWidget; + +template <class T> class QValueList; + +typedef unsigned int uint; + +class KURL; +class KURLLabel; + +namespace RSS +{ + class Article; + class Enclosure; +} + +namespace Akregator +{ + namespace Backend + { + class FeedStorage; + } + class Feed; + /** A proxy class for RSS::Article with some additional methods to assist sorting. */ + class Article + { + public: + enum Status { Unread=0, Read, New }; + typedef QValueList<Article> List; + + Article(); + /** creates am article object for an existing article. + The constructor accesses the archive to load it's data + */ + Article(const QString& guid, Feed* feed); + /** creates an article object from a parsed librss Article + the article is added to the archive if not yet stored, or updated if stored but modified + */ + Article(RSS::Article article, Feed* feed); + + Article(RSS::Article article, Backend::FeedStorage* archive); + Article(const Article &other); + Article &operator=(const Article &other); + bool operator==(const Article &other) const; + bool operator!=(const Article &other) const { return !operator==(other); } + virtual ~Article(); + + bool isNull() const; + + int status() const; + void setStatus(int s); + + void offsetPubDate(int secs); + + QString title() const; + KURL link() const; + + QString author() const; + + QString description() const; + QString guid() const; + /** if true, the article should be kept even when expired **/ + bool keep() const; + void setKeep(bool keep); + bool isDeleted() const; + + RSS::Enclosure enclosure() const; + + void setDeleted(); + + + Feed* feed() const; + + /** returns a hash value used to detect changes in articles with non-hash GUIDs. If the guid is a hash itself, it returns @c 0 */ + + uint hash() const; + + /** returns if the guid is a hash or an ID taken from the source */ + + bool guidIsHash() const; + + bool guidIsPermaLink() const; + + const QDateTime& pubDate() const; + + KURL commentsLink() const; + + int comments() const; + + void addTag(const QString& tag); + void removeTag(const QString& tag); + bool hasTag(const QString& tag) const; + QStringList tags() const; + + bool operator<(const Article &other) const; + bool operator<=(const Article &other) const; + bool operator>(const Article &other) const; + bool operator>=(const Article &other) const; + + + private: + void initialize(RSS::Article article, Backend::FeedStorage* archive); + static QString buildTitle(const QString& description); + + int statusBits() const; // returns all of the status bits for the article. this + // differs from status() which only returns the most relevant + // status flag. + + + struct Private; + Private *d; + }; +} + +#endif diff --git a/akregator/src/articlefilter.cpp b/akregator/src/articlefilter.cpp new file mode 100644 index 000000000..f21605fad --- /dev/null +++ b/akregator/src/articlefilter.cpp @@ -0,0 +1,715 @@ +/* + * articlefilter.cpp + * + * Copyright (c) 2004, 2005 Frerich Raabe <raabe@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "articlefilter.h" +#include "article.h" +#include "shared.h" + +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kurl.h> + +#include <qregexp.h> + +namespace Akregator { +namespace Filters { + +QString Criterion::subjectToString(Subject subj) +{ + switch (subj) + { + case Title: + return QString::fromLatin1("Title"); + case Link: + return QString::fromLatin1("Link"); + case Author: + return QString::fromLatin1("Author"); + case Description: + return QString::fromLatin1("Description"); + case Status: + return QString::fromLatin1("Status"); + case KeepFlag: + return QString::fromLatin1("KeepFlag"); + default: // should never happen (TM) + return QString::fromLatin1("Description"); + } +} + +Criterion::Subject Criterion::stringToSubject(const QString& subjStr) +{ + if (subjStr == QString::fromLatin1("Title")) + return Title; + else if (subjStr == QString::fromLatin1("Link")) + return Link; + else if (subjStr == QString::fromLatin1("Description")) + return Description; + else if (subjStr == QString::fromLatin1("Author")) + return Author; + else if (subjStr == QString::fromLatin1("Status")) + return Status; + else if (subjStr == QString::fromLatin1("KeepFlag")) + return KeepFlag; + + // hopefully never reached + return Description; +} + +QString Criterion::predicateToString(Predicate pred) +{ + switch (pred) + { + case Contains: + return QString::fromLatin1("Contains"); + case Equals: + return QString::fromLatin1("Equals"); + case Matches: + return QString::fromLatin1("Matches"); + case Negation: + return QString::fromLatin1("Negation"); + default:// hopefully never reached + return QString::fromLatin1("Contains"); + } +} + +Criterion::Predicate Criterion::stringToPredicate(const QString& predStr) +{ + if (predStr == QString::fromLatin1("Contains")) + return Contains; + else if (predStr == QString::fromLatin1("Equals")) + return Equals; + else if (predStr == QString::fromLatin1("Matches")) + return Matches; + else if (predStr == QString::fromLatin1("Negation")) + return Negation; + + // hopefully never reached + return Contains; +} + +Criterion::Criterion() +{ +} + +Criterion::Criterion( Subject subject, Predicate predicate, const QVariant &object ) + : m_subject( subject ) + , m_predicate( predicate ) + , m_object( object ) +{ + +} + +void Criterion::writeConfig(KConfig* config) const +{ + config->writeEntry(QString::fromLatin1("subject"), subjectToString(m_subject)); + + config->writeEntry(QString::fromLatin1("predicate"), predicateToString(m_predicate)); + + config->writeEntry(QString::fromLatin1("objectType"), QString(m_object.typeName())); + + config->writeEntry(QString::fromLatin1("objectValue"), m_object); +} + +void Criterion::readConfig(KConfig* config) +{ + m_subject = stringToSubject(config->readEntry(QString::fromLatin1("subject"))); + m_predicate = stringToPredicate(config->readEntry(QString::fromLatin1("predicate"))); + QVariant::Type type = QVariant::nameToType(config->readEntry(QString::fromLatin1("objType")).ascii()); + + if (type != QVariant::Invalid) + { + m_object = config->readPropertyEntry(QString::fromLatin1("objectValue"), type); + } +} + +bool Criterion::satisfiedBy( const Article &article ) const +{ + QVariant concreteSubject; + + switch ( m_subject ) { + case Title: + concreteSubject = QVariant(article.title()); + break; + case Description: + concreteSubject = QVariant(article.description()); + break; + case Author: + concreteSubject = QVariant(article.author()); + break; + case Link: + // ### Maybe use prettyURL here? + concreteSubject = QVariant(article.link().url()); + break; + case Status: + concreteSubject = QVariant(article.status()); + break; + case KeepFlag: + concreteSubject = QVariant(article.keep()); + default: + break; + } + + bool satisfied = false; + + const Predicate predicateType = static_cast<Predicate>( m_predicate & ~Negation ); + QString subjectType=concreteSubject.typeName(); + + switch ( predicateType ) { + case Contains: + satisfied = concreteSubject.toString().find( m_object.toString(), 0, false ) != -1; + break; + case Equals: + if (subjectType=="int") + satisfied = concreteSubject.toInt() == m_object.toInt(); + else + satisfied = concreteSubject.toString() == m_object.toString(); + break; + case Matches: + satisfied = QRegExp( m_object.toString() ).search( concreteSubject.toString() ) != -1; + break; + default: + kdDebug() << "Internal inconsistency; predicateType should never be Negation" << endl; + break; + } + + if ( m_predicate & Negation ) { + satisfied = !satisfied; + } + + return satisfied; +} + +Criterion::Subject Criterion::subject() const +{ + return m_subject; +} + +Criterion::Predicate Criterion::predicate() const +{ + return m_predicate; +} + +QVariant Criterion::object() const +{ + return m_object; +} + +ArticleMatcher::ArticleMatcher() + : m_association( None ) +{ +} + +ArticleMatcher::~ArticleMatcher() +{ +} + +bool ArticleMatcher::matchesAll() const +{ + return m_criteria.isEmpty(); +} + +ArticleMatcher* ArticleMatcher::clone() const +{ + return new ArticleMatcher(*this); +} + +ArticleMatcher::ArticleMatcher( const QValueList<Criterion> &criteria, Association assoc) + : m_criteria( criteria ) + , m_association( assoc ) +{ +} + +ArticleMatcher& ArticleMatcher::operator=(const ArticleMatcher& other) +{ + m_association = other.m_association; + m_criteria = other.m_criteria; + return *this; +} + +ArticleMatcher::ArticleMatcher(const ArticleMatcher& other) : AbstractMatcher(other) +{ + *this = other; +} + +bool ArticleMatcher::matches( const Article &a ) const +{ + switch ( m_association ) { + case LogicalOr: + return anyCriterionMatches( a ); + case LogicalAnd: + return allCriteriaMatch( a ); + default: + break; + } + return true; +} + +void ArticleMatcher::writeConfig(KConfig* config) const +{ + config->writeEntry(QString::fromLatin1("matcherAssociation"), associationToString(m_association)); + + config->writeEntry(QString::fromLatin1("matcherCriteriaCount"), m_criteria.count()); + + int index = 0; + + for (QValueList<Criterion>::ConstIterator it = m_criteria.begin(); it != m_criteria.end(); ++it) + { + config->setGroup(config->group()+QString::fromLatin1("_Criterion")+QString::number(index)); + (*it).writeConfig(config); + ++index; + } +} + +void ArticleMatcher::readConfig(KConfig* config) +{ + m_criteria.clear(); + m_association = stringToAssociation(config->readEntry(QString::fromLatin1("matcherAssociation"))); + + int count = config->readNumEntry(QString::fromLatin1("matcherCriteriaCount"), 0); + + for (int i = 0; i < count; ++i) + { + Criterion c; + config->setGroup(config->group()+QString::fromLatin1("_Criterion")+QString::number(i)); + c.readConfig(config); + m_criteria.append(c); + } +} + +bool ArticleMatcher::operator==(const AbstractMatcher& other) const +{ + AbstractMatcher* ptr = const_cast<AbstractMatcher*>(&other); + ArticleMatcher* o = dynamic_cast<ArticleMatcher*>(ptr); + if (!o) + return false; + else + return m_association == o->m_association && m_criteria == o->m_criteria; +} +bool ArticleMatcher::operator!=(const AbstractMatcher& other) const +{ + return !(*this == other); +} + +bool ArticleMatcher::anyCriterionMatches( const Article &a ) const +{ + if (m_criteria.count()==0) + return true; + QValueList<Criterion>::ConstIterator it = m_criteria.begin(); + QValueList<Criterion>::ConstIterator end = m_criteria.end(); + for ( ; it != end; ++it ) { + if ( ( *it ).satisfiedBy( a ) ) { + return true; + } + } + return false; +} + +bool ArticleMatcher::allCriteriaMatch( const Article &a ) const +{ + if (m_criteria.count()==0) + return true; + QValueList<Criterion>::ConstIterator it = m_criteria.begin(); + QValueList<Criterion>::ConstIterator end = m_criteria.end(); + for ( ; it != end; ++it ) { + if ( !( *it ).satisfiedBy( a ) ) { + return false; + } + } + return true; +} + +ArticleMatcher::Association ArticleMatcher::stringToAssociation(const QString& assocStr) +{ + if (assocStr == QString::fromLatin1("LogicalAnd")) + return LogicalAnd; + else if (assocStr == QString::fromLatin1("LogicalOr")) + return LogicalOr; + else + return None; +} + +QString ArticleMatcher::associationToString(Association association) +{ + switch (association) + { + case LogicalAnd: + return QString::fromLatin1("LogicalAnd"); + case LogicalOr: + return QString::fromLatin1("LogicalOr"); + default: + return QString::fromLatin1("None"); + } +} + + +class TagMatcher::TagMatcherPrivate +{ + public: + QString tagID; + bool operator==(const TagMatcherPrivate& other) const + { + return tagID == other.tagID; + } +}; + +TagMatcher::TagMatcher(const QString& tagID) : d(new TagMatcherPrivate) +{ + d->tagID = tagID; +} + +TagMatcher::TagMatcher() : d(new TagMatcherPrivate) +{ +} + +TagMatcher::~TagMatcher() +{ + delete d; + d = 0; +} + +bool TagMatcher::matches(const Article& article) const +{ + return article.hasTag(d->tagID); +} + +TagMatcher* TagMatcher::clone() const +{ + return new TagMatcher(*this); +} + + +TagMatcher::TagMatcher(const TagMatcher& other) : AbstractMatcher(other), d(0) +{ + *this = other; +} + +void TagMatcher::writeConfig(KConfig* config) const +{ + config->writeEntry(QString::fromLatin1("matcherType"), QString::fromLatin1("TagMatcher")); + config->writeEntry(QString::fromLatin1("matcherParams"), d->tagID); +} + +void TagMatcher::readConfig(KConfig* config) +{ + d->tagID = config->readEntry(QString::fromLatin1("matcherParams")); +} + +bool TagMatcher::operator==(const AbstractMatcher& other) const +{ + AbstractMatcher* ptr = const_cast<AbstractMatcher*>(&other); + TagMatcher* tagFilter = dynamic_cast<TagMatcher*>(ptr); + return tagFilter ? *d == *(tagFilter->d) : false; +} + +bool TagMatcher::operator!=(const AbstractMatcher &other) const +{ + return !(*this == other); +} + +TagMatcher& TagMatcher::operator=(const TagMatcher& other) +{ + d = new TagMatcherPrivate; + *d = *(other.d); + return *this; +} + +void DeleteAction::exec(Article& article) +{ + if (!article.isNull()) + article.setDeleted(); +} + +SetStatusAction::SetStatusAction(int status) : m_status(status) +{ +} + +void SetStatusAction::exec(Article& article) +{ + if (!article.isNull()) + article.setStatus(m_status); +} + +int SetStatusAction::status() const +{ + return m_status; +} + +void SetStatusAction::setStatus(int status) +{ + m_status = status; +} + +void SetStatusAction::writeConfig(KConfig* config) const +{ + config->writeEntry(QString::fromLatin1("actionType"), QString::fromLatin1("SetStatusAction")); + config->writeEntry(QString::fromLatin1("actionParams"), m_status); +} + +void SetStatusAction::readConfig(KConfig* config) +{ + m_status = config->readNumEntry(QString::fromLatin1("actionParams"), Article::Read); +} + +bool SetStatusAction::operator==(const AbstractAction& other) +{ + AbstractAction* ptr = const_cast<AbstractAction*>(&other); + SetStatusAction* o = dynamic_cast<SetStatusAction*>(ptr); + if (!o) + return false; + else + return m_status == o->m_status; +} + + +AssignTagAction::AssignTagAction(const QString& tagID) : m_tagID(tagID) +{ +} + +void AssignTagAction::exec(Article& article) +{ + if (!article.isNull()) + article.addTag(m_tagID); +} + +class ArticleFilter::ArticleFilterPrivate : public Shared +{ + public: + AbstractAction* action; + AbstractMatcher* matcher; + QString name; + int id; + +}; + +ArticleFilter::ArticleFilter() : d(new ArticleFilterPrivate) +{ + d->id = KApplication::random(); + d->action = 0; + d->matcher = 0; +} + +ArticleFilter::ArticleFilter(const AbstractMatcher& matcher, const AbstractAction& action) : d(new ArticleFilterPrivate) +{ + d->id = KApplication::random(); + d->matcher = matcher.clone(); + d->action = action.clone(); +} + +ArticleFilter::ArticleFilter(const ArticleFilter& other) +{ + *this = other; +} + +ArticleFilter::~ArticleFilter() +{ + if (d->deref()) + { + delete d->action; + delete d->matcher; + delete d; + d = 0; + } + +} + +AbstractMatcher* ArticleFilter::matcher() const +{ + return d->matcher; +} + +AbstractAction* ArticleFilter::action() const +{ + return d->action; +} + +void ArticleFilter::setMatcher(const AbstractMatcher& matcher) +{ + delete d->matcher; + d->matcher = matcher.clone(); +} + +void ArticleFilter::setAction(const AbstractAction& action) +{ + delete d->action; + d->action = action.clone(); +} + +ArticleFilter& ArticleFilter::operator=(const ArticleFilter& other) +{ + if (this != &other) + { + other.d->ref(); + if (d && d->deref()) + delete d; + d = other.d; + } + return *this; +} + +int ArticleFilter::id() const +{ + return d->id; +} + +bool ArticleFilter::operator==(const ArticleFilter& other) const +{ + return *(d->matcher) == *(other.d->matcher) && *(d->action) == *(other.d->action) && d->name == other.d->name; +} + +void ArticleFilterList::writeConfig(KConfig* config) const +{ + config->setGroup(QString::fromLatin1("Filters")); + config->writeEntry(QString::fromLatin1("count"), count()); + int index = 0; + for (ArticleFilterList::ConstIterator it = begin(); it != end(); ++it) + { + config->setGroup(QString::fromLatin1("Filters_")+QString::number(index)); + (*it).writeConfig(config); + ++index; + } +} + +void ArticleFilterList::readConfig(KConfig* config) +{ + clear(); + config->setGroup(QString::fromLatin1("Filters")); + int count = config->readNumEntry(QString::fromLatin1("count"), 0); + for (int i = 0; i < count; ++i) + { + config->setGroup(QString::fromLatin1("Filters_")+QString::number(i)); + ArticleFilter filter; + filter.readConfig(config); + append(filter); + } +} + + +void AssignTagAction::readConfig(KConfig* config) +{ + m_tagID = config->readEntry(QString::fromLatin1("actionParams")); +} + +void AssignTagAction::writeConfig(KConfig* config) const +{ + config->writeEntry(QString::fromLatin1("actionType"), QString::fromLatin1("AssignTagAction")); + config->writeEntry(QString::fromLatin1("actionParams"), m_tagID); +} + +bool AssignTagAction::operator==(const AbstractAction& other) +{ + AbstractAction* ptr = const_cast<AbstractAction*>(&other); + AssignTagAction* o = dynamic_cast<AssignTagAction*>(ptr); + if (!o) + return false; + else + return m_tagID == o->m_tagID; +} + +const QString& AssignTagAction::tagID() const +{ + return m_tagID; +} + +void AssignTagAction::setTagID(const QString& tagID) +{ + m_tagID = tagID; +} + +void DeleteAction::readConfig(KConfig* /*config*/) +{ +} + +void DeleteAction::writeConfig(KConfig* config) const +{ + config->writeEntry(QString::fromLatin1("actionType"), QString::fromLatin1("DeleteAction")); +} + +bool DeleteAction::operator==(const AbstractAction& other) +{ + AbstractAction* ptr = const_cast<AbstractAction*>(&other); + DeleteAction* o = dynamic_cast<DeleteAction*>(ptr); + return o != 0; +} + +void ArticleFilter::readConfig(KConfig* config) +{ + delete d->matcher; + d->matcher = 0; + delete d->action; + d->action = 0; + + d->name = config->readEntry(QString::fromLatin1("name")); + d->id = config->readNumEntry(QString::fromLatin1("id"), 0); + + QString matcherType = config->readEntry(QString::fromLatin1("matcherType")); + + if (matcherType == QString::fromLatin1("TagMatcher")) + d->matcher = new TagMatcher(); + else if (matcherType == QString::fromLatin1("ArticleMatcher")) + d->matcher = new ArticleMatcher(); + + if (d->matcher) + d->matcher->readConfig(config); + + + QString actionType = config->readEntry(QString::fromLatin1("actionType")); + + if (actionType == QString::fromLatin1("AssignTagAction")) + d->action = new AssignTagAction(); + else if (actionType == QString::fromLatin1("DeleteAction")) + d->action = new DeleteAction(); + else if (actionType == QString::fromLatin1("SetStatusAction")) + d->action = new SetStatusAction(); + + if (d->action) + d->action->readConfig(config); +} + +void ArticleFilter::writeConfig(KConfig* config) const +{ + config->writeEntry(QString::fromLatin1("name"), d->name); + config->writeEntry(QString::fromLatin1("id"), d->id); + d->matcher->writeConfig(config); + d->action->writeConfig(config); +} + +void ArticleFilter::setName(const QString& name) +{ + d->name = name; +} + +const QString& ArticleFilter::name() const +{ + return d->name; +} + +void ArticleFilter::applyTo(Article& article) const +{ + if (d->matcher && d->action && d->matcher->matches(article)) + d->action->exec(article); +} +} //namespace Filters +} //namespace Akregator diff --git a/akregator/src/articlefilter.h b/akregator/src/articlefilter.h new file mode 100644 index 000000000..6acdcbc4f --- /dev/null +++ b/akregator/src/articlefilter.h @@ -0,0 +1,301 @@ +/* + * articlefilter.h + * + * Copyright (c) 2004, 2005 Frerich Raabe <raabe@kde.org> + * 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef ARTICLEFILTER_H +#define ARTICLEFILTER_H + +#include <qstring.h> +#include <qvaluelist.h> +#include <qvariant.h> + +class KConfig; + +namespace Akregator { + +class Article; + +namespace Filters { + +class AbstractAction; +class AbstractMatcher; +class Criterion; + +/** an article filter, basically a matcher and an action. + * @author Frank Osterfeld + */ +class ArticleFilter +{ + public: + + ArticleFilter(); + ArticleFilter(const AbstractMatcher& matcher, const AbstractAction& action); + ArticleFilter(const ArticleFilter& other); + + virtual ~ArticleFilter(); + + /** checks whether an article matches the matcher, and executes the action if so */ + void applyTo(Article& article) const; + + + + /** name of the filter, for display in filter list */ + const QString& name() const; + void setName(const QString& name); + + int id() const; + + AbstractMatcher* matcher() const; + void setMatcher(const AbstractMatcher& matcher); + + AbstractAction* action() const; + void setAction(const AbstractAction& action); + + ArticleFilter& operator=(const ArticleFilter& other); + bool operator==(const ArticleFilter& other) const; + + void writeConfig(KConfig* config) const; + void readConfig(KConfig* config); + + private: + class ArticleFilterPrivate; + ArticleFilterPrivate* d; + +}; + +class ArticleFilterList : public QValueList<ArticleFilter> +{ +public: + + void writeConfig(KConfig* config) const; + void readConfig(KConfig* config); +}; + +/** Abstract base class for matchers, a matcher just takes an article and checks whether the article matches some criterion or not. + * @author Frank Osterfeld + */ +class AbstractMatcher +{ + public: + + virtual ~AbstractMatcher() {} + /** returns a copy of the matcher */ + virtual AbstractMatcher* clone() const = 0; + + virtual bool matches(const Article& article) const = 0; + + virtual void writeConfig(KConfig* config) const = 0; + virtual void readConfig(KConfig* config) = 0; + + virtual bool operator==(const AbstractMatcher&) const = 0; + virtual bool operator!=(const AbstractMatcher &other) const = 0; +}; + +class TagMatcher : public AbstractMatcher +{ + public: + + TagMatcher(); + TagMatcher(const QString& tagID); + TagMatcher(const TagMatcher& other); + + virtual ~TagMatcher(); + + + virtual bool matches(const Article& article) const; + + virtual TagMatcher* clone() const; + + virtual void writeConfig(KConfig* config) const; + virtual void readConfig(KConfig* config); + + TagMatcher& operator=(const TagMatcher& other); + virtual bool operator==(const AbstractMatcher&) const; + virtual bool operator!=(const AbstractMatcher &other) const; + + private: + + class TagMatcherPrivate; + TagMatcherPrivate* d; +}; + +class AbstractAction +{ + public: + virtual void exec(Article& article) = 0; + + virtual void writeConfig(KConfig* config) const = 0; + virtual void readConfig(KConfig* config) = 0; + + virtual AbstractAction* clone() const = 0; + virtual bool operator==(const AbstractAction& other) = 0; +}; + +class DeleteAction : public AbstractAction +{ + public: + virtual void exec(Article& article); + + virtual void writeConfig(KConfig* config) const; + virtual void readConfig(KConfig* config); + + virtual DeleteAction* clone() const { return new DeleteAction; } + virtual bool operator==(const AbstractAction& other); +}; + +class SetStatusAction : public AbstractAction +{ + public: + SetStatusAction(int status=0); + + virtual void exec(Article& article); + + int status() const; + void setStatus(int status); + + virtual void writeConfig(KConfig* config) const; + virtual void readConfig(KConfig* config); + + virtual SetStatusAction* clone() const { return new SetStatusAction(*this); } + virtual bool operator==(const AbstractAction& other); + + private: + int m_status; +}; + +class AssignTagAction : public AbstractAction +{ + public: + + AssignTagAction(const QString& tagID=QString::null); + virtual void exec(Article& article); + + const QString& tagID() const; + void setTagID(const QString& tagID); + + virtual void writeConfig(KConfig* config) const; + virtual void readConfig(KConfig* config); + + virtual AssignTagAction* clone() const { return new AssignTagAction(*this); } + virtual bool operator==(const AbstractAction& other); + + private: + QString m_tagID; +}; + +/** a powerful matcher supporting multiple criterions, which can be combined via logical OR or AND + * @author Frerich Raabe + */ +class ArticleMatcher : public AbstractMatcher +{ + public: + + enum Association { + None, LogicalAnd, LogicalOr + }; + + ArticleMatcher(); + ArticleMatcher( const QValueList<Criterion> &criteria, Association assoc); + + ArticleMatcher(const ArticleMatcher& other); + virtual ~ArticleMatcher(); + + /** + * returns whether the matcher matches all articles anyway (empty criteria list), + * so there is no need to call matches() at all. + */ + virtual bool matchesAll() const; + + ArticleMatcher& operator=(const ArticleMatcher& other); + virtual ArticleMatcher* clone() const; + virtual bool matches(const Article &article) const; + virtual bool operator==(const AbstractMatcher &other) const; + virtual bool operator!=(const AbstractMatcher &other) const; + + + virtual void writeConfig(KConfig* config) const; + virtual void readConfig(KConfig* config); + + private: + + static Association stringToAssociation(const QString& assocStr); + static QString associationToString(Association association); + + bool anyCriterionMatches( const Article &a ) const; + bool allCriteriaMatch( const Article &a ) const; + + QValueList<Criterion> m_criteria; + Association m_association; +}; + +/** Criterion for ArticleMatcher + * @author Frerich Raabe + */ +class Criterion +{ + public: + + enum Subject { + Title, Description, Author, Link, Status, KeepFlag + }; + + static QString subjectToString(Subject subj); + static Subject stringToSubject(const QString& subjStr); + + enum Predicate { + Contains = 0x01, + Equals = 0x02, + Matches = 0x03, + Negation = 0x80 + }; + + static QString predicateToString(Predicate pred); + static Predicate stringToPredicate(const QString& predStr); + + Criterion(); + Criterion( Subject subject, Predicate predicate, const QVariant &object ); + + bool satisfiedBy( const Article &article ) const; + + virtual void writeConfig(KConfig* config) const; + virtual void readConfig(KConfig* config); + + Subject subject() const; + Predicate predicate() const; + QVariant object() const; + bool operator==(const Criterion& other) const + { return m_subject == other.m_subject && m_predicate == other.m_predicate && m_object == other.m_object; } + + private: + Subject m_subject; + Predicate m_predicate; + QVariant m_object; +}; + +} // namespace Filters +} // namespace Akregator + +#endif diff --git a/akregator/src/articleinterceptor.cpp b/akregator/src/articleinterceptor.cpp new file mode 100644 index 000000000..c184713ad --- /dev/null +++ b/akregator/src/articleinterceptor.cpp @@ -0,0 +1,52 @@ +#include "article.h" +#include "articleinterceptor.h" + +#include <qvaluelist.h> +#include <kstaticdeleter.h> + +namespace Akregator +{ + +class ArticleInterceptorManager::ArticleInterceptorManagerPrivate +{ + public: + QValueList<ArticleInterceptor*> interceptors; +}; + + +ArticleInterceptorManager* ArticleInterceptorManager::m_self = 0; +KStaticDeleter<ArticleInterceptorManager> interceptormanagersd; + +ArticleInterceptorManager* ArticleInterceptorManager::self() +{ + if (!m_self) + interceptormanagersd.setObject(m_self, new ArticleInterceptorManager); + return m_self; +} + +ArticleInterceptorManager::~ArticleInterceptorManager() +{ + delete d; + d = 0; +} + +ArticleInterceptorManager::ArticleInterceptorManager() : d(new ArticleInterceptorManagerPrivate) +{} + +void ArticleInterceptorManager::addInterceptor(ArticleInterceptor* interceptor) +{ + d->interceptors.append(interceptor); +} + +void ArticleInterceptorManager::removeInterceptor(ArticleInterceptor* interceptor) +{ + d->interceptors.remove(interceptor); +} + +QValueList<ArticleInterceptor*> ArticleInterceptorManager::interceptors() const +{ + return d->interceptors; +} + +} + diff --git a/akregator/src/articleinterceptor.h b/akregator/src/articleinterceptor.h new file mode 100644 index 000000000..ed3c550c0 --- /dev/null +++ b/akregator/src/articleinterceptor.h @@ -0,0 +1,46 @@ +#ifndef AKREGATOR_ARTICLEINTERCEPTOR_H +#define AKREGATOR_ARTICLEINTERCEPTOR_H + +#include "akregator_export.h" + +template <class T> class QValueList; + +namespace Akregator { + +class Article; + +/** Interface for intercepting new articles which were just fetched before adding + * them to the archive. E.g. an article filter could implement this interface to + * get fetched articles and label them */ + +class AKREGATOR_EXPORT ArticleInterceptor +{ + public: + /** processes an article. Note that the interceptor may modify the article */ + virtual void processArticle(Article& article) = 0; + +}; + +/** Singleton managing the interceptors */ +class AKREGATOR_EXPORT ArticleInterceptorManager +{ + public: + + static ArticleInterceptorManager* self(); + + ArticleInterceptorManager(); + virtual ~ArticleInterceptorManager(); + + void addInterceptor(ArticleInterceptor* interceptor); + void removeInterceptor(ArticleInterceptor* interceptor); + QValueList<ArticleInterceptor*> interceptors() const; + + private: + static ArticleInterceptorManager* m_self; + class ArticleInterceptorManagerPrivate; + ArticleInterceptorManagerPrivate* d; +}; + +} //namespace Akregator + +#endif // AKREGATOR_ARTICLEINTERCEPTOR_H diff --git a/akregator/src/articlelistview.cpp b/akregator/src/articlelistview.cpp new file mode 100644 index 000000000..210da275b --- /dev/null +++ b/akregator/src/articlelistview.cpp @@ -0,0 +1,812 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "akregatorconfig.h" +#include "actionmanager.h" +#include "articlelistview.h" +#include "article.h" +#include "articlefilter.h" +#include "dragobjects.h" +#include "feed.h" +#include "treenode.h" +#include "treenodevisitor.h" + +#include <kstandarddirs.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kcharsets.h> +#include <kurl.h> + +#include <qdatetime.h> +#include <qpixmap.h> +#include <qpopupmenu.h> +#include <qptrlist.h> +#include <qvaluelist.h> +#include <qwhatsthis.h> +#include <qheader.h> +#include <qdragobject.h> +#include <qsimplerichtext.h> +#include <qpainter.h> +#include <qapplication.h> + +#include <ctime> + +namespace Akregator { + +class ArticleListView::ArticleListViewPrivate +{ + public: + + ArticleListViewPrivate(ArticleListView* parent) : m_parent(parent) { } + + void ensureCurrentItemVisible() + { + if (m_parent->currentItem()) + { + m_parent->center( m_parent->contentsX(), m_parent->itemPos(m_parent->currentItem()), 0, 9.0 ); + } + } + + ArticleListView* m_parent; + + /** maps article to article item */ + QMap<Article, ArticleItem*> articleMap; + TreeNode* node; + Akregator::Filters::ArticleMatcher textFilter; + Akregator::Filters::ArticleMatcher statusFilter; + enum ColumnMode { groupMode, feedMode }; + ColumnMode columnMode; + int feedWidth; + bool noneSelected; + + ColumnLayoutVisitor* columnLayoutVisitor; +}; + +class ArticleListView::ColumnLayoutVisitor : public TreeNodeVisitor +{ + public: + ColumnLayoutVisitor(ArticleListView* view) : m_view(view) {} + + virtual bool visitTagNode(TagNode* /*node*/) + { + if (m_view->d->columnMode == ArticleListViewPrivate::feedMode) + { + m_view->setColumnWidth(1, m_view->d->feedWidth); + m_view->d->columnMode = ArticleListViewPrivate::groupMode; + } + return true; + } + + virtual bool visitFolder(Folder* /*node*/) + { + if (m_view->d->columnMode == ArticleListViewPrivate::feedMode) + { + m_view->setColumnWidth(1, m_view->d->feedWidth); + m_view->d->columnMode = ArticleListViewPrivate::groupMode; + } + return true; + } + + virtual bool visitFeed(Feed* /*node*/) + { + if (m_view->d->columnMode == ArticleListViewPrivate::groupMode) + { + m_view->d->feedWidth = m_view->columnWidth(1); + m_view->hideColumn(1); + m_view->d->columnMode = ArticleListViewPrivate::feedMode; + } + return true; + } + private: + + ArticleListView* m_view; + +}; + +class ArticleListView::ArticleItem : public KListViewItem + { + friend class ArticleListView; + + public: + ArticleItem( QListView *parent, const Article& a); + ~ArticleItem(); + + Article& article(); + + void paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align ); + virtual int compare(QListViewItem *i, int col, bool ascending) const; + + void updateItem(const Article& article); + + virtual ArticleItem* itemAbove() { return static_cast<ArticleItem*>(KListViewItem::itemAbove()); } + + virtual ArticleItem* nextSibling() { return static_cast<ArticleItem*>(KListViewItem::nextSibling()); } + + private: + Article m_article; + time_t m_pubDate; + static QPixmap keepFlag() { + static QPixmap s_keepFlag = QPixmap(locate("data", "akregator/pics/akregator_flag.png")); + return s_keepFlag; + } +}; + +// FIXME: Remove resolveEntities for KDE 4.0, it's now done in the parser +ArticleListView::ArticleItem::ArticleItem( QListView *parent, const Article& a) + : KListViewItem( parent, KCharsets::resolveEntities(a.title()), a.feed()->title(), KGlobal::locale()->formatDateTime(a.pubDate(), true, false) ), m_article(a), m_pubDate(a.pubDate().toTime_t()) +{ + if (a.keep()) + setPixmap(0, keepFlag()); +} + +ArticleListView::ArticleItem::~ArticleItem() +{ +} + +Article& ArticleListView::ArticleItem::article() +{ + return m_article; +} + +// paint ze peons +void ArticleListView::ArticleItem::paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align ) +{ + if (article().status() == Article::Read) + KListViewItem::paintCell( p, cg, column, width, align ); + else + { + // if article status is unread or new, we change the color: FIXME: make colors configurable + QColorGroup cg2(cg); + + if (article().status() == Article::Unread) + cg2.setColor(QColorGroup::Text, Qt::blue); + else // New + cg2.setColor(QColorGroup::Text, Qt::red); + + KListViewItem::paintCell( p, cg2, column, width, align ); + } + +} + +void ArticleListView::ArticleItem::updateItem(const Article& article) +{ + m_article = article; + setPixmap(0, m_article.keep() ? keepFlag() : QPixmap()); + setText(0, KCharsets::resolveEntities(m_article.title())); + setText(1, m_article.feed()->title()); + setText(2, KGlobal::locale()->formatDateTime(m_article.pubDate(), true, false)); +} + +int ArticleListView::ArticleItem::compare(QListViewItem *i, int col, bool ascending) const { + if (col == 2) + { + ArticleItem* item = static_cast<ArticleItem*>(i); + if (m_pubDate == item->m_pubDate) + return 0; + return (m_pubDate > item->m_pubDate) ? 1 : -1; + } + return KListViewItem::compare(i, col, ascending); +} + +/* ==================================================================================== */ + +ArticleListView::ArticleListView(QWidget *parent, const char *name) + : KListView(parent, name) +{ + d = new ArticleListViewPrivate(this); + d->noneSelected = true; + d->node = 0; + d->columnMode = ArticleListViewPrivate::feedMode; + + d->columnLayoutVisitor = new ColumnLayoutVisitor(this); + setMinimumSize(250, 150); + addColumn(i18n("Article")); + addColumn(i18n("Feed")); + addColumn(i18n("Date")); + setSelectionMode(QListView::Extended); + setColumnWidthMode(2, QListView::Maximum); + setColumnWidthMode(1, QListView::Manual); + setColumnWidthMode(0, QListView::Manual); + setRootIsDecorated(false); + setItemsRenameable(false); + setItemsMovable(false); + setAllColumnsShowFocus(true); + setDragEnabled(true); // FIXME before we implement dragging between archived feeds?? + setAcceptDrops(false); // FIXME before we implement dragging between archived feeds?? + setFullWidth(false); + + setShowSortIndicator(true); + setDragAutoScroll(true); + setDropHighlighter(false); + + int c = Settings::sortColumn(); + setSorting((c >= 0 && c <= 2) ? c : 2, Settings::sortAscending()); + + int w; + w = Settings::titleWidth(); + if (w > 0) { + setColumnWidth(0, w); + } + + w = Settings::feedWidth(); + if (w > 0) { + setColumnWidth(1, w); + } + + w = Settings::dateWidth(); + if (w > 0) { + setColumnWidth(2, w); + } + + d->feedWidth = columnWidth(1); + hideColumn(1); + + header()->setStretchEnabled(true, 0); + + QWhatsThis::add(this, i18n("<h2>Article list</h2>" + "Here you can browse articles from the currently selected feed. " + "You can also manage articles, as marking them as persistent (\"Keep Article\") or delete them, using the right mouse button menu." + "To view the web page of the article, you can open the article internally in a tab or in an external browser window.")); + + connect(this, SIGNAL(currentChanged(QListViewItem*)), this, SLOT(slotCurrentChanged(QListViewItem* ))); + connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); + connect(this, SIGNAL(doubleClicked(QListViewItem*, const QPoint&, int)), this, SLOT(slotDoubleClicked(QListViewItem*, const QPoint&, int)) ); + connect(this, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)), + this, SLOT(slotContextMenu(KListView*, QListViewItem*, const QPoint&))); + + connect(this, SIGNAL(mouseButtonPressed(int, QListViewItem *, const QPoint &, int)), this, SLOT(slotMouseButtonPressed(int, QListViewItem *, const QPoint &, int))); +} + +Article ArticleListView::currentArticle() const +{ + ArticleItem* ci = dynamic_cast<ArticleItem*>(KListView::currentItem()); + return (ci && !selectedItems().isEmpty()) ? ci->article() : Article(); +} + +void ArticleListView::slotSetFilter(const Akregator::Filters::ArticleMatcher& textFilter, const Akregator::Filters::ArticleMatcher& statusFilter) +{ + if ( (textFilter != d->textFilter) || (statusFilter != d->statusFilter) ) + { + d->textFilter = textFilter; + d->statusFilter = statusFilter; + + applyFilters(); + } +} + +void ArticleListView::slotShowNode(TreeNode* node) +{ + if (node == d->node) + return; + + slotClear(); + + if (!node) + return; + + d->node = node; + connectToNode(node); + + d->columnLayoutVisitor->visit(node); + + setUpdatesEnabled(false); + + QValueList<Article> articles = d->node->articles(); + + QValueList<Article>::ConstIterator end = articles.end(); + QValueList<Article>::ConstIterator it = articles.begin(); + + for (; it != end; ++it) + { + if (!(*it).isNull() && !(*it).isDeleted()) + { + ArticleItem* ali = new ArticleItem(this, *it); + d->articleMap.insert(*it, ali); + } + } + + sort(); + applyFilters(); + d->noneSelected = true; + setUpdatesEnabled(true); + triggerUpdate(); +} + +void ArticleListView::slotClear() +{ + if (d->node) + disconnectFromNode(d->node); + + d->node = 0; + d->articleMap.clear(); + clear(); +} + +void ArticleListView::slotArticlesAdded(TreeNode* /*node*/, const QValueList<Article>& list) +{ + setUpdatesEnabled(false); + + bool statusActive = !(d->statusFilter.matchesAll()); + bool textActive = !(d->textFilter.matchesAll()); + + for (QValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it) + { + if (!d->articleMap.contains(*it)) + { + if (!(*it).isNull() && !(*it).isDeleted()) + { + ArticleItem* ali = new ArticleItem(this, *it); + ali->setVisible( (!statusActive || d->statusFilter.matches( ali->article())) + && (!textActive || d->textFilter.matches( ali->article())) ); + d->articleMap.insert(*it, ali); + } + } + } + setUpdatesEnabled(true); + triggerUpdate(); +} + +void ArticleListView::slotArticlesUpdated(TreeNode* /*node*/, const QValueList<Article>& list) +{ + setUpdatesEnabled(false); + + // if only one item is selected and this selected item + // is deleted, we will select the next item in the list + bool singleSelected = selectedArticles().count() == 1; + + bool statusActive = !(d->statusFilter.matchesAll()); + bool textActive = !(d->textFilter.matchesAll()); + + QListViewItem* next = 0; // the item to select if a selected item is deleted + + for (QValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it) + { + + if (!(*it).isNull() && d->articleMap.contains(*it)) + { + ArticleItem* ali = d->articleMap[*it]; + + if (ali) + { + if ((*it).isDeleted()) // if article was set to deleted, delete item + { + if (singleSelected && ali->isSelected()) + { + if (ali->itemBelow()) + next = ali->itemBelow(); + else if (ali->itemAbove()) + next = ali->itemAbove(); + } + + d->articleMap.remove(*it); + delete ali; + } + else + { + ali->updateItem(*it); + // if the updated article matches the filters after the update, + // make visible. If it matched them before but not after update, + // they should stay visible (to not confuse users) + if ((!statusActive || d->statusFilter.matches(ali->article())) + && (!textActive || d->textFilter.matches( ali->article())) ) + ali->setVisible(true); + } + } // if ali + } + } + + // if the only selected item was deleted, select + // an item next to it + if (singleSelected && next != 0) + { + setSelected(next, true); + setCurrentItem(next); + } + else + { + d->noneSelected = true; + } + + + setUpdatesEnabled(true); + triggerUpdate(); +} + +void ArticleListView::slotArticlesRemoved(TreeNode* /*node*/, const QValueList<Article>& list) +{ + // if only one item is selected and this selected item + // is deleted, we will select the next item in the list + bool singleSelected = selectedArticles().count() == 1; + + QListViewItem* next = 0; // the item to select if a selected item is deleted + + setUpdatesEnabled(false); + + for (QValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it) + { + if (d->articleMap.contains(*it)) + { + ArticleItem* ali = d->articleMap[*it]; + d->articleMap.remove(*it); + + if (singleSelected && ali->isSelected()) + { + if (ali->itemBelow()) + next = ali->itemBelow(); + else if (ali->itemAbove()) + next = ali->itemAbove(); + } + + delete ali; + } + } + + // if the only selected item was deleted, select + // an item next to it + if (singleSelected && next != 0) + { + setSelected(next, true); + setCurrentItem(next); + } + else + { + d->noneSelected = true; + } + + setUpdatesEnabled(true); + triggerUpdate(); +} + +void ArticleListView::connectToNode(TreeNode* node) +{ + connect(node, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotClear()) ); + connect(node, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesAdded(TreeNode*, const QValueList<Article>&)) ); + connect(node, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesUpdated(TreeNode*, const QValueList<Article>&)) ); + connect(node, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesRemoved(TreeNode*, const QValueList<Article>&)) ); +} + +void ArticleListView::disconnectFromNode(TreeNode* node) +{ + disconnect(node, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotClear()) ); + disconnect(node, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesAdded(TreeNode*, const QValueList<Article>&)) ); + disconnect(node, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesUpdated(TreeNode*, const QValueList<Article>&)) ); + disconnect(node, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesRemoved(TreeNode*, const QValueList<Article>&)) ); +} + +void ArticleListView::applyFilters() +{ + bool statusActive = !(d->statusFilter.matchesAll()); + bool textActive = !(d->textFilter.matchesAll()); + + ArticleItem* ali = 0; + + if (!statusActive && !textActive) + { + for (QListViewItemIterator it(this); it.current(); ++it) + { + (static_cast<ArticleItem*> (it.current()))->setVisible(true); + } + } + else if (statusActive && !textActive) + { + for (QListViewItemIterator it(this); it.current(); ++it) + { + ali = static_cast<ArticleItem*> (it.current()); + ali->setVisible( d->statusFilter.matches( ali->article()) ); + } + } + else if (!statusActive && textActive) + { + for (QListViewItemIterator it(this); it.current(); ++it) + { + ali = static_cast<ArticleItem*> (it.current()); + ali->setVisible( d->textFilter.matches( ali->article()) ); + } + } + else // both true + { + for (QListViewItemIterator it(this); it.current(); ++it) + { + ali = static_cast<ArticleItem*> (it.current()); + ali->setVisible( d->statusFilter.matches( ali->article()) + && d->textFilter.matches( ali->article()) ); + } + } + +} + +int ArticleListView::visibleArticles() +{ + int visible = 0; + ArticleItem* ali = 0; + for (QListViewItemIterator it(this); it.current(); ++it) { + ali = static_cast<ArticleItem*> (it.current()); + visible += ali->isVisible() ? 1 : 0; + } + return visible; +} + +// from amarok :) +void ArticleListView::paintInfoBox(const QString &message) +{ + QPainter p( viewport() ); + QSimpleRichText t( message, QApplication::font() ); + + if ( t.width()+30 >= viewport()->width() || t.height()+30 >= viewport()->height() ) + //too big, giving up + return; + + const uint w = t.width(); + const uint h = t.height(); + const uint x = (viewport()->width() - w - 30) / 2 ; + const uint y = (viewport()->height() - h - 30) / 2 ; + + p.setBrush( colorGroup().background() ); + p.drawRoundRect( x, y, w+30, h+30, (8*200)/w, (8*200)/h ); + t.draw( &p, x+15, y+15, QRect(), colorGroup() ); +} + +void ArticleListView::viewportPaintEvent(QPaintEvent *e) +{ + + KListView::viewportPaintEvent(e); + + if(!e) + return; + + QString message = QString::null; + + //kdDebug() << "visible articles: " << visibleArticles() << endl; + + if(childCount() != 0) // article list is not empty + { + if (visibleArticles() == 0) + { + message = i18n("<div align=center>" + "<h3>No matches</h3>" + "Filter does not match any articles, " + "please change your criteria and try again." + "</div>"); + } + + } + else // article list is empty + { + if (!d->node) // no node selected + { + message = i18n("<div align=center>" + "<h3>No feed selected</h3>" + "This area is article list. " + "Select a feed from the feed list " + "and you will see its articles here." + "</div>"); + } + else // empty node + { + // TODO: we could display message like "empty node, choose "fetch" to update it" + } + } + + if (!message.isNull()) + paintInfoBox(message); +} + +QDragObject *ArticleListView::dragObject() +{ + QDragObject* d = 0; + QValueList<Article> articles = selectedArticles(); + if (!articles.isEmpty()) + { + d = new ArticleDrag(articles, this); + } + return d; +} + +void ArticleListView::slotPreviousArticle() +{ + ArticleItem* ali = 0; + if (!currentItem() || selectedItems().isEmpty()) + ali = dynamic_cast<ArticleItem*>(lastChild()); + else + ali = dynamic_cast<ArticleItem*>(currentItem()->itemAbove()); + + if (ali) + { + Article a = ali->article(); + setCurrentItem(d->articleMap[a]); + clearSelection(); + setSelected(d->articleMap[a], true); + d->ensureCurrentItemVisible(); + } +} + +void ArticleListView::slotNextArticle() +{ + ArticleItem* ali = 0; + if (!currentItem() || selectedItems().isEmpty()) + ali = dynamic_cast<ArticleItem*>(firstChild()); + else + ali = dynamic_cast<ArticleItem*>(currentItem()->itemBelow()); + + if (ali) + { + Article a = ali->article(); + setCurrentItem(d->articleMap[a]); + clearSelection(); + setSelected(d->articleMap[a], true); + d->ensureCurrentItemVisible(); + } +} + +void ArticleListView::slotNextUnreadArticle() +{ + ArticleItem* start = 0L; + if (!currentItem() || selectedItems().isEmpty()) + start = dynamic_cast<ArticleItem*>(firstChild()); + else + start = dynamic_cast<ArticleItem*>(currentItem()->itemBelow() ? currentItem()->itemBelow() : firstChild()); + + ArticleItem* i = start; + ArticleItem* unread = 0L; + + do + { + if (i == 0L) + i = static_cast<ArticleItem*>(firstChild()); + else + { + if (i->article().status() != Article::Read) + unread = i; + else + i = static_cast<ArticleItem*>(i && i->itemBelow() ? i->itemBelow() : firstChild()); + } + } + while (!unread && i != start); + + if (unread) + { + Article a = unread->article(); + setCurrentItem(d->articleMap[a]); + clearSelection(); + setSelected(d->articleMap[a], true); + d->ensureCurrentItemVisible(); + } +} + +void ArticleListView::slotPreviousUnreadArticle() +{ + ArticleItem* start = 0L; + if (!currentItem() || selectedItems().isEmpty()) + start = dynamic_cast<ArticleItem*>(lastChild()); + else + start = dynamic_cast<ArticleItem*>(currentItem()->itemAbove() ? currentItem()->itemAbove() : firstChild()); + + ArticleItem* i = start; + ArticleItem* unread = 0L; + + do + { + if (i == 0L) + i = static_cast<ArticleItem*>(lastChild()); + else + { + if (i->article().status() != Article::Read) + unread = i; + else + i = static_cast<ArticleItem*>(i->itemAbove() ? i->itemAbove() : lastChild()); + } + } + while ( !(unread != 0L || i == start) ); + + if (unread) + { + Article a = unread->article(); + setCurrentItem(d->articleMap[a]); + clearSelection(); + setSelected(d->articleMap[a], true); + d->ensureCurrentItemVisible(); + } +} + +void ArticleListView::keyPressEvent(QKeyEvent* e) +{ + e->ignore(); +} + +void ArticleListView::slotSelectionChanged() +{ + // if there is only one article in the list, currentItem is set initially to + // that article item, although the user hasn't selected it. If the user selects + // the article, selection changes, but currentItem does not. + // executed. So we have to handle this case by observing selection changes. + + if (d->noneSelected) + { + d->noneSelected = false; + slotCurrentChanged(currentItem()); + } +} + +void ArticleListView::slotCurrentChanged(QListViewItem* item) +{ + ArticleItem* ai = dynamic_cast<ArticleItem*> (item); + if (ai) + emit signalArticleChosen( ai->article() ); + else + { + d->noneSelected = true; + emit signalArticleChosen( Article() ); + } +} + + +void ArticleListView::slotDoubleClicked(QListViewItem* item, const QPoint& p, int i) +{ + ArticleItem* ali = dynamic_cast<ArticleItem*>(item); + if (ali) + emit signalDoubleClicked(ali->article(), p, i); +} + +void ArticleListView::slotContextMenu(KListView* /*list*/, QListViewItem* /*item*/, const QPoint& p) +{ + QWidget* w = ActionManager::getInstance()->container("article_popup"); + QPopupMenu* popup = static_cast<QPopupMenu *>(w); + if (popup) + popup->exec(p); +} + +void ArticleListView::slotMouseButtonPressed(int button, QListViewItem* item, const QPoint& p, int column) +{ + ArticleItem* ali = dynamic_cast<ArticleItem*>(item); + if (ali) + emit signalMouseButtonPressed(button, ali->article(), p, column); +} + +ArticleListView::~ArticleListView() +{ + Settings::setTitleWidth(columnWidth(0)); + Settings::setFeedWidth(columnWidth(1) > 0 ? columnWidth(1) : d->feedWidth); + Settings::setSortColumn(sortColumn()); + Settings::setSortAscending(sortOrder() == Ascending); + Settings::writeConfig(); + delete d->columnLayoutVisitor; + delete d; + d = 0; +} + +QValueList<Article> ArticleListView::selectedArticles() const +{ + QValueList<Article> ret; + QPtrList<QListViewItem> items = selectedItems(false); + for (QListViewItem* i = items.first(); i; i = items.next() ) + ret.append((static_cast<ArticleItem*>(i))->article()); + return ret; +} + +} // namespace Akregator + +#include "articlelistview.moc" +// vim: ts=4 sw=4 et diff --git a/akregator/src/articlelistview.h b/akregator/src/articlelistview.h new file mode 100644 index 000000000..7b8ec08e9 --- /dev/null +++ b/akregator/src/articlelistview.h @@ -0,0 +1,137 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef AKREGATORARTICLELISTVIEW_H +#define AKREGATORARTICLELISTVIEW_H + +#include <klistview.h> + +class QKeyEvent; +class QDragObject; +template <class T> class QValueList; + +namespace Akregator +{ + class Article; + class TreeNode; + + namespace Filters + { + class ArticleMatcher; + } + + class ArticleListView : public KListView + { + Q_OBJECT + public: + ArticleListView(QWidget *parent = 0, const char *name = 0); + virtual ~ArticleListView(); + + /** returns the current article, or a null article if there is none */ + Article currentArticle() const; + + /** returns a list of currently selected articles */ + QValueList<Article> selectedArticles() const; + + enum Columns { itemTitle, feedTitle, pubDate }; + + public slots: + + /** show article list of tree node @c node (also connects to the notification signals of the node) */ + void slotShowNode(TreeNode* node); + + /** clears the list and disconnects from the observed node (if any) */ + void slotClear(); + + /** sets text filter and status filter + @param textFilter filters text + @param statusFilter filters status (read, unread, new) */ + void slotSetFilter(const Akregator::Filters::ArticleMatcher& textFilter, const Akregator::Filters::ArticleMatcher& statusFilter); + + /** selects previous article in list view, first article if no article was selected */ + void slotPreviousArticle(); + + /** selects next article in list view, first article if no article was selected */ + void slotNextArticle(); + + /** selects previous unread article in list view, first unread article if no article was selected */ + void slotPreviousUnreadArticle(); + + /** selects next unread article in list view, first unread article if no article was selected */ + void slotNextUnreadArticle(); + + signals: + void signalArticleChosen(const Article& article); + void signalDoubleClicked(const Article&, const QPoint&, int); + //void signalContextMenu(KListView*, ArticleItem*, const QPoint&); + void signalMouseButtonPressed(int, const Article&, const QPoint &, int); + + protected: + /** reimplemented for kmail-like behaviour */ + virtual void keyPressEvent(QKeyEvent* e); + + /** applies text filter and status filter by setting visibility + of items accordingly */ + virtual void applyFilters(); + + /** + * @return count of visible articles, used for info boxes + */ + int visibleArticles(); + + /** Paints infobox for filtering and stuff + */ + void paintInfoBox(const QString &message); + + virtual void viewportPaintEvent(QPaintEvent *e); + + void connectToNode(TreeNode* node); + void disconnectFromNode(TreeNode* node); + + virtual QDragObject *dragObject(); + + protected slots: + + void slotArticlesAdded(TreeNode* node, const QValueList<Article>& list); + void slotArticlesUpdated(TreeNode* node, const QValueList<Article>& list); + void slotArticlesRemoved(TreeNode* node, const QValueList<Article>& list); + + virtual void slotCurrentChanged(QListViewItem* item); + virtual void slotSelectionChanged(); + virtual void slotDoubleClicked(QListViewItem* item, const QPoint& p, int i); + virtual void slotContextMenu(KListView* list, QListViewItem* item, const QPoint& p); + virtual void slotMouseButtonPressed(int, QListViewItem *, const QPoint &, int); + + public: // compat with KDE-3.x assertions, remove for KDE 4 + // private: + class ArticleListViewPrivate; + ArticleListViewPrivate* d; + + friend class ColumnLayoutVisitor; + class ColumnLayoutVisitor; + + class ArticleItem; + }; +} + +#endif diff --git a/akregator/src/articleviewer.cpp b/akregator/src/articleviewer.cpp new file mode 100644 index 000000000..942ce623e --- /dev/null +++ b/akregator/src/articleviewer.cpp @@ -0,0 +1,797 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qdatetime.h> +#include <qevent.h> +#include <qscrollview.h> +#include <qvaluelist.h> + +#include <kaction.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kglobalsettings.h> +#include <khtmlview.h> +#include <klocale.h> +#include <kprocess.h> +#include <krun.h> +#include <kstandarddirs.h> +#include <kshell.h> +#include <kmessagebox.h> +#include <kio/netaccess.h> +#include <libkdepim/kfileio.h> + +#include "aboutdata.h" +#include "akregator_run.h" +#include "akregatorconfig.h" +#include "articleviewer.h" +#include "feed.h" +#include "folder.h" +#include "article.h" +#include "treenode.h" +#include "treenodevisitor.h" +#include "tagnode.h" +#include "utils.h" + +namespace Akregator { + +// from kmail::headerstyle.cpp +static inline QString directionOf(const QString &str) +{ + return str.isRightToLeft() ? "rtl" : "ltr" ; +} + +class ArticleViewer::ShowSummaryVisitor : public TreeNodeVisitor +{ + public: + + ShowSummaryVisitor(ArticleViewer* view) : m_view(view) {} + + virtual bool visitFeed(Feed* node) + { + m_view->m_link = QString(); + + QString text; + text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr"); + + text += QString("<div class=\"headertitle\" dir=\"%1\">").arg(directionOf(Utils::stripTags(node->title()))); + text += node->title(); + if(node->unread() == 0) + text += i18n(" (no unread articles)"); + else + text += i18n(" (1 unread article)", " (%n unread articles)", node->unread()); + text += "</div>\n"; // headertitle + text += "</div>\n"; // /headerbox + + if (!node->image().isNull()) // image + { + text += QString("<div class=\"body\">"); + QString url=node->xmlUrl(); + QString file = url.replace("/", "_").replace(":", "_"); + KURL u(m_view->m_imageDir); + u.setFileName(file); + text += QString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(node->htmlUrl()).arg(u.url()); + } + else text += "<div class=\"body\">"; + + + if( !node->description().isEmpty() ) + { + text += QString("<div dir=\"%1\">").arg(Utils::stripTags(directionOf(node->description()))); + text += i18n("<b>Description:</b> %1<br><br>").arg(node->description()); + text += "</div>\n"; // /description + } + + if ( !node->htmlUrl().isEmpty() ) + { + text += QString("<div dir=\"%1\">").arg(directionOf(node->htmlUrl())); + text += i18n("<b>Homepage:</b> <a href=\"%1\">%2</a>").arg(node->htmlUrl()).arg(node->htmlUrl()); + text += "</div>\n"; // / link + } + + //text += i18n("<b>Unread articles:</b> %1").arg(node->unread()); + text += "</div>"; // /body + + m_view->renderContent(text); + return true; + } + + virtual bool visitFolder(Folder* node) + { + m_view->m_link = QString(); + + QString text; + text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr"); + text += QString("<div class=\"headertitle\" dir=\"%1\">%2").arg(directionOf(Utils::stripTags(node->title()))).arg(node->title()); + if(node->unread() == 0) + text += i18n(" (no unread articles)"); + else + text += i18n(" (1 unread article)", " (%n unread articles)", node->unread()); + text += QString("</div>\n"); + text += "</div>\n"; // /headerbox + + m_view->renderContent(text); + return true; + } + + virtual bool visitTagNode(TagNode* node) + { + m_view->m_link = QString(); + + QString text; + text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr"); + text += QString("<div class=\"headertitle\" dir=\"%1\">%2").arg(directionOf(Utils::stripTags(node->title()))).arg(node->title()); + if(node->unread() == 0) + text += i18n(" (no unread articles)"); + else + text += i18n(" (1 unread article)", " (%n unread articles)", node->unread()); + text += QString("</div>\n"); + text += "</div>\n"; // /headerbox + + m_view->renderContent(text); + return true; + } + + private: + + ArticleViewer* m_view; +}; + +ArticleViewer::ArticleViewer(QWidget *parent, const char *name) + : Viewer(parent, name), m_htmlFooter(), m_currentText(), m_node(0), m_viewMode(NormalView) +{ + setJScriptEnabled(false); + setJavaEnabled(false); + setPluginsEnabled(false); + + m_showSummaryVisitor = new ShowSummaryVisitor(this); + setXMLFile(locate("data", "akregator/articleviewer.rc"), true); + + generateNormalModeCSS(); + generateCombinedModeCSS(); + new KAction( i18n("&Scroll Up"), QString::null, "Up", this, SLOT(slotScrollUp()), actionCollection(), "articleviewer_scroll_up" ); + new KAction( i18n("&Scroll Down"), QString::null, "Down", this, SLOT(slotScrollDown()), actionCollection(), "articleviewer_scroll_down" ); + + connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); + + connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(slotPaletteOrFontChanged()) ); + connect(kapp, SIGNAL(kdisplayFontChanged()), this, SLOT(slotPaletteOrFontChanged()) ); + + m_imageDir.setPath(KGlobal::dirs()->saveLocation("cache", "akregator/Media/")); + m_htmlFooter = "</body></html>"; +} + +ArticleViewer::~ArticleViewer() +{ + delete m_showSummaryVisitor; +} + +void ArticleViewer::generateNormalModeCSS() +{ + const QColorGroup & cg = QApplication::palette().active(); + + // from kmail::headerstyle.cpp + m_normalModeCSS = QString( + "@media screen, print {" + "body {\n" + " font-family: \"%1\" ! important;\n" + " font-size: %2 ! important;\n" + " color: %3 ! important;\n" + " background: %4 ! important;\n" + "}\n\n").arg(Settings::standardFont()) + .arg(QString::number(pointsToPixel(Settings::mediumFontSize()))+"px") + .arg(cg.text().name()) + .arg(cg.base().name()); + m_normalModeCSS += QString( + "a {\n" + + QString(" color: %1 ! important;\n") + + QString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "") + + "}\n\n" + +".headerbox {\n" + +" background: %2 ! important;\n" + +" color: %3 ! important;\n" + +" border:1px solid #000;\n" + +" margin-bottom: 10pt;\n" +// +" width: 99%;\n" + + "}\n\n") + .arg(cg.link().name()) + .arg(cg.background().name()) + .arg(cg.text().name()); + + m_normalModeCSS += QString(".headertitle a:link { color: %1 ! important; }\n" + ".headertitle a:visited { color: %2 ! important; }\n" + ".headertitle a:hover{ color: %3 ! important; }\n" + ".headertitle a:active { color: %4 ! important; }\n") + .arg(cg.highlightedText().name()) + .arg(cg.highlightedText().name()) + .arg(cg.highlightedText().name()) + .arg(cg.highlightedText().name()); + + m_normalModeCSS += QString( + ".headertitle {\n" + " background: %1 ! important;\n" + " padding:2px;\n" + " color: %2 ! important;\n" + " font-weight: bold;\n" + "}\n\n" + ".header {\n" + " font-weight: bold;\n" + " padding:2px;\n" + " margin-right: 5px;\n" + "}\n\n" + ".headertext {\n" + "}\n\n" + ".headimage {\n" + " float: right;\n" + " margin-left: 5px;\n" + "}\n\n").arg(cg.highlight().name()) + .arg(cg.highlightedText().name()); + + m_normalModeCSS += QString( + "body { clear: none; }\n\n" + ".content {\n" + " display: block;\n" + " margin-bottom: 6px;\n" + "}\n\n" + // these rules make sure that there is no leading space between the header and the first of the text + ".content > P:first-child {\n margin-top: 1px; }\n" + ".content > DIV:first-child {\n margin-top: 1px; }\n" + ".content > BR:first-child {\n display: none; }\n" + "iframe {display: none !important; }\n" + "frame {display: none !important; }\n" + "frameset {display: none !important; }\n" + "object {display: none !important; }\n" + "applet {display: none !important; }\n" + "}\n\n"); // @media screen, print +} + +void ArticleViewer::generateCombinedModeCSS() +{ + const QColorGroup & cg = QApplication::palette().active(); + + // from kmail::headerstyle.cpp + m_combinedModeCSS = QString ( +// "<style type=\"text/css\">\n" + "@media screen, print {" + "body {\n" + " font-family: \"%1\" ! important;\n" + " font-size: %2 ! important;\n" + " color: %3 ! important;\n" + " background: %4 ! important;\n" + "}\n\n").arg(Settings::standardFont()) + .arg(QString::number(pointsToPixel(Settings::mediumFontSize()))+"px") + .arg(cg.text().name()) + .arg(cg.base().name()); + m_combinedModeCSS += ( + "a {\n" + + QString(" color: %1 ! important;\n") + + QString(!Settings::underlineLinks() ? " text-decoration: none ! important;\n" : "") + + "}\n\n" + +".headerbox {\n" + +" background: %2 ! important;\n" + +" color: %3 ! important;\n" + +" border:1px solid #000;\n" + +" margin-bottom: 10pt;\n" +// +" width: 99%;\n" + + "}\n\n") + .arg(cg.link().name()) + .arg(cg.background().name()) + .arg(cg.text().name()); + + m_combinedModeCSS += QString(".headertitle a:link { color: %1 ! important; }\n" + ".headertitle a:visited { color: %2 ! important; }\n" + ".headertitle a:hover{ color: %3 ! important; }\n" + ".headertitle a:active { color: %4 ! important; }\n") + .arg(cg.highlightedText().name()) + .arg(cg.highlightedText().name()) + .arg(cg.highlightedText().name()) + .arg(cg.highlightedText().name()); + m_combinedModeCSS += QString( + ".headertitle {\n" + " background: %1 ! important;\n" + " padding:2px;\n" + " color: %2 ! important;\n" + " font-weight: bold;\n" + "}\n\n" + ".header {\n" + " font-weight: bold;\n" + " padding:2px;\n" + " margin-right: 5px;\n" + "}\n\n" + ".headertext {\n" + "}\n\n" + ".headimage {\n" + " float: right;\n" + " margin-left: 5px;\n" + "}\n\n").arg(cg.highlight().name()) + .arg(cg.highlightedText().name()); + + m_combinedModeCSS += QString( + "body { clear: none; }\n\n" + ".content {\n" + " display: block;\n" + " margin-bottom: 6px;\n" + "}\n\n" + // these rules make sure that there is no leading space between the header and the first of the text + ".content > P:first-child {\n margin-top: 1px; }\n" + ".content > DIV:first-child {\n margin-top: 1px; }\n" + ".content > BR:first-child {\n display: none; }\n" + "iframe {display: none !important; }\n" + "frame {display: none !important; }\n" + "frameset {display: none !important; }\n" + "object {display: none !important; }\n" + "applet {display: none !important; }\n" + "}\n\n"); // @media screen, print +} + +void ArticleViewer::reload() +{ + beginWriting(); + write(m_currentText); + endWriting(); +} + +bool ArticleViewer::openURL(const KURL& url) +{ + if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite()) + { + return Viewer::openURL(url); + } + else + { + reload(); + return true; + } +} + +void ArticleViewer::displayAboutPage() +{ + QString location = locate("data", "akregator/about/main.html"); + QString content = KPIM::kFileToString(location); + content = content.arg( locate( "data", "libkdepim/about/kde_infopage.css" ) ); + if ( kapp->reverseLayout() ) + content = content.arg( "@import \"%1\";" ).arg( locate( "data", "libkdepim/about/kde_infopage_rtl.css" ) ); + else + content = content.arg( "" ); + + begin(KURL( location )); + QString info = + i18n("%1: Akregator version; %2: help:// URL; %3: homepage URL; " + "--- end of comment ---", + "<h2 style='margin-top: 0px;'>Welcome to Akregator %1</h2>" + "<p>Akregator is an RSS feed aggregator for the K Desktop Environment. " + "Feed aggregators provide a convenient way to browse different kinds of " + "content, including news, blogs, and other content from online sites. " + "Instead of checking all your favorite web sites manually for updates, " + "Akregator collects the content for you.</p>" + "<p>For more information about using Akregator, check the " + "<a href=\"%3\">Akregator website</a>. If you do not want to see this page anymore, <a href=\"config:/disable_introduction\">click here</a>.</p>" + "<p>We hope that you will enjoy Akregator.</p>\n" + "<p>Thank you,</p>\n" + "<p style='margin-bottom: 0px'> The Akregator Team</p>\n") + .arg(AKREGATOR_VERSION) // Akregator version + .arg("http://akregator.kde.org/"); // Akregator homepage URL + + QString fontSize = QString::number( pointsToPixel( Settings::mediumFontSize() )); + QString appTitle = i18n("Akregator"); + QString catchPhrase = ""; //not enough space for a catch phrase at default window size i18n("Part of the Kontact Suite"); + QString quickDescription = i18n("An RSS feed reader for the K Desktop Environment."); + write(content.arg(fontSize).arg(appTitle).arg(catchPhrase).arg(quickDescription).arg(info)); + end(); +} + +QString ArticleViewer::formatArticleNormalMode(Feed* feed, const Article& article) +{ + QString text; + text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr"); + + if (!article.title().isEmpty()) + { + text += QString("<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title()))); + if (article.link().isValid()) + text += "<a href=\""+article.link().url()+"\">"; + text += article.title().replace("<", "<").replace(">", ">"); // TODO: better leave things escaped in the parser + if (article.link().isValid()) + text += "</a>"; + text += "</div>\n"; + } + if (article.pubDate().isValid()) + { + text += QString("<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Date"))); + text += QString ("%1:").arg(i18n("Date")); + text += "</span><span class=\"headertext\">"; + text += KGlobal::locale()->formatDateTime(article.pubDate(), false, false)+"</span>\n"; // TODO: might need RTL? + } + QString author = article.author(); + if (!author.isEmpty()) + { + text += QString("<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Author"))); + text += QString ("%1:").arg(i18n("Author")); + text += "</span><span class=\"headertext\">"; + text += author+"</span>\n"; // TODO: might need RTL? + } + text += "</div>\n"; // end headerbox + + if (feed && !feed->image().isNull()) + { + QString file = Utils::fileNameForUrl(feed->xmlUrl()); + KURL u(m_imageDir); + u.setFileName(file); + text += QString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url()); + } + + + + if (!article.description().isEmpty()) + { + text += QString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) ); + text += "<span class=\"content\">"+article.description()+"</span>"; + text += "</div>"; + } + + text += "<div class=\"body\">"; + + if (article.commentsLink().isValid()) + { + text += "<a class=\"contentlink\" href=\""; + text += article.commentsLink().url(); + text += "\">" + i18n( "Comments"); + if (article.comments()) + { + text += " ("+ QString::number(article.comments()) +")"; + } + text += "</a>"; + } + + if (article.link().isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid())) + { + text += "<p><a class=\"contentlink\" href=\""; + // in case link isn't valid, fall back to the guid permaLink. + if (article.link().isValid()) + { + text += article.link().url(); + } + else + { + text += article.guid(); + } + text += "\">" + i18n( "Complete Story" ) + "</a></p>"; + } + text += "</div>"; + + if (!article.enclosure().isNull()) + { + //QString url = article.enclosure().url(); + //QString type = article.enclosure().type(); + //int length = article.enclosure().length(); + //QString lengthStr = KIO::convertSize(length); + + //text += QString("<hr><div><a href=\"%1\">%2</a> (%3, %4)</div>").arg(url).arg(url).arg(lengthStr).arg(type); + } + //kdDebug() << text << endl; + return text; + +} + +QString ArticleViewer::formatArticleCombinedMode(Feed* feed, const Article& article) +{ + QString text; + text = QString("<div class=\"headerbox\" dir=\"%1\">\n").arg(QApplication::reverseLayout() ? "rtl" : "ltr"); + + KURL link = article.link(); + + if (!article.title().isEmpty()) + { + text += QString("<div class=\"headertitle\" dir=\"%1\">\n").arg(directionOf(Utils::stripTags(article.title()))); + if (link.isValid()) + text += "<a href=\""+link.url()+"\">"; + text += article.title().replace("<", "<").replace(">", ">"); // TODO: better leave things escaped in the parser + if (link.isValid()) + text += "</a>"; + text += "</div>\n"; + } + if (article.pubDate().isValid()) + { + text += QString("<span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Date"))); + text += QString ("%1:").arg(i18n("Date")); + text += "</span><span class=\"headertext\">"; + text += KGlobal::locale()->formatDateTime(article.pubDate(), false, false)+"</span>\n"; // TODO: might need RTL? + } + + QString author = article.author(); + if (!author.isEmpty()) + { + text += QString("<br/><span class=\"header\" dir=\"%1\">").arg(directionOf(i18n("Author"))); + text += QString ("%1:").arg(i18n("Author")); + text += "</span><span class=\"headertext\">"; + text += author+"</span>\n"; // TODO: might need RTL? + } + + text += "</div>\n"; // end headerbox + + if (feed && !feed->image().isNull()) + { + QString file = Utils::fileNameForUrl(feed->xmlUrl()); + KURL u(m_imageDir); + u.setFileName(file); + text += QString("<a href=\"%1\"><img class=\"headimage\" src=\"%2.png\"></a>\n").arg(feed->htmlUrl()).arg(u.url()); + } + + + + if (!article.description().isEmpty()) + { + text += QString("<div dir=\"%1\">").arg(directionOf(Utils::stripTags(article.description())) ); + text += "<span class=\"content\">"+article.description()+"</span>"; + text += "</div>"; + } + + text += "<div class=\"body\">"; + + if (article.commentsLink().isValid()) + { + text += "<a class=\"contentlink\" href=\""; + text += article.commentsLink().url(); + text += "\">" + i18n( "Comments"); + if (article.comments()) + { + text += " ("+ QString::number(article.comments()) +")"; + } + text += "</a>"; + } + + if (link.isValid() || (article.guidIsPermaLink() && KURL(article.guid()).isValid())) + { + text += "<p><a class=\"contentlink\" href=\""; + // in case link isn't valid, fall back to the guid permaLink. + if (link.isValid()) + { + text += link.url(); + } + else + { + text += article.guid(); + } + text += "\">" + i18n( "Complete Story" ) + "</a></p>"; + } + text += "</div>"; + //kdDebug() << text << endl; + return text; + +} + +void ArticleViewer::renderContent(const QString& text) +{ + closeURL(); + m_currentText = text; + beginWriting(); + //kdDebug() << text << endl; + write(text); + endWriting(); +} + +void ArticleViewer::beginWriting() +{ + QString head = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n <html><head><title>.</title></head>"); + view()->setContentsPos(0,0); + begin(m_link); + setUserStyleSheet(m_viewMode == CombinedView ? m_combinedModeCSS : m_normalModeCSS); + write(head); +} + +void ArticleViewer::endWriting() +{ + write(m_htmlFooter); + //kdDebug() << m_htmlFooter << endl; + end(); +} + +void ArticleViewer::slotShowSummary(TreeNode* node) +{ + m_viewMode = SummaryView; + + if (!node) + { + slotClear(); + return; + } + + if (node != m_node) + { + disconnectFromNode(m_node); + connectToNode(node); + m_node = node; + } + m_showSummaryVisitor->visit(node); +} + + +void ArticleViewer::slotShowArticle(const Article& article) +{ + m_viewMode = NormalView; + disconnectFromNode(m_node); + m_article = article; + m_node = 0; + m_link = article.link(); + if (article.feed()->loadLinkedWebsite()) + openURL(article.link()); + else + renderContent( formatArticleNormalMode(article.feed(), article) ); +} + +void ArticleViewer::slotSetFilter(const Akregator::Filters::ArticleMatcher& textFilter, const Akregator::Filters::ArticleMatcher& statusFilter) +{ + if (m_statusFilter == statusFilter && m_textFilter == textFilter) + return; + + m_textFilter = textFilter; + m_statusFilter = statusFilter; + + slotUpdateCombinedView(); +} + +void ArticleViewer::slotUpdateCombinedView() +{ + if (m_viewMode != CombinedView) + return; + + if (!m_node) + return slotClear(); + + QValueList<Article> articles = m_node->articles(); + qHeapSort(articles); + QValueList<Article>::ConstIterator end = articles.end(); + QValueList<Article>::ConstIterator it = articles.begin(); + + QString text; + + int num = 0; + QTime spent; + spent.start(); + + for ( ; it != end; ++it) + { + if ( !(*it).isDeleted() && m_textFilter.matches(*it) && m_statusFilter.matches(*it) ) + { + text += "<p><div class=\"article\">"+formatArticleCombinedMode(0, *it)+"</div><p>"; + ++num; + } + } + //kdDebug() << "Combined view rendering: (" << num << " articles):\n" << "generating HTML: " << spent.elapsed() << "ms " << endl; + renderContent(text); + //kdDebug() << "HTML rendering: " << spent.elapsed() << "ms" << endl; + + +} + +void ArticleViewer::slotArticlesUpdated(TreeNode* /*node*/, const QValueList<Article>& /*list*/) +{ + if (m_viewMode == CombinedView) + slotUpdateCombinedView(); +} + +void ArticleViewer::slotArticlesAdded(TreeNode* /*node*/, const QValueList<Article>& /*list*/) +{ +} + +void ArticleViewer::slotArticlesRemoved(TreeNode* /*node*/, const QValueList<Article>& /*list*/) +{ +} + +/* testingtesting :) +void ArticleViewer::slotPopupMenu(KXMLGUIClient*, const QPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t) +{ + kdDebug() << m_link << endl; + kdDebug() << kurl.url() << endl; +}*/ + + +void ArticleViewer::slotClear() +{ + disconnectFromNode(m_node); + m_node = 0; + m_article = Article(); + + renderContent(QString()); +} + +void ArticleViewer::slotShowNode(TreeNode* node) +{ + m_viewMode = CombinedView; + + if (node != m_node) + disconnectFromNode(m_node); + + connectToNode(node); + + m_article = Article(); + m_node = node; + + if (node && !node->articles().isEmpty()) + m_link = node->articles().first().link(); + else + m_link = KURL(); + + slotUpdateCombinedView(); +} + +void ArticleViewer::keyPressEvent(QKeyEvent* e) +{ + e->ignore(); +} + +void ArticleViewer::urlSelected(const QString &url, int button, int state, const QString& _target, KParts::URLArgs args) +{ + if(url == "config:/disable_introduction") { + if(KMessageBox::questionYesNo( widget(), i18n("Are you sure you want to disable this introduction page?"), i18n("Disable Introduction Page"), i18n("Disable"), i18n("Keep Enabled") ) == KMessageBox::Yes) { + KConfig *conf = Settings::self()->config(); + conf->setGroup("General"); + conf->writeEntry("Disable Introduction", "true"); + } + } + else + Viewer::urlSelected(url, button, state, _target, args); +} + +void ArticleViewer::slotPaletteOrFontChanged() +{ + generateNormalModeCSS(); + generateCombinedModeCSS(); + reload(); +} + +void ArticleViewer::connectToNode(TreeNode* node) +{ + if (node) + { + if (m_viewMode == CombinedView) + { +// connect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotUpdateCombinedView() ) ); + connect( node, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesAdded(TreeNode*, const QValueList<Article>&))); + connect( node, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesRemoved(TreeNode*, const QValueList<Article>&))); + connect( node, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesUpdated(TreeNode*, const QValueList<Article>&))); + } + if (m_viewMode == SummaryView) + connect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotShowSummary(TreeNode*) ) ); + + connect( node, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotClear() ) ); + } +} + +void ArticleViewer::disconnectFromNode(TreeNode* node) +{ + if (node) + { +// disconnect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotUpdateCombinedView() ) ); + disconnect( node, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotClear() ) ); + disconnect( node, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotShowSummary(TreeNode*) ) ); + disconnect( node, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesAdded(TreeNode*, const QValueList<Article>&))); + disconnect( node, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesRemoved(TreeNode*, const QValueList<Article>&))); + disconnect( node, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesUpdated(TreeNode*, const QValueList<Article>&))); + + } +} + +} +#include "articleviewer.moc" + diff --git a/akregator/src/articleviewer.h b/akregator/src/articleviewer.h new file mode 100644 index 000000000..a9eb65b65 --- /dev/null +++ b/akregator/src/articleviewer.h @@ -0,0 +1,154 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef ARTICLEVIEWER_H +#define ARTICLEVIEWER_H + +#include <klocale.h> + +#include <qcolor.h> +#include <qfont.h> + +#include "article.h" +#include "articlefilter.h" +#include "viewer.h" + +class QKeyEvent; + +namespace Akregator +{ + class Feed; + class Folder; + class TreeNode; + + /** This HTML viewer is used to display articles. + Use the high-level interface provided by the public slots whereever possible (and extend them when necessary instead of using low-level methods).*/ + class ArticleViewer : public Viewer + { + Q_OBJECT + public: + /** Constructor */ + ArticleViewer(QWidget* parent, const char* name); + virtual ~ArticleViewer(); + + virtual bool openURL(const KURL &url); + + /** Repaints the view. */ + void reload(); + + void displayAboutPage(); + + public slots: + + // Commandment: We are your interfaces. + // You shall not use strange interfaces before us. + + /** Show single article (normal view) + @param article the article to render */ + void slotShowArticle(const Article& article); + + /** Shows the articles of the tree node @c node (combined view). Changes in the node will update the view automatically. + @param node The node to observe */ + void slotShowNode(TreeNode* node); + + /** Set filters @c textFilter and @c statusFilter which will be used if the viewer is in combined view mode + @param textFilter text filter + @param statusFilter status filter */ + void slotSetFilter(const Akregator::Filters::ArticleMatcher& textFilter, const Akregator::Filters::ArticleMatcher& statusFilter); + + /** Update view if combined view mode is set. Has to be called when the displayed node gets modified. */ + void slotUpdateCombinedView(); + + /** Clears the canvas and disconnects from the currently observed node (if in combined view mode). */ + void slotClear(); + + void slotShowSummary(TreeNode *node); + + virtual void slotPaletteOrFontChanged(); + + protected slots: + + void slotArticlesUpdated(TreeNode* node, const QValueList<Article>& list); + void slotArticlesAdded(TreeNode* node, const QValueList<Article>& list); + void slotArticlesRemoved(TreeNode* node, const QValueList<Article>& list); + + protected: + + virtual void keyPressEvent(QKeyEvent* e); + virtual void urlSelected (const QString &url, int button, int state, const QString &_target, KParts::URLArgs args); + + public: // compat with KDE-3.x assertions, remove for KDE 4 + // private: + + friend class ShowNodeSummaryVisitor; + class ShowSummaryVisitor; + ShowSummaryVisitor* m_showSummaryVisitor; + + /** renders @c body. Use this method whereever possible. + @param body html to render, without header and footer */ + void renderContent(const QString& body); + + /** Takes an article and renders it as HTML with settings for normal view and widescreen view + @param feed article's feed (used for feed icon atm) -- article.feed() would do. better use a (No)Icon flag. -fo + @param article The article to render + @return the rendered article as HTML */ + QString formatArticleNormalMode(Feed* feed, const Article& article); + + /** Takes an article and renders it as HTML with settings for combined view + @param feed article's feed (used for feed icon atm) -- article.feed() would do. better use a (No)Icon flag. -fo + @param article The article to render + @return the rendered article as HTML */ + QString formatArticleCombinedMode(Feed* feed, const Article& article); + + /** Resets the canvas and adds writes the HTML header to it. + */ + void beginWriting(); + + /** Finishes writing to the canvas and completes the HTML (by adding closing tags) */ + void endWriting(); + + /** generates the CSS used for rendering in single article mode (normal and wide screen view) */ + void generateNormalModeCSS(); + /** generates the CSS for combined view mode */ + void generateCombinedModeCSS(); + void connectToNode(TreeNode* node); + void disconnectFromNode(TreeNode* node); + + QString m_normalModeCSS; + QString m_combinedModeCSS; + QString m_htmlFooter; + QString m_currentText; + KURL m_imageDir; + TreeNode* m_node; + Article m_article; + KURL m_link; + Akregator::Filters::ArticleMatcher m_textFilter; + Akregator::Filters::ArticleMatcher m_statusFilter; + enum ViewMode { NormalView, CombinedView, SummaryView }; + ViewMode m_viewMode; + }; +} + +#endif // ARTICLEVIEWER_H diff --git a/akregator/src/articleviewer.rc b/akregator/src/articleviewer.rc new file mode 100644 index 000000000..5b61457b2 --- /dev/null +++ b/akregator/src/articleviewer.rc @@ -0,0 +1,30 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="articleviewer" version="7"> +<MenuBar> +<Menu noMerge="1" name="file"> + <Action name="viewer_print"/> + <Separator/> + <Merge/> +</Menu> + +<Menu noMerge="1" name="edit"> + <Action name="viewer_copy"/> + <Action name="selectAll"/> + <Separator/> + <Action name="find"/> + <Action name="findNext"/> +</Menu> +<Menu noMerge="1" name="view"> + <Separator/> + <Action name="incFontSizes"/> + <Action name="decFontSizes"/> +</Menu> + +</MenuBar> +<!-- this menu doesn't show up in the app, just groups the actions --> +<Menu name="articleviewer_various"> + <Action name="articleviewer_scroll_up"/> + <Action name="articleviewer_scroll_down"/> +</Menu> + +</kpartgui> diff --git a/akregator/src/configdialog.cpp b/akregator/src/configdialog.cpp new file mode 100644 index 000000000..c64f0ac0d --- /dev/null +++ b/akregator/src/configdialog.cpp @@ -0,0 +1,77 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qstringlist.h> + +#include <kcombobox.h> +#include <klocale.h> + +#include "configdialog.h" + +#include "akregatorconfig.h" +#include "settings_advanced.h" +#include "settings_appearance.h" +#include "settings_archive.h" +#include "settings_browser.h" +#include "settings_general.h" +#include <qslider.h> +#include <qlabel.h> + +namespace Akregator +{ + +ConfigDialog::ConfigDialog(QWidget* parent, const char* name, KConfigSkeleton* config, DialogType dialogType, int dialogButtons, ButtonCode defaultButton, bool modal) : KConfigDialog(parent, name, config, dialogType, dialogButtons, defaultButton, modal) +{ + addPage(new SettingsGeneral(this, "General"), i18n("General"), "package_settings"); + addPage(new SettingsArchive(this, "Archive"), i18n("Archive"), "package_settings"); + m_settingsAppearance = new SettingsAppearance(this, "Appearance"); + addPage(m_settingsAppearance, i18n("Appearance"), "fonts"); + addPage(new SettingsBrowser(this, "Browser"), i18n("Browser"), "package_network"); + m_settingsAdvanced = new SettingsAdvanced(this, "Advanced"); + addPage(m_settingsAdvanced, i18n("Advanced"), "package_network"); + m_settingsAdvanced->selectFactory(Settings::archiveBackend()); + m_config = config; +} + +void ConfigDialog::updateSettings() +{ + Settings::setArchiveBackend(m_settingsAdvanced->selectedFactory()); + KConfigDialog::updateSettings(); +} + +void ConfigDialog::updateWidgets() +{ + m_settingsAdvanced->selectFactory(Settings::archiveBackend()); + m_settingsAppearance->slider_minimumFontSize->setDisabled(m_config->isImmutable("MinimumFontSize")); + m_settingsAppearance->slider_mediumFontSize->setDisabled(m_config->isImmutable("MediumFontSize")); + m_settingsAppearance->lbl_MinimumFontSize->setDisabled(m_config->isImmutable("MinimumFontSize")); + m_settingsAppearance->lbl_MediumFontSize->setDisabled(m_config->isImmutable("MediumFontSize")); + KConfigDialog::updateWidgets(); +} + +ConfigDialog::~ConfigDialog() {} + +} // namespace Akregator + +#include "configdialog.moc" diff --git a/akregator/src/configdialog.h b/akregator/src/configdialog.h new file mode 100644 index 000000000..01e8b958a --- /dev/null +++ b/akregator/src/configdialog.h @@ -0,0 +1,58 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_CONFIGDIALOG_H +#define AKREGATOR_CONFIGDIALOG_H + +#include <kconfigdialog.h> + +namespace Akregator { + +class SettingsAdvanced; +class SettingsAppearance; + +class ConfigDialog : public KConfigDialog +{ + Q_OBJECT + public: + + ConfigDialog(QWidget *parent, const char *name, KConfigSkeleton *config, DialogType dialogType=IconList, int dialogButtons=Default|Ok|Apply|Cancel|Help, ButtonCode defaultButton=Ok, bool modal=false); + + virtual ~ConfigDialog(); + + protected slots: + + virtual void updateSettings(); + + virtual void updateWidgets(); + + private: + KConfigSkeleton* m_config; + SettingsAdvanced* m_settingsAdvanced; + SettingsAppearance* m_settingsAppearance; + +}; + +} // namespace Akregator +#endif // AKREGATOR_CONFIGDIALOG_H diff --git a/akregator/src/cr16-app-akregator_empty.png b/akregator/src/cr16-app-akregator_empty.png Binary files differnew file mode 100644 index 000000000..4094b858e --- /dev/null +++ b/akregator/src/cr16-app-akregator_empty.png diff --git a/akregator/src/dragobjects.cpp b/akregator/src/dragobjects.cpp new file mode 100644 index 000000000..9f7f812b7 --- /dev/null +++ b/akregator/src/dragobjects.cpp @@ -0,0 +1,119 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "dragobjects.h" +#include "feed.h" + +#include <qcstring.h> + +namespace Akregator { + +class Article; + +ArticleDrag::ArticleDrag(const QValueList<Article>& articles, QWidget* dragSource, const char* name) +: KURLDrag(articleURLs(articles), dragSource, name), m_items(articlesToDragItems(articles)) +{} + +bool ArticleDrag::canDecode(const QMimeSource* e) +{ + return e->provides("akregator/articles"); +} + +bool ArticleDrag::decode(const QMimeSource* e, QValueList<ArticleDragItem>& articles) +{ + articles.clear(); + QByteArray array = e->encodedData("akregator/articles"); + + QDataStream stream(array, IO_ReadOnly); + + while (!stream.atEnd()) + { + ArticleDragItem i; + stream >> i.feedURL; + stream >> i.guid; + articles.append(i); + } + + return true; +} + +const char* ArticleDrag::format(int i) const +{ + if (i == 0) + return "text/uri-list"; + else if (i == 1) + return "akregator/articles"; + + return 0; +} + +QByteArray ArticleDrag::encodedData(const char* mime) const +{ + QCString mimetype(mime); + if (mimetype == "akregator/articles") + { + QByteArray ba; + QDataStream stream(ba, IO_WriteOnly); + + QValueList<ArticleDragItem>::ConstIterator end = m_items.end(); + for (QValueList<ArticleDragItem>::ConstIterator it = m_items.begin(); it != end; ++it) + { + stream << (*it).feedURL; + stream << (*it).guid; + } + return ba; + } + else + { + return KURLDrag::encodedData(mime); + } +} + +QValueList<ArticleDragItem> ArticleDrag::articlesToDragItems(const QValueList<Article>& articles) +{ + QValueList<ArticleDragItem> items; + + QValueList<Article>::ConstIterator end(articles.end()); + + for (QValueList<Article>::ConstIterator it = articles.begin(); it != end; ++it) + { + ArticleDragItem i; + i.feedURL = (*it).feed() ? (*it).feed()->xmlUrl() : ""; + i.guid = (*it).guid(); + items.append(i); + } + + return items; +} + +KURL::List ArticleDrag::articleURLs(const QValueList<Article>& articles) +{ + KURL::List urls; + QValueList<Article>::ConstIterator end(articles.end()); + for (QValueList<Article>::ConstIterator it = articles.begin(); it != end; ++it) + urls.append((*it).link()); + return urls; +} + +} // namespace Akregator diff --git a/akregator/src/dragobjects.h b/akregator/src/dragobjects.h new file mode 100644 index 000000000..b83514c3c --- /dev/null +++ b/akregator/src/dragobjects.h @@ -0,0 +1,72 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_DRAGOBJECTS_H +#define AKREGATOR_DRAGOBJECTS_H + +#include "article.h" + +#include <kurl.h> +#include <kurldrag.h> + +#include <qstring.h> +#include <qvaluelist.h> + +typedef class QMemArray<char> QByteArray; + +namespace Akregator { + +class Article; + +struct ArticleDragItem +{ + QString feedURL; + QString guid; +}; + +class ArticleDrag : public KURLDrag +{ + +public: + + ArticleDrag(const QValueList<Article>& articles, QWidget* dragSource=0, const char* name=0); + + static bool canDecode(const QMimeSource* e); + static bool decode(const QMimeSource* e, QValueList<ArticleDragItem>& articles); + +protected: + + virtual QByteArray encodedData(const char* mime) const; + virtual const char* format(int i) const; + +private: + + static QValueList<ArticleDragItem> articlesToDragItems(const QValueList<Article>& articles); + static KURL::List articleURLs(const QValueList<Article>& articles); + QValueList<ArticleDragItem> m_items; +}; + +} // namespace Akregator + +#endif // AKREGATOR_DRAGOBJECTS_H diff --git a/akregator/src/eventsrc b/akregator/src/eventsrc new file mode 100644 index 000000000..3be7516a8 --- /dev/null +++ b/akregator/src/eventsrc @@ -0,0 +1,219 @@ +[!Global!] +IconName=akregator +Comment=Akregator +Comment[da]=Akregator Plugin +Comment[ne]=एक्रिगेटर +Comment[sv]=aKregator + +[feed_added] +Name=Feed added +Name[af]=Stroom bygevoeg +Name[bg]=Добавена е новина +Name[ca]=Enllaç afegit +Name[cs]=Přidán kanál +Name[da]=Kilde tilføjet +Name[de]=Nachrichtenquelle hinzugefügt +Name[el]=Προστέθηκε ροή +Name[eo]=Fluo aldonita +Name[es]=Origen añadido +Name[et]=Uudisevoog lisatud +Name[eu]=Iturria gehituta +Name[fa]=خوراندن اضافهشده +Name[fi]=Syöte lisätty +Name[fr]=Flux ajouté +Name[fy]=Nijsoanfier taheakke +Name[ga]=Cuireadh fotha leis +Name[gl]=Engadiuse unha fonte +Name[he]=נוסף ערוץ +Name[hu]=Hírforrás felvéve +Name[is]=Straum bætt við +Name[it]=Aggiunta fonte +Name[ja]=フィード追加 +Name[ka]=დამატებულია კვება +Name[kk]=Қор қосылды +Name[km]=បានបន្ថែមមតិព័ត៌មាន +Name[ko]=피드 추가됨 +Name[lt]=Kanalas pridėtas +Name[ms]=Suapan ditambah +Name[nb]=Kanal lagt til +Name[nds]=Narichtenstroom toföögt +Name[ne]=फिड थपियो +Name[nl]=Feed toegevoegd +Name[nn]=Kanal lagd til +Name[pl]=Kanał dodany +Name[pt]=Fonte adicionada +Name[pt_BR]=Fonte de notícias adicionada +Name[ru]=Лента новостей добавлена +Name[se]=Gáldu lasihuvvon +Name[sk]=Kŕmitko pridané +Name[sl]=Vir je dodan +Name[sr]=Довод је додат +Name[sr@Latn]=Dovod je dodat +Name[sv]=Kanal tillagd +Name[ta]=பீஃட் சேர்க்கப்பட்டது +Name[tr]=Haber kaynağı eklendi +Name[uk]=Подачу додано +Name[uz]=Yangiliklar tasmasi qoʻshildi +Name[uz@cyrillic]=Янгиликлар тасмаси қўшилди +Name[zh_CN]=添加了新闻源 +Name[zh_TW]=已加入 Feed +Comment=A new feed was remotely added to Akregator +Comment[af]='n Nuwe stroom was vanaf 'n afgeleë ligging by Akregator bygevoeg. +Comment[bg]=Отдалечено е добавена нова новина към Akregator +Comment[ca]=S'ha afegit remotament un enllaç a l'Akregator +Comment[cs]=Byl přidán nový kanál do Akregatoru +Comment[da]=En ny kilde blev tilføjet eksternt til Akregator +Comment[de]=Eine neue Nachrichtenquelle wurde von extern zu Akregator hinzugefügt +Comment[el]=Μια νέα ροή προστέθηκε απομακρυσμένα στο Akregator +Comment[es]=Se ha añadido remotamente un origen a Akregator +Comment[et]=Akregatorile lisati väljastpoolt uus uudisevoog +Comment[eu]=Iturri berri bat gehitu da urrunetik Akregator-era +Comment[fa]=خوراندن جدیدی که به صورت دور به Akregator اضافه شد +Comment[fi]=Uusi syöte lisättiin Akregatoriin ulkopuolelta +Comment[fr]=Un flux a été ajouté à distance à Akregator +Comment[fy]=Der wie ekstern nije nijsoanfier taheakke oan Akregator +Comment[gl]=Engadiuse remotamente unha nova fonte a Akregator +Comment[he]=נוסף ערוץ ל־Akregator מרחוק +Comment[hu]=Egy hírforrást távolról felvettek az Akregatorba +Comment[is]=Nýjum straum var bætt við Akregator +Comment[it]=Una nuova fonte è stata aggiunta ad Akregator da remoto +Comment[ja]=新規フィードがリモートで Akregator に追加されました +Comment[ka]=Akregator-ის სიას დისტანციურად ახალი კვება დაემატა +Comment[kk]=Akregator-ға қашықтан жаңа қор қосылды +Comment[km]=បានបន្ថែមមតិព័ត៌មានថ្មីមួយពីចម្ងាយទៅ Akregator +Comment[ko]=Akregator에 피드가 원격으로 추가됨 +Comment[lt]=Naujas nutolęs naujienų kanalas buvo įdėtas į Akregator +Comment[ms]=Suapan baru ditambah dari jauh kepada Akregator +Comment[nb]=En ny kanal ble lagt til Akgregator utenfra +Comment[nds]=En nieg Stroom wöör Akregator vun buten toföögt +Comment[ne]=एक्रिगेटरमा एउटा नयाँ फिड टाढाबाट थपिएको थियो +Comment[nl]=Er was extern een nieuwe feed toegevoegd aan Akregator +Comment[nn]=Ein ny kanal vart lagd til Akregator +Comment[pl]=Nowy kanał został zdalnie dodany do Akregatora +Comment[pt]=Foi adicionada remotamente uma nova fonte ao Akregator +Comment[pt_BR]=Uma nova fonte de notícias foi adicionada remotamente ao Akregator +Comment[ru]=Новая лента новостей добавлена в список Akregator +Comment[se]=Ođđa gáldu lasihuvvui Akregatorii +Comment[sk]=Nové kŕmitko bolo vzdialene pridané do Akregator +Comment[sl]=Nov vir je bil oddaljeno dodan v Akregator +Comment[sr]=Нови довод је удаљено додат у Akregator +Comment[sr@Latn]=Novi dovod je udaljeno dodat u Akregator +Comment[sv]=En ny kanal har lagts till i aKregator utifrån +Comment[ta]=Akregatorக்கு ஒரு புதிய உள்ளீடு சேர்க்கப்பட்டது +Comment[tr]=Akregator'a yeni bir haber kaynağı eklendi +Comment[uk]=Нову подачу було віддалено додано до Akregator +Comment[uz]=Akregator dasturiga yangi yangiliklar tasmasi qoʻshildi +Comment[uz@cyrillic]=Akregator дастурига янги янгиликлар тасмаси қўшилди +Comment[zh_CN]=新闻源远程添加进了 Akregator +Comment[zh_TW]=已從遠端加入新的 feed 到 Akregator +default_presentation=4 + +[new_articles] +Name=New Articles +Name[af]=Nuwe Artikels +Name[ar]=المقالات الجديدة +Name[be]=Новыя артыкулы +Name[bg]=Нови статии +Name[br]=Pennadoù nevez +Name[ca]=Nous articles +Name[cs]=Nové články +Name[da]=Nye artikler +Name[de]=Neue Artikel +Name[el]=Άρθρα νέων +Name[eo]=Novaj Artikoloj +Name[es]=Artículos nuevos +Name[et]=Uued artiklid +Name[eu]=Artikulu berriak +Name[fa]=مقالات جدید +Name[fi]=Uudet artikkelit +Name[fr]=Nouveaux articles +Name[fy]=Nije artikels +Name[ga]=Altanna Nua +Name[gl]=Novos Artigos +Name[he]=מאמרים חדשים +Name[hu]=Hírekk +Name[is]=Nýjar greinar +Name[it]=Nuovi articoli +Name[ja]=新規記事 +Name[ka]=ახალი სტატიები +Name[kk]=Жаңа мақалалар +Name[km]=អត្ថបទថ្មី +Name[ko]=새 글 +Name[lt]=Nauji straipsniai +Name[mk]=Нови написи +Name[ms]=Artikel Baru +Name[nb]=Nye artikler +Name[nds]=Niege Artikeln +Name[ne]=नयाँ लेख +Name[nl]=Nieuwe artikelen +Name[nn]=Nye artiklar +Name[pl]=Nowe artykuły +Name[pt]=Novos Artigos +Name[pt_BR]=Novos Artigos +Name[ru]=Новые статьи +Name[se]=Ođđa artihkkalat +Name[sk]=Nový článok +Name[sl]=Novi članki +Name[sr]=Нови чланци +Name[sr@Latn]=Novi članci +Name[sv]=Nya artiklar +Name[ta]= புதிய செய்திகள் +Name[tr]=Yeni Haberler +Name[uk]=Нові статті +Name[uz]=Yangi maqolalar +Name[uz@cyrillic]=Янги мақолалар +Name[zh_CN]=新闻文章 +Name[zh_TW]=新文章 +Comment=New articles were fetched +Comment[af]=Nuwe artikels was afgelaai +Comment[ar]=تمّ جلب المقالات الجديدة +Comment[bg]=Пристигнали са нови статии +Comment[ca]=S'han obtingut nous articles +Comment[cs]=Byly staženy nové články +Comment[da]=Nye artikler blev hentet +Comment[de]=Neue Artikel wurden abgeholt +Comment[el]=Έφτασαν νέα άρθρα +Comment[eo]=Novaj artikoloj estis alprenataj +Comment[es]=Se obtuvieron los artículos nuevos +Comment[et]=Tõmmati uued artiklid +Comment[eu]=Artikulu berriak eskuratu dira +Comment[fa]=مقالات جدید واکشی شدند +Comment[fi]=Uutta postia saapunut +Comment[fr]=Les nouveaux articles ont été relevés +Comment[fy]=Nije artikels binne ophelle +Comment[ga]=Fuarthas ailt nua +Comment[gl]=Obtivéronse novos artigos +Comment[he]=הורדו מאמרים נוספים +Comment[hu]=Új hírek lettek letöltve +Comment[is]=Nýjar greinar voru sóttar +Comment[it]=I nuovi articoli sono stati recuperati +Comment[ja]=新規記事を取得しました +Comment[ka]=მიღებულია ახალი სტატიები +Comment[kk]=Жаңа мақалалар қабылданды +Comment[km]=បានប្រមូលអត្ថបទថ្មី +Comment[ko]=새 글을 가져옴 +Comment[lt]=Nauji straipsniai parsiųsti +Comment[mk]=Беа земени нови написи +Comment[ms]=Artikel baru telah dikutip +Comment[nb]=Nye artikler ble hentet +Comment[nds]=Niege Artikeln wöörn haalt +Comment[ne]=नयाँ लेख तानिएका थिय +Comment[nl]=Nieuw artikelen zijn opgehaald +Comment[nn]=Nye artiklar vart henta +Comment[pl]=Pobrano nowe artykuły +Comment[pt]=Foram recebidos novos artigos +Comment[pt_BR]=Há novos artigos disponíveis +Comment[ru]=Получены новые статьи +Comment[se]=Ođđa artihkkalat vižžon +Comment[sk]=Získali sa nové články +Comment[sl]=Povlečeni so bili novi članki +Comment[sr]=Добављени су нови чланци +Comment[sr@Latn]=Dobavljeni su novi članci +Comment[sv]=Nya artiklar har hämtats +Comment[ta]=புதிய கட்டுரைகள் கொண்டுவரப்பட்டது +Comment[tr]=Yeni haberler alındı +Comment[uk]=Отримано нові статті +Comment[zh_CN]=获取了新文章 +Comment[zh_TW]=已抓到新文章 +default_presentation=4 + diff --git a/akregator/src/feed.cpp b/akregator/src/feed.cpp new file mode 100644 index 000000000..c088654a2 --- /dev/null +++ b/akregator/src/feed.cpp @@ -0,0 +1,857 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qtimer.h> +#include <qdatetime.h> +#include <qlistview.h> +#include <qdom.h> +#include <qmap.h> +#include <qpixmap.h> +#include <qvaluelist.h> + +#include <kurl.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kstandarddirs.h> + +#include "akregatorconfig.h" +#include "article.h" +#include "articleinterceptor.h" +#include "feed.h" +#include "folder.h" +#include "fetchqueue.h" +#include "feediconmanager.h" +#include "feedstorage.h" +#include "storage.h" +#include "treenodevisitor.h" +#include "utils.h" + +#include "librss/librss.h" + +namespace Akregator { + +class Feed::FeedPrivate +{ + public: + bool autoFetch; + int fetchInterval; + ArchiveMode archiveMode; + int maxArticleAge; + int maxArticleNumber; + bool markImmediatelyAsRead; + bool useNotification; + bool loadLinkedWebsite; + + bool fetchError; + + int lastErrorFetch; // save time of last fetch that went wrong. + // != lastFetch property from the archive + // (that saves the last _successfull fetch!) + // workaround for 3.5.x + + int fetchTries; + bool followDiscovery; + RSS::Loader* loader; + bool articlesLoaded; + Backend::FeedStorage* archive; + + QString xmlUrl; + QString htmlUrl; + QString description; + + /** list of feed articles */ + QMap<QString, Article> articles; + + /** caches guids of tagged articles. key: tag, value: list of guids */ + QMap<QString, QStringList> taggedArticles; + + /** list of deleted articles. This contains **/ + QValueList<Article> deletedArticles; + + /** caches guids of deleted articles for notification */ + + QValueList<Article> addedArticlesNotify; + QValueList<Article> removedArticlesNotify; + QValueList<Article> updatedArticlesNotify; + + QPixmap imagePixmap; + RSS::Image image; + QPixmap favicon; +}; + +QString Feed::archiveModeToString(ArchiveMode mode) +{ + switch (mode) + { + case keepAllArticles: + return "keepAllArticles"; + case disableArchiving: + return "disableArchiving"; + case limitArticleNumber: + return "limitArticleNumber"; + case limitArticleAge: + return "limitArticleAge"; + default: + return "globalDefault"; + } + + // in a perfect world, this is never reached + + return "globalDefault"; +} + +Feed* Feed::fromOPML(QDomElement e) +{ + + Feed* feed = 0; + + if( e.hasAttribute("xmlUrl") || e.hasAttribute("xmlurl") || e.hasAttribute("xmlURL") ) + { + QString title = e.hasAttribute("text") ? e.attribute("text") : e.attribute("title"); + + QString xmlUrl = e.hasAttribute("xmlUrl") ? e.attribute("xmlUrl") : e.attribute("xmlurl"); + if (xmlUrl.isEmpty()) + xmlUrl = e.attribute("xmlURL"); + + bool useCustomFetchInterval = e.attribute("useCustomFetchInterval") == "true" || e.attribute("autoFetch") == "true"; + // "autoFetch" is used in 3.4 + // Will be removed in KDE4 + + QString htmlUrl = e.attribute("htmlUrl"); + QString description = e.attribute("description"); + int fetchInterval = e.attribute("fetchInterval").toInt(); + ArchiveMode archiveMode = stringToArchiveMode(e.attribute("archiveMode")); + int maxArticleAge = e.attribute("maxArticleAge").toUInt(); + int maxArticleNumber = e.attribute("maxArticleNumber").toUInt(); + bool markImmediatelyAsRead = e.attribute("markImmediatelyAsRead") == "true"; + bool useNotification = e.attribute("useNotification") == "true"; + bool loadLinkedWebsite = e.attribute("loadLinkedWebsite") == "true"; + uint id = e.attribute("id").toUInt(); + + feed = new Feed(); + feed->setTitle(title); + feed->setXmlUrl(xmlUrl); + feed->setCustomFetchIntervalEnabled(useCustomFetchInterval); + feed->setHtmlUrl(htmlUrl); + feed->setId(id); + feed->setDescription(description); + feed->setArchiveMode(archiveMode); + feed->setUseNotification(useNotification); + feed->setFetchInterval(fetchInterval); + feed->setMaxArticleAge(maxArticleAge); + feed->setMaxArticleNumber(maxArticleNumber); + feed->setMarkImmediatelyAsRead(markImmediatelyAsRead); + feed->setLoadLinkedWebsite(loadLinkedWebsite); + feed->loadArticles(); // TODO: make me fly: make this delayed + feed->loadImage(); + } + + return feed; +} + +bool Feed::accept(TreeNodeVisitor* visitor) +{ + if (visitor->visitFeed(this)) + return true; + else + return visitor->visitTreeNode(this); +} + +QStringList Feed::tags() const +{ + return d->archive->tags(); +} + +Article Feed::findArticle(const QString& guid) const +{ + return d->articles[guid]; +} + +QValueList<Article> Feed::articles(const QString& tag) +{ + if (!d->articlesLoaded) + loadArticles(); + if (tag.isNull()) + return d->articles.values(); + else + { + QValueList<Article> tagged; + QStringList guids = d->archive->articles(tag); + for (QStringList::ConstIterator it = guids.begin(); it != guids.end(); ++it) + tagged += d->articles[*it]; + return tagged; + + } +} + +void Feed::loadImage() +{ + QString imageFileName = KGlobal::dirs()->saveLocation("cache", "akregator/Media/") + + Utils::fileNameForUrl(d->xmlUrl) + +".png"; + d->imagePixmap.load(imageFileName, "PNG"); +} + +void Feed::loadArticles() +{ + if (d->articlesLoaded) + return; + + if (!d->archive) + d->archive = Backend::Storage::getInstance()->archiveFor(xmlUrl()); + + QStringList list = d->archive->articles(); + for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + Article mya(*it, this); + d->articles[mya.guid()] = mya; + if (mya.isDeleted()) + d->deletedArticles.append(mya); + } + + d->articlesLoaded = true; + enforceLimitArticleNumber(); + recalcUnreadCount(); +} + +void Feed::recalcUnreadCount() +{ + QValueList<Article> tarticles = articles(); + QValueList<Article>::Iterator it; + QValueList<Article>::Iterator en = tarticles.end(); + + int oldUnread = d->archive->unread(); + + int unread = 0; + + for (it = tarticles.begin(); it != en; ++it) + if (!(*it).isDeleted() && (*it).status() != Article::Read) + ++unread; + + if (unread != oldUnread) + { + d->archive->setUnread(unread); + nodeModified(); + } +} + +Feed::ArchiveMode Feed::stringToArchiveMode(const QString& str) +{ + if (str == "globalDefault") + return globalDefault; + if (str == "keepAllArticles") + return keepAllArticles; + if (str == "disableArchiving") + return disableArchiving; + if (str == "limitArticleNumber") + return limitArticleNumber; + if (str == "limitArticleAge") + return limitArticleAge; + + return globalDefault; +} + +Feed::Feed() : TreeNode(), d(new FeedPrivate) +{ + d->autoFetch = false; + d->fetchInterval = 30; + d->archiveMode = globalDefault; + d->maxArticleAge = 60; + d->maxArticleNumber = 1000; + d->markImmediatelyAsRead = false; + d->useNotification = false; + d->fetchError = false; + d->lastErrorFetch = 0; + d->fetchTries = 0; + d->loader = 0; + d->articlesLoaded = false; + d->archive = 0; + d->loadLinkedWebsite = false; +} + +Feed::~Feed() +{ + slotAbortFetch(); + emitSignalDestroyed(); + delete d; + d = 0; +} + +bool Feed::useCustomFetchInterval() const { return d->autoFetch; } + +void Feed::setCustomFetchIntervalEnabled(bool enabled) { d->autoFetch = enabled; } + +int Feed::fetchInterval() const { return d->fetchInterval; } + +void Feed::setFetchInterval(int interval) { d->fetchInterval = interval; } + +int Feed::maxArticleAge() const { return d->maxArticleAge; } + +void Feed::setMaxArticleAge(int maxArticleAge) { d->maxArticleAge = maxArticleAge; } + +int Feed::maxArticleNumber() const { return d->maxArticleNumber; } + +void Feed::setMaxArticleNumber(int maxArticleNumber) { d->maxArticleNumber = maxArticleNumber; } + +bool Feed::markImmediatelyAsRead() const { return d->markImmediatelyAsRead; } + +void Feed::setMarkImmediatelyAsRead(bool enabled) +{ + d->markImmediatelyAsRead = enabled; + if (enabled) + slotMarkAllArticlesAsRead(); +} + +void Feed::setUseNotification(bool enabled) +{ + d->useNotification = enabled; +} + +bool Feed::useNotification() const +{ + return d->useNotification; +} + +void Feed::setLoadLinkedWebsite(bool enabled) +{ + d->loadLinkedWebsite = enabled; +} + +bool Feed::loadLinkedWebsite() const +{ + return d->loadLinkedWebsite; +} + +const QPixmap& Feed::favicon() const { return d->favicon; } + +const QPixmap& Feed::image() const { return d->imagePixmap; } + +const QString& Feed::xmlUrl() const { return d->xmlUrl; } + +void Feed::setXmlUrl(const QString& s) { d->xmlUrl = s; } + +const QString& Feed::htmlUrl() const { return d->htmlUrl; } + +void Feed::setHtmlUrl(const QString& s) { d->htmlUrl = s; } + +const QString& Feed::description() const { return d->description; } + +void Feed::setDescription(const QString& s) { d->description = s; } + +bool Feed::fetchErrorOccurred() { return d->fetchError; } + +bool Feed::isArticlesLoaded() const { return d->articlesLoaded; } + + +QDomElement Feed::toOPML( QDomElement parent, QDomDocument document ) const +{ + QDomElement el = document.createElement( "outline" ); + el.setAttribute( "text", title() ); + el.setAttribute( "title", title() ); + el.setAttribute( "xmlUrl", d->xmlUrl ); + el.setAttribute( "htmlUrl", d->htmlUrl ); + el.setAttribute( "id", QString::number(id()) ); + el.setAttribute( "description", d->description ); + el.setAttribute( "useCustomFetchInterval", (useCustomFetchInterval() ? "true" : "false") ); + el.setAttribute( "fetchInterval", QString::number(fetchInterval()) ); + el.setAttribute( "archiveMode", archiveModeToString(d->archiveMode) ); + el.setAttribute( "maxArticleAge", d->maxArticleAge ); + el.setAttribute( "maxArticleNumber", d->maxArticleNumber ); + if (d->markImmediatelyAsRead) + el.setAttribute( "markImmediatelyAsRead", "true" ); + if (d->useNotification) + el.setAttribute( "useNotification", "true" ); + if (d->loadLinkedWebsite) + el.setAttribute( "loadLinkedWebsite", "true" ); + el.setAttribute( "maxArticleNumber", d->maxArticleNumber ); + el.setAttribute( "type", "rss" ); // despite some additional fields, its still "rss" OPML + el.setAttribute( "version", "RSS" ); + parent.appendChild( el ); + return el; +} + +void Feed::slotMarkAllArticlesAsRead() +{ + if (unread() > 0) + { + setNotificationMode(false, true); + QValueList<Article> tarticles = articles(); + QValueList<Article>::Iterator it; + QValueList<Article>::Iterator en = tarticles.end(); + + for (it = tarticles.begin(); it != en; ++it) + (*it).setStatus(Article::Read); + setNotificationMode(true, true); + } +} +void Feed::slotAddToFetchQueue(FetchQueue* queue, bool intervalFetchOnly) +{ + if (!intervalFetchOnly) + queue->addFeed(this); + else + { + uint now = QDateTime::currentDateTime().toTime_t(); + + // workaround for 3.5.x: if the last fetch went wrong, try again after 30 minutes + // this fixes annoying behaviour of akregator, especially when the host is reachable + // but Akregator can't parse the feed (the host is hammered every minute then) + if ( fetchErrorOccurred() && now - d->lastErrorFetch <= 30*60 ) + return; + + int interval = -1; + + if (useCustomFetchInterval() ) + interval = fetchInterval() * 60; + else + if ( Settings::useIntervalFetch() ) + interval = Settings::autoFetchInterval() * 60; + + uint lastFetch = d->archive->lastFetch(); + + if ( interval > 0 && now - lastFetch >= (uint)interval ) + queue->addFeed(this); + } +} + + +void Feed::appendArticles(const RSS::Document &doc) +{ + bool changed = false; + + RSS::Article::List d_articles = doc.articles(); + RSS::Article::List::ConstIterator it; + RSS::Article::List::ConstIterator en = d_articles.end(); + + int nudge=0; + + QValueList<Article> deletedArticles = d->deletedArticles; + + for (it = d_articles.begin(); it != en; ++it) + { + if ( !d->articles.contains((*it).guid()) ) // article not in list + { + Article mya(*it, this); + mya.offsetPubDate(nudge); + nudge--; + appendArticle(mya); + + QValueList<ArticleInterceptor*> interceptors = ArticleInterceptorManager::self()->interceptors(); + for (QValueList<ArticleInterceptor*>::ConstIterator it = interceptors.begin(); it != interceptors.end(); ++it) + (*it)->processArticle(mya); + + d->addedArticlesNotify.append(mya); + + if (!mya.isDeleted() && !markImmediatelyAsRead()) + mya.setStatus(Article::New); + else + mya.setStatus(Article::Read); + + changed = true; + } + else // article is in list + { + // if the article's guid is no hash but an ID, we have to check if the article was updated. That's done by comparing the hash values. + Article old = d->articles[(*it).guid()]; + Article mya(*it, this); + if (!mya.guidIsHash() && mya.hash() != old.hash() && !old.isDeleted()) + { + mya.setKeep(old.keep()); + int oldstatus = old.status(); + old.setStatus(Article::Read); + + d->articles.remove(old.guid()); + appendArticle(mya); + + mya.setStatus(oldstatus); + + d->updatedArticlesNotify.append(mya); + changed = true; + } + else if (old.isDeleted()) + deletedArticles.remove(mya); + } + } + + QValueList<Article>::ConstIterator dit = deletedArticles.begin(); + QValueList<Article>::ConstIterator dtmp; + QValueList<Article>::ConstIterator den = deletedArticles.end(); + + // delete articles with delete flag set completely from archive, which aren't in the current feed source anymore + while (dit != den) + { + dtmp = dit; + ++dit; + d->articles.remove((*dtmp).guid()); + d->archive->deleteArticle((*dtmp).guid()); + d->deletedArticles.remove(*dtmp); + } + + if (changed) + articlesModified(); +} + +bool Feed::usesExpiryByAge() const +{ + return ( d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleAge) || d->archiveMode == limitArticleAge; +} + +bool Feed::isExpired(const Article& a) const +{ + QDateTime now = QDateTime::currentDateTime(); + int expiryAge = -1; +// check whether the feed uses the global default and the default is limitArticleAge + if ( d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleAge) + expiryAge = Settings::maxArticleAge() *24*3600; + else // otherwise check if this feed has limitArticleAge set + if ( d->archiveMode == limitArticleAge) + expiryAge = d->maxArticleAge *24*3600; + + return ( expiryAge != -1 && a.pubDate().secsTo(now) > expiryAge); +} + +void Feed::appendArticle(const Article& a) +{ + if ( (a.keep() && Settings::doNotExpireImportantArticles()) || ( !usesExpiryByAge() || !isExpired(a) ) ) // if not expired + { + if (!d->articles.contains(a.guid())) + { + d->articles[a.guid()] = a; + if (!a.isDeleted() && a.status() != Article::Read) + setUnread(unread()+1); + } + } +} + + +void Feed::fetch(bool followDiscovery) +{ + d->followDiscovery = followDiscovery; + d->fetchTries = 0; + + // mark all new as unread + QValueList<Article> articles = d->articles.values(); + QValueList<Article>::Iterator it; + QValueList<Article>::Iterator en = articles.end(); + for (it = articles.begin(); it != en; ++it) + { + if ((*it).status() == Article::New) + { + (*it).setStatus(Article::Unread); + } + } + + emit fetchStarted(this); + + tryFetch(); +} + +void Feed::slotAbortFetch() +{ + if (d->loader) + { + d->loader->abort(); + } +} + +void Feed::tryFetch() +{ + d->fetchError = false; + + d->loader = RSS::Loader::create( this, SLOT(fetchCompleted(Loader *, Document, Status)) ); + //connect(d->loader, SIGNAL(progress(unsigned long)), this, SLOT(slotSetProgress(unsigned long))); + d->loader->loadFrom( d->xmlUrl, new RSS::FileRetriever ); +} + +void Feed::slotImageFetched(const QPixmap& image) +{ + if (image.isNull()) + return; + d->imagePixmap=image; + d->imagePixmap.save(KGlobal::dirs()->saveLocation("cache", "akregator/Media/") + + Utils::fileNameForUrl(d->xmlUrl) + +".png","PNG"); + nodeModified(); +} + +void Feed::fetchCompleted(RSS::Loader *l, RSS::Document doc, RSS::Status status) +{ + // Note that loader instances delete themselves + d->loader = 0; + + // fetching wasn't successful: + if (status != RSS::Success) + { + if (status == RSS::Aborted) + { + d->fetchError = false; + emit fetchAborted(this); + } + else if (d->followDiscovery && (status == RSS::ParseError) && (d->fetchTries < 3) && (l->discoveredFeedURL().isValid())) + { + d->fetchTries++; + d->xmlUrl = l->discoveredFeedURL().url(); + emit fetchDiscovery(this); + tryFetch(); + } + else + { + d->fetchError = true; + d->lastErrorFetch = QDateTime::currentDateTime().toTime_t(); + emit fetchError(this); + } + return; + } + + loadArticles(); // TODO: make me fly: make this delayed + + // Restore favicon. + if (d->favicon.isNull()) + loadFavicon(); + + d->fetchError = false; + + if (doc.image() && d->imagePixmap.isNull()) + { + d->image = *doc.image(); + connect(&d->image, SIGNAL(gotPixmap(const QPixmap&)), this, SLOT(slotImageFetched(const QPixmap&))); + d->image.getPixmap(); + } + + if (title().isEmpty()) + setTitle( doc.title() ); + + d->description = doc.description(); + d->htmlUrl = doc.link().url(); + + appendArticles(doc); + + d->archive->setLastFetch( QDateTime::currentDateTime().toTime_t()); + emit fetched(this); +} + +void Feed::loadFavicon() +{ + FeedIconManager::self()->fetchIcon(this); +} + +void Feed::slotDeleteExpiredArticles() +{ + if ( !usesExpiryByAge() ) + return; + + QValueList<Article> articles = d->articles.values(); + + QValueList<Article>::Iterator en = articles.end(); + + setNotificationMode(false); + + // check keep flag only if it should be respected for expiry + // the code could be more compact, but we better check + // doNotExpiredArticles once instead of in every iteration + if (Settings::doNotExpireImportantArticles()) + { + for (QValueList<Article>::Iterator it = articles.begin(); it != en; ++it) + { + if (!(*it).keep() && isExpired(*it)) + { + (*it).setDeleted(); + } + } + } + else + { + for (QValueList<Article>::Iterator it = articles.begin(); it != en; ++it) + { + if (isExpired(*it)) + { + (*it).setDeleted(); + } + } + } + setNotificationMode(true); +} + +void Feed::setFavicon(const QPixmap &p) +{ + d->favicon = p; + nodeModified(); +} + +Feed::ArchiveMode Feed::archiveMode() const +{ + return d->archiveMode; +} + +void Feed::setArchiveMode(ArchiveMode archiveMode) +{ + d->archiveMode = archiveMode; +} + +int Feed::unread() const +{ + return d->archive ? d->archive->unread() : 0; +} + +void Feed::setUnread(int unread) +{ + if (d->archive && unread != d->archive->unread()) + { + d->archive->setUnread(unread); + nodeModified(); + } +} + + +void Feed::setArticleDeleted(Article& a) +{ + if (!d->deletedArticles.contains(a)) + d->deletedArticles.append(a); + + if (!d->removedArticlesNotify.contains(a)) + d->removedArticlesNotify.append(a); + + articlesModified(); +} + +void Feed::setArticleChanged(Article& a, int oldStatus) +{ + if (oldStatus != -1) + { + int newStatus = a.status(); + if (oldStatus == Article::Read && newStatus != Article::Read) + setUnread(unread()+1); + else if (oldStatus != Article::Read && newStatus == Article::Read) + setUnread(unread()-1); + } + d->updatedArticlesNotify.append(a); + articlesModified(); +} + +int Feed::totalCount() const +{ + return d->articles.count(); +} + +TreeNode* Feed::next() +{ + if ( nextSibling() ) + return nextSibling(); + + Folder* p = parent(); + while (p) + { + if ( p->nextSibling() ) + return p->nextSibling(); + else + p = p->parent(); + } + return 0; +} + +void Feed::doArticleNotification() +{ + if (!d->addedArticlesNotify.isEmpty()) + { + // copy list, otherwise the refcounting in Article::Private breaks for + // some reason (causing segfaults) + QValueList<Article> l = d->addedArticlesNotify; + emit signalArticlesAdded(this, l); + d->addedArticlesNotify.clear(); + } + if (!d->updatedArticlesNotify.isEmpty()) + { + // copy list, otherwise the refcounting in Article::Private breaks for + // some reason (causing segfaults) + QValueList<Article> l = d->updatedArticlesNotify; + emit signalArticlesUpdated(this, l); + d->updatedArticlesNotify.clear(); + } + if (!d->removedArticlesNotify.isEmpty()) + { + // copy list, otherwise the refcounting in Article::Private breaks for + // some reason (causing segfaults) + QValueList<Article> l = d->removedArticlesNotify; + emit signalArticlesRemoved(this, l); + d->removedArticlesNotify.clear(); + } + TreeNode::doArticleNotification(); +} + +void Feed::enforceLimitArticleNumber() +{ + int limit = -1; + if (d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleNumber) + limit = Settings::maxArticleNumber(); + else if (d->archiveMode == limitArticleNumber) + limit = maxArticleNumber(); + + if (limit == -1 || limit >= d->articles.count() - d->deletedArticles.count()) + return; + + setNotificationMode(false); + QValueList<Article> articles = d->articles.values(); + qHeapSort(articles); + QValueList<Article>::Iterator it = articles.begin(); + QValueList<Article>::Iterator tmp; + QValueList<Article>::Iterator en = articles.end(); + + int c = 0; + + if (Settings::doNotExpireImportantArticles()) + { + while (it != en) + { + tmp = it; + ++it; + if (c < limit) + { + if (!(*tmp).isDeleted() && !(*tmp).keep()) + c++; + } + else if (!(*tmp).keep()) + (*tmp).setDeleted(); + } + } + else + { + while (it != en) + { + tmp = it; + ++it; + if (c < limit && !(*tmp).isDeleted()) + { + c++; + } + else + { + (*tmp).setDeleted(); + } + } + } + setNotificationMode(true); +} + +} // namespace Akregator +#include "feed.moc" diff --git a/akregator/src/feed.h b/akregator/src/feed.h new file mode 100644 index 000000000..e3e63da06 --- /dev/null +++ b/akregator/src/feed.h @@ -0,0 +1,292 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATORFEED_H +#define AKREGATORFEED_H + +#include "treenode.h" +#include "librss/librss.h" + +class QDomElement; +class QPixmap; +class QString; +class QStringList; +class KURL; + + +namespace KPIM { + class ProgressItem; +} + +// needed for slot fetchCompleted() +using RSS::Document; +using RSS::Loader; +using RSS::Status; + +namespace Akregator +{ + class Article; + class FetchQueue; + class Folder; + class TreeNodeVisitor; + + namespace Backend + { + class FeedStorage; + } + /** + represents a feed + */ + class Feed : public TreeNode + { + friend class Article; + + Q_OBJECT + public: + /** the archiving modes: + - globalDefault: use default from Settings (default) + - keepAllArticles: Don't delete any articles + - disableArchiving: Don't save any articles except articles with keep flag set (equal to maxArticleNumber() == 0) + - limitArticleNumber: Save maxArticleNumber() articles, plus the ones with keep flag set + - limitArticleAge: Save articles not older than maxArticleAge() (or keep flag set) + */ + enum ArchiveMode { globalDefault, keepAllArticles, disableArchiving, limitArticleNumber, limitArticleAge }; + + // class methods + /** converts strings to ArchiveMode value + if parsing fails, it returns ArchiveMode::globalDefault + */ + static ArchiveMode stringToArchiveMode(const QString& str); + + /** converts ArchiveMode values to corresponding strings */ + static QString archiveModeToString(ArchiveMode mode); + + /** creates a Feed object from a description in OPML format */ + static Feed* fromOPML(QDomElement e); + + /** default constructor */ + Feed(); + + virtual ~Feed(); + + virtual bool accept(TreeNodeVisitor* visitor); + + /** exports the feed settings to OPML */ + virtual QDomElement toOPML( QDomElement parent, QDomDocument document ) const; + + /** + returns whether this feed uses its own fetch interval or the global setting + @return @c true iff this feed has a custom fetch interval + */ + bool useCustomFetchInterval() const; + + /** set if the feed has its custom fetch interval or uses the + global setting + @param enabled @c true: use custom interval, @c false: use global default + */ + void setCustomFetchIntervalEnabled(bool enabled); + + // FIXME is it -1 or 0 to disable interval fetching? + /** Returns custom auto fetch interval of this feed. + @return custom fetch interval in minutes, 0 if disabled */ + int fetchInterval() const; + + /** Sets custom auto fetch interval. + @param interval interval in minutes, -1 for disabling auto fetching */ + void setFetchInterval(int interval); + + /** returns the archiving mode which is used for this feed */ + ArchiveMode archiveMode() const; + + /** sets the archiving mode for this feed */ + void setArchiveMode(ArchiveMode archiveMode); + + /** returns the maximum age of articles used for expiration by age (used in @c limitArticleAge archive mode) + @return expiry age in days */ + int maxArticleAge() const; + + /** sets the maximum age of articles used for expiration by age (used in @c limitArticleAge archive mode) + @param maxArticleAge expiry age in days */ + void setMaxArticleAge(int maxArticleAge); + + + /** returns the article count limit used in @c limitArticleNumber archive mode **/ + int maxArticleNumber() const; + + /** sets the article count limit used in @c limitArticleNumber archive mode **/ + void setMaxArticleNumber(int maxArticleNumber); + + /** if @c true, new articles are marked immediately as read instead of new/unread. Useful for high-traffic feeds. */ + bool markImmediatelyAsRead() const; + + void setMarkImmediatelyAsRead(bool enabled); + + void setUseNotification(bool enabled); + + bool useNotification() const; + + /** if true, the linked URL is loaded directly in the article viewer instead of showing the description */ + void setLoadLinkedWebsite(bool enabled); + + bool loadLinkedWebsite() const; + + /** returns the favicon */ + const QPixmap& favicon() const; + + /** sets the favicon (used in the tree view) */ + void setFavicon(const QPixmap& p); + + /** returns the feed image */ + const QPixmap& image() const; + + /** returns the url of the actual feed source (rss/rdf/atom file) */ + const QString& xmlUrl() const; + /** sets the url of the actual feed source (rss/rdf/atom file) */ + void setXmlUrl(const QString& s); + + /** returns the URL of the HTML page of this feed */ + const QString& htmlUrl() const; + /** sets the URL of the HTML page of this feed */ + void setHtmlUrl(const QString& s); + + /** returns the description of this feed */ + const QString& description() const; + + /** sets the description of this feed */ + void setDescription(const QString& s); + + virtual QValueList<Article> articles(const QString& tag=QString::null); + + /** returns the article with the given @c guid, or a null article if it not exists */ + virtual Article findArticle(const QString& guid) const; + + virtual QStringList tags() const; + + /** returns whether a fetch error has occurred */ + bool fetchErrorOccurred(); + + /** returns the unread count for this feed */ + virtual int unread() const; + + /** returns the number of total articles in this feed + @return number of articles */ + + virtual int totalCount() const; + + /** returns if the article archive of this feed is loaded */ + bool isArticlesLoaded() const; + + /** returns if this node is a feed group (@c false here) */ + virtual bool isGroup() const { return false; } + + /** returns the next node in the tree. + Calling next() unless it returns 0 iterates through the tree in pre-order + */ + virtual TreeNode* next(); + + /** downloads the favicon */ + void loadFavicon(); + + /** load the image from the cache if it is in there */ + void loadImage(); + + public slots: + /** starts fetching */ + void fetch(bool followDiscovery=false); + + void slotAbortFetch(); + + /** deletes expired articles */ + virtual void slotDeleteExpiredArticles(); + + /** mark all articles in this feed as read */ + virtual void slotMarkAllArticlesAsRead(); + + /** add this feed to the fetch queue @c queue */ + virtual void slotAddToFetchQueue(FetchQueue* queue, bool intervalFetchOnly=false); + + signals: + /** emitted when fetching started */ + void fetchStarted(Feed*); + /** emitted when feed finished fetching */ + void fetched(Feed *); + /** emitted when a fetch error occurred */ + void fetchError(Feed *); + /** emitted when a feed URL was found by auto discovery */ + void fetchDiscovery(Feed *); + /** emitted when a fetch is aborted */ + void fetchAborted(Feed *); + + protected: + /** loads articles from archive **/ + void loadArticles(); + + void recalcUnreadCount(); + + virtual void doArticleNotification(); + + /** sets the unread count for this feed */ + void setUnread(int unread); + + + private slots: + + void fetchCompleted(Loader *loader, Document doc, Status status); + void slotImageFetched(const QPixmap& image); + + private: + + /** notifies that article @c mya was set to "deleted". + To be called by @ref Article + */ + void setArticleDeleted(Article& a); + + /** notifies that article @c mya was changed + @param oldStatus if the status was changed, it contains the old status, -1 otherwise + To be called by @ref Article + */ + void setArticleChanged(Article& a, int oldStatus=-1); + + void enforceLimitArticleNumber(); + + void appendArticles(const RSS::Document &d); + /** appends article @c a to the article list */ + void appendArticle(const Article& a); + + /** checks whether article @c a is expired (considering custom and global archive mode settings) */ + bool isExpired(const Article& a) const; + + /** returns @c true if either this article uses @c limitArticleAge as custom setting or uses the global default, which is @c limitArticleAge */ + bool usesExpiryByAge() const; + + /** executes the actual fetch action */ + void tryFetch(); + + class FeedPrivate; + FeedPrivate* d; + }; +} + +#endif diff --git a/akregator/src/feed.protocol b/akregator/src/feed.protocol new file mode 100644 index 000000000..6e96649e9 --- /dev/null +++ b/akregator/src/feed.protocol @@ -0,0 +1,12 @@ +[Protocol] +exec=akregator -a "%u" +protocol=feed +input=none +output=none +helper=true +reading=false +listing=false +writing=false +makedir=false +deleting=false + diff --git a/akregator/src/feediconmanager.cpp b/akregator/src/feediconmanager.cpp new file mode 100644 index 000000000..b115ed920 --- /dev/null +++ b/akregator/src/feediconmanager.cpp @@ -0,0 +1,157 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "feed.h" +#include "feediconmanager.h" + +#include <dcopclient.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kstaticdeleter.h> +#include <kurl.h> + +#include <qdict.h> +#include <qpixmap.h> +#include <qvaluelist.h> + +namespace Akregator { + +class FeedIconManager::FeedIconManagerPrivate +{ + public: + QValueList<Feed*> registeredFeeds; + QDict<Feed> urlDict; +}; + +FeedIconManager *FeedIconManager::m_instance = 0; + +static KStaticDeleter<FeedIconManager> feediconmanagersd; + +FeedIconManager* FeedIconManager::self() +{ + if (!m_instance) + m_instance = feediconmanagersd.setObject(m_instance, new FeedIconManager); + return m_instance; +} + +void FeedIconManager::fetchIcon(Feed* feed) +{ + if (!d->registeredFeeds.contains(feed)) + { + d->registeredFeeds.append(feed); + connect(feed, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotFeedDestroyed(TreeNode*))); + } + QString iconURL = getIconURL(KURL(feed->xmlUrl())); + d->urlDict.insert(iconURL, feed); + loadIcon(iconURL); +} + +FeedIconManager::FeedIconManager(QObject * parent, const char *name) +: QObject(parent, name), DCOPObject("FeedIconManager"), d(new FeedIconManagerPrivate) +{ + connectDCOPSignal("kded", + "favicons", "iconChanged(bool, QString, QString)", + "slotIconChanged(bool, QString, QString)", false); +} + + +FeedIconManager::~FeedIconManager() +{ + delete d; + d = 0; +} + +void FeedIconManager::loadIcon(const QString & url) +{ + KURL u(url); + + QString iconFile = iconLocation(u); + + if (iconFile.isNull()) + { + QByteArray data; + QDataStream ds(data, IO_WriteOnly); + ds << u; + kapp->dcopClient()->send("kded", "favicons", "downloadHostIcon(KURL)", + data); + } + else + slotIconChanged(false, url, iconFile); + +} + +QString FeedIconManager::getIconURL(const KURL& url) +{ + return "http://" +url.host() + "/"; +} + +QString FeedIconManager::iconLocation(const KURL & url) const +{ + QByteArray data, reply; + QCString replyType; + QDataStream ds(data, IO_WriteOnly); + + ds << url; + + kapp->dcopClient()->call("kded", "favicons", "iconForURL(KURL)", data, + replyType, reply); + + if (replyType == "QString") { + QDataStream replyStream(reply, IO_ReadOnly); + QString result; + replyStream >> result; + return result; + } + + return QString::null; +} + +void FeedIconManager::slotFeedDestroyed(TreeNode* node) +{ + Feed* feed = dynamic_cast<Feed*>(node); + if (feed) + while (d->registeredFeeds.contains(feed)) + d->registeredFeeds.remove(d->registeredFeeds.find(feed)); +} + +void FeedIconManager::slotIconChanged(bool /*isHost*/, const QString& hostOrURL, + const QString& iconName) +{ + QString iconFile = KGlobal::dirs()->findResource("cache", + iconName+".png"); + Feed* f; + QPixmap p = QPixmap(iconFile); + if (!p.isNull()) // we don't set null pixmaps, as feed checks pixmap.isNull() to find out whether the icon was already loaded or not. It would request the icon another time, resulting an infinite loop (until stack overflow that is + { + while (( f = d->urlDict.take(hostOrURL) )) + if (d->registeredFeeds.contains(f)) + f->setFavicon(p); + } + emit signalIconChanged(hostOrURL, iconFile); +} + +} // namespace Akregator +#include "feediconmanager.moc" diff --git a/akregator/src/feediconmanager.h b/akregator/src/feediconmanager.h new file mode 100644 index 000000000..04da5c97f --- /dev/null +++ b/akregator/src/feediconmanager.h @@ -0,0 +1,86 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef FEEDICONMGR_H +#define FEEDICONMGR_H + +#include <dcopobject.h> + +#include <qobject.h> + +class QPixmap; +class QString; + +class DCOPClient; +class KURL; + + +namespace Akregator +{ + class Feed; + class TreeNode; + + class FeedIconManager:public QObject, public DCOPObject + { + Q_OBJECT + K_DCOP + + public: + + FeedIconManager(QObject * = 0L, const char * = 0L); + ~FeedIconManager(); + + static FeedIconManager *self(); + + void fetchIcon(Feed* feed); + + QString iconLocation(const KURL &) const; + + k_dcop: + void slotIconChanged(bool, const QString&, const QString&); + + signals: + void signalIconChanged(const QString &, const QPixmap &); + + public slots: + void slotFeedDestroyed(TreeNode* node); + + protected: + + /** returns the url used to access the icon, e.g. + http://dot.kde.org/ for "dot.kde.org/1113317400/" */ + QString getIconURL(const KURL& url); + + void loadIcon(const QString &); + + private: + static FeedIconManager *m_instance; + + class FeedIconManagerPrivate; + FeedIconManagerPrivate* d; + }; +} + +#endif diff --git a/akregator/src/feeditem.cpp b/akregator/src/feeditem.cpp new file mode 100644 index 000000000..785548cf8 --- /dev/null +++ b/akregator/src/feeditem.cpp @@ -0,0 +1,119 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "actionmanager.h" +#include "feed.h" +#include "feeditem.h" + +#include <qpopupmenu.h> +#include <kaction.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <qstring.h> + +namespace Akregator { + +FeedItem::FeedItem(FolderItem* parent, Feed* node) : TreeNodeItem(parent, node) +{ + initialize(node); +} + +FeedItem::FeedItem(KListView* parent, Feed* node) : TreeNodeItem(parent, node) +{ + initialize(node); +} + +FeedItem::FeedItem(KListView* parent, TreeNodeItem* after, Feed* node) : TreeNodeItem(parent, after, node) +{ + initialize(node); +} + + +FeedItem::FeedItem(FolderItem* parent, TreeNodeItem* after, Feed* node) : TreeNodeItem(parent, after, node) +{ + initialize(node); +} + +FeedItem::~FeedItem() +{ +} + +Feed* FeedItem::node() +{ + return static_cast<Feed*> (m_node); +} + +void FeedItem::nodeChanged() +{ + if ( node()->fetchErrorOccurred() ) + setPixmap(0, errorPixmap()); + else + { + if (!node()->favicon().isNull()) + setPixmap(0, node()->favicon()); + else + { + setPixmap( 0, defaultPixmap() ); + node()->loadFavicon(); + } + } + + TreeNodeItem::nodeChanged(); +} + +QPixmap FeedItem::errorPixmap() +{ + return KGlobal::iconLoader()->loadIcon("error", KIcon::Small); +} + +QPixmap FeedItem::defaultPixmap() +{ + return KGlobal::iconLoader()->loadIcon("txt", KIcon::Small); +} + +void FeedItem::initialize(Feed* node) +{ + setExpandable(false); + if (node) + { + setText(0, node->title()); + if (!node->favicon().isNull()) + setPixmap( 0, node->favicon() ); + else + { + setPixmap( 0, defaultPixmap() ); + node->loadFavicon(); + } + } +} + +void FeedItem::showContextMenu(const QPoint& p) +{ + QWidget* w = ActionManager::getInstance()->container("feeds_popup"); + if (w) + static_cast<QPopupMenu *>(w)->exec(p); +} + +} // namespace Akregator + diff --git a/akregator/src/feeditem.h b/akregator/src/feeditem.h new file mode 100644 index 000000000..e51913ab1 --- /dev/null +++ b/akregator/src/feeditem.h @@ -0,0 +1,63 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATORFEEDITEM_H +#define AKREGATORFEEDITEM_H + +#include "treenodeitem.h" + +namespace Akregator +{ + +class Feed; +class FolderItem; + +/** +* the item class corresponding to a Feed +*/ +class FeedItem : public TreeNodeItem +{ + public: + + FeedItem(FolderItem* parent, Feed* node); + FeedItem(FolderItem* parent, TreeNodeItem* after, Feed* node); + FeedItem(KListView* parent, Feed* node); + FeedItem(KListView* parent, TreeNodeItem* after, Feed* node); + + virtual ~FeedItem(); + virtual Feed* node(); + virtual void nodeChanged(); + virtual void showContextMenu(const QPoint& p); + + static QPixmap errorPixmap(); + static QPixmap defaultPixmap(); + +private: + void initialize(Feed* node); + +}; + +} + +#endif diff --git a/akregator/src/feedlist.cpp b/akregator/src/feedlist.cpp new file mode 100644 index 000000000..814f82e1a --- /dev/null +++ b/akregator/src/feedlist.cpp @@ -0,0 +1,268 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#include "feedlist.h" + +#include <qdatetime.h> +#include <qdom.h> +#include <qmap.h> +#include <qvaluelist.h> + +#include <kdebug.h> +#include <klocale.h> + +#include "article.h" +#include "feed.h" +#include "folder.h" +#include "treenode.h" +#include "treenodevisitor.h" + +namespace Akregator { + +class FeedList::FeedListPrivate +{ + public: + + QMap<QString, QValueList<Feed*> > urlMap; + AddNodeVisitor* addNodeVisitor; + RemoveNodeVisitor* removeNodeVisitor; +}; + +class FeedList::AddNodeVisitor : public TreeNodeVisitor +{ + public: + AddNodeVisitor(FeedList* list) : m_list(list) {} + + + virtual bool visitFeed(Feed* node) + { + m_list->idMap()->insert(node->id(), node); + m_list->flatList()->append(node); + return true; + } + + private: + FeedList* m_list; +}; + +class FeedList::RemoveNodeVisitor : public TreeNodeVisitor +{ + public: + RemoveNodeVisitor(FeedList* list) : m_list(list) {} + + virtual bool visitFeed(Feed* node) + { + m_list->d->urlMap[node->xmlUrl()].remove(node); + return true; + } + + private: + FeedList* m_list; +}; + +FeedList::FeedList(QObject *parent, const char *name) + : NodeList(parent, name), d(new FeedListPrivate) +{ + d->addNodeVisitor = new AddNodeVisitor(this); + d->removeNodeVisitor = new RemoveNodeVisitor(this); + + Folder* rootNode = new Folder(i18n("All Feeds")); + rootNode->setId(1); + setRootNode(rootNode); + addNode(rootNode, true); +} + +void FeedList::addNode(TreeNode* node, bool preserveID) +{ + NodeList::addNode(node, preserveID); + d->addNodeVisitor->visit(node); +} + +void FeedList::removeNode(TreeNode* node) +{ + NodeList::removeNode(node); + d->removeNodeVisitor->visit(node); +} + +void FeedList::parseChildNodes(QDomNode &node, Folder* parent) +{ + QDomElement e = node.toElement(); // try to convert the node to an element. + + if( !e.isNull() ) + { + QString title = e.hasAttribute("text") ? e.attribute("text") : e.attribute("title"); + + if (e.hasAttribute("xmlUrl") || e.hasAttribute("xmlurl") || e.hasAttribute("xmlURL") ) + { + Feed* feed = Feed::fromOPML(e); + if (feed) + { + if (!d->urlMap[feed->xmlUrl()].contains(feed)) + d->urlMap[feed->xmlUrl()].append(feed); + parent->appendChild(feed); + } + } + else + { + Folder* fg = Folder::fromOPML(e); + parent->appendChild(fg); + + if (e.hasChildNodes()) + { + QDomNode child = e.firstChild(); + while(!child.isNull()) + { + parseChildNodes(child, fg); + child = child.nextSibling(); + } + } + } + } +} + +bool FeedList::readFromXML(const QDomDocument& doc) +{ + QDomElement root = doc.documentElement(); + + kdDebug() << "loading OPML feed " << root.tagName().lower() << endl; + + kdDebug() << "measuring startup time: START" << endl; + QTime spent; + spent.start(); + + if (root.tagName().lower() != "opml") + { + return false; + } + QDomNode bodyNode = root.firstChild(); + + while (!bodyNode.isNull() && bodyNode.toElement().tagName().lower() != "body") + bodyNode = bodyNode.nextSibling(); + + + if (bodyNode.isNull()) + { + kdDebug() << "Failed to acquire body node, markup broken?" << endl; + return false; + } + + QDomElement body = bodyNode.toElement(); + + QDomNode i = body.firstChild(); + + while( !i.isNull() ) + { + parseChildNodes(i, rootNode()); + i = i.nextSibling(); + } + + for (TreeNode* i = rootNode()->firstChild(); i && i != rootNode(); i = i->next() ) + if (i->id() == 0) + { + uint id = generateID(); + i->setId(id); + idMap()->insert(id, i); + } + + kdDebug() << "measuring startup time: STOP, " << spent.elapsed() << "ms" << endl; + kdDebug() << "Number of articles loaded: " << rootNode()->totalCount() << endl; + return true; +} + +FeedList::~FeedList() +{ + emit signalDestroyed(this); + setRootNode(0); + delete d->addNodeVisitor; + delete d->removeNodeVisitor; + delete d; + d = 0; +} + +Feed* FeedList::findByURL(const QString& feedURL) const +{ + if (d->urlMap[feedURL].isEmpty()) + return 0; + else + return *(d->urlMap[feedURL].begin()); +} + +Article FeedList::findArticle(const QString& feedURL, const QString& guid) const +{ + Feed* feed = findByURL(feedURL); + + return feed ? feed->findArticle(guid) : Article(); +} + +void FeedList::append(FeedList* list, Folder* parent, TreeNode* after) +{ + if ( list == this ) + return; + + if ( !flatList()->contains(parent) ) + parent = rootNode(); + + QValueList<TreeNode*> children = list->rootNode()->children(); + + QValueList<TreeNode*>::ConstIterator end( children.end() ); + for (QValueList<TreeNode*>::ConstIterator it = children.begin(); it != end; ++it) + { + list->rootNode()->removeChild(*it); + parent->insertChild(*it, after); + after = *it; + } +} + +QDomDocument FeedList::toXML() const +{ + QDomDocument doc; + doc.appendChild( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) ); + + QDomElement root = doc.createElement( "opml" ); + root.setAttribute( "version", "1.0" ); + doc.appendChild( root ); + + QDomElement head = doc.createElement( "head" ); + root.appendChild( head ); + + QDomElement ti = doc.createElement( "text" ); + head.appendChild( ti ); + + QDomText t = doc.createTextNode( title() ); + ti.appendChild( t ); + + QDomElement body = doc.createElement( "body" ); + root.appendChild( body ); + + QValueList<TreeNode*> children = rootNode()->children(); + + QValueList<TreeNode*>::ConstIterator end( children.end() ); + + for (QValueList<TreeNode*>::ConstIterator it = children.begin(); it != end; ++it) + body.appendChild( (*it)->toOPML(body, doc) ); + + return doc; +} + +} // namespace Akregator +#include "feedlist.moc" diff --git a/akregator/src/feedlist.h b/akregator/src/feedlist.h new file mode 100644 index 000000000..5562e8c4a --- /dev/null +++ b/akregator/src/feedlist.h @@ -0,0 +1,102 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATORFEEDLIST_H +#define AKREGATORFEEDLIST_H + +#include "nodelist.h" + +class QDomDocument; +class QDomNode; +class QString; + +namespace Akregator +{ + +class Article; +class Feed; +class Folder; +class TreeNode; + +/** The model of a feed tree, represents an OPML document. Contains an additional root node "All Feeds" which isn't stored. Note that a node instance must not be in more than one FeedList at a time! When deleting the feed list, all contained nodes are deleted! */ + +class FeedList : public NodeList +{ +Q_OBJECT +public: + + FeedList(QObject *parent = 0, const char *name = 0); + + /** Destructor. Contained nodes are deleted! */ + ~FeedList(); + + /** appends another feed list as sub tree. The root node of @c list is ignored. NOTE: nodes are _moved_ from @c list to this feed list, not copied */ + + void append(FeedList* list, Folder* parent=0, TreeNode* after=0); + + /** reads an OPML document and appends the items to this list + @param doc the OPML document to parse + @return whether parsing was successful or not (TODO: make errors more detailed) + */ + virtual bool readFromXML(const QDomDocument& doc); + + /** exports the feed list as OPML. The root node ("All Feeds") is ignored! */ + virtual QDomDocument toXML() const; + + /** returns a feed object for a given feed URL. If the feed list does not contain a feed with @c url, NULL is returned. If it contains the same feed multiple times, any of the Feed objects is returned. */ + Feed* findByURL(const QString& feedURL) const; + + Article findArticle(const QString& feedURL, const QString& guid) const; + +signals: + + void signalDestroyed(FeedList*); + +protected: + + virtual void addNode(TreeNode* node, bool preserveID); + virtual void removeNode(TreeNode* node); + +public: // compat with KDE-3.x assertions, remove for KDE 4 +// private: + + void parseChildNodes(QDomNode &node, Folder* parent); + + // never call these + FeedList(const FeedList&) : NodeList() {} + FeedList& operator=(const FeedList&) { return *this; } + + friend class AddNodeVisitor; + class AddNodeVisitor; + + friend class RemoveNodeVisitor; + class RemoveNodeVisitor; + + class FeedListPrivate; + FeedListPrivate* d; +}; + +} + +#endif diff --git a/akregator/src/feedlistview.cpp b/akregator/src/feedlistview.cpp new file mode 100644 index 000000000..585e85098 --- /dev/null +++ b/akregator/src/feedlistview.cpp @@ -0,0 +1,1023 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "dragobjects.h" +#include "folder.h" +#include "folderitem.h" +#include "tagfolder.h" +#include "tagfolderitem.h" +#include "feedlistview.h" +#include "feed.h" +#include "feeditem.h" +#include "feedlist.h" +#include "tag.h" +#include "tagnode.h" +#include "tagnodeitem.h" +#include "tagnodelist.h" +#include "treenode.h" +#include "treenodeitem.h" +#include "treenodevisitor.h" + +#include <kdebug.h> +#include <kiconeffect.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmultipledrag.h> +#include <kstringhandler.h> +#include <kurldrag.h> + +#include <qfont.h> +#include <qheader.h> +#include <qpainter.h> +#include <qptrdict.h> +#include <qtimer.h> +#include <qwhatsthis.h> + +namespace Akregator { + +class NodeListView::NodeListViewPrivate +{ + public: +/** used for finding the item belonging to a node */ + QPtrDict<TreeNodeItem> itemDict; + NodeList* nodeList; + bool showTagFolders; + + // Drag and Drop variables + QListViewItem *parent; + QListViewItem *afterme; + QTimer autoopentimer; + ConnectNodeVisitor* connectNodeVisitor; + DisconnectNodeVisitor* disconnectNodeVisitor; + CreateItemVisitor* createItemVisitor; + DeleteItemVisitor* deleteItemVisitor; +}; + +class NodeListView::ConnectNodeVisitor : public TreeNodeVisitor +{ + public: + ConnectNodeVisitor(NodeListView* view) : m_view(view) {} + + virtual bool visitTreeNode(TreeNode* node) + { + connect(node, SIGNAL(signalDestroyed(TreeNode*)), m_view, SLOT(slotNodeDestroyed(TreeNode*) )); + connect(node, SIGNAL(signalChanged(TreeNode*)), m_view, SLOT(slotNodeChanged(TreeNode*) )); + return true; + } + + virtual bool visitFolder(Folder* node) + { + visitTreeNode(node); + connect(node, SIGNAL(signalChildAdded(TreeNode*)), m_view, SLOT(slotNodeAdded(TreeNode*) )); + connect(node, SIGNAL(signalChildRemoved(Folder*, TreeNode*)), m_view, SLOT(slotNodeRemoved(Folder*, TreeNode*) )); + return true; + } + + virtual bool visitFeed(Feed* node) + { + visitTreeNode(node); + + connect(node, SIGNAL(fetchStarted(Feed*)), m_view, SLOT(slotFeedFetchStarted(Feed*))); + connect(node, SIGNAL(fetchAborted(Feed*)), m_view, SLOT(slotFeedFetchAborted(Feed*))); + connect(node, SIGNAL(fetchError(Feed*)), m_view, SLOT(slotFeedFetchError(Feed*))); + connect(node, SIGNAL(fetched(Feed*)), m_view, SLOT(slotFeedFetchCompleted(Feed*))); + return true; + } + private: + + NodeListView* m_view; + +}; + +class NodeListView::DisconnectNodeVisitor : public TreeNodeVisitor +{ + public: + DisconnectNodeVisitor(NodeListView* view) : m_view(view) {} + + virtual bool visitTagNode(TagNode* node) + { + disconnect(node, SIGNAL(signalDestroyed(TreeNode*)), m_view, SLOT(slotNodeDestroyed(TreeNode*) )); + disconnect(node, SIGNAL(signalChanged(TreeNode*)), m_view, SLOT(slotNodeChanged(TreeNode*) )); + return true; + } + + virtual bool visitFolder(Folder* node) + { + disconnect(node, SIGNAL(signalChildAdded(TreeNode*)), m_view, SLOT(slotNodeAdded(TreeNode*) )); + disconnect(node, SIGNAL(signalChildRemoved(Folder*, TreeNode*)), m_view, SLOT(slotNodeRemoved(Folder*, TreeNode*) )); + + disconnect(node, SIGNAL(signalDestroyed(TreeNode*)), m_view, SLOT(slotNodeDestroyed(TreeNode*) )); + disconnect(node, SIGNAL(signalChanged(TreeNode*)), m_view, SLOT(slotNodeChanged(TreeNode*) )); + return true; + } + + virtual bool visitFeed(Feed* node) + { + + disconnect(node, SIGNAL(signalDestroyed(TreeNode*)), m_view, SLOT(slotNodeDestroyed(TreeNode*) )); + disconnect(node, SIGNAL(signalChanged(TreeNode*)), m_view, SLOT(slotNodeChanged(TreeNode*) )); + disconnect(node, SIGNAL(fetchStarted(Feed*)), m_view, SLOT(slotFeedFetchStarted(Feed*))); + disconnect(node, SIGNAL(fetchAborted(Feed*)), m_view, SLOT(slotFeedFetchAborted(Feed*))); + disconnect(node, SIGNAL(fetchError(Feed*)), m_view, SLOT(slotFeedFetchError(Feed*))); + disconnect(node, SIGNAL(fetched(Feed*)), m_view, SLOT(slotFeedFetchCompleted(Feed*))); + return true; + } + private: + + NodeListView* m_view; +}; + +class NodeListView::DeleteItemVisitor : public TreeNodeVisitor +{ + public: + + DeleteItemVisitor(NodeListView* view) : m_view(view) {} + + virtual bool visitTreeNode(TreeNode* node) + { + TreeNodeItem* item = m_view->d->itemDict.take(node); + + if (!item) + return true; + + if ( m_selectNeighbour && item->isSelected() ) + { + if (item->itemBelow()) + m_view->setSelected(item->itemBelow(), true); + else if (item->itemAbove()) + m_view->setSelected(item->itemAbove(), true); + else + m_view->setSelected(item, false); + } + + m_view->disconnectFromNode(node); + delete item; + return true; + + } + + virtual bool visitFolder(Folder* node) + { + // delete child items recursively before deleting parent + QValueList<TreeNode*> children = node->children(); + for (QValueList<TreeNode*>::ConstIterator it = children.begin(); it != children.end(); ++it ) + visit(*it); + + visitTreeNode(node); + + return true; + } + + void deleteItem(TreeNode* node, bool selectNeighbour) + { + m_selectNeighbour = selectNeighbour; + visit(node); + } + + private: + NodeListView* m_view; + bool m_selectNeighbour; +}; + +class NodeListView::CreateItemVisitor : public TreeNodeVisitor +{ + public: + CreateItemVisitor(NodeListView* view) : m_view(view) {} + + virtual bool visitTagNode(TagNode* node) + { + if (m_view->findNodeItem(node)) + return true; + + TagNodeItem* item = 0; + TreeNode* prev = node->prevSibling(); + FolderItem* parentItem = static_cast<FolderItem*>(m_view->findNodeItem(node->parent())); + if (parentItem) + { + if (prev) + { + item = new TagNodeItem( parentItem, m_view->findNodeItem(prev), node); + } + else + item = new TagNodeItem( parentItem, node); + } + else + { + if (prev) + { + item = new TagNodeItem(m_view, m_view->findNodeItem(prev), node); + } + else + item = new TagNodeItem(m_view, node); + } + item->nodeChanged(); + m_view->d->itemDict.insert(node, item); + m_view->connectToNode(node); + if (parentItem) + parentItem->sortChildItems(0, true); + return true; + } + + virtual bool visitTagFolder(TagFolder* node) + { + if (m_view->findNodeItem(node)) + return true; + + TagFolderItem* item = 0; + TreeNode* prev = node->prevSibling(); + FolderItem* parentItem = static_cast<FolderItem*>(m_view->findNodeItem(node->parent())); + if (parentItem) + { + if (prev) + { + item = new TagFolderItem( parentItem, m_view->findNodeItem(prev), node); + } + else + item = new TagFolderItem(parentItem, node); + } + else + { + if (prev) + { + item = new TagFolderItem(m_view, m_view->findNodeItem(prev), node); + } + else + item = new TagFolderItem(m_view, node); + + } + m_view->d->itemDict.insert(node, item); + QValueList<TreeNode*> children = node->children(); + + // add children recursively + for (QValueList<TreeNode*>::ConstIterator it = children.begin(); it != children.end(); ++it ) + visit(*it); + + m_view->connectToNode(node); + return true; + } + + virtual bool visitFolder(Folder* node) + { + if (m_view->findNodeItem(node)) + return true; + + FolderItem* item = 0; + TreeNode* prev = node->prevSibling(); + FolderItem* parentItem = static_cast<FolderItem*>(m_view->findNodeItem(node->parent())); + if (parentItem) + { + if (prev) + { + item = new FolderItem( parentItem, m_view->findNodeItem(prev), node); + } + else + item = new FolderItem(parentItem, node); + } + else + { + if (prev) + { + item = new FolderItem(m_view, m_view->findNodeItem(prev), node); + } + else + item = new FolderItem(m_view, node); + } + m_view->d->itemDict.insert(node, item); + + // add children recursively + QValueList<TreeNode*> children = node->children(); + for (QValueList<TreeNode*>::ConstIterator it = children.begin(); it != children.end(); ++it ) + visit(*it); + + m_view->connectToNode(node); + return true; + } + + virtual bool visitFeed(Feed* node) + { + if (m_view->findNodeItem(node)) + return true; + + FeedItem* item = 0; + TreeNode* prev = node->prevSibling(); + FolderItem* parentItem = static_cast<FolderItem*>(m_view->findNodeItem(node->parent())); + + if (parentItem) + { + if (prev) + { + item = new FeedItem( parentItem, m_view->findNodeItem(prev), node); + } + else + item = new FeedItem( parentItem, node); + } + else + { + if (prev) + { + item = new FeedItem(m_view, m_view->findNodeItem(prev), node); + } + else + item = new FeedItem(m_view, node); + } + + item->nodeChanged(); + m_view->d->itemDict.insert(node, item); + m_view->connectToNode(node); + return true; + } + + private: + NodeListView* m_view; +}; + +NodeListView::NodeListView( QWidget *parent, const char *name) + : KListView(parent, name), d(new NodeListViewPrivate) +{ + d->showTagFolders = true; + d->connectNodeVisitor = new ConnectNodeVisitor(this), + d->disconnectNodeVisitor = new DisconnectNodeVisitor(this); + d->createItemVisitor = new CreateItemVisitor(this); + d->deleteItemVisitor = new DeleteItemVisitor(this); + + setMinimumSize(150, 150); + addColumn(i18n("Feeds")); + setRootIsDecorated(false); + setItemsRenameable(false); // NOTE: setting this this to true collides with setRenameEnabled() in items and breaks in-place renaming in strange ways. Do not enable! + setItemMargin(2); + + setFullWidth(true); + setSorting(-1); + setDragAutoScroll(true); + setDropVisualizer(true); + //setDropHighlighter(false); + + setDragEnabled(true); + setAcceptDrops(true); + setItemsMovable(true); + + connect( this, SIGNAL(dropped(QDropEvent*, QListViewItem*)), this, SLOT(slotDropped(QDropEvent*, QListViewItem*)) ); + connect( this, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(slotSelectionChanged(QListViewItem*)) ); + connect( this, SIGNAL(itemRenamed(QListViewItem*, int, const QString&)), this, SLOT(slotItemRenamed(QListViewItem*, int, const QString&)) ); + connect( this, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)), this, SLOT(slotContextMenu(KListView*, QListViewItem*, const QPoint&)) ); + connect( &(d->autoopentimer), SIGNAL( timeout() ), this, SLOT( openFolder() ) ); + + clear(); + + QWhatsThis::add(this, i18n("<h2>Feeds tree</h2>" + "Here you can browse tree of feeds. " + "You can also add feeds or feed groups (folders) " + "using right-click menu, or reorganize them using " + "drag and drop.")); + setUpdatesEnabled(true); +} + +NodeListView::~NodeListView() +{ + delete d->connectNodeVisitor; + delete d->disconnectNodeVisitor; + delete d->createItemVisitor; + delete d->deleteItemVisitor; + delete d; + d = 0; +} + +void NodeListView::setNodeList(NodeList* nodeList) +{ + if (nodeList == d->nodeList) + return; + + clear(); + + disconnectFromNodeList(d->nodeList); + + if (!nodeList) + return; + + d->nodeList = nodeList; + connectToNodeList(nodeList); + + + Folder* rootNode = nodeList->rootNode(); + if (!rootNode) + return; + + slotNodeAdded(rootNode); + slotRootNodeChanged(rootNode); +} + +Folder* NodeListView::rootNode() +{ + return d->nodeList ? d->nodeList->rootNode() : 0; +} + +TreeNode* NodeListView::selectedNode() +{ + TreeNodeItem* item = dynamic_cast<TreeNodeItem*> (selectedItem()); + + return ( item ? item->node() : 0) ; +} + +void NodeListView::setSelectedNode(TreeNode* node) +{ + TreeNodeItem* item = findNodeItem(node); + if ( node && item ) + setSelected(item, true); +} + +TreeNode* NodeListView::findNodeByTitle(const QString& title) +{ + TreeNodeItem* item = dynamic_cast<TreeNodeItem*>(findItemByTitle(title, 0)); + if (!item) + return 0; + else + return item->node(); +} + +TreeNodeItem* NodeListView::findNodeItem(TreeNode* node) +{ + return d->itemDict.find(node); +} + +TreeNodeItem* NodeListView::findItemByTitle(const QString& text, int column, ComparisonFlags compare) const +{ + return dynamic_cast<TreeNodeItem*> (KListView::findItem(text, column, compare)); +} + +void NodeListView::ensureNodeVisible(TreeNode* node) +{ + ensureItemVisible(findNodeItem(node)); +} + +void NodeListView::startNodeRenaming(TreeNode* node) +{ + TreeNodeItem* item = findNodeItem(node); + if (item) + { + item->startRename(0); + } +} + +void NodeListView::clear() +{ + QPtrDictIterator<TreeNodeItem> it(d->itemDict); + for( ; it.current(); ++it ) + disconnectFromNode( it.current()->node() ); + d->itemDict.clear(); + d->nodeList = 0; + + KListView::clear(); +} + +void NodeListView::drawContentsOffset( QPainter * p, int ox, int oy, + int cx, int cy, int cw, int ch ) +{ + bool oldUpdatesEnabled = isUpdatesEnabled(); + setUpdatesEnabled(false); + KListView::drawContentsOffset( p, ox, oy, cx, cy, cw, ch ); + setUpdatesEnabled(oldUpdatesEnabled); +} + +void NodeListView::slotDropped( QDropEvent *e, QListViewItem* +/*after*/) +{ + d->autoopentimer.stop(); + + if (e->source() != viewport()) + { + openFolder(); + + if (KURLDrag::canDecode(e)) + { + FolderItem* parent = dynamic_cast<FolderItem*> (d->parent); + TreeNodeItem* afterMe = 0; + + if(d->afterme) + afterMe = dynamic_cast<TreeNodeItem*> (d->afterme); + + KURL::List urls; + KURLDrag::decode( e, urls ); + e->accept(); + emit signalDropped( urls, afterMe ? afterMe->node() : 0, parent ? parent->node() : 0); + } + } + else + { + } +} + +void NodeListView::movableDropEvent(QListViewItem* /*parent*/, QListViewItem* /*afterme*/) +{ + d->autoopentimer.stop(); + if (d->parent) + { + openFolder(); + + Folder* parentNode = (dynamic_cast<FolderItem*> (d->parent))->node(); + TreeNode* afterMeNode = 0; + TreeNode* current = selectedNode(); + + if (d->afterme) + afterMeNode = (dynamic_cast<TreeNodeItem*> (d->afterme))->node(); + + current->parent()->removeChild(current); + parentNode->insertChild(current, afterMeNode); + KListView::movableDropEvent(d->parent, d->afterme); + } +} + +void NodeListView::setShowTagFolders(bool enabled) +{ + d->showTagFolders = enabled; +} + +void NodeListView::contentsDragMoveEvent(QDragMoveEvent* event) +{ + QPoint vp = contentsToViewport(event->pos()); + QListViewItem *i = itemAt(vp); + + QListViewItem *qiparent; + QListViewItem *qiafterme; + findDrop( event->pos(), qiparent, qiafterme ); + + if (event->source() == viewport()) { + // disable any drops where the result would be top level nodes + if (i && !i->parent()) + { + event->ignore(); + d->autoopentimer.stop(); + return; + } + + // prevent dragging nodes from All Feeds to My Tags or vice versa + QListViewItem* root1 = i; + while (root1 && root1->parent()) + root1 = root1->parent(); + + QListViewItem* root2 = selectedItem(); + while (root2 && root2->parent()) + root2 = root2->parent(); + + if (root1 != root2) + { + event->ignore(); + d->autoopentimer.stop(); + return; + } + + // don't drop node into own subtree + QListViewItem* p = qiparent; + while (p) + if (p == selectedItem()) + { + event->ignore(); + d->autoopentimer.stop(); + return; + } + else + { + p = p->parent(); + } + + // disable drags onto the item itself + if (selectedItem() == i) + { + event->ignore(); + d->autoopentimer.stop(); + return; + } + } + + // what the hell was this good for? -fo + // if (!i || event->pos().x() > header()->cellPos(header()->mapToIndex(0)) + + // treeStepSize() * (i->depth() + 1) + itemMargin() || + // event->pos().x() < header()->cellPos(header()->mapToIndex(0))) + // {} else + + // do we want to move inside the old parent or do we want to move to a new parent + if (i && (itemAt(vp - QPoint(0,5)) == i && itemAt(vp + QPoint(0,5)) == i)) + { + setDropVisualizer(false); + setDropHighlighter(true); + cleanDropVisualizer(); + + TreeNode *iNode = (dynamic_cast<TreeNodeItem*> (i))->node(); + if (iNode->isGroup()) + { + if (i != d->parent) + d->autoopentimer.start(750); + + d->parent = i; + d->afterme = 0; + } + else + { + event->ignore(); + d->autoopentimer.stop(); + d->afterme = i; + return; + } + } + else + { + setDropVisualizer(true); + setDropHighlighter(false); + cleanItemHighlighter(); + d->parent = qiparent; + d->afterme = qiafterme; + d->autoopentimer.stop(); + } + + // the rest is handled by KListView. + KListView::contentsDragMoveEvent(event); +} + +bool NodeListView::acceptDrag(QDropEvent *e) const +{ + if (!acceptDrops() || !itemsMovable()) + return false; + + if (e->source() != viewport()) + { + return KURLDrag::canDecode(e); + } + else + { + // disable dragging of top-level nodes (All Feeds, My Tags) + if (selectedItem() && !selectedItem()->parent()) + return false; + else + return true; + } + + return true; +} + +void NodeListView::slotItemUp() +{ + if (selectedItem() && selectedItem()->itemAbove()) + { + setSelected( selectedItem()->itemAbove(), true ); + ensureItemVisible(selectedItem()); + } +} + +void NodeListView::slotItemDown() +{ + if (selectedItem() && selectedItem()->itemBelow()) + { + setSelected( selectedItem()->itemBelow(), true ); + ensureItemVisible(selectedItem()); + } +} + +void NodeListView::slotItemBegin() +{ + setSelected( firstChild(), true ); + ensureItemVisible(firstChild()); +} + +void NodeListView::slotItemEnd() +{ + QListViewItem* elt = firstChild(); + if (elt) + while (elt->itemBelow()) + elt = elt->itemBelow(); + setSelected( elt, true ); + ensureItemVisible(elt); +} + +void NodeListView::slotItemLeft() +{ + QListViewItem* sel = selectedItem(); + + if (!sel || sel == findNodeItem(rootNode())) + return; + + if (sel->isOpen()) + sel->setOpen(false); + else + { + if (sel->parent()) + setSelected( sel->parent(), true ); + } + + ensureItemVisible( selectedItem() ); +} + +void NodeListView::slotItemRight() +{ + QListViewItem* sel = selectedItem(); + if (!sel) + { + setSelected( firstChild(), true ); + sel = firstChild(); + } + if (sel->isExpandable() && !sel->isOpen()) + sel->setOpen(true); + else + { + if (sel->firstChild()) + setSelected( sel->firstChild(), true ); + } + ensureItemVisible( selectedItem() ); +} + +void NodeListView::slotPrevFeed() +{ + for (QListViewItemIterator it( selectedItem()); it.current(); --it ) + { + TreeNodeItem* tni = dynamic_cast<TreeNodeItem*>(*it); + if (tni && !tni->isSelected() && !tni->node()->isGroup() ) + { + setSelected(tni, true); + ensureItemVisible(tni); + return; + } + } +} + +void NodeListView::slotNextFeed() +{ + for (QListViewItemIterator it( selectedItem()); it.current(); ++it ) + { + TreeNodeItem* tni = dynamic_cast<TreeNodeItem*>(*it); + if ( tni && !tni->isSelected() && !tni->node()->isGroup() ) + { + setSelected(tni, true); + ensureItemVisible(tni); + return; + } + } +} + +void NodeListView::slotPrevUnreadFeed() +{ + if (!firstChild() || !firstChild()->firstChild()) + return; + if ( !selectedItem() ) + slotNextUnreadFeed(); + + QListViewItemIterator it( selectedItem() ); + + for ( ; it.current(); --it ) + { + TreeNodeItem* tni = dynamic_cast<TreeNodeItem*> (it.current()); + if (!tni) + break; + if ( !tni->isSelected() && !tni->node()->isGroup() && tni->node()->unread() > 0) + { + setSelected(tni, true); + ensureItemVisible(tni); + return; + } + } + // reached when there is no unread feed above the selected one + // => cycle: go to end of list... + if (rootNode()->unread() > 0) + { + + it = QListViewItemIterator(lastItem()); + + for ( ; it.current(); --it) + { + + TreeNodeItem* tni = dynamic_cast<TreeNodeItem*> (it.current()); + + if (!tni) + break; + + if (!tni->isSelected() && !tni->node()->isGroup() && tni->node()->unread() > 0) + { + setSelected(tni, true); + ensureItemVisible(tni); + return; + } + } + } +} + +void NodeListView::slotNextUnreadFeed() +{ + QListViewItemIterator it; + + if ( !selectedItem() ) + { + // if all feeds doesnt exists or is empty, return + if (!firstChild() || !firstChild()->firstChild()) + return; + else + it = QListViewItemIterator( firstChild()->firstChild()); + } + else + it = QListViewItemIterator( selectedItem() ); + + for ( ; it.current(); ++it ) + { + TreeNodeItem* tni = dynamic_cast<TreeNodeItem*> (it.current()); + if (!tni) + break; + if ( !tni->isSelected() && !tni->node()->isGroup() && tni->node()->unread() > 0) + { + setSelected(tni, true); + ensureItemVisible(tni); + return; + } + } + // if reached, we are at the end of the list++ + if (rootNode()->unread() > 0) + { + clearSelection(); + slotNextUnreadFeed(); + } +} + +void NodeListView::slotSelectionChanged(QListViewItem* item) +{ + TreeNodeItem* ni = dynamic_cast<TreeNodeItem*> (item); + + if (ni) + { + emit signalNodeSelected(ni->node()); + } +} + +void NodeListView::slotItemRenamed(QListViewItem* item, int col, const QString& text) +{ + TreeNodeItem* ni = dynamic_cast<TreeNodeItem*> (item); + if ( !ni || !ni->node() ) + return; + if (col == 0) + { + if (text != ni->node()->title()) + { + ni->node()->setTitle(text); + } + } +} +void NodeListView::slotContextMenu(KListView* list, QListViewItem* item, const QPoint& p) +{ + TreeNodeItem* ti = dynamic_cast<TreeNodeItem*>(item); + emit signalContextMenu(list, ti ? ti->node() : 0, p); + if (ti) + ti->showContextMenu(p); +} + +void NodeListView::slotFeedFetchStarted(Feed* feed) +{ + // Disable icon to show it is fetching. + if (!feed->favicon().isNull()) + { + TreeNodeItem* item = findNodeItem(feed); + if (item) + { + KIconEffect iconEffect; + QPixmap tempIcon = iconEffect.apply(feed->favicon(), KIcon::Small, KIcon::DisabledState); + item->setPixmap(0, tempIcon); + } + } + +} + +void NodeListView::slotFeedFetchAborted(Feed* feed) +{ + TreeNodeItem* item = findNodeItem(feed); + if (item) + item->nodeChanged(); +} + +void NodeListView::slotFeedFetchError(Feed* feed) +{ + TreeNodeItem* item = findNodeItem(feed); + if (item) + item->nodeChanged(); +} + +void NodeListView::slotFeedFetchCompleted(Feed* feed) +{ + TreeNodeItem* item = findNodeItem(feed); + if (item) + item->nodeChanged(); +} + +void NodeListView::slotNodeAdded(TreeNode* node) +{ + if (node) + d->createItemVisitor->visit(node); +} + +void NodeListView::slotNodeRemoved(Folder* /*parent*/, TreeNode* node) +{ + if (node) + d->deleteItemVisitor->deleteItem(node, false); +} + +void NodeListView::connectToNode(TreeNode* node) +{ + if (node) + d->connectNodeVisitor->visit(node); +} + +void NodeListView::connectToNodeList(NodeList* list) +{ + if (!list) + return; + + connect(list, SIGNAL(signalDestroyed(NodeList*)), this, SLOT(slotNodeListDestroyed(NodeList*)) ); + connect(list->rootNode(), SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotRootNodeChanged(TreeNode*))); +} + +void NodeListView::disconnectFromNodeList(NodeList* list) +{ + if (!list) + return; + + disconnect(list, SIGNAL(signalDestroyed(NodeList*)), this, SLOT(slotNodeListDestroyed(NodeList*)) ); + disconnect(list->rootNode(), SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotRootNodeChanged(TreeNode*))); +} + +void NodeListView::disconnectFromNode(TreeNode* node) +{ + if (node) + d->disconnectNodeVisitor->visit(node); +} + +void NodeListView::slotNodeListDestroyed(NodeList* list) +{ + if (list != d->nodeList) + return; + + setNodeList(0); +} + +void NodeListView::slotNodeDestroyed(TreeNode* node) +{ + if (node) + d->deleteItemVisitor->deleteItem(node, true); +} + +void NodeListView::slotRootNodeChanged(TreeNode* rootNode) +{ + emit signalRootNodeChanged(this, rootNode); +} + +void NodeListView::slotNodeChanged(TreeNode* node) +{ + TreeNodeItem* item = findNodeItem(node); + if (item) + { + item->nodeChanged(); + triggerUpdate(); + } +} + +QDragObject *NodeListView::dragObject() +{ + KMultipleDrag *md = new KMultipleDrag(viewport()); + QDragObject *obj = KListView::dragObject(); + if (obj) { + md->addDragObject(obj); + } + TreeNodeItem *i = dynamic_cast<TreeNodeItem*>(currentItem()); + if (i) { + md->setPixmap(*(i->pixmap(0))); + FeedItem *fi = dynamic_cast<FeedItem*>(i); + if (fi) { + md->addDragObject(new KURLDrag(KURL(fi->node()->xmlUrl()), 0L)); + } + } + return md; +} + +void NodeListView::openFolder() { + d->autoopentimer.stop(); + if (d->parent && !d->parent->isOpen()) + { + d->parent->setOpen(true); + } +} + +} // namespace Akregator + +#include "feedlistview.moc" diff --git a/akregator/src/feedlistview.h b/akregator/src/feedlistview.h new file mode 100644 index 000000000..bf31574d8 --- /dev/null +++ b/akregator/src/feedlistview.h @@ -0,0 +1,206 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef AKREGATORFEEDLISTVIEW_H +#define AKREGATORFEEDLISTVIEW_H + +#include <klistview.h> +#include <kurl.h> + +namespace Akregator +{ +class Feed; +class Folder; +class NodeList; +class TreeNode; +class TreeNodeItem; +class TagNodeList; + +class NodeListView : public KListView +{ +Q_OBJECT +public: + NodeListView( QWidget *parent = 0, const char *name = 0 ); + virtual ~NodeListView(); + + /** sets the feed list to show. Disconnects from the old feed list, if there is any. */ + void setNodeList(NodeList* nodeList); + + /** Returns root node ("All Feeds"). + * @return root node + */ + Folder* rootNode(); + + /** Returns the currently selected node, @c null when no one is selected. + @return selected node + */ + TreeNode* selectedNode(); + + /** selects @c node, if it exists + * @param node the node to select + */ + void setSelectedNode(TreeNode* node); + + /** Find first node with title @c title + returns 0 if no node was found + @param title + @return node + */ + TreeNode* findNodeByTitle(const QString& title); + + /** ensures that @c node is visible. */ + void ensureNodeVisible(TreeNode* node); + + /** activates in-place renaming for the item of @c node */ + void startNodeRenaming(TreeNode* node); + + + /** reimplemented: clears the view and creates the root node ("All Feeds") */ + virtual void clear(); + + /** if enabled, the view shows tag folders */ + void setShowTagFolders(bool enabled); + +public slots: + + /** go one item up */ + void slotItemUp(); + /** go one item down */ + void slotItemDown(); + /** select the first item in the list */ + void slotItemBegin(); + /** select last item in the list */ + void slotItemEnd(); + /** go to parent item */ + void slotItemLeft(); + /** go to first child */ + void slotItemRight(); + + void slotPrevFeed(); + void slotNextFeed(); + void slotPrevUnreadFeed(); + void slotNextUnreadFeed(); + +signals: + void signalDropped (KURL::List &, TreeNode*, Folder*); + void signalNodeSelected(TreeNode*); + void signalRootNodeChanged(NodeListView*, TreeNode*); + void signalContextMenu(KListView*, TreeNode*, const QPoint&); + +public: // compat with KDE-3.x assertions, remove for KDE 4 +// protected: + + /** Find item belonging to tree node @c node, @c null when node is not in tree + @return item representing node + @param node a tree node */ + + TreeNodeItem* findNodeItem(TreeNode* node); + + /** reimplemented to return TreeNodeItem* */ + virtual TreeNodeItem* findItemByTitle(const QString& text, int column, ComparisonFlags compare = ExactMatch | CaseSensitive ) const; + + /** observe @c node: connect status change signals of @c node to slots */ + virtual void connectToNode(TreeNode* node); + + /** stop observing @c node: disconnect from status change signals of @c node */ + virtual void disconnectFromNode(TreeNode* node); + + virtual void connectToNodeList(NodeList* list); + virtual void disconnectFromNodeList(NodeList* list); + + virtual void drawContentsOffset( QPainter * p, int ox, int oy, + int cx, int cy, int cw, int ch ); + virtual void contentsDragMoveEvent(QDragMoveEvent* event); + virtual bool acceptDrag(QDropEvent *event) const; + virtual void movableDropEvent(QListViewItem* parent, QListViewItem* afterme); + + virtual QDragObject *dragObject(); + + +protected slots: + + + void slotDropped(QDropEvent *e, QListViewItem* after); + void slotRootNodeChanged(TreeNode*); + virtual void slotSelectionChanged(QListViewItem* item); + virtual void slotContextMenu(KListView* list, QListViewItem* item, const QPoint& p); + virtual void slotItemRenamed(QListViewItem* item, int col, const QString& text); + virtual void slotFeedFetchStarted(Feed* feed); + virtual void slotFeedFetchAborted(Feed* feed); + virtual void slotFeedFetchError(Feed* feed); + virtual void slotFeedFetchCompleted(Feed* feed); + void openFolder(); + + /** called when a node is added to the tree. If no item for the node exists, it will be created */ + virtual void slotNodeAdded(TreeNode* node); + + /** Called when a node in the tree is taken out of the tree (parent->removeChild()) + + Removes a node and its children from the tree. Note that it doesn't delete the corresponding view items (get deleted only when the node itself gets deleted) */ + virtual void slotNodeRemoved(Folder* parent, TreeNode* node); + + /** deletes the item belonging to the deleted node */ + virtual void slotNodeDestroyed(TreeNode* node); + + /** update the item belonging to the node */ + virtual void slotNodeChanged(TreeNode* node); + + virtual void slotNodeListDestroyed(NodeList*); + +public: // compat with KDE-3.x assertions, remove for KDE 4 +// private: + friend class ConnectNodeVisitor; + class ConnectNodeVisitor; + + friend class DisconnectNodeVisitor; + class DisconnectNodeVisitor; + + friend class CreateItemVisitor; + class CreateItemVisitor; + + friend class DeleteItemVisitor; + class DeleteItemVisitor; + + friend class DragAndDropVisitor; + class DragAndDropVisitor; + + class NodeListViewPrivate; + NodeListViewPrivate* d; +}; + + +class TagNodeListView : public NodeListView +{ + Q_OBJECT + public: + TagNodeListView(QWidget *parent = 0, const char *name = 0) {} + virtual ~TagNodeListView() {} + + private: + class TagNodeListViewPrivate; + TagNodeListViewPrivate* d; +}; + +} // namespace Akregator + +#endif diff --git a/akregator/src/feedstorage.h b/akregator/src/feedstorage.h new file mode 100644 index 000000000..eda8163d3 --- /dev/null +++ b/akregator/src/feedstorage.h @@ -0,0 +1,146 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef AKREGATOR_BACKEND_FEEDSTORAGE_H +#define AKREGATOR_BACKEND_FEEDSTORAGE_H + +#include <qobject.h> +#include <qstring.h> + +#include "akregator_export.h" + +class QStringList; + + +namespace Akregator { +namespace Backend { + +/** a convenience class to handle categories in the backend */ +class AKREGATOR_EXPORT Category +{ + public: + + QString term; + QString scheme; + QString name; + + /** two categories are equal when scheme and term are equal, name is ignored */ + + bool operator==(const Category& other) const + { + return term == other.term && scheme == other.scheme; + } + + bool operator!=(const Category& other) const + { + return !operator==(other); + } + /** we need this for QMaps */ + bool operator<(const Category& other) const + { + return other.scheme < other.scheme || (other.scheme == other.scheme && term < other.term); + } +}; + +class Storage; + +class AKREGATOR_EXPORT FeedStorage : public QObject +{ + public: + + virtual int unread() = 0; + virtual void setUnread(int unread) = 0; + virtual int totalCount() = 0; + virtual int lastFetch() = 0; + virtual void setLastFetch(int lastFetch) = 0; + + /** returns the guids of all articles in this storage. If a tagID is given, only articles with this tag are returned */ + virtual QStringList articles(const QString& tagID=QString::null) = 0; + + /** returns the guid of the articles in a given category */ + virtual QStringList articles(const Category& cat) = 0; + + /** Appends all articles from another storage. If there is already an article in this feed with the same guid, it is replaced by the article from the source + @param source the archive which articles should be appended + */ + virtual void add(FeedStorage* source) = 0; + + /** reads an article from another storage and adds it to this storage */ + virtual void copyArticle(const QString& guid, FeedStorage* source) = 0; + + /** deletes all articles from the archive */ + virtual void clear() = 0; + + + virtual bool contains(const QString& guid) = 0; + virtual void addEntry(const QString& guid) = 0; + virtual void deleteArticle(const QString& guid) = 0; + virtual int comments(const QString& guid) = 0; + virtual QString commentsLink(const QString& guid) = 0; + virtual void setCommentsLink(const QString& guid, const QString& commentsLink) = 0; + virtual void setComments(const QString& guid, int comments) = 0; + virtual bool guidIsHash(const QString& guid) = 0; + virtual void setGuidIsHash(const QString& guid, bool isHash) = 0; + virtual bool guidIsPermaLink(const QString& guid) = 0; + virtual void setGuidIsPermaLink(const QString& guid, bool isPermaLink) = 0; + virtual uint hash(const QString& guid) = 0; + virtual void setHash(const QString& guid, uint hash) = 0; + virtual void setDeleted(const QString& guid) = 0; + virtual QString link(const QString& guid) = 0; + virtual void setLink(const QString& guid, const QString& link) = 0; + virtual uint pubDate(const QString& guid) = 0; + virtual void setPubDate(const QString& guid, uint pubdate) = 0; + virtual int status(const QString& guid) = 0; + virtual void setStatus(const QString& guid, int status) = 0; + virtual QString title(const QString& guid) = 0; + virtual void setTitle(const QString& guid, const QString& title) = 0; + virtual QString description(const QString& guid) = 0; + virtual void setDescription(const QString& guid, const QString& description) = 0; + + virtual void addTag(const QString& guid, const QString& tag) = 0; + virtual void removeTag(const QString& guid, const QString& tag) = 0; + + /** returns the tags of a given article. If @c guid is null, it returns all tags used in this feed */ + virtual QStringList tags(const QString& guid=QString::null) = 0; + + virtual void addCategory(const QString& guid, const Category& category) = 0; + virtual QValueList<Category> categories(const QString& guid=QString::null) = 0; + + virtual void setEnclosure(const QString& guid, const QString& url, const QString& type, int length) = 0; + virtual void removeEnclosure(const QString& guid) = 0; + + virtual void setAuthor(const QString& /*guid*/, const QString& /*author*/) {} + virtual QString author(const QString& /*guid*/) { return QString(); } + + virtual void enclosure(const QString& guid, bool& hasEnclosure, QString& url, QString& type, int& length) = 0; + virtual void close() = 0; + virtual void commit() = 0; + virtual void rollback() = 0; + + virtual void convertOldArchive() = 0; +}; + +} +} + +#endif diff --git a/akregator/src/feedstoragedummyimpl.cpp b/akregator/src/feedstoragedummyimpl.cpp new file mode 100644 index 000000000..f1e194949 --- /dev/null +++ b/akregator/src/feedstoragedummyimpl.cpp @@ -0,0 +1,469 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "feedstoragedummyimpl.h" +#include "storagedummyimpl.h" + +#include <feed.h> + +#include <qmap.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> + +//typedef unsigned int uint; +namespace Akregator { +namespace Backend { + +class FeedStorageDummyImpl::FeedStorageDummyImplPrivate +{ + public: + class Entry + { + public: + Entry() : guidIsHash(false), guidIsPermaLink(false), status(0), pubDate(0), hash(0) {} + StorageDummyImpl* mainStorage; + QValueList<Category> categories; + QString title; + QString description; + QString link; + QString author; + QString commentsLink; + bool guidIsHash; + bool guidIsPermaLink; + int comments; + int status; + uint pubDate; + uint hash; + QStringList tags; + bool hasEnclosure; + QString enclosureUrl; + QString enclosureType; + int enclosureLength; + }; + QMap<QString, Entry> entries; + + // all tags occurring in the feed + QStringList tags; + + // tag -> articles index + QMap<QString, QStringList > taggedArticles; + + QValueList<Category> categories; + QMap<Category, QStringList> categorizedArticles; + + Storage* mainStorage; + QString url; +}; + + +void FeedStorageDummyImpl::convertOldArchive() +{ +} + +FeedStorageDummyImpl::FeedStorageDummyImpl(const QString& url, StorageDummyImpl* main) : d(new FeedStorageDummyImplPrivate) +{ + d->url = url; + d->mainStorage = main; +} + +FeedStorageDummyImpl::~FeedStorageDummyImpl() +{ + delete d; d = 0; +} + +void FeedStorageDummyImpl::commit() +{ +} + +void FeedStorageDummyImpl::rollback() +{ +} + +void FeedStorageDummyImpl::close() +{ +} + +int FeedStorageDummyImpl::unread() +{ + return d->mainStorage->unreadFor(d->url); +} + +void FeedStorageDummyImpl::setUnread(int unread) +{ + d->mainStorage->setUnreadFor(d->url, unread); +} + +int FeedStorageDummyImpl::totalCount() +{ + return d->mainStorage->totalCountFor(d->url); +} + +void FeedStorageDummyImpl::setTotalCount(int total) +{ + d->mainStorage->setTotalCountFor(d->url, total); +} + +int FeedStorageDummyImpl::lastFetch() +{ + return d->mainStorage->lastFetchFor(d->url); +} + +void FeedStorageDummyImpl::setLastFetch(int lastFetch) +{ + d->mainStorage->setLastFetchFor(d->url, lastFetch); +} + +QStringList FeedStorageDummyImpl::articles(const QString& tag) +{ + return tag.isNull() ? QStringList(d->entries.keys()) : d->taggedArticles[tag]; +} + +QStringList FeedStorageDummyImpl::articles(const Category& cat) +{ + return d->categorizedArticles[cat]; +} + +void FeedStorageDummyImpl::addEntry(const QString& guid) +{ + if (!d->entries.contains(guid)) + { + d->entries[guid] = FeedStorageDummyImplPrivate::Entry(); + setTotalCount(totalCount()+1); + } +} + +bool FeedStorageDummyImpl::contains(const QString& guid) +{ + return d->entries.contains(guid); +} + +void FeedStorageDummyImpl::deleteArticle(const QString& guid) +{ + if (!d->entries.contains(guid)) + return; + + setDeleted(guid); + + d->entries.remove(guid); +} + +int FeedStorageDummyImpl::comments(const QString& guid) +{ + + return contains(guid) ? d->entries[guid].comments : 0; +} + +QString FeedStorageDummyImpl::commentsLink(const QString& guid) +{ + return contains(guid) ? d->entries[guid].commentsLink : ""; +} + +bool FeedStorageDummyImpl::guidIsHash(const QString& guid) +{ + return contains(guid) ? d->entries[guid].guidIsHash : false; +} + +bool FeedStorageDummyImpl::guidIsPermaLink(const QString& guid) +{ + return contains(guid) ? d->entries[guid].guidIsPermaLink : false; +} + +uint FeedStorageDummyImpl::hash(const QString& guid) +{ + return contains(guid) ? d->entries[guid].hash : 0; +} + + +void FeedStorageDummyImpl::setDeleted(const QString& guid) +{ + if (!contains(guid)) + return; + + FeedStorageDummyImplPrivate::Entry entry = d->entries[guid]; + + // remove article from tag->article index + QStringList::ConstIterator it = entry.tags.begin(); + QStringList::ConstIterator end = entry.tags.end(); + + for ( ; it != end; ++it) + { + d->taggedArticles[*it].remove(guid); + if (d->taggedArticles[*it].count() == 0) + d->tags.remove(*it); + } + + // remove article from tag->category index + QValueList<Category>::ConstIterator it2 = entry.categories.begin(); + QValueList<Category>::ConstIterator end2 = entry.categories.end(); + + for ( ; it2 != end2; ++it2) + { + d->categorizedArticles[*it2].remove(guid); + if (d->categorizedArticles[*it2].count() == 0) + d->categories.remove(*it2); + } + + entry.description = ""; + entry.title = ""; + entry.link = ""; + entry.commentsLink = ""; +} + +QString FeedStorageDummyImpl::link(const QString& guid) +{ + return contains(guid) ? d->entries[guid].link : ""; +} + +uint FeedStorageDummyImpl::pubDate(const QString& guid) +{ + return contains(guid) ? d->entries[guid].pubDate : 0; +} + +int FeedStorageDummyImpl::status(const QString& guid) +{ + return contains(guid) ? d->entries[guid].status : 0; +} + +void FeedStorageDummyImpl::setStatus(const QString& guid, int status) +{ + if (contains(guid)) + d->entries[guid].status = status; +} + +QString FeedStorageDummyImpl::title(const QString& guid) +{ + return contains(guid) ? d->entries[guid].title : ""; +} + +QString FeedStorageDummyImpl::description(const QString& guid) +{ + return contains(guid) ? d->entries[guid].description : ""; +} + + +void FeedStorageDummyImpl::setPubDate(const QString& guid, uint pubdate) +{ + if (contains(guid)) + d->entries[guid].pubDate = pubdate; +} + +void FeedStorageDummyImpl::setGuidIsHash(const QString& guid, bool isHash) +{ + if (contains(guid)) + d->entries[guid].guidIsHash = isHash; +} + +void FeedStorageDummyImpl::setLink(const QString& guid, const QString& link) +{ + if (contains(guid)) + d->entries[guid].link = link; +} + +void FeedStorageDummyImpl::setHash(const QString& guid, uint hash) +{ + if (contains(guid)) + d->entries[guid].hash = hash; +} + +void FeedStorageDummyImpl::setTitle(const QString& guid, const QString& title) +{ + if (contains(guid)) + d->entries[guid].title = title; +} + +void FeedStorageDummyImpl::setDescription(const QString& guid, const QString& description) +{ + if (contains(guid)) + d->entries[guid].description = description; +} + +void FeedStorageDummyImpl::setCommentsLink(const QString& guid, const QString& commentsLink) +{ + if (contains(guid)) + d->entries[guid].commentsLink = commentsLink; +} + +void FeedStorageDummyImpl::setComments(const QString& guid, int comments) +{ + if (contains(guid)) + d->entries[guid].comments = comments; +} + + +void FeedStorageDummyImpl::setGuidIsPermaLink(const QString& guid, bool isPermaLink) +{ + if (contains(guid)) + d->entries[guid].guidIsPermaLink = isPermaLink; +} + +void FeedStorageDummyImpl::addTag(const QString& guid, const QString& tag) +{ + if (contains(guid)) + { + d->entries[guid].tags.append(tag); + if (!d->taggedArticles[tag].contains(guid)) + d->taggedArticles[tag].append(guid); + if (!d->tags.contains(tag)) + d->tags.append(tag); + } + +} + +void FeedStorageDummyImpl::addCategory(const QString& guid, const Category& cat) +{ + if (!contains(guid)) + return; + + d->entries[guid].categories.append(cat); + + if (d->categorizedArticles[cat].count() == 0) + d->categories.append(cat); + d->categorizedArticles[cat].append(guid); +} + +void FeedStorageDummyImpl::setAuthor(const QString& guid, const QString& author) +{ + if (contains(guid)) + d->entries[guid].author = author; +} + +QString FeedStorageDummyImpl::author(const QString& guid) +{ + return contains(guid) ? d->entries[guid].author : QString(); +} + +QValueList<Category> FeedStorageDummyImpl::categories(const QString& guid) +{ + if (!guid.isNull()) + return contains(guid) ? d->entries[guid].categories : QValueList<Category>(); + else + return d->categories; +} + + +void FeedStorageDummyImpl::removeTag(const QString& guid, const QString& tag) +{ + if (contains(guid)) + { + d->entries[guid].tags.remove(tag); + d->taggedArticles[tag].remove(guid); + if (d->taggedArticles[tag].count() == 0) + d->tags.remove(tag); + } +} + +QStringList FeedStorageDummyImpl::tags(const QString& guid) +{ + if (!guid.isNull()) + return contains(guid) ? d->entries[guid].tags : QStringList(); + else + { + return d->tags; + } +} + +void FeedStorageDummyImpl::add(FeedStorage* source) +{ + QStringList articles = source->articles(); + for (QStringList::ConstIterator it = articles.begin(); it != articles.end(); ++it) + copyArticle(*it, source); + setUnread(source->unread()); + setLastFetch(source->lastFetch()); + setTotalCount(source->totalCount()); +} + +void FeedStorageDummyImpl::copyArticle(const QString& guid, FeedStorage* source) +{ + if (!contains(guid)) + addEntry(guid); + + setComments(guid, source->comments(guid)); + setCommentsLink(guid, source->commentsLink(guid)); + setDescription(guid, source->description(guid)); + setGuidIsHash(guid, source->guidIsHash(guid)); + setGuidIsPermaLink(guid, source->guidIsPermaLink(guid)); + setHash(guid, source->hash(guid)); + setLink(guid, source->link(guid)); + setPubDate(guid, source->pubDate(guid)); + setStatus(guid, source->status(guid)); + setTitle(guid, source->title(guid)); + QStringList tags = source->tags(guid); + + for (QStringList::ConstIterator it = tags.begin(); it != tags.end(); ++it) + addTag(guid, *it); +} + +void FeedStorageDummyImpl::clear() +{ + d->entries.clear(); + setUnread(0); + setTotalCount(0); +} + +void FeedStorageDummyImpl::setEnclosure(const QString& guid, const QString& url, const QString& type, int length) +{ + if (contains(guid)) + { + FeedStorageDummyImplPrivate::Entry entry = d->entries[guid]; + entry.hasEnclosure = true; + entry.enclosureUrl = url; + entry.enclosureType = type; + entry.enclosureLength = length; + } +} + +void FeedStorageDummyImpl::removeEnclosure(const QString& guid) +{ + if (contains(guid)) + { + FeedStorageDummyImplPrivate::Entry entry = d->entries[guid]; + entry.hasEnclosure = false; + entry.enclosureUrl = QString::null; + entry.enclosureType = QString::null; + entry.enclosureLength = -1; + } +} + +void FeedStorageDummyImpl::enclosure(const QString& guid, bool& hasEnclosure, QString& url, QString& type, int& length) +{ + if (contains(guid)) + { + FeedStorageDummyImplPrivate::Entry entry = d->entries[guid]; + hasEnclosure = entry.hasEnclosure; + url = entry.enclosureUrl; + type = entry.enclosureType; + length = entry.enclosureLength; + } + else + { + hasEnclosure = false; + url = QString::null; + type = QString::null; + length = -1; + } +} + +} // namespace Backend +} // namespace Akregator diff --git a/akregator/src/feedstoragedummyimpl.h b/akregator/src/feedstoragedummyimpl.h new file mode 100644 index 000000000..563edb5c3 --- /dev/null +++ b/akregator/src/feedstoragedummyimpl.h @@ -0,0 +1,106 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef AKREGATOR_BACKEND_FEEDSTORAGEDUMMYIMPL_H +#define AKREGATOR_BACKEND_FEEDSTORAGEDUMMYIMPL_H + +#include "feedstorage.h" +namespace Akregator { +namespace Backend { + +class StorageDummyImpl; +class FeedStorageDummyImpl : public FeedStorage +{ + public: + FeedStorageDummyImpl(const QString& url, StorageDummyImpl* main); + virtual ~FeedStorageDummyImpl(); + + virtual void add(FeedStorage* source); + virtual void copyArticle(const QString& guid, FeedStorage* source); + + virtual void clear(); + virtual int unread(); + virtual void setUnread(int unread); + virtual int totalCount(); + virtual int lastFetch(); + virtual void setLastFetch(int lastFetch); + + virtual QStringList articles(const QString& tag=QString::null); + + virtual QStringList articles(const Category& cat); + + + virtual bool contains(const QString& guid); + virtual void addEntry(const QString& guid); + virtual void deleteArticle(const QString& guid); + virtual int comments(const QString& guid); + virtual QString commentsLink(const QString& guid); + virtual void setCommentsLink(const QString& guid, const QString& commentsLink); + virtual void setComments(const QString& guid, int comments); + virtual bool guidIsHash(const QString& guid); + virtual void setGuidIsHash(const QString& guid, bool isHash); + virtual bool guidIsPermaLink(const QString& guid); + virtual void setGuidIsPermaLink(const QString& guid, bool isPermaLink); + virtual uint hash(const QString& guid); + virtual void setHash(const QString& guid, uint hash); + virtual void setDeleted(const QString& guid); + virtual QString link(const QString& guid); + virtual void setLink(const QString& guid, const QString& link); + virtual uint pubDate(const QString& guid); + virtual void setPubDate(const QString& guid, uint pubdate); + virtual int status(const QString& guid); + virtual void setStatus(const QString& guid, int status); + virtual QString title(const QString& guid); + virtual void setTitle(const QString& guid, const QString& title); + virtual QString description(const QString& guid); + virtual void setDescription(const QString& guid, const QString& description); + virtual void addTag(const QString& guid, const QString& tag); + virtual void removeTag(const QString& guid, const QString& tag); + virtual QStringList tags(const QString& guid=QString::null); + + virtual void setEnclosure(const QString& guid, const QString& url, const QString& type, int length); + virtual void removeEnclosure(const QString& guid); + virtual void enclosure(const QString& guid, bool& hasEnclosure, QString& url, QString& type, int& length); + + virtual void addCategory(const QString& guid, const Category& category); + virtual QValueList<Category> categories(const QString& guid=QString::null); + + virtual void setAuthor(const QString& guid, const QString& author); + virtual QString author(const QString& guid); + + virtual void close(); + virtual void commit(); + virtual void rollback(); + + virtual void convertOldArchive(); + private: + /** finds article by guid, returns -1 if not in archive **/ + int findArticle(const QString& guid); + void setTotalCount(int total); + class FeedStorageDummyImplPrivate; + FeedStorageDummyImplPrivate* d; +}; + +} +} +#endif // AKREGATOR_FEEDSTORAGEDUMMYIMPL_H diff --git a/akregator/src/fetchqueue.cpp b/akregator/src/fetchqueue.cpp new file mode 100644 index 000000000..5a3634304 --- /dev/null +++ b/akregator/src/fetchqueue.cpp @@ -0,0 +1,160 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qvaluelist.h> + +#include "akregatorconfig.h" +#include "fetchqueue.h" +#include "feed.h" +#include "treenode.h" + + +namespace Akregator { + +class FetchQueue::FetchQueuePrivate +{ + public: + + QValueList<Feed*> queuedFeeds; + QValueList<Feed*> fetchingFeeds; +}; + + +FetchQueue::FetchQueue(QObject* parent, const char* name): QObject(parent, name), d(new FetchQueuePrivate) {} + + +FetchQueue::~FetchQueue() +{ + slotAbort(); + delete d; + d = 0; +} + +void FetchQueue::slotAbort() +{ + for (QValueList<Feed*>::Iterator it = d->fetchingFeeds.begin(); it != d->fetchingFeeds.end(); ++it) + { + disconnectFromFeed(*it); + (*it)->slotAbortFetch(); + } + d->fetchingFeeds.clear(); + + for (QValueList<Feed*>::Iterator it = d->queuedFeeds.begin(); it != d->queuedFeeds.end(); ++it) + { + disconnectFromFeed(*it); + } + d->queuedFeeds.clear(); + + emit signalStopped(); +} + +void FetchQueue::addFeed(Feed *f) +{ + if (!d->queuedFeeds.contains(f) && !d->fetchingFeeds.contains(f)) + { + connectToFeed(f); + d->queuedFeeds.append(f); + fetchNextFeed(); + } +} + +void FetchQueue::fetchNextFeed() +{ + if (!d->queuedFeeds.isEmpty() && d->fetchingFeeds.count() < Settings::concurrentFetches()) + { + if (d->fetchingFeeds.isEmpty() && d->queuedFeeds.count() == 1) + emit signalStarted(); + Feed* f = *(d->queuedFeeds.begin()); + d->queuedFeeds.pop_front(); + d->fetchingFeeds.append(f); + f->fetch(false); + + } +} + +void FetchQueue::slotFeedFetched(Feed *f) +{ + emit fetched(f); + feedDone(f); +} + +void FetchQueue::slotFetchError(Feed *f) +{ + emit fetchError(f); + feedDone(f); +} + +void FetchQueue::slotFetchAborted(Feed *f) +{ + emit fetched(f); // FIXME: better use a signal like signalAborted(Feed*) + feedDone(f); +} + +bool FetchQueue::isEmpty() const +{ + return d->queuedFeeds.isEmpty() && d->fetchingFeeds.isEmpty(); +} + +void FetchQueue::feedDone(Feed *f) +{ + disconnectFromFeed(f); + d->fetchingFeeds.remove(f); + if (isEmpty()) + emit signalStopped(); + else + fetchNextFeed(); +} + +void FetchQueue::connectToFeed(Feed* feed) +{ + connect (feed, SIGNAL(fetched(Feed*)), this, SLOT(slotFeedFetched(Feed*))); + connect (feed, SIGNAL(fetchError(Feed*)), this, SLOT(slotFetchError(Feed*))); + connect (feed, SIGNAL(fetchAborted(Feed*)), this, SLOT(slotFetchAborted(Feed*))); + connect (feed, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotNodeDestroyed(TreeNode*))); +} + +void FetchQueue::disconnectFromFeed(Feed* feed) +{ + disconnect (feed, SIGNAL(fetched(Feed*)), this, SLOT(slotFeedFetched(Feed*))); + disconnect (feed, SIGNAL(fetchError(Feed*)), this, SLOT(slotFetchError(Feed*))); + disconnect (feed, SIGNAL(fetchAborted(Feed*)), this, SLOT(slotFetchAborted(Feed*))); + disconnect (feed, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotNodeDestroyed(TreeNode*))); +} + + +void FetchQueue::slotNodeDestroyed(TreeNode* node) +{ + Feed* feed = dynamic_cast<Feed*> (node); + + if (feed) + { + d->fetchingFeeds.remove(feed); + d->queuedFeeds.remove(feed); + } +} + +} // namespace Akregator + +#include "fetchqueue.moc" diff --git a/akregator/src/fetchqueue.h b/akregator/src/fetchqueue.h new file mode 100644 index 000000000..f388344a6 --- /dev/null +++ b/akregator/src/fetchqueue.h @@ -0,0 +1,90 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_FETCHQUEUE_H +#define AKREGATOR_FETCHQUEUE_H + +#include <qobject.h> + +namespace Akregator { + +class Feed; +class TreeNode; + +class FetchQueue : public QObject +{ + Q_OBJECT + + public: + + FetchQueue(QObject* parent=0, const char* name=0); + virtual ~FetchQueue(); + + /** returns true when no feeds are neither fetching nor queued */ + bool isEmpty() const; + + /** adds a feed to the queue */ + void addFeed(Feed *f); + + public slots: + + /** aborts currently fetching feeds and empties the queue */ + void slotAbort(); + + signals: + + void signalStarted(); + void signalStopped(); + void fetched(Feed*); + void fetchError(Feed*); + + protected: + + /** fetches the next feed in the queue, unless the maximum of concurrent fetches is reached */ + void fetchNextFeed(); + + void feedDone(Feed *f); + void connectToFeed(Feed* feed); + void disconnectFromFeed(Feed* feed); + + protected slots: + + void slotNodeDestroyed(TreeNode* node); + void slotFeedFetched(Feed *); + void slotFetchError(Feed *); + void slotFetchAborted(Feed *); + + private: + + class FetchQueuePrivate; + FetchQueuePrivate* d; +}; + +} // namespace Akregator + +#endif + +// vim: set et ts=4 sts=4 sw=4: + diff --git a/akregator/src/folder.cpp b/akregator/src/folder.cpp new file mode 100644 index 000000000..f7dc8afbf --- /dev/null +++ b/akregator/src/folder.cpp @@ -0,0 +1,347 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2004-2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#include "article.h" +#include "folder.h" +#include "fetchqueue.h" +#include "treenodevisitor.h" + +#include <qlistview.h> +#include <qdom.h> +#include <qstringlist.h> +#include <qvaluelist.h> + +#include <kdebug.h> + +namespace Akregator { + +class Folder::FolderPrivate +{ + public: + /** List of children */ + QValueList<TreeNode*> children; + /** caching unread count of children */ + int unread; + /** whether or not the folder is expanded */ + bool open; + + /** caches guids for notifying added articles */ + QValueList<Article> addedArticlesNotify; + /** caches guids for notifying removed articles */ + QValueList<Article> removedArticlesNotify; +}; + +bool Folder::accept(TreeNodeVisitor* visitor) +{ + if (visitor->visitFolder(this)) + return true; + else + return visitor->visitTreeNode(this); +} + +Folder* Folder::fromOPML(QDomElement e) +{ + Folder* fg = new Folder(e.hasAttribute(QString::fromLatin1("text")) ? e.attribute(QString::fromLatin1("text")) : e.attribute(QString::fromLatin1("title"))); + fg->setOpen( e.attribute(QString::fromLatin1("isOpen")) != QString::fromLatin1(("false"))); + fg->setId( e.attribute(QString::fromLatin1("id")).toUInt() ); + return fg; +} + +Folder::Folder(const QString& title) : TreeNode(), d(new FolderPrivate) +{ + d->unread = 0; + setTitle(title); +} + +Folder::~Folder() +{ + TreeNode* tmp = 0; + for (QValueList<TreeNode*>::ConstIterator it = d->children.begin(); it != d->children.end(); ++it) + { + delete tmp; + tmp = *it; + } + delete tmp; + + emitSignalDestroyed(); + + delete d; + d = 0; +} + +QStringList Folder::tags() const +{ + QStringList t; + QValueList<TreeNode*>::ConstIterator en = d->children.end(); + for (QValueList<TreeNode*>::ConstIterator it = d->children.begin(); it != en; ++it) + { + // intersect tag sets instead of appending lists, to avoid dupes. This sucks. Definitely. I want QSet. Now. + QStringList t2 = (*it)->tags(); + for (QStringList::ConstIterator it2 = t2.begin(); it2 != t2.end(); ++it2) + if (!t.contains(*it2)) + t.append(*it2); + } + return t; +} + +QValueList<Article> Folder::articles(const QString& tag) +{ + QValueList<Article> seq; + QValueList<TreeNode*>::ConstIterator en = d->children.end(); + for (QValueList<TreeNode*>::ConstIterator it = d->children.begin(); it != en; ++it) + seq += (*it)->articles(tag); + + return seq; +} + +QDomElement Folder::toOPML( QDomElement parent, QDomDocument document ) const +{ + QDomElement el = document.createElement( "outline" ); + el.setAttribute( "text", title() ); + parent.appendChild( el ); + el.setAttribute("isOpen", d->open ? "true" : "false"); + el.setAttribute( "id", QString::number(id()) ); + + QValueList<TreeNode*>::ConstIterator en = d->children.end(); + for (QValueList<TreeNode*>::ConstIterator it = d->children.begin(); it != en; ++it) + el.appendChild( (*it)->toOPML(el, document) ); + + return el; +} + +QValueList<TreeNode*> Folder::children() const +{ + return d->children; +} + +void Folder::insertChild(TreeNode* node, TreeNode* after) +{ + int pos = d->children.findIndex(after); + + if (pos < 0) + prependChild(node); + else + insertChild(pos+1, node); +} + +void Folder::insertChild(uint index, TreeNode* node) +{ +// kdDebug() << "enter Folder::insertChild(int, node) " << node->title() << endl; + if (node) + { + if (index >= d->children.size()) + d->children.append(node); + else + d->children.insert(d->children.at(index), node); + node->setParent(this); + connectToNode(node); + updateUnreadCount(); + emit signalChildAdded(node); + d->addedArticlesNotify += node->articles(); + articlesModified(); + nodeModified(); + } +// kdDebug() << "leave Folder::insertChild(int, node) " << node->title() << endl; +} + +void Folder::appendChild(TreeNode* node) +{ +// kdDebug() << "enter Folder::appendChild() " << node->title() << endl; + if (node) + { + d->children.append(node); + node->setParent(this); + connectToNode(node); + updateUnreadCount(); + emit signalChildAdded(node); + d->addedArticlesNotify += node->articles(); + articlesModified(); + nodeModified(); + } +// kdDebug() << "leave Folder::appendChild() " << node->title() << endl; +} + +void Folder::prependChild(TreeNode* node) +{ +// kdDebug() << "enter Folder::prependChild() " << node->title() << endl; + if (node) + { + d->children.prepend(node); + node->setParent(this); + connectToNode(node); + updateUnreadCount(); + emit signalChildAdded(node); + d->addedArticlesNotify += node->articles(); + articlesModified(); + nodeModified(); + } +// kdDebug() << "leave Folder::prependChild() " << node->title() << endl; +} + +void Folder::removeChild(TreeNode* node) +{ +// kdDebug() << "enter Folder::removeChild() node:" << (node ? node->title() : "null") << endl; + if (node && d->children.contains(node)) + { + node->setParent(0); + d->children.remove(node); + disconnectFromNode(node); + updateUnreadCount(); + emit signalChildRemoved(this, node); + d->removedArticlesNotify += node->articles(); + articlesModified(); // articles were removed, TODO: add guids to a list + nodeModified(); + } +// kdDebug() << "leave Folder::removeChild() node: " << (node ? node->title() : "null") << endl; +} + + +TreeNode* Folder::firstChild() +{ + return d->children.isEmpty() ? 0 : d->children.first(); +} + +TreeNode* Folder::lastChild() +{ + return d->children.isEmpty() ? 0 : d->children.last(); +} + +bool Folder::isOpen() const +{ + return d->open; +} + +void Folder::setOpen(bool open) +{ + d->open = open; +} + +int Folder::unread() const +{ + return d->unread; +} + +int Folder::totalCount() const +{ + int totalCount = 0; + + QValueList<TreeNode*>::ConstIterator en = d->children.end(); + for (QValueList<TreeNode*>::ConstIterator it = d->children.begin(); it != en; ++it) + totalCount += (*it)->totalCount(); + + return totalCount; +} + +void Folder::updateUnreadCount() +{ + int unread = 0; + + QValueList<TreeNode*>::ConstIterator en = d->children.end(); + for (QValueList<TreeNode*>::ConstIterator it = d->children.begin(); it != en; ++it) + unread += (*it)->unread(); + + d->unread = unread; +} + +void Folder::slotMarkAllArticlesAsRead() +{ + setNotificationMode(false); + QValueList<TreeNode*>::ConstIterator en = d->children.end(); + for (QValueList<TreeNode*>::ConstIterator it = d->children.begin(); it != en; ++it) + (*it)->slotMarkAllArticlesAsRead(); + setNotificationMode(true, true); +} + +void Folder::slotChildChanged(TreeNode* /*node*/) +{ + updateUnreadCount(); + nodeModified(); +} + +void Folder::slotChildDestroyed(TreeNode* node) +{ + d->children.remove(node); + updateUnreadCount(); + nodeModified(); +} + +void Folder::slotDeleteExpiredArticles() +{ + setNotificationMode(false); + QValueList<TreeNode*>::ConstIterator en = d->children.end(); + for (QValueList<TreeNode*>::ConstIterator it = d->children.begin(); it != en; ++it) + (*it)->slotDeleteExpiredArticles(); + setNotificationMode(true, true); +} + +void Folder::slotAddToFetchQueue(FetchQueue* queue, bool intervalFetchOnly) +{ + QValueList<TreeNode*>::ConstIterator en = d->children.end(); + for (QValueList<TreeNode*>::ConstIterator it = d->children.begin(); it != en; ++it) + (*it)->slotAddToFetchQueue(queue, intervalFetchOnly); +} + +void Folder::doArticleNotification() +{ +} + +void Folder::connectToNode(TreeNode* child) +{ + connect(child, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotChildChanged(TreeNode*))); + connect(child, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotChildDestroyed(TreeNode*))); + connect(child, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&))); + connect(child, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&))); + connect(child, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&))); +} + +void Folder::disconnectFromNode(TreeNode* child) +{ + disconnect(child, SIGNAL(signalChanged(TreeNode*)), this, SLOT(slotChildChanged(TreeNode*))); + disconnect(child, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotChildDestroyed(TreeNode*))); + disconnect(child, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&))); + disconnect(child, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&))); + disconnect(child, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&))); +} + +TreeNode* Folder::next() +{ + if ( firstChild() ) + return firstChild(); + + if ( nextSibling() ) + return nextSibling(); + + Folder* p = parent(); + while (p) + { + if ( p->nextSibling() ) + return p->nextSibling(); + else + p = p->parent(); + } + return 0; +} + +} // namespace Akregator +#include "folder.moc" diff --git a/akregator/src/folder.h b/akregator/src/folder.h new file mode 100644 index 000000000..5c437d314 --- /dev/null +++ b/akregator/src/folder.h @@ -0,0 +1,179 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2004-2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_FOLDER_H +#define AKREGATOR_FOLDER_H + +#include "treenode.h" + +class QDomDocument; +class QDomElement; +class QStringList; +template <class T> class QValueList; + +namespace Akregator +{ + class Article; + class FetchQueue; + class TreeNodeVisitor; + + /** Represents a folder (containing feeds and/or other folders) + */ + class Folder : public TreeNode + { + Q_OBJECT + public: + /** creates a feed group parsed from a XML dom element. + Child nodes are not inserted or parsed. + @param e the element representing the feed group + @return a freshly created feed group */ + static Folder* fromOPML(QDomElement e); + + /** Creates a new folder with a given title + @param title The title of the feed group + */ + Folder(const QString& title = QString::null); + + virtual ~Folder(); + + virtual bool accept(TreeNodeVisitor* visitor); + + /** returns recursively concatenated articles of children + @return an article sequence containing articles of children */ + virtual QValueList<Article> articles(const QString& tag=QString::null); + + /** returns a list of all tags occurring in the subtree of this folder */ + virtual QStringList tags() const; + + /** returns the number of unread articles in all children + @return number of unread articles */ + virtual int unread() const; + + /** returns the number of articles in all children + @return number of articles */ + virtual int totalCount() const; + + /** Helps the rest of the app to decide if node should be handled as group or not. Use only where necessary, use polymorphism where possible. */ + virtual bool isGroup() const { return true; } + + /** converts the feed group into OPML format for save and export and appends it to node @c parent in document @document. + Children are processed and appended recursively. + @param parent The parent element + @param document The DOM document + @return The newly created element representing this feed group */ + virtual QDomElement toOPML( QDomElement parent, QDomDocument document ) const; + + /** returns the (direct) children of this node. + @return a list of pointers to the child nodes + */ + virtual QValueList<TreeNode*> children() const; + + /** inserts @c node as child after child node @c after. + if @c after is not a child of this group, @c node will be inserted as first child + @param node the tree node to insert + @param after the node after which @c node will be inserted */ + virtual void insertChild(TreeNode* node, TreeNode* after); + + /** inserts @c node as first child + @param node the tree node to insert */ + virtual void prependChild(TreeNode* node); + + /** inserts @c node as last child + @param node the tree node to insert */ + virtual void appendChild(TreeNode* node); + + /** remove @c node from children. Note that @c node will not be deleted + @param node the child node to remove */ + virtual void removeChild(TreeNode* node); + + /** returns the first child of the group, 0 if none exist */ + virtual TreeNode* firstChild(); + + /** returns the last child of the group, 0 if none exist */ + virtual TreeNode* lastChild(); + + /** returns whether the feed group is opened or not.. + Use only in \ref FolderItem. */ + virtual bool isOpen() const; + + /** open/close the feed group (display it as expanded/collapsed in the tree view). Use only in \ref FolderItem. */ + virtual void setOpen(bool open); + + signals: + /** emitted when a child was added */ + void signalChildAdded(TreeNode*); + + /** emitted when a child was removed */ + void signalChildRemoved(Folder*, TreeNode*); + + public slots: + + /** Delete expired articles recursively. */ + virtual void slotDeleteExpiredArticles(); + + /** Mark articles of children recursively as read. */ + virtual void slotMarkAllArticlesAsRead(); + + /** Called when a child was modified. + @param node the child that was changed + */ + virtual void slotChildChanged(TreeNode* node); + + /** Called when a child was destroyed. + @param node the child that was destroyed + */ + virtual void slotChildDestroyed(TreeNode* node); + + /** enqueues children recursively for fetching + @param queue a fetch queue + @param internvalFetchesOnly */ + virtual void slotAddToFetchQueue(FetchQueue* queue, bool intervalFetchesOnly=false); + + /** returns the next node in the tree. + Calling next() unless it returns 0 iterates through the tree in pre-order + */ + virtual TreeNode* next(); + + protected: + + /** inserts @c node as child on position @c index + @param index the position where to insert + @param node the tree node to insert */ + virtual void insertChild(uint index, TreeNode* node); + + virtual void doArticleNotification(); + private: + + void connectToNode(TreeNode* child); + void disconnectFromNode(TreeNode* child); + + virtual void updateUnreadCount(); + + class FolderPrivate; + FolderPrivate* d; + }; +} + +#endif // AKREGATOR_FOLDER_H diff --git a/akregator/src/folderitem.cpp b/akregator/src/folderitem.cpp new file mode 100644 index 000000000..1745295ea --- /dev/null +++ b/akregator/src/folderitem.cpp @@ -0,0 +1,86 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "actionmanager.h" +#include "folder.h" +#include "folderitem.h" +#include "treenode.h" + +#include <qpopupmenu.h> +#include <kaction.h> +#include <kiconloader.h> + +namespace Akregator { + +FolderItem::FolderItem(FolderItem* parent, Folder* node) : TreeNodeItem(parent, node) +{ + initialize(node); +} + +FolderItem::FolderItem(FolderItem* parent, TreeNodeItem* after, Folder* node) : TreeNodeItem(parent, after, node) +{ + initialize(node); +} + +FolderItem::FolderItem(KListView* parent, Folder* node) : TreeNodeItem(parent, node) +{ + initialize(node); +} + +FolderItem::FolderItem(KListView* parent, TreeNodeItem* after, Folder* node) : TreeNodeItem(parent, after, node) +{ + initialize(node); +} + +void FolderItem::initialize(Folder* node) +{ + setOpen(node->isOpen()); + setPixmap ( 0, KGlobal::iconLoader()->loadIcon("folder", KIcon::Small) ); + if (node) + setText(0, node->title()); +} + +Folder* FolderItem::node() +{ + return static_cast<Folder*> (m_node); +} + +void FolderItem::setOpen(bool open) +{ + node()->setOpen(open); + KListViewItem::setOpen(open); +} + +FolderItem::~FolderItem() +{} + + +void FolderItem::showContextMenu(const QPoint& p) +{ + QWidget* w = ActionManager::getInstance()->container("feedgroup_popup"); + if (w) + static_cast<QPopupMenu *>(w)->exec(p); +} + +} // namespace Akregator diff --git a/akregator/src/folderitem.h b/akregator/src/folderitem.h new file mode 100644 index 000000000..86c9e67a3 --- /dev/null +++ b/akregator/src/folderitem.h @@ -0,0 +1,61 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef AKREGATOR_FOLDERITEM_H +#define AKREGATOR_FOLDERITEM_H + +#include "treenodeitem.h" +#include "treenode.h" +#include "folder.h" + +#include <qpixmap.h> + +class QPoint; + +namespace Akregator +{ + +class Folder; + +class FolderItem : public TreeNodeItem +{ + +public: + FolderItem(FolderItem* parent, Folder* node); + FolderItem(FolderItem* parent, TreeNodeItem* after, Folder* node); + FolderItem(KListView* parent, Folder* node); + FolderItem(KListView* parent, TreeNodeItem* after, Folder* node); + virtual ~FolderItem(); + + virtual Folder* node(); + + virtual void setOpen(bool open); + virtual void showContextMenu(const QPoint& p); + +private: + void initialize(Folder* node); +}; + +} + +#endif diff --git a/akregator/src/frame.cpp b/akregator/src/frame.cpp new file mode 100644 index 000000000..faa8e371c --- /dev/null +++ b/akregator/src/frame.cpp @@ -0,0 +1,213 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qregexp.h> +#include <qstylesheet.h> + +#include <kactioncollection.h> +#include <kdebug.h> +#include <klocale.h> +#include <kparts/browserextension.h> +#include <kparts/part.h> + +#include <libkdepim/progressmanager.h> + +#include "frame.h" + +namespace Akregator { + +Frame::Frame(QObject * parent, KParts::ReadOnlyPart *p, QWidget *visWidget, const QString& tit, bool watchSignals) + :QObject(parent, "aKregatorFrame") +{ + m_autoDeletePart = false; + m_part=p; + m_widget=visWidget; + m_title=tit; + m_state=Idle; + m_progress=-1; + m_progressItem=0; + + if (watchSignals) // e.g, articles tab has no part + { + connect(m_part, SIGNAL(setWindowCaption (const QString &)), this, SLOT(setCaption (const QString &))); + connect(m_part, SIGNAL(setStatusBarText (const QString &)), this, SLOT(setStatusText (const QString &))); + + KParts::BrowserExtension *ext=KParts::BrowserExtension::childObject( p ); + if (ext) + connect( ext, SIGNAL(loadingProgress(int)), this, SLOT(setProgress(int)) ); + + connect(p, SIGNAL(started(KIO::Job*)), this, SLOT(setStarted())); + connect(p, SIGNAL(completed()), this, SLOT(setCompleted())); + connect(p, SIGNAL(canceled(const QString &)), this, SLOT(setCanceled(const QString&))); + connect(p, SIGNAL(completed(bool)), this, SLOT(setCompleted())); + +/* KActionCollection *coll=p->actionCollection(); + if (coll) + { + connect( coll, SIGNAL( actionStatusText( const QString & ) ), + this, SLOT( slotActionStatusText( const QString & ) ) ); + connect( coll, SIGNAL( clearStatusText() ), + this, SLOT( slotClearStatusText() ) ); + } +*/ + } +} + +void Frame::setAutoDeletePart(bool autoDelete) +{ + m_autoDeletePart = autoDelete; +} + +Frame::~Frame() +{ + if(m_progressItem) + { + m_progressItem->setComplete(); + } + if (m_autoDeletePart) + m_part->deleteLater(); +} + +int Frame::state() const +{ + return m_state; +} + +KParts::ReadOnlyPart *Frame::part() const +{ + return m_part; +} + +QWidget *Frame::widget() const +{ + return m_widget; +} + +void Frame::setTitle(const QString &s) +{ + if (m_title != s) + { + m_title = s; + emit titleChanged(this, s); + } +} + +void Frame::setCaption(const QString &s) +{ + if(m_progressItem) m_progressItem->setLabel(s); + m_caption=s; + emit captionChanged(s); +} + +void Frame::setStatusText(const QString &s) +{ + m_statusText=s; + m_statusText.replace(QRegExp("<[^>]*>"), ""); + emit statusText(m_statusText); +} + +void Frame::setProgress(int a) +{ + if(m_progressItem) { + m_progressItem->setProgress((int)a); + } + m_progress=a; + emit loadingProgress(a); +} + +void Frame::setState(int a) +{ + m_state=a; + + switch (m_state) + { + case Frame::Started: + emit started(); + break; + case Frame::Canceled: + emit canceled(QString::null); + break; + case Frame::Idle: + case Frame::Completed: + default: + emit completed(); + }} + + + +const QString& Frame::title() const +{ + return m_title; +} + +const QString& Frame::caption() const +{ + return m_caption; +} + +const QString& Frame::statusText() const +{ + return m_statusText; +} + +void Frame::setStarted() +{ + if(m_progressId.isNull() || m_progressId.isEmpty()) m_progressId = KPIM::ProgressManager::getUniqueID(); + m_progressItem = KPIM::ProgressManager::createProgressItem(m_progressId, QStyleSheet::escape( title() ), QString::null, false); + m_progressItem->setStatus(i18n("Loading...")); + //connect(m_progressItem, SIGNAL(progressItemCanceled(KPIM::ProgressItem*)), SLOT(slotAbortFetch())); + m_state=Started; + emit started(); +} + +void Frame::setCanceled(const QString &s) +{ + if(m_progressItem) { + m_progressItem->setStatus(i18n("Loading canceled")); + m_progressItem->setComplete(); + m_progressItem = 0; + } + m_state=Canceled; + emit canceled(s); +} + +void Frame::setCompleted() +{ + if(m_progressItem) { + m_progressItem->setStatus(i18n("Loading completed")); + m_progressItem->setComplete(); + m_progressItem = 0; + } + m_state=Completed; + emit completed(); +} + +int Frame::progress() const +{ + return m_progress; +} + +} // namespace Akregator + +#include "frame.moc" diff --git a/akregator/src/frame.h b/akregator/src/frame.h new file mode 100644 index 000000000..d0fe2a4ef --- /dev/null +++ b/akregator/src/frame.h @@ -0,0 +1,100 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef FRAME_H +#define FRAME_H + +#include <qobject.h> + +namespace KParts +{ + class ReadOnlyPart; +} + +namespace KPIM +{ + class ProgressItem; +} + +namespace Akregator +{ + + class Frame : public QObject + { + Q_OBJECT + + public: + Frame(QObject *parent, KParts::ReadOnlyPart *part, QWidget *w, const QString& tit, bool watchSignals=true); + virtual ~Frame(); + + enum {Idle, Started, Completed, Canceled}; + + KParts::ReadOnlyPart *part() const; + QWidget *widget() const; + const QString& title() const; + const QString& caption() const; + int state() const; + int progress() const; + const QString& statusText() const; + + /** if set to true, the part is deleted when the frame is deleted. Set to @c false by default */ + void setAutoDeletePart(bool autoDelete); + + public slots: + void setStarted(); + void setCanceled(const QString &); + void setCompleted(); + void setState(int); + void setProgress(int); + void setCaption(const QString &); + void setTitle(const QString &); + void setStatusText(const QString &); + + signals: + void captionChanged(const QString &); + void titleChanged(Frame*, const QString&); + void started(); + void canceled(const QString &); + void completed(); + void loadingProgress(int); + void statusText(const QString &); + + private: + KParts::ReadOnlyPart *m_part; + QWidget *m_widget; + QString m_title; + QString m_caption; + int m_state; + int m_progress; + QString m_statusText; + QString m_progressId; + KPIM::ProgressItem *m_progressItem; + bool m_autoDeletePart; + }; +} + +#endif + + +// vim: set et ts=4 sts=4 sw=4: diff --git a/akregator/src/hi128-app-akregator.png b/akregator/src/hi128-app-akregator.png Binary files differnew file mode 100644 index 000000000..abaeebfe9 --- /dev/null +++ b/akregator/src/hi128-app-akregator.png diff --git a/akregator/src/hi16-app-akregator.png b/akregator/src/hi16-app-akregator.png Binary files differnew file mode 100644 index 000000000..ed970689e --- /dev/null +++ b/akregator/src/hi16-app-akregator.png diff --git a/akregator/src/hi22-app-akregator.png b/akregator/src/hi22-app-akregator.png Binary files differnew file mode 100644 index 000000000..32a4b0332 --- /dev/null +++ b/akregator/src/hi22-app-akregator.png diff --git a/akregator/src/hi32-app-akregator.png b/akregator/src/hi32-app-akregator.png Binary files differnew file mode 100644 index 000000000..56c5ac0ac --- /dev/null +++ b/akregator/src/hi32-app-akregator.png diff --git a/akregator/src/hi48-app-akregator.png b/akregator/src/hi48-app-akregator.png Binary files differnew file mode 100644 index 000000000..143ae1917 --- /dev/null +++ b/akregator/src/hi48-app-akregator.png diff --git a/akregator/src/hi64-app-akregator.png b/akregator/src/hi64-app-akregator.png Binary files differnew file mode 100644 index 000000000..0d95fb0ad --- /dev/null +++ b/akregator/src/hi64-app-akregator.png diff --git a/akregator/src/hisc-app-akregator.svgz b/akregator/src/hisc-app-akregator.svgz Binary files differnew file mode 100644 index 000000000..1fbc778d9 --- /dev/null +++ b/akregator/src/hisc-app-akregator.svgz diff --git a/akregator/src/kcursorsaver.h b/akregator/src/kcursorsaver.h new file mode 100644 index 000000000..c77a7377b --- /dev/null +++ b/akregator/src/kcursorsaver.h @@ -0,0 +1,67 @@ +// taken from kmail/, moved to Akregator namespace to avoid clashes + +#ifndef kcursorsaver_h +#define kcursorsaver_h + +#include <qcursor.h> +#include <qapplication.h> + +namespace Akregator { + +/** + * @short sets a cursor and makes sure it's restored on destruction + * Create a KCursorSaver object when you want to set the cursor. + * As soon as it gets out of scope, it will restore the original + * cursor. + */ +class KCursorSaver : public Qt +{ +public: + /// constructor taking QCursor shapes + KCursorSaver(Qt::CursorShape shape) { + QApplication::setOverrideCursor( QCursor(shape) ); + inited = true; + } + + /// copy constructor. The right side won't restore the cursor + KCursorSaver( const KCursorSaver &rhs ) { + *this = rhs; + } + + /// restore the cursor + ~KCursorSaver() { + if (inited) + QApplication::restoreOverrideCursor(); + } + + /// call this to explitly restore the cursor + inline void restoreCursor(void) { + QApplication::restoreOverrideCursor(); + inited = false; + } + +protected: + void operator=( const KCursorSaver &rhs ) { + inited = rhs.inited; + rhs.inited = false; + } + +private: + mutable bool inited; +}; + +/** + * convenience functions + */ +namespace KBusyPtr { + inline KCursorSaver idle() { + return KCursorSaver(QCursor::ArrowCursor); + } + inline KCursorSaver busy() { + return KCursorSaver(QCursor::WaitCursor); + } +} + +} // namespace Akregator + +#endif /*kbusyptr_h_*/ diff --git a/akregator/src/kernel.cpp b/akregator/src/kernel.cpp new file mode 100644 index 000000000..197333664 --- /dev/null +++ b/akregator/src/kernel.cpp @@ -0,0 +1,60 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "kernel.h" + +#include <kstaticdeleter.h> + +#include "fetchqueue.h" +#include "tagset.h" + +namespace Akregator +{ + +Kernel* Kernel::m_self = 0; + +static KStaticDeleter<Kernel> kernelsd; + +Kernel* Kernel::self() +{ + if (!m_self) + m_self = kernelsd.setObject(m_self, new Kernel); + return m_self; +} + +Kernel::Kernel() +{ + m_fetchQueue = new FetchQueue(); + m_tagSet = new TagSet(); + m_storage = 0; + m_feedList = 0; +} + +Kernel::~Kernel() +{ + delete m_fetchQueue; +} + + +} diff --git a/akregator/src/kernel.h b/akregator/src/kernel.h new file mode 100644 index 000000000..51204cd78 --- /dev/null +++ b/akregator/src/kernel.h @@ -0,0 +1,80 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_KERNEL_H +#define AKREGATOR_KERNEL_H + +#include "articlefilter.h" + +namespace Akregator { + +namespace Backend +{ + class Storage; +} + +class FeedList; +class FetchQueue; +class TagSet; + +class Kernel +{ + public: + + static Kernel* self(); + + Kernel(); + virtual ~Kernel(); + + Backend::Storage* storage() { return m_storage; } + void setStorage(Backend::Storage* storage) { m_storage = storage; } + + FeedList* feedList() { return m_feedList; } + void setFeedList(FeedList* feedList) { m_feedList = feedList; } + + FetchQueue* fetchQueue() { return m_fetchQueue; } + + TagSet* tagSet() { return m_tagSet; } + + void setArticleFilterList(const Filters::ArticleFilterList& list) + { + m_articleFilterList = list; + } + + Filters::ArticleFilterList articleFilterList() const { return m_articleFilterList; } + + private: + + static Kernel* m_self; + + Backend::Storage* m_storage; + FeedList* m_feedList; + FetchQueue* m_fetchQueue; + TagSet* m_tagSet; + Filters::ArticleFilterList m_articleFilterList; +}; + +} + +#endif // AKREGATOR_KERNEL_H diff --git a/akregator/src/librss/COPYING b/akregator/src/librss/COPYING new file mode 100644 index 000000000..cca2a5c9a --- /dev/null +++ b/akregator/src/librss/COPYING @@ -0,0 +1,20 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/akregator/src/librss/Makefile.am b/akregator/src/librss/Makefile.am new file mode 100644 index 000000000..060d393f9 --- /dev/null +++ b/akregator/src/librss/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES = \ + -I$(top_srcdir)/src \ + $(all_includes) + +noinst_LTLIBRARIES = \ + librsslocal.la + +noinst_HEADERS = article.h document.h global.h image.h textinput.h \ + loader.h librss.h enclosure.h + +librsslocal_la_SOURCES = article.cpp document.cpp image.cpp textinput.cpp \ + tools_p.cpp loader.cpp enclosure.cpp category.cpp feeddetector.cpp + +librsslocal_la_METASOURCES = AUTO + +check_PROGRAMS = testlibrss +testlibrss_SOURCES = testlibrss.cpp +testlibrss_LDFLAGS = $(all_libraries) +testlibrss_LDADD = librsslocal.la $(LIB_KIO) + +DOXYGEN_REFERENCES = kdeui +include $(top_srcdir)/admin/Doxyfile.am diff --git a/akregator/src/librss/README b/akregator/src/librss/README new file mode 100644 index 000000000..77d945015 --- /dev/null +++ b/akregator/src/librss/README @@ -0,0 +1,6 @@ +This is NOT original librss by Frerich Raabe, though based on it. + +This version is supposed to be called libsyndication but is not renamed to relieve packaging burden a bit +(honestly, we just didn't yet get to it). + +Please DO NOT report any bugs about it to Frerich, since he most probably did not introduce the found bugs. diff --git a/akregator/src/librss/article.cpp b/akregator/src/librss/article.cpp new file mode 100644 index 000000000..010cf5dcb --- /dev/null +++ b/akregator/src/librss/article.cpp @@ -0,0 +1,290 @@ +/* + * article.cpp + * + * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#include "article.h" +#include "tools_p.h" +#include "enclosure.h" +#include "category.h" + +#include <kdebug.h> +#include <krfcdate.h> +#include <kurl.h> +#include <kurllabel.h> +#include <kmdcodec.h> + +#include <qdatetime.h> +#include <qdom.h> + +using namespace RSS; +namespace RSS +{ + KMD5 md5Machine; +} + +struct Article::Private : public Shared +{ + QString title; + KURL link; + QString description; + QDateTime pubDate; + QString guid; + QString author; + bool guidIsPermaLink; + MetaInfoMap meta; + KURL commentsLink; + int numComments; + Enclosure enclosure; + QValueList<Category> categories; +}; + +Article::Article() : d(new Private) +{ +} + +Article::Article(const Article &other) : d(0) +{ + *this = other; +} + +Enclosure Article::enclosure() const +{ + return d->enclosure; +} + +QValueList<Category> Article::categories() const +{ + return d->categories; +} + + +Article::Article(const QDomNode &node, Format format, Version version) : d(new Private) +{ + QString elemText; + + d->numComments=0; + + if (!(elemText = extractTitle(node)).isNull()) + d->title = elemText; + + if (format==AtomFeed) + { + QDomNode n; + for (n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + const QDomElement e = n.toElement(); + if ( (e.tagName()==QString::fromLatin1("link")) && + (e.attribute(QString::fromLatin1("rel"), QString::fromLatin1("alternate")) == QString::fromLatin1("alternate"))) + { + d->link=n.toElement().attribute(QString::fromLatin1("href")); + break; + } + } + } + else + { + if (!(elemText = extractNode(node, QString::fromLatin1("link"))).isNull()) + d->link = elemText; + } + + + // prefer content/content:encoded over summary/description for feeds that provide it + QString tagName=(format==AtomFeed)? QString::fromLatin1("content"): QString::fromLatin1("content:encoded"); + + if (!(elemText = extractNode(node, tagName, false)).isNull()) + d->description = elemText; + + if (d->description.isEmpty()) + { + if (!(elemText = extractNode(node, QString::fromLatin1("body"), false)).isNull()) + d->description = elemText; + + if (d->description.isEmpty()) // 3rd try: see http://www.intertwingly.net/blog/1299.html + { + if (!(elemText = extractNode(node, QString::fromLatin1((format==AtomFeed)? "summary" : "description"), false)).isNull()) + d->description = elemText; + } + } + + time_t time = 0; + + if (format == AtomFeed) + { + if (version == vAtom_1_0) + elemText = extractNode(node, QString::fromLatin1("updated")); + else + elemText = extractNode(node, QString::fromLatin1("issued")); + + if (!elemText.isNull()) + time = parseISO8601Date(elemText); + } + else + { + elemText = extractNode(node, QString::fromLatin1("pubDate")); + if (!elemText.isNull()) + time = KRFCDate::parseDate(elemText); + } + + if (!(elemText = extractNode(node, QString::fromLatin1("dc:date"))).isNull()) + { + time = parseISO8601Date(elemText); + } + + // 0 means invalid, not epoch (parsers return epoch+1 when parsing epoch, see the KRFCDate::parseDate() docs) + if (time != 0) + d->pubDate.setTime_t(time); + + if (!(elemText = extractNode(node, QString::fromLatin1("wfw:comment"))).isNull()) { + d->commentsLink = elemText; + } + + if (!(elemText = extractNode(node, QString::fromLatin1("slash:comments"))).isNull()) { + d->numComments = elemText.toInt(); + } + + QDomElement element = QDomNode(node).toElement(); + + // in RSS 1.0, we use <item about> attribute as ID + // FIXME: pass format version instead of checking for attribute + + if (!element.isNull() && element.hasAttribute(QString::fromLatin1("rdf:about"))) + { + d->guid = element.attribute(QString::fromLatin1("rdf:about")); // HACK: using ns properly did not work + d->guidIsPermaLink = false; + } + else + { + tagName=(format==AtomFeed)? QString::fromLatin1("id"): QString::fromLatin1("guid"); + QDomNode n = node.namedItem(tagName); + if (!n.isNull()) + { + d->guidIsPermaLink = (format==AtomFeed)? false : true; + if (n.toElement().attribute(QString::fromLatin1("isPermaLink"), "true") == "false") d->guidIsPermaLink = false; + if (!(elemText = extractNode(node, tagName)).isNull()) + d->guid = elemText; + } + } + + if(d->guid.isEmpty()) { + d->guidIsPermaLink = false; + + md5Machine.reset(); + QDomNode n(node); + md5Machine.update(d->title.utf8()); + md5Machine.update(d->description.utf8()); + d->guid = QString(md5Machine.hexDigest().data()); + d->meta[QString::fromLatin1("guidIsHash")] = QString::fromLatin1("true"); + } + + QDomNode enclosure = element.namedItem(QString::fromLatin1("enclosure")); + if (enclosure.isElement()) + d->enclosure = Enclosure::fromXML(enclosure.toElement()); + + d->author = parseItemAuthor(element, format, version); + + for (QDomNode i = node.firstChild(); !i.isNull(); i = i.nextSibling()) + { + if (i.isElement()) + { + if (i.toElement().tagName() == QString::fromLatin1("metaInfo:meta")) + { + QString type = i.toElement().attribute(QString::fromLatin1("type")); + d->meta[type] = i.toElement().text(); + } + else if (i.toElement().tagName() == QString::fromLatin1("category")) + { + d->categories.append(Category::fromXML(i.toElement())); + } + } + } +} + +Article::~Article() +{ + if (d->deref()) + delete d; +} + +QString Article::title() const +{ + return d->title; +} + +QString Article::author() const +{ + return d->author; +} + +const KURL &Article::link() const +{ + return d->link; +} + +QString Article::description() const +{ + return d->description; +} + +QString Article::guid() const +{ + return d->guid; +} + +bool Article::guidIsPermaLink() const +{ + return d->guidIsPermaLink; +} + +const QDateTime &Article::pubDate() const +{ + return d->pubDate; +} + +const KURL &Article::commentsLink() const +{ + return d->commentsLink; +} + +int Article::comments() const +{ + return d->numComments; +} + + +QString Article::meta(const QString &key) const +{ + return d->meta[key]; +} + +KURLLabel *Article::widget(QWidget *parent, const char *name) const +{ + KURLLabel *label = new KURLLabel(d->link.url(), d->title, parent, name); + label->setUseTips(true); + if (!d->description.isNull()) + label->setTipText(d->description); + + return label; +} + +Article &Article::operator=(const Article &other) +{ + if (this != &other) { + other.d->ref(); + if (d && d->deref()) + delete d; + d = other.d; + } + return *this; +} + +bool Article::operator==(const Article &other) const +{ + return d->guid == other.guid(); +} + +// vim:noet:ts=4 diff --git a/akregator/src/librss/article.h b/akregator/src/librss/article.h new file mode 100644 index 000000000..74deb2539 --- /dev/null +++ b/akregator/src/librss/article.h @@ -0,0 +1,172 @@ +/* + * article.h + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#ifndef LIBRSS_ARTICLE_H +#define LIBRSS_ARTICLE_H + +#include <qmap.h> + +#include "global.h" + +class QDateTime; +class QDomNode; +template <class> class QValueList; +class QString; +class QWidget; +class KURL; +class KURLLabel; + +namespace RSS +{ + class Category; + class Enclosure; + + /** + * Represents an article as stored in a RSS file. You don't have to + * instantiate one of these yourself, the common way to access instances + * is via Document::articles(). + * @see Document::articles() + */ + class KDE_EXPORT Article + { + public: + /** + * A list of articles. + */ + typedef QValueList<Article> List; + + /** + * Default constructor. + */ + Article(); + + /** + * Copy constructor. + * @param other The Article object to copy. + */ + Article(const Article &other); + + /** + * Constructs an Article from a piece of RSS markup. + * @param node A QDomNode which references the DOM leaf to be used + * for constructing the Article. + */ + Article(const QDomNode &node, Format format, Version version); + + /** + * Assignment operator. + * @param other The Article object to clone. + * @return A reference to the cloned Article object. + */ + Article &operator=(const Article &other); + + /** + * Compares two articles. Two articles are treated to be identical + * if all their properties (title, link, description etc.) are + * equal. + * @param other The article this article should be compared with. + * @return Whether the two articles are equal. + */ + bool operator==(const Article &other) const; + + /** + * Convenience method. Simply calls !operator==(). + * @param other The article this article should be compared with. + * @return Whether the two articles are unequal. + */ + bool operator!=(const Article &other) const { return !operator==(other); } + + /** + * Destructor. + */ + virtual ~Article(); + + /** + * RSS 0.90 and upwards + * @return The headline of this article, or QString::null if + * no headline was available. + */ + QString title() const; + + /** + * RSS 0.90 and upwards + * @return A URL referencing the complete text for this article, + * or an empty KURL if no link was available. + * Note that the RSS 0.91 Specification dictates that URLs not + * starting with "http://" or "ftp://" are considered invalid. + */ + const KURL &link() const; + + /** + * RSS 0.91 and upwards + * @return A story synopsis, or QString::null if no description + * was available. + */ + QString description() const; + + /** + * a string desribing the author of the item. + */ + QString author() const; + + /** + * RSS 2.0 and upwards + * @return An article GUID (globally unique identifier). + */ + QString guid() const; + + /** + * RSS 2.0 and upwards + * @return If this article GUID is permalink. Has no meaning when guid() is QString::null. + */ + bool guidIsPermaLink() const; + + /** + * RSS 2.0 and upwards + * @return The date when the article was published. + */ + const QDateTime &pubDate() const; + + const KURL &commentsLink() const; + int comments() const; + + Enclosure enclosure() const; + + /** returns a list of categories this article is assigned to. (RSS2 only, Atom is not supported yet) */ + QValueList<Category> categories() const; + + QString meta(const QString &key) const; + + /** + * @param parent The parent widget for the KURLLabel. + * @param name A name for the widget which will be used internally. + * @return a widget (a KURLLabel in this case) for the Article. + * This makes building a user-interface which contains the + * information in this Article object more convenient. + * The returned KURLLabel's caption will be the title(), clicking + * on it will emit the URL link(), and it has a QToolTip attached + * to it which displays the description() (in case it has one, + * if there is no description, the URL which the label links to + * will be used). + * Note that you have to delete the KURLLabel object returned by + * this method yourself. + */ + KURLLabel *widget(QWidget *parent = 0, const char *name = 0) const; + + typedef QMap<QString, QString> MetaInfoMap; + + private: + struct Private; + Private *d; + }; +} + +#endif // LIBRSS_ARTICLE_H +// vim: noet:ts=4 diff --git a/akregator/src/librss/category.cpp b/akregator/src/librss/category.cpp new file mode 100644 index 000000000..07508ecaf --- /dev/null +++ b/akregator/src/librss/category.cpp @@ -0,0 +1,129 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "category.h" +#include "tools_p.h" + +#include <qdom.h> +#include <qstring.h> + +class QString; + +namespace RSS +{ + +class Category::CategoryPrivate : public Shared +{ + public: + bool isNull; + QString category; + QString domain; + + bool operator==(const CategoryPrivate &other) const + { + return (isNull && other.isNull) || (category == other.category && domain == other.domain); + } + + static CategoryPrivate* copyOnWrite(CategoryPrivate* ep) + { + if (ep->count > 1) + { + ep->deref(); + ep = new CategoryPrivate(*ep); + } + return ep; + } +}; + +bool Category::isNull() const +{ + return d == 0; +} + +Category Category::fromXML(const QDomElement& e) +{ + Category obj; + if (e.hasAttribute(QString::fromLatin1("domain"))) + obj.d->domain = e.attribute(QString::fromLatin1("domain")); + obj.d->category = e.text(); + obj.d->isNull = false; + return obj; +} + +Category::Category() : d(new CategoryPrivate) +{ + d->isNull = true; +} + +Category::Category(const Category& other) : d(0) +{ + *this = other; +} + +Category::Category(const QString& category, const QString& domain) : d(new CategoryPrivate) +{ + d->isNull = false; + d->category = category; + d->domain = domain; +} + +Category::~Category() +{ + if (d->deref()) + { + delete d; + d = 0; + } +} + +Category& Category::operator=(const Category& other) +{ + if (d != other.d) + { + other.d->ref(); + if (d && d->deref()) + delete d; + d = other.d; + } + return *this; +} + +bool Category::operator==(const Category &other) const +{ + return *d == *other.d; +} + +QString Category::category() const +{ + return !d->isNull ? d->category : QString::null; +} + +QString Category::domain() const +{ + return !d->isNull ? d->domain : QString::null; +} + +} // namespace RSS + + diff --git a/akregator/src/librss/category.h b/akregator/src/librss/category.h new file mode 100644 index 000000000..43267b903 --- /dev/null +++ b/akregator/src/librss/category.h @@ -0,0 +1,64 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef LIBRSS_RSS_CATEGORY_H +#define LIBRSS_RSS_CATEGORY_H + +#include "global.h" + +class QDomDocument; +class QDomElement; +class QString; + +namespace RSS +{ + class KDE_EXPORT Category + { + public: + + static Category fromXML(const QDomElement& e); + + Category(); + Category(const Category& other); + Category(const QString& category, const QString& domain); + virtual ~Category(); + + Category& operator=(const Category& other); + bool operator==(const Category& other) const; + + QString category() const; + + QString domain() const; + + bool isNull() const; + + private: + + class CategoryPrivate; + CategoryPrivate* d; + + }; +} // namespace RSS + +#endif // LIBRSS_RSS_CATEGORY_H diff --git a/akregator/src/librss/document.cpp b/akregator/src/librss/document.cpp new file mode 100644 index 000000000..ad9614f98 --- /dev/null +++ b/akregator/src/librss/document.cpp @@ -0,0 +1,653 @@ +/* + * document.cpp + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + * + */ +#include "document.h" +#include "article.h" +#include "image.h" +#include "textinput.h" +#include "tools_p.h" + +#include <krfcdate.h> +#include <kurl.h> + +#include <qdatetime.h> +#include <qdom.h> +#include <qptrlist.h> + +#include <kdebug.h> + +using namespace RSS; + +struct Document::Private : public Shared +{ + Private() : version(v0_90), image(NULL), textInput(NULL), language(en) + { + format=UnknownFormat; + valid=false; + ttl=-1; + } + + ~Private() + { + delete textInput; + delete image; + } + + Version version; + QString title; + QString description; + KURL link; + Image *image; + TextInput *textInput; + Article::List articles; + Language language; + Format format; + QString copyright; + QDateTime pubDate; + QDateTime lastBuildDate; + QString rating; + KURL docs; + int ttl; + QString managingEditor; + QString webMaster; + HourList skipHours; + DayList skipDays; + bool valid; +}; + +Document::Document() : d(new Private) +{ +} + +Document::Document(const Document &other) : d(0) +{ + *this = other; +} + +static QString extractLink(const QDomNode& node, Format format) +{ + if (format == AtomFeed) + { + QDomNode n; + for (n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + const QDomElement e = n.toElement(); + if ( (e.tagName() == QString::fromLatin1("link")) + && (e.attribute(QString::fromLatin1("rel"), QString::fromLatin1("alternate")) == QString::fromLatin1("alternate"))) + { + return n.toElement().attribute(QString::fromLatin1("href")); + } + } + } + + return extractNode(node, QString::fromLatin1("link")); + +} + +Document::Document(const QDomDocument &doc) : d(new Private) +{ + QString elemText; + QDomNode rootNode = doc.documentElement(); + + // Determine the version of the present RSS markup. + QString attr; + + // we should probably check that it ISN'T feed or rss, rather than check if it is xhtml + if (rootNode.toElement().tagName()==QString::fromLatin1("html")) + d->valid=false; + else + d->valid=true; + + attr = rootNode.toElement().attribute(QString::fromLatin1("version"), QString::null); + if (rootNode.toElement().tagName()==QString::fromLatin1("feed")) + { + d->format=AtomFeed; + if (attr == QString::fromLatin1("0.3")) + d->version = vAtom_0_3; + else if (attr == QString::fromLatin1("0.2")) /* smt -> review */ + d->version = vAtom_0_2; + else if (attr == QString::fromLatin1("0.1")) /* smt -> review */ + d->version = vAtom_0_1; + else + d->version = vAtom_1_0; + } + else + { + d->format=RSSFeed; + if (attr == QString::fromLatin1("0.91")) + d->version = v0_91; + else if (attr == QString::fromLatin1("0.92")) + d->version = v0_92; + else if (attr == QString::fromLatin1("0.93")) + d->version = v0_93; + else if (attr == QString::fromLatin1("0.94")) + d->version = v0_94; + else // otherwise, we just assume a RSS2 compatible feed. As rss2 is generally + // backward-compatible, this should work + d->version = v2_0; + } + + + if (d->format==UnknownFormat) + { + attr = rootNode.toElement().attribute(QString::fromLatin1("xmlns"), QString::null); + if (!attr.isNull()) { + /* + * Hardcoding these URLs is actually a bad idea, since the DTD doesn't + * dictate a specific namespace. Still, most RSS files seem to use + * these two, so I'll go for them now. If it turns out that many + * mirrors of this RSS namespace are in use, I'll probably have to + * distinguish the RSS versions by analyzing the relationship between + * the nodes. + */ + if (attr == QString::fromLatin1("http://my.netscape.com/rdf/simple/0.9/")) { + d->format=RSSFeed; + d->version = v0_90; + } + else if (attr == QString::fromLatin1("http://purl.org/rss/1.0/")) { + d->format=RSSFeed; + d->version = v1_0; + } + } + } + + QDomNode channelNode; + + if (d->format == AtomFeed) + channelNode=rootNode; + else + channelNode=rootNode.namedItem(QString::fromLatin1("channel")); + + if (!(elemText = extractTitle(channelNode)).isNull()) + d->title = elemText; + QString descriptionTagName = "description"; + + if (d->format == AtomFeed) + { + if (d->version == vAtom_1_0) + descriptionTagName = "subtitle"; + else + descriptionTagName = "tagline"; + } + + if (!(elemText = extractNode(channelNode, descriptionTagName)).isNull()) + d->description = elemText; + + d->link = extractLink(channelNode, d->format); + + + /* This is ugly but necessary since RSS 0.90 and 1.0 have a different parent + * node for <image>, <textinput> and <item> than RSS 0.91-0.94 and RSS 2.0. + */ + QDomNode parentNode; + if (d->version == v0_90 || d->version == v1_0 || d->format == AtomFeed) + parentNode = rootNode; + else + { + // following is a HACK for broken 0.91 feeds like xanga.com's + if (!rootNode.namedItem(QString::fromLatin1("item")).isNull()) + parentNode = rootNode; + else + parentNode = channelNode; + } + + // image and textinput aren't supported by Atom.. handle in case feed provides + QDomNode n = parentNode.namedItem(QString::fromLatin1("image")); + if (!n.isNull()) + d->image = new Image(n); + + n = parentNode.namedItem(QString::fromLatin1("textinput")); + if (!n.isNull()) + d->textInput = new TextInput(n); + + // Our (hopefully faster) version of elementsByTagName() + QString tagName; + if (d->format == AtomFeed) + tagName=QString::fromLatin1("entry"); + else + tagName=QString::fromLatin1("item"); + + for (n = parentNode.firstChild(); !n.isNull(); n = n.nextSibling()) { + const QDomElement e = n.toElement(); + if (e.tagName() == tagName) + d->articles.append(Article(e, d->format, d->version)); + } + + if (!(elemText = extractNode(channelNode, QString::fromLatin1("copyright"))).isNull()) + d->copyright = elemText; + + if (d->format == AtomFeed) + elemText = rootNode.toElement().attribute(QString::fromLatin1("xml:lang"), QString::null); + else + elemText = extractNode(channelNode, QString::fromLatin1("language")); + + if (!elemText.isNull()){ + if (elemText == QString::fromLatin1("af")) + d->language = af; + else if (elemText == QString::fromLatin1("sq")) + d->language = sq; + else if (elemText == QString::fromLatin1("eu")) + d->language = eu; + else if (elemText == QString::fromLatin1("be")) + d->language = be; + else if (elemText == QString::fromLatin1("bg")) + d->language = bg; + else if (elemText == QString::fromLatin1("ca")) + d->language = ca; + else if (elemText == QString::fromLatin1("zh-cn")) + d->language = zh_cn; + else if (elemText == QString::fromLatin1("zh-tw")) + d->language = zh_tw; + else if (elemText == QString::fromLatin1("hr")) + d->language = hr; + else if (elemText == QString::fromLatin1("cs")) + d->language = cs; + else if (elemText == QString::fromLatin1("da")) + d->language = da; + else if (elemText == QString::fromLatin1("nl")) + d->language = nl; + else if (elemText == QString::fromLatin1("nl-be")) + d->language = nl_be; + else if (elemText == QString::fromLatin1("nl-nl")) + d->language = nl_nl; + else if (elemText == QString::fromLatin1("en")) + d->language = en; + else if (elemText == QString::fromLatin1("en-au")) + d->language = en_au; + else if (elemText == QString::fromLatin1("en-bz")) + d->language = en_bz; + else if (elemText == QString::fromLatin1("en-ca")) + d->language = en_ca; + else if (elemText == QString::fromLatin1("en-ie")) + d->language = en_ie; + else if (elemText == QString::fromLatin1("en-jm")) + d->language = en_jm; + else if (elemText == QString::fromLatin1("en-nz")) + d->language = en_nz; + else if (elemText == QString::fromLatin1("en-ph")) + d->language = en_ph; + else if (elemText == QString::fromLatin1("en-za")) + d->language = en_za; + else if (elemText == QString::fromLatin1("en-tt")) + d->language = en_tt; + else if (elemText == QString::fromLatin1("en-gb")) + d->language = en_gb; + else if (elemText == QString::fromLatin1("en-us")) + d->language = en_us; + else if (elemText == QString::fromLatin1("en-zw")) + d->language = en_zw; + else if (elemText == QString::fromLatin1("fo")) + d->language = fo; + else if (elemText == QString::fromLatin1("fi")) + d->language = fi; + else if (elemText == QString::fromLatin1("fr")) + d->language = fr; + else if (elemText == QString::fromLatin1("fr-be")) + d->language = fr_be; + else if (elemText == QString::fromLatin1("fr-ca")) + d->language = fr_ca; + else if (elemText == QString::fromLatin1("fr-fr")) + d->language = fr_fr; + else if (elemText == QString::fromLatin1("fr-lu")) + d->language = fr_lu; + else if (elemText == QString::fromLatin1("fr-mc")) + d->language = fr_mc; + else if (elemText == QString::fromLatin1("fr-ch")) + d->language = fr_ch; + else if (elemText == QString::fromLatin1("gl")) + d->language = gl; + else if (elemText == QString::fromLatin1("gd")) + d->language = gd; + else if (elemText == QString::fromLatin1("de")) + d->language = de; + else if (elemText == QString::fromLatin1("de-at")) + d->language = de_at; + else if (elemText == QString::fromLatin1("de-de")) + d->language = de_de; + else if (elemText == QString::fromLatin1("de-li")) + d->language = de_li; + else if (elemText == QString::fromLatin1("de-lu")) + d->language = de_lu; + else if (elemText == QString::fromLatin1("de-ch")) + d->language = de_ch; + else if (elemText == QString::fromLatin1("el")) + d->language = el; + else if (elemText == QString::fromLatin1("hu")) + d->language = hu; + else if (elemText == QString::fromLatin1("is")) + d->language = is; + else if (elemText == QString::fromLatin1("id")) + d->language = id; + else if (elemText == QString::fromLatin1("ga")) + d->language = ga; + else if (elemText == QString::fromLatin1("it")) + d->language = it; + else if (elemText == QString::fromLatin1("it-it")) + d->language = it_it; + else if (elemText == QString::fromLatin1("it-ch")) + d->language = it_ch; + else if (elemText == QString::fromLatin1("ja")) + d->language = ja; + else if (elemText == QString::fromLatin1("ko")) + d->language = ko; + else if (elemText == QString::fromLatin1("mk")) + d->language = mk; + else if (elemText == QString::fromLatin1("no")) + d->language = no; + else if (elemText == QString::fromLatin1("pl")) + d->language = pl; + else if (elemText == QString::fromLatin1("pt")) + d->language = pt; + else if (elemText == QString::fromLatin1("pt-br")) + d->language = pt_br; + else if (elemText == QString::fromLatin1("pt-pt")) + d->language = pt_pt; + else if (elemText == QString::fromLatin1("ro")) + d->language = ro; + else if (elemText == QString::fromLatin1("ro-mo")) + d->language = ro_mo; + else if (elemText == QString::fromLatin1("ro-ro")) + d->language = ro_ro; + else if (elemText == QString::fromLatin1("ru")) + d->language = ru; + else if (elemText == QString::fromLatin1("ru-mo")) + d->language = ru_mo; + else if (elemText == QString::fromLatin1("ru-ru")) + d->language = ru_ru; + else if (elemText == QString::fromLatin1("sr")) + d->language = sr; + else if (elemText == QString::fromLatin1("sk")) + d->language = sk; + else if (elemText == QString::fromLatin1("sl")) + d->language = sl; + else if (elemText == QString::fromLatin1("es")) + d->language = es; + else if (elemText == QString::fromLatin1("es-ar")) + d->language = es_ar; + else if (elemText == QString::fromLatin1("es-bo")) + d->language = es_bo; + else if (elemText == QString::fromLatin1("es-cl")) + d->language = es_cl; + else if (elemText == QString::fromLatin1("es-co")) + d->language = es_co; + else if (elemText == QString::fromLatin1("es-cr")) + d->language = es_cr; + else if (elemText == QString::fromLatin1("es-do")) + d->language = es_do; + else if (elemText == QString::fromLatin1("es-ec")) + d->language = es_ec; + else if (elemText == QString::fromLatin1("es-sv")) + d->language = es_sv; + else if (elemText == QString::fromLatin1("es-gt")) + d->language = es_gt; + else if (elemText == QString::fromLatin1("es-hn")) + d->language = es_hn; + else if (elemText == QString::fromLatin1("es-mx")) + d->language = es_mx; + else if (elemText == QString::fromLatin1("es-ni")) + d->language = es_ni; + else if (elemText == QString::fromLatin1("es-pa")) + d->language = es_pa; + else if (elemText == QString::fromLatin1("es-py")) + d->language = es_py; + else if (elemText == QString::fromLatin1("es-pe")) + d->language = es_pe; + else if (elemText == QString::fromLatin1("es-pr")) + d->language = es_pr; + else if (elemText == QString::fromLatin1("es-es")) + d->language = es_es; + else if (elemText == QString::fromLatin1("es-uy")) + d->language = es_uy; + else if (elemText == QString::fromLatin1("es-ve")) + d->language = es_ve; + else if (elemText == QString::fromLatin1("sv")) + d->language = sv; + else if (elemText == QString::fromLatin1("sv-fi")) + d->language = sv_fi; + else if (elemText == QString::fromLatin1("sv-se")) + d->language = sv_se; + else if (elemText == QString::fromLatin1("tr")) + d->language = tr; + else if (elemText == QString::fromLatin1("uk")) + d->language = uk; + else + d->language = UndefinedLanguage; + } + + if (d->format == AtomFeed) + tagName=QString::fromLatin1("issued"); // atom doesn't specify this for feeds + // but some broken feeds do this + else + tagName=QString::fromLatin1("pubDate"); + + if (!(elemText = extractNode(channelNode, tagName)).isNull()) { + time_t _time; + + if (d->format == AtomFeed) + _time=parseISO8601Date(elemText); + else + _time=KRFCDate::parseDate(elemText); + /* \bug This isn't really the right way since it will set the date to + * Jan 1 1970, 1:00:00 if the passed date was invalid; this means that + * we cannot distinguish between that date, and invalid values. :-/ + */ + d->pubDate.setTime_t(_time); + } + + if (!(elemText = extractNode(channelNode, QString::fromLatin1("dc:date"))).isNull()) { + time_t _time = parseISO8601Date(elemText); + /* \bug This isn't really the right way since it will set the date to + * Jan 1 1970, 1:00:00 if the passed date was invalid; this means that + * we cannot distinguish between that date, and invalid values. :-/ + */ + d->pubDate.setTime_t(_time); + } + + if (d->format == AtomFeed) + tagName=QString::fromLatin1("modified"); + else + tagName=QString::fromLatin1("lastBuildDate"); + if (!(elemText = extractNode(channelNode, tagName)).isNull()) { + time_t _time; + if (d->format == AtomFeed) + _time = parseISO8601Date(elemText); + else + _time = KRFCDate::parseDate(elemText); + d->lastBuildDate.setTime_t(_time); + } + + if (!(elemText = extractNode(channelNode, QString::fromLatin1("rating"))).isNull()) + d->rating = elemText; + if (!(elemText = extractNode(channelNode, QString::fromLatin1("docs"))).isNull()) + d->docs = elemText; + if (!(elemText = extractNode(channelNode, QString::fromLatin1((d->format == AtomFeed) ? "author" : "managingEditor"))).isNull()) + d->managingEditor = elemText; + if (!(elemText = extractNode(channelNode, QString::fromLatin1("webMaster"))).isNull()) + d->webMaster = elemText; + + if (!(elemText = extractNode(channelNode, QString::fromLatin1("ttl"))).isNull()) + d->ttl = elemText.toUInt(); + + n = channelNode.namedItem(QString::fromLatin1("skipHours")); + if (!n.isNull()) + for (QDomElement e = n.firstChild().toElement(); !e.isNull(); e = e.nextSibling().toElement()) + if (e.tagName() == QString::fromLatin1("hour")) + d->skipHours.append(e.text().toUInt()); + + n = channelNode.namedItem(QString::fromLatin1("skipDays")); + if (!n.isNull()) { + Day day; + QString elemText; + for (QDomElement e = n.firstChild().toElement(); !e.isNull(); e = e.nextSibling().toElement()) + if (e.tagName() == QString::fromLatin1("day")) { + elemText = e.text().lower(); + if (elemText == QString::fromLatin1("monday")) + day = Monday; + else if (elemText == QString::fromLatin1("tuesday")) + day = Tuesday; + else if (elemText == QString::fromLatin1("wednesday")) + day = Wednesday; + else if (elemText == QString::fromLatin1("thursday")) + day = Thursday; + else if (elemText == QString::fromLatin1("friday")) + day = Friday; + else if (elemText == QString::fromLatin1("saturday")) + day = Saturday; + else if (elemText == QString::fromLatin1("sunday")) + day = Sunday; + else + day = UndefinedDay; + if (day != UndefinedDay) + d->skipDays.append(day); + } + } +} + +Document::~Document() +{ + if (d->deref()) + delete d; +} + +bool Document::isValid() const +{ + return d->valid; +} + +Version Document::version() const +{ + return d->version; +} + +QString Document::verbVersion() const +{ + switch (d->version) { + case v0_90: return QString::fromLatin1("0.90"); + case v0_91: return QString::fromLatin1("0.91"); + case v0_92: return QString::fromLatin1("0.92"); + case v0_93: return QString::fromLatin1("0.93"); + case v0_94: return QString::fromLatin1("0.94"); + case v1_0: return QString::fromLatin1("1.0"); + case v2_0: return QString::fromLatin1("2.0"); + case vAtom_0_3: return QString::fromLatin1("0.3"); + case vAtom_0_2: return QString::fromLatin1("0.2"); + case vAtom_0_1: return QString::fromLatin1("0.1"); + case vAtom_1_0: return QString::fromLatin1("1.0"); + } + return QString::null; +} + +QString Document::title() const +{ + return d->title; +} + +QString Document::description() const +{ + return d->description; +} + +const KURL &Document::link() const +{ + return d->link; +} + +Image *Document::image() +{ + return d->image; +} + +const Image *Document::image() const +{ + return d->image; +} + +TextInput *Document::textInput() +{ + return d->textInput; +} + +const TextInput *Document::textInput() const +{ + return d->textInput; +} + +const Article::List &Document::articles() const +{ + return d->articles; +} + +Language Document::language() const +{ + return d->language; +} + +QString Document::copyright() const +{ + return d->copyright; +} + +const QDateTime &Document::pubDate() const +{ + return d->pubDate; +} + +const QDateTime &Document::lastBuildDate() const +{ + return d->lastBuildDate; +} + +QString Document::rating() const +{ + return d->rating; +} + +const KURL &Document::docs() const +{ + return d->docs; +} + +QString Document::managingEditor() const +{ + return d->managingEditor; +} + +QString Document::webMaster() const +{ + return d->webMaster; +} + +const HourList &Document::skipHours() const +{ + return d->skipHours; +} + +const DayList &Document::skipDays() const +{ + return d->skipDays; +} + +int Document::ttl() const +{ + return d->ttl; +} + +Document &Document::operator=(const Document &other) +{ + if (this != &other) { + other.d->ref(); + if (d && d->deref()) + delete d; + d = other.d; + } + return *this; +} + +// vim:noet:ts=4 diff --git a/akregator/src/librss/document.h b/akregator/src/librss/document.h new file mode 100644 index 000000000..161960720 --- /dev/null +++ b/akregator/src/librss/document.h @@ -0,0 +1,237 @@ +/* + * document.h + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#ifndef LIBRSS_DOCUMENT_H +#define LIBRSS_DOCUMENT_H + +#include "article.h" +#include "global.h" + +class QDateTime; +class QDomDocument; + +namespace RSS +{ + class Image; + class TextInput; + + /** + * Represents a RSS document and provides all the features and properties + * as stored in it. You usually don't need to instantiate this one yourself + * but rather use Loader::loadFrom() to produce a Document object. + * @see Loader::loadForm() + */ + class KDE_EXPORT Document + { + public: + /** + * Default constructor. + */ + Document(); + + /** + * Copy constructor. + * @param other The Document object to copy. + */ + Document(const Document &other); + + /** + * Constructs a Document from a piece of XML markup. + */ + Document(const QDomDocument &doc); + + /** + * Assignment operator. + * @param other The Document object to clone. + * @return A reference to the cloned Document object. + */ + Document &operator=(const Document &other); + + /** + * Destructor. + */ + ~Document(); + + /** + * @return If document is valid + */ + bool isValid() const; + + /** + * @return The version of this document (one of the values of the + * enum RSS::Version). This value can be used to determine which + * features this RSS document provides. + * @see verbVersion() + */ + Version version() const; + + /** + * Convenience method. Differs from version() only in how the result + * is returned. + * @return A QString representing the verbose version of the + * document. + * @see version() + */ + QString verbVersion() const; + + /** + * RSS 0.90 and upwards + * @return The title of the RSS document, or QString::null if no + * title was available. This is often the name of the news source + * from which the RSS document was retrieved. + */ + QString title() const; + + /** + * RSS 0.90 and upwards + * @return The description of the RSS document, or QString::null + * if no description was available. This is usually a short slogan + * or description of the news source from which the RSS document + * was retrieved. + */ + QString description() const; + + /** + * RSS 0.90 and upwards + * @return A link pointing to some website, or an empty KURL if no + * link was available. This URL mostly points to the homepage of + * the news site from which the RSS document was retrieved. + * Note that the RSS 0.91 Specification dictates that URLs not + * starting with "http://" or "ftp://" are considered invalid. + */ + const KURL &link() const; + + /** + * RSS 0.90 and upwards + * @return An Image object as stored in the RSS document, or a + * null pointer if there was no image available. + * @see Image + */ + Image *image(); + + /** + * A version of the method above, with stricter const-ness. + */ + const Image *image() const; + + /** + * RSS 0.90 and upwards + * @return A TextInput object as stored in the RSS document, or a + * null pointer if there was no text input available. + * @see TextInput + */ + TextInput *textInput(); + + /** + * A version of the method above, with stricter const-ness. + */ + const TextInput *textInput() const; + + /** + * RSS 0.90 and upwards + * @return A list of Article objects as stored in the RSS document, + * or a null pointer if there were no articles available. Every RSS + * DTD requires that there is at least one article defined, so a + * null pointer indicates an invalid RSS file! + * @see Article + */ + const Article::List &articles() const; + + /** + * RSS 0.91 and upwards + * @return The language used in the RSS document (for the article + * headlines etc.). This was originally introduced to assist with + * determining the correct page encoding but acts as a solely + * optional information in this library since you don't have to care + * about the encoding as Unicode is used in the whole library. + * @see RSS::Language + */ + Language language() const; + + /** + * RSS 0.91 and upwards + * @return A copyright of the information contained in the RSS + * document, or QString::null if no copyright is available. + */ + QString copyright() const; + + /** + * RSS 0.91 and upwards + * @return The date when the RSS document was published. + */ + const QDateTime &pubDate() const; + + /** + * RSS 0.91 and upwards. + * @return The last time the channel was modified. + */ + const QDateTime &lastBuildDate() const; + + /** + * RSS 0.91 and upwards + * @return A <a href="http://www.w3.org/PICS/#Specs">PICS</a> + * rating for this page. + */ + QString rating() const; + + /** + * RSS 0.91 and upwards + * @return This tag should contain either a URL that references a + * description of the channel, or a pointer to the documentation + * for the format used in the RSS file. + */ + const KURL &docs() const; + + /** + * RSS 0.91 and upwards + * @return The email address of the managing editor of the site, + * the person to contact for editorial inquiries. The suggested + * format for email addresses in RSS documents is + * bull@mancuso.com (Bull Mancuso). + * @see webMaster() + */ + QString managingEditor() const; + + /** + * RSS 0.91 and upwards + * @return The email address of the webmaster for the site, the + * person to contact if there are technical problems with the + * channel, or QString::null if this information isn't available. + * @see managingEditor() + */ + QString webMaster() const; + + /** + * RSS 0.91 and upwards + * @return A list of hours indicating the hours in the day, GMT, + * when the channel is unlikely to be updated. If this item is + * omitted, the channel is assumed to be updated hourly. Each + * hour should be an integer value between 0 and 23. + * @see skipDays() + */ + const HourList &skipHours() const; + + /** + * RSS 0.91 and upwards + * @return A list of \<day\>s of the week, in English, indicating + * the days of the week when the RSS document will not be updated. + * @see skipHours(), DayList, Day + */ + const DayList &skipDays() const; + int ttl() const; + + private: + struct Private; + Private *d; + }; +} + +#endif // LIBRSS_DOCUMENT_H +// vim: noet:ts=4 diff --git a/akregator/src/librss/enclosure.cpp b/akregator/src/librss/enclosure.cpp new file mode 100644 index 000000000..60898f1bf --- /dev/null +++ b/akregator/src/librss/enclosure.cpp @@ -0,0 +1,154 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "enclosure.h" +#include "tools_p.h" + +#include <qdom.h> +#include <qstring.h> + +#include <kdebug.h> + +namespace RSS +{ + + +class Enclosure::EnclosurePrivate : public Shared +{ + public: + + bool isNull; + QString url; + int length; + QString type; + + bool operator==(const EnclosurePrivate &other) const + { + return ( isNull == other.isNull || (url == other.url && + length == other.length && + type == other.type)); + } +}; + + +Enclosure Enclosure::fromXML(const QDomElement& e) +{ + QString url, type; + int length = -1; + + if (e.hasAttribute(QString::fromLatin1("url"))) + url = e.attribute(QString::fromLatin1("url")); + + if (e.hasAttribute(QString::fromLatin1("length"))) + { + bool ok; + int c = e.attribute(QString::fromLatin1("length")).toInt(&ok); + length = ok ? c : -1; + } + if (e.hasAttribute(QString::fromLatin1("type"))) + type = e.attribute(QString::fromLatin1("type")); + + return Enclosure(url, length, type); +} + +QDomElement Enclosure::toXML(QDomDocument document) const +{ + QDomElement e = document.createElement(QString::fromLatin1("enclosure")); + if (!d->url.isNull()) + e.setAttribute(QString::fromLatin1("url"), d->url); + if (d->length != -1) + e.setAttribute(QString::fromLatin1("length"), QString::number(d->length)); + if (!d->type.isNull()) + e.setAttribute(QString::fromLatin1("type"), d->type); + + return e; +} + +Enclosure::Enclosure() : d(new EnclosurePrivate) +{ + d->isNull = true; + d->length = -1; +} + +Enclosure::Enclosure(const Enclosure& other) : d(0) +{ + *this = other; +} + +Enclosure::Enclosure(const QString& url, int length, const QString& type) : d(new EnclosurePrivate) +{ + d->isNull = false; + d->url = url; + d->length = length; + d->type = type; +} + +Enclosure::~Enclosure() +{ + if (d->deref()) + { + delete d; + d = 0; + } +} + +Enclosure& Enclosure::operator=(const Enclosure& other) +{ + if (d != other.d) + { + other.d->ref(); + if (d && d->deref()) + delete d; + d = other.d; + } + return *this; +} + +bool Enclosure::operator==(const Enclosure &other) const +{ + return *d == *other.d; +} + +bool Enclosure::isNull() const +{ + return d->isNull; +} + +QString Enclosure::url() const +{ + return d->url; +} + +int Enclosure::length() const +{ + return d->length; +} + +QString Enclosure::type() const +{ + return d->type; +} + + +} // namespace RSS diff --git a/akregator/src/librss/enclosure.h b/akregator/src/librss/enclosure.h new file mode 100644 index 000000000..d3db5d12a --- /dev/null +++ b/akregator/src/librss/enclosure.h @@ -0,0 +1,69 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef LIBRSS_RSS_ENCLOSURE_H +#define LIBRSS_RSS_ENCLOSURE_H + +#include "global.h" + +class QDomDocument; +class QDomElement; +class QString; + +namespace RSS +{ + class KDE_EXPORT Enclosure + { + public: + + static Enclosure fromXML(const QDomElement& e); + QDomElement toXML(QDomDocument document) const; + + Enclosure(); + Enclosure(const Enclosure& other); + Enclosure(const QString& url, int length, const QString& type); + virtual ~Enclosure(); + + bool isNull() const; + + Enclosure& operator=(const Enclosure& other); + bool operator==(const Enclosure& other) const; + + /** returns the URL of the enclosure */ + QString url() const; + + /** returns the size of the enclosure in bytes */ + int length() const; + + /** returns the mime type of the enclosure */ + QString type() const; + + private: + + class EnclosurePrivate; + EnclosurePrivate* d; + }; + +} // namespace RSS +#endif // LIBRSS_RSS_ENCLOSURE_H diff --git a/akregator/src/librss/feeddetector.cpp b/akregator/src/librss/feeddetector.cpp new file mode 100644 index 000000000..a45b18776 --- /dev/null +++ b/akregator/src/librss/feeddetector.cpp @@ -0,0 +1,179 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Teemu Rytilahti <tpr@d5k.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qregexp.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <kcharsets.h> +#include <kurl.h> + +#include "feeddetector.h" + + +using namespace RSS; + +FeedDetectorEntryList FeedDetector::extractFromLinkTags(const QString& s) +{ + //reduce all sequences of spaces, newlines etc. to one space: + QString str = s.simplifyWhiteSpace(); + + // extracts <link> tags + QRegExp reLinkTag("<[\\s]?LINK[^>]*REL[\\s]?=[\\s]?\\\"[\\s]?(ALTERNATE|SERVICE\\.FEED)[\\s]?\\\"[^>]*>", false); + + // extracts the URL (href="url") + QRegExp reHref("HREF[\\s]?=[\\s]?\\\"([^\\\"]*)\\\"", false); + // extracts type attribute + QRegExp reType("TYPE[\\s]?=[\\s]?\\\"([^\\\"]*)\\\"", false); + // extracts the title (title="title") + QRegExp reTitle("TITLE[\\s]?=[\\s]?\\\"([^\\\"]*)\\\"", false); + + int pos = 0; + int matchpos = 0; + + // get all <link> tags + QStringList linkTags; + //int strlength = str.length(); + while ( matchpos != -1 ) + { + matchpos = reLinkTag.search(str, pos); + if (matchpos != -1) + { + linkTags.append( str.mid(matchpos, reLinkTag.matchedLength()) ); + pos = matchpos + reLinkTag.matchedLength(); + } + } + + FeedDetectorEntryList list; + + for ( QStringList::Iterator it = linkTags.begin(); it != linkTags.end(); ++it ) + { + QString type; + int pos = reType.search(*it, 0); + if (pos != -1) + type = reType.cap(1).lower(); + + // we accept only type attributes indicating a feed + if ( type != "application/rss+xml" && type != "application/rdf+xml" + && type != "application/atom+xml" && type != "text/xml" ) + continue; + + QString title; + pos = reTitle.search(*it, 0); + if (pos != -1) + title = reTitle.cap(1); + + title = KCharsets::resolveEntities(title); + + QString url; + pos = reHref.search(*it, 0); + if (pos != -1) + url = reHref.cap(1); + + url = KCharsets::resolveEntities(url); + + // if feed has no title, use the url as preliminary title (until feed is parsed) + if ( title.isEmpty() ) + title = url; + + if ( !url.isEmpty() ) + list.append(FeedDetectorEntry(url, title) ); + } + + + return list; +} + +QStringList FeedDetector::extractBruteForce(const QString& s) +{ + QString str = s.simplifyWhiteSpace(); + + QRegExp reAhrefTag("<[\\s]?A[^>]?HREF=[\\s]?\\\"[^\\\"]*\\\"[^>]*>", false); + + // extracts the URL (href="url") + QRegExp reHref("HREF[\\s]?=[\\s]?\\\"([^\\\"]*)\\\"", false); + + QRegExp rssrdfxml(".*(RSS|RDF|XML)", false); + + int pos = 0; + int matchpos = 0; + + // get all <a href> tags and capture url + QStringList list; + //int strlength = str.length(); + while ( matchpos != -1 ) + { + matchpos = reAhrefTag.search(str, pos); + if ( matchpos != -1 ) + { + QString ahref = str.mid(matchpos, reAhrefTag.matchedLength()); + int hrefpos = reHref.search(ahref, 0); + if ( hrefpos != -1 ) + { + QString url = reHref.cap(1); + + url = KCharsets::resolveEntities(url); + + if ( rssrdfxml.exactMatch(url) ) + list.append(url); + } + + pos = matchpos + reAhrefTag.matchedLength(); + } + } + + return list; +} + +QString FeedDetector::fixRelativeURL(const QString &s, const KURL &baseurl) +{ + QString s2=s; + KURL u; + if (KURL::isRelativeURL(s2)) + { + if (s2.startsWith("//")) + { + s2=s2.prepend(baseurl.protocol()+":"); + u=s2; + } + else if (s2.startsWith("/")) + { + KURL b2(baseurl); + b2.setPath(QString()); // delete path and query, so that only protocol://host remains + b2.setQuery(QString()); + u = KURL(b2, s2.remove(0,1)); // remove leading "/" + } + else + { + u = KURL(baseurl, s2); + } + } + else + u=s2; + + u.cleanPath(); + //kdDebug() << "AKREGATOR_PLUGIN_FIXURL: " << "url=" << s << " baseurl=" << baseurl.url() << " fixed=" << u.url() << + //endl; + return u.url(); +} diff --git a/akregator/src/librss/feeddetector.h b/akregator/src/librss/feeddetector.h new file mode 100644 index 000000000..c27acf76e --- /dev/null +++ b/akregator/src/librss/feeddetector.h @@ -0,0 +1,80 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Teemu Rytilahti <tpr@d5k.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef LIBRSS_FEEDDETECTOR_H +#define LIBRSS_FEEDDETECTOR_H + +#include <qstring.h> +#include <qvaluelist.h> + +class QStringList; +class KURL; + +namespace RSS +{ + + class FeedDetectorEntry + { + public: + FeedDetectorEntry() {} + FeedDetectorEntry(const QString& url, const QString& title) + : m_url(url), m_title(title) {} + + const QString& url() const { return m_url; } + const QString& title() const { return m_title; } + + private: + const QString m_url; + const QString m_title; + }; + + typedef QValueList<FeedDetectorEntry> FeedDetectorEntryList; + + /** a class providing functions to detect linked feeds in HTML sources */ + class FeedDetector + { + public: + /** \brief searches an HTML page for feeds listed in @c <link> tags + @c <link> tags with @c rel attribute values @c alternate or + @c service.feed are considered as feeds + @param s the html source to scan (the actual source, no URI) + @return a list containing the detected feeds + */ + static FeedDetectorEntryList extractFromLinkTags(const QString& s); + + /** \brief searches an HTML page for slightly feed-like looking links and catches everything not running away quickly enough. + Extracts links from @c <a @c href> tags which end with @c xml, @c rss or @c rdf + @param s the html source to scan (the actual source, no URI) + @return a list containing the detected feeds + */ + static QStringList extractBruteForce(const QString& s); + + static QString fixRelativeURL(const QString &s, const KURL &baseurl); + + private: + FeedDetector() {} + }; +} + +#endif //LIBRSS_FEEDDETECTOR_H diff --git a/akregator/src/librss/global.h b/akregator/src/librss/global.h new file mode 100644 index 000000000..cec9609c7 --- /dev/null +++ b/akregator/src/librss/global.h @@ -0,0 +1,148 @@ +/* + * global.h + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#ifndef LIBRSS_GLOBAL_H +#define LIBRSS_GLOBAL_H + +#include <kdepimmacros.h> + +template <class> +class QValueList; + +namespace RSS +{ + /** + * Versions currently supported by this library. This enumeration is + * subject to be extended in the future and used by Document::version() to + * provide an interface to the client using which he can find out what + * version the loaded RSS file actually is. + */ + enum Version { + v0_90, /// RSS v0.90 + v0_91, /// RSS v0.91 + v0_92, /// RSS v0.92 + v0_93, /// RSS v0.93 + v0_94, /// RSS v0.94 + v1_0, /// RSS v1.0 + v2_0, /// RSS v2.0 + vAtom_0_1, /// Atom v0.1 + vAtom_0_2, /// Atom v0.2 + vAtom_0_3, /// Atom v0.3 + vAtom_1_0 /// Atom v1.0 + }; + + /** + * Possible status values returned by the signal + * Loader::loadingComplete(). + */ + enum Status { + Success, /** + * Nothing went wrong so far, but you still have to check + * what values are returned by the classes since it's not + * guaranteed that the retrieved RSS markup actually + * complies to one of the RSS DTDs.*/ + Aborted, /** the loader was aborted manually + */ + RetrieveError, /** + * Something went wrong while retrieving the RSS data, + * this could be a problem while resolving the host name + * (assuming the source file loader was used) or a + * problem with the program to be executed (in case the + * program loader was used.).*/ + ParseError /** + * The overall format of the RSS markup wasn't XML + * conform. This only indicates that the data wasn't + * valid (for example, if the data returned by a + * DataRetriever isn't well-formed XML). + * @see DataRetriever */ + }; + + /** + * Possible languages which are returned by Document::language(). + */ + enum Language { + UndefinedLanguage, /** Unknown / undefined language */ + + af, /** Afrikaans */ sq, /** Albanian */ + eu, /** Basque */ be, /** Belarusian */ + bg, /** Bulgarian */ ca, /** Catalan */ + zh_cn, /** Chinese (Simplified) */ zh_tw, /** Chinese (Traditional */ + hr, /** Croatian */ cs, /** Czech */ + da, /** Danish */ nl, /** Dutch */ + nl_be, /** Dutch (Belgium) */ nl_nl, /** Dutch (Netherlands) */ + en, /** English */ en_au, /** English (Australia) */ + en_bz, /** English (Belize) */ en_ca, /** English (Canada) */ + en_ie, /** English (Ireland) */ en_jm, /** English (Jamaica) */ + en_nz, /** English (New Zealand) */ en_ph, /** English (Phillipines) */ + en_za, /** English (South Africa) */ en_tt, /** English (Trinidad) */ + en_gb, /** English (Great Britain) */en_us, /** English (United States) */ + en_zw, /** English (Zimbabwe) */ fo, /** Faeroese */ + fi, /** Finnish */ fr, /** French */ + fr_be, /** French (Belgium) */ fr_ca, /** French (Canada) */ + fr_fr, /** French (France) */ fr_lu, /** French (Luxembourg) */ + fr_mc, /** French (Monaco) */ fr_ch, /** French (Switzerland) */ + gl, /** Galician */ gd, /** Gaelic */ + de, /** German */ de_at, /** German (Austria) */ + de_de, /** German (Germany) */ de_li, /** German (Liechtenstein) */ + de_lu, /** German (Luxembourg) */ de_ch, /** German (Switzerland) */ + el, /** Greek */ hu, /** Hungarian */ + is, /** Icelandic */ id, /** Indonesian */ + ga, /** Irish */ it, /** Italian */ + it_it, /** Italian (Italy) */ it_ch, /** Italian (Switzerland) */ + ja, /** Japanese */ ko, /** Korean */ + mk, /** Macedonian */ no, /** Norwegian */ + pl, /** Polish */ pt, /** Portuguese */ + pt_br, /** Portuguese (Brazil) */ pt_pt, /** Portuguese (Portugal) */ + ro, /** Romanian */ ro_mo, /** Romanian (Moldova) */ + ro_ro, /** Romanian (Romania) */ ru, /** Russian */ + ru_mo, /** Russian (Moldova) */ ru_ru, /** Russian (Russia) */ + sr, /** Serbian */ sk, /** Slovak */ + sl, /** Slovenian */ es, /** Spanish */ + es_ar, /** Spanish (Argentina) */ es_bo, /** Spanish (Bolivia) */ + es_cl, /** Spanish (Chile) */ es_co, /** Spanish (Colombia) */ + es_cr, /** Spanish (Costa Rica) */ es_do, /** Spanish (Dominican Rep.) */ + es_ec, /** Spanish (Ecuador) */ es_sv, /** Spanish (El Salvador) */ + es_gt, /** Spanish (Guatemala) */ es_hn, /** Spanish (Honduras) */ + es_mx, /** Spanish (Mexico) */ es_ni, /** Spanish (Nicaragua) */ + es_pa, /** Spanish (Panama) */ es_py, /** Spanish (Paraguay) */ + es_pe, /** Spanish (Peru) */ es_pr, /** Spanish (Puerto Rico) */ + es_es, /** Spanish (Spain) */ es_uy, /** Spanish (Uruguay) */ + es_ve, /** Spanish (Venezuela) */ sv, /** Swedish */ + sv_fi, /** Swedish (Finland) */ sv_se, /** Swedish (Sweden) */ + tr, /** Turkish */ uk /** Ukranian */ + }; + + /** + * Possible values contained in a DayList. + */ + enum Day { + UndefinedDay, + Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday + }; + + enum Format { + UnknownFormat, + AtomFeed, + RSSFeed + }; + + /** + * This type is used by Document::skipDays(). + */ + typedef QValueList<Day> DayList; + + /** + * This type is used by Document::skipHours(). + */ + typedef QValueList<unsigned short> HourList; +} + +#endif // LIBRSS_GLOBAL_H +// vim: noet:ts=4 diff --git a/akregator/src/librss/image.cpp b/akregator/src/librss/image.cpp new file mode 100644 index 000000000..33e1544ae --- /dev/null +++ b/akregator/src/librss/image.cpp @@ -0,0 +1,167 @@ +/* + * image.cpp + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#include "image.h" +#include "tools_p.h" + +#include <kio/job.h> +#include <kurl.h> + +#include <qbuffer.h> +#include <qdom.h> +#include <qpixmap.h> + +using namespace RSS; + +struct Image::Private : public Shared +{ + Private() : height(31), width(88), pixmapBuffer(NULL), job(NULL) + { } + + QString title; + KURL url; + KURL link; + QString description; + unsigned int height; + unsigned int width; + QBuffer *pixmapBuffer; + KIO::Job *job; +}; + +Image::Image() : QObject(), d(new Private) +{ +} + +Image::Image(const Image &other) : QObject(), d(0) +{ + *this = other; +} + +Image::Image(const QDomNode &node) : QObject(), d(new Private) +{ + QString elemText; + + if (!(elemText = extractNode(node, QString::fromLatin1("title"))).isNull()) + d->title = elemText; + if (!(elemText = extractNode(node, QString::fromLatin1("url"))).isNull()) + d->url = elemText; + if (!(elemText = extractNode(node, QString::fromLatin1("link"))).isNull()) + d->link = elemText; + if (!(elemText = extractNode(node, QString::fromLatin1("description"))).isNull()) + d->description = elemText; + if (!(elemText = extractNode(node, QString::fromLatin1("height"))).isNull()) + d->height = elemText.toUInt(); + if (!(elemText = extractNode(node, QString::fromLatin1("width"))).isNull()) + d->width = elemText.toUInt(); +} + +Image::~Image() +{ + if (d->deref()) + { + delete d->pixmapBuffer; + d->pixmapBuffer=0L; + delete d; + } +} + +QString Image::title() const +{ + return d->title; +} + +const KURL &Image::url() const +{ + return d->url; +} + +const KURL &Image::link() const +{ + return d->link; +} + +QString Image::description() const +{ + return d->description; +} + +unsigned int Image::height() const +{ + return d->height; +} + +unsigned int Image::width() const +{ + return d->width; +} + +void Image::getPixmap() +{ + // Ignore subsequent calls if we didn't finish the previous download. + if (d->pixmapBuffer) + return; + + d->pixmapBuffer = new QBuffer; + d->pixmapBuffer->open(IO_WriteOnly); + + d->job = KIO::get(d->url, false, false); + connect(d->job, SIGNAL(data(KIO::Job *, const QByteArray &)), + this, SLOT(slotData(KIO::Job *, const QByteArray &))); + connect(d->job, SIGNAL(result(KIO::Job *)), this, SLOT(slotResult(KIO::Job *))); +} + +void Image::slotData(KIO::Job *, const QByteArray &data) +{ + d->pixmapBuffer->writeBlock(data.data(), data.size()); +} + +void Image::slotResult(KIO::Job *job) +{ + QPixmap pixmap; + if (!job->error()) + pixmap = QPixmap(d->pixmapBuffer->buffer()); + emit gotPixmap(pixmap); + + delete d->pixmapBuffer; + d->pixmapBuffer = NULL; +} + +void Image::abort() +{ + if (d->job) + { + d->job->kill(true); + d->job = NULL; + } +} + +Image &Image::operator=(const Image &other) +{ + if (this != &other) { + other.d->ref(); + if (d && d->deref()) + delete d; + d = other.d; + } + return *this; +} + +bool Image::operator==(const Image &other) const +{ + return d->title == other.title() && + d->url == other.url() && + d->description == other.description() && + d->height == other.height() && + d->width == other.width() && + d->link == other.link(); +} + +#include "image.moc" +// vim:noet:ts=4 diff --git a/akregator/src/librss/image.h b/akregator/src/librss/image.h new file mode 100644 index 000000000..74197edb6 --- /dev/null +++ b/akregator/src/librss/image.h @@ -0,0 +1,173 @@ +/* + * image.h + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#ifndef LIBRSS_IMAGE_H +#define LIBRSS_IMAGE_H + +#include "global.h" + +#include <qobject.h> + +class QDomNode; + +namespace KIO +{ + class Job; +} +class KURL; + +namespace RSS +{ + /** + * Represents an image as stored in a RSS file. You don't have to + * instantiate one of these yourself, the common way to access instances + * is via Document::image(). + * @see Document::image() + */ + class KDE_EXPORT Image : public QObject + { + Q_OBJECT + public: + /** + * Default constructor. + */ + Image(); + + /** + * Copy constructor. + * @param other The Image object to copy. + */ + Image(const Image &other); + + /** + * Constructs an Image from a piece of RSS markup. + * @param node A QDomNode which references the DOM leaf to be used + * for constructing the Image. + */ + Image(const QDomNode &node); + + /** + * Assignment operator. + * @param other The Image object to clone. + * @return A reference to the cloned Image object. + */ + Image &operator=(const Image &other); + + /** + * Compares two images. Two images are considered identical if + * their properties (title, description, link etc.) are identical. + * Note that this does not include the actual pixmap data! + * @param other The image to compare with. + * @return Whether the two images are equal. + */ + bool operator==(const Image &other) const; + + /** + * Convenience method. Simply calls !operator==(). + * @param other The image to compared with. + * @return Whether the two images are unequal. + */ + bool operator!=(const Image &other) const { return !operator==(other); } + + /** + * Destructor. + */ + virtual ~Image(); + + /** + * RSS 0.90 and upwards + * @return The 'caption' of this image, or QString::null if no + * caption is available. + */ + QString title() const; + + /** + * RSS 0.90 and upwards + * @return The URL pointing to the file containing the graphic + * data (GIF, JPEG or PNG format), or an empty KURL if no URL + * is available. You can use getPixmap() and gotPixmap() to have + * the Image download the pixmap data itself. + * Note that the RSS 0.91 Specification dictates that URLs not + * starting with "http://" or "ftp://" are considered invalid. + */ + const KURL &url() const; + + /** + * RSS 0.90 and upwards + * @return A link to some resource, or an empty KURL of no link is + * available. Clicking on the image should lead the user to the + * resource referenced by this URL. + * Note that the RSS 0.91 Specification dictates that URLs not + * starting with "http://" or "ftp://" are considered invalid. + */ + const KURL &link() const; + + /** + * RSS 0.91 and upwards + * @return A description of what this picture shows, or + * QString::null if no description is available. Useful for + * people who deactivated images but want or need to know what is + * shown. + */ + QString description() const; + + /** + * RSS 0.91 and upwards + * @return The height in pixels as reported by the news site, the + * default value is 31 pixels. The RSS 0.91 Specification requires + * this value to be between 1 and 400. + * '0' if this information isn't available. This is merely provided + * for completeness, you should not rely on this value but rather + * check what height the QPixmap as returned by gotPixmap() + * reports. + */ + unsigned int height() const; + + /** + * RSS 0.91 and upwards + * @return The width in pixels as reported by the news site, the + * default value is 88 pixels. The RSS 0.91 Specification requires + * this value to be between 1 and 144. + * This is merely provided for completeness, you should not rely + * on this value but rather check what width the QPixmap as + * returned by gotPixmap() reports. + */ + unsigned int width() const; + + /** + * Makes the image download the image data as referenced by the + * URL returned by url(). You have to connect to the signal + * gotPixmap() first and then call getPixmap(). + */ + void getPixmap(); + void abort(); + + signals: + /** + * Emitted when this Image is done downloading the actual graphics + * data as referenced by the URL returned by url(). You can trigger + * this download by calling getPixmap(). + * @param pixmap The pixmap as constructed from the data referenced + * by the URL returned by link(). + */ + void gotPixmap(const QPixmap &pixmap); + + private slots: + void slotData(KIO::Job *job, const QByteArray &data); + void slotResult(KIO::Job *job); + + private: + struct Private; + Private *d; + }; +} + +#endif // LIBRSS_IMAGE_H +// vim: noet:ts=4 diff --git a/akregator/src/librss/librss.doxyfile b/akregator/src/librss/librss.doxyfile new file mode 100644 index 000000000..c81ac168e --- /dev/null +++ b/akregator/src/librss/librss.doxyfile @@ -0,0 +1,921 @@ +# Doxyfile 1.2.14 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = librss + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.1 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc/ + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French, +# German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish, +# Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consist of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse. + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the Html help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, +# or Internet explorer 4.0+). Note that for large projects the tree generation +# can take a very long time. In such cases it is better to disable this feature. +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line and do not end with a semicolon. Such function macros are typically +# used for boiler-plate code, and will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yield more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are gif, jpg, and png +# If left blank gif will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermedate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/akregator/src/librss/librss.h b/akregator/src/librss/librss.h new file mode 100644 index 000000000..b99556b1c --- /dev/null +++ b/akregator/src/librss/librss.h @@ -0,0 +1,24 @@ +/* + * librss.h + * + * Copyright (c) 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#ifndef LIBRSS_LIBRSS_H +#define LIBRSS_LIBRSS_H + +#include "article.h" +#include "category.h" +#include "document.h" +#include "enclosure.h" +#include "global.h" +#include "image.h" +#include "loader.h" +#include "textinput.h" + +#endif // LIBRSS_LIBRSS_H +// vim: noet:ts=4 diff --git a/akregator/src/librss/loader.cpp b/akregator/src/librss/loader.cpp new file mode 100644 index 000000000..d63d17676 --- /dev/null +++ b/akregator/src/librss/loader.cpp @@ -0,0 +1,413 @@ +/* + * loader.cpp + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#include "loader.h" +#include "document.h" +#include "feeddetector.h" + +#include <kio/job.h> +#include <kprocess.h> +#include <kstaticdeleter.h> +#include <kurl.h> +#include <kdebug.h> + +#include <qdom.h> +#include <qbuffer.h> +#include <qregexp.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qtimer.h> + +using namespace RSS; + +DataRetriever::DataRetriever() +{ +} + +DataRetriever::~DataRetriever() +{ +} + +class FileRetriever::Private +{ + public: + + Private() + : buffer(NULL), + lastError(0), job(NULL) + { + } + + ~Private() + { + delete buffer; + } + + QBuffer *buffer; + int lastError; + KIO::Job *job; + static KStaticDeleter<QString> userAgentsd; + static QString* userAgent; +}; + +KStaticDeleter<QString> FileRetriever::Private::userAgentsd; +QString* FileRetriever::Private::userAgent = 0L; +FileRetriever::FileRetriever() + : d(new Private) +{ +} + +FileRetriever::~FileRetriever() +{ + delete d; +} + +bool FileRetriever::m_useCache = true; + +QString FileRetriever::userAgent() +{ + if (Private::userAgent == 0L) + FileRetriever::Private::userAgentsd.setObject(Private::userAgent, new QString); + return *Private::userAgent; +} + +void FileRetriever::setUserAgent(const QString &ua) +{ + if (Private::userAgent == 0L) + FileRetriever::Private::userAgentsd.setObject(Private::userAgent, new QString); + (*Private::userAgent) = ua; +} + +void FileRetriever::setUseCache(bool enabled) +{ + m_useCache = enabled; +} + +void FileRetriever::retrieveData(const KURL &url) +{ + if (d->buffer) + return; + + d->buffer = new QBuffer; + d->buffer->open(IO_WriteOnly); + + KURL u=url; + + if (u.protocol()=="feed") + u.setProtocol("http"); + + d->job = KIO::get(u, false, false); + d->job->addMetaData("cache", m_useCache ? "refresh" : "reload"); + + QString ua = userAgent(); + if (!ua.isEmpty()) + d->job->addMetaData("UserAgent", ua); + + + QTimer::singleShot(1000*90, this, SLOT(slotTimeout())); + + connect(d->job, SIGNAL(data(KIO::Job *, const QByteArray &)), + SLOT(slotData(KIO::Job *, const QByteArray &))); + connect(d->job, SIGNAL(result(KIO::Job *)), SLOT(slotResult(KIO::Job *))); + connect(d->job, SIGNAL(permanentRedirection(KIO::Job *, const KURL &, const KURL &)), + SLOT(slotPermanentRedirection(KIO::Job *, const KURL &, const KURL &))); +} + +void FileRetriever::slotTimeout() +{ + abort(); + + delete d->buffer; + d->buffer = NULL; + + d->lastError = KIO::ERR_SERVER_TIMEOUT; + + emit dataRetrieved(QByteArray(), false); +} + +int FileRetriever::errorCode() const +{ + return d->lastError; +} + +void FileRetriever::slotData(KIO::Job *, const QByteArray &data) +{ + d->buffer->writeBlock(data.data(), data.size()); +} + +void FileRetriever::slotResult(KIO::Job *job) +{ + QByteArray data = d->buffer->buffer(); + data.detach(); + + delete d->buffer; + d->buffer = NULL; + + d->lastError = job->error(); + emit dataRetrieved(data, d->lastError == 0); +} + +void FileRetriever::slotPermanentRedirection(KIO::Job *, const KURL &, const KURL &newUrl) +{ + emit permanentRedirection(newUrl); +} + +void FileRetriever::abort() +{ + if (d->job) + { + d->job->kill(true); + d->job = NULL; + } +} + +struct OutputRetriever::Private +{ + Private() : process(NULL), + buffer(NULL), + lastError(0) + { + } + + ~Private() + { + delete process; + delete buffer; + } + + KShellProcess *process; + QBuffer *buffer; + int lastError; +}; + +OutputRetriever::OutputRetriever() : + d(new Private) +{ +} + +OutputRetriever::~OutputRetriever() +{ + delete d; +} + +void OutputRetriever::retrieveData(const KURL &url) +{ + // Ignore subsequent calls if we didn't finish the previous job yet. + if (d->buffer || d->process) + return; + + d->buffer = new QBuffer; + d->buffer->open(IO_WriteOnly); + + d->process = new KShellProcess(); + connect(d->process, SIGNAL(processExited(KProcess *)), + SLOT(slotExited(KProcess *))); + connect(d->process, SIGNAL(receivedStdout(KProcess *, char *, int)), + SLOT(slotOutput(KProcess *, char *, int))); + *d->process << url.path(); + d->process->start(KProcess::NotifyOnExit, KProcess::Stdout); +} + +int OutputRetriever::errorCode() const +{ + return d->lastError; +} + +void OutputRetriever::slotOutput(KProcess *, char *data, int length) +{ + d->buffer->writeBlock(data, length); +} + +void OutputRetriever::slotExited(KProcess *p) +{ + if (!p->normalExit()) + d->lastError = p->exitStatus(); + + QByteArray data = d->buffer->buffer(); + data.detach(); + + delete d->buffer; + d->buffer = NULL; + + delete d->process; + d->process = NULL; + + emit dataRetrieved(data, p->normalExit() && p->exitStatus() == 0); +} + +struct Loader::Private +{ + Private() : retriever(NULL), + lastError(0) + { + } + + ~Private() + { + delete retriever; + } + + DataRetriever *retriever; + int lastError; + KURL discoveredFeedURL; + KURL url; +}; + +Loader *Loader::create() +{ + return new Loader; +} + +Loader *Loader::create(QObject *object, const char *slot) +{ + Loader *loader = create(); + connect(loader, SIGNAL(loadingComplete(Loader *, Document, Status)), + object, slot); + return loader; +} + +Loader::Loader() : d(new Private) +{ +} + +Loader::~Loader() +{ + delete d; +} + +void Loader::loadFrom(const KURL &url, DataRetriever *retriever) +{ + if (d->retriever != NULL) + return; + + d->url=url; + d->retriever = retriever; + + connect(d->retriever, SIGNAL(dataRetrieved(const QByteArray &, bool)), + this, SLOT(slotRetrieverDone(const QByteArray &, bool))); + + d->retriever->retrieveData(url); +} + +int Loader::errorCode() const +{ + return d->lastError; +} + +void Loader::abort() +{ + if (d && d->retriever) + { + d->retriever->abort(); + delete d->retriever; + d->retriever=NULL; + } + emit loadingComplete(this, QDomDocument(), Aborted); + delete this; +} + +const KURL &Loader::discoveredFeedURL() const +{ + return d->discoveredFeedURL; +} + +void Loader::slotRetrieverDone(const QByteArray &data, bool success) +{ + d->lastError = d->retriever->errorCode(); + + delete d->retriever; + d->retriever = NULL; + + Document rssDoc; + Status status = Success; + + if (success) { + QDomDocument doc; + + /* Some servers insert whitespace before the <?xml...?> declaration. + * QDom doesn't tolerate that (and it's right, that's invalid XML), + * so we strip that. + */ + + const char *charData = data.data(); + int len = data.count(); + + while (len && QChar(*charData).isSpace()) { + --len; + ++charData; + } + + if ( len > 3 && QChar(*charData) == QChar(0357) ) { // 0357 0273 0277 + len -= 3; + charData += 3; + } + QByteArray tmpData; + tmpData.setRawData(charData, len); + + if (doc.setContent(tmpData)) + { + rssDoc = Document(doc); + if (!rssDoc.isValid()) + { + discoverFeeds(tmpData); + status = ParseError; + } + } + else + { + discoverFeeds(tmpData); + status = ParseError; + } + + tmpData.resetRawData(charData, len); + } else + status = RetrieveError; + + emit loadingComplete(this, rssDoc, status); + + delete this; +} + +void Loader::discoverFeeds(const QByteArray &data) +{ + QString str = QString(data).simplifyWhiteSpace(); + + QStringList feeds; + + FeedDetectorEntryList list = FeedDetector::extractFromLinkTags(str); + + for (FeedDetectorEntryList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + feeds += (*it).url(); + } + + if (list.isEmpty()) + feeds = FeedDetector::extractBruteForce(str); + + QString feed = feeds.first(); + QString host = d->url.host(); + KURL testURL; + // loop through, prefer feeds on same host + QStringList::Iterator end( feeds.end() ); + for ( QStringList::Iterator it = feeds.begin(); it != end; ++it) + { + testURL=*it; + if (testURL.host() == host) + { + feed = *it; + break; + } + } + + d->discoveredFeedURL = feed.isNull() ? QString() : FeedDetector::fixRelativeURL(feed, d->url); +} + +#include "loader.moc" +// vim:noet:ts=4 diff --git a/akregator/src/librss/loader.h b/akregator/src/librss/loader.h new file mode 100644 index 000000000..d60bad744 --- /dev/null +++ b/akregator/src/librss/loader.h @@ -0,0 +1,341 @@ +/* + * loader.h + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#ifndef LIBRSS_LOADER_H +#define LIBRSS_LOADER_H + +#include "global.h" + +class KURL; + +#include <qobject.h> + +namespace KIO +{ + class Job; +} +class KProcess; + +namespace RSS +{ + class Document; + + /** + * Abstract baseclass for all data retriever classes. Subclass this to add + * a new retrieval algorithm which can then be plugged into the RSS loader. + * @see Loader, FileRetriever, OutputRetriever + */ + class KDE_EXPORT DataRetriever : public QObject + { + Q_OBJECT + public: + /** + * Default constructor. + */ + DataRetriever(); + + /** + * Destructor. + */ + virtual ~DataRetriever(); + + /** + * Retrieve data from the given URL. This method is supposed to get + * reimplemented by subclasses. It will be called by the Loader + * class in case it needs to retrieve the data. + * @see Loader::loadFrom() + */ + virtual void retrieveData(const KURL &url) = 0; + + /** + * @return An error code which might give a more precise information + * about what went wrong in case the 'success' flag returned with + * the dataRetrieved() signal was 'false'. Note that the meaning of + * the returned integer depends on the actual data retriever. + */ + virtual int errorCode() const = 0; + + virtual void abort() = 0; + signals: + /** + * Emit this signal to tell the Loader class that the retrieval + * process was finished. + * @param data Should contain the retrieved data and will get + * parsed by the Loader class. + * @param success Indicates whether there were any problems during + * the retrieval process. Pass 'true' to indicate that everything + * went seamlessy, 'false' to tell the Loader that something went + * wrong and that the data parameter might contain no or invalid + * data. + */ + void dataRetrieved(const QByteArray &data, bool success); + + private: + DataRetriever(const DataRetriever &other); + DataRetriever &operator=(const DataRetriever &other); + }; + + /** + * Implements a file retriever, to be used with Loader::loadFrom(). + * @see DataRetriever, Loader::loadFrom() + */ + class KDE_EXPORT FileRetriever : public DataRetriever + { + Q_OBJECT + public: + /** + * Default constructor. + */ + FileRetriever(); + + /** + * Destructor. + */ + virtual ~FileRetriever(); + + /** + * Downloads the file referenced by the given URL and passes it's + * contents on to the Loader. + * @param url An URL referencing a file which is assumed to + * reference valid XML. + * @see Loader::loadFrom() + */ + virtual void retrieveData(const KURL &url); + + /** + * @return The error code for the last process of retrieving data. + * The returned numbers correspond directly to the error codes + * <a href="http://developer.kde.org/documentation/library/cvs-api/classref/kio/KIO.html#Error">as + * defined by KIO</a>. + */ + virtual int errorCode() const; + + virtual void abort(); + + static void setUseCache(bool enabled); + static void setUserAgent(const QString &ua); + static QString userAgent(); + + signals: + /** + * Signals a permanent redirection. The redirection itself is + * handled internally, so you don't need to call Loader::loadFrom() + * with the new URL. This signal is useful in case you want to + * notify the user, or adjust a database entry. + * @see Loader::loadFrom() + */ + void permanentRedirection(const KURL &url); + + protected slots: + void slotTimeout(); + + private slots: + void slotData(KIO::Job *job, const QByteArray &data); + void slotResult(KIO::Job *job); + void slotPermanentRedirection(KIO::Job *job, const KURL &fromUrl, + const KURL &toUrl); + + private: + static bool m_useCache; + + FileRetriever(const FileRetriever &other); + FileRetriever &operator=(const FileRetriever &other); + + struct Private; + Private *d; + }; + + /** + * Implements a data retriever which executes a program and stores returned + * by the program on stdout. To be used with Loader::loadFrom(). + * @see DataRetriever, Loader::loadFrom() + */ + class OutputRetriever : public DataRetriever + { + Q_OBJECT + public: + /** + * Default constructor. + */ + OutputRetriever(); + + /** + * Destructor. + */ + virtual ~OutputRetriever(); + + /** + * Executes the program referenced by the given URL and retrieves + * the data which the program prints to stdout. + * @param url An URL which is supposed to reference an executable + * file. + * @see Loader::loadFrom() + */ + virtual void retrieveData(const KURL &url); + + /** + * @return The error code for the last process of retrieving data. + * 0 is returned in case there was no error, otherwise an error + * code which depends on the particular program which was run is + * returned. + */ + virtual int errorCode() const; + + virtual void abort() {} + + private slots: + void slotOutput(KProcess *process, char *data, int length); + void slotExited(KProcess *process); + + private: + OutputRetriever(const OutputRetriever &other); + OutputRetriever &operator=(const OutputRetriever &other); + + struct Private; + Private *d; + }; + + /** + * This class is the preferred way of loading RSS files. Usage is very + * straightforward: + * + * \code + * Loader *loader = Loader::create(); + * connect(loader, SIGNAL(loadingComplete(Loader *, Document, Status)), + * this, SLOT(slotLoadingComplete(Loader *, Document, Status))); + * loader->loadFrom("http://www.blah.org/foobar.rdf", new FileRetriever); + * \endcode + * + * This creates a Loader object, connects it's loadingComplete() signal to + * your custom slot and then makes it load the file + * 'http://www.blah.org/foobar.rdf' using the FileRetriever. You could've + * done something like this as well: + * + * \code + * // create the Loader, connect it's signal... + * loader->loadFrom("/home/myself/some-script.py", new OutputRetriever); + * \endcode + * + * That'd make the Loader use another algorithm for retrieving the RSS data; + * 'OutputRetriever' will make it execute the script + * '/home/myself/some-script.py' and assume whatever that script prints to + * stdout is RSS markup. This is e.g. handy for conversion scripts, which + * download an HTML file and convert it's contents into RSS markup. + * + * No matter what kind of retrieval algorithm you employ, your + * 'slotLoadingComplete' method might look like this: + * + * \code + * void MyClass::slotLoadingComplete(Loader *loader, Document doc, Status status) + * { + * // Note that Loader::~Loader() is private, so you cannot delete Loader instances. + * // You don't need to do that anyway since Loader instances delete themselves. + * + * if (status != RSS::Success) + * return; + * + * QString title = doc.title(); + * // do whatever you want with the information. + * } + * \endcode + * + * \note You have to create a copy of the passed Document instance in + * case you want/need to use it after the slot attached to the + * loadingComplete signal goes out of scope. This is e.g. the case if you + * intend to call getPixmap() on Document::image()! + */ + class KDE_EXPORT Loader : public QObject + { + Q_OBJECT + friend class someClassWhichDoesNotExist; + public: + /** + * Constructs a Loader instance. This is pretty much what the + * default constructor would do, except that it ensures that all + * Loader instances have been allocated on the heap (this is + * required so that Loader's can delete themselves safely after they + * emitted the loadingComplete() signal.). + * @return A pointer to a new Loader instance. + */ + static Loader *create(); + + /** + * Convenience method. Does the same as the above method except that + * it also does the job of connecting the loadingComplete() signal + * to the given slot for you. + * @param object A QObject which features the specified slot + * @param slot Which slot to connect to. + */ + static Loader *create(QObject *object, const char *slot); + + /** + * Loads the RSS file referenced by the given URL using the + * specified retrieval algorithm. Make sure that you connected + * to the loadingComplete() signal before calling this method so + * that you're guaranteed to get notified when the loading finished. + * \note A Loader object cannot load from multiple URLs simultaneously; + * consequently, subsequent calls to loadFrom will be discarded + * silently, only the first loadFrom request will be executed. + * @param url An URL referencing the input file. + * @param retriever A subclass of DataRetriever which implements a + * specialized retrieval behaviour. Note that the ownership of the + * retriever is transferred to the Loader, i.e. the Loader will + * delete it when it doesn't need it anymore. + * @see DataRetriever, Loader::loadingComplete() + */ + void loadFrom(const KURL &url, DataRetriever *retriever); + + /** + * Retrieves the error code of the last loading process (if any), + * as reported by the employed data retrever. + */ + int errorCode() const; + + const KURL &discoveredFeedURL() const; + + void abort(); + + signals: + /** + * This signal gets emitted when the loading process triggered by + * calling loadFrom() finished. + * @param loader A pointer pointing to the loader object which + * emitted this signal; this is handy in case you connect multiple + * loaders to a single slot. + * @param doc In case status is Success, this parameter holds the + * parsed RSS file. In case it's RetrieveError, you should query + * loader->errorCode() for the actual error code. + * Note that you have to create a copy of the passed Document + * instance in case you want/need to use it after the slot attached + * to the loadingComplete signal goes out of scope. This is e.g. + * the case if you intend to call getPixmap() on Document::image()! + * @param status A status byte telling whether there were any problems + * while retrieving or parsing the data. + * @see Document, Status + */ + void loadingComplete(Loader *loader, Document doc, Status status); + + private slots: + void slotRetrieverDone(const QByteArray &data, bool success); + + private: + Loader(); + Loader(const Loader &other); + Loader &operator=(const Loader &other); + ~Loader(); + void discoverFeeds(const QByteArray &data); + + struct Private; + Private *d; + }; +} + +#endif // LIBRSS_LOADER_H +// vim: noet:ts=4 diff --git a/akregator/src/librss/rss-faq.html b/akregator/src/librss/rss-faq.html new file mode 100644 index 000000000..480b19f98 --- /dev/null +++ b/akregator/src/librss/rss-faq.html @@ -0,0 +1,396 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html> +<head> +<title>RSS Headline Syndication - Frequently Asked Questions for Content Providers</title> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252" /> +<meta name="description" content="RSS Headline Syndication - Frequently Asked Questions for Content Providers" /> +<meta name="keywords" content="rss faq, rss, faq, rich site summary, rdf site summary, really simple syndication, headline syndication, syndication" /> +<meta name="robots" content="index,follow" /> +<style type="text/css"> +<!-- +td.content +{ +font-family: Verdana, Arial, Helvetica, sans-serif; +color: #000000; +font-size: 10pt; +font-weight: normal; +} +td.contentbold +{ +font-family: Verdana, Arial, Helvetica, sans-serif; +color: #000000; +font-size: 10pt; +font-weight: bold; +} +td.contentsmall +{ +font-family: Verdana, Arial, Helvetica, sans-serif; +color: #000000; +font-size: 8pt; +font-weight: normal; +} +--> +</style> +</head> +<body bgcolor="#ffffff"> +<p align="right"> +<i> + This document was taken from <a href="http://www.purplepages.ie/rss/">http://www.purplepages.ie/rss/</a>.<br/> + <tt><a href="mailto:raabe@kde.org">Frerich Raabe</a></tt> +</i> +</p> +<table border="0" width="620" cellpadding="1" cellspacing="0"> + <tr> + <td width="620" class="contentbold" colspan="2">RSS Headline Syndication<br /><br /> + <a name="top">Frequently Asked Questions for Content Providers</a> + </td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"> + <br /><a href="#whatishs">1. What is headline syndication?</a><br /> + <a href="#rss">2. What is RSS?</a><br /> + <a href="#whyrss">3. Why syndicate your headlines with RSS?</a><br /> + <a href="#howrss">4. How can I create an RSS file?</a><br /> + <a href="#promoterss">5. How can I promote my RSS file?</a><br /> + <a href="#morerss">6. Where can I find more information about RSS?</a><br /> + </td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + <a name="whatishs">1. What is headline syndication?</a> + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + Websites that publish new content regularly usually provide a list of news headline style links to their latest content. In addition to displaying these headlines on their own websites, it is very common for publishers to make them available for syndication, so that other websites or applications can also include their headlines.<br /><br /> + Headline syndication does not deal with the full text of articles, it is simply about syndicating an automatically updating list of headlines, with each headline being a link to the item that it refers to on the publishers website. + </td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + <a name="rss">2. What is RSS?</a> + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + <a href="#top">top</a> + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + RSS is the name given to a simple and well-established XML format used to syndicate headlines. Once a website creates an RSS file they have created a means to allow others to syndicate their headlines.<br /><br /> + The first version of RSS (RSS 0.9) was released by <a href="http://www.netscape.com/">Netscape</a> in March 1999 as a format for adding news channels to their <a href="http://my.netscape.com/">My.Netscape.Com</a> portal. Then in July 1999 Netscape released RSS 0.91, incorporating most of the features of a format called <scriptingNews>, which was created by <a href="http://www.userland.com/">UserLand</a>. Shortly thereafter Netscape discontinued developing the RSS format, however UserLand persisted and RSS continued to grow in strength. In December 2000, the separate RSS-DEV Working Group released RSS 1.0 and Userland announced RSS 0.92. As of April 2001, Userland is now planning RSS 0.93. Although RSS is not clearly an acronym of anything, different people have called it Rich Site Summary, RDF Site Summary and Really Simple Syndication at different times.<br /><br /> + The lack of clarity in what RSS stands for or which version is the correct one to use can seem confusing to beginners. However these issues don't need to addressed by a website wanting to create an RSS file. RSS is a very well recognised format, in fact it is often referred to as the most successful XML format to date. Some websites have a preference for one version, others create more than one RSS file and support multiple versions and a recent survey suggests that the first two versions of RSS (0.9 and 0.91) are still by far the most popular. + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + <a href="#top">top</a> + </td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + <a name="whyrss">3. Why syndicate your headlines with RSS?</a> + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + Syndicating headlines is an excellent and cost-effective way of driving traffic to, and increasing brand awareness of, any website that publishes new content regularly.<br /><br /> + Once a website produces an RSS file they are enabling others to syndicate their headlines, without any further work on their part.<br /><br /> + The main benefits of creating an RSS file:<br /> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content">RSS content can be included in customisable online news portals that aggregate RSS headlines like <a href="http://my.userland.com/">My.Userland.Com</a>. + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content">Websites that display news headlines can use an RSS file to incorporate another websites headlines into their own. + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content">RSS content can be added to personal desktop news reading applications like <a href="http://www.headlineviewer.com/">Headline Viewer</a> or <a href="http://radio.userland.com/">Radio Userland</a>. + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content">Email newsletter providers could allow users to subscribe to RSS channels. <a href="http://www.xml.com/">XML.com</a> and <a href="http://www.xmltree.com/">XMLTree.com</a> previously offered such a service called Newsboy. + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + One positive side effect of producing an RSS file is that it can also be used by headline aggregation services like <a href="http://www.moreover.com/">Moreover.com</a>, who power news portals, specialist news search engines, business intelligence services or provide newsfeeds to websites. Most such companies use crawler-based technologies to aggregate and do not insist upon content being available in RSS, however they do have some requirements which having an RSS file addresses, sparing the need for any work on the part of a website that already publishes its headlines in RSS. + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + <a href="#top">top</a> + </td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + <a name="howrss">4. How can I create an RSS file?</a> + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + RSS is a simple XML format and anyone who has experience in a mark-up language like HTML or XML should find it very easy to create and maintain an RSS file by hand. + <br /><br />Many websites prefer to generate their RSS file using a programming language, which involves a little more work to begin with but means that maintenance is no longer an issue. + </td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + In this section: + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"> + <a href="#specifications">RSS Specifications</a><br /> + <a href="#validators">RSS Validators</a><br /> + <a href="#tutorials">RSS Tutorials - The Basics</a><br /> + <a href="#tutorialsgen">RSS Tutorials - Generating RSS</a><br /> + <a href="#examples">RSS Examples</a><br /> + <a href="#tools">RSS Tools & Utilities</a><br /> + </td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + <a name="specifications">RSS Specifications:</a><br /> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" valign="top"><strong>RSS 0.93</strong> (Planning stage, April 2001)<br /><a href="http://backend.userland.com/rss093">http://backend.userland.com/rss093</a> (Userland) + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" valign="top"><strong>RSS 0.92</strong> (December 2000)<br /><a href="http://backend.userland.com/rss092">http://backend.userland.com/rss092</a> (Userland)<br /> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" valign="top"><strong>RSS 1.0</strong> (December 2000)<br /><a href="http://groups.yahoo.com/group/rss-dev/files/specification.html">http://groups.yahoo.com/group/rss-dev/files/specification.html</a> (RSS-DEV Working Group)<br /> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" valign="top"><strong>RSS 0.91</strong> (July 1999)<br /><a href="http://backend.userland.com/rss091">http://backend.userland.com/rss091</a> (Userland)<br /> + <a href="http://www.purplepages.ie/RSS/netscape/rss0.91.html">http://www.purplepages.ie/RSS/netscape/rss0.91.html</a> (Netscape)<br /> + <a href="http://my.netscape.com/publish/formats/rss-spec-0.91.html">http://my.netscape.com/publish/formats/rss-spec-0.91.html</a> (Netscape, Revision 3)<br /> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" valign="top"><strong>RSS 0.90</strong> (March 1999)<br /><a href="http://www.purplepages.ie/RSS/netscape/rss0.90.html">http://www.purplepages.ie/RSS/netscape/rss0.90.html</a> (Netscape)<br /> + </td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + <a name="validators">RSS Validators</a>:<br /> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content"><a href="http://aggregator.userland.com/validator">http://aggregator.userland.com/validator</a> (RSS 0.91, RSS 0.92)</td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content"><a href="http://www.bath.ac.uk/~ccslrd/rss_validator/1.0/">http://www.bath.ac.uk/~ccslrd/rss_validator/1.0/</a> (RSS 1.0)</td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content"><a href="http://www.bath.ac.uk/~ccslrd/rss_validator/">http://www.bath.ac.uk/~ccslrd/rss_validator/</a> (RSS 0.9)</td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + <strong><a name="tutorials">RSS Tutorials - The Basics:</a></strong> (See also <a href="#specifications">RSS Specifications</a>, <a href="#websites">Websites</a>)<br /> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"> - <a href="http://www.oreillynet.com/pub/a/network/4000/08/25/magazine/rss_tut.html">A step-by-step guide to building an RSS 1.0 document from the O'Reilly Network.</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"> - <a href="http://publishing.about.com/arts/publishing/library/blrss.htm">An easy to understand introduction to RSS 0.91 from About.com.</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"> - <a href="http://webreference.com/xml/column13/index.html">A comprehensive guide to creating RSS 0.91 files from Webreference.</a></td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + <a name="tutorialsgen">RSS Tutorials - Generating RSS</a>:<br /> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content">Active Server Pages (ASP)<br /> + <a href="http://www.purplepages.ie/site/articles/article.asp?faq=6&fldAuto=76">An article explaining how RSS files can be generated using ASP.</a> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content">Perl<br /> + <a href="http://www.webtechniques.com/archives/2000/02/eisenzopf/">Jonathan Eisenzopf explains how his XML::RSS module can be used to create an RSS file.</a> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content">PHP<br /> + <a href="http://linux.gelrevision.nl/php/">phpChannel, a set of two PHP class files to write rss files.</a> + </td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + <a name="tools">RSS Tools & Utilities</a>: + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + Aaron Swartz provides a useful online utility called <a href="http://logicerror.com/blogifyYourPage">BlogifyYourPage</a>, that makes it easy to produce an RSS 1.0 file for any page.<br /><br /> + The <a href="http://www.webreference.com/perl/tools/">RSS Channel Editor</a> is a simple Perl CGI script that makes it easy to maintain an RSS channel. It can be used online at Webreference and you can also download the source. + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + <a href="#top">top</a> + </td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + <a name="examples">RSS Examples</a>: + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + <a href="http://newsfeeds.manilasites.com/">Newsfeeds</a> reviews sources of RSS files, good examples and ideas you can use in putting together your own feed.<br /><br /> + <a href="http://www.ourfavoritesongs.com/">OurFavoriteSongs.Com</a> is a source of popular syndicated files, the top picks of <a href="http://radio.userland.com/">Radio Userland</a> users. + </td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + <a name="promoterss"><strong>5. How can I promote my RSS file?</strong></a> + <br /><br />There are a couple of important places to register RSS files, firstly <a href="http://www.xmltree.com/">XMLTree.com</a>, a specialist directory of XML content, and secondly <a href="http://my.userland.com/">My.Userland.Com</a>. Once an RSS file has been included in these sources it is likely to be found by websites, online news portals or news reading applications seeking RSS content.<br /><br /> + Websites should also create an information page, about syndicating their headlines. This will make existing users aware that the website has an RSS file so they can add it to their news reading applications or even include it on their own websites.<br /><br /> + This information page will be indexed by regular search engines and can also be submitted to various niche directories: + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.4freecontent.com/">4FreeContent</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.findsticky.com/">FindSticky</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.freesticky.com/">FreeSticky</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://newsfeeds.manilasites.com/">Newsfeeds</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.purplepages.ie/site/content/">Purple Pages</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.woodoggy.com/">WooDoggy</a></td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + Websites that are interested in having their headlines picked up by organisations that aggregate headline content may also wish to visit: + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.linkyournews.com/">LinkYourNews.com</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.magportal.com/">MagPortal.com</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.moreover.com/">Moreover.com</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.newsnow.co.uk/">NewsNow.co.uk</a></td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.newsisfree.com/">NewsIsFree.com</a></td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + <a name="morerss">6. Where can I find more information about RSS?</a><br /><br /> + <a name="websites">Websites</a> + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.oreillynet.com/rss/">O'Reilly DevCenter RSS</a> - Articles about RSS from the O'Reilly Network.</td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://blogspace.com/rss/">RSS Info</a> - News and information on the RSS format</td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://rsswhys.weblogger.com/">RSS Why?s</a> - A site that aims to objectively and concisely explore all the points surrounding the creation, maintenance, and history of RSS.</td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.webreference.com/authoring/languages/xml/rss/">WebReference RSS Articles</a> - A collection of RSS articles and resources from Webreference.</td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + Discussion Lists + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://groups.yahoo.com/group/reallySimpleSyndication">ReallySimpleSyndication</a> - RSS 0.93.</td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://groups.yahoo.com/group/rss-dev">RSS-DEV</a> - RSS 1.0.</td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://groups.yahoo.com/group/syndication">Syndication</a> - XML syndication, mainly RSS 0.91.</td> + </tr> + <tr> + <td width="620" class="contentbold" colspan="2"><br /> + More RSS FAQs + </td> + </tr> + <tr> + <td width="15" class="content" align="center" valign="top">•</td> + <td width="605" class="content" colspan="2"><a href="http://www.voidstar.com/rssfaq">RSS FAQ</a> - A detailed RSS FAQ from Julian Bond, readers can also contribute.</td> + </tr> + <tr> + <td width="620" class="content" colspan="2"><br /> + <a href="#top">top</a> + </td> + </tr> + <tr> + <td width="620" class="contentsmall" colspan="2"><br /><br /><a href="http://www.rssfaq.com/">RSSFAQ</a> Copyright © 2001 Members of the Syndication, RSS-DEV and ReallySimpleSyndication Groups.</td> + </tr> + <tr> + <td width="620" class="contentsmall" colspan="2">You may freely copy and distribute this document. Please give acknowledgements if you do.</td> + </tr> + <tr> + <td width="620" class="contentsmall" colspan="2">Last Updated: 24-August-2001 <a href="mailto:alis@purplepages.ie">Alis Marsden</a>.</td> + </tr> +</table> +</body> +</html> diff --git a/akregator/src/librss/testlibrss.cpp b/akregator/src/librss/testlibrss.cpp new file mode 100644 index 000000000..5d98bba46 --- /dev/null +++ b/akregator/src/librss/testlibrss.cpp @@ -0,0 +1,75 @@ +#include "testlibrss.h" + +#include "image.h" + +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <kapplication.h> +#include <kdebug.h> + +using namespace RSS; + +static const KCmdLineOptions options[] = +{ + { "+url", I18N_NOOP("URL of feed"), 0 }, + KCmdLineLastOption +}; + + +void Tester::test( const QString &url ) +{ + Loader *loader = Loader::create(); + connect( loader, SIGNAL( loadingComplete( Loader *, Document, Status ) ), + this, SLOT( slotLoadingComplete( Loader *, Document, Status ) ) ); + loader->loadFrom( url, new FileRetriever ); +} + +void Tester::slotLoadingComplete( Loader *loader, Document doc, Status status ) +{ + if ( status == Success ) + { + kdDebug() << "Successfully retrieved '" << doc.title() << "'" << endl; + kdDebug() << doc.description() << endl; + + if ( doc.image() ) { + kdDebug() << "Image: "; + kdDebug() << " Title: " << doc.image()->title() << endl; + kdDebug() << " URL: " << doc.image()->url() << endl; + kdDebug() << " Link: " << doc.image()->link() << endl; + } + + kdDebug() << "Articles:" << endl; + + Article::List list = doc.articles(); + Article::List::ConstIterator it; + Article::List::ConstIterator en=list.end(); + for (it = list.begin(); it != en; ++it) + { + kdDebug() << "\tTitle: " << (*it).title() << endl; + kdDebug() << "\tText: " << (*it).description() << endl; + } + } + + if ( status != Success ) + kdDebug() << "ERROR " << loader->errorCode() << endl; + + kapp->quit(); +} + +int main( int argc, char **argv ) +{ + KAboutData aboutData( "testlibrss", "testlibrss", "0.1" ); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app; + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if ( args->count() != 1 ) args->usage(); + + Tester tester; + tester.test( args->arg( 0 ) ); + + return app.exec(); +} + +#include "testlibrss.moc" diff --git a/akregator/src/librss/testlibrss.h b/akregator/src/librss/testlibrss.h new file mode 100644 index 000000000..c65fa3bd2 --- /dev/null +++ b/akregator/src/librss/testlibrss.h @@ -0,0 +1,25 @@ +#ifndef TESTLIBRSS_H +#define TESTLIBRSS_H + +#include <qobject.h> + +#include "loader.h" +#include "document.h" +#include "article.h" +#include "global.h" + +using RSS::Loader; +using RSS::Document; +using RSS::Status; + +class Tester : public QObject +{ + Q_OBJECT + public: + void test( const QString &url ); + + private slots: + void slotLoadingComplete( Loader *loader, Document doc, Status status ); +}; + +#endif diff --git a/akregator/src/librss/textinput.cpp b/akregator/src/librss/textinput.cpp new file mode 100644 index 000000000..432b773aa --- /dev/null +++ b/akregator/src/librss/textinput.cpp @@ -0,0 +1,96 @@ +/* + * textinput.cpp + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#include "textinput.h" +#include "tools_p.h" + +#include <kurl.h> + +#include <qdom.h> + +using namespace RSS; + +struct TextInput::Private : public Shared +{ + QString title; + QString description; + QString name; + KURL link; +}; + +TextInput::TextInput() : d(new Private) +{ +} + +TextInput::TextInput(const TextInput &other) : d(0) +{ + *this = other; +} + +TextInput::TextInput(const QDomNode &node) : d(new Private) +{ + QString elemText; + + if (!(elemText = extractNode(node, QString::fromLatin1("title"))).isNull()) + d->title = elemText; + if (!(elemText = extractNode(node, QString::fromLatin1("description"))).isNull()) + d->description = elemText; + if (!(elemText = extractNode(node, QString::fromLatin1("name")))) + d->name = elemText; + if (!(elemText = extractNode(node, QString::fromLatin1("link"))).isNull()) + d->link = elemText; +} + +TextInput::~TextInput() +{ + if (d->deref()) + delete d; +} + +QString TextInput::title() const +{ + return d->title; +} + +QString TextInput::description() const +{ + return d->description; +} + +QString TextInput::name() const +{ + return d->name; +} + +const KURL &TextInput::link() const +{ + return d->link; +} + +TextInput &TextInput::operator=(const TextInput &other) +{ + if (this != &other) { + other.d->ref(); + if (d && d->deref()) + delete d; + d = other.d; + } + return *this; +} + +bool TextInput::operator==(const TextInput &other) const +{ + return d->title == other.title() && + d->description == other.description() && + d->name == other.name() && + d->link == other.link(); +} + +// vim:noet:ts=4 diff --git a/akregator/src/librss/textinput.h b/akregator/src/librss/textinput.h new file mode 100644 index 000000000..dd13c424b --- /dev/null +++ b/akregator/src/librss/textinput.h @@ -0,0 +1,121 @@ +/* + * textinput.h + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#ifndef LIBRSS_TEXTINPUT_H +#define LIBRSS_TEXTINPUT_H + +#include "global.h" + +class KURL; + +class QDomNode; +class QString; + +namespace RSS +{ + /** + * Represents a text input facility as stored in a RSS file for the purpose + * of allowing users to submit queries back to the publisher's site. You + * don't have to instantiate one of these yourself, the common way to access + * instances is via Document::textInput(). + * @see Document::textInput() + */ + class TextInput + { + public: + /** + * Default constructor. + */ + TextInput(); + + /** + * Copy constructor. + * @param other The TextInput object to copy. + */ + TextInput(const TextInput &other); + + /** + * Constructs a TextInput from a piece of RSS markup. + * @param node A QDomNode which references the DOM leaf to be used + * for constructing the TextInput. + */ + TextInput(const QDomNode &node); + + /** + * Assignment operator. + * @param other The TextInput object to clone. + * @return A reference to the cloned TextInput object. + */ + TextInput &operator=(const TextInput &other); + + /** + * Compares two text inputs. Two text inputs are considered + * identical if their properties (title, description, link etc.) + * are identical. + * @param other The text input to compare with. + * @return Whether the two text inputs are equal. + */ + bool operator==(const TextInput &other) const; + + /** + * Convenience method. Simply calls !operator==(). + * @param other The text input to compared with. + * @return Whether the two text inputs are unequal. + */ + bool operator!=(const TextInput &other) const { return !operator==(other); } + + /** + * Destructor. + */ + virtual ~TextInput(); + + /** + * RSS 0.90 and upwards + * @return The title (often a label to be used for the input field) + * of the text input, or QString::null if no title is available. + */ + QString title() const; + + /** + * RSS 0.90 and upwards + * @return The description (usually used as a tooltip which appears + * if the mouse hovers above the input field for a short time) of + * the text input, or QString::null if no description is + * available. + */ + QString description() const; + + /** + * RSS 0.90 and upwards + * @return The name of the text input (what's this for?) of the + * text input, or QString::null, if no name is available. + */ + QString name() const; + + /** + * RSS 0.90 and upwards + * @return A link to which the contents of the input field should + * be sent after the user specified them. This is often a CGI + * program on a remote server which evaluates the entered + * information. An empty KURL is returned in case no link is + * available. + * Note that the RSS 0.91 Specification dictates that URLs not + * starting with "http://" or "ftp://" are considered invalid. + */ + const KURL &link() const; + + private: + struct Private; + Private *d; + }; +} + +#endif // LIBRSS_TEXTINPUT_H +// vim: noet:ts=4 diff --git a/akregator/src/librss/tools_p.cpp b/akregator/src/librss/tools_p.cpp new file mode 100644 index 000000000..dec831814 --- /dev/null +++ b/akregator/src/librss/tools_p.cpp @@ -0,0 +1,249 @@ +/* + * tools_p.cpp + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#include "tools_p.h" + +#include <krfcdate.h> +#include <qdom.h> +#include <kcharsets.h> +#include <qregexp.h> + +namespace RSS { + +time_t parseISO8601Date(const QString &s) +{ + // do some sanity check: 26-12-2004T00:00+00:00 is parsed to epoch+1 in the KRFCDate, which is wrong. So let's check if the date begins with YYYY -fo + if (s.stripWhiteSpace().left(4).toInt() < 1000) + return 0; // error + + // FIXME: imho this is done in KRFCDate::parseDateISO8601() automatically, so we could omit it? -fo + if (s.find('T') != -1) + return KRFCDate::parseDateISO8601(s); + else + return KRFCDate::parseDateISO8601(s + "T12:00:00"); +} + +QString childNodesAsXML(const QDomNode& parent) +{ + QDomNodeList list = parent.childNodes(); + QString str; + QTextStream ts( &str, IO_WriteOnly ); + for (uint i = 0; i < list.count(); ++i) + ts << list.item(i); + return str.stripWhiteSpace(); +} + +static QString plainTextToHtml(const QString& plainText) +{ + QString str(plainText); + str.replace("&", "&"); + str.replace("\"", """); + str.replace("<", "<"); + //str.replace(">", ">"); + str.replace("\n", "<br/>"); + return str; +} + +enum ContentFormat { Text, HTML, XML, Binary }; + +static ContentFormat mapTypeToFormat(const QString& modep, const QString& typep, const QString& src) +{ + QString mode = modep.isNull() ? "escaped" : modep; + QString type = typep; + + //"If neither the type attribute nor the src attribute is provided, + //Atom Processors MUST behave as though the type attribute were + //present with a value of "text"" + if (type.isNull() && src.isEmpty()) + type = QString::fromUtf8("text"); + + if (type == QString::fromUtf8("html") + || type == QString::fromUtf8("text/html")) + return HTML; + + if (type == QString::fromUtf8("text") + || (type.startsWith(QString::fromUtf8("text/"), false) + && !type.startsWith(QString::fromUtf8("text/xml"), false)) + ) + return Text; + + QStringList xmltypes; + xmltypes.append(QString::fromUtf8("xhtml")); + // XML media types as defined in RFC3023: + xmltypes.append(QString::fromUtf8("text/xml")); + xmltypes.append(QString::fromUtf8("application/xml")); + xmltypes.append(QString::fromUtf8("text/xml-external-parsed-entity")); + xmltypes.append(QString::fromUtf8("application/xml-external-parsed-entity")); + xmltypes.append(QString::fromUtf8("application/xml-dtd")); + + + if (xmltypes.contains(type) + || type.endsWith(QString::fromUtf8("+xml"), false) + || type.endsWith(QString::fromUtf8("/xml"), false)) + return XML; + + return Binary; +} + +static QString extractAtomContent(const QDomElement& e) +{ + ContentFormat format = mapTypeToFormat(e.attribute("mode"), + e.attribute("type"), + e.attribute("src")); + + switch (format) + { + case HTML: + { + const bool hasPre = e.text().contains( "<pre>", false ) || e.text().contains( "<pre ", false ); + return KCharsets::resolveEntities( hasPre ? e.text() : e.text().simplifyWhiteSpace() ); + } + case Text: + return plainTextToHtml(e.text().stripWhiteSpace()); + case XML: + return childNodesAsXML(e).simplifyWhiteSpace(); + case Binary: + default: + return QString(); + } + + return QString(); +} + +QString extractNode(const QDomNode &parent, const QString &elemName, bool isInlined) +{ + QDomNode node = parent.namedItem(elemName); + if (node.isNull()) + return QString::null; + + QDomElement e = node.toElement(); + QString result = e.text().stripWhiteSpace(); // let's assume plain text + + if (elemName == "content") // we have Atom here + { + result = extractAtomContent(e); + } + else // check for HTML; not necessary for Atom:content + { + bool hasPre = result.contains("<pre>", false) || result.contains("<pre ", false); + bool hasHtml = hasPre || result.contains("<"); // FIXME: test if we have html, should be more clever -> regexp + if(!isInlined && !hasHtml) // perform nl2br if not a inline elt and it has no html elts + result = result = result.replace(QChar('\n'), "<br />"); + if(!hasPre) // strip white spaces if no <pre> + result = result.simplifyWhiteSpace(); + } + + return result.isEmpty() ? QString::null : result; +} + +QString extractTitle(const QDomNode & parent) +{ + QDomNode node = parent.namedItem(QString::fromLatin1("title")); + if (node.isNull()) + return QString::null; + + QString result = node.toElement().text(); + + result = KCharsets::resolveEntities(KCharsets::resolveEntities(result).replace(QRegExp("<[^>]*>"), "").remove("\\")); + result = result.simplifyWhiteSpace(); + + if (result.isEmpty()) + return QString::null; + + return result; +} + +static void authorFromString(const QString& strp, QString& name, QString& email) +{ + QString str = strp.stripWhiteSpace(); + if (str.isEmpty()) + return; + + // look for something looking like a mail address ( "foo@bar.com", + // "<foo@bar.com>") and extract it + + QRegExp remail("<?([^@\\s<]+@[^>\\s]+)>?"); // FIXME: user "proper" regexp, + // search kmail source for it + + int pos = remail.search(str); + if (pos != -1) + { + QString all = remail.cap(0); + email = remail.cap(1); + str.replace(all, ""); // remove mail address + } + + // simplify the rest and use it as name + + name = str.simplifyWhiteSpace(); + + // after removing the email, str might have + // the format "(Foo M. Bar)". We cut off + // parentheses if there are any. However, if + // str is of the format "Foo M. Bar (President)", + // we should not cut anything. + + QRegExp rename("^\\(([^\\)]*)\\)"); + + pos = rename.search(name); + + if (pos != -1) + { + name = rename.cap(1); + } + + name = name.isEmpty() ? QString() : name; + email = email.isEmpty() ? QString() : email; +} + +QString parseItemAuthor(const QDomElement& element, Format format, Version version) +{ + QString name; + QString email; + + QDomElement dcCreator = element.namedItem("dc:creator").toElement(); + + if (!dcCreator.isNull()) + authorFromString(dcCreator.text(), name, email); + else if (format == AtomFeed) + { + QDomElement atomAuthor = element.namedItem("author").toElement(); + if (atomAuthor.isNull()) + atomAuthor = element.namedItem("atom:author").toElement(); + if (!atomAuthor.isNull()) + { + QDomElement atomName = atomAuthor.namedItem("name").toElement(); + if (atomName.isNull()) + atomName = atomAuthor.namedItem("atom:name").toElement(); + name = atomName.text().stripWhiteSpace(); + + QDomElement atomEmail = atomAuthor.namedItem("email").toElement(); + if (atomEmail.isNull()) + atomEmail = atomAuthor.namedItem("atom:email").toElement(); + email = atomEmail.text().stripWhiteSpace(); + } + } + else if (format == RSSFeed) + { + authorFromString(element.namedItem("author").toElement().text(), name, email); + } + + if (name.isNull()) + name = email; + + if (!email.isNull()) + return QString("<a href=\"mailto:%1\">%2</a>").arg(email).arg(name); + else + return name; +} + +} // namespace RSS + +// vim:noet:ts=4 diff --git a/akregator/src/librss/tools_p.h b/akregator/src/librss/tools_p.h new file mode 100644 index 000000000..a257da8a6 --- /dev/null +++ b/akregator/src/librss/tools_p.h @@ -0,0 +1,40 @@ +/* + * tools_p.h + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#ifndef LIBRSS_TOOLS_P_H +#define LIBRSS_TOOLS_P_H + +#include "global.h" +#include <time.h> + + +class QDomNode; +class QDomElement; +class QString; + +namespace RSS +{ + struct Shared + { + Shared() : count(1) { } + void ref() { count++; } + bool deref() { return !--count; } + unsigned int count; + }; + + QString extractNode(const QDomNode &parent, const QString &elemName, bool isInlined=true); + QString extractTitle(const QDomNode &parent); + QString childNodesAsXML(const QDomNode& parent); + time_t parseISO8601Date(const QString &s); + QString parseItemAuthor(const QDomElement& element, Format format, Version version); +} + +#endif // LIBRSS_TOOLS_P_H +// vim:noet:ts=4 diff --git a/akregator/src/listtabwidget.cpp b/akregator/src/listtabwidget.cpp new file mode 100644 index 000000000..78f1d0d30 --- /dev/null +++ b/akregator/src/listtabwidget.cpp @@ -0,0 +1,234 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "listtabwidget.h" +#include "feedlistview.h" +#include "folder.h" +#include "treenode.h" + +#include <kmultitabbar.h> + +#include <qiconset.h> +#include <qlayout.h> +#include <qmap.h> +#include <qptrlist.h> +#include <qstring.h> +#include <qvaluelist.h> +#include <qwidgetstack.h> + +#include <kdebug.h> + +namespace Akregator { + +class ListTabWidget::ListTabWidgetPrivate +{ + +public: + int idCounter; + KMultiTabBar* tabBar; + QWidgetStack* stack; + NodeListView* current; + int currentID; + QValueList<NodeListView*> views; + QMap<int, NodeListView*> idToView; + QHBoxLayout* layout; + ViewMode viewMode; + QMap<QWidget*, QString> captions; +}; + + +void ListTabWidget::slotItemUp() +{ + if (d->current) + d->current->slotItemUp(); +} + +void ListTabWidget::slotItemDown() +{ + if (d->current) + d->current->slotItemDown(); +} + +void ListTabWidget::slotItemBegin() +{ + if (d->current) + d->current->slotItemBegin(); +} + +void ListTabWidget::slotItemEnd() +{ + if (d->current) + d->current->slotItemEnd(); +} + +void ListTabWidget::slotItemLeft() +{ + if (d->current) + d->current->slotItemLeft(); +} + +void ListTabWidget::slotItemRight() +{ + if (d->current) + d->current->slotItemRight(); +} + +void ListTabWidget::slotPrevFeed() +{ + if (d->current) + d->current->slotPrevFeed(); +} + +void ListTabWidget::slotNextFeed() +{ + if (d->current) + d->current->slotNextFeed(); +} + +void ListTabWidget::slotPrevUnreadFeed() +{ + if (d->current) + d->current->slotPrevUnreadFeed(); +} + +void ListTabWidget::slotNextUnreadFeed() +{ + if (d->current) + d->current->slotNextUnreadFeed(); +} + +void ListTabWidget::slotRootNodeChanged(NodeListView* view, TreeNode* node) +{ +/* + int unread = node->unread(); + if (unread > 0) + { + //uncomment this to append unread count + //d->tabWidget->changeTab(view, QString("<qt>%1 (%2)").arg(d->captions[view]).arg(node->unread())); + d->tabWidget->changeTab(view, d->captions[view]); + } + else + { + d->tabWidget->changeTab(view, d->captions[view]); + } +*/ +} + +void ListTabWidget::slotTabClicked(int id) +{ + NodeListView* view = d->idToView[id]; + if (view) + { + d->stack->raiseWidget(view); + d->current = view; + + if (d->currentID >= 0) + d->tabBar->setTab(d->currentID, false); + d->currentID = id; + d->tabBar->setTab(d->currentID, true); + + emit signalNodeSelected(d->current->selectedNode()); + } +} + +ListTabWidget::ListTabWidget(QWidget* parent, const char* name) : QWidget(parent, name), d(new ListTabWidgetPrivate) +{ + d->idCounter = 0; + d->current = 0; + d->currentID = -1; + d->viewMode = verticalTabs; + d->layout = new QHBoxLayout(this); + //d->layout = new QGridLayout(this, 1, 2); + d->tabBar = new KMultiTabBar(KMultiTabBar::Vertical, this); + d->tabBar->setStyle(KMultiTabBar::KDEV3ICON); + //d->tabBar->setStyle(KMultiTabBar::KDEV3); + d->tabBar->showActiveTabTexts(true); + d->tabBar->setPosition(KMultiTabBar::Left); + d->layout->addWidget(d->tabBar/*, 0, 0*/); + + d->stack = new QWidgetStack(this); + d->layout->addWidget(d->stack/*, 0, 1*/); + +// connect(d->tabBar, SIGNAL(currentChanged(QWidget*)), this, SLOT(slotCurrentChanged(QWidget*))); +} + +ListTabWidget::~ListTabWidget() +{ + delete d; + d = 0; +} + + +void ListTabWidget::setViewMode(ViewMode mode) +{ + if (mode == d->viewMode) + return; + + d->viewMode = mode; + + // if mode is "single", we hide the tab bar + d->tabBar->setHidden(mode == single); +} + +ListTabWidget::ViewMode ListTabWidget::viewMode() const +{ + return d->viewMode; +} + +void ListTabWidget::addView(NodeListView* view, const QString& caption, const QPixmap& icon) +{ + d->captions[view] = caption; + + view->reparent(d->stack, QPoint(0,0)); + d->stack->addWidget(view); + + int tabId = d->idCounter++; + d->tabBar->appendTab(icon, tabId, caption); + d->idToView[tabId] = view; + connect(d->tabBar->tab(tabId), SIGNAL(clicked(int)), this, SLOT(slotTabClicked(int))); + + + connect(view, SIGNAL(signalNodeSelected(TreeNode*)), this, SIGNAL(signalNodeSelected(TreeNode*))); + connect(view, SIGNAL(signalRootNodeChanged(NodeListView*, TreeNode*)), this, SLOT(slotRootNodeChanged(NodeListView*, TreeNode*))); + + + if (tabId == 0) // first widget + { + d->current = view; + d->currentID = tabId; + d->tabBar->setTab(d->currentID, true); + d->stack->raiseWidget(view); + } +} + +NodeListView* ListTabWidget::activeView() const +{ + return d->current; +} + +} + +#include "listtabwidget.h" + +#include "listtabwidget.moc" diff --git a/akregator/src/listtabwidget.h b/akregator/src/listtabwidget.h new file mode 100644 index 000000000..72e423e84 --- /dev/null +++ b/akregator/src/listtabwidget.h @@ -0,0 +1,99 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_LISTTABWIDGET_H +#define AKREGATOR_LISTTABWIDGET_H + +#include <qwidget.h> + +class QIconSet; +class QPixmap; +class QString; + +namespace Akregator { + +class NodeListView; +class TreeNode; + +/** A widget containing multiple list views, e.g. feed list, tag list etc. + It also forwards slot calls triggered by keyboard shortcuts to the currently active view. + Lists are added to a tab widget + + @author Frank Osterfeld + */ +class ListTabWidget : public QWidget +{ + +Q_OBJECT + +public: + + ListTabWidget(QWidget* parent=0, const char* name=0); + virtual ~ListTabWidget(); + + enum ViewMode { single, /* horizontalTabs, */ verticalTabs }; + + void setViewMode(ViewMode mode); + ViewMode viewMode() const; + + void addView(NodeListView* view, const QString& caption, const QPixmap& icon); + + NodeListView* activeView() const; + +public slots: + + /** go one item up */ + void slotItemUp(); + /** go one item down */ + void slotItemDown(); + /** select the first item in the list */ + void slotItemBegin(); + /** select last item in the list */ + void slotItemEnd(); + /** go to parent item */ + void slotItemLeft(); + /** go to first child */ + void slotItemRight(); + + void slotPrevFeed(); + void slotNextFeed(); + void slotPrevUnreadFeed(); + void slotNextUnreadFeed(); + +signals: + void signalNodeSelected(TreeNode*); + +protected slots: + + void slotRootNodeChanged(NodeListView*, TreeNode*); + void slotTabClicked(int id); + +private: + class ListTabWidgetPrivate; + ListTabWidgetPrivate* d; +}; + +} // namespace Akregator + +#endif // AKREGATOR_LISTTABWIDGET_H diff --git a/akregator/src/main.cpp b/akregator/src/main.cpp new file mode 100644 index 000000000..5c07d0cd4 --- /dev/null +++ b/akregator/src/main.cpp @@ -0,0 +1,115 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qstringlist.h> + +#include <dcopref.h> +#include <kcmdlineargs.h> +#include <klocale.h> +#include <knotifyclient.h> +#include <kuniqueapplication.h> + +#include "aboutdata.h" +#include "mainwindow.h" +#include "akregator_options.h" + +namespace Akregator { + +class Application : public KUniqueApplication { + public: + Application() : mMainWindow( ) {} + ~Application() {} + + int newInstance(); + + private: + Akregator::MainWindow *mMainWindow; +}; + +int Application::newInstance() +{ + if (!isRestored()) + { + DCOPRef akr("akregator", "AkregatorIface"); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + if ( !mMainWindow ) { + mMainWindow = new Akregator::MainWindow(); + setMainWidget( mMainWindow ); + mMainWindow->loadPart(); + mMainWindow->setupProgressWidgets(); + if (!args->isSet("hide-mainwindow")) + mMainWindow->show(); + akr.send("openStandardFeedList"); + } + + QString addFeedGroup = !args->getOption("group").isEmpty() + ? QString::fromLocal8Bit(args->getOption("group")) + : i18n("Imported Folder"); + + QCStringList feeds = args->getOptionList("addfeed"); + QStringList feedsToAdd; + QCStringList::ConstIterator end( feeds.end() ); + for (QCStringList::ConstIterator it = feeds.begin(); it != end; ++it) + feedsToAdd.append(*it); + + if (!feedsToAdd.isEmpty()) + akr.send("addFeedsToGroup", feedsToAdd, addFeedGroup ); + + args->clear(); + } + return KUniqueApplication::newInstance(); +} + +} // namespace Akregator + +int main(int argc, char **argv) +{ + Akregator::AboutData about; + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions( Akregator::akregator_options ); + KUniqueApplication::addCmdLineOptions(); + + Akregator::Application app; + + // start knotifyclient if not already started. makes it work for people who doesn't use full kde, according to kmail devels + KNotifyClient::startDaemon(); + + // see if we are starting with session management + if (app.isRestored()) + { +#undef RESTORE +#define RESTORE(type) { int n = 1;\ + while (KMainWindow::canBeRestored(n)){\ + (new type)->restore(n, false);\ + n++;}} + + RESTORE(Akregator::MainWindow); + } + + return app.exec(); +} + + diff --git a/akregator/src/mainwindow.cpp b/akregator/src/mainwindow.cpp new file mode 100644 index 000000000..41f307b8e --- /dev/null +++ b/akregator/src/mainwindow.cpp @@ -0,0 +1,290 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "mainwindow.h" +#include "akregator_part.h" +#include "akregatorconfig.h" + +//settings + +#include <dcopclient.h> + +#include <kaction.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kedittoolbar.h> +#include <kfiledialog.h> +#include <kglobal.h> +#include <kkeydialog.h> +#include <klibloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kparts/partmanager.h> +#include <ksqueezedtextlabel.h> +#include <kstandarddirs.h> +#include <kstatusbar.h> +#include <kstdaction.h> +#include <kurl.h> + +#include "progressdialog.h" +#include "statusbarprogresswidget.h" +#include "trayicon.h" + +#include <qmetaobject.h> +#include <qpen.h> +#include <qpainter.h> +#include <private/qucomextra_p.h> +#include <qtimer.h> + + +namespace Akregator { + +BrowserInterface::BrowserInterface( MainWindow *shell, const char *name ) + : KParts::BrowserInterface( shell, name ) +{ + m_shell = shell; +} + +MainWindow::MainWindow() + : KParts::MainWindow( 0L, "akregator_mainwindow" ){ + // set the shell's ui resource file + setXMLFile("akregator_shell.rc"); + + m_browserIface=new BrowserInterface(this, "browser_interface"); + + m_part=0; + + // then, setup our actions + + toolBar()->show(); + // and a status bar + statusBar()->show(); + + int statH=fontMetrics().height()+2; + m_statusLabel = new KSqueezedTextLabel(this); + m_statusLabel->setTextFormat(Qt::RichText); + m_statusLabel->setSizePolicy(QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Fixed )); + m_statusLabel->setMinimumWidth( 0 ); + m_statusLabel->setFixedHeight( statH ); + statusBar()->addWidget (m_statusLabel, 1, false); + + setupActions(); + createGUI(0L); +} + +bool MainWindow::loadPart() +{ + // this routine will find and load our Part. it finds the Part by + // name which is a bad idea usually.. but it's alright in this + // case since our Part is made for this Shell + KLibFactory *factory = KLibLoader::self()->factory("libakregatorpart"); + if (factory) + { + // now that the Part is loaded, we cast it to a Part to get + // our hands on it + m_part = static_cast<Akregator::Part*>(factory->create(this, "akregator_part", "KParts::ReadOnlyPart" )); + + if (m_part) + { + // tell the KParts::MainWindow that this is indeed the main widget + setCentralWidget(m_part->widget()); + + connect(m_part, SIGNAL(setWindowCaption (const QString &)), this, SLOT(setCaption (const QString &))); + + connect(TrayIcon::getInstance(), SIGNAL(quitSelected()), this, SLOT(slotQuit())); + // and integrate the part's GUI with the shell's + connectActionCollection(m_part->actionCollection()); + createGUI(m_part); + browserExtension(m_part)->setBrowserInterface(m_browserIface); + setAutoSaveSettings(); + return true; + } + return false; + } + else + { + KMessageBox::error(this, i18n("Could not find the Akregator part; please check your installation.")); + return false; + } + +} + +void MainWindow::setupProgressWidgets() +{ + KPIM::ProgressDialog *progressDialog = new KPIM::ProgressDialog( statusBar(), this ); + progressDialog->raise(); + progressDialog->hide(); + m_progressBar = new KPIM::StatusbarProgressWidget( progressDialog, statusBar() ); + m_progressBar->show(); + statusBar()->addWidget( m_progressBar, 0, true ); +} + +MainWindow::~MainWindow() +{ +} + +void MainWindow::setCaption(const QString &a) +{ + KParts::MainWindow::setCaption(a); +} + +void MainWindow::setupActions() +{ + connectActionCollection(actionCollection()); + + KStdAction::quit(kapp, SLOT(quit()), actionCollection()); + + setStandardToolBarMenuEnabled(true); + createStandardStatusBarAction(); + + KStdAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection()); + KStdAction::configureToolbars(this, SLOT(optionsConfigureToolbars()), actionCollection()); +} + +void MainWindow::saveProperties(KConfig* config) +{ + if (!m_part) + loadPart(); + + static_cast<Akregator::Part*>(m_part)->saveProperties(config); + config->writeEntry("docked", isHidden()); + + //delete m_part; +} + +void MainWindow::readProperties(KConfig* config) +{ + if (!m_part) + loadPart(); + static_cast<Akregator::Part*>(m_part)->readProperties(config); + + if (Settings::showTrayIcon() && config->readBoolEntry("docked", false)) + hide(); + else + show(); +} + +void MainWindow::optionsConfigureKeys() +{ + KKeyDialog dlg( true, this ); + + dlg.insert(actionCollection()); + if (m_part) + dlg.insert(m_part->actionCollection()); + + dlg.configure(); +} + +void MainWindow::optionsConfigureToolbars() +{ + saveMainWindowSettings(KGlobal::config(), autoSaveGroup()); + + // use the standard toolbar editor + KEditToolbar dlg(factory()); + connect(&dlg, SIGNAL(newToolbarConfig()), + this, SLOT(applyNewToolbarConfig())); + dlg.exec(); +} + + + +void MainWindow::applyNewToolbarConfig() +{ + applyMainWindowSettings(KGlobal::config(), autoSaveGroup()); +} + + +KParts::BrowserExtension *MainWindow::browserExtension(KParts::ReadOnlyPart *p) +{ + return KParts::BrowserExtension::childObject( p ); +} + + +// from konqmainwindow +void MainWindow::connectActionCollection( KActionCollection *coll ) +{ + if (!coll) return; + connect( coll, SIGNAL( actionStatusText( const QString & ) ), + m_statusLabel, SLOT( setText( const QString & ) ) ); + connect( coll, SIGNAL( clearStatusText() ), + this, SLOT( slotClearStatusText() ) ); +} + +bool MainWindow::queryExit() +{ + kdDebug() << "MainWindow::queryExit()" << endl; + if ( !kapp->sessionSaving() ) + { + delete m_part; // delete that here instead of dtor to ensure nested khtmlparts are deleted before singleton objects like KHTMLPageCache + m_part = 0; + } + else + kdDebug("MainWindow::queryExit(): saving session"); + + return KMainWindow::queryExit(); +} + +void MainWindow::slotQuit() +{ + if (TrayIcon::getInstance()) + TrayIcon::getInstance()->hide(); + kapp->quit(); +} + +bool MainWindow::queryClose() +{ + if (kapp->sessionSaving() || TrayIcon::getInstance() == 0 || TrayIcon::getInstance()->isHidden() ) + { + return true; + } + else + { + QPixmap shot = TrayIcon::getInstance()->takeScreenshot(); + + // Associate source to image and show the dialog: + QMimeSourceFactory::defaultFactory()->setPixmap("systray_shot", shot); + KMessageBox::information(this, i18n( "<qt><p>Closing the main window will keep Akregator running in the system tray. Use 'Quit' from the 'File' menu to quit the application.</p><p><center><img source=\"systray_shot\"></center></p></qt>" ), i18n( "Docking in System Tray" ), "hideOnCloseInfo"); + hide(); + return false; + } +} + + +void MainWindow::slotClearStatusText() +{ + m_statusLabel->setText(QString()); +} + +void MainWindow::slotSetStatusBarText( const QString & text ) +{ + m_statusLabel->setText(text); +} + +} // namespace Akregator + +#include "mainwindow.moc" + + +// vim: set et ts=4 sts=4 sw=4: diff --git a/akregator/src/mainwindow.h b/akregator/src/mainwindow.h new file mode 100644 index 000000000..c88b08174 --- /dev/null +++ b/akregator/src/mainwindow.h @@ -0,0 +1,151 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef _AKREGATOR_H_ +#define _AKREGATOR_H_ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kapplication.h> +#include <kparts/mainwindow.h> +#include <kparts/browserinterface.h> +#include <kparts/browserextension.h> +#include <kio/job.h> + +class KActionCollection; +class KToggleAction; +class KSqueezedTextLabel; +class KProgress; +class KParts::BrowserExtension; +class KParts::PartManager; + +namespace KPIM +{ + class StatusbarProgressWidget; +} + +namespace Akregator +{ + +class Part; +class MainWindow; + +class BrowserInterface : public KParts::BrowserInterface +{ + Q_OBJECT + +public: + BrowserInterface(Akregator::MainWindow *shell, const char *name ); + +private: + Akregator::MainWindow *m_shell; +}; + + +/** + * This is the application "Shell". It has a menubar, toolbar, and + * statusbar but relies on the "Part" to do all the real work. + * + * @short Application Shell + */ +class MainWindow : public KParts::MainWindow +{ + Q_OBJECT +public: + + MainWindow(); + virtual ~MainWindow(); + + /** + * Creates the progress widget in the status bar and the ProgressDialog + * and connects them. + */ + void setupProgressWidgets(); + + virtual void setCaption(const QString &); + + /** + Loads the part + @return Whether the part has been successfully created or not. + */ + bool loadPart(); + +public slots: + void slotClearStatusText(); + void slotSetStatusBarText(const QString &c); + +protected: + /** + * This method is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + + /** + * This method is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties + */ + void readProperties(KConfig *); + /** + * Reimplemented to save settings + */ + virtual bool queryExit(); + + /** + * Reimplemented to say if app will be running in system tray if necessary + */ + virtual bool queryClose(); + +protected slots: + + void slotQuit(); + +private: + + void setupActions(); + void connectActionCollection(KActionCollection *coll); + + KParts::BrowserExtension *browserExtension(KParts::ReadOnlyPart *p); + +private slots: + + void optionsConfigureKeys(); + void optionsConfigureToolbars(); + + void applyNewToolbarConfig(); + +private: + BrowserInterface *m_browserIface; + + Akregator::Part *m_part; + KPIM::StatusbarProgressWidget *m_progressBar; + KSqueezedTextLabel *m_statusLabel; +}; + +} // namespace Akregator + +#endif // _AKREGATOR_H_ diff --git a/akregator/src/mk4storage/Makefile.am b/akregator/src/mk4storage/Makefile.am new file mode 100644 index 000000000..4f2ebfae5 --- /dev/null +++ b/akregator/src/mk4storage/Makefile.am @@ -0,0 +1,30 @@ +kde_module_LTLIBRARIES = libakregator_mk4storage_plugin.la + +SUBDIRS = metakit + +INCLUDES = \ + -I$(top_srcdir)/akregator/src \ + -I$(top_srcdir)/akregator/src/mk4storage/metakit/include \ + $(all_includes) + +libakregator_mk4storage_plugin_la_LIBADD = \ + $(top_builddir)/akregator/src/mk4storage/metakit/src/libmetakitlocal.la \ + $(top_builddir)/akregator/src/librss/librsslocal.la \ + ../libakregatorprivate.la \ + $(LIB_KFILE) \ + $(LIB_KDECORE) + +libakregator_mk4storage_plugin_la_LDFLAGS = \ + $(KDE_PLUGIN) \ + $(all_libraries) + +libakregator_mk4storage_plugin_la_SOURCES = feedstoragemk4impl.cpp \ + storagemk4impl.cpp \ + storagefactorymk4impl.cpp \ + mk4plugin.cpp + +METASOURCES = AUTO + +kde_services_DATA = akregator_mk4storage_plugin.desktop + +kde_kcfg_DATA = mk4config.kcfg diff --git a/akregator/src/mk4storage/akregator_mk4storage_plugin.desktop b/akregator/src/mk4storage/akregator_mk4storage_plugin.desktop new file mode 100644 index 000000000..c7d899a7a --- /dev/null +++ b/akregator/src/mk4storage/akregator_mk4storage_plugin.desktop @@ -0,0 +1,109 @@ +[Desktop Entry] +Type=Service +Name=Metakit storage backend +Name[af]=Metakit stoor agterkant +Name[bg]=Приставка за архивиране Metakit +Name[ca]=Dorsal de emmagatzematge Metakit +Name[cs]=Metakit úložiště +Name[da]=Metakit lagringsgrænseflade +Name[de]=Metakit Archiv-Modul +Name[el]=Σύστημα υποστήριξης αποθήκευσης Metakit +Name[eo]=Metakit datumstora maŝino +Name[es]=Dorsal de almacenamiento Metakit +Name[et]=Metakiti salvestamisrakendus +Name[eu]=Metakit-en biltegiratze euskarria +Name[fa]=پشتیبان ذخیرهگاه Metakit +Name[fi]=Metakit-tallennusajuri +Name[fr]=Stockage avec Metakit +Name[fy]=Metakit-opslachefterein +Name[gl]=Manexador do almacenador Metakit +Name[hu]=Metakit tároló +Name[is]=Metakit geymslu bakendi +Name[it]=Backend archiviazione metakit +Name[ja]=メタキットストレージバックエンド +Name[ka]=Metakit მეხსიერების ბუფერი +Name[kk]=Metakit архивтеу бағдарламасы +Name[km]=កម្មវិធីខាងក្រោយសម្រាប់រក្សាទុក (Metakit) +Name[ko]=Metakit 저장소 백엔드 +Name[lt]=Metakit saugojimo programinė sąsaja +Name[ms]=Hujung belakang storan Metakit +Name[nb]=Metakit lagringsbakstykke +Name[nds]=Metakit-Archivmoduul +Name[ne]=मेटाकिट भण्डारण ब्याकइन्ड +Name[nl]=Metakit-opslagbackend +Name[nn]=Metakit-lagringsbakstykke +Name[pl]=System przechowywania Metakit +Name[pt]=Infra-estrutura de armazenamento Metakit +Name[pt_BR]=Mecanismo de armazenamento Metakit +Name[ru]=Движок Metakit +Name[sl]=Shranjevanje Metakit +Name[sr]=Систем за смештање Мета комплета +Name[sr@Latn]=Sistem za smeštanje Meta kompleta +Name[sv]=Metakit lagringsgränssnitt +Name[tr]=Metakit depolama arka ucu +Name[uk]=Програма зберігання Metakit +Name[zh_CN]=Metakit 存储后端 +Name[zh_TW]=Metakit 儲存後端介面 +X-KDE-Library=libakregator_mk4storage_plugin +Comment=Plugin for Akregator +Comment[af]=Inprop module vir Akregator +Comment[be]=Утулка для Akregator +Comment[bg]=Приставка за Akregator +Comment[br]=Lugent evit Akregator +Comment[ca]=Endollable per a l'Akregator +Comment[cs]=Modul pro Akregator +Comment[de]=Modul für Akregator +Comment[el]=Πρόσθετο για το Akregator +Comment[eo]=Kromprogramo por Akregator +Comment[es]=Extensión para Akregator +Comment[et]=Akregatori plugin +Comment[eu]=Akregator-en plugina +Comment[fa]=وصله برای Akregator +Comment[fi]=Liitännäinen Akregatoriin +Comment[fr]=Module pour Akregator +Comment[fy]=Plugin foar Akregator +Comment[ga]=Breiseán Akregator +Comment[gl]=Extensión para Akregator +Comment[he]=תוסף עבור Akregator +Comment[hu]=Akregator bővítőmodul +Comment[is]=Íforrit fyrir Akregator +Comment[it]=Plugin per Akregator +Comment[ja]=Akregator 用プラグイン +Comment[ka]=Akregator-ის მოდული +Comment[kk]=Akregator-дың плагин модулі +Comment[km]=កម្មវិធីជំនួយ Akregator +Comment[ko]=Akregator 플러그인 +Comment[lt]=Akregator skirtas priedas +Comment[mk]=Приклучок за Akregator +Comment[ms]=Plugin untuk Akregator +Comment[nb]=Programtillegg for Akregator +Comment[nds]=Moduul för Akregator +Comment[ne]=एक्रिगेटरका लागि प्लगइन +Comment[nl]=Plugin voor Akregator +Comment[nn]=Programtillegg til Akregator +Comment[pl]=Wtyczka dla Akregatora +Comment[pt]='Plugin' para o Akregator +Comment[pt_BR]=Plug-in para o Akregator +Comment[ru]=Модуль Akregator +Comment[se]=Lassemoduvla Akregatorii +Comment[sk]=Modul pre Akregator +Comment[sl]=Vstavek za Akregator +Comment[sr]=Прикључак за Akregator +Comment[sr@Latn]=Priključak za Akregator +Comment[sv]=Insticksprogram för Akregator +Comment[tr]=Akregator Eklentisi +Comment[uk]=Втулок для Akregator +Comment[uz]=Akregator uchun plagin +Comment[uz@cyrillic]=Akregator учун плагин +Comment[zh_CN]=Akregator 插件 +Comment[zh_TW]=Akregator 外掛程式 +ServiceTypes=Akregator/Plugin + +X-KDE-akregator-plugintype=storage +X-KDE-akregator-name=metakit +X-KDE-akregator-authors=Frank Osterfeld +X-KDE-akregator-email=frank.osterfeld@kdemail.net +X-KDE-akregator-rank=255 +X-KDE-akregator-version=1 +X-KDE-akregator-framework-version=1 + diff --git a/akregator/src/mk4storage/feedstoragemk4impl.cpp b/akregator/src/mk4storage/feedstoragemk4impl.cpp new file mode 100644 index 000000000..521f9f326 --- /dev/null +++ b/akregator/src/mk4storage/feedstoragemk4impl.cpp @@ -0,0 +1,871 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "feedstoragemk4impl.h" +#include "storagemk4impl.h" + +#include "../article.h" +#include "../utils.h" +#include "../librss/article.h" +#include "../librss/document.h" +#include <mk4.h> + +#include <qdom.h> +#include <qfile.h> + +#include <kdebug.h> +#include <kglobal.h> +#include <kstandarddirs.h> + +namespace Akregator { +namespace Backend { + +class FeedStorageMK4Impl::FeedStorageMK4ImplPrivate +{ + public: + FeedStorageMK4ImplPrivate() : + modified(false), + pguid("guid"), + ptitle("title"), + pdescription("description"), + plink("link"), + pcommentsLink("commentsLink"), + ptag("tag"), + pEnclosureType("enclosureType"), + pEnclosureUrl("enclosureUrl"), + pcatTerm("catTerm"), + pcatScheme("catScheme"), + pcatName("catName"), + pauthor("author"), + phash("hash"), + pguidIsHash("guidIsHash"), + pguidIsPermaLink("guidIsPermaLink"), + pcomments("comments"), + pstatus("status"), + ppubDate("pubDate"), + pHasEnclosure("hasEnclosure"), + pEnclosureLength("enclosureLength"), + ptags("tags"), + ptaggedArticles("taggedArticles"), + pcategorizedArticles("categorizedArticles"), + pcategories("categories") + {} + + QString url; + c4_Storage* storage; + StorageMK4Impl* mainStorage; + c4_View archiveView; + + c4_Storage* catStorage; + c4_View catView; + c4_Storage* tagStorage; + c4_View tagView; + bool autoCommit; + bool modified; + bool taggingEnabled; + bool convert; + QString oldArchivePath; + c4_StringProp pguid, ptitle, pdescription, plink, pcommentsLink, ptag, pEnclosureType, pEnclosureUrl, pcatTerm, pcatScheme, pcatName, pauthor; + c4_IntProp phash, pguidIsHash, pguidIsPermaLink, pcomments, pstatus, ppubDate, pHasEnclosure, pEnclosureLength; + c4_ViewProp ptags, ptaggedArticles, pcategorizedArticles, pcategories; +}; + +void FeedStorageMK4Impl::convertOldArchive() +{ + if (!d->convert) + return; + + d->convert = false; + QFile file(d->oldArchivePath); + + if ( !file.open(IO_ReadOnly) ) + return; + + QTextStream stream(&file); + stream.setEncoding(QTextStream::UnicodeUTF8); + QString data=stream.read(); + QDomDocument xmldoc; + + if (!xmldoc.setContent(data)) + return; + + RSS::Document doc(xmldoc); + + RSS::Article::List d_articles = doc.articles(); + RSS::Article::List::ConstIterator it = d_articles.begin(); + RSS::Article::List::ConstIterator en = d_articles.end(); + + int unr = 0; + for (; it != en; ++it) + { + Article a(*it, this); + if (a.status() != Article::Read) + unr++; + } + + setUnread(unr); + markDirty(); + commit(); +} + +FeedStorageMK4Impl::FeedStorageMK4Impl(const QString& url, StorageMK4Impl* main) +{ + d = new FeedStorageMK4ImplPrivate; + d->autoCommit = main->autoCommit(); + d->url = url; + d->mainStorage = main; + d->taggingEnabled = main->taggingEnabled(); + + QString url2 = url; + + if (url.length() > 255) + { + url2 = url.left(200) + QString::number(Akregator::Utils::calcHash(url), 16); + } + + kdDebug() << url2 << endl; + QString t = url2; + QString t2 = url2; + QString filePath = main->archivePath() +"/"+ t.replace("/", "_").replace(":", "_"); + d->oldArchivePath = KGlobal::dirs()->saveLocation("data", "akregator/Archive/") + t2.replace("/", "_").replace(":", "_") + ".xml"; + + d->convert = !QFile::exists(filePath + ".mk4") && QFile::exists(d->oldArchivePath); + d->storage = new c4_Storage((filePath + ".mk4").local8Bit(), true); + + d->archiveView = d->storage->GetAs("articles[guid:S,title:S,hash:I,guidIsHash:I,guidIsPermaLink:I,description:S,link:S,comments:I,commentsLink:S,status:I,pubDate:I,tags[tag:S],hasEnclosure:I,enclosureUrl:S,enclosureType:S,enclosureLength:I,categories[catTerm:S,catScheme:S,catName:S],author:S]"); + + c4_View hash = d->storage->GetAs("archiveHash[_H:I,_R:I]"); + d->archiveView = d->archiveView.Hash(hash, 1); // hash on guid + + d->tagStorage = 0; + + if (d->taggingEnabled) + { + d->tagStorage = new c4_Storage((filePath + ".mk4___TAGS").local8Bit(), true); + d->tagView = d->tagStorage->GetAs("tagIndex[tag:S,taggedArticles[guid:S]]"); + hash = d->tagStorage->GetAs("archiveHash[_H:I,_R:I]"); + d->tagView = d->tagView.Hash(hash, 1); // hash on tag + } + //d->catStorage = new c4_Storage((filePath + ".mk4___CATEGORIES").local8Bit(), true); + //d->catView = d->catStorage->GetAs("catIndex[catTerm:S,catScheme:S,catName:S,categorizedArticles[guid:S]]"); +} + + +FeedStorageMK4Impl::~FeedStorageMK4Impl() +{ + delete d->storage; + if (d->taggingEnabled) + delete d->tagStorage; +// delete d->catStorage; + delete d; d = 0; +} + +void FeedStorageMK4Impl::markDirty() +{ + if (!d->modified) + { + d->modified = true; + // Tell this to mainStorage + d->mainStorage->markDirty(); + } +} + +void FeedStorageMK4Impl::commit() +{ + if (d->modified) + { + d->storage->Commit(); + if (d->taggingEnabled) + d->tagStorage->Commit(); +// d->catStorage->Commit(); + } + d->modified = false; +} + +void FeedStorageMK4Impl::rollback() +{ + d->storage->Rollback(); + if (d->taggingEnabled) + d->tagStorage->Rollback(); +// d->catStorage->Rollback(); +} + +void FeedStorageMK4Impl::close() +{ + if (d->autoCommit) + commit(); +} +int FeedStorageMK4Impl::unread() +{ + return d->mainStorage->unreadFor(d->url); +} +void FeedStorageMK4Impl::setUnread(int unread) +{ + d->mainStorage->setUnreadFor(d->url, unread); +} + +int FeedStorageMK4Impl::totalCount() +{ + return d->mainStorage->totalCountFor(d->url); +} + +void FeedStorageMK4Impl::setTotalCount(int total) +{ + d->mainStorage->setTotalCountFor(d->url, total); +} + +int FeedStorageMK4Impl::lastFetch() +{ + return d->mainStorage->lastFetchFor(d->url); +} + +void FeedStorageMK4Impl::setLastFetch(int lastFetch) +{ + d->mainStorage->setLastFetchFor(d->url, lastFetch); +} + +QStringList FeedStorageMK4Impl::articles(const QString& tag) +{ + QStringList list; + if (tag.isNull()) // return all articles + { + int size = d->archiveView.GetSize(); + for (int i = 0; i < size; i++) // fill with guids + list += QString(d->pguid(d->archiveView.GetAt(i))); + } + else if (d->taggingEnabled) + { + c4_Row tagrow; + d->ptag(tagrow) = tag.utf8().data(); + int tagidx = d->tagView.Find(tagrow); + if (tagidx != -1) + { + tagrow = d->tagView.GetAt(tagidx); + c4_View tagView = d->ptaggedArticles(tagrow); + int size = tagView.GetSize(); + for (int i = 0; i < size; i++) + list += QString(d->pguid(tagView.GetAt(i))); + } + + } + return list; +} + +QStringList FeedStorageMK4Impl::articles(const Category& cat) +{ + QStringList list; + /* + c4_Row catrow; + d->pcatTerm(catrow) = cat.term.utf8().data(); + d->pcatScheme(catrow) = cat.scheme.utf8().data(); + + int catidx = d->catView.Find(catrow); + if (catidx != -1) + { + catrow = d->catView.GetAt(catidx); + c4_View catView = d->pcategorizedArticles(catrow); + int size = catView.GetSize(); + for (int i = 0; i < size; i++) + list += QString(d->pguid(catView.GetAt(i))); + } + */ + return list; +} + +void FeedStorageMK4Impl::addEntry(const QString& guid) +{ + c4_Row row; + d->pguid(row) = guid.ascii(); + if (!contains(guid)) + { + d->archiveView.Add(row); + markDirty(); + setTotalCount(totalCount()+1); + } +} + +bool FeedStorageMK4Impl::contains(const QString& guid) +{ + return findArticle(guid) != -1; +} + +int FeedStorageMK4Impl::findArticle(const QString& guid) +{ + c4_Row findrow; + d->pguid(findrow) = guid.ascii(); + return d->archiveView.Find(findrow); +} + +void FeedStorageMK4Impl::deleteArticle(const QString& guid) +{ + + int findidx = findArticle(guid); + if (findidx != -1) + { + QStringList list = tags(guid); + for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + removeTag(guid, *it); + setTotalCount(totalCount()-1); + d->archiveView.RemoveAt(findidx); + markDirty(); + } +} + +int FeedStorageMK4Impl::comments(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? d->pcomments(d->archiveView.GetAt(findidx)) : 0; +} + +QString FeedStorageMK4Impl::commentsLink(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? QString(d->pcommentsLink(d->archiveView.GetAt(findidx))) : ""; +} + +bool FeedStorageMK4Impl::guidIsHash(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? d->pguidIsHash(d->archiveView.GetAt(findidx)) : false; +} + +bool FeedStorageMK4Impl::guidIsPermaLink(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? d->pguidIsPermaLink(d->archiveView.GetAt(findidx)) : false; +} + +uint FeedStorageMK4Impl::hash(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? d->phash(d->archiveView.GetAt(findidx)) : 0; +} + + +void FeedStorageMK4Impl::setDeleted(const QString& guid) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + + c4_Row row; + row = d->archiveView.GetAt(findidx); + QStringList list = tags(guid); + for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + removeTag(guid, *it); + d->pdescription(row) = ""; + d->ptitle(row) = ""; + d->plink(row) = ""; + d->pauthor(row) = ""; + d->pcommentsLink(row) = ""; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +QString FeedStorageMK4Impl::link(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? QString(d->plink(d->archiveView.GetAt(findidx))) : ""; +} + +uint FeedStorageMK4Impl::pubDate(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? d->ppubDate(d->archiveView.GetAt(findidx)) : 0; +} + +int FeedStorageMK4Impl::status(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? d->pstatus(d->archiveView.GetAt(findidx)) : 0; +} + +void FeedStorageMK4Impl::setStatus(const QString& guid, int status) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->pstatus(row) = status; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +QString FeedStorageMK4Impl::title(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? QString::fromUtf8(d->ptitle(d->archiveView.GetAt(findidx))) : ""; +} + +QString FeedStorageMK4Impl::description(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? QString::fromUtf8(d->pdescription(d->archiveView.GetAt(findidx))) : ""; +} + + +void FeedStorageMK4Impl::setPubDate(const QString& guid, uint pubdate) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->ppubDate(row) = pubdate; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +void FeedStorageMK4Impl::setGuidIsHash(const QString& guid, bool isHash) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->pguidIsHash(row) = isHash; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +void FeedStorageMK4Impl::setLink(const QString& guid, const QString& link) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->plink(row) = !link.isEmpty() ? link.ascii() : ""; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +void FeedStorageMK4Impl::setHash(const QString& guid, uint hash) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->phash(row) = hash; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +void FeedStorageMK4Impl::setTitle(const QString& guid, const QString& title) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->ptitle(row) = !title.isEmpty() ? title.utf8().data() : ""; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +void FeedStorageMK4Impl::setDescription(const QString& guid, const QString& description) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->pdescription(row) = !description.isEmpty() ? description.utf8().data() : ""; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +void FeedStorageMK4Impl::setAuthor(const QString& guid, const QString& author) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->pauthor(row) = !author.isEmpty() ? author.utf8().data() : ""; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +QString FeedStorageMK4Impl::author(const QString& guid) +{ + int findidx = findArticle(guid); + return findidx != -1 ? QString::fromUtf8(d->pauthor(d->archiveView.GetAt(findidx))) : ""; +} + + +void FeedStorageMK4Impl::setCommentsLink(const QString& guid, const QString& commentsLink) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->pcommentsLink(row) = !commentsLink.isEmpty() ? commentsLink.utf8().data() : ""; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +void FeedStorageMK4Impl::setComments(const QString& guid, int comments) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->pcomments(row) = comments; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + + +void FeedStorageMK4Impl::setGuidIsPermaLink(const QString& guid, bool isPermaLink) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->pguidIsPermaLink(row) = isPermaLink; + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +void FeedStorageMK4Impl::addCategory(const QString& /*guid*/, const Category& /*cat*/) +{ + return; + /* + int findidx = findArticle(guid); + if (findidx == -1) + return; + + c4_Row row; + row = d->archiveView.GetAt(findidx); + c4_View catView = d->pcategories(row); + c4_Row findrow; + + d->pcatTerm(findrow) = cat.term.utf8().data(); + d->pcatScheme(findrow) = cat.scheme.utf8().data(); + + int catidx = catView.Find(findrow); + if (catidx == -1) + { + d->pcatName(findrow) = cat.name.utf8().data(); + catidx = catView.Add(findrow); + d->pcategories(row) = catView; + d->archiveView.SetAt(findidx, row); + + // add to category->articles index + c4_Row catrow; + d->pcatTerm(catrow) = cat.term.utf8().data(); + d->pcatScheme(catrow) = cat.scheme.utf8().data(); + d->pcatName(catrow) = cat.name.utf8().data(); + + int catidx2 = d->catView.Find(catrow); + + if (catidx2 == -1) + { + catidx2 = d->catView.Add(catrow); + } + + c4_Row catrow2 = d->catView.GetAt(catidx2); + c4_View catView2 = d->pcategorizedArticles(catrow2); + + c4_Row row3; + d->pguid(row3) = guid.ascii(); + int guididx = catView2.Find(row3); + if (guididx == -1) + { + guididx = catView2.Add(row3); + catView2.SetAt(guididx, row3); + d->pcategorizedArticles(catrow2) = catView2; + d->catView.SetAt(catidx2, catrow2); + } + + markDirty(); + } + */ +} + +QValueList<Category> FeedStorageMK4Impl::categories(const QString& /*guid*/) +{ + + QValueList<Category> list; + return list; + /* + if (!guid.isNull()) // return categories for an article + { + int findidx = findArticle(guid); + if (findidx == -1) + return list; + + c4_Row row; + row = d->archiveView.GetAt(findidx); + c4_View catView = d->pcategories(row); + int size = catView.GetSize(); + + for (int i = 0; i < size; ++i) + { + Category cat; + + cat.term = QString::fromUtf8(d->pcatTerm(catView.GetAt(i))); + cat.scheme = QString::fromUtf8(d->pcatScheme(catView.GetAt(i))); + cat.name = QString::fromUtf8(d->pcatName(catView.GetAt(i))); + + list += cat; + } + } + else // return all categories in the feed + { + int size = d->catView.GetSize(); + for (int i = 0; i < size; i++) + { + c4_Row row = d->catView.GetAt(i); + + Category cat; + cat.term = QString(d->pcatTerm(row)); + cat.scheme = QString(d->pcatScheme(row)); + cat.name = QString(d->pcatName(row)); + + list += cat; + } + } + + return list;*/ +} + +void FeedStorageMK4Impl::addTag(const QString& guid, const QString& tag) +{ + if (!d->taggingEnabled) + return; + + int findidx = findArticle(guid); + if (findidx == -1) + return; + + c4_Row row; + row = d->archiveView.GetAt(findidx); + c4_View tagView = d->ptags(row); + c4_Row findrow; + d->ptag(findrow) = tag.utf8().data(); + int tagidx = tagView.Find(findrow); + if (tagidx == -1) + { + tagidx = tagView.Add(findrow); + d->ptags(row) = tagView; + d->archiveView.SetAt(findidx, row); + + // add to tag->articles index + c4_Row tagrow; + d->ptag(tagrow) = tag.utf8().data(); + int tagidx2 = d->tagView.Find(tagrow); + if (tagidx2 == -1) + tagidx2 = d->tagView.Add(tagrow); + tagrow = d->tagView.GetAt(tagidx2); + c4_View tagView2 = d->ptaggedArticles(tagrow); + + c4_Row row3; + d->pguid(row3) = guid.ascii(); + int guididx = tagView2.Find(row3); + if (guididx == -1) + { + guididx = tagView2.Add(row3); + tagView2.SetAt(guididx, row3); + d->ptaggedArticles(tagrow) = tagView2; + d->tagView.SetAt(tagidx2, tagrow); + } + markDirty(); + } +} + +void FeedStorageMK4Impl::removeTag(const QString& guid, const QString& tag) +{ + if (!d->taggingEnabled) + return; + + int findidx = findArticle(guid); + if (findidx == -1) + return; + + c4_Row row; + row = d->archiveView.GetAt(findidx); + c4_View tagView = d->ptags(row); + c4_Row findrow; + d->ptag(findrow) = tag.utf8().data(); + int tagidx = tagView.Find(findrow); + if (tagidx != -1) + { + tagView.RemoveAt(tagidx); + d->ptags(row) = tagView; + d->archiveView.SetAt(findidx, row); + + // remove from tag->articles index + c4_Row tagrow; + d->ptag(tagrow) = tag.utf8().data(); + int tagidx2 = d->tagView.Find(tagrow); + if (tagidx2 != -1) + { + tagrow = d->tagView.GetAt(tagidx2); + c4_View tagView2 = d->ptaggedArticles(tagrow); + + c4_Row row3; + d->pguid(row3) = guid.ascii(); + int guididx = tagView2.Find(row3); + if (guididx != -1) + { + tagView2.RemoveAt(guididx); + d->ptaggedArticles(tagrow) = tagView2; + d->tagView.SetAt(tagidx2, tagrow); + } + } + + markDirty(); + } +} + +QStringList FeedStorageMK4Impl::tags(const QString& guid) +{ + QStringList list; + + if (!d->taggingEnabled) + return list; + + if (!guid.isNull()) // return tags for an articles + { + int findidx = findArticle(guid); + if (findidx == -1) + return list; + + c4_Row row; + row = d->archiveView.GetAt(findidx); + c4_View tagView = d->ptags(row); + int size = tagView.GetSize(); + + for (int i = 0; i < size; ++i) + list += QString::fromUtf8(d->ptag(tagView.GetAt(i))); + } + else // return all tags in the feed + { + int size = d->tagView.GetSize(); + for (int i = 0; i < size; i++) + list += QString(d->ptag(d->tagView.GetAt(i))); + } + + return list; +} + +void FeedStorageMK4Impl::add(FeedStorage* source) +{ + QStringList articles = source->articles(); + for (QStringList::ConstIterator it = articles.begin(); it != articles.end(); ++it) + copyArticle(*it, source); + setUnread(source->unread()); + setLastFetch(source->lastFetch()); + setTotalCount(source->totalCount()); +} + +void FeedStorageMK4Impl::copyArticle(const QString& guid, FeedStorage* source) +{ + if (!contains(guid)) + addEntry(guid); + setComments(guid, source->comments(guid)); + setCommentsLink(guid, source->commentsLink(guid)); + setDescription(guid, source->description(guid)); + setGuidIsHash(guid, source->guidIsHash(guid)); + setGuidIsPermaLink(guid, source->guidIsPermaLink(guid)); + setHash(guid, source->hash(guid)); + setLink(guid, source->link(guid)); + setPubDate(guid, source->pubDate(guid)); + setStatus(guid, source->status(guid)); + setTitle(guid, source->title(guid)); + setAuthor(guid, source->author(guid)); + + QStringList tags = source->tags(guid); + for (QStringList::ConstIterator it = tags.begin(); it != tags.end(); ++it) + addTag(guid, *it); +} + +void FeedStorageMK4Impl::setEnclosure(const QString& guid, const QString& url, const QString& type, int length) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->pHasEnclosure(row) = true; + d->pEnclosureUrl(row) = !url.isEmpty() ? url.utf8().data() : ""; + d->pEnclosureType(row) = !type.isEmpty() ? type.utf8().data() : ""; + d->pEnclosureLength(row) = length; + + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +void FeedStorageMK4Impl::removeEnclosure(const QString& guid) +{ + int findidx = findArticle(guid); + if (findidx == -1) + return; + c4_Row row; + row = d->archiveView.GetAt(findidx); + d->pHasEnclosure(row) = false; + d->pEnclosureUrl(row) = ""; + d->pEnclosureType(row) = ""; + d->pEnclosureLength(row) = -1; + + d->archiveView.SetAt(findidx, row); + markDirty(); +} + +void FeedStorageMK4Impl::enclosure(const QString& guid, bool& hasEnclosure, QString& url, QString& type, int& length) +{ + int findidx = findArticle(guid); + if (findidx == -1) + { + hasEnclosure = false; + url = QString::null; + type = QString::null; + length = -1; + return; + } + c4_Row row = d->archiveView.GetAt(findidx); + hasEnclosure = d->pHasEnclosure(row); + url = d->pEnclosureUrl(row); + type = d->pEnclosureType(row); + length = d->pEnclosureLength(row); +} + +void FeedStorageMK4Impl::clear() +{ + d->storage->RemoveAll(); + if (d->taggingEnabled) + d->tagStorage->RemoveAll(); + setUnread(0); + markDirty(); +} + +} // namespace Backend +} // namespace Akregator diff --git a/akregator/src/mk4storage/feedstoragemk4impl.h b/akregator/src/mk4storage/feedstoragemk4impl.h new file mode 100644 index 000000000..d27933d0f --- /dev/null +++ b/akregator/src/mk4storage/feedstoragemk4impl.h @@ -0,0 +1,107 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef FEEDSTORAGEMK4IMPL_H +#define FEEDSTORAGEMK4IMPL_H + +#include "feedstorage.h" +namespace Akregator { +namespace Backend { + +class StorageMK4Impl; +class FeedStorageMK4Impl : public FeedStorage +{ + public: + FeedStorageMK4Impl(const QString& url, StorageMK4Impl* main); + virtual ~FeedStorageMK4Impl(); + + + virtual void add(FeedStorage* source); + virtual void copyArticle(const QString& guid, FeedStorage* source); + virtual void clear(); + + virtual int unread(); + virtual void setUnread(int unread); + virtual int totalCount(); + virtual int lastFetch(); + virtual void setLastFetch(int lastFetch); + + virtual QStringList articles(const QString& tag=QString::null); + + virtual QStringList articles(const Category& cat); + + virtual bool contains(const QString& guid); + virtual void addEntry(const QString& guid); + virtual void deleteArticle(const QString& guid); + virtual int comments(const QString& guid); + virtual QString commentsLink(const QString& guid); + virtual void setCommentsLink(const QString& guid, const QString& commentsLink); + virtual void setComments(const QString& guid, int comments); + virtual bool guidIsHash(const QString& guid); + virtual void setGuidIsHash(const QString& guid, bool isHash); + virtual bool guidIsPermaLink(const QString& guid); + virtual void setGuidIsPermaLink(const QString& guid, bool isPermaLink); + virtual uint hash(const QString& guid); + virtual void setHash(const QString& guid, uint hash); + virtual void setDeleted(const QString& guid); + virtual QString link(const QString& guid); + virtual void setLink(const QString& guid, const QString& link); + virtual uint pubDate(const QString& guid); + virtual void setPubDate(const QString& guid, uint pubdate); + virtual int status(const QString& guid); + virtual void setStatus(const QString& guid, int status); + virtual QString title(const QString& guid); + virtual void setTitle(const QString& guid, const QString& title); + virtual QString description(const QString& guid); + virtual void setDescription(const QString& guid, const QString& description); + virtual void setEnclosure(const QString& guid, const QString& url, const QString& type, int length); + virtual void removeEnclosure(const QString& guid); + virtual void enclosure(const QString& guid, bool& hasEnclosure, QString& url, QString& type, int& length); + + virtual void addTag(const QString& guid, const QString& tag); + virtual void removeTag(const QString& guid, const QString& tag); + virtual QStringList tags(const QString& guid=QString::null); + + virtual void addCategory(const QString& guid, const Category& category); + virtual QValueList<Category> categories(const QString& guid=QString::null); + + virtual void setAuthor(const QString& guid, const QString& author); + virtual QString author(const QString& guid); + + virtual void close(); + virtual void commit(); + virtual void rollback(); + + virtual void convertOldArchive(); + private: + void markDirty(); + /** finds article by guid, returns -1 if not in archive **/ + int findArticle(const QString& guid); + void setTotalCount(int total); + class FeedStorageMK4ImplPrivate; + FeedStorageMK4ImplPrivate* d; +}; + +} +} +#endif // FEEDSTORAGEMK4IMPL_H diff --git a/akregator/src/mk4storage/metakit/CHANGES b/akregator/src/mk4storage/metakit/CHANGES new file mode 100644 index 000000000..160c4bad9 --- /dev/null +++ b/akregator/src/mk4storage/metakit/CHANGES @@ -0,0 +1,1690 @@ +2004-01-26 ############################################ MK 2.4.9.3 + + Maintenance release, consolidates past 9 months of changes/tweaks. + + Fixed test for "__name__" in python/metakit.py to run a quick test. + +2004-01-22 Fixed refcount problem with temp rows in Mk4tcl + + This was a long-standing bug: "mk::row create" did not work right + because the tracking of temporary rows was completely messed up. + Added test case for Tcl (mk6.8), fixes FB14, BTS#78, and BTS#29. + +2004-01-21 Documentation updates + + Updated copyright notices to 2004, and udated to Doxygen 1.3.5 for + a new set of C++ documentation. In anticipation of next release. + +2004-01-20 Don't trip over duplicate property names + + Added code in c4_Field constructor to avoid crashing when there is a + duplicate property name in the format description string. Duplicate + names are now ignored (there is no good way to report errors at this + point). Avoids an even bigger problem: conflicting property types. + + Added test s49 to detect this case (FB20, reported by Brian Kelley). + +2004-01-18 Fixed rare but very serious subview resizing bug + + Fixed rare but serious file damaging bug, when resizing a comitted + subview to empty, committing, and then resizing back to containing + data. Added new s48 test to force this bug to the surface. + + Fortunately this usage pattern *never* happens in blocked views! + Fixes are at the end of c4_FormatV::Insert and c4_FormatV::Remove. + +2004-01-16 Gracefully deal with bad property type specifiers + + When GetAs is a called with a bad description, such as for example + "myview[name:A]", the release code would crash on a null pointer + dereference at some point. Changed so the code now treats any type + it does not know about as "I" (works a bit better than using "S"). + + The debug build still hits an assertion, as before. Added s47 test. + +2004-01-03 Fixed typo in PyView.cpp + + Forgot to fix closing brace after the 2003-12-11 mods. + +2003-12-21 Fixed Mk4too sorting on subview of length 1 + + There was a silly bug when sorting on subviews in the new Mk4too + interface (not in Mk4tcl), which returns a view, instead of a list + of integers. In the case of 1 row, optimization was done wrongly, + omitting the sort as well as the remapping. Fixed. + + Oomk needs to be patched to work around this (don't sort if n=1). + +2003-12-13 Tweak to avoid two unisgned/signed compiler warnings + + In remap.cpp, compiled in debug mode on Win32 (DWORD vs. t4_i32). + +2003-12-11 Checked in numerous changes to Mk4py by Nicholas Riley + + All changes are local to the python/directory - for details, see + http://www.equi4.com/pipermail/metakit/2003-September/001407.html + With a big thank you to Nicholas for contributing these improvements. + +2003-11-23 Bumped to Python 2.3, doc tweaks, lots of name fixes + + Adjusted makefile to now look for Python 2.3 by default. + Regenerated config files. Fixed obsolete links in the doc files. + + Got rid of tons of Metakit "interCap" cases, should be Metakit. + This affects a huge number of source files and one #define (which + has been defined twice for compatibility: d4_MetakitLibraryVersion). + Use "cvs diff -i" to see real changes, other than case swapping. + +2003-10-28 Get rid of --enable-python, check in c22.txt + + It's not valid, but some files mentioned it. Use --with-python. + Forgot to check in the new c22.txt file added earlier this month. + +2003-10-16 Added note to Tcl docs + + Added a note to doc/tcl.html on how to load a MK datafiles stored in + another one, as needed when using VFS for example. + +2003-10-10 Added c22 test + + Added test to make sure groubpy/select bug is not present in the C++ + core (bts #75 reports the bug found for Mk4tcl/Mk4too). + +2003-10-01 Fixed bugs in Tcl test suite + + The tests in tcl/test/ were incorrectly closing non-test datafiles, + such as the "exe" one open while tclkit runs, and needed when a + slave is created which needs to re-run init.tcl etc from VFS. + +2003-09-30 Python 2.3.1 & cleanup + + Tweaked PWONumber.h and PyRowRef.cpp and MSVC6 project so it can + compile Mk4py for Python 2.3.1 again. Windows binary uploaded. + + Removed the win/msvc60/tests/ directory, it's no longer needed. + +2003-09-20 Autoconf and libtool rebuilds + + In an attempt to stay ahead of version trouble and other nonsense, + the autoconf / configure and libtool files have been regenerated. + Autoconf is at 2.57 and libtoolize is at 1.4.3 on this dev system. + +2003-08-26 Documentation fix + + Fixed install comments for Mk4py, as reported in bts #59. + +2003-07-17 Fixes to Mk4py (Gordon) + + Fix crash when db.description(nm) is called for missing view "nm". + Fix wrap - for the special case of wrapping a list of primitives + (eg, ints, floats, strings) in a single property "view". + +2003-07-11 Fix for Linux not finding .lai file + + Posted for RH 8.0 on MK mailing list by Jeff Web on July 3rd. + +2003-07-01 Fixed "Metakit" (preferred) vs "Metakit" (obsolete) + + Get rid of InterCappedWords. Time to grow up, d00d... + +2003-06-06 Fix to Mk4py for case (in)sensitivity. + + When using a dict (or keyword args), makeRow now gets the + names from the dict's keys and gets the properties by name. + This allows case insensitive matching. Note that using an + instance still requires that the instance attributes have + names that match (case sensitive) with the view. + +2003-05-15 Add distutils setup.py script (Gordon). + + Tested on Linux & Windows (MSVC). + You can now do a plain configure / make (without python) + then cd python; python setup.py build | install + +2003-05-08 Fixed array bound bug when not using mmap-ed files + + There was an incorrect test, when dealing with a new file and + memory mapping is not enabled (which is uncommon, these days). + Thanks to V Demarco for catching and resolving this bug. + +2003-04-28 Sourceforge + + Synced to SF's CVS, see http://sourceforge.net/projects/metakit + +2003-04-25 Autoconf/libtool update + + Did "autoreconf -force" with autoconf 2.5.7 and libtool 1.4.3, as + suggested by Gerfried Fuchs/Ryan Murray. Some Makefile.in tweaks. + +2003-04-22 Fixes to Mk4py (Gordon). + + Make view.append(instance) work again. + Fix recursively adding subview data. + Make properties compare properly. + Initial (incomplete) unittest based test script. + +2003-03-16 ############################################ MK 2.4.9.2 + + Also allow passing pairs to "mk::row append" as a list (Mk4tcl). + +2003-03-10 Fixes for sparc64 configure and AIX strcasecmp + + Both changes contributed by Andreas Kupries. + +2003-03-07 Fix more bugs in blocked view, add 64-bit Sparc support + + The blocked viewer deletion was STILL incorrect. Fixed bad offset + calculations, added several more assertion checks, and added a new + m07 test case which checks for numerous cases of deletion overlap, + i.e. cases where multi-row deletions cross subview boundaries. + + Added __sparc9 #define to mk4.h (thanks again, Andreas K) + +2003-03-05 Fixed two potential races in C++ threaded build + + There was a serious bug in view.cpp, whereby Unix (p)thread locks + were non-functional in release mode (#define NDEBUG). Yikes! + + In addition the logic was flawed (both Unix and Windows), failing to + protect from indexing while sPropNames / sPropCounts were being + resized (i.e. a realloc). Added explicit locking to all affected + paths, and removed the now obsolete count locks (i.e. AddRef). + + Thanks to Murat Berk for chasing this and explaining the problem. + + This bug "only" affects C++ builds. Tcl and Python each have an + extra layer on top which means there can be no races inside MK. + +2003-03-03 ############################################ MK 2.4.9.1 + + Moving to a new 4-level bugfix release number. + +2003-03-02 Fixes to Mk4py (gmcm) + + Modify some recent patches that were Python 2.2 only so they would + still work with 2.1. Add a view.properties() method (returning a + dict of propname -> Property) for cases where a property is masked + by a method name. + +2003-03-01 Reverted changes in Mk4py/scxx, avoid Mk4tcl warning + + One was in SCXX, PWONumber.h - ouch and apologies (jcw). Changed + comparison logic in mk4too.cpp (64-bit ints, new code). + +2003-02-28 Fixed relaxed layout in Mk4tcl, several Mk4py fixes + + Use list operators to convert a Tcl layout to MK format. This used + to crash, Tcl test 6.7 added to verify the fix. Changed test 3.1 to + use a different notation for nested defs (^). + + Added to ViewerAsSeq, to allow v1+v2+v3 (thanks Michael Scharf). + Also fixed several mem leaks - thx again! + +2003-02-27 Added support for HPUX aCC "long long" + + Added #ifdef's to mk4.h to match autoconf HAVE_LONG_LONG settings. + Tweaks to configure.in by Andreas K to better support HP/UX (thx!). + +2003-02-26 Added 64-bit int support to Mk4tcl, fix mingw + + The "L" type was not exposed, though Tcl >= 8.4 has "wide" ints! + Will now support 64-bit ints, if built against a Tcl 8.4+ header, but + still defaults to without for use in Tcl's <= 8.3. + + Fix _strdup #ifdef in univ.cpp when compiled from mingw on win32. + +2003-02-24 Break was missing in switch Mk4py + + Fixed a serious problem, which must have come from editing too + wildly (Mk4py needs a test suite!). + +2003-02-20 Remove a stray include, remove CR's + + Remove "#include <stdio.h>" from remap.cpp, leftover from debugging. + Also removed CR's left behind from editing under Windows (doh!). + +2003-02-18 ############################################## MK 2.4.9 + + This is mostly a bug fix release (some features added to Mk4tcl). + +2003-02-18 Fixed a bug in blocked view deletion, and hash byteorder + + There was an off-by-one error in the deletion of multiple rows + which span more than an entire subview block. Test m06 is ok now. + Also added consistency checks (when compiled in debug mode). + + Fixed a too-strict assertion in mk4tcl.cpp, Item destructor. + + Fixed a byte-order sensitivity in hash views for numeric properties. + +2003-02-17 Configure tweaks for hpux/ia64 + + Re-instated hpux changes by A. Kupries, for ia64, also -lpthread. + +2003-02-14 Bug found in blocked viewer modification + + Added test m06 to catch a problem first detected in the Tcl test + suite (test 5.7) - recoded to C++. This is a show-stopper for the + upcoming 2.4.9 release - fixing it is now a top priority. + +2003-02-14 Some changes to OO interface in Tcl + + The OO "select" cmd now returns a view, not a list of row #'s. + Added "product" and "rename" operators, they were not exposed. + +2003-02-14 Enable stdio buffering + + On platforms where stdio is used, the setbuf(..., 0) calls have been + removed (fileio.cpp). This improves performance, and lets caller set + up whatever buffering they like. + +2003-02-07 Tweaks to restore broken MK ports + + Fixes by A. Kupries to fix MK builds I broke (Itanium/HPUX). + +2003-02-07 Changed code to avoid compiler warning + + In remap.cpp, LookDict(), add return at end to avoid a mistaken + compiler warning (on AIX 5.1). No effect on runtime behavior. + +2003-02-02 Work around optimizer bug in gcc 3.2.1 + + The sign-extension in c4_ColOfInts::Get_16r was not being done right + with -O2 on Linux. This only manifests itself with files having + reversed bytes (e.g. PowerPC/Mac). Changed code to use a local temp + buffer instead. This bug was the reason why some starkits failed to + load when created on Mac and used on Linux. + +2003-01-24 Fixed cleanup order bug in Mk4tcl + + There was a long-standing bug in Mk4tcl, in which cleanup order of + MkPath objects caused them to access their MkWorkspace parent even + though it was already deleted. This may have caused some of the + crash-on-exit bugs in the past. + +2003-01-22 Add missing -lstdc++ + + For unknown reasons, the current make failed to produce shared libs + with libstdc++ linked in - manually added to LDFLAGS again. + +2003-01-19 Tweak to temp object use + + Two changes in handler.cpp to avoid bug in compilers which get the + cleanup logic of temp objects wrong (thx, Dan Gregory). + +2003-01-17 Add synonym for mk4tcl "info" command + + To avoid a name clash, "$view info" can now also be written as + "$view properties" (or an abbreviation of that). + +2003-01-16 Allow access to root view in Mk4tcl + + Access to root view (i.e. the storage object itself) was not + allowed - added a small change to allow this (MkPath::AttachView). + +2003-01-15 Use strdup + + On Unix, use strdup i.s.o own code for _strdup, see univ.cpp. + Perhaps it's time to start reversing the names, and make unix std. + +2003-01-10 Build improvements, Mk4py long and Mac improvements + + Merged changes submitted by Nicholas Riley (thanks!) into CVS: + - changes to build Mk4py on MacOS X (framework/dylib) + - support 'L' fields, handle overflow, throw exceptions + - the beginnings of a test suite for Mk4py (in python/test/) + + Separated PPC and 68K projects (Mac classic), because PPC is now + built with 8.3 (Carbon support), while 68K requires 6.3. The x86 + cross-compile-from-mac projects have been dropped. + +2003-01-09 String compare tweak, Mac Carbon runtime mmap code + + Use strcasecmp on unix, instead of own code (string.cpp). + + Added code based on Paul Snively's contributed patch to recognize + running under OSX, so carbon apps can benefit from mmapped files. + All changes are within fileio.cpp, requires CW8 to build this way. + Added "PPC Carbon" target to cw.mcp, derived from "PPC DLL" one. + +2002-12-23 Tweak for Borland builder 5 & 6 + + Changed #ifdef line 22 in univ.cpp (fix by S. Cusack). + +2002-12-09 Fixed bug in selection view change propagation + + Fixed a bug when a row is inserted in a view on which a selection + depends, when the inserted position is not part of the derived one. + This looks like an oversight in 2.3/2.4 changes, and must have been + in there for quite some time. Added new n14 test to verify this. + +2002-12-02 Fixed bug in MK old-file format conversion + + Fixed bug in on-the-fly conversion of old 1.x/2.0.x format files. + +2002-11-24 Fixed Mk4tcl threaded build + + Release and reclaim mutex lock while calling eval inside loops. + Added "--enable-threads" option to configure script. + +2002-11-22 Configure tweak for HPUX/Itanium + + Removed ia64 check in configure.in, now that libtool has been fixed. + +2002-11-16 Tweaks to compile on Mac + + Small changes to the source code to avoid errors from the MWCW 8.x + compilers. Omit stricmp and strdup with CW version >= 8. + +2002-11-04 Fixed typo in Makefile + + This prevented Mk4py from getting installed (thanks F. Majid). + +2002-11-03 ############################################## MK 2.4.8 + + Reverted non-portable change in Makefile to copy to "Mk4tcl.*". + +2002-10-28 Workaround for bugs on ARM/WinCE and HP/UX, Mk4tcl/WinCE + + Turn off c4_ColofInts::Set_8i optimization when compiling for ARM on + WinCE using EVC3. All regression tests now pass (on ARM & x86emu). + + On HP/UX, enable copying in fileio.cpp, line 265, so a write is not + done directly from a mmap'ed file (this can hang a process, hard). + + Added a "mktcl" subproject to msevc3, to build "Mk4tcl.dll". + +2002-10-27 Added multi-thread support for Unix + + Added changes to support same appartment-threading model on Unix as + on Win32 (pthreads based). This merely adds support to allow safe + use of the current "each datafile in one thread" design on Unix. All + changes in view.cpp (thank you, M. Berk). Define q4_MULTI to enable. + +2002-10-26 Merged WinCE changes + + Merged changes to build MK for WinCE using MS Embedded VC 3.0. + These changes are based on Joseph Tate's modifications (thanks!). + +2002-10-21 Updated autoconf & libtool (on teevie) + + Files in unix/ regenerated with autoconf 2.53a and libtool 1.4.1. + +2002-10-16 Added "dup" subcommand to Mk4tcl OO + + There was no way to duplicate a view in the mk4tcl OO interface. + Needed to properly deal with re-use / ref counts - added "dup". + +2002-10-11 Cast widened in Mk4tcl, support q4_TINY def for Mk4tcl + + Changed cast from int to long, to avoid compiler warning on some + 64-bit machines (mk4tcl.cpp, line 2013). + + Remove float/double code when q4_TINY is defined, also in Mk4tcl. + +2002-10-10 Makefile tweaks + + Removed duplicate flags from CXXFLAGS definition. + +2002-10-09 Fixed blocked/subview bug, tweak for the ARM platform + + Blocked viewers were not doing the right thing when rows had subviews + in them - fixed the logic, and add a new m05 test to verify it all. + + Fixed a signed-char bug which prevented MK from passing all tests on + the ARM, in this case the Compaq iPaq PDA with Linux and gcc 2.95.4. + +2002-10-07 Tweak to prevent gcc compiler bug + + Added intermediate temp var in derived.cpp to prevent gcc -O failure. + +2002-10-04 Config and makefile adjustments + + Adjusted configure.in to use ".sl" for HP-UX, and ".dll" for Cygwin. + + Changed "install -d" to "mkdir -p" (2x) in Makefile.in, since the + former is not supported by all incarnations of install (A Kupries). + + Added @CXXFLAGS@ to the end of "CXXFLAGS =" lines in Makefile.in, + as suggested by Donal K. Fellows. Used for his IRIX(64) builds. + + Added logic to build properly on HP-UX, including a small assembly + file (!) which allows loading C++ shared lib from C, as needed in + Tcl - with thanks to Andreas Kupries for solving and patching this. + +2002-09-25 Build tweaks for Mac OS X + + Applied patches by Daniel Steffen to deal with ".dylib" (thanks!). + + Use -O i.s.o. -O2, which caused test b27 to fail on OS X (gcc 3.1). + +2002-09-09 More 64-bit platforms recognized + + Added #ifdefs for more 64-bit platforms, thanks to Reinhard Max. + +2002-09-08 Make tweaks for HP-UX + + Tweaks to build on HP-UX / 9000 (added __hpux #ifdef in header.h). + +2002-09-03 Fixed Mac OSX build problem + + Compile Mk4tcl lib with stubs only if building the shared lib. + Ignore strip errors (fails with Mk4tcl.so on OSX / dyld libs). + +2002-07-01 Python and Tcl installation improved + + Now installs Python Mk4py.so and metakit.py in the most common dir + location by default, i.e. "/usr/local/lib/python2.2/site-packages". + Locations can be overridden through $pyincludedir and $pylibdir. + + For Tcl, "make install" now constructs a standard package dir, i.e. + "/usr/local/lib/Mk4tcl/" with entries "Mk4tcl.so" and "pkgIndex.tcl". + Locations can be overridden through $tclincludedir and $tcllibdir. + +2002-05-30 ############################################## MK 2.4.7 + + Fix CONST84 logic so source compiles under both Tcl 8.3 and 8.4. + Fix creation of tests/CVS/ so diff in regression tests won't fail. + +2002-05-14 Fixed an adaptive int insert/delete bug + + Another bug in the 2.3/2.4 codebase, related to adaptive integers. + + Symptom: one int entry has incorrect bytes after insert/delete. + + Scenario: ints are 1..8 bits, and an odd number are added/removed, + leaving an odd-sized internal "gap". Then store a 16/32 bit value, + forcing resizing. Once this is done, there will be one value which + cannot be properly read or set because its data is split *across* the + gap (commit is ok and removes the problem). Fix in column.{h,cpp}, + with a new regression test s46 added to make sure things are ok now. + + Let's hope that this is truly the *last* deeply embarrassing bug... + +2002-05-06 ############################################## MK 2.4.6 + + Recent bug fix was critical enough to warrant a new revision. + It is safest to avoid using versions 2.4.2 .. 2.4.5 altogether. + +2002-05-05 Fixed major bug in string/bytes after multiple commits + + Finally found a way to reproduce spurious bugs reported in the latest + revisions. It turns out that one of the optimizations of the past 2 + months (no exact details) caused memo's to be tracked incorrectly in + their free space use. This causes trouble with strings over 10 Kb + (or smaller if there are over 1000 rows). + + The bug is forced by new (frightfully short) test cases s44 + s45. + Fixed by performing a slightly less agressive optimization in the + c4_FormatB::Commit (which is also shared with strings, type 'S'). + + Removed a "--exclude" from diff, which is not portable enough. + +2002-05-01 Added support for Windows CE + + Patches submitted by Joseph Tate (thank you!), with minor tweaks. + +2002-04-29 ############################################## MK 2.4.5 + + Various bug fixes, fixed a number of platform issues. + +2002-04-28 Fix small-int re-use view bug + + A nasty bug was reported by VPI which caused upper bytes to be + truncated from int values. The problem appears when storing ints of + 1..4 bits in a view, then clearing the view (so a gap is placed past + the end of the column), then adding a row with an int of 2 or more + bytes. This uncovered a bug in forgetting to truncate columns with + sub-byte int storage. Now fixed, added regression test s43. + + This bug could also have affected string and byte storage, since + these use int columns to store item sizes. Under very specific + conditions, it may have lead to truncated or even mixed-up values. + +2002-04-27 Fix nested mk4tcl loop bug + + Loops would exit prematurely when nested - due to objc/objv being + overwritten in the inner loop. Affects mk4tcl.cpp and mk4too.cpp. + +2002-04-10 Fix bug introduced in recent blocked viewer optimization + + Murphy was at it again. The bug affected the way row inserts and + deletes were done, and can cause incorrect data to be copied. It is + relatively hard to reproduce (the test dataset was 12 Mb), but the + change explains things fully and is in fact very small. + +2002-04-02 Fix bug in debug code + + In remap.cpp line 531, a debug assertion was moved in the wrong way. + +2002-04-01 Backed out to libtool 1.4d, fix test diff and Tcl const + + Backed out to the more official 1.4d release of libtool (instead of + the CVS version, which is adding "tags" we will not need anyway). + The unix/Makefile.in has been simplified, back to how it used to be. + + Fixed the "diff" call at the end of "make test" so that it no longer + generates extra output if things match and now fails if they do not. + + Added fix to allow compiling mk4tcl.cpp with "pre-constified" Tcl + code, thanks to a tip by Don Porter (see "grep CONST84 mk4tcl.*"). + +2002-03-31 ############################################## MK 2.4.4 + + Various bug fixes and (blocked view) performance enhacements. + +2002-03-28 More blocked optimizations, IRIX tweaks + + Switched Slot() to binary search. This seems to slow down a few + percentage point for smaller views, but with 5 millions rows this is + reported to make a huge difference (from code by Zhang Dehua). + + Added header "bool" fix by Erik Hofman so MK compiles on IRIX (SGI). + +2002-03-27 Added definitions for AIX + + Added six operators defs before c4_Cursor class, to avoid compile + errors on AIX. With apologies to Murat Berk for taking so long... + +2002-03-26 Re-instated the c4_View::RelocateRows operation + + Re-enabled code which supports efficient moves of rows from one view + to another - avoiding copying of subviews. Strings/memos are still + being copied for now. Also moved a slow test out into a new call + "c4_View::IsCompatibleWith", this must be checked to make sure that + RelocateRows can work. Passing incompatible views will still cause + an assertion in debug mode, but must be avoided in release builds. + + This change ought to have a dramatic effect on inserts/deletes for + blocked views with large subviews. Added examples/blockdels.tcl to + thoroughly exercise this new code and all boundary conditions. + +2002-03-22 Fixed a serious bug in serialization code + + When serializing string/bytes columns with large strings using the + c4_Storage::SaveTo function, memo's would sometimes not be written, + leading to a *damaged* datafile (and incorrect free-space tracking). + +2002-03-15 Better configure logic, "mk::view layout" fix + + Many tweaks to configures, makes, and libtool setup. Get rid of the + library version numbers and the special Mk4*.so targets. Instead of + Mk4tcl.so use libmk4tcl.so (likewise Mk4py.so is now libmk4py.so). + + Fixed "mk::view layout", it was broken by the 10-2-2002 change. + +2002-03-13 Extend partial access 'B' usage + + Added code to Access and Modify to simulate partial data access the + hard way when the underlying view does not support it (c4_Bytes + only), i.e. copy via full temp. Added test s41, doing partial access + on a blocked view. + +2002-03-12 Add test for serialized input + + The c4_Storage::LoadFrom() call (mk::file load) can be tricky. After + a commit, the header is adjusted so that reading from the start + works. This is not the case in commit-extend mode, where a load will + read th state as it was before extending - it cannot know there is + more. + + Added test s40 to verify this case. Also reorged a few more files + (tbasics and tstore4 split into 2 each). + + Added test in mk4tl.cpp so LoadFrom failure generates an error + return, instead of being ignored. + +2002-03-10 Tweaked March 8 fix a tad further + + The workaround implemented below wasn't 100% complete. + +2002-03-08 Workaround for file extend on Win + + There seems to be a bug when extending a file on at least NT4 with + NTFS (file server works fine). When extending the file in such a way + that a gap is created to save new data during commit, the file + appears to get rounded up to the next 512-byte boundary. At that + point, the datafile no longer has a valid end marker and is sort of + corrupt. + + Wrote workaround for Win32 builds, see persist.cpp notes names "March + 8, 2002". One way to force this bug is to run "mkhash Dmhs 250000" - + after row 150000, it asserts on bad filesize when built in debug mode + and run on NTFS. + +2002-02-19 Fixed over-aggressive optimization + + Change made on Feb 7 was releasing too many objects in a commit, + causing it to detach empty top-level views, even when in use. Fixed, + added s39 to test for this case. + +2002-02-10 Improve Mk4tcl's "mk::view layout" + + Avoid crash when asking for the layout of a non-existent view. Now + returns an error instead. + +2002-02-07 Fast commit with many empty subviews + + Avoid creating subviews when committing, if these are empty anyway. + Added new s38 test for this case. + +2002-02-02 Small optimizations + + Changed a few c4_Property instances to const& references to avoid + copying (2 in view.cpp, 2 in custom.cpp). + +2002-02-01 ############################################## MK 2.4.3 + + Bug-fix release, mostly. + + Python include path now upgraded to python2.2 (was 2.1). + +2002-01-31 Cross-platform serialization, Tcl + + There was a bug with serializing a datafile (SaveTo) when it was + created on a platform with reverse endian-ness. Fixed so serialized + data also flags reversed byte order. + + Tweaks to fix const changes in the latest Tcl cvs branch. Fixed a + recently-introduced UTF8 path bug in mk4tcl.cpp. + +2001-12-21 Optimized GetAs + + Now that GetAs is used so much more, optimize the common case where a + description does not require restructuring. Can lead to order-of + magnitude speed improvement in cases where a storage contains many + views. + +2001-12-20 Fixed bug in Locate, comparison issue + + Testing for the Mk4py changes uncovered a serious bug in + c4_View::Locate, causing it to sometimes return zero, even if there + are matching rows (thanks, Gordon). + + But a very fundamental weakness also showed up, being that row + comparisons (and that includes the C++ operators) have the confusing + property of not being symmetric in all cases. The problem occurs + when left- and right-hand sides do not contain the same (number of) + properties. In that case, the *left-hand* row participating in the + comparison determines which properties take part in a comparison. + + In the case of Locate, this caused improper comparisons. And it is + very easy to get bitten by this, such as here: c4_RowRef key = ...; + int n = view.Search(key); bool match = n >= 0 && view[n] == key; The + above code is *wrong*. The last line needs to be: bool match = n >= + 0 && key == view[n]; + + This is very clearly a design mistake. Comparisons should have been + *either* implemented *or* named differently. + + A new "m04" test has been added to the regression suite. + +2001-12-19 Changes to mk4py by Gordon McMillan + + Several changes and cleanups. Mk4py now has logic to track different + view categories, e.g. to make sure a R/O view is not being written + to. This should greatly reduce the number of silently ignored + incorrect calls, as well as crashes, and will produce appropriate + error messages instead. + +2001-12-18 Cleanup + + Cleaned up source comments and got rid of yet more warnings. + +2001-12-14 Fixed yet another case of crash-on-exit + + The new Unmapped() code of 2.4.2 forgot one case of cleaning up, + which has now been fixed (in c4_FormatB::Commit). + +2001-12-12 ############################################## MK 2.4.2 + + Better portability, hashing improved + + This release marks the consolidation of a number of changes, mostly + relating to better portability & hashing. The speed of commits with + many strings and subviews should be notably better. The Tcl + extension no longer needs a "stub" library to compile (it now has + that code itself), just std headers. + +2001-12-08 Changes to commit cleanup, Mac stuff + + Changed the logic of how ReleaseAllSegments gets called at the end of + commits. There was at least one case of leaving a column pointing + into mapped file space when it was about to be remapped. This should + fix a very long-tanding bug which shows itself as freeing unallocated + memory during commit or cleanup of the storage object. + + Changes to test coding and PyRowRef.cpp to deal with builds on + Macintosh (different issues for MacOS 9 and MacOS X). The + mac/cw.sea.hqx project has been upgraded to MW CW 6.3. Verified Tcl + 8.3.2 and Python 2.1.1 builds with CW6 on Mac. + +2001-12-06 Tweaks to Tcl interface + + More robust, added "$vw loop var ... {body}" object command. The + "$vw size" command now takes an optional newsize arg. + +2001-12-04 Tweaks to makefile and configure + + Tweaks, in preparation of an upcoming 2.4.2 release. + +2001-12-03 Changes in M4py, avoid gcc problem, hash + + The "-fomit-frame-pointer" option for gcc has been turned off, + because it causes problems with exception handling in Mk4py. All + failures in Mk4py now propagate properly to Python AFAIK. + + Simplified Mk4py - by removing a layer of exception handler classes + in scxx. Errors now set info and throw a plain int. + + The mkhash.cpp sample program exposed a problem with multi-key + hashing: the order of properties in the search key must match exactly + the order in the hash view itself. For now, this has been left as is + (it's easy to do, once you are aware of it). + +2001-11-30 Win MT fix, commit tweak, indent cleanup + + Drop static buffer in fileio.cpp (DataWrite), uses stack now, so the + code can be used in MT context even on Win 95/98. It does more + copying than would be needed for NT (2K?), alas. + + More changes in c4_FormatB::Commit to properly detect memo use in + blocked views. The recent changes introduced a bug which shows up + only with blocked views and large string/bytes items. Reported by + Steve Baxter with demo, new "m02" regression test. + + Changed fileio.cpp to turn off file buffering, this avoids a few + reads when writing and seeking a lot. It does not have as big an + impact as one might expect, but every little bit helps. + + Cleaned up new 2-space indentation in several source files. + + Added new "mk::file space" in Mk4tcl, to inspect the current file + space usage. This is only intended for internal testing. + +2001-11-28 Fixed memory leak in string/memo cleanup + + There was a mem-leak in c4_FormatB::Commit which showed up due to + yesterday's more extensive testing. Only showed up after a commit, + in string columns with widely varying item sizes. This caused + regression test s37 to fail in MFC-debug compile. + +2001-11-27 Major performance bug fix, and MT strings + + A serious problem has been resolved, which slowed down commits, and + prevented blocked views from committing efficiently. The reason was + that for string props, the string size was always being saved anew, + even if no changes in that view took place. This did not affect + proper operation, just speed, and was most noticeable with many + (sub-)views containing many string props. + + The solution is in src/format.cpp, the examples/mkhash.cpp code was + further adjusted to better expose and measure the effects. Thanks to + P. Baspeyras and S. Baxter for helping me resolve it. + + Another change was to alter the way empty strings are allocated in + the src/string.cpp code, making it compatible with multi- threaded + use and removing the remaining memory leak. + +2001-11-26 Fix in assertion check (blocked view) + + Corrected an off-by-one bug. Only shows up with debugging on, since + it's inside an assert(). Thanks to Steve Baxter. + +2001-11-25 More arg checking in Mk4py, locate + + Added more checks against incorrect usage, based on sample code by + Mitch Chapman. The "throw" code appears to be inconsistent when + called at top of *some* calls, using a workaround for now. + + Added "view.locate(key)" wrapper, returns (pos,count) tuple. + + Various source code formatting adjustments (indents and such). + +2001-11-04 Added alternate calls to c4_CustomViewer + + Added extra defs to "mk4.h" of Lookup and InsertRows which take a + "const c4_RowRef&" i.s.o. a c4_Cursor. Inlined in "mk4.inl". The + "c4_Cursor" datatype might one day become obsolete. + +2001-11-03 Removed tcl/kit/ and copyright notices + + Removed the entire tcl/kit/ tree, Tclkit will be distributed as + separate package from now on (the 2.4.1 release still builds ok). + + Also replaced all copyright notices by version Id's and an URL. + +2001-11-02 Fixed partial memo commit bug, makefile fix + + Modifying a small item as memo (i.e. through Modify) properly + committed a change, but subsequently left an incorrect pointer after + the commit. Fixed, and added test s37 to catch this case. + + Don't strip symbols from installed static libs (whoops!). + +2001-10-31 Fixed Mk4py error flag clear on delete + + When deleting rows, a slice was constructed from a PWOSequence, which + generates an otherwise harmless error when its length is checked. + The flag was not cleared, causing errors in subsequent Python + statements. Changed to a PWOTuple in PyView.cpp (2x). + +2001-10-26 Fixed Mk4tcl re-open test + + The tcl/test/mk1basic.tcl test #6 was reporting open failure on + non-Linux systems. Fixed, the code works, the message was wrong. + +2001-10-19 Rearranged some demo files, Lua binding + + Rearranged some of the demos/samples from the python/ and tcl/ areas, + and placed them in examples/ instead, for consistency. + + Added "selmap.tcl" to illustrate how to turn select into a view. + + Added a basic Lua binding (incomplete, but it's a start) in lua/. + +2001-10-18 Fixed recent hash bug, and add to ordered + + The recent "fix" to deal with hash misses introduced a huge bug, + causing the mapping to be recalculated on each insertion. + + Adding to an ordered view did not always work, because the code was + based on SetAtGrow, which is not suitable for an ordered view in + which the row position is determined implicitly. Fixed by changing + c4_View::Add to use c4_View::InsertAt instead. + +2001-10-14 ############################################## MK 2.4.1 + + Custom-extended Tclkit, and threading + + Minor kitInit.c change allows wrapping apps as a "custom-extended" + version of Tclkit. Enable threaded Tcl build on Unix. + + This release consolidates the most recent fixes and code tweaks. + +2001-09-27 Fix temp storage open in Mk4tcl + + A recent change made it impossible to open temporary storages from + Tcl ("mk::file open db") - <blush> - now fixed again. + +2001-09-19 Bug fixes + + Fixed a problem during commit, when memo's are modified, causing them + to be stored inline again. This caused a late reference to mapped + memory - leading to intermittent crashes on commit. + + Changes/fixes in mk4tcl.cpp, to prevent a problem when a datafile is + re-opened again under the same name later on. Old references could + in some cases end up stale - leading to crashes much later. + +2001-09-05 Bug fixes + + Hashing bug fixed: failed to terminate when looking for a missing key + after a hash collision. The fill count was not being tracked + properly to enforce that there is always at least one unused slot. + + Fixed bug when setting a string from a higher row: the data gets + trashed, because too little copying was done while creating a gap. + This bug may have led to some other cases of "damaged" datafiles. + Solved by copying input data in c4_FormatB::SetOne(). + + Fixed long-standing bug with commit-after-load, by now doing a full + view/subview copy. This is inefficient, but it avoids all sorts of + mapping/strategy problems. This also fixes test s33 (at last!). + + Added regression tests b27, c21, m01, and s36 to check above fixes. + +2001-06-29 ############################################## MK 2.4.0 + + More changes to the Mk4py interface + + - allow setting a row to a value if the row has a single property + - there is an unexplained crash when setting slices with wrong type + +2001-06-22 Changes to the Mk4py interface + + Modifications and fixes gratefully accepted from John Barnard: + - c4_LongProp support ('L' datatype) + - row.__attrs__ returns the list of all properties + - row.__view__ returns the container view of the row + - row.__index__ returns current position in view + - view.setsize(n) added, extends/truncates number of rows + - generalized makerow to allow any sequence, not just lists + + Changed PyRowRef to inc/dec the reference it has to the underlying + view. Should prevent dangling pointer problems, such as deleting a + storage while rows are still in use. This adds a little overhead. + + Fixed a PyErr_Clear issue when accessing non-existent properties. + +2001-06-12 Close DB filedesc on exec + + Added an fcntl call to fileio.cpp, so that on Unix database file + decriptors are closed on exec (relevant when doing "exec ... &"). + +2001-05-30 Fixed mem-leak in Mk4tcl + + A long-standing bug, in TclSelector::ExactKeyProps. + +2001-05-28 Security fix in Tclkit + + See end of the tcl/kit/README file for deatils. + +2001-03-30 Fixed long-standing commit bug + + There was an intermittent bug in c4_Persist::Commit, when properties + were "restructured away" (dropped). The bug was hard to track down, + because it depended on what address ranges the O/S assigned to + mem-maps. Might also fix other spurious commit/exit crashes. + +2001-03-29 ############################################## MK 2.3.4 + + The "last" release candidate + + Now checked into its *new* CVS home at equi4.com. Mailings list(s) + have also been moved to this site. + +2001-03-28 Fix Win build, broken on Mar 27 + + Dropped kBufMax from mk4.h, it caused compiler errors in MSVC6 - + switched to "sizeof" in a couple of places. This error was + introduced by the double-fix of 3/27. + +2001-03-28 Allow builds of Tclkit with Sun CC + + Integrated a few changes provided by D.J. Hagberg. Note: the + M-solaris.sh and M-dyn.sh scripts need to be manually edited when + choosing between CC and gcc. + +2001-03-27 Double-alignment bug on Solaris + + Two changes (c4_Bytes in mk4.h and src/column.h) to fix an alignment + problem for 8-byte doubles on Solaris. This caused tests b17, b23, + b24, s22, and s28 to fail. + +2001-03-26 Fixed cross-platform commit bug + + There was a nasty bug in the 2.3.x code, which wrote incorrect field + sizes when committing to a datafile with a different byte order + (i.e. created on a machine using different endian-ness from the one + doing commit). + +2001-02-14 Removed file events from Tclkit + + Changed kit/rechan.cpp to not generate file events. This avoids a + bug in Tclkit whereby an open file can generate a continuous stream + of file events as long as the file is open - the console will seem + to be frozen, though "close $file" does work and fixes it. + +2000-12-13 Added missing c4_LongRef export + + Added a line in mk4dll.h to resolve references to the new + c4_LongProp/c4_LongRef datatype on Windows. + +2000-12-04 Fixed conversion bug + + There was a bug in c4_FormatV::OldDefine, causing MK to crash when + trying to convert an old-format file with empty subviews in it. Bug + introduced on 14 Nov. + +2000-12-02 Fixes in Tclkit code + + Fixed a Tcl-level bug causing memory leaks for all compressed files + stored with MK opened for reading. + + Disabled event sources (mk4tcl.cpp and rechan.cpp) to avoid a GUI + freeze-up while a fake file is open. + +2000-11-16 Simplified c4_Storage + + Made some changes so that a c4_Storage no longer has a separate + c4_Persist* copy. As a result, a storage can now be reconstructed + from any root-level view. This generalizes views, and prepares for + a merge. + +2000-11-14 Fixed mem leak in conversion code + + The conversion code from pre 2.3 files had a memory leak in + c4_FormatV::OldDefine, causing f06 to leak, as well as several + subsequent erroneous leak reports. Changed a few "new" to "d4_new" + calls along the way. + + Remove FlipBytes members, they are no longer needed. + +2000-11-08 Tclkit now in the distribution + + The "Tclkit" project has been merged into Metakit. Details and + updated build info at "tcl/kit/README". The SourceForge CVS + repository is up to date again. + + Mk4py: improved number conversion and error handling. + +2000-11-03 ############################################## MK 2.3.3 + + First final release candidate + + Yes, there will probably be a second one as well... + +2000-10-31 Improved error handling + + More logic added to catch errors in flush and streams. This affects + the C++ core as well as Mk4py and Mk4tcl. API of c4_Stream::Write + changed to return success flag. + +2000-10-30 Added autocommit call to Mk4tcl + + To better support VFS, Mk4tcl now has a new command "mk::file + autocommit <db>" to force commit on close. + +2000-10-26 Changes to compile with Borland C++ + + Minor tweaks to compile with Borland C++ Builder 4.0, which does not + support "long long". A new Kitviewer has been built (new code to + replace c4_View::Describe). + +2000-10-03 Fixes for Alpha Unix + + Moved _item in column.h up to fix alignment sensitivity. Config.h + did not get SIZE_LONG right on Alpha Unix (0?). + +2000-09-27 Contributed fixes and Python 2.0b2 + + Adjusted makefile to build with Python 2.0b2 release. + + Source code tweaks to avoid DEC CXX 5.7 compiler errors. Add + no-inherit flags for Win32 to not leak file handles. + +2000-08-27 Allow derived row deletes in Mk4py + + Added code to PyView.cpp to handle deletes (and slice deletes) in + derived views, see "examples/derived.py". + +2000-07-30 Major auto-convert 1.8.6 file bug + + Bug in on-the-fly conversion of bytes properties ('B') in pre-2.0 + datafiles (i.e. 1.8.6 and earlier) resolved. + + Unfortunately, this bug can not be 100% unambiguously fixed. The + new code *will* properly detect most cases, and convert both 1.8.6 + and 2.0 datafiles on the fly, but especially for views with only a + few rows and at most a few bytes of data per row - the conversion + *might* fail. + + In this case, MK will have to be compiled with a define to force it + to either assume all old datafiles are 1.8.6 (-Dq4_OLD_IS_PRE_V2), + or to assume that they are always 2.0 (-Dq4_OLD_IS_ALWAYS_V2). If + you are currently using MK 1.8.6, then you should *skip* the update + to 2.01, and consider updating to 2.3.x. This way you never have + any 2.0 files around, and can force all your code to handle 1.8.6 + files properly (by using "-Dq4_OLD_IS_PRE_V2"). + + See src/format.cpp, c4_FormatB::OldDefine for details. + + This bug *only* applies to bytes properties in pre-2.0 data files. + Conversion of 2.0x files is unaffected. + +2000-07-25 Fixed new self-referential views + + The new recursive / self-referential view definition style has been + fixed, e.g. "view[data:S,self[^]]" will now let you store a tree of + arbitrary depth, with each 'self' subview having data and self + properties. See the demo in "examples/selfref.py" to see how this + all works. + +2000-07-22 Fixed bug in double restructuring + + Solved a very long-standing bug in restructuring, which caused + incorrect (non-zero, small) default values when a c4_DoubleProp was + added to a view which already had rows. + +2000-07-18 Added remapwith and pair to Mk4py + + Exposed C++'s c4_View::RemapWith as v1.remapwith(v2), and + c4_View::Pair as v1.pair(v2) in the Mk4py Python binding. Added + pair.py, remap.py, and wrap.py in "examples/" dir. + +2000-07-12 Added metakit.py wrapper + + Added "metakit.py" script to wrap Mk4py, including a new + metakit.dump() to pretty-print views. More utility code will be + added over time. The preferred way to use Metakit from Python is + now "import metakit". + +2000-07-06 Conversion fix, warning cleanup + + Fixed on-the fly conversion of old datafiles. The free space was + not managed properly - changed to never touch any data inside the + file during conversion. + + Some source code change to get the compile through gcc flags + "-fguiding-decls -Wall -pedantic -Wno-unused". The only remaining + complaint is about using "long long". + +2000-07-04 MkSQL subtree, "indexed" mapped viewer + + Added the sql and mksql subtrees to the distribution, with Gordon + McMillan's MkSQL engine, written in Python. The "isql.py" script is + a simple interactive shell around it. + + Started work on a new viewer which maintains a persistent index (as + a one-int-prop permutation), see src/remap.cpp. + +2000-07-03 Mk4tcl fixes + + Fixed view rename problem and "delete end" (Matt Newman). Adjusted + the tests in tcl/test/mk5object.tcl accordingly. + +2000-06-30 Tequila fixes + + Close fix and failure handler (Steve Landers). + +2000-06-29 ############################################## MK 2.3.2 + + First beta release + + The new release is 99% feature-complete. What remains is to further + document C++/Python/Tcl use and to fix bugs. + + Python sample code in "python/aside.py" and "python/find.py". Tcl + samples in "tcl/test/mk5object.tcl" and "tcl/mapped.tcl". + +2000-06-28 Hash/blocked/ordered: changes and fixes + + Changed hash insertion to insert at specified position. This makes + it possible to use hashes "under" ordered views. For best + performance, insert rows at end of hash views. + +2000-06-26 Documentation, example, Mk4tcl OO fixes + + Moved C++ member documentation out of "mk4.h" header. Added + examples/ directory, with a find.py timing example. Fixed bugs in + new Mk4tcl: "$vw find" and "$vw delete end". Added tests for new + Mk4tcl OO interface: ":mk5object.test". + +2000-06-16 Improved modifiable custom viewers + + The Pair and Slice viewers now support set/insert/remove, while + RemapWith/Concat/Rename support setting values. + +2000-06-15 Many changes to the Tcl code, hashing + + Contributed by Matt Newman, it adds support for most custom viewers, + including the new hash etc. Added a fast find. Caveat: most old + custom viewers are still not modifiable. + + Hash calculation improved, far less collisions than before. + + Blocked viewer seems to work. Ordered on top is sub-optimal. + +2000-06-12 GetUpperLimit, Blocked, Ordered, mk4too + + Removed c4_View::GetUpperLimit (it's equivalent to GetSize-1 and was + not being used anywhere). + + Start implementing c4_BlockedViewer, a simple balanced/blocked + nested data structure. Also started on a c4_OrderedViewer, which + keeps the underlying view sorted during changes. These two can be + combined to implement an efficient 2-level btree. + + Adopted code by Matt Newman for oo-cmd's for Mk4tcl views. First + trials work, started to extend with new custom viewers. + +2000-06-09 Change case of a few Mk4py members + + Changed all top-level members in the Python interface to lower case: + storage, property, view, wrap (will break existing code). + + Fix bounds check in Mk4tcl.cpp for commit/rollback (new code). + + Whoops, forgot to add new src/remap.{h,cpp} to the cvs tree. + +2000-06-08 Implemented hash lookup + + Added a new virtual c4_Sequence::RestrictSearch, which lets a view + take over searching (used by c4_View::Find). The result is that the + new hash viewer gives a huge speedup for finds. Find requests which + require linear scanning are unaffected. + +2000-06-07 Documentation extraction based on Doxygen + + The automatically generated output from Doxygen is working out well + and looking pretty, added "src/doxy.h" with more comments. + +2000-06-05 Started hash and btree custom viewers + + The hash implementation is nearly done, also usable from Python. + Btrees are being implemented as fixed 2-level for now. + +2000-06-01 Fixes + + Fixed crash when opening missing file r/o new in 2.3.1 alpha. Fixed + incorrect on-the-fly-conversion of 2.0 format subviews. Added + "storage.aside(storage)" to the Python interface. + +2000-05-30 Fixed commit-aside + + The new commit-aside code was botched by recent changes. Fixed + SetAside to pick up new root seq, changed by implicit rollback. + Note that a commit-aside is not finished until you *also* commit the + secondary file containing all newly generated changes. + + Fixed bug in c4_BytesRef::Access, introduced in 2.3.1 alpha. + +2000-05-29 Added new 64-bit long datatype + + Added support for 64-bit longs (type 'L'), and c4_LongProp, etc. + This type is not autosizing, it always uses 8 bytes per entry. Uses + "long long" or "__int64", else defines struct with 2 longs. This is + not yet correct for platforms which have no 64b ints. No regression + tests or Python/Tcl interfaces yet. + +2000-05-28 Better file mark scanning + + Added c4_Strategy::EndOfData, to determine the logical end of a + Metakit datafile. This call can be used to check whether a file + contains any data, and whether a commit-extend has been performed. + Old-style scripted documents (with preamble) can now be opened. + Changed strategy class, DataSeek has been merged with read/write. + Removed c4_LoadContext, LoadIt member moved to c4_Persist. + +2000-05-27 Tweaked configuration define's + + Make the release build the default (no assertions, use inlines). + Enable booleans for gnuc by default (it's pretty standard by now). + Added extra include path to better find Python's includes. + +2000-05-26 Fixed Tcl dependency + + Changed configure script to no longer look for Tcl if the + "--with-tcl=..." parameter is not specified. + +2000-05-25 ############################################## MK 2.3.1 + + First alpha release + + Officially, this is "Metakit 2.3.1 alpha" (ignore "2.3.0" in mk4.h). + All alpha's are 2.3.1 (beta's will be 2.3.2, finals start at 2.3.5). + Builds on Linux/Mac/Win appear ok - as do Mk4py, Mk4tcl, and Tclkit. + +2000-05-06 Massive changes to the core + + To summarize the main issues: management info is now stored in such + a way that it need not be read in right away - file open is now + instant. The S(tring) datatype is now stored as B, making it far + more scalable (API/use is unchanged). Storage objects now derived + from views, both can be initialized from a stream (data will be kept + in a buffer, beware of potentially large memory use). + + Several file format changes are "for future expansion". + +2000-05-05 Fixed builds without Tcl + + In 2.0.1, the make would fail if there was no Tcl to build with, or + not an appropriate release. Changed Makefile to report and skip Tcl + builds in that case. + +2000-04-06 Fixed a nasty restructure/mmap bug + + When a property is deleted by a restructure, then committed, then + later committed again, a problem can cause MK to crash. It has only + been detected in debug builds, but the problem turns out to be a + fundamental one (only happens with memory-mapped files, if the file + is resized). Fix in next rel (c4_HandlerSeq::DetachFromStorage). + +2000-04-02 Memo properties are no longer needed + + The M datatype is gone from the public API (and now illegal). + Everything binary should now be stored in B(ytes) properties, which + then adaptively decide which internal format to use, based on a + simple heuristic (which will be refined later). Existing datafiles + will automatically convert from M to B. The partial Access and + Modify calls now also work on B items. + +2000-03-30 Minor change in c4_Strategy + + Dropped the _keepAfterCommit flag in c4_Strategy, it probably has + never been used and it interferes with new features. + +2000-03-27 Change in API for creating storages + + It is no longer possible to create a storage and define its + structure with a single call. Instead, open a storage in r/w mode + (i.e. "1") and then call the (now public) SetStructure member to + define the structure of all views. This change is necessary to + prepare for the upcoming "commit-aside" logic. + +2000-03-23 File format changes (in progress) + + The new file format has an incompatible header, so old code will not + recognize new datafiles. Major changes are: added a file tail + marker, the serialized format is now a very good way to compress + datafiles, since it can be efficiently opened in on-demand/mmap'ed + mode. The new format supports several planned features. Code to + convert existing files on-the-fly will be added before this change + is released. + +2000-03-19 Added c4_Strategy::FileSize + + The Strategy::FileSize call is used for a file format change. + +2000-03-18 Added c4_View::Locate + + Locate returns the number of matching rows, and optionally the + position of the first one, using binary search. Like the + c4_View::Search function, it requires the view to be sorted. + +2000-03-17 ############################################## MK 2.01 + + Maintenance release, it's solid + + Updated MK version number to 2.01, this maintenance release + represents a very stable version. + + Small change to b07 test to avoid evaluation order problems. + + Added unix/metakit.spec file for RPM, thanks to Sean Summers. + +2000-03-16 Drop Store, fix deep copy, drop segments + + c4_Storage::Store never worked properly under all conditions. It's + been deprecated for some time and has now been removed. Made a + handful of changes to test- and demo code to drop it. + + Duplicating a view with deep copy never worked, because it used the + buggy Store call as well. Changed to use recursion. + + Dropped support for segmented tree-walk storage, which hasn't been + used since 1.5 (use a commit with 2.0 to convert files). This is + necessary to prepare for some file format changes. + +2000-03-15 Modifiable custom viewers, other tweaks + + A start has been made with making custom viewers updatable. The new + methods are Set, InsertAt, RemoveAt, and Move, but the number of + viewers which implement this is still limited. Mk4py has been + adjusted to allow "set" on wrapped views. + + Removed c4_Strategy::DataLoad, it was only used in one place. Small + optimization of the 2 calls to c4_Streamer::NextByte. Get rid of / + disallow read calls on memory mapped files. + +2000-03-14 Makefile tweaks, non-commits smarter + + Changed from --enable-tcl to --with-tcl=DIR, because the old + approach only worked with Tcl installed in a standard place. + + Commits of a R/O file now fail. Also, if no changes have been made, + a commit will no longer write anything to file. + +2000-03-13 Several new commands added to Mk4py + + Several changes were submitted by Gordon McMillan, which add better + support for his upcoming SQL engine. + +2000-03-12 Allow embedding MK datafile at end of EXE + + Mk4tcl was changed to look for an optional trailer for quick access + to the start offset. This makes it possible to append datafiles to + executables, even if they are larger than 4 Kb. + +2000-02-29 Fixed rare bug with lots of memo fields + + There was a bug in free space management (persist.cpp), which can + only occur when exactly 7500 free space gaps are present, and a + commit crosses the threshold. There was also a small mistake in + that same code causing a bit of free space waste. + +2000-02-24 Added proxy support to Tequila + + Tequila can now be used as basic client/server setup for Tcl + scripts. See tcl/tequila/README for details and an example. + +2000-02-04 Fixed mk::views (Mk4tcl) + + The mk::views command failed to list the first view in the file + (this bug was introduced by changes in MK 1.99). + +2000-01-02 Adjusted y2k + + Copyright and license dates adjusted. + +1999-12-26 More Mk4py changes + + Added sortrev, and fixed "select(low,high)". Docs updated. + +1999-12-23 New view operators in Mk4py + + Added rename, project, groupby, and counts operators to Mk4py. + These were already part of the C++ core. + +1999-12-22 Avoid GetId inline warning + + Reordered GetId in "mk4.inl" to avoid (harmless) inline warning. + +1999-12-21 Checked-in Catfish and Kitviewer sources + + Added win/catfish and win/kitviewer areas. Catfish was built with + MSVC 1.52, so the win/msvc152 area has also been added. + + Kitviewer requires Borland C++ Builder 4.0 to build (using VCL), it + has been adjusted to now also recognize scripted documents. + +1999-12-20 Bug fix in set-after-get situations + + A bug has surfaced when setting string/byte/memo values which span a + 4 Kb block boundary. The bug can only happen if data is first + fetched and subsequently changed. The affected code is in + src/format.cpp (3x). Added Tcl test 5.5 to catch this. + +1999-12-19 Mac tweaks + + Changes to make the Mac versions build from the CVS repository. The + Mac can also cross-compile Windows libraries using MWCW 5. + +1999-12-17 Add the Tequila example + + Added the Tequila global Tcl array data server, see tcl/tequila/. + +1999-12-15 MK 2.0 Official Open Source release + + Removed a bad assertion from FormatX::Compare. + + Several new services set up on the excellent SourceForge.com site. + +1999-12-14 Documentation added + + The C++ API documentation has been added to the distribution, as + well as a document describing the file format details of Metakit. + +1999-12-13 Bug fix affecting c4_View::Description + + There was a problem with c4_ViewScanner::Describe, due to a change + from c4_String to (const char*). Now c4_View::Store works again. + +1999-12-12 MK 1.99 New release, as open source software + + The major change is that Metakit has been released as open source + software, based on the liberal X/MIT-style license. Commercial + support remains unchanged for all recent commercial customers, and + for those who purchase the Enterprise License. The Universal Source + license has been terminated, because full source code is now freely + downloadable by anyone from the website. + + Sources and documentation files have been adjusted accordingly. + +1999-12-08 MK 1.9h Bool support for gcc/egcs, minor fix + + Clear _field after delete in c4_HandlerSeq::DefineRoot. This + triggered an assertion on Linux, when compiled in debug mode. + + Added pre-processor logic to detect whether gcc supports bools. + + Removed all indentation from #define's, #ifdef's, etc. This was + done after a report that some compilers can get confused by this. + +1999-12-06 Derived view row copy fix + + There was a problem when using SetAt with derived views as source, + due to a remapping problem. Fixed viewx.cpp, added new test b25. + +1999-11-25 MK 1.9g Makefile changes, thread-safe Mk4tcl + + Renamed options to --enable-python and --enable-tcl, both now off by + default, since most people probably don't want to enable both. + + The new "-shared" changes to make Mk4tcl thread-safe have been + folded into the main source code. + +1999-11-22 Channel improvements Mk4tcl + + There was a close conflict in mk::channel, also several changes to + improve mk::channel fileevent handling. + +1999-11-19 Bug fix in Mk4tcl + + There was an array overrun when mk::get was called without fields. + Added code to avoid this. + +1999-11-11 Mk4tcl exit handling + + Simplified Mk4tcl exit handling, fixes "interp delete" crash. Added + new mk5fixed.4 test to catch this case. + +1999-11-10 Mk4tcl shared and multi-threaded access + + Made a first experimental version of Mk4tcl (1.2.1, not announced) + which allows sharing a database between interpreters and threads. + +1999-11-09 STL, MkWrap, compare caching + + Tweaked the makefile to support STL builds (also adjusted README). + + Fixed bug in MkWrap, calling storage.description() without args. + + Yet more fixes in comparisons, this is all one problem, caused by + changes in caching for ints, floats, and doubles. Added B24 test. + These (last?) problems occurred in custom / compound views. + +1999-11-08 MK 1.9f Fixed sort comparisons + + There was a cache problem with comparisons of int / float / double + sorts. Fixed, also added new B23 regression test to check for it. + +1999-11-07 Little nasty details + + Tweaked some make/project files. All regress tests and Tcl tests + now reported to really work on Solaris, Linux, Windows, and Mac. + Regression tests required more memory on Mac to get past L03 case. + +1999-11-06 MK 1.9e Big oops: Mk4tcl and MkWrap were broken! + + Fixed a c4_Strategy / c4_FileStrategy mixup in Mk4tcl and MkWrap. + +1999-11-05 Simplify c4_Storage + + Moved state out of c4_Storage and into c4_Persist, to prepare for + multi-thread wrappers. Made a new, simpler design to achieve that. + +1999-11-04 MK 1.9d Factor out stdio dependencies + + Moved all stdio dependencies out of core into new "fileio.cpp" src. + The mk4.h header no longer includes <stdio.h>, added new "mk4io.h" + header with a c4_FileStrategy class, derived from c4_Strategy. + + Also factored c4_Stream/c4_FileStream out of the c4_Strategy class. + This alters the API slightly, but makes it 100% portable/embeddable. + + Added "tcl/iohan.tcl", a simple wrapper for generic storage access: + locally, on a FTP server, in a local MK datafile, or using a remote + Tequila server (see http://www.equi4.com/tclkit/tequila.html). + +1999-11-03 Improved detach/restructure handling + + Changed detach to drop all persistent formathandlers, but leave the + number of rows intact. It will be much faster to destroy columns + than to delete (and propagate) rows. The result is that a pointer + to a view of which the underlying storage object goes away will end + up with the same number of rows as before, but no properties at all. + + Fixed a problem which would have occurred when properties are being + "restructured away" and then committed. The solution is to check + for this and delete all such properties at commit time. + + Moved Buffer() out of c4_HandlerSeq and c4_CustomSeq, and made the + new version in c4_Sequence allocate the c4_Bytes object lazily. + Faster, and decreases sequence object size (for lots of subviews). + + Mk4tcl, mk::layout now returns the proper layout even if the views + are empty. Solved by adding extra arg to c4_Storage::Description, + to return structure of a single top-level view. Fixes "mk8basic.1", + and the returned string now has one bogus list layer stripped off. + + MkWrap also adjusted with optional arg for storage.description(). + +1999-11-02 New Wrap code in MkWrap + + MkWrap, added new Wrap(seq,props,byPos) member, a c4_CustomViewer + which wraps any Python sequence as MK view (for use in joins, etc). + +1999-11-01 MK 1.9c Mk4tcl changes + + Mk4tcl, several changes: added "-size" option to mk::get to return + the size of prop value without fetching it (see new basic.9 test). + Added "-globnc" for case-insensitive globbing (for regexp, this is + available through the new (?i) metasytnax of Tcl 8.2 (see basic.10). + +1999-10-31 MK 1.9b Solved shared lib unload with Tcl 8.2 + + Changed property symbol table setup to avoid static initializers, to + work around a problem with shared library cleanup from Tcl 8.2. + Added new c4_Property::CleanupInternalData call to clean up 100% + (this code need not usually be called, only if memory is tracked). + This fixes the crash-on-exit bug in Mk4tcl (Linux and Solaris). + +1999-10-29 Make support for MkWrap and Mk4tcl + + Both MSVC and MWCW now also build Mk4tcl and MkWrap extensions. The + MWCW project compiles for Win and Mac *on* either Win or Mac. + Makefile extended to build Mk4tcl and MkWrap, added dist target. + Updated to latest Perceps 3.5 beta, started generating docs again. + +1999-10-27 MK 1.9a New build / directory structure + + Completely reworked the directory structure to simplify all builds. + Created new "builds/" area for all intermediate and output files. + New MSVC 6.0 project structure created in "win/msvc60/". + + Moved Mk4tcl to the "tcl/" top-level directory, and MkWrap to the + "python/" dir. The MkWrap code has been removed from the project. + + Removed c4_View::Match and the regular expression package, since it + can just as efficiently be done with a wrapper around MK, now that + string results no longer allocate a temporary copy. + + Started writing a Tcl-based test suite for use with Mk4tcl. + +MAJOR CHANGE SINCE 1.8.6 + + Merge handler.cpp and format.cpp classes to get rid of special-cased + in-memory version of handlers. This has major effects on how data + is kept for unattached views (they still exist, but no longer + special). The code is leaner and meaner, it passes all regression + tests. + +ALSO LISTED IN THE RELEASE HISTORY + + Added c4_MemoRef::Access and c4_Memoref::Modify for partial access + to memo fields. Avoids copying and allows inserts/deletes anywhere. + MkWrap and Mk4tcl have both been extended to allow using Memo fields + for simulated file IO (mk::channel for Tcl, MkMemoIO.py for Python). + + Added c4_Reference::GetSize to determine the size of a value without + accessing it. For ints, returns negated bit width if 1/2/4 bits. + + Added experimental c4_View::RelocateRows to move rows from one view + to another (both must be in same storage, with the same structure). + Moves do not involve any data copying w.r.t. subviews and memo's. + +The old release history is at http://www.equi4.com/metakit/history.html + +# vim: tw=72 diff --git a/akregator/src/mk4storage/metakit/Makefile.am b/akregator/src/mk4storage/metakit/Makefile.am new file mode 100644 index 000000000..af437a64d --- /dev/null +++ b/akregator/src/mk4storage/metakit/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/akregator/src/mk4storage/metakit/README b/akregator/src/mk4storage/metakit/README new file mode 100644 index 000000000..21edd0fc2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/README @@ -0,0 +1,153 @@ +The Metakit Library 2.4.9.3 Jan 2004 +============================================================================== + + +WHAT IT IS - Metakit is an embeddable database which runs on Unix, Windows, + Macintosh, and other platforms. It lets you build applications which + store their data efficiently, in a portable way, and which will not need a + complex runtime installation. In terms of the data model, Metakit takes + the middle ground between RDBMS, OODBMS, and flat-file databases - yet it + is quite different from each of them. + +TECHNOLOGY - Everything is stored variable-sized yet with efficient positional + row access. Changing an existing datafile structure is as simple as re- + opening it with that new structure. All changes are transacted, including + restructuring. You can mix and match software written in C++, Python, + and Tcl. Things can't get much more flexible... + +CORE - The Metakit core library is written in C++. It has a code footprint of + just over 100 Kb on Windows. It can be used as DLL, or linked statically. + Debug builds include extensive assertion checks to catch problems early. + +PYTHON - The binding for Python is called "Mk4py". It uses SCXX by Gordon + McMillan as C++ glue interface. The source is in directory "python/". + +TCL/TK - The MK extension for Tcl is called "Mk4tcl". It is being used in a + number of commercial projects. The source is in directory "tcl/". + +LICENSE AND SUPPORT - Metakit is now distributed as open source software (the + X/MIT-style license is at the end of this document). Commercial support + is available through an Enterprise License, see the URL mentioned below. + +DOCUMENTATION - All documentation uses HTML. The main page is "Metakit.html", + which leads to the rest of the documentation in the "doc/" directory. + The C++ API Reference is extracted from the source code using Doxygen. + +WEBSITE URLS - The main pages on the world wide web, for news and downloads: + Homepage: http://www.equi4.com/metakit.html + Python news: http://www.equi4.com/metakit/python.html + Tcl/Tk news: http://www.equi4.com/metakit/tcl.html + License info: http://www.equi4.com/mklicense.html + Contact info: http://www.equi4.com/contact.html + +ACKNOWLEDGEMENTS - Thanks to everyone who has helped shape and extend Metakit, + including Kyrill Denisenko, Mark Roseman, Gordon McMillan, Matt Newman, + Christian Tismer, John Bushakra, Steve Landers, Jacob Levy, John Barnard, + Nicholas Riley, Brian Kelley, and many more people who have reported bugs + and helped fix them. Last but not least, many thanks to all enterprise + license customers and all my clients for funding Metakit work. + + +INSTALLATION +============ + +(NOTE to Python users: ignore this and do "cd python; python setup.py ...") + +All platform builds and language bindings are designed to work from a single +common "builds/" directory. Where possible, that is - it turns out to be +impossible to keep build side-effects limited to *just* this directory +(CodeWarrior can't be told where to place its temp data, and Visual C++ still +alters a few files next to the project ".dsw" file, to name two offenders). + +UNIX + + It is no longer advised to build the Unix code in the "unix/" directory. + Instead, you should perform the following steps: + % cd builds + % ../unix/configure + % make + % make test + And optionally (this only installs the core lib, not script extensions): + % make install + + By switching to the "builds/" directory, you will keep the distribution + directory tree 100% unaltered. All changes are made in this subdir, and + all final build results are left behind in this same subdir. + + To build with STL containers and strings, you can do the following: + make CXXFLAGS='-Dq4_STD' test # add -O3 etc, as needed + + To build the Mk4tcl extension on Unix, change the configure to: + % ../unix/configure --with-tcl=<dir-where-tclConfig.sh-is> + + Use "../unix/configure --help" to find out about all the other settings. + +WINDOWS + + There is a "win/" directory which contains subdirectories for a number of + compiler systems. Metakit has been built with many different compilers + in the past (Microsoft, Borland, Watcom, Symantec, Metrowerks, Optima), + only a few are maintained (there are 12 configurations for MSVC6 alone!). + + The MS Visual C++ 6.0 project is "win/msvc60/mksrc.dsw", with subprojects + for the C++ demo (mkdemo), building dll's (mkdll), static libs (mklib), + regression tests (mktest), as well as Tcl (mktcl) and Python (mkpython). + It has been set up to place all intermediate files and final results in + the "builds/" subdirectory, even though you'll launch it from "win/". + + To build with STL containers and strings under MSVC, define "q4_STD". + To build with MFC containers and strings under MSVC, define "q4_MFC". + + The Metrowerks Codewarrior project is in the "mac/" directory, and can be + used to build both Mac and Windows versions (on either Mac *or* Windows). + The core libraries are built with "mac/cw5.mcp", demos / tests are built + with "cw5apps.mcp", Tcl is "cw5tcl.mcp", and Python is "cw5python.mcp". + + The Borland C++ Builder projects have not yet been incorporated in this + release, but the "KitViewer" application is an example of how to use BCB. + + The Cygwin build (B20.1 / gcc 2.95.2) is different, because it uses the + unix autoconf system, and must be launched as described above for UNIX. + I have upgraded to the latest development of libtool to be able to build + DLL's with Cygwin. You can build the "-mno-cygwin" version by editing + the Makefile by hand and adding that option to CXXFLAGS. + + Rob Bloodgood adds that the following GCC options are for maximum code + efficiency on x86 hardware: "-O2 -m486 -malign-loops=2 -malign-jumps=2". + I have not yet tried this myself, but am passing on the tip. + +MACINTOSH CLASSIC + + The Mac version requires Metrowerks CodeWarrior 6. See the info above + in the Windows section (MWCW is multi-platform). The projects are all + located in the "mac/" folder, which is also where MWCW will place its own + "... Data" folders with intermediate results. As with all other setups, + final results are made to end up in the "builds/" directory. + + +LICENSE AND COPYRIGHT STATEMENT +=============================== + +Copyright (c) 1996-2004 Jean-Claude Wippler + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +============================================================================== +-- Jean-Claude Wippler <jcw@equi4.com> diff --git a/akregator/src/mk4storage/metakit/include/mk4.h b/akregator/src/mk4storage/metakit/include/mk4.h new file mode 100644 index 000000000..33016dba0 --- /dev/null +++ b/akregator/src/mk4storage/metakit/include/mk4.h @@ -0,0 +1,1078 @@ +// mk4.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Main Metakit library include file + */ + +#ifndef __MK4_H__ +#define __MK4_H__ + +//--------------------------------------------------------------------------- +// +// TITLE +// +// The Metakit Library, by Jean-Claude Wippler, Equi4 Software, NL. +// +// DESCRIPTION +// +// Structured data storage with commit / rollback and on-demand loading. +// +// ACKNOWLEDGEMENTS +// +// To Liesbeth and Myra, for making this possible. +// +//--------------------------------------------------------------------------- +// +// NAMING CONVENTIONS PREFIX REMARKS +// +// Compile time options q4_ Always defined as 1 or 0, capitalized +// Preprocessor defines d4_ Use with "#ifdef" or "#if defined()" +// Classes c4_ Classes, listed at start of headers +// Typedefs t4_ Type definitions, if outside classes +// Global functions f4_ Internal, these are rarely defined +// +// Member functions Start in uppercase +// Instance variables _ And start in lowercase +// Static members _ And start in uppercase +// +// Local variable names Start in lowercase +// Formal parameter names Start lowercase, end with underscore +// +//--------------------------------------------------------------------------- + + /// Current release = 100 * major + 10 * minor + maintenance +#define d4_MetakitLibraryVersion 249 // 2.4.9.3 release, Jan 26, 2004 +#define d4_MetaKitLibraryVersion d4_MetakitLibraryVersion // compat, yuck + +//--------------------------------------------------------------------------- +// Declarations in this file + + class c4_View; // a view on underlying data + class c4_Cursor; // an index into a view + class c4_RowRef; // a reference to a row + class c4_Row; // one row in a view + class c4_Bytes; // used to pass around generic data + class c4_Storage; // manages view persistence + class c4_CustomViewer; // used for customizable views + class c4_Stream; // abstract stream class + class c4_Strategy; // system and file interface + + class c4_Property; // for access inside rows + class c4_IntProp; + class c4_LongProp; + class c4_FloatProp; + class c4_DoubleProp; + class c4_StringProp; + class c4_BytesProp; + class c4_ViewProp; + + // Everything below is part of the implementation, not for public use + + class c4_Sequence; // a collection of rows + + class c4_Reference; // refers to the actual data values + class c4_IntRef; + class c4_LongRef; + class c4_FloatRef; + class c4_DoubleRef; + class c4_BytesRef; + class c4_StringRef; + class c4_ViewRef; + + class c4_Dependencies; // not defined here + class c4_Handler; // not defined here + class c4_Notifier; // not defined here + class c4_Persist; // not defined here + +//--------------------------------------------------------------------------- + + // determine whether we need to include "mk4dll.h" to link as DLL +#if defined (MKDLL_EXPORTS) && !defined (q4_KITDLL) +#define q4_KITDLL 1 +#endif + + // omit floats and doubles in small model 16-bit Intel builds +#if defined (_DOS) && defined (_M_I86SM) && !defined (q4_TINY) +#define q4_TINY 1 +#endif + + // and here's the other end of the scale... +#if !defined (_WIN32) && !defined (q4_LONG64) +#if (defined (_PA_RISC2_0) && defined(__hpux)) || defined (__powerpc64__) || defined(__sparcv9) || \ + defined (__x86_64__) || defined (__s390x__) || defined (__alpha) || defined (__mips64) || \ + (defined (__ia64) && (!defined (__HP_aCC) || defined(__LP64__))) +#define q4_LONG64 1 +#endif +#endif + + // default to inlining for maximum performance +#if !defined (q4_INLINE) +#define q4_INLINE 1 +#endif + +//--------------------------------------------------------------------------- + + // Borland C++ and C++ Builder +#if defined (__BORLANDC__) + // by default, if runtime is linked as a DLL, then so is Metakit +#if defined (_RTLDLL) && !defined (q4_KITDLL) +#define q4_KITDLL 1 +#endif + + // Borland 5.0 supports the bool datatype +#if __BORLANDC__ >= 0x500 +#define q4_BOOL 1 +#endif +#endif // __BORLANDC__ + + // IRIX supports the bool datatype + // define before gcc to cover both the gcc and MipsPRO compiler +#if defined (sgi) +#define q4_BOOL 1 +#undef bool +#undef true +#undef false +#endif + + // GNU gcc/egcs +#if defined (__GNUC__) +#ifndef q4_BOOL +#define q4_BOOL 1 +#endif +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG 1 +#endif +#endif + + // HP aCC +#if defined (__HP_aCC) +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG 1 +#endif +#endif + + // Metrowerks CodeWarrior +#if defined (__MWERKS__) +#if __option(bool) +#define q4_BOOL 1 // bool datatype is optionally supported + // undef, these conflict with c4_Storage::c4_Storage overloading +#undef bool +#undef true +#undef false +#endif +#endif + + // Microsoft Visual C++ +#if defined (_MSC_VER) + // MSVC 5.0 supports the bool datatype, MSVC 4.x has no namespaces +#if _MSC_VER >= 1100 +#define q4_BOOL 1 +#define LONG_LONG __int64 +#else +#define q4_NO_NS 1 +#endif + + // a kludge to avoid having to use ugly DLL exprt defs in this header +#pragma warning(disable: 4273) // inconsistent dll linkage +#endif // _MSC_VER + +//--------------------------------------------------------------------------- +// Other definitions needed by the public Metakit library header files + +#if !q4_BOOL && !q4_STD // define a bool datatype +#define false 0 +#define true 1 +#define bool int +#endif + +#if q4_KITDLL // add declaration specifiers +#include "mk4dll.h" +#endif + +#if q4_INLINE // enable inline expansion +#define d4_inline inline +#else +#define d4_inline +#endif + +typedef unsigned char t4_byte; // create typedefs for t4_byte, etc. + +#if q4_LONG64 +typedef int t4_i32; // if longs are 64b, then int must be 32b +#else +typedef long t4_i32; // if longs aren't 64b, then they are 32b +#endif + +#if q4_LONG64 // choose a way to represent 64b integers +typedef long t4_i64; +#elif defined (LONG_LONG) +typedef LONG_LONG t4_i64; +#elif HAVE_LONG_LONG +typedef long long t4_i64; +#else +struct t4_i64 { long l1; long l2; }; +bool operator== (const t4_i64 a_, const t4_i64 b_); +bool operator< (const t4_i64 a_, const t4_i64 b_); +#endif + +//--------------------------------------------------------------------------- + +class c4_View +{ +protected: + c4_Sequence* _seq; + +public: +/* Construction / destruction / assignment */ + c4_View (c4_Sequence* =0); + c4_View (c4_CustomViewer*); + c4_View (c4_Stream*); + c4_View (const c4_Property& property_); + c4_View (const c4_View&); + ~c4_View (); + + c4_View& operator= (const c4_View&); + c4_Persist* Persist() const; // added 16-11-2000 to simplify c4_Storage + +/* Getting / setting the number of rows */ + int GetSize() const; + void SetSize(int, int =-1); + + void RemoveAll(); + +/*: Getting / setting individual elements */ + c4_RowRef GetAt(int) const; + c4_RowRef operator[] (int) const; + + void SetAt(int, const c4_RowRef&); + c4_RowRef ElementAt(int); + + bool GetItem(int, int, c4_Bytes&) const; + void SetItem(int, int, const c4_Bytes&) const; + +/* These can increase the number of rows */ + void SetAtGrow(int, const c4_RowRef&); + int Add(const c4_RowRef&); + +/* Insertion / deletion of rows */ + void InsertAt(int, const c4_RowRef&, int =1); + void RemoveAt(int, int =1); + void InsertAt(int, const c4_View&); + + bool IsCompatibleWith(const c4_View&) const; + void RelocateRows(int, int, c4_View&, int); + +/* Dealing with the properties of this view */ + int NumProperties() const; + const c4_Property& NthProperty(int) const; + int FindProperty(int); + int FindPropIndexByName(const char*) const; + c4_View Duplicate() const; + c4_View Clone() const; + int AddProperty(const c4_Property&); + c4_View operator, (const c4_Property&) const; + + const char* Description() const; + +/* Derived views */ + c4_View Sort() const; + c4_View SortOn(const c4_View&) const; + c4_View SortOnReverse(const c4_View&, const c4_View&) const; + + c4_View Select(const c4_RowRef&) const; + c4_View SelectRange(const c4_RowRef&, const c4_RowRef&) const; + + c4_View Project(const c4_View&) const; + c4_View ProjectWithout(const c4_View&) const; + + int GetIndexOf(const c4_RowRef&) const; + int RestrictSearch(const c4_RowRef&, int&, int&); + +/* Custom views */ + c4_View Slice(int, int =-1, int =1) const; + c4_View Product(const c4_View&) const; + c4_View RemapWith(const c4_View&) const; + c4_View Pair(const c4_View&) const; + c4_View Concat(const c4_View&) const; + c4_View Rename(const c4_Property&, const c4_Property&) const; + + c4_View GroupBy(const c4_View&, const c4_ViewProp&) const; + c4_View Counts(const c4_View&, const c4_IntProp&) const; + c4_View Unique() const; + + c4_View Union(const c4_View&) const; + c4_View Intersect(const c4_View&) const; + c4_View Different(const c4_View&) const; + c4_View Minus(const c4_View&) const; + + c4_View JoinProp(const c4_ViewProp&, bool =false) const; + c4_View Join(const c4_View&, const c4_View&, bool =false) const; + + c4_View ReadOnly() const; + c4_View Hash(const c4_View&, int =1) const; + c4_View Blocked() const; + c4_View Ordered(int =1) const; + c4_View Indexed(const c4_View&, const c4_View&, bool =false) const; + +/* Searching */ + int Find(const c4_RowRef&, int =0) const; + int Search(const c4_RowRef&) const; + int Locate(const c4_RowRef&, int* =0) const; + +/* Comparing view contents */ + int Compare(const c4_View&) const; + + friend bool operator== (const c4_View&, const c4_View&); + friend bool operator!= (const c4_View&, const c4_View&); + friend bool operator< (const c4_View&, const c4_View&); + friend bool operator> (const c4_View&, const c4_View&); + friend bool operator<= (const c4_View&, const c4_View&); + friend bool operator>= (const c4_View&, const c4_View&); + +protected: + void _IncSeqRef(); + void _DecSeqRef(); + + /// View references are allowed to peek inside view objects + friend class c4_ViewRef; + + // DROPPED: Structure() const; + // DROPPED: Description(const c4_View& view_); +}; + +//--------------------------------------------------------------------------- + +#if defined(os_aix) && defined(compiler_ibmcxx) && (compiler_ibmcxx > 500) + bool operator== (const c4_RowRef& a_, const c4_RowRef& b_); + bool operator!= (const c4_RowRef& a_, const c4_RowRef& b_); + bool operator<= (const c4_RowRef& a_, const c4_RowRef& b_); + bool operator>= (const c4_RowRef& a_, const c4_RowRef& b_); + bool operator> (const c4_RowRef& a_, const c4_RowRef& b_); + bool operator< (const c4_RowRef& a_, const c4_RowRef& b_); +#endif + +class c4_Cursor +{ +public: + /// Pointer to the sequence + c4_Sequence* _seq; + /// Current index into the sequence + int _index; + +/* Construction / destruction / dereferencing */ + /// Construct a new cursor + c4_Cursor (c4_Sequence&, int); + + /// Dereference this cursor to "almost" a row + c4_RowRef operator* () const; + + /// This is the same as *(cursor + offset) + c4_RowRef operator[] (int) const; + +/* Stepping the iterator forwards / backwards */ + /// Pre-increment the cursor + c4_Cursor& operator++ (); + /// Post-increment the cursor + c4_Cursor operator++ (int); + /// Pre-decrement the cursor + c4_Cursor& operator-- (); + /// Post-decrement the cursor + c4_Cursor operator-- (int); + + /// Advance by a given offset + c4_Cursor& operator+= (int); + /// Back up by a given offset + c4_Cursor& operator-= (int); + + /// Subtract a specified offset + c4_Cursor operator- (int) const; + /// Return the distance between two cursors + int operator- (c4_Cursor) const; + + /// Add specified offset + friend c4_Cursor operator+ (c4_Cursor, int); + /// Add specified offset to cursor + friend c4_Cursor operator+ (int, c4_Cursor); + +/* Comparing row positions */ + /// Return true if both cursors are equal + friend bool operator== (c4_Cursor, c4_Cursor); + /// Return true if both cursors are not equal + friend bool operator!= (c4_Cursor, c4_Cursor); + /// True if first cursor is less than second cursor + friend bool operator< (c4_Cursor, c4_Cursor); + /// True if first cursor is greater than second cursor + friend bool operator> (c4_Cursor, c4_Cursor); + /// True if first cursor is less or equal to second cursor + friend bool operator<= (c4_Cursor, c4_Cursor); + /// True if first cursor is greater or equal to second cursor + friend bool operator>= (c4_Cursor, c4_Cursor); + +/* Comparing row contents */ + /// Return true if the contents of both rows are equal + friend bool operator== (const c4_RowRef&, const c4_RowRef&); + /// Return true if the contents of both rows are not equal + friend bool operator!= (const c4_RowRef&, const c4_RowRef&); + /// True if first row is less than second row + friend bool operator< (const c4_RowRef&, const c4_RowRef&); + /// True if first row is greater than second row + friend bool operator> (const c4_RowRef&, const c4_RowRef&); + /// True if first row is less or equal to second row + friend bool operator<= (const c4_RowRef&, const c4_RowRef&); + /// True if first row is greater or equal to second row + friend bool operator>= (const c4_RowRef&, const c4_RowRef&); +}; + +//--------------------------------------------------------------------------- + +class c4_RowRef +{ + /// A row reference is a cursor in disguise + c4_Cursor _cursor; + +public: +/* General operations */ + /// Assign the value of another row to this one + c4_RowRef operator= (const c4_RowRef&); + /// Return the cursor associated to this row + c4_Cursor operator& () const; + /// Return the underlying container view + c4_View Container() const; + +protected: + /// Constructor, not for general use + c4_RowRef (c4_Cursor); + + friend class c4_Cursor; + friend class c4_Row; +}; + +//--------------------------------------------------------------------------- +/// An entry in a collection with copy semantics. +// +// Rows can exist by themselves and as contents of views. Row assignment +// implies that a copy of the contents of the originating row is made. +// +// A row is implemented as an unattached view with exactly one element. + +class c4_Row : public c4_RowRef +{ +public: + /// Construct a row with no properties + c4_Row (); + /// Construct a row from another one + c4_Row (const c4_Row&); + /// Construct a row copy from a row reference + c4_Row (const c4_RowRef&); + /// Destructor + ~c4_Row (); + + /// Assign a copy of another row to this one + c4_Row& operator= (const c4_Row&); + /// Copy another row to this one + c4_Row& operator= (const c4_RowRef&); + + /// Add all properties and values into this row + void ConcatRow(const c4_RowRef&); + /// Return a new row which is the concatenation of two others + friend c4_Row operator+ (const c4_RowRef&, const c4_RowRef&); + +private: + static c4_Cursor Allocate(); + static void Release(c4_Cursor); +}; + +//--------------------------------------------------------------------------- + +class c4_Bytes +{ + union { + t4_byte _buffer [16]; + double _aligner; // on a Sparc, the int below wasn't enough... + }; + + t4_byte* _contents; + int _size; + bool _copy; + +public: + c4_Bytes (); + c4_Bytes (const void*, int); + c4_Bytes (const void*, int, bool); + c4_Bytes (const c4_Bytes&); + ~c4_Bytes (); + + c4_Bytes& operator= (const c4_Bytes&); + void Swap(c4_Bytes&); + + int Size() const; + const t4_byte* Contents() const; + + t4_byte* SetBuffer(int); + t4_byte* SetBufferClear(int); + + friend bool operator== (const c4_Bytes&, const c4_Bytes&); + friend bool operator!= (const c4_Bytes&, const c4_Bytes&); + +private: + void _MakeCopy(); + void _LoseCopy(); +}; + +//--------------------------------------------------------------------------- + +class c4_Storage : public c4_View +{ +public: + /// Construct streaming-only storage object + c4_Storage (); + /// Construct a storage using the specified strategy handler + c4_Storage (c4_Strategy&, bool =false, int =1); + /// Construct a storage object, keeping the current structure + c4_Storage (const char*, int); + /// Reconstruct a storage object from a suitable view + c4_Storage (const c4_View&); + /// Destructor, usually closes file, but does not commit by default + ~c4_Storage (); + + void SetStructure(const char*); + bool AutoCommit(bool =true); + c4_Strategy& Strategy() const; + const char* Description(const char* =0); + + bool SetAside(c4_Storage&); + c4_Storage* GetAside() const; + + bool Commit(bool =false); + bool Rollback(bool =false); + + c4_ViewRef View(const char*); + c4_View GetAs(const char*); + + bool LoadFrom(c4_Stream&); + void SaveTo(c4_Stream&); + + //DROPPED: c4_Storage (const char* filename_, const char* description_); + //DROPPED: c4_View Store(const char* name_, const c4_View& view_); + //DROPPED: c4_HandlerSeq& RootTable() const; + //DROPPED: c4_RowRef xContents() const; + +private: + void Initialize(c4_Strategy&, bool, int); +}; + +//--------------------------------------------------------------------------- + +class c4_Property +{ + short _id; + char _type; + +public: + /// Construct a new property with the give type and id + c4_Property (char, int); + /// Construct a new property with the give type and name + c4_Property (char, const char*); + ~c4_Property (); + + c4_Property (const c4_Property&); + void operator= (const c4_Property&); + + const char* Name() const; + char Type() const; + + int GetId() const; + + c4_Reference operator() (const c4_RowRef&) const; + + void Refs(int) const; + + c4_View operator, (const c4_Property&) const; + + static void CleanupInternalData(); +}; + + /// Integer properties. +class c4_IntProp : public c4_Property +{ +public: + /// Construct a new property + c4_IntProp (const char*); + /// Destructor + ~c4_IntProp (); + + /// Get or set an integer property in a row + c4_IntRef operator() (const c4_RowRef&) const; + /// Get an integer property in a row + t4_i32 Get(const c4_RowRef&) const; + /// Set an integer property in a row + void Set(const c4_RowRef&, t4_i32) const; + + /// Creates a row with one integer, shorthand for AsRow. + c4_Row operator[] (t4_i32) const; + /// Creates a row with one integer. + c4_Row AsRow(t4_i32) const; +}; + +#if !q4_TINY + + /// Long int properties. +class c4_LongProp : public c4_Property +{ +public: + /// Construct a new property + c4_LongProp (const char*); + /// Destructor + ~c4_LongProp (); + + /// Get or set a long int property in a row + c4_LongRef operator() (const c4_RowRef&) const; + /// Get a long int property in a row + t4_i64 Get(const c4_RowRef&) const; + /// Set a long int property in a row + void Set(const c4_RowRef&, t4_i64) const; + + /// Creates a row with one long int, shorthand for AsRow. + c4_Row operator[] (t4_i64) const; + /// Creates a row with one long int. + c4_Row AsRow(t4_i64) const; +}; + + /// Floating point properties. +class c4_FloatProp : public c4_Property +{ +public: + /// Construct a new property + c4_FloatProp (const char*); + /// Destructor + ~c4_FloatProp (); + + /// Get or set a floating point property in a row + c4_FloatRef operator() (const c4_RowRef&) const; + /// Get a floating point property in a row + double Get(const c4_RowRef&) const; + /// Set a floating point property in a row + void Set(const c4_RowRef&, double) const; + + /// Create a row with one floating point value, shorthand for AsRow + c4_Row operator[] (double) const; + /// Create a row with one floating point value + c4_Row AsRow(double) const; +}; + + /// Double precision properties. +class c4_DoubleProp : public c4_Property +{ +public: + /// Construct a new property. + c4_DoubleProp (const char*); + /// Destructor + ~c4_DoubleProp (); + + /// Get or set a double precision property in a row + c4_DoubleRef operator() (const c4_RowRef&) const; + /// Get a double precision property in a row + double Get(const c4_RowRef&) const; + /// Set a double precision property in a row + void Set(const c4_RowRef&, double) const; + + /// Create a row with one double precision value, shorthand for AsRow + c4_Row operator[] (double) const; + /// Create a row with one double precision value + c4_Row AsRow(double) const; +}; +#endif // !q4_TINY + + /// String properties. +class c4_StringProp : public c4_Property +{ +public: + /// Construct a new property + c4_StringProp (const char*); + /// Destructor + ~c4_StringProp (); + + /// Get or set a string property in a row + c4_StringRef operator() (const c4_RowRef&) const; + /// Get a string property in a row + const char* Get(const c4_RowRef&) const; + /// Set a string property in a row + void Set(const c4_RowRef&, const char*) const; + + /// Create a row with one string, shorthand for AsRow + c4_Row operator[] (const char*) const; + /// Create a row with one string + c4_Row AsRow(const char*) const; +}; + + /// Binary properties. +class c4_BytesProp : public c4_Property +{ +public: + /// Construct a new property + c4_BytesProp (const char*); + /// Destructor + ~c4_BytesProp (); + + /// Get or set a bytes property in a row + c4_BytesRef operator() (const c4_RowRef&) const; + /// Get a bytes property in a row + c4_Bytes Get(const c4_RowRef&) const; + /// Set a bytes property in a row + void Set(const c4_RowRef&, const c4_Bytes&) const; + + /// Create a row with one bytes object, shorthand for AsRow + c4_Row operator[] (const c4_Bytes&) const; + /// Create a row with one bytes object + c4_Row AsRow(const c4_Bytes&) const; +}; + + /// View properties. +class c4_ViewProp : public c4_Property +{ +public: + /// Construct a new property + c4_ViewProp (const char*); + /// Destructor + ~c4_ViewProp (); + + /// Get or set a view property in a row + c4_ViewRef operator() (const c4_RowRef&) const; + /// Get a view property in a row + c4_View Get(const c4_RowRef&) const; + /// Set a view property in a row + void Set(const c4_RowRef&, const c4_View&) const; + + /// Create a row with one view, shorthand for AsRow + c4_Row operator[] (const c4_View&) const; + /// Create a row with one view + c4_Row AsRow(const c4_View&) const; +}; + +//--------------------------------------------------------------------------- + +class c4_CustomViewer +{ +protected: + /// Constructor, must be overriden in derived class + c4_CustomViewer (); +public: + /// Destructor + virtual ~c4_CustomViewer (); + + /// Return the structure of this view (initialization, called once) + virtual c4_View GetTemplate() = 0; + /// Return the number of rows in this view + virtual int GetSize() = 0; + int Lookup(const c4_RowRef&, int&); + virtual int Lookup(c4_Cursor, int&); + /// Fetch one data item, return it as a generic data value + virtual bool GetItem(int, int, c4_Bytes&) = 0; + virtual bool SetItem(int, int, const c4_Bytes&); + bool InsertRows(int, const c4_RowRef&, int =1); + virtual bool InsertRows(int, c4_Cursor, int =1); + virtual bool RemoveRows(int, int =1); +}; + +//--------------------------------------------------------------------------- +/// A stream is a virtual helper class to serialize in binary form. + +class c4_Stream +{ +public: + virtual ~c4_Stream (); + + /// Fetch some bytes sequentially + virtual int Read(void*, int) = 0; + /// Store some bytes sequentially + virtual bool Write(const void*, int) = 0; +}; + +//--------------------------------------------------------------------------- +/// A strategy encapsulates code dealing with the I/O system interface. + +class c4_Strategy +{ +public: + c4_Strategy (); + virtual ~c4_Strategy (); + + virtual bool IsValid() const; + virtual int DataRead(t4_i32, void*, int); + virtual void DataWrite(t4_i32, const void*, int); + virtual void DataCommit(t4_i32); + virtual void ResetFileMapping(); + virtual t4_i32 FileSize(); + virtual t4_i32 FreshGeneration(); + + void SetBase(t4_i32); + t4_i32 EndOfData(t4_i32 =-1); + + /// True if the storage format is not native (default is false) + bool _bytesFlipped; + /// Error code of last failed I/O operation, zero if I/O was ok + int _failure; + /// First byte in file mapping, zero if not active + const t4_byte* _mapStart; + /// Number of bytes filled with active data + t4_i32 _dataSize; + /// All file positions are relative to this offset + t4_i32 _baseOffset; + /// The root position of the shallow tree walks + t4_i32 _rootPos; + /// The size of the root column + t4_i32 _rootLen; +}; + +//--------------------------------------------------------------------------- +/// A sequence is an abstract base class for views on ranges of records. +// +// Sequences represent arrays of rows (or indexed collections / tables). +// Insertion and removal of entries is allowed, but could take linear time. +// A reference count is maintained to decide when the object should go away. + +class c4_Sequence +{ + /// Reference count + int _refCount; + /// Pointer to dependency list, or null if nothing depends on this + c4_Dependencies* _dependencies; + +protected: + /// Optimization: cached property index + int _propertyLimit; + /// Optimization: property map for faster access + short* _propertyMap; // see c4_HandlerSeq::Reset() + /// allocated on first use by c4_Sequence::Buffer() + c4_Bytes* _tempBuf; + +public: +/* General */ + /// Abstract constructor + c4_Sequence (); + + virtual int Compare(int, c4_Cursor) const; + virtual bool RestrictSearch(c4_Cursor, int&, int&); + void SetAt(int, c4_Cursor); + virtual int RemapIndex(int, const c4_Sequence*) const; + +/* Reference counting */ + void IncRef(); + void DecRef(); + int NumRefs() const; + +/* Adding / removing rows */ + /// Return the current number of rows + virtual int NumRows() const = 0; + void Resize(int, int =-1); + + virtual void InsertAt(int, c4_Cursor, int =1); + virtual void RemoveAt(int, int =1); + virtual void Move(int, int); + +/* Properties */ + int NthPropId(int) const; + int PropIndex(int); + int PropIndex(const c4_Property&); + + /// Return the number of data handlers in this sequence + virtual int NumHandlers() const = 0; + /// Return a reference to the N-th handler in this sequence + virtual c4_Handler& NthHandler(int) const = 0; + /// Return the context of the N-th handler in this sequence + virtual const c4_Sequence* HandlerContext(int) const = 0; + /// Add the specified data handler to this sequence + virtual int AddHandler(c4_Handler*) = 0; + /// Create a handler of the appropriate type + virtual c4_Handler* CreateHandler(const c4_Property&) = 0; + + virtual const char* Description(); + +/* Element access */ + /// Return width of specified data item + virtual int ItemSize(int, int); + /// Retrieve one data item from this sequence + virtual bool Get(int, int, c4_Bytes&); + /// Store a data item into this sequence + virtual void Set(int, const c4_Property&, const c4_Bytes&); + +/* Dependency notification */ + void Attach(c4_Sequence*); + void Detach(c4_Sequence*); + /// Return a pointer to the dependencies, or null + c4_Dependencies* GetDependencies() const; + + virtual c4_Notifier* PreChange(c4_Notifier&); + virtual void PostChange(c4_Notifier&); + + const char* UseTempBuffer(const char*); + +protected: + virtual ~c4_Sequence (); + + void ClearCache(); + +public: //! for c4_Table::Sequence setup + virtual void SetNumRows(int) = 0; + virtual c4_Persist* Persist() const; + + c4_Bytes& Buffer(); + +private: + c4_Sequence (const c4_Sequence&); // not implemented + void operator= (const c4_Sequence&); // not implemented +}; + +//--------------------------------------------------------------------------- +/// A reference is used to get or set typed data, using derived classes. +// +// Objects of this class are only intended to be used as a temporary handle +// while getting and setting properties in a row. They are normally only +// constructed as result of function overload operators: "property (row)". + +class c4_Reference +{ +protected: + /// The cursor which points to the data + c4_Cursor _cursor; + /// The property associated to this reference + const c4_Property& _property; + +public: + /// Constructor + c4_Reference (const c4_RowRef&, const c4_Property&); + + /// Assignment of one data item + c4_Reference& operator= (const c4_Reference&); + + /// Return width of the referenced data item + int GetSize() const; + /// Retrieve the value of the referenced data item + bool GetData(c4_Bytes&) const; + /// Store a value into the referenced data item + void SetData(const c4_Bytes&) const; + + /// Return true if the contents of both references is equal + friend bool operator== (const c4_Reference&, const c4_Reference&); + /// Return true if the contents of both references is not equal + friend bool operator!= (const c4_Reference&, const c4_Reference&); + +private: + void operator& () const; // not implemented +}; + +//--------------------------------------------------------------------------- + + /// Used to get or set integer values. +class c4_IntRef : public c4_Reference +{ +public: + /// Constructor + c4_IntRef (const c4_Reference&); + /// Get the value as integer + operator t4_i32 () const; + /// Set the value to the specified integer + c4_IntRef& operator= (t4_i32); +}; + +#if !q4_TINY + + /// Used to get or set long int values. +class c4_LongRef : public c4_Reference +{ +public: + /// Constructor + c4_LongRef (const c4_Reference&); + /// Get the value as long int + operator t4_i64 () const; + /// Set the value to the specified long int + c4_LongRef& operator= (t4_i64); +}; + + /// Used to get or set floating point values. +class c4_FloatRef : public c4_Reference +{ +public: + /// Constructor + c4_FloatRef (const c4_Reference&); + /// Get the value as floating point + operator double () const; + /// Set the value to the specified floating point + c4_FloatRef& operator= (double); +}; + + /// Used to get or set double precision values. +class c4_DoubleRef : public c4_Reference +{ +public: + /// Constructor + c4_DoubleRef (const c4_Reference&); + /// Get the value as floating point + operator double () const; + /// Set the value to the specified floating point + c4_DoubleRef& operator= (double); +}; + +#endif // !q4_TINY + + /// Used to get or set binary object values. +class c4_BytesRef : public c4_Reference +{ +public: + /// Constructor + c4_BytesRef (const c4_Reference&); + /// Get the value as binary object + operator c4_Bytes () const; + /// Set the value to the specified binary object + c4_BytesRef& operator= (const c4_Bytes&); + + /// Fetch data from the memo field, up to end if length is zero + c4_Bytes Access(t4_i32, int =0) const; + /// Store data, resize by diff_ bytes, return true if successful + bool Modify(const c4_Bytes&, t4_i32, int =0) const; +}; + + /// Used to get or set string values. +class c4_StringRef : public c4_Reference +{ +public: + /// Constructor + c4_StringRef (const c4_Reference&); + /// Get the value as string + operator const char* () const; + /// Set the value to the specified string + c4_StringRef& operator= (const char*); +}; + + /// Used to get or set view values. +class c4_ViewRef : public c4_Reference +{ +public: + /// Constructor + c4_ViewRef (const c4_Reference&); + /// Get the value as view + operator c4_View () const; + /// Set the value to the specified view + c4_ViewRef& operator= (const c4_View&); +}; + +//--------------------------------------------------------------------------- +// Debug logging option, can generate log of changes for one/all properties + +#if q4_LOGPROPMODS +FILE* f4_LogPropMods(FILE* fp_, int propId_); +#else +#define f4_LogPropMods(a,b) 0 +#endif + +//--------------------------------------------------------------------------- + +#if q4_INLINE +#include "mk4.inl" +#endif + +//--------------------------------------------------------------------------- + +#endif // __MK4_H__ diff --git a/akregator/src/mk4storage/metakit/include/mk4.inl b/akregator/src/mk4storage/metakit/include/mk4.inl new file mode 100644 index 000000000..1c717a367 --- /dev/null +++ b/akregator/src/mk4storage/metakit/include/mk4.inl @@ -0,0 +1,874 @@ +// mk4.inl -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Public definitions which are usually inlined + */ + +///////////////////////////////////////////////////////////////////////////// +// Reordered inlines so they are always defined before their first use + +d4_inline c4_Cursor c4_RowRef::operator& () const +{ + return _cursor; +} + +/** Return a unique id for this property + * + * A property object in fact merely represents an entry in a globally + * maintained symbol table. Each property is assigned a unique id, + * which remains valid as long as some reference to that property + * exists. In general, property id's remain unique as long as the + * application runs. Do not store id's on file, since they are + * not guaranteed to remain the same across program invocations. + * All properties with the same name are given the same id. + */ +d4_inline int c4_Property::GetId() const +{ + return _id; +} + +////////////////////////////////////////////////////////////////////////////////// + +#if !q4_LONG64 && !defined (LONG_LONG) && !HAVE_LONG_LONG + +d4_inline bool operator== (const t4_i64 a_, const t4_i64 b_) +{ + return a_.l1 == b_.l1 && a_.l2 == b_.l2; +} + +d4_inline bool operator< (const t4_i64 a_, const t4_i64 b_) +{ + return a_.l2 < b_.l2 || a_.l2 == b_.l2 && a_.l2 < b_.l2; +} + +#endif + +////////////////////////////////////////////////////////////////////////////////// +// c4_View + +/// Returns the number of entries in this view. +d4_inline int c4_View::GetSize() const +{ + return _seq->NumRows(); +} + +/** Change the size of this view + * Since views act like dynamic arrays, you can quickly + * change their size. Increasing the size will append rows + * with zero/empty values, while decreasing it will delete + * the last rows. The growBy_ parameter is currently unused. + */ +d4_inline void c4_View::SetSize(int newSize_, int growBy_) +{ + _seq->Resize(newSize_, growBy_); +} + +/// Removes all entries (sets size to zero). +d4_inline void c4_View::RemoveAll() +{ + SetSize(0); +} + +/// Return a pointer to the persistence handler, or zero +d4_inline c4_Persist* c4_View::Persist() const +{ + return _seq->Persist(); +} + +/** + * Change the value of the specified entry. If the new value has + * other properties, these will be added to the underlying view. + * + * @param index_ the zero-based row index + * @param newElem_ the row to copy to this view + */ +d4_inline void c4_View::SetAt(int index_, const c4_RowRef& newElem_) +{ + _seq->SetAt(index_, &newElem_); +} + +/** + * Insert a copy of the contents of another view. This is identical to + * inserting the specified number of default entries and then setting + * each of them to the new element value passed as argument. + */ +d4_inline void c4_View::InsertAt( + int index_, ///< zero-based row index + const c4_RowRef& newElem_, ///< the value to insert + int count_ ///< number of copies to insert, must be > 0 + ) +{ + _seq->InsertAt(index_, &newElem_, count_); +} + +/** + * Remove entries starting at the given index. Entries which have + * other view references may cause these views to be deleted if their + * reference counts drop to zero because of this removal. + * + * @param index_ the zero-based row index + * @param count_ the number of entries to remove + */ +d4_inline void c4_View::RemoveAt(int index_, int count_) +{ + _seq->RemoveAt(index_, count_); +} + +/** Return the number of properties present in this view. + * @return A non-negative integer + */ +d4_inline int c4_View::NumProperties() const +{ + return _seq->NumHandlers(); +} + +/** Find the index of a property, given its id + * @param propId_ Unique id associated to a specific propoerty + * @return The index of the property, or -1 of it was not found + */ +d4_inline int c4_View::FindProperty(int propId_) +{ + return _seq->PropIndex(propId_); +} + + /// Return a decription if there is a fixed structure, else zero +d4_inline const char* c4_View::Description() const +{ + return _seq->Description(); +} + + /// Increase the reference count of the associated sequence +d4_inline void c4_View::_IncSeqRef() +{ + _seq->IncRef(); +} + + /// Decrease the reference count of the associated sequence +d4_inline void c4_View::_DecSeqRef() +{ + _seq->DecRef(); +} + +/// Destructor, decrements reference count +d4_inline c4_View::~c4_View () +{ + _DecSeqRef(); +} + + /// Return true if the contents of both views are equal +d4_inline bool operator== (const c4_View& a_, const c4_View& b_) +{ + return a_.GetSize() == b_.GetSize() && a_.Compare(b_) == 0; +} + + /// Return true if the contents of both views are not equal +d4_inline bool operator!= (const c4_View& a_, const c4_View& b_) +{ + return !(a_ == b_); +} + + /// True if first view is less than second view +d4_inline bool operator< (const c4_View& a_, const c4_View& b_) +{ + return a_.Compare(b_) < 0; +} + + /// True if first view is greater than second view +d4_inline bool operator> (const c4_View& a_, const c4_View& b_) +{ + return b_ < a_; +} + + /// True if first view is less or equal to second view +d4_inline bool operator<= (const c4_View& a_, const c4_View& b_) +{ + return !(b_ < a_); +} + + /// True if first view is greater or equal to second view +d4_inline bool operator>= (const c4_View& a_, const c4_View& b_) +{ + return !(a_ < b_); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_Cursor + +/** Constructs a new cursor. + * + * Cursor cannot be created without an underlying view, but you could + * define a global "nullView" object and then initialize the cursor with + * "&nullView[0]". This works because cursors need not point to a valid row. + */ +d4_inline c4_Cursor::c4_Cursor (c4_Sequence& seq_, int index_) + : _seq (&seq_), _index (index_) +{ +} + +/// Pre-increments the cursor. +d4_inline c4_Cursor& c4_Cursor::operator++ () +{ + ++_index; + return *this; +} + +/// Post-increments the cursor. +d4_inline c4_Cursor c4_Cursor::operator++ (int) +{ + return c4_Cursor (*_seq, _index++); +} + +/// Pre-decrements the cursor. +d4_inline c4_Cursor& c4_Cursor::operator-- () +{ + --_index; + return *this; +} + +/// Post-decrements the cursor. +d4_inline c4_Cursor c4_Cursor::operator-- (int) +{ + return c4_Cursor (*_seq, _index--); +} + +/// Advances by a given offset. +d4_inline c4_Cursor& c4_Cursor::operator+= (int offset_) +{ + _index += offset_; + return *this; +} + +/// Backs up by a given offset. +d4_inline c4_Cursor& c4_Cursor::operator-= (int offset_) +{ + _index -= offset_; + return *this; +} + +/// Subtracts a specified offset. +d4_inline c4_Cursor c4_Cursor::operator- (int offset_) const +{ + return c4_Cursor (*_seq, _index - offset_); +} + +/// Returns the distance between two cursors. +d4_inline int c4_Cursor::operator- (c4_Cursor cursor_) const +{ + return _index - cursor_._index; +} + +/// Add a specified offset. +d4_inline c4_Cursor operator+ (c4_Cursor cursor_, int offset_) +{ + return c4_Cursor (*cursor_._seq, cursor_._index + offset_); +} + +/// Adds specified offset to cursor. +d4_inline c4_Cursor operator+ (int offset_, c4_Cursor cursor_) +{ + return cursor_ + offset_; +} + +d4_inline bool operator== (c4_Cursor a_, c4_Cursor b_) +{ + return a_._seq == b_._seq && a_._index == b_._index; +} + +d4_inline bool operator!= (c4_Cursor a_, c4_Cursor b_) +{ + return !(a_ == b_); +} + +d4_inline bool operator< (c4_Cursor a_, c4_Cursor b_) +{ + return a_._seq < b_._seq || + a_._seq == b_._seq && a_._index < b_._index; +} + +d4_inline bool operator> (c4_Cursor a_, c4_Cursor b_) +{ + return b_ < a_; +} + +d4_inline bool operator<= (c4_Cursor a_, c4_Cursor b_) +{ + return !(b_ < a_); +} + +d4_inline bool operator>= (c4_Cursor a_, c4_Cursor b_) +{ + return !(a_ < b_); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_RowRef + +d4_inline c4_RowRef::c4_RowRef (c4_Cursor cursor_) + : _cursor (cursor_) +{ +} + +d4_inline c4_RowRef c4_RowRef::operator= (const c4_RowRef& rowRef_) +{ + if (_cursor != rowRef_._cursor) + _cursor._seq->SetAt(_cursor._index, &rowRef_); + + return *this; +} + +d4_inline c4_View c4_RowRef::Container() const +{ + return _cursor._seq; +} + +d4_inline bool operator== (const c4_RowRef& a_, const c4_RowRef& b_) +{ + return (&a_)._seq->Compare((&a_)._index, &b_) == 0; +} + +d4_inline bool operator!= (const c4_RowRef& a_, const c4_RowRef& b_) +{ + return !(a_ == b_); +} + +d4_inline bool operator< (const c4_RowRef& a_, const c4_RowRef& b_) +{ + // 25-5-1998: don't exchange a and b, this comparison is -not- symmetric + return (&a_)._seq->Compare((&a_)._index, &b_) < 0; +} + +d4_inline bool operator> (const c4_RowRef& a_, const c4_RowRef& b_) +{ + // 25-5-1998: don't exchange a and b, this comparison is -not- symmetric + return (&a_)._seq->Compare((&a_)._index, &b_) > 0; +} + +d4_inline bool operator<= (const c4_RowRef& a_, const c4_RowRef& b_) +{ + return !(a_ > b_); +} + +d4_inline bool operator>= (const c4_RowRef& a_, const c4_RowRef& b_) +{ + return !(a_ < b_); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_Bytes + + /// Construct an empty binary object +d4_inline c4_Bytes::c4_Bytes () + : _size (0), _copy (false) +{ + _contents = 0; // moved out of intializers for DEC CXX 5.7 +} + + /// Construct an object with contents, no copy +d4_inline c4_Bytes::c4_Bytes (const void* buf_, int len_) + : _size (len_), _copy (false) +{ + _contents = (t4_byte*) buf_; // moved out of intializers for DEC CXX 5.7 +} + +/// Returns a pointer to the contents. +d4_inline const t4_byte* c4_Bytes::Contents() const +{ + return _contents; +} + +/// Returns the number of bytes of its contents. +d4_inline int c4_Bytes::Size() const +{ + return _size; +} + +d4_inline void c4_Bytes::_LoseCopy() +{ + if (_copy) + delete [] (char*) _contents; +} + +/// Returns true if the contents of both objects is not equal. +d4_inline bool operator!= (const c4_Bytes& a_, const c4_Bytes& b_) +{ + return !(a_ == b_); +} + +/// Destructor, if a copy was made, it will be released here. +d4_inline c4_Bytes::~c4_Bytes () +{ + _LoseCopy(); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_Reference + +d4_inline c4_Reference::c4_Reference (const c4_RowRef& rowRef_, + const c4_Property& prop_) + : _cursor (&rowRef_), _property (prop_) +{ +} + +d4_inline int c4_Reference::GetSize() const +{ + return _cursor._seq->ItemSize(_cursor._index, _property.GetId()); +} + +d4_inline bool c4_Reference::GetData(c4_Bytes& buf_) const +{ + return _cursor._seq->Get(_cursor._index, _property.GetId(), buf_); +} + +d4_inline void c4_Reference::SetData(const c4_Bytes& buf_) const +{ + _cursor._seq->Set(_cursor._index, _property, buf_); +} + +d4_inline bool operator!= (const c4_Reference& a_, const c4_Reference& b_) +{ + return !(a_ == b_); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_IntRef + +d4_inline c4_IntRef::c4_IntRef (const c4_Reference& value_) + : c4_Reference (value_) +{ +} + +///////////////////////////////////////////////////////////////////////////// +#if !q4_TINY +///////////////////////////////////////////////////////////////////////////// +// c4_LongRef + +d4_inline c4_LongRef::c4_LongRef (const c4_Reference& value_) + : c4_Reference (value_) +{ +} + +///////////////////////////////////////////////////////////////////////////// +// c4_FloatRef + +d4_inline c4_FloatRef::c4_FloatRef (const c4_Reference& value_) + : c4_Reference (value_) +{ +} + +///////////////////////////////////////////////////////////////////////////// +// c4_DoubleRef + +d4_inline c4_DoubleRef::c4_DoubleRef (const c4_Reference& value_) + : c4_Reference (value_) +{ +} + +///////////////////////////////////////////////////////////////////////////// +#endif // !q4_TINY +///////////////////////////////////////////////////////////////////////////// +// c4_BytesRef + +d4_inline c4_BytesRef::c4_BytesRef (const c4_Reference& value_) + : c4_Reference (value_) +{ +} + +///////////////////////////////////////////////////////////////////////////// +// c4_StringRef + +d4_inline c4_StringRef::c4_StringRef (const c4_Reference& value_) + : c4_Reference (value_) +{ +} + +///////////////////////////////////////////////////////////////////////////// +// c4_ViewRef + +d4_inline c4_ViewRef::c4_ViewRef (const c4_Reference& value_) + : c4_Reference (value_) +{ +} + +///////////////////////////////////////////////////////////////////////////// +// c4_Property + +d4_inline c4_Property::c4_Property (char type_, int id_) + : _id ((short) id_), _type (type_) +{ +} + + /// Get or set this untyped property in a row +d4_inline c4_Reference c4_Property::operator() (const c4_RowRef& rowRef_) const +{ + return c4_Reference (rowRef_, *this); +} + + /// Return a view like the first, with a property appended to it +d4_inline c4_View c4_Property::operator, (const c4_Property& prop_) const +{ + return c4_View (*this), prop_; +} + + /// Return the type of this property +d4_inline char c4_Property::Type() const +{ + return _type; +} + +///////////////////////////////////////////////////////////////////////////// +// c4_IntProp + +d4_inline c4_IntProp::c4_IntProp (const char* name_) + : c4_Property ('I', name_) +{ +} + +d4_inline c4_IntProp::~c4_IntProp () +{ +} + +d4_inline c4_IntRef c4_IntProp::operator() (const c4_RowRef& rowRef_) const +{ + return c4_Reference (rowRef_, *this); +} + +d4_inline t4_i32 c4_IntProp::Get(const c4_RowRef& rowRef_) const +{ + return operator() (rowRef_); +} + +d4_inline void c4_IntProp::Set(const c4_RowRef& rowRef_, t4_i32 value_) const +{ + operator() (rowRef_) = value_; +} + +d4_inline c4_Row c4_IntProp::AsRow(t4_i32 value_) const +{ + c4_Row row; + operator() (row) = value_; + return row; +} + +d4_inline c4_Row c4_IntProp::operator[] (t4_i32 value_) const +{ + return AsRow(value_); +} + +///////////////////////////////////////////////////////////////////////////// +#if !q4_TINY +///////////////////////////////////////////////////////////////////////////// +// c4_LongProp + +d4_inline c4_LongProp::c4_LongProp (const char* name_) + : c4_Property ('L', name_) +{ +} + +d4_inline c4_LongProp::~c4_LongProp () +{ +} + +d4_inline c4_LongRef c4_LongProp::operator() (const c4_RowRef& rowRef_) const +{ + return c4_Reference (rowRef_, *this); +} + +d4_inline t4_i64 c4_LongProp::Get(const c4_RowRef& rowRef_) const +{ + return operator() (rowRef_); +} + +d4_inline void c4_LongProp::Set(const c4_RowRef& rowRef_, t4_i64 value_) const +{ + operator() (rowRef_) = value_; +} + +d4_inline c4_Row c4_LongProp::AsRow(t4_i64 value_) const +{ + c4_Row row; + operator() (row) = value_; + return row; +} + +d4_inline c4_Row c4_LongProp::operator[] (t4_i64 value_) const +{ + return AsRow(value_); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_FloatProp + +d4_inline c4_FloatProp::c4_FloatProp (const char* name_) + : c4_Property ('F', name_) +{ +} + +d4_inline c4_FloatProp::~c4_FloatProp () +{ +} + +d4_inline c4_FloatRef c4_FloatProp::operator() (const c4_RowRef& rowRef_) const +{ + return c4_Reference (rowRef_, *this); +} + +d4_inline double c4_FloatProp::Get(const c4_RowRef& rowRef_) const +{ + return operator() (rowRef_); +} + +d4_inline void c4_FloatProp::Set(const c4_RowRef& rowRef_, double value_) const +{ + operator() (rowRef_) = value_; +} + +d4_inline c4_Row c4_FloatProp::AsRow(double value_) const +{ + c4_Row row; + operator() (row) = value_; + return row; +} + +d4_inline c4_Row c4_FloatProp::operator[] (double value_) const +{ + return AsRow(value_); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_DoubleProp + +d4_inline c4_DoubleProp::c4_DoubleProp (const char* name_) + : c4_Property ('D', name_) +{ +} + +d4_inline c4_DoubleProp::~c4_DoubleProp () +{ +} + +d4_inline c4_DoubleRef c4_DoubleProp::operator() (const c4_RowRef& rowRef_) const +{ + return c4_Reference (rowRef_, *this); +} + +d4_inline double c4_DoubleProp::Get(const c4_RowRef& rowRef_) const +{ + return operator() (rowRef_); +} + +d4_inline void c4_DoubleProp::Set(const c4_RowRef& rowRef_, double value_) const +{ + operator() (rowRef_) = value_; +} + +d4_inline c4_Row c4_DoubleProp::AsRow(double value_) const +{ + c4_Row row; + operator() (row) = value_; + return row; +} + +d4_inline c4_Row c4_DoubleProp::operator[] (double value_) const +{ + return AsRow(value_); +} + +///////////////////////////////////////////////////////////////////////////// +#endif // !q4_TINY +///////////////////////////////////////////////////////////////////////////// +// c4_BytesProp + +d4_inline c4_BytesProp::c4_BytesProp (const char* name_) + : c4_Property ('B', name_) +{ +} + +d4_inline c4_BytesProp::~c4_BytesProp () +{ +} + +d4_inline c4_BytesRef c4_BytesProp::operator() (const c4_RowRef& rowRef_) const +{ + return c4_Reference (rowRef_, *this); +} + +d4_inline c4_Bytes c4_BytesProp::Get(const c4_RowRef& rowRef_) const +{ + return operator() (rowRef_); +} + +d4_inline void c4_BytesProp::Set(const c4_RowRef& rowRef_, const c4_Bytes& value_) const +{ + operator() (rowRef_) = value_; +} + +d4_inline c4_Row c4_BytesProp::AsRow(const c4_Bytes& value_) const +{ + c4_Row row; + operator() (row) = value_; + return row; +} + +d4_inline c4_Row c4_BytesProp::operator[] (const c4_Bytes& value_) const +{ + return AsRow(value_); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_StringProp + +d4_inline c4_StringProp::c4_StringProp (const char* name_) + : c4_Property ('S', name_) +{ +} + +d4_inline c4_StringProp::~c4_StringProp () +{ +} + +d4_inline c4_StringRef c4_StringProp::operator() (const c4_RowRef& rowRef_) const +{ + return c4_Reference (rowRef_, *this); +} + +d4_inline const char* c4_StringProp::Get(const c4_RowRef& rowRef_) const +{ + return operator() (rowRef_); +} + +d4_inline void c4_StringProp::Set(const c4_RowRef& rowRef_, const char* value_) const +{ + operator() (rowRef_) = value_; +} + +d4_inline c4_Row c4_StringProp::AsRow(const char* value_) const +{ + c4_Row row; + operator() (row) = value_; + return row; +} + +d4_inline c4_Row c4_StringProp::operator[] (const char* value_) const +{ + return AsRow(value_); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_ViewProp + +d4_inline c4_ViewProp::c4_ViewProp (const char* name_) + : c4_Property ('V', name_) +{ +} + +d4_inline c4_ViewProp::~c4_ViewProp () +{ +} + +d4_inline c4_ViewRef c4_ViewProp::operator() (const c4_RowRef& rowRef_) const +{ + return c4_Reference (rowRef_, *this); +} + +d4_inline c4_View c4_ViewProp::Get(const c4_RowRef& rowRef_) const +{ + return operator() (rowRef_); +} + +d4_inline void c4_ViewProp::Set(const c4_RowRef& rowRef_, const c4_View& value_) const +{ + operator() (rowRef_) = value_; +} + +d4_inline c4_Row c4_ViewProp::AsRow(const c4_View& value_) const +{ + c4_Row row; + operator() (row) = value_; + return row; +} + +d4_inline c4_Row c4_ViewProp::operator[] (const c4_View& value_) const +{ + return AsRow(value_); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_Strategy + + /// True if we can do I/O with this object +d4_inline bool c4_Strategy::IsValid() const +{ + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// c4_CustomViewer + +d4_inline c4_CustomViewer::c4_CustomViewer() +{ +} + +d4_inline int c4_CustomViewer::Lookup(const c4_RowRef& r_, int& n_) +{ + return Lookup(&r_, n_); // c4_Cursor +} + +d4_inline bool c4_CustomViewer::InsertRows(int p_, const c4_RowRef& r_, int n_) +{ + return InsertRows(p_, &r_, n_); // c4_Cursor +} + +///////////////////////////////////////////////////////////////////////////// +// c4_Sequence + +d4_inline c4_Dependencies* c4_Sequence::GetDependencies() const +{ + return _dependencies; +} + +///////////////////////////////////////////////////////////////////////////// +// Reordered inlines so they are always used after their definition + +/// Dereferences this cursor to "almost" a row. +d4_inline c4_RowRef c4_Cursor::operator* () const +{ + return *(c4_Cursor*) this; // cast avoids a const problem with BCPP 4.52 +} + +/// This is the same as *(cursor + offset). +d4_inline c4_RowRef c4_Cursor::operator[] (int offset_) const +{ + return *(*this + offset_); +} + +/// Returns a reference to specified entry, for use as RHS or LHS +d4_inline c4_RowRef c4_View::GetAt(int index_) const +{ + return * c4_Cursor (*_seq, index_); +} + +/** Element access, shorthand for GetAt + * @return A reference to the specified row in the view. + * This reference can be used on either side of the assignment operator. + */ +d4_inline c4_RowRef c4_View::operator[] ( + int index_ ///< zero-based row index + ) const +{ + return GetAt(index_); +} + +/** Element access, shorthand for GetAt + * @return A reference to the specified row in the view. + * This reference can be used on either side of the assignment operator. + */ +d4_inline c4_RowRef c4_View::ElementAt( + int index_ ///< zero-based row index + ) +{ + return GetAt(index_); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/include/mk4dll.h b/akregator/src/mk4storage/metakit/include/mk4dll.h new file mode 100644 index 000000000..979971fac --- /dev/null +++ b/akregator/src/mk4storage/metakit/include/mk4dll.h @@ -0,0 +1,112 @@ +// mk4dll.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ +// +// Import declarations for DLLs + +#ifndef __MK4_H__ +#error This file is included by "mk4.h", it cannot be used standalone +#endif + +#ifndef d4_DLL +#ifdef _WIN32 +#ifdef _USRDLL +#define d4_DLL __declspec(dllexport) +#else +#define d4_DLL __declspec(dllimport) +#endif +#else +#define d4_DLL +#endif +#endif + +#ifndef d4_DLLSPEC +#ifdef _MSC_VER +#define d4_DLLSPEC(t) d4_DLL t +#else +#define d4_DLLSPEC(t) t d4_DLL +#endif +#endif + +///////////////////////////////////////////////////////////////////////////// +// Declarations in this file + + class d4_DLL c4_Bytes; + class d4_DLL c4_BytesProp; + class d4_DLL c4_BytesRef; + class d4_DLL c4_Cursor; + class d4_DLL c4_CustomViewer; + class d4_DLL c4_DoubleProp; + class d4_DLL c4_DoubleRef; + class d4_DLL c4_FileStrategy; + class d4_DLL c4_FileStream; + class d4_DLL c4_FloatProp; + class d4_DLL c4_FloatRef; + class d4_DLL c4_IntProp; + class d4_DLL c4_IntRef; + class d4_DLL c4_LongRef; + class d4_DLL c4_Property; + class d4_DLL c4_Reference; + class d4_DLL c4_Row; + class d4_DLL c4_RowRef; + class d4_DLL c4_Sequence; + class d4_DLL c4_Storage; + class d4_DLL c4_Strategy; + class d4_DLL c4_Stream; + class d4_DLL c4_StringProp; + class d4_DLL c4_StringRef; + class d4_DLL c4_View; + class d4_DLL c4_ViewProp; + class d4_DLL c4_ViewRef; + +#if !q4_MFC + class d4_DLL c4_String; +#endif + +///////////////////////////////////////////////////////////////////////////// + + d4_DLLSPEC(bool) operator== (const c4_View& a_, const c4_View& b_); + d4_DLLSPEC(bool) operator!= (const c4_View& a_, const c4_View& b_); + d4_DLLSPEC(bool) operator< (const c4_View& a_, const c4_View& b_); + d4_DLLSPEC(bool) operator> (const c4_View& a_, const c4_View& b_); + d4_DLLSPEC(bool) operator<= (const c4_View& a_, const c4_View& b_); + d4_DLLSPEC(bool) operator>= (const c4_View& a_, const c4_View& b_); + + d4_DLLSPEC(bool) operator== (c4_Cursor a_, c4_Cursor b_); + d4_DLLSPEC(bool) operator!= (c4_Cursor a_, c4_Cursor b_); + d4_DLLSPEC(bool) operator< (c4_Cursor a_, c4_Cursor b_); + d4_DLLSPEC(bool) operator> (c4_Cursor a_, c4_Cursor b_); + d4_DLLSPEC(bool) operator<= (c4_Cursor a_, c4_Cursor b_); + d4_DLLSPEC(bool) operator>= (c4_Cursor a_, c4_Cursor b_); + d4_DLLSPEC(c4_Cursor) operator+ (c4_Cursor cursor_, int offset_); + d4_DLLSPEC(c4_Cursor) operator+ (int offset_, c4_Cursor cursor_); + + d4_DLLSPEC(bool) operator== (const c4_RowRef& a_, const c4_RowRef& b_); + d4_DLLSPEC(bool) operator!= (const c4_RowRef& a_, const c4_RowRef& b_); + d4_DLLSPEC(bool) operator< (const c4_RowRef& a_, const c4_RowRef& b_); + d4_DLLSPEC(bool) operator> (const c4_RowRef& a_, const c4_RowRef& b_); + d4_DLLSPEC(bool) operator<= (const c4_RowRef& a_, const c4_RowRef& b_); + d4_DLLSPEC(bool) operator>= (const c4_RowRef& a_, const c4_RowRef& b_); + d4_DLLSPEC(c4_Row) operator+ (const c4_RowRef& a_, const c4_RowRef& b_); + + d4_DLLSPEC(bool) operator== (const c4_Bytes& a_, const c4_Bytes& b_); + d4_DLLSPEC(bool) operator!= (const c4_Bytes& a_, const c4_Bytes& b_); + + d4_DLLSPEC(bool) operator== (const c4_Reference&, const c4_Reference&); + d4_DLLSPEC(bool) operator!= (const c4_Reference&, const c4_Reference&); + +#if !q4_MFC + d4_DLLSPEC(c4_String) operator+ (const c4_String&, const c4_String&); + d4_DLLSPEC(c4_String) operator+ (const c4_String&, const char*); + d4_DLLSPEC(c4_String) operator+ (const char*, const c4_String&); + + d4_DLLSPEC(bool) operator== (const c4_String&, const c4_String&); + d4_DLLSPEC(bool) operator!= (const c4_String&, const c4_String&); + d4_DLLSPEC(bool) operator== (const c4_String& s1, const char* s2); + d4_DLLSPEC(bool) operator== (const char* s1, const c4_String& s2); + d4_DLLSPEC(bool) operator!= (const c4_String& s1, const char* s2); + d4_DLLSPEC(bool) operator!= (const char* s1, const c4_String& s2); +#endif + +///////////////////////////////////////////////////////////////////////////// + diff --git a/akregator/src/mk4storage/metakit/include/mk4io.h b/akregator/src/mk4storage/metakit/include/mk4io.h new file mode 100644 index 000000000..d70db9c52 --- /dev/null +++ b/akregator/src/mk4storage/metakit/include/mk4io.h @@ -0,0 +1,66 @@ +// mk4io.h -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Declaration of the file stream and strategy classes. + */ + +#ifndef __MK4IO_H__ +#define __MK4IO_H__ + +#include <stdio.h> + +///////////////////////////////////////////////////////////////////////////// +/// A file stream can be used to serialize using the stdio library. + +class c4_FileStream : public c4_Stream +{ +public: + c4_FileStream (FILE* stream_, bool owned_ =false); + virtual ~c4_FileStream (); + + virtual int Read(void* buffer_, int length_); + virtual bool Write(const void* buffer_, int length_); + + FILE* _stream; + bool _owned; +}; + +///////////////////////////////////////////////////////////////////////////// +/// A file strategy encapsulates code dealing with all file I/O. + +class c4_FileStrategy : public c4_Strategy +{ +public: + /// Construct a new strategy object + c4_FileStrategy (FILE* file_ =0); + virtual ~c4_FileStrategy (); + + /// True if we can do I/O with this object + virtual bool IsValid() const; + /// Open a data file by name + virtual bool DataOpen(const char* fileName_, int mode_); + /// Read a number of bytes + virtual int DataRead(t4_i32 pos_, void* buffer_, int length_); + /// Write a number of bytes, return true if successful + virtual void DataWrite(t4_i32 pos_, const void* buffer_, int length_); + /// Flush and truncate file + virtual void DataCommit(t4_i32 newSize_); + /// Support for memory-mapped files + virtual void ResetFileMapping(); + /// Report total size of the datafile + virtual t4_i32 FileSize(); + /// Return a good value to use as fresh generation counter + virtual t4_i32 FreshGeneration(); + +protected: + /// Pointer to file object + FILE* _file; + /// Pointer to same file object, if it must be deleted at end + FILE* _cleanup; +}; + +///////////////////////////////////////////////////////////////////////////// + +#endif // __MK4IO_H__ diff --git a/akregator/src/mk4storage/metakit/include/mk4str.h b/akregator/src/mk4storage/metakit/include/mk4str.h new file mode 100644 index 000000000..7124a29ea --- /dev/null +++ b/akregator/src/mk4storage/metakit/include/mk4str.h @@ -0,0 +1,181 @@ +// mk4str.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Declarations of the string package. + */ + +#ifndef __MK4STR_H__ +#define __MK4STR_H__ + +///////////////////////////////////////////////////////////////////////////// + +#if q4_MFC // Microsoft Foundation Classes + +#ifdef _WINDOWS +#include <afxwin.h> +#else +#include <afxcoll.h> +#endif + +#if _MSC_VER == 800 +// MSVC 1.52 thinks a typedef has no constructor, use define instead +#define c4_String CString +#elif _MSC_VER >= 1300 +// VC 7.0 does not like "class" (6-2-2002, Zhang Dehua) +typedef CString c4_String; +#else +typedef class CString c4_String; +#endif + +#elif q4_STD // STL and standard strings + +#include <string> + +#if !defined (d4_std) // the default is to use namespaces +#define d4_std std +#endif + + /// STL-based string class, modeled after the MFC version +class c4_String : public d4_std::string +{ + typedef d4_std::string string; + +public: + c4_String (); + c4_String (char ch, int nDup =1); + c4_String (const char* str); + c4_String (const void* ptr, int len); + c4_String (const d4_std::string& s); + c4_String (const c4_String& s); + ~c4_String (); + + const c4_String& operator= (const c4_String&); + + operator const char* () const; + + char operator[] (int i) const; + + friend c4_String operator+ (const c4_String&, const c4_String&); + friend c4_String operator+ (const c4_String&, const char*); + friend c4_String operator+ (const char*, const c4_String&); + + const c4_String& operator+= (const c4_String& s); + const c4_String& operator+= (const char* s); + + int GetLength() const; + bool IsEmpty() const; + void Empty(); + + c4_String Mid(int nFirst, int nCount =25000) const; + c4_String Left(int nCount) const; + c4_String Right(int nCount) const; + + int Compare(const char* str) const; + int CompareNoCase(const char* str) const; + + bool operator< (const c4_String& str) const; + + int Find(char ch) const; + int ReverseFind(char ch) const; + int FindOneOf(const char* set) const; + + int Find(const char* sub) const; + + c4_String SpanIncluding(const char* set) const; + c4_String SpanExcluding(const char* set) const; +}; + +bool operator== (const c4_String&, const c4_String&); +bool operator!= (const c4_String&, const c4_String&); + +d4_inline bool operator== (const c4_String& s1, const char* s2); +d4_inline bool operator== (const char* s1, const c4_String& s2); + +d4_inline bool operator!= (const c4_String& s1, const char* s2); +d4_inline bool operator!= (const char* s1, const c4_String& s2); + +#else // Universal replacement classes + + /// An efficient string class, modeled after the MFC version +class c4_String +{ +public: + c4_String (); + c4_String (char ch, int nDup =1); + c4_String (const char* str); + c4_String (const unsigned char* str); + c4_String (const void* ptr, int len); + c4_String (const c4_String& s); + ~c4_String (); + + const c4_String& operator= (const c4_String&); + + operator const char* () const; + operator const unsigned char* () const; + + char operator[] (int i) const; + + friend c4_String operator+ (const c4_String&, const c4_String&); + friend c4_String operator+ (const c4_String&, const char*); + friend c4_String operator+ (const char*, const c4_String&); +// friend c4_String operator+ (const c4_String&, char); +// friend c4_String operator+ (char, const c4_String&); + + const c4_String& operator+= (const c4_String& s); + const c4_String& operator+= (const char* s); +// const c4_String& operator+= (char c); + + int GetLength() const; + bool IsEmpty() const; + void Empty(); // free up the data + + c4_String Mid(int nFirst, int nCount =25000) const; + c4_String Left(int nCount) const; // first nCount chars + c4_String Right(int nCount) const; // last nCount chars + + friend bool operator== (const c4_String&, const c4_String&); // memcmp + friend bool operator!= (const c4_String&, const c4_String&); // opposite + + // only defined for strings having no zero bytes inside them: + + int Compare(const char* str) const; // strcmp + int CompareNoCase(const char* str) const; // stricmp + + bool operator< (const c4_String& str) const; + + int Find(char ch) const; // strchr + int ReverseFind(char ch) const; // strrchr + int FindOneOf(const char* set) const; // strpbrk + + int Find(const char* sub) const; // strstr + + c4_String SpanIncluding(const char* set) const; // strspn + c4_String SpanExcluding(const char* set) const; // strcspn + +private: + void Init(const void* p, int n); + const char* Data() const; + int FullLength() const; + + unsigned char* _value; +}; + +bool operator== (const c4_String& s1, const char* s2); +bool operator== (const char* s1, const c4_String& s2); + +bool operator!= (const c4_String& s1, const char* s2); +bool operator!= (const char* s1, const c4_String& s2); + +#endif // q4_MFC elif q4_STD else q4_UNIV + +///////////////////////////////////////////////////////////////////////////// + +#if q4_INLINE +#include "mk4str.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + +#endif // __MK4STR_H__ diff --git a/akregator/src/mk4storage/metakit/include/mk4str.inl b/akregator/src/mk4storage/metakit/include/mk4str.inl new file mode 100644 index 000000000..4f95fa514 --- /dev/null +++ b/akregator/src/mk4storage/metakit/include/mk4str.inl @@ -0,0 +1,299 @@ +// mk4str.inl -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Members of the string package which are usually inlined + */ + +///////////////////////////////////////////////////////////////////////////// +// c4_String + +#if q4_MFC // Microsoft Foundation Classes + +#elif q4_STD // STL and standard strings + +/// Construct empty string +d4_inline c4_String::c4_String () +{ +} + +d4_inline c4_String::c4_String (char ch_, int nDup_) + : string (nDup_, ch_) +{ +} + +d4_inline c4_String::c4_String (const char* str_) + : string (str_) +{ +} + +d4_inline c4_String::c4_String (const void* ptr_, int len_) + : string ((const char*) ptr_, len_) +{ +} + +d4_inline c4_String::c4_String (const d4_std::string& s_) + : string (s_) +{ +} + +d4_inline c4_String::c4_String (const c4_String& s_) + : string (s_) +{ +} + +d4_inline c4_String::~c4_String () +{ +} + +d4_inline const c4_String& c4_String::operator= (const c4_String& s_) +{ + *(string*) this = s_; + return *this; +} + +d4_inline c4_String::operator const char* () const +{ + return c_str(); +} + +d4_inline char c4_String::operator[] (int i) const +{ + return at(i); +} + +d4_inline c4_String operator+ (const c4_String& a_, const c4_String& b_) +{ + return (d4_std::string) a_ + (d4_std::string) b_; +} + +d4_inline c4_String operator+ (const c4_String& a_, const char* b_) +{ + return (d4_std::string) a_ + (d4_std::string) b_; +} + +d4_inline c4_String operator+ (const char* a_, const c4_String& b_) +{ + return (d4_std::string) a_ + (d4_std::string) b_; +} + +d4_inline const c4_String& c4_String::operator+= (const c4_String& s_) +{ + *(string*) this += s_; + return *this; +} + +d4_inline const c4_String& c4_String::operator+= (const char* s_) +{ + *(string*) this += s_; + return *this; +} + +d4_inline int c4_String::GetLength() const +{ + return length(); +} + +d4_inline bool c4_String::IsEmpty() const +{ + return empty(); +} + +d4_inline void c4_String::Empty() +{ + erase(); +} + +d4_inline c4_String c4_String::Left(int nCount_) const +{ + if (nCount_ > length()) + nCount_ = length(); + return substr(0, nCount_); +} + +d4_inline c4_String c4_String::Right(int nCount_) const +{ + if (nCount_ > length()) + nCount_ = length(); + return substr(length() - nCount_, nCount_); +} + +d4_inline int c4_String::Compare(const char* str_) const +{ + return compare(str_); +} + +d4_inline bool c4_String::operator< (const c4_String& str_) const +{ + return compare(str_) < 0; +} + +d4_inline int c4_String::Find(char ch_) const +{ + return find(ch_); +} + +d4_inline int c4_String::ReverseFind(char ch_) const +{ + return rfind(ch_); +} + +d4_inline int c4_String::FindOneOf(const char* set_) const +{ + return find_first_of(set_); +} + +d4_inline int c4_String::Find(const char* sub_) const +{ + return find(sub_); +} + +d4_inline c4_String c4_String::SpanIncluding(const char* set_) const +{ + return substr(0, find_first_not_of(set_)); +} + +d4_inline c4_String c4_String::SpanExcluding(const char* set_) const +{ + return substr(0, find_first_of(set_)); +} + +d4_inline bool operator== (const c4_String& a_, const c4_String& b_) +{ + return (d4_std::string) a_ == (d4_std::string) b_; +} + +d4_inline bool operator!= (const c4_String& a_, const c4_String& b_) +{ + return (d4_std::string) a_ != (d4_std::string) b_; +} + +d4_inline bool operator== (const c4_String& a_, const char* b_) +{ + return (d4_std::string) a_ == (d4_std::string) b_; +} + +d4_inline bool operator== (const char* a_, const c4_String& b_) +{ + return (d4_std::string) a_ == (d4_std::string) b_; +} + +d4_inline bool operator!= (const c4_String& a_, const char* b_) +{ + return (d4_std::string) a_ != (d4_std::string) b_; +} + +d4_inline bool operator!= (const char* a_, const c4_String& b_) +{ + return (d4_std::string) a_ != (d4_std::string) b_; +} + +#else // Universal replacement classes + +/// Construct empty string +d4_inline c4_String::c4_String () +{ + Init(0, 0); +} + +/// Construct string from Pascal-style <count,chars...> data +d4_inline c4_String::c4_String (const unsigned char* ptr) +{ + Init(ptr + 1, ptr ? *ptr : 0); +} + +/// Construct string from a specified area in memory +d4_inline c4_String::c4_String (const void* ptr, int len) +{ + Init(ptr, len); +} + +d4_inline const c4_String& c4_String::operator+= (const c4_String& s) +{ + return *this = *this + s; +} + +d4_inline const c4_String& c4_String::operator+= (const char* s) +{ + return *this += (c4_String) s; +} + +d4_inline const char* c4_String::Data() const +{ + return (const char*) (_value + 2); +} + +d4_inline c4_String::operator const char* () const +{ + return Data(); +} + +d4_inline c4_String::operator const unsigned char* () const +{ + return (unsigned char*) Data() - 1; +} + +d4_inline char c4_String::operator[] (int i) const +{ + return Data()[i]; +} + +d4_inline int c4_String::GetLength() const +{ + return _value[1] != 255 ? _value[1] : FullLength(); +} + +d4_inline bool c4_String::IsEmpty() const +{ + return GetLength() == 0; +} + +d4_inline void c4_String::Empty() +{ + *this = ""; +} + +d4_inline bool c4_String::operator< (const c4_String& a) const +{ + return Compare(a) < 0; +} + +d4_inline c4_String operator+ (const char* a, const c4_String& b) +{ + return (c4_String) a + b; +} + +d4_inline c4_String operator+ (const c4_String& a, const char* b) +{ + return a + (c4_String) b; +} + +d4_inline bool operator== (const char* a, const c4_String& b) +{ + return b.Compare(a) == 0; +} + +d4_inline bool operator== (const c4_String& a, const char* b) +{ + return a.Compare(b) == 0; +} + +d4_inline bool operator!= (const c4_String& a, const c4_String& b) +{ + return !(a == b); +} + +d4_inline bool operator!= (const char* a, const c4_String& b) +{ + return b.Compare(a) != 0; +} + +d4_inline bool operator!= (const c4_String& a, const char* b) +{ + return a.Compare(b) != 0; +} + +#endif // q4_UNIV + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/Makefile.am b/akregator/src/mk4storage/metakit/src/Makefile.am new file mode 100644 index 000000000..e428fe930 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/Makefile.am @@ -0,0 +1,10 @@ +INCLUDES = -I$(srcdir)/../include + +noinst_LTLIBRARIES = libmetakitlocal.la + +noinst_HEADERS = borc.h column.h column.inl custom.h derived.h field.h field.inl format.h gnuc.h handler.h handler.inl header.h mfc.h \ + msvc.h mwcw.h persist.h remap.h std.h store.h store.inl univ.h univ.inl win.h + +libmetakitlocal_la_SOURCES = column.cpp custom.cpp derived.cpp field.cpp fileio.cpp format.cpp handler.cpp persist.cpp remap.cpp std.cpp store.cpp \ + string.cpp table.cpp univ.cpp view.cpp viewx.cpp + diff --git a/akregator/src/mk4storage/metakit/src/borc.h b/akregator/src/mk4storage/metakit/src/borc.h new file mode 100644 index 000000000..2b881fc90 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/borc.h @@ -0,0 +1,33 @@ +// borc.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Configuration header for Borland C++ + */ + +#define q4_BORC 1 + + // get rid of several common warning messages +#if !q4_STRICT +#pragma warn -aus // 'identifier' is assigned a value that is never used +#pragma warn -par // Parameter 'parameter' is never used. +#pragma warn -sig // Conversion may lose significant digits +#pragma warn -use // 'identifier' declared but never used +#endif + +#if __BORLANDC__ >= 0x500 +#define q4_BOOL 1 // supports the bool datatype + // undo previous defaults, because q4_BOOL is not set early enough +#undef false +#undef true +#undef bool +#endif + +#if !defined (q4_EXPORT) +#define q4_EXPORT 1 // requires export/import specifiers +#endif + +#if defined (__MT__) +#define q4_MULTI 1 // uses multi-threading +#endif diff --git a/akregator/src/mk4storage/metakit/src/column.cpp b/akregator/src/mk4storage/metakit/src/column.cpp new file mode 100644 index 000000000..2d191c645 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/column.cpp @@ -0,0 +1,1533 @@ +// column.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Implements c4_Column, c4_ColOfInts, and c4_ColIter + */ + +#include "header.h" +#include "column.h" +#include "persist.h" + +#if !q4_INLINE +#include "column.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + +#if !HAVE_MEMMOVE && !HAVE_BCOPY + // in case we have no library memmove, or one that can't handle overlap + + void f4_memmove (void* to_, const void* from_, int n_) + { + char* to = (char*) to_; + const char* from = (const char*) from_; + + if (to + n_ <= from || from + n_ <= to) + memcpy(to, from, n_); + else if (to < from) + while (--n_ >= 0) + *to++ = *from++; + else if (to > from) + while (--n_ >= 0) + to[n_] = from[n_]; + } +#endif + +///////////////////////////////////////////////////////////////////////////// +// c4_Column + +c4_Column::c4_Column (c4_Persist* persist_) + : _position (0), _size (0), _persist (persist_), _gap (0), + _slack (0), _dirty (false) +{ +} + +#if q4_CHECK + + // debugging version to verify that the internal data is consistent + void c4_Column::Validate() const + { + d4_assert(0 <= _slack && _slack < kSegMax); + + if (_segments.GetSize() == 0) + return; // ok, not initialized + + d4_assert(_gap <= _size); + + int n = fSegIndex(_size + _slack); + d4_assert(n == _segments.GetSize() - 1); + + t4_byte* p = (t4_byte*) _segments.GetAt(n); + + if (fSegRest(_size + _slack) == 0) + d4_assert(p == 0); + else + d4_assert(p != 0); + + while (--n >= 0) { + t4_byte* p = (t4_byte*) _segments.GetAt(n); + d4_assert(p != 0); + } + } + +#else + + // nothing, so inline this thing to avoid even the calling overhead + d4_inline void c4_Column::Validate() const + { + } + +#endif + +c4_Column::~c4_Column () +{ + Validate(); + ReleaseAllSegments(); + + // this is needed to remove this column from the cache + d4_assert(_slack == 0); + FinishSlack(); + + _slack = -1; // bad value in case we try to set up again (!) +} + +c4_Strategy& c4_Column::Strategy() const +{ + d4_assert(_persist != 0); + + return _persist->Strategy(); +} + +bool c4_Column::IsMapped() const +{ + return _position > 1 && _persist != 0 && Strategy()._mapStart != 0; +} + +bool c4_Column::UsesMap(const t4_byte* ptr_) const +{ + // the most common falsifying case is checked first + return _persist != 0 && ptr_ >= Strategy()._mapStart && + Strategy()._dataSize != 0 && // added 2003-05-08, thx V DeMarco + ptr_ - Strategy()._mapStart < Strategy()._dataSize; +} + +bool c4_Column::RequiresMap() const +{ + if (_persist != 0 && Strategy()._mapStart != 0) + for (int i = _segments.GetSize(); --i >= 0; ) + if (UsesMap((t4_byte*) _segments.GetAt(i))) + return true; + return false; +} + +void c4_Column::ReleaseSegment(int index_) +{ + t4_byte* p = (t4_byte*) _segments.GetAt(index_); + if (!UsesMap(p)) + delete [] p; +} + +void c4_Column::ReleaseAllSegments() +{ + //for (int i = 0; i < _segments.GetSize(); ++i) + for (int i = _segments.GetSize(); --i >= 0; ) + ReleaseSegment(i); // last one might be a null pointer + + _segments.SetSize(0); + + _gap = 0; + _slack = 0; + + if (_size == 0) + _position = 0; + + _dirty = false; +} + + //@func Define where data is on file, or setup buffers (opt cleared). +void c4_Column::SetLocation(t4_i32 pos_, t4_i32 size_) +{ + d4_assert(size_ > 0 || pos_ == 0); + + ReleaseAllSegments(); + + _position = pos_; + _size = size_; + + // There are two position settings: + // + // 0 = raw buffer, no file access + // >1 = file position from where data can be loaded on demand + + _dirty = pos_ == 0; +} + +void c4_Column::PullLocation(const t4_byte*& ptr_) +{ + d4_assert(_segments.GetSize() == 0); + + _size = PullValue(ptr_); + _position = 0; + if (_size > 0) { + _position = PullValue(ptr_); + if (_position > 0) { + d4_assert(_persist != 0); + _persist->OccupySpace(_position, _size); + } + } + + _dirty = false; +} + + //@func How many contiguous bytes are there at a specified position. +int c4_Column::AvailAt(t4_i32 offset_) const +{ + d4_assert(offset_ <= _size); + d4_assert(_gap <= _size); + + t4_i32 limit = _gap; + + if (offset_ >= _gap) { + offset_ += _slack; + limit = _size + _slack; + } + + int count = kSegMax - fSegRest(offset_); + if (offset_ + count > limit) + count = (int) (limit - offset_); + + // either some real data or it must be at the very end of all data + d4_assert(0 < count && count <= kSegMax || + count == 0 && offset_ == _size + _slack); + return count; +} + +void c4_Column::SetupSegments() +{ + d4_assert(_segments.GetSize() == 0); + d4_assert(_gap == 0); + d4_assert(_slack == 0); + + // The last entry in the _segments array is either a partial block + // or a null pointer, so calling "fSegIndex(_size)" is always allowed. + + int n = fSegIndex(_size) + 1; + _segments.SetSize(n); + + // treat last block differently if it is a partial entry + int last = n; + if (fSegRest(_size)) + --last; // this block is partial, size is 1 .. kSegMax-1 + else + --n; // the last block is left as a null pointer + + int id = -1; + if (_position < 0) { // special aside id, figure out the real position + d4_assert(_persist != 0); + id = ~_position; + _position = _persist->LookupAside(id); + d4_assert(_position >= 0); + } + + if (IsMapped()) { + // setup for mapped files is quick, just fill in the pointers + d4_assert(_position > 1); + d4_assert(_position + (n-1) * kSegMax <= Strategy()._dataSize); + const t4_byte* map = Strategy()._mapStart + _position; + + for (int i = 0; i < n; ++i) { + _segments.SetAt(i, (t4_byte*) map); // loses const + map += kSegMax; + } + } else { + int chunk = kSegMax; + t4_i32 pos = _position; + + // allocate buffers, load them if necessary + for (int i = 0; i < n; ++i) { + if (i == last) + chunk = fSegRest(_size); + + t4_byte* p = d4_new t4_byte [chunk]; + _segments.SetAt(i, p); + + if (_position > 0) { + d4_dbgdef(int n =) + Strategy().DataRead(pos, p, chunk); + d4_assert(n == chunk); + pos += chunk; + } + } + } + + if (id >= 0) { + d4_assert(_persist != 0); + _persist->ApplyAside(id, *this); + } + + Validate(); +} + + //@func Makes sure the requested data is in a modifiable buffer. +t4_byte* c4_Column::CopyNow(t4_i32 offset_) +{ + d4_assert(offset_ <= _size); + + _dirty = true; + + const t4_byte* ptr = LoadNow(offset_); + if (UsesMap(ptr)) { + if (offset_ >= _gap) + offset_ += _slack; + + // this will only force creation of a buffer + ptr = CopyData(offset_, offset_, 0); + d4_assert(!UsesMap(ptr)); + } + + return (t4_byte*) ptr; +} + + //@func Copies data, creating a buffer if needed. Must be in single segment. +t4_byte* c4_Column::CopyData(t4_i32 to_, t4_i32 from_, int count_) +{ + int i = fSegIndex(to_); + t4_byte* p = (t4_byte*) _segments.GetAt(i); + + if (UsesMap(p)) { + int n = kSegMax; + if (fSegOffset(i) + n > _size + _slack) + n = (int) (_size + _slack - fSegOffset(i)); + + d4_assert(n > 0); + + t4_byte* q = d4_new t4_byte [n]; + memcpy(q, p, n); // some copying can be avoided, overwritten below... + _segments.SetAt(i, q); + + p = q; + } + + p += fSegRest(to_); + + if (count_ > 0) { + d4_assert(fSegIndex(to_ + count_ - 1) == i); + + const t4_byte* src = (const t4_byte*) _segments.GetAt(fSegIndex(from_)); + d4_memmove(p, src + fSegRest(from_), count_); + } + + return p; +} + + /* + * Resizing a segmented vector can be a complicated operation. + * For now, simply making it work in all cases is the first priority. + * + * A major simplification - and good performance improvement - is caused + * by the trick of maintaining a "gap" in the data, which can be "moved" + * around to allow fast insertion as well as simple (delayed) deletion. + * + * The only complexity comes from the fact that the gap must end up being + * less than one full segment in size. Therefore, insertion and removal + * across segment boundaries needs to handle a variety of situations. + * + * Since complete segments can be inserted quickly, this approach avoids + * lots of copying when consecutive insertions/deletions are clustered. + * Even random changes move half as much (on average) as without a gap. + * + * The price is the overhead of up to one segment of empty space, and the + * complexity of this code (all the magic is within this c4_Column class). + */ + +void c4_Column::MoveGapUp(t4_i32 dest_) +{ + d4_assert(dest_ <= _size); + d4_assert(_gap < dest_); + d4_assert(_slack > 0); + + // forward loop to copy contents down, in little pieces if need be + while (_gap < dest_) { + int n = kSegMax - fSegRest(_gap); + t4_i32 curr = _gap + n; + if (curr > dest_) + curr = dest_; + + // copy to [_gap..curr), which is inside one segment + d4_assert(_gap < curr); + d4_assert(fSegIndex(_gap) == fSegIndex(curr - 1)); + + // copy from [_gap + _slack .. curr + _slack), of the same size + t4_i32 fromBeg = _gap + _slack; + t4_i32 fromEnd = curr + _slack; + + while (fromBeg < fromEnd) { + int k = kSegMax - fSegRest(fromBeg); + if (fromBeg + k > fromEnd) + k = (int) (fromEnd - fromBeg); + + d4_assert(k > 0); + + CopyData(_gap, fromBeg, k); + + _gap += k; + fromBeg += k; + } + + _gap = curr; + } + + d4_assert(_gap == dest_); +} + +void c4_Column::MoveGapDown(t4_i32 dest_) +{ + d4_assert(dest_ <= _size); + d4_assert(_gap > dest_); + d4_assert(_slack > 0); + + // reverse loop to copy contents up, in little pieces if need be + t4_i32 toEnd = _gap + _slack; + t4_i32 toBeg = dest_ + _slack; + + while (toEnd > toBeg) { + int n = fSegRest(toEnd); + t4_i32 curr = toEnd - (n ? n : kSegMax); + if (curr < toBeg) + curr = toBeg; + + // copy to [curr..toEnd), which is inside one segment + d4_assert(curr < toEnd); + d4_assert(fSegIndex(curr) == fSegIndex(toEnd - 1)); + + // copy from [fromBeg .. _gap), which has the same size + t4_i32 fromBeg = _gap - (toEnd - curr); + + while (_gap > fromBeg) { + int k = fSegRest(_gap); + if (k == 0) + k = kSegMax; + if (_gap - k < fromBeg) + k = (int) (_gap - fromBeg); + + d4_assert(k > 0); + + toEnd -= k; + _gap -= k; + + CopyData(toEnd, _gap, k); + } + } + + d4_assert(_gap == dest_); +} + +void c4_Column::MoveGapTo(t4_i32 pos_) +{ + d4_assert(pos_ <= _size); + + if (_slack == 0) // if there is no real gap, then just move it + _gap = pos_; + else if (_gap < pos_) // move the gap up, ie. some bytes down + MoveGapUp(pos_); + else if (_gap > pos_) // move the gap down, ie. some bytes up + if (_gap - pos_ > _size - _gap + fSegRest(pos_)) { + RemoveGap(); // it's faster to get rid of the gap instead + _gap = pos_; + } + else // normal case, move some bytes up + MoveGapDown(pos_); + + d4_assert(_gap == pos_); + + Validate(); +} + +void c4_Column::RemoveGap() +{ + if (_slack > 0) { + if (_gap < _size) + MoveGapUp(_size); + + d4_assert(_gap == _size); // the gap is now at the end + d4_assert(_slack < kSegMax); + + // Case 1: gap is at start of segment + // ================================== + // + // G G+S + // + // | | + // :----+xx: + // | | + // + // i i+1 (limit) + // + // Case 2: gap is inside segment + // ============================= + // + // G G+S + // + // | | + // :--+--+x: + // | | + // + // i i+1 (limit) + // + // Case 3: gap runs to end of segment + // ================================== + // + // G G+S + // + // | | + // :--+----:0000000: + // | | | + // + // i i+1 i+2 (limit) + // + // Case 4: gap is across two segments + // ================================== + // + // G G+S + // + // | | + // :--+----:-+xxxxx: + // | | | + // + // i i+1 i+2 (limit) + + int i = fSegIndex(_gap); + int n = fSegRest(_gap); + + if (n == 0) { // case 1 + ReleaseSegment(i); + _segments.SetAt(i, 0); + } else { + if (n + _slack > kSegMax) // case 4 + ReleaseSegment(i+1); + + // truncate rest of segment + t4_byte* p = d4_new t4_byte [n]; + memcpy(p, _segments.GetAt(i), n); + + ReleaseSegment(i); + _segments.SetAt(i, p); + _segments.SetSize(i + 1); + } + + _slack = 0; + } + + Validate(); +} + +void c4_Column::Grow(t4_i32 off_, t4_i32 diff_) +{ + d4_assert(off_ <= _size); + d4_assert(diff_ > 0); + + if (_segments.GetSize() == 0) + SetupSegments(); + + Validate(); + + _dirty = true; + + // move the gap so it starts where we want to insert + MoveGapTo(off_); + + t4_i32 bigSlack = _slack; + if (bigSlack < diff_) { // only do more if this isn't good enough + // number of segments to insert + int n = fSegIndex(diff_ - _slack + kSegMax - 1); + d4_assert(n > 0); + + int i1 = fSegIndex(_gap); + int i2 = fSegIndex(_gap + _slack); + + bool moveBack = false; + + if (i2 > i1) // cases 3 and 4 + ++i1; + else if (fSegRest(_gap)) // case 2 + moveBack = true; + + _segments.InsertAt(i1, 0, n); + for (int i = 0; i < n; ++i) + _segments.SetAt(i1 + i, d4_new t4_byte [(int) kSegMax]); + + bigSlack += fSegOffset(n); + + if (moveBack) { + d4_assert(i1 == fSegIndex(_gap)); + + // we have inserted too low, move bytes in front of gap back + CopyData(fSegOffset(i1), fSegOffset(i1 + n), fSegRest(_gap)); + } + } + + d4_assert(diff_ <= bigSlack && bigSlack < diff_ + kSegMax); + + _gap += diff_; + _slack = (int) (bigSlack - diff_); + _size += diff_; + + FinishSlack(); +} + +void c4_Column::Shrink(t4_i32 off_, t4_i32 diff_) +{ + d4_assert(off_ <= _size); + d4_assert(diff_ > 0); + + if (_segments.GetSize() == 0) + SetupSegments(); + + Validate(); + + _dirty = true; + + // the simplification here is that we have in fact simply *two* + // gaps and we must merge them together and end up with just one + + if (_slack > 0) { + if (_gap < off_) // if too low, move the gap up + MoveGapTo(off_); + else if (off_ + diff_ < _gap) // if too high, move down to end + MoveGapTo(off_ + diff_); + + // the gap is now inside, or adjacent to, the deleted area + d4_assert(off_ <= _gap && _gap <= off_ + diff_); + } + + _gap = off_; + + // check whether the merged gap would cross a segment boundary + int i1 = fSegIndex(_gap); + int i2 = fSegIndex(_gap + _slack + diff_); + + // drop complete segments, not a partially filled boundary + if (fSegRest(_gap)) + ++i1; + + // moved up (was after the next if in the 1.7 May 28 build) + _slack += diff_; + _size -= diff_; + + int n = i2 - i1; + if (n > 0) { + for (int i = i1; i < i2; ++i) + ReleaseSegment(i); + + _segments.RemoveAt(i1, n); + + // the logic in 1.7 of May 28 was warped (the assert "fix" was wrong) + d4_assert(_slack >= fSegOffset(n)); + _slack -= fSegOffset(n); + } + + d4_assert(0 <= _slack && _slack < 2 * kSegMax); + + // if the gap is at the end, get rid of a partial segment after it + if (_gap == _size) { + int i = fSegIndex(_size + _slack); + if (i != fSegIndex(_gap)) { + d4_assert(i == fSegIndex(_gap) + 1); + d4_assert(i == _segments.GetSize() - 1); + + ReleaseSegment(i); + _segments.SetAt(i, 0); + + _slack -= fSegRest(_size + _slack); + + d4_assert(_slack < kSegMax); + d4_assert(fSegRest(_gap + _slack) == 0); + } + } + + // the slack may still be too large to leave as is + if (_slack >= kSegMax) { + // move the bytes just after the end of the gap one segment down + int x = fSegRest(_gap + _slack); + int r = kSegMax - x; + if (_gap + r > _size) + r = (int) (_size - _gap); + + CopyData(_gap, _gap + _slack, r); + + int i = fSegIndex(_gap + kSegMax - 1); + ReleaseSegment(i); + + if (r + x < kSegMax) + _segments.SetAt(i, 0); + else + _segments.RemoveAt(i); + + _slack -= r + x; + _gap += r; + } + + // if we have no data anymore, make sure not to use the file map either + if (_size == 0 && _slack > 0) + CopyNow(0); + + FinishSlack(); +} + +void c4_Column::FinishSlack() +{ + Validate(); + + // optimization: if partial end segment easily fits in slack, move it down + t4_i32 gapEnd = _gap + _slack; + if (!fSegRest(gapEnd) && gapEnd >= _size + 500) { + // slack is at least 500 bytes more than the partial end segment + // also, the gap must end exactly on a segment boundary + int i = fSegIndex(gapEnd); + d4_assert(i == _segments.GetSize() - 1); + + int n = _size - _gap; + CopyData(gapEnd - n, gapEnd, n); + + ReleaseSegment(i); + _segments.SetAt(i, 0); + + _slack -= n; + d4_assert(_slack >= 500); + + Validate(); + } +} + +void c4_Column::SaveNow(c4_Strategy& strategy_, t4_i32 pos_) +{ + if (_segments.GetSize() == 0) + SetupSegments(); + + // write all segments + c4_ColIter iter (*this, 0, _size); + while (iter.Next(kSegMax)) { + int n = iter.BufLen(); + strategy_.DataWrite(pos_, iter.BufLoad(), n); + if (strategy_._failure != 0) + break; + pos_ += n; + } +} + +const t4_byte* c4_Column::FetchBytes(t4_i32 pos_, int len_, c4_Bytes& buffer_, bool forceCopy_) +{ + d4_assert(len_ > 0); + d4_assert(pos_ + len_ <= ColSize()); + d4_assert(0 <= _slack && _slack < kSegMax); + + c4_ColIter iter (*this, pos_, pos_ + len_); + iter.Next(); + + // most common case, all bytes are inside the same segment + if (!forceCopy_ && iter.BufLen() == len_) + return iter.BufLoad(); + + t4_byte* p = buffer_.SetBuffer(len_); + do { + d4_assert(iter.BufLen() > 0); + memcpy(p, iter.BufLoad(), iter.BufLen()); + p += iter.BufLen(); + } while (iter.Next()); + d4_assert(p == buffer_.Contents() + len_); + + return buffer_.Contents(); +} + +void c4_Column::StoreBytes(t4_i32 pos_, const c4_Bytes& buffer_) +{ + int n = buffer_.Size(); + if (n > 0) { + d4_assert(pos_ + n <= ColSize()); + + c4_ColIter iter (*this, pos_, pos_ + n); + + const t4_byte* p = buffer_.Contents(); + while (iter.Next(n)) { + d4_assert(iter.BufLen() > 0); + memcpy(iter.BufSave(), p, iter.BufLen()); + p += iter.BufLen(); + } + d4_assert(p == buffer_.Contents() + n); + } +} + + /* + PushValue and PullValue deal with variable-sized storage of + one unsigned integer value of up to 32 bits. Depending on the + magnitude of the integer, 1..6 bytes are used to represent it. + Each byte holds 7 significant bits and one continuation bit. + This saves storage, but it is also byte order independent. + Negative values are stored as a zero byte plus positive value. + */ + +t4_i32 c4_Column::PullValue(const t4_byte*& ptr_) +{ + t4_i32 mask = *ptr_ ? 0 : ~0; + + t4_i32 v = 0; + for (;;) { + v = (v << 7) + *ptr_; + if (*ptr_++ & 0x80) + break; + } + + return mask ^ (v - 0x80); // oops, last byte had bit 7 set +} + +void c4_Column::PushValue(t4_byte*& ptr_, t4_i32 v_) +{ + if (v_ < 0) { + v_ = ~v_; + *ptr_++ = 0; + } + + int n = 0; + do + n += 7; + while ((v_ >> n) && n < 32); + + while (n) { + n -= 7; + t4_byte b = (t4_byte) ((v_ >> n) & 0x7F); + if (!n) + b |= 0x80; // set bit 7 on the last byte + *ptr_++ = b; + } +} + +void c4_Column::InsertData(t4_i32 index_, t4_i32 count_, bool clear_) +{ + d4_assert(index_ <= ColSize()); + + if (count_ > 0) { + Grow(index_, count_); + + // clear the contents, in separate chunks if necessary + if (clear_) { + c4_ColIter iter (*this, index_, index_ + count_); + while (iter.Next()) + memset(iter.BufSave(), 0, iter.BufLen()); + } + } +} + +void c4_Column::RemoveData(t4_i32 index_, t4_i32 count_) +{ + d4_assert(index_ + count_ <= ColSize()); + + if (count_ > 0) + Shrink(index_, count_); +} + +///////////////////////////////////////////////////////////////////////////// + +void c4_ColOfInts::Get_0b(int) +{ + *(t4_i32*) _item = 0; +} + +void c4_ColOfInts::Get_1b(int index_) +{ + t4_i32 off = index_ >> 3; + d4_assert(off < ColSize()); + + *(t4_i32*) _item = (*LoadNow(off) >> (index_ & 7)) & 0x01; +} + +void c4_ColOfInts::Get_2b(int index_) +{ + t4_i32 off = index_ >> 2; + d4_assert(off < ColSize()); + + *(t4_i32*) _item = (*LoadNow(off) >> ((index_ & 3) << 1)) & 0x03; +} + +void c4_ColOfInts::Get_4b(int index_) +{ + t4_i32 off = index_ >> 1; + d4_assert(off < ColSize()); + + *(t4_i32*) _item = (*LoadNow(off) >> ((index_ & 1) << 2)) & 0x0F; +} + +void c4_ColOfInts::Get_8i(int index_) +{ + t4_i32 off = index_; + d4_assert(off < ColSize()); + + *(t4_i32*) _item = *(const signed char*) LoadNow(off); +} + +void c4_ColOfInts::Get_16i(int index_) +{ + t4_i32 off = index_ * (t4_i32) 2; + d4_assert(off + 2 <= ColSize()); + + const t4_byte* vec = LoadNow(off); + + _item[0] = vec[0]; + _item[1] = vec[1]; + + *(t4_i32*) _item = *(const short*) _item; +} + +void c4_ColOfInts::Get_16r(int index_) +{ + t4_i32 off = index_ * (t4_i32) 2; + d4_assert(off + 2 <= ColSize()); + + const t4_byte* vec = LoadNow(off); + + // 2003-02-02 - gcc 3.2.1 on linux (!) fails to compile this + // sign-extension trick properly, use a temp buffer instead: + //*(t4_i32*) _item = *(const short*) _item; + + t4_byte temp[2]; + temp[1] = vec[0]; + temp[0] = vec[1]; + *(t4_i32*) _item = *(const short*) temp; +} + +void c4_ColOfInts::Get_32i(int index_) +{ + t4_i32 off = index_ * (t4_i32) 4; + d4_assert(off + 4 <= ColSize()); + + const t4_byte* vec = LoadNow(off); + + _item[0] = vec[0]; + _item[1] = vec[1]; + _item[2] = vec[2]; + _item[3] = vec[3]; +} + +void c4_ColOfInts::Get_32r(int index_) +{ + t4_i32 off = index_ * (t4_i32) 4; + d4_assert(off + 4 <= ColSize()); + + const t4_byte* vec = LoadNow(off); + + _item[3] = vec[0]; + _item[2] = vec[1]; + _item[1] = vec[2]; + _item[0] = vec[3]; +} + +void c4_ColOfInts::Get_64i(int index_) +{ + t4_i32 off = index_ * (t4_i32) 8; + d4_assert(off + 8 <= ColSize()); + + const t4_byte* vec = LoadNow(off); + + for (int i = 0; i < 8; ++i) + _item[i] = vec[i]; +} + +void c4_ColOfInts::Get_64r(int index_) +{ + t4_i32 off = index_ * (t4_i32) 8; + d4_assert(off + 8 <= ColSize()); + + const t4_byte* vec = LoadNow(off); + + for (int i = 0; i < 8; ++i) + _item[7-i] = vec[i]; +} + +///////////////////////////////////////////////////////////////////////////// + + static int fBitsNeeded(t4_i32 v) + { + if ((v >> 4) == 0) { + static int bits[] = { 0, 1, 2, 2, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4 }; + return bits[(int) v]; + } + + if (v < 0) // first flip all bits if bit 31 is set + v = ~ v; // ... bit 31 is now always zero + + // then check if bits 15-31 used (32b), 7-31 used (16b), else (8b) + return v >> 15 ? 32 : v >> 7 ? 16 : 8; + } + +bool c4_ColOfInts::Set_0b(int, const t4_byte* item_) +{ + t4_i32 v = *(const t4_i32*) item_; + return v == 0; +} + +bool c4_ColOfInts::Set_1b(int index_, const t4_byte* item_) +{ + t4_i32 v = *(const t4_i32*) item_; + + t4_i32 off = index_ >> 3; + d4_assert(off < ColSize()); + + index_ &= 7; + + t4_byte* p = CopyNow(off); + *p = (*p & ~(1 << index_)) | (((t4_byte) v & 1) << index_); + + return (v >> 1) == 0; +} + +bool c4_ColOfInts::Set_2b(int index_, const t4_byte* item_) +{ + t4_i32 v = *(const t4_i32*) item_; + + t4_i32 off = index_ >> 2; + d4_assert(off < ColSize()); + + const int n = (index_ & 3) << 1; + + t4_byte* p = CopyNow(off); + *p = (*p & ~(0x03 << n)) | (((t4_byte) v & 0x03) << n); + + return (v >> 2) == 0; +} + +bool c4_ColOfInts::Set_4b(int index_, const t4_byte* item_) +{ + t4_i32 v = *(const t4_i32*) item_; + + t4_i32 off = index_ >> 1; + d4_assert(off < ColSize()); + + const int n = (index_ & 1) << 2; + + t4_byte* p = CopyNow(off); + *p = (*p & ~(0x0F << n)) | (((t4_byte) v & 0x0F) << n); + + return (v >> 4) == 0; +} + +// avoid a bug in MS EVC 3.0's code gen for ARM (i.e. WinCE) +#ifdef _ARM_ +#pragma optimize("g",off) +#endif + +bool c4_ColOfInts::Set_8i(int index_, const t4_byte* item_) +{ + t4_i32 v = *(const t4_i32*) item_; + + t4_i32 off = index_; + d4_assert(off < ColSize()); + + *(char*) CopyNow(off) = (char) v; + + return v == (signed char) v; +} + +#ifdef _ARM_ +#pragma optimize("",on) +#endif + +bool c4_ColOfInts::Set_16i(int index_, const t4_byte* item_) +{ + t4_i32 v = *(const t4_i32*) item_; + + t4_i32 off = index_ * (t4_i32) 2; + d4_assert(off + 2 <= ColSize()); + + *(short*) CopyNow(off) = (short) v; + + return v == (short) v; +} + +bool c4_ColOfInts::Set_16r(int index_, const t4_byte* item_) +{ + t4_i32 v = *(const t4_i32*) item_; + + t4_byte buf [2]; + *(short*) buf = (short) v; + + t4_i32 off = index_ * (t4_i32) 2; + d4_assert(off + 2 <= ColSize()); + + t4_byte* vec = CopyNow(off); + + vec[1] = buf[0]; + vec[0] = buf[1]; + + return v == (short) v; +} + +bool c4_ColOfInts::Set_32i(int index_, const t4_byte* item_) +{ + t4_i32 v = *(const t4_i32*) item_; + + t4_i32 off = index_ * (t4_i32) 4; + d4_assert(off + 4 <= ColSize()); + + *(t4_i32*) CopyNow(off) = (t4_i32) v; + + return true; +} + +bool c4_ColOfInts::Set_32r(int index_, const t4_byte* item_) +{ + t4_i32 off = index_ * (t4_i32) 4; + d4_assert(off + 4 <= ColSize()); + + t4_byte* vec = CopyNow(off); + + vec[3] = item_[0]; + vec[2] = item_[1]; + vec[1] = item_[2]; + vec[0] = item_[3]; + + return true; +} + +bool c4_ColOfInts::Set_64i(int index_, const t4_byte* item_) +{ + t4_i32 off = index_ * (t4_i32) 8; + d4_assert(off + 8 <= ColSize()); + + t4_byte* vec = CopyNow(off); + + for (int i = 0; i < 8; ++i) + vec[i] = item_[i]; + + return true; +} + +bool c4_ColOfInts::Set_64r(int index_, const t4_byte* item_) +{ + t4_i32 off = index_ * (t4_i32) 8; + d4_assert(off + 8 <= ColSize()); + + t4_byte* vec = CopyNow(off); + + for (int i = 0; i < 8; ++i) + vec[7-i] = item_[i]; + + return true; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_ColOfInts::c4_ColOfInts (c4_Persist* persist_, int width_) + : c4_Column (persist_), + _getter (&c4_ColOfInts::Get_0b), _setter (&c4_ColOfInts::Set_0b), + _currWidth (0), _dataWidth (width_), _numRows (0), _mustFlip (false) +{ +} + +void c4_ColOfInts::ForceFlip() +{ + _mustFlip = true; +} + +int c4_ColOfInts::RowCount() const +{ + d4_assert(_numRows >= 0); + + return _numRows; +} + +int c4_ColOfInts::CalcAccessWidth(int numRows_, t4_i32 colSize_) +{ + d4_assert(numRows_ > 0); + + int w = (int) ((colSize_ << 3) / numRows_); + + // deduce sub-byte sizes for small vectors, see c4_ColOfInts::Set + if (numRows_ <= 7 && 0 < colSize_ && colSize_ <= 6) { + static t4_byte realWidth [][6] = { + // sz = 1: 2: 3: 4: 5: 6: + { 8, 16, 1, 32, 2, 4 }, // n = 1 + { 4, 8, 1, 16, 2, 0 }, // n = 2 + { 2, 4, 8, 1, 0, 16 }, // n = 3 + { 2, 4, 0, 8, 1, 0 }, // n = 4 + { 1, 2, 4, 0, 8, 0 }, // n = 5 + { 1, 2, 4, 0, 0, 8 }, // n = 6 + { 1, 2, 0, 4, 0, 0 }, // n = 7 + }; + + w = realWidth [numRows_-1] [(int) colSize_ - 1]; + d4_assert(w > 0); + } + + return (w & (w - 1)) == 0 ? w : -1; +} + +void c4_ColOfInts::SetRowCount(int numRows_) +{ + _numRows = numRows_; + if (numRows_ > 0) { + int w = CalcAccessWidth(numRows_, ColSize()); + d4_assert(w >= 0); + SetAccessWidth(w); + } +} + +void c4_ColOfInts::FlipBytes() +{ + if (_currWidth > 8) { + int step = _currWidth >> 3; + + c4_ColIter iter (*this, 0, ColSize()); + while (iter.Next(step)) { + t4_byte* data = iter.BufSave(); + d4_assert(data != 0); + + for (int j = 0; j < step; ++j) { + t4_byte c = data[j]; + data[j] = data[step-j-1]; + data[step-j-1] = c; + } + } + } +} + +void c4_ColOfInts::SetAccessWidth(int bits_) +{ + d4_assert((bits_ & (bits_ - 1)) == 0); + + int l2bp1 = 0; // "log2 bits plus one" needed to represent value + while (bits_) { + ++l2bp1; + bits_ >>= 1; + } + d4_assert(0 <= l2bp1 && l2bp1 < 8); + + _currWidth = (1 << l2bp1) >> 1; + + if (l2bp1 > 4 && (_mustFlip || Persist() != 0 && Strategy()._bytesFlipped)) + l2bp1 += 3; // switch to the trailing entries for byte flipping + + // Metrowerks Codewarrior 11 is dumb, it requires the "&c4_ColOfInts::" + + static tGetter gTab [] = + { + &c4_ColOfInts::Get_0b, // 0: 0 bits/entry + &c4_ColOfInts::Get_1b, // 1: 1 bits/entry + &c4_ColOfInts::Get_2b, // 2: 2 bits/entry + &c4_ColOfInts::Get_4b, // 3: 4 bits/entry + + &c4_ColOfInts::Get_8i, // 4: 8 bits/entry + &c4_ColOfInts::Get_16i, // 5: 16 bits/entry + &c4_ColOfInts::Get_32i, // 6: 32 bits/entry + &c4_ColOfInts::Get_64i, // 7: 64 bits/entry + + &c4_ColOfInts::Get_16r, // 8: 16 bits/entry, reversed + &c4_ColOfInts::Get_32r, // 9: 32 bits/entry, reversed + &c4_ColOfInts::Get_64r, // 10: 64 bits/entry, reversed + }; + + static tSetter sTab [] = + { + &c4_ColOfInts::Set_0b, // 0: 0 bits/entry + &c4_ColOfInts::Set_1b, // 1: 1 bits/entry + &c4_ColOfInts::Set_2b, // 2: 2 bits/entry + &c4_ColOfInts::Set_4b, // 3: 4 bits/entry + + &c4_ColOfInts::Set_8i, // 4: 8 bits/entry + &c4_ColOfInts::Set_16i, // 5: 16 bits/entry + &c4_ColOfInts::Set_32i, // 6: 32 bits/entry + &c4_ColOfInts::Set_64i, // 7: 64 bits/entry + + &c4_ColOfInts::Set_16r, // 8: 16 bits/entry, reversed + &c4_ColOfInts::Set_32r, // 9: 32 bits/entry, reversed + &c4_ColOfInts::Set_64r, // 10: 64 bits/entry, reversed + }; + + d4_assert(l2bp1 < sizeof gTab / sizeof *gTab); + + _getter = gTab[l2bp1]; + _setter = sTab[l2bp1]; + + d4_assert(_getter != 0 && _setter != 0); +} + +int c4_ColOfInts::ItemSize(int) +{ + return _currWidth >= 8 ? _currWidth >> 3 : - _currWidth; +} + +const void* c4_ColOfInts::Get(int index_, int& length_) +{ + d4_assert(sizeof _item >= _dataWidth); + + (this->*_getter)(index_); + + length_ = _dataWidth; + return _item; +} + +void c4_ColOfInts::Set(int index_, const c4_Bytes& buf_) +{ + d4_assert(buf_.Size() == _dataWidth); + + if ((this->*_setter)(index_, buf_.Contents())) + return; + + d4_assert(buf_.Size() == sizeof (t4_i32)); + + int n = fBitsNeeded(*(const t4_i32*) buf_.Contents()); + if (n > _currWidth) { + int k = RowCount(); + + t4_i32 oldEnd = ColSize(); + t4_i32 newEnd = ((t4_i32) k * n + 7) >> 3; + + if (newEnd > oldEnd) { + InsertData(oldEnd, newEnd - oldEnd, _currWidth == 0); + + // 14-5-2002: need to get rid of gap in case it risks not being a + // multiple of the increased size (bug, see s46 regression test) + // + // Example scenario: gap size is odd, data gets resized to 2/4-byte + // ints, data at end fits without moving gap to end, then we end + // up with a vector that has an int split *across* the gap - this + // commits just fine, but access to that split int is now bad. + // + // Lesson: need stricter/simpler consistency, it's way too complex! + if (n > 8) + RemoveGap(); + } + + // data value exceeds width, expand to new size and repeat + if (_currWidth > 0) { + d4_assert(n % _currWidth == 0); // must be expanding by a multiple + + // To expand, we start by inserting a new appropriate chunk + // at the end, and expand the entries in place (last to first). + + tGetter oldGetter = _getter; + SetAccessWidth(n); + + d4_assert(sizeof _item >= _dataWidth); + + // this expansion in place works because it runs backwards + while (--k >= 0) { + (this->*oldGetter)(k); + (this->*_setter)(k, _item); + } + } else { + if (_dataWidth > (int) sizeof (t4_i32)) + n = _dataWidth << 3; // don't trust setter result, use max instead + + SetAccessWidth(n); + } + + // now repeat the failed call to _setter + /* bool f = */ (this->*_setter)(index_, buf_.Contents()); + //? d4_assert(f); + } +} + +t4_i32 c4_ColOfInts::GetInt(int index_) +{ + int n; + const void* p = Get(index_, n); + d4_assert(n == sizeof (t4_i32)); + return *(const t4_i32*) p; +} + +void c4_ColOfInts::SetInt(int index_, t4_i32 value_) +{ + Set(index_, c4_Bytes (&value_, sizeof value_)); +} + +int c4_ColOfInts::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + d4_assert(b1_.Size() == sizeof (t4_i32)); + d4_assert(b2_.Size() == sizeof (t4_i32)); + + t4_i32 v1 = *(const t4_i32*) b1_.Contents(); + t4_i32 v2 = *(const t4_i32*) b2_.Contents(); + + return v1 == v2 ? 0 : v1 < v2 ? -1 : +1; +} + +void c4_ColOfInts::Insert(int index_, const c4_Bytes& buf_, int count_) +{ + d4_assert(buf_.Size() == _dataWidth); + d4_assert(count_ > 0); + + bool clear = true; + const t4_byte* ptr = buf_.Contents(); + + for (int i = 0; i < _dataWidth; ++i) + if (*ptr++) { + clear = false; + break; + } + + ResizeData(index_, count_, clear); + + if (!clear) + while (--count_ >= 0) + Set(index_++, buf_); +} + +void c4_ColOfInts::Remove(int index_, int count_) +{ + d4_assert(count_ > 0); + + ResizeData(index_, - count_); +} + +void c4_ColOfInts::ResizeData(int index_, int count_, bool clear_) +{ + _numRows += count_; + + if (!(_currWidth & 7)) { // not 1, 2, or 4 + const t4_i32 w = (t4_i32) (_currWidth >> 3); + if (count_ > 0) + InsertData(index_ * w, count_ * w, clear_); + else + RemoveData(index_ * w, - count_ * w); + return; + } + + d4_assert(_currWidth == 1 || _currWidth == 2 || _currWidth == 4); + + /* _currwidth 1: 2: 4: + * shiftPos 3 2 1 shift the offset right this much + * maskPos 7 3 1 mask the offset with this + */ + + const int shiftPos = _currWidth == 4 ? 1 : 4 - _currWidth; + const int maskPos = (1 << shiftPos) - 1; + + // the following code is similar to c4_Column::Resize, but at bit level + + // turn insertion into deletion by inserting entire bytes + if (count_ > 0) { + unsigned off = (unsigned) index_ >> shiftPos; + int gapBytes = (count_ + maskPos) >> shiftPos; + + InsertData(off, gapBytes, clear_); + + // oops, we might have inserted too low by a few entries + const int bits = (index_ & maskPos) * _currWidth; + if (bits) { + const int maskLow = (1 << bits) - 1; + + // move the first few bits to start of inserted range + t4_byte* p = CopyNow(off + gapBytes); + t4_byte one = *p & maskLow; + *p &= ~maskLow; + + * CopyNow(off) = one; + } + + index_ += count_; + count_ -= gapBytes << shiftPos; + d4_assert(count_ <= 0); + } + + // now perform a deletion using a forward loop to copy down + if (count_ < 0) { + c4_Bytes temp; + + while (index_ < _numRows) { + int length; + const void* ptr = Get(index_ - count_, length); + Set(index_++, c4_Bytes (ptr, length)); + } + } + else { + d4_assert(count_ == 0); + } + + FixSize(false); +} + +void c4_ColOfInts::FixSize(bool fudge_) +{ + int n = RowCount(); + t4_i32 needBytes = ((t4_i32) n * _currWidth + 7) >> 3; + + // use a special trick to mark sizes less than 1 byte in storage + if (fudge_ && 1 <= n && n <= 4 && (_currWidth & 7)) { + const int shiftPos = _currWidth == 4 ? 1 : 4 - _currWidth; + + static t4_byte fakeSizes [3][4] = { // n: 1: 2: 3: 4: + { 6, 1, 2, 2 }, // 4-bit entries: 4b 8b 12b 16b + { 5, 5, 1, 1 }, // 2-bit entries: 2b 4b 6b 8b + { 3, 3, 4, 5 }, // 1-bit entries: 1b 2b 3b 4b + }; + + // The idea is to use an "impossible" size (ie. 5, for n = 2) + // to give information about the current bit packing density. + d4_assert(needBytes <= 2); + needBytes = fakeSizes [shiftPos-1] [n-1]; + } + + t4_i32 currSize = ColSize(); + + if (needBytes < currSize) + RemoveData(needBytes, currSize - needBytes); + else if (needBytes > currSize) + InsertData(currSize, needBytes - currSize, true); +} + +///////////////////////////////////////////////////////////////////////////// + +bool c4_ColIter::Next() +{ + _pos += _len; + + _len = _column.AvailAt(_pos); + _ptr = _column.LoadNow(_pos); + + if (!_ptr) + _len = 0; + else if (_pos + _len >= _limit) + _len = _limit - _pos; + else { // 19990831 - optimization to avoid most copying + // while the end is adjacent to the next segment, extend it + while (_ptr + _len == _column.LoadNow(_pos + _len)) { + int n = _column.AvailAt(_pos + _len); + if (n == 0) + break; // may be a short column (strings) + + _len += n; + + if (_pos + _len >= _limit) { + _len = _limit - _pos; + break; + } + } + } + + return _len > 0; +} + +bool c4_ColIter::Next(int max_) +{ + _pos += _len; + + _len = _column.AvailAt(_pos); + _ptr = _column.LoadNow(_pos); + + if (!_ptr) + _len = 0; + else if (_pos + _len > _limit) + _len = _limit - _pos; + + if (_len <= 0) + return false; + + if (_len > max_) + _len = max_; + + return true; +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/column.h b/akregator/src/mk4storage/metakit/src/column.h new file mode 100644 index 000000000..3f6e4f157 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/column.h @@ -0,0 +1,212 @@ +// column.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Definition of the column classes + */ + +#ifndef __COLUMN_H__ +#define __COLUMN_H__ + +///////////////////////////////////////////////////////////////////////////// +// Declarations in this file + + class c4_Column; // a column in a table + class c4_ColIter; // an iterator over column data + class c4_ColCache; // manages a cache for columns + + class c4_Persist; // not defined here + class c4_Strategy; // not defined here + +///////////////////////////////////////////////////////////////////////////// + +class c4_Column +{ + c4_PtrArray _segments; + t4_i32 _position; + t4_i32 _size; + c4_Persist* _persist; + t4_i32 _gap; + int _slack; + bool _dirty; + +public: + c4_Column (c4_Persist* persist_); + //: Constructs a column using the specified persistence manager. + ~c4_Column (); + + void SetBuffer(t4_i32); + //: Allocate a new buffer of the specified size. + + c4_Persist* Persist() const; + //: Returns persistence manager for this column, or zero. + c4_Strategy& Strategy() const; + //: Returns the associated strategy pointer. + t4_i32 Position() const; + //: Special access for the DUMP program. + t4_i32 ColSize() const; + //: Returns the number of bytes as stored on disk. + bool IsDirty() const; + //: Returns true if contents needs to be saved. + + void SetLocation(t4_i32, t4_i32); + //: Sets the position and size of this column on file. + void PullLocation(const t4_byte*& ptr_); + //: Extract position and size of this column. + + int AvailAt(t4_i32 offset_) const; + //: Returns number of bytes we can access at once. + const t4_byte* LoadNow(t4_i32); + //: Makes sure the data is loaded into memory. + t4_byte* CopyNow(t4_i32); + //: Makes sure a copy of the data is in memory. + void Grow(t4_i32, t4_i32); + //: Grows the buffer by inserting space. + void Shrink(t4_i32, t4_i32); + //: Shrinks the buffer by removing space. + void SaveNow(c4_Strategy&, t4_i32 pos_); + //: Save the buffer to file. + + const t4_byte* FetchBytes(t4_i32 pos_, int len_, c4_Bytes& buffer_, bool forceCopy_); + //: Returns pointer to data, use buffer only if non-contiguous. + void StoreBytes(t4_i32 pos_, const c4_Bytes& buffer_); + //: Stores a copy of the buffer in the column. + + bool RequiresMap() const; + void ReleaseAllSegments(); + + static t4_i32 PullValue(const t4_byte*& ptr_); + static void PushValue(t4_byte*& ptr_, t4_i32 v_); + + void InsertData(t4_i32 index_, t4_i32 count_, bool clear_); + void RemoveData(t4_i32 index_, t4_i32 count_); + void RemoveGap(); + + enum { kSegBits = 12, kSegMax = 1 << kSegBits, kSegMask = kSegMax - 1 }; + +private: + static int fSegIndex(t4_i32 offset_); + static t4_i32 fSegOffset(int index_); + static int fSegRest(t4_i32 offset_); + + bool UsesMap(const t4_byte*) const; + bool IsMapped() const; + + void ReleaseSegment(int); + void SetupSegments(); + void Validate() const; + void FinishSlack(); + + void MoveGapUp(t4_i32 pos_); + void MoveGapDown(t4_i32 pos_); + void MoveGapTo(t4_i32 pos_); + + t4_byte* CopyData(t4_i32, t4_i32, int); +}; + +///////////////////////////////////////////////////////////////////////////// + +class c4_ColOfInts : public c4_Column +{ +public: + c4_ColOfInts (c4_Persist* persist_, int width_ =sizeof (t4_i32)); + + int RowCount() const; + void SetRowCount(int numRows_); + + void FlipBytes(); + + int ItemSize(int index_); + const void* Get(int index_, int& length_); + void Set(int index_, const c4_Bytes& buf_); + + t4_i32 GetInt(int index_); + void SetInt(int index_, t4_i32 value_); + + void Insert(int index_, const c4_Bytes& buf_, int count_); + void Remove(int index_, int count_); + + static int CalcAccessWidth(int numRows_, t4_i32 colSize_); + + void SetAccessWidth(int bits_); + void FixSize(bool fudge_); + void ForceFlip(); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); + +private: + typedef void (c4_ColOfInts::*tGetter) (int); + typedef bool (c4_ColOfInts::*tSetter) (int, const t4_byte*); + + void Get_0b(int index_); + void Get_1b(int index_); + void Get_2b(int index_); + void Get_4b(int index_); + void Get_8i(int index_); + void Get_16i(int index_); + void Get_16r(int index_); + void Get_32i(int index_); + void Get_32r(int index_); + void Get_64i(int index_); + void Get_64r(int index_); + + bool Set_0b(int index_, const t4_byte* item_); + bool Set_1b(int index_, const t4_byte* item_); + bool Set_2b(int index_, const t4_byte* item_); + bool Set_4b(int index_, const t4_byte* item_); + bool Set_8i(int index_, const t4_byte* item_); + bool Set_16i(int index_, const t4_byte* item_); + bool Set_16r(int index_, const t4_byte* item_); + bool Set_32i(int index_, const t4_byte* item_); + bool Set_32r(int index_, const t4_byte* item_); + bool Set_64i(int index_, const t4_byte* item_); + bool Set_64r(int index_, const t4_byte* item_); + + void ResizeData(int index_, int count_, bool clear_ =false); + + tGetter _getter; + tSetter _setter; + + union { + t4_byte _item[8]; // holds temp result (careful with alignment!) + double _aligner; // needed for SPARC + }; + + int _currWidth; // number of bits used for one entry (0..64) + int _dataWidth; // number of bytes used for passing a value along + int _numRows; + bool _mustFlip; +}; + +///////////////////////////////////////////////////////////////////////////// + +class c4_ColIter +{ + c4_Column& _column; + t4_i32 _limit; + t4_i32 _pos; + int _len; + const t4_byte* _ptr; + +public: + c4_ColIter (c4_Column& col_, t4_i32 offset_, t4_i32 limit_); +// ~c4_ColIter (); + + bool Next(); + bool Next(int max_); + + const t4_byte* BufLoad() const; + t4_byte* BufSave(); + int BufLen() const; +}; + +///////////////////////////////////////////////////////////////////////////// + +#if q4_INLINE +#include "column.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/akregator/src/mk4storage/metakit/src/column.inl b/akregator/src/mk4storage/metakit/src/column.inl new file mode 100644 index 000000000..58f34437e --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/column.inl @@ -0,0 +1,89 @@ +// column.inl -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Inlined members of the column classes + */ + +///////////////////////////////////////////////////////////////////////////// +// c4_Column + +d4_inline int c4_Column::fSegIndex(t4_i32 offset_) +{ + // limited by max array: 1 << (kSegBits + 15) with 16-bit ints + return (int) (offset_ >> kSegBits); +} + +d4_inline t4_i32 c4_Column::fSegOffset(int index_) +{ + return (t4_i32) index_ << kSegBits; +} + +d4_inline int c4_Column::fSegRest(t4_i32 offset_) +{ + return ((int) offset_ & kSegMask); +} + +d4_inline c4_Persist* c4_Column::Persist() const +{ + return _persist; +} + +d4_inline t4_i32 c4_Column::Position() const +{ + return _position; +} + +d4_inline t4_i32 c4_Column::ColSize() const +{ + return _size; +} + +d4_inline bool c4_Column::IsDirty() const +{ + return _dirty; +} + +d4_inline void c4_Column::SetBuffer(t4_i32 length_) +{ + SetLocation(0, length_); + _dirty = true; +} + +d4_inline const t4_byte* c4_Column::LoadNow(t4_i32 offset_) +{ + if (_segments.GetSize() == 0) + SetupSegments(); + + if (offset_ >= _gap) + offset_ += _slack; + + t4_byte* ptr = (t4_byte*) _segments.GetAt(fSegIndex(offset_)); + return ptr + fSegRest(offset_); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_ColIter + +d4_inline c4_ColIter::c4_ColIter (c4_Column& col_, t4_i32 offset_, t4_i32 limit_) + : _column (col_), _limit (limit_), _pos (offset_), _len (0), _ptr (0) +{ +} + +d4_inline const t4_byte* c4_ColIter::BufLoad() const +{ + return _ptr; +} + +d4_inline t4_byte* c4_ColIter::BufSave() +{ + return _column.CopyNow(_pos); +} + +d4_inline int c4_ColIter::BufLen() const +{ + return _len; +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/custom.cpp b/akregator/src/mk4storage/metakit/src/custom.cpp new file mode 100644 index 000000000..a6275cea7 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/custom.cpp @@ -0,0 +1,1066 @@ +// custom.cpp -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Implementation of many custom viewer classes + */ + +#include "header.h" + +#include "custom.h" +#include "format.h" + +///////////////////////////////////////////////////////////////////////////// + +class c4_CustomHandler : public c4_Handler +{ + c4_CustomSeq* _seq; + +public: + c4_CustomHandler (const c4_Property& prop_, c4_CustomSeq* seq_); + virtual ~c4_CustomHandler (); + + virtual int ItemSize(int index_); + virtual const void* Get(int index_, int& length_); + virtual void Set(int index_, const c4_Bytes& buf_); + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_); + virtual void Remove(int index_, int count_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_CustomHandler::c4_CustomHandler (const c4_Property& prop_, + c4_CustomSeq* seq_) + : c4_Handler (prop_), _seq (seq_) +{ + d4_assert(_seq != 0); +} + +c4_CustomHandler::~c4_CustomHandler () +{ +} + +int c4_CustomHandler::ItemSize(int index_) +{ + c4_Bytes& buf = _seq->Buffer(); + + int colnum = _seq->PropIndex(Property().GetId()); + d4_assert(colnum >= 0); + + if (!_seq->DoGet(index_, colnum, buf)) + return 0; + + return buf.Size(); +} + +const void* c4_CustomHandler::Get(int index_, int& length_) +{ + c4_Bytes& buf = _seq->Buffer(); + + int colnum = _seq->PropIndex(Property().GetId()); + d4_assert(colnum >= 0); + + if (!_seq->DoGet(index_, colnum, buf)) + ClearBytes(buf); + + length_ = buf.Size(); + return buf.Contents(); +} + +void c4_CustomHandler::Set(int index_, const c4_Bytes& buf_) +{ + int colnum = _seq->PropIndex(Property().GetId()); + d4_assert(colnum >= 0); + + _seq->DoSet(index_, colnum, buf_); +} + +void c4_CustomHandler::Insert(int, const c4_Bytes&, int) +{ + d4_assert(0); //! not yet +} + +void c4_CustomHandler::Remove(int, int) +{ + d4_assert(0); //! not yet +} + +c4_Handler* c4_CustomSeq::CreateHandler(const c4_Property& prop_) +{ + return d4_new c4_CustomHandler (prop_, this); +} + +///////////////////////////////////////////////////////////////////////////// + +c4_CustomSeq::c4_CustomSeq (c4_CustomViewer* viewer_) + : c4_HandlerSeq (0), _viewer (viewer_), _inited (false) +{ + d4_assert(_viewer != 0); + + // set up handlers to match a template obtained from the viewer + c4_View v = viewer_->GetTemplate(); + + for (int i = 0; i < v.NumProperties(); ++i) + PropIndex(v.NthProperty(i)); + + _inited = true; +} + +c4_CustomSeq::~c4_CustomSeq () +{ + delete _viewer; +} + +int c4_CustomSeq::NumRows() const +{ + return _inited ? _viewer->GetSize() : 0; +} + +bool c4_CustomSeq::RestrictSearch(c4_Cursor cursor_, int& pos_, int& count_) +{ + if (count_ > 0) + { + int n; + int o = _viewer->Lookup(cursor_, n); + // a -1 result means: "don't know, please scan all" + if (o < 0) + return count_ > 0; + + if (n > 0) + { + if (pos_ < o) + { + count_ -= o - pos_; + pos_ = o; + } + + if (pos_ + count_ > o + n) + count_ = o + n - pos_; + + if (count_ > 0) + return true; + } + } + + count_ = 0; + return false; +} + +void c4_CustomSeq::InsertAt(int p_, c4_Cursor c_, int n_) +{ + _viewer->InsertRows(p_, c_, n_); +} + +void c4_CustomSeq::RemoveAt(int p_, int n_) +{ + _viewer->RemoveRows(p_, n_); +} + +void c4_CustomSeq::Move(int, int) +{ + d4_assert(false); //! not yet +} + +bool c4_CustomSeq::DoGet(int row_, int col_, c4_Bytes& buf_) const +{ + d4_assert(_inited); + + return _viewer->GetItem(row_, col_, buf_); +} + +void c4_CustomSeq::DoSet(int row_, int col_, const c4_Bytes& buf_) +{ + d4_assert(_inited); + + d4_dbgdef(const bool f =) + _viewer->SetItem(row_, col_, buf_); + d4_assert(f); +} + +///////////////////////////////////////////////////////////////////////////// + +/** @class c4_CustomViewer + * + * Abstract base class for definition of custom views. + * + * A custom view is a view which can be accessed like any other view, using + * row and property operations, but which is fully managed by a customized + * "viewer" class. The viewer will eventually handle all requests for the + * view, such as defining its structure and size, as well as providing the + * actual data values when requested. + * + * Custom views cannot propagate changes. + * + * To implement a custom view, you must derive your viewer from this base + * class and define each of the virtual members. Then create a new object + * of this type on the heap and pass it to the c4_View constructor. Your + * viewer will automatically be destroyed when the last reference to its + * view goes away. See the DBF2MK sample code for an example of a viewer. + */ + +c4_CustomViewer::~c4_CustomViewer () +{ +} + + /// Locate a row in this view, try to use native searches +int c4_CustomViewer::Lookup(c4_Cursor, int& count_) +{ + count_ = GetSize(); + return 0; // not implemented, return entire view range +} + + /// Store one data item, supplied as a generic data value +bool c4_CustomViewer::SetItem(int, int, const c4_Bytes&) +{ + return false; // default is not modifiable +} + + /// Insert one or more copies of a row (if possible) +bool c4_CustomViewer::InsertRows(int, c4_Cursor, int) +{ + return false; // default is not modifiable +} + + /// Remove one or more rows (this is not always possible) +bool c4_CustomViewer::RemoveRows(int, int) +{ + return false; // default is not modifiable +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_SliceViewer : public c4_CustomViewer +{ + c4_View _parent; + int _first, _limit, _step; + +public: + c4_SliceViewer (c4_Sequence& seq_, int first_, int limit_, int step_); + virtual ~c4_SliceViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + bool SetItem(int row_, int col_, const c4_Bytes& buf_); + virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); + virtual bool RemoveRows(int pos_, int count_=1); +}; + +c4_SliceViewer::c4_SliceViewer (c4_Sequence& seq_, int first_, int limit_, int step_) + : _parent (&seq_), _first (first_), _limit (limit_), _step (step_) +{ + d4_assert(_step != 0); +} + +c4_SliceViewer::~c4_SliceViewer () +{ +} + +c4_View c4_SliceViewer::GetTemplate() +{ + return _parent.Clone(); // could probably return _parent just as well +} + +int c4_SliceViewer::GetSize() +{ + int n = _limit >= 0 ? _limit : _parent.GetSize(); + if (n < _first) + n = _first; + + int k = _step < 0 ? -_step : _step; + return (n - _first + k - 1) / k; +} + +bool c4_SliceViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + row_ = _first + _step * (_step > 0 ? row_ : row_ - GetSize() + 1); + + return _parent.GetItem(row_, col_, buf_); +} + +bool c4_SliceViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + row_ = _first + _step * (_step > 0 ? row_ : row_ - GetSize() + 1); + + _parent.SetItem(row_, col_, buf_); + return true; +} + +bool c4_SliceViewer::InsertRows(int pos_, c4_Cursor value_, int count_) +{ + if (_step != 1) + return false; + + pos_ = _first + _step * (_step > 0 ? pos_ : pos_ - GetSize() + 1); + if (_limit >= 0) + _limit += count_; + + _parent.InsertAt(pos_, *value_, count_); + return true; +} + +bool c4_SliceViewer::RemoveRows(int pos_, int count_) +{ + if (_step != 1) + return false; + + pos_ = _first + _step * (_step > 0 ? pos_ : pos_ - GetSize() + 1); + if (_limit >= 0) + _limit -= count_; + + _parent.RemoveAt(pos_, count_); + return true; +} + +c4_CustomViewer* f4_CustSlice(c4_Sequence& seq_, int first_, int limit_, int step_) +{ + return d4_new c4_SliceViewer (seq_, first_, limit_, step_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_ProductViewer : public c4_CustomViewer +{ + c4_View _parent, _argView, _template; + +public: + c4_ProductViewer (c4_Sequence& seq_, const c4_View& view_); + virtual ~c4_ProductViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); +}; + +c4_ProductViewer::c4_ProductViewer (c4_Sequence& seq_, const c4_View& view_) + : _parent (&seq_), _argView (view_), _template (_parent.Clone()) +{ + for (int i = 0; i < _argView.NumProperties(); ++i) + _template.AddProperty(_argView.NthProperty(i)); +} + +c4_ProductViewer::~c4_ProductViewer () +{ +} + +c4_View c4_ProductViewer::GetTemplate() +{ + return _template; +} + +int c4_ProductViewer::GetSize() +{ + return _parent.GetSize() * _argView.GetSize(); +} + +bool c4_ProductViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + + if (col_ < v.NumProperties()) + { + row_ /= _argView.GetSize(); + } + else + { + v = _argView; + row_ %= _argView.GetSize(); + col_ = v.FindProperty(_template.NthProperty(col_).GetId()); + + d4_assert(col_ >= 0); + } + + return v.GetItem(row_, col_, buf_); +} + +c4_CustomViewer* f4_CustProduct(c4_Sequence& seq_, const c4_View& view_) +{ + return d4_new c4_ProductViewer (seq_, view_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_RemapWithViewer : public c4_CustomViewer +{ + c4_View _parent, _argView; + +public: + c4_RemapWithViewer (c4_Sequence& seq_, const c4_View& view_); + virtual ~c4_RemapWithViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + bool SetItem(int row_, int col_, const c4_Bytes& buf_); +}; + +c4_RemapWithViewer::c4_RemapWithViewer (c4_Sequence& seq_, const c4_View& view_) + : _parent (&seq_), _argView (view_) +{ +} + +c4_RemapWithViewer::~c4_RemapWithViewer () +{ +} + +c4_View c4_RemapWithViewer::GetTemplate() +{ + return _parent.Clone(); // could probably return _parent just as well +} + +int c4_RemapWithViewer::GetSize() +{ + return _argView.GetSize(); +} + +bool c4_RemapWithViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + const c4_Property& map = _argView.NthProperty(0); + d4_assert(map.Type() == 'I'); + + row_ = ((const c4_IntProp&) map) (_argView[row_]); + + return _parent.GetItem(row_, col_, buf_); +} + +bool c4_RemapWithViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + const c4_Property& map = _argView.NthProperty(0); + d4_assert(map.Type() == 'I'); + + row_ = ((const c4_IntProp&) map) (_argView[row_]); + + _parent.SetItem(row_, col_, buf_); + return true; +} + +c4_CustomViewer* f4_CustRemapWith(c4_Sequence& seq_, const c4_View& view_) +{ + return d4_new c4_RemapWithViewer (seq_, view_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_PairViewer : public c4_CustomViewer +{ + c4_View _parent, _argView, _template; + +public: + c4_PairViewer (c4_Sequence& seq_, const c4_View& view_); + virtual ~c4_PairViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + bool SetItem(int row_, int col_, const c4_Bytes& buf_); + virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); + virtual bool RemoveRows(int pos_, int count_=1); +}; + +c4_PairViewer::c4_PairViewer (c4_Sequence& seq_, const c4_View& view_) + : _parent (&seq_), _argView (view_), _template (_parent.Clone()) +{ + for (int i = 0; i < _argView.NumProperties(); ++i) + _template.AddProperty(_argView.NthProperty(i)); +} + +c4_PairViewer::~c4_PairViewer () +{ +} + +c4_View c4_PairViewer::GetTemplate() +{ + return _template; +} + +int c4_PairViewer::GetSize() +{ + return _parent.GetSize(); +} + +bool c4_PairViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + + if (col_ >= v.NumProperties()) + { + v = _argView; + col_ = v.FindProperty(_template.NthProperty(col_).GetId()); + d4_assert(col_ >= 0); + } + + return v.GetItem(row_, col_, buf_); +} + +bool c4_PairViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + c4_View v = _parent; + + if (col_ >= v.NumProperties()) + { + v = _argView; + col_ = v.FindProperty(_template.NthProperty(col_).GetId()); + d4_assert(col_ >= 0); + } + + v.SetItem(row_, col_, buf_); + return true; +} + +bool c4_PairViewer::InsertRows(int pos_, c4_Cursor value_, int count_) +{ + _parent.InsertAt(pos_, *value_, count_); + _argView.InsertAt(pos_, *value_, count_); + return true; +} + +bool c4_PairViewer::RemoveRows(int pos_, int count_) +{ + _parent.RemoveAt(pos_, count_); + _argView.RemoveAt(pos_, count_); + return true; +} + +c4_CustomViewer* f4_CustPair(c4_Sequence& seq_, const c4_View& view_) +{ + return d4_new c4_PairViewer (seq_, view_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_ConcatViewer : public c4_CustomViewer +{ + c4_View _parent, _argView; + +public: + c4_ConcatViewer (c4_Sequence& seq_, const c4_View& view_); + virtual ~c4_ConcatViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + bool SetItem(int row_, int col_, const c4_Bytes& buf_); +}; + +c4_ConcatViewer::c4_ConcatViewer (c4_Sequence& seq_, const c4_View& view_) + : _parent (&seq_), _argView (view_) +{ +} + +c4_ConcatViewer::~c4_ConcatViewer () +{ +} + +c4_View c4_ConcatViewer::GetTemplate() +{ + return _parent.Clone(); // could probably return _parent just as well +} + +int c4_ConcatViewer::GetSize() +{ + return _parent.GetSize() + _argView.GetSize(); +} + +bool c4_ConcatViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + + if (row_ >= _parent.GetSize()) + { + v = _argView; + row_ -= _parent.GetSize(); + col_ = v.FindProperty(_parent.NthProperty(col_).GetId()); + + if (col_ < 0) + return false; + } + + return v.GetItem(row_, col_, buf_); +} + +bool c4_ConcatViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + c4_View v = _parent; + + if (row_ >= _parent.GetSize()) + { + v = _argView; + row_ -= _parent.GetSize(); + col_ = v.FindProperty(_parent.NthProperty(col_).GetId()); + d4_assert(col_ >= 0); + } + + v.SetItem(row_, col_, buf_); + return true; +} + +c4_CustomViewer* f4_CustConcat(c4_Sequence& seq_, const c4_View& view_) +{ + return d4_new c4_ConcatViewer (seq_, view_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_RenameViewer : public c4_CustomViewer +{ + c4_View _parent, _template; + +public: + c4_RenameViewer (c4_Sequence& seq_, const c4_Property& old_, + const c4_Property& new_); + virtual ~c4_RenameViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_); + //virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); + //virtual bool RemoveRows(int pos_, int count_=1); +}; + +c4_RenameViewer::c4_RenameViewer (c4_Sequence& seq_, const c4_Property& old_, + const c4_Property& new_) + : _parent (&seq_) +{ + for (int i = 0; i < _parent.NumProperties(); ++i) + { + const c4_Property& prop = _parent.NthProperty(i); + _template.AddProperty(prop.GetId() == old_.GetId() ? new_ : prop); + } +} + +c4_RenameViewer::~c4_RenameViewer () +{ +} + +c4_View c4_RenameViewer::GetTemplate() +{ + return _template; +} + +int c4_RenameViewer::GetSize() +{ + return _parent.GetSize(); +} + +bool c4_RenameViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + return _parent.GetItem(row_, col_, buf_); +} + +bool c4_RenameViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + _parent.SetItem(row_, col_, buf_); + return true; +} + +c4_CustomViewer* f4_CustRename(c4_Sequence& seq_, const c4_Property& old_, + const c4_Property& new_) +{ + return d4_new c4_RenameViewer (seq_, old_, new_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_GroupByViewer : public c4_CustomViewer +{ + c4_View _parent, _keys, _sorted, _temp; + c4_Property _result; + c4_DWordArray _map; + + int ScanTransitions(int lo_, int hi_, + t4_byte* flags_, const c4_View& match_) const; + +public: + c4_GroupByViewer (c4_Sequence& seq_, const c4_View& keys_, + const c4_Property& result_); + virtual ~c4_GroupByViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); +}; + +c4_GroupByViewer::c4_GroupByViewer (c4_Sequence& seq_, const c4_View& keys_, + const c4_Property& result_) + : _parent (&seq_), _keys (keys_), _result (result_) +{ + _sorted = _parent.SortOn(_keys); + int n = _sorted.GetSize(); + + c4_Bytes temp; + t4_byte* buf = temp.SetBufferClear(n); + + int groups = 0; + if (n > 0) + { + ++buf[0]; // the first entry is always a transition + groups = 1 + ScanTransitions(1, n, buf, _sorted.Project(_keys)); + } + + // set up a map pointing to each transition + _map.SetSize(groups + 1); + int j = 0; + + for (int i = 0; i < n; ++i) + if (buf[i]) + _map.SetAt(j++, i); + + // also append an entry to point just past the end + _map.SetAt(j, n); + + d4_assert(_map.GetAt(0) == 0); + d4_assert(j == groups); +} + +c4_GroupByViewer::~c4_GroupByViewer () +{ +} + +int c4_GroupByViewer::ScanTransitions(int lo_, int hi_, + t4_byte* flags_, const c4_View& match_) const +{ + d4_assert(lo_ > 0); + + int m = hi_ - lo_; + d4_assert(m >= 0); + + // done if nothing left or if entire range is identical + if (m == 0 || match_ [lo_-1] == match_ [hi_-1]) + return 0; + + // range has a transition, done if it is exactly of size one + if (m == 1) + { + ++(flags_[lo_]); + return 1; + } + + // use binary splitting if the range has enough entries + if (m >= 5) + return ScanTransitions(lo_, lo_ + m / 2, flags_, match_) + + ScanTransitions(lo_ + m / 2, hi_, flags_, match_); + + // else use a normal linear scan + int n = 0; + + for (int i = lo_; i < hi_; ++i) + if (match_ [i] != match_ [i-1]) + { + ++(flags_[i]); + ++n; + } + + return n; +} + +c4_View c4_GroupByViewer::GetTemplate() +{ + c4_View v = _keys.Clone(); + v.AddProperty(_result); + + return v; +} + +int c4_GroupByViewer::GetSize() +{ + d4_assert(_map.GetSize() > 0); + + return _map.GetSize() - 1; +} + +bool c4_GroupByViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + if (col_ < _keys.NumProperties()) + return _sorted.GetItem(_map.GetAt(row_), col_, buf_); + + d4_assert(col_ == _keys.NumProperties()); + + t4_i32 count; + switch (_result.Type()) + { + case 'I': count = _map.GetAt(row_ + 1) - _map.GetAt(row_); + buf_ = c4_Bytes (&count, sizeof count, true); + break; + case 'V': _temp = _sorted.Slice(_map.GetAt(row_), _map.GetAt(row_ + 1)) + .ProjectWithout(_keys); + buf_ = c4_Bytes (&_temp, sizeof _temp, true); + break; + default: d4_assert(0); + } + + return true; +} + +c4_CustomViewer* f4_CustGroupBy(c4_Sequence& seq_, const c4_View& template_, + const c4_Property& result_) +{ + return d4_new c4_GroupByViewer (seq_, template_, result_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_JoinPropViewer : public c4_CustomViewer +{ + c4_View _parent, _template; + c4_ViewProp _sub; + int _subPos, _subWidth; + c4_DWordArray _base, _offset; + +public: + c4_JoinPropViewer (c4_Sequence& seq_, const c4_ViewProp& sub_, bool outer_); + virtual ~c4_JoinPropViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); +}; + +c4_JoinPropViewer::c4_JoinPropViewer (c4_Sequence& seq_, + const c4_ViewProp& sub_, bool outer_) + : _parent (&seq_), + _sub (sub_), _subPos (_parent.FindProperty(sub_.GetId())), _subWidth (0) +{ + d4_assert(_subPos >= 0); + + for (int k = 0; k < _parent.NumProperties(); ++k) + { + if (k != _subPos) + _template.AddProperty(_parent.NthProperty(k)); + else // if there are no rows, then this join does very little anyway + //! OOPS: if this is an unattached view, then the subviews can differ + if (_parent.GetSize() > 0) + { + c4_View view = sub_ (_parent[0]); + for (int l = 0; l < view.NumProperties(); ++l) + { + _template.AddProperty(view.NthProperty(l)); + ++_subWidth; + } + } + } + + _base.SetSize(0, 5); + _offset.SetSize(0, 5); + + for (int i = 0; i < _parent.GetSize(); ++i) + { + c4_View v = _sub (_parent[i]); + + int n = v.GetSize(); + if (n == 0 && outer_) + { + _base.Add(i); + _offset.Add(~ (t4_i32) 0); // special null entry for outer joins + } + else + for (int j = 0; j < n; ++j) + { + _base.Add(i); + _offset.Add(j); + } + } +} + +c4_JoinPropViewer::~c4_JoinPropViewer () +{ +} + +c4_View c4_JoinPropViewer::GetTemplate() +{ + return _template; +} + +int c4_JoinPropViewer::GetSize() +{ + return _base.GetSize(); +} + +bool c4_JoinPropViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + int r = _base.GetAt(row_); + + if (col_ >= _subPos) + if (col_ >= _subPos + _subWidth) + { + col_ -= _subWidth - 1; + } + else + { + v = _sub (_parent[r]); + r = _offset.GetAt(row_); + if (r < 0) + return false; // if this is a null row in an outer join + + col_ = v.FindProperty(_template.NthProperty(col_).GetId()); + if (col_ < 0) + return false; // if subview doesn't have all properties + } + + return v.GetItem(r, col_, buf_); +} + +c4_CustomViewer* f4_CustJoinProp(c4_Sequence& seq_, + const c4_ViewProp& sub_, bool outer_) +{ + return d4_new c4_JoinPropViewer (seq_, sub_, outer_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_JoinViewer : public c4_CustomViewer +{ + c4_View _parent, _argView, _template; + c4_DWordArray _base, _offset; + +public: + c4_JoinViewer (c4_Sequence& seq_, const c4_View& keys_, + const c4_View& view_, bool outer_); + virtual ~c4_JoinViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); +}; + +c4_JoinViewer::c4_JoinViewer (c4_Sequence& seq_,const c4_View& keys_, + const c4_View& view_, bool outer_) + : _parent (&seq_), _argView (view_.SortOn(keys_)) +{ + // why not in GetTemplate, since we don't need to know this... + _template = _parent.Clone(); + for (int l = 0; l < _argView.NumProperties(); ++l) + _template.AddProperty(_argView.NthProperty(l)); + + c4_View sorted = _parent.SortOn(keys_).Project(keys_); + c4_View temp = _argView.Project(keys_); + + _base.SetSize(0, 5); + _offset.SetSize(0, 5); + + int j = 0, n = 0; + + for (int i = 0; i < sorted.GetSize(); ++i) + { + int orig = _parent.GetIndexOf(sorted[i]); + d4_assert(orig >= 0); + + if (i > 0 && sorted[i] == sorted[i-1]) + { + // if last key was same, repeat the same join + int last = _offset.GetSize() - n; + for (int k = 0; k < n; ++k) + { + _base.Add(orig); + _offset.Add(_offset.GetAt(last + k)); + } + } + else // no, this is a new combination + { + bool match = false; + + // advance until the temp view entry is >= this sorted entry + while (j < temp.GetSize()) + if (sorted[i] <= temp[j]) + { + match = sorted[i] == temp[j]; + break; + } + else + ++j; + + n = 0; + + if (match) + { + do { + _base.Add(orig); + _offset.Add(j); + ++n; + } while (++j < temp.GetSize() && temp[j] == temp[j-1]); + } + else if (outer_) + { + // no match, add an entry anyway if this is an outer join + _base.Add(orig); + _offset.Add(~ (t4_i32) 0); // special null entry + ++n; + } + } + } +} + +c4_JoinViewer::~c4_JoinViewer () +{ +} + +c4_View c4_JoinViewer::GetTemplate() +{ + return _template; +} + +int c4_JoinViewer::GetSize() +{ + return _base.GetSize(); +} + +bool c4_JoinViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + int r = _base.GetAt(row_); + + if (col_ >= v.NumProperties()) + { + v = _argView; + r = _offset.GetAt(row_); + if (r < 0) + return false; // if this is a null row in an outer join + + col_ = v.FindProperty(_template.NthProperty(col_).GetId()); + if (col_ < 0) + return false; // if second view doesn't have all properties + } + + return v.GetItem(r, col_, buf_); +} + +#if 0 +bool c4_JoinViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + c4_View v = _parent; + + int o = 0; + int r = _offset.GetAt(row_); + + if (r < 0) + { + o = ~r; + if (o == 0) + return false; // if this is a null row in an outer join + r -= o; + } + + if (col_ >= v.NumProperties()) + { + v = _argView; + r = _o; + + col_ = v.FindProperty(_template.NthProperty(col_)); + if (col_ < 0) + return false; // if second view doesn't have all properties + } + + return v.GetItem(r, col_, buf_); +} +#endif + +c4_CustomViewer* f4_CustJoin(c4_Sequence& seq_, const c4_View& keys_, + const c4_View& view_, bool outer_) +{ + return d4_new c4_JoinViewer (seq_, keys_, view_, outer_); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/custom.h b/akregator/src/mk4storage/metakit/src/custom.h new file mode 100644 index 000000000..80555d3b6 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/custom.h @@ -0,0 +1,63 @@ +// custom.h -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Encapsulation of many custom viewer classes + */ + +#ifndef __CUSTOM_H__ +#define __CUSTOM_H__ + +#ifndef __FIELD_H__ +#include "field.h" +#endif +#ifndef __STORE_H__ +#include "handler.h" +#endif + +///////////////////////////////////////////////////////////////////////////// + +class c4_CustomSeq : public c4_HandlerSeq +{ + c4_CustomViewer* _viewer; + bool _inited; + +public: + c4_CustomSeq (c4_CustomViewer* viewer_); + virtual ~c4_CustomSeq (); + + virtual int NumRows() const; + + virtual bool RestrictSearch(c4_Cursor, int&, int&); + + virtual void InsertAt(int, c4_Cursor, int =1); + virtual void RemoveAt(int, int =1); + virtual void Move(int from_, int); + + bool DoGet(int row_, int col_, c4_Bytes& buf_) const; + void DoSet(int row_, int col_, const c4_Bytes& buf_); + +private: // this *is* used, as override + virtual c4_Handler* CreateHandler(const c4_Property&); +}; + +///////////////////////////////////////////////////////////////////////////// + + extern c4_CustomViewer* f4_CustSlice(c4_Sequence&, int, int , int); + extern c4_CustomViewer* f4_CustProduct(c4_Sequence&, const c4_View&); + extern c4_CustomViewer* f4_CustRemapWith(c4_Sequence&, const c4_View&); + extern c4_CustomViewer* f4_CustPair(c4_Sequence&, const c4_View&); + extern c4_CustomViewer* f4_CustConcat(c4_Sequence&, const c4_View&); + extern c4_CustomViewer* f4_CustRename(c4_Sequence&, + const c4_Property&, const c4_Property&); + extern c4_CustomViewer* f4_CustGroupBy(c4_Sequence&, + const c4_View&, const c4_Property&); + extern c4_CustomViewer* f4_CustJoinProp(c4_Sequence&, + const c4_ViewProp&, bool); + extern c4_CustomViewer* f4_CustJoin(c4_Sequence&, + const c4_View&, const c4_View&, bool); + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/akregator/src/mk4storage/metakit/src/derived.cpp b/akregator/src/mk4storage/metakit/src/derived.cpp new file mode 100644 index 000000000..3baf5e3e7 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/derived.cpp @@ -0,0 +1,1003 @@ +// derived.cpp -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Derived views are virtual views which track changes + */ + +#include "header.h" +#include "handler.h" +#include "store.h" +#include "derived.h" + +#include <stdlib.h> // qsort + +///////////////////////////////////////////////////////////////////////////// +// Implemented in this file + +// class c4_Sequence; + class c4_DerivedSeq; + class c4_FilterSeq; + class c4_SortSeq; + class c4_ProjectSeq; + +///////////////////////////////////////////////////////////////////////////// + +class c4_FilterSeq : public c4_DerivedSeq +{ +protected: + c4_DWordArray _rowMap; + c4_DWordArray _revMap; + c4_Row _lowRow; + c4_Row _highRow; + c4_Bytes _rowIds; + +protected: + c4_FilterSeq (c4_Sequence& seq_); + virtual ~c4_FilterSeq (); + + void FixupReverseMap(); + int PosInMap(int index_) const; + bool Match(int index_, c4_Sequence& seq_, + const int* =0, const int* =0) const; + bool MatchOne(int prop_, const c4_Bytes& data_) const; + +public: + c4_FilterSeq (c4_Sequence& seq_, c4_Cursor low_, c4_Cursor high_); + + virtual int RemapIndex(int, const c4_Sequence*) const; + + virtual int NumRows() const; + + virtual int Compare(int, c4_Cursor) const; + virtual bool Get(int, int, c4_Bytes&); + + virtual void InsertAt(int, c4_Cursor, int =1); + virtual void RemoveAt(int, int =1); + virtual void Set(int, const c4_Property&, const c4_Bytes&); + virtual void SetSize(int); + + virtual c4_Notifier* PreChange(c4_Notifier& nf_); + virtual void PostChange(c4_Notifier& nf_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FilterSeq::c4_FilterSeq (c4_Sequence& seq_) + : c4_DerivedSeq (seq_) +{ + _rowMap.SetSize(_seq.NumRows()); + _revMap.SetSize(_seq.NumRows()); + d4_assert(NumRows() == _seq.NumRows()); + + for (int i = 0; i < NumRows(); ++i) + { + _rowMap.SetAt(i, i); + _revMap.SetAt(i, i); + } +} + +c4_FilterSeq::c4_FilterSeq (c4_Sequence& seq_, c4_Cursor low_, + c4_Cursor high_) + : c4_DerivedSeq (seq_), _lowRow (*low_), _highRow (*high_) +{ + d4_assert((&_lowRow)._index == 0); + d4_assert((&_highRow)._index == 0); + + // use a sneaky way to obtain the sequence pointers and indices + c4_Sequence* lowSeq = (& _lowRow)._seq; + c4_Sequence* highSeq = (& _highRow)._seq; + d4_assert(lowSeq && highSeq); + + // prepare column numbers to avoid looking them up on every row + // lowCols is a vector of column numbers to use for the low limits + // highCols is a vector of column numbers to use for the high limits + int nl = lowSeq->NumHandlers(), nh = highSeq->NumHandlers(); + c4_Bytes lowVec, highVec; + int* lowCols = (int*) lowVec.SetBufferClear(nl * sizeof (int)); + int* highCols = (int*) highVec.SetBufferClear(nh * sizeof (int)); + + for (int il = 0; il < nl; ++il) + lowCols[il] = seq_.PropIndex(lowSeq->NthPropId(il)); + for (int ih = 0; ih < nh; ++ih) + highCols[ih] = seq_.PropIndex(highSeq->NthPropId(ih)); + + // set _rowIds flag buffer for fast matching + { + int max = -1; + + { + for (int i1 = 0; i1 < nl; ++i1) + { + int n = lowSeq->NthPropId(i1); + if (max < n) + max = n; + } + for (int i2 = 0; i2 < nh; ++i2) + { + int n = highSeq->NthPropId(i2); + if (max < n) + max = n; + } + } + + t4_byte* p = _rowIds.SetBufferClear(max + 1); + + { + for (int i1 = 0; i1 < nl; ++i1) + p[lowSeq->NthPropId(i1)] |= 1; + for (int i2 = 0; i2 < nh; ++i2) + p[highSeq->NthPropId(i2)] |= 2; + } + } + + // now go through all rows and select the ones that are in range + + _rowMap.SetSize(_seq.NumRows()); // avoid growing, use safe upper bound + + int n = 0; + + for (int i = 0; i < _seq.NumRows(); ++i) + if (Match(i, _seq, lowCols, highCols)) + _rowMap.SetAt(n++, i); + + _rowMap.SetSize(n); + + FixupReverseMap(); +} + +c4_FilterSeq::~c4_FilterSeq () +{ +} + +void c4_FilterSeq::FixupReverseMap() +{ + int n = _seq.NumRows(); + + _revMap.SetSize(0); + + if (n > 0) + { + _revMap.InsertAt(0, ~ (t4_i32) 0, n); //! + + for (int i = 0; i < _rowMap.GetSize(); ++i) + _revMap.SetAt((int) _rowMap.GetAt(i), i); + } +} + +bool c4_FilterSeq::MatchOne(int prop_, const c4_Bytes& data_) const +{ + d4_assert(prop_ < _rowIds.Size()); + + t4_byte flag = _rowIds.Contents()[prop_]; + d4_assert(flag); + + if (flag & 1) + { + c4_Sequence* lowSeq = (& _lowRow)._seq; + + c4_Handler& h = lowSeq->NthHandler(lowSeq->PropIndex(prop_)); + if (h.Compare(0, data_) > 0) + return false; + } + + if (flag & 2) + { + c4_Sequence* highSeq = (& _highRow)._seq; + + c4_Handler& h = highSeq->NthHandler(highSeq->PropIndex(prop_)); + if (h.Compare(0, data_) < 0) + return false; + } + + return true; +} + +bool c4_FilterSeq::Match(int index_, c4_Sequence& seq_, + const int* lowCols_, const int* highCols_) const +{ + // use a sneaky way to obtain the sequence pointers and indices + c4_Sequence* lowSeq = (& _lowRow)._seq; + c4_Sequence* highSeq = (& _highRow)._seq; + d4_assert(lowSeq && highSeq); + + int nl = lowSeq->NumHandlers(), nh = highSeq->NumHandlers(); + + c4_Bytes data; + + // check each of the lower limits + for (int cl = 0; cl < nl; ++cl) + { + c4_Handler& hl = lowSeq->NthHandler(cl); + + int n = lowCols_ ? lowCols_[cl] + : seq_.PropIndex(lowSeq->NthPropId(cl)); + if (n >= 0) + { + c4_Handler& h = seq_.NthHandler(n); + const c4_Sequence* hc = seq_.HandlerContext(n); + int i = seq_.RemapIndex(index_, hc); + + h.GetBytes(i, data); + } + else + hl.ClearBytes(data); + + if (hl.Compare(0, data) > 0) + return false; + } + + // check each of the upper limits + for (int ch = 0; ch < nh; ++ch) + { + c4_Handler& hh = highSeq->NthHandler(ch); + + int n = highCols_ ? highCols_[ch] + : seq_.PropIndex(highSeq->NthPropId(ch)); + if (n >= 0) + { + c4_Handler& h = seq_.NthHandler(n); + const c4_Sequence* hc = seq_.HandlerContext(n); + int i = seq_.RemapIndex(index_, hc); + + h.GetBytes(i, data); + } + else + hh.ClearBytes(data); + + if (hh.Compare(0, data) < 0) + return false; + } + + return true; +} + +int c4_FilterSeq::RemapIndex(int index_, const c4_Sequence* seq_) const +{ + return seq_ == this ? index_ + : _seq.RemapIndex((int) _rowMap.GetAt(index_), seq_); +} + +int c4_FilterSeq::NumRows() const +{ + return _rowMap.GetSize(); +} + +int c4_FilterSeq::Compare(int index_, c4_Cursor cursor_) const +{ + return _seq.Compare((int) _rowMap.GetAt(index_), cursor_); +} + +bool c4_FilterSeq::Get(int index_, int propId_, c4_Bytes& bytes_) +{ + return _seq.Get((int) _rowMap.GetAt(index_), propId_, bytes_); +} + +void c4_FilterSeq::InsertAt(int, c4_Cursor, int) +{ + d4_assert(0); +} + +void c4_FilterSeq::RemoveAt(int, int) +{ + d4_assert(0); +} + +void c4_FilterSeq::Set(int, const c4_Property&, const c4_Bytes&) +{ + d4_assert(0); +} + +void c4_FilterSeq::SetSize(int) +{ + d4_assert(0); +} + +int c4_FilterSeq::PosInMap(int index_) const +{ + int i = 0; + + while (i < NumRows()) + if ((int) _rowMap.GetAt(i) >= index_) + break; + else + ++i; + + return i; +} + +c4_Notifier* c4_FilterSeq::PreChange(c4_Notifier& nf_) +{ + if (!GetDependencies()) + return 0; + + c4_Notifier* chg = d4_new c4_Notifier (this); + + bool pass = false; + + switch (nf_._type) + { + case c4_Notifier::kSet: + pass = nf_._propId >= _rowIds.Size() || + _rowIds.Contents()[nf_._propId] == 0; + // fall through... + + case c4_Notifier::kSetAt: + { + int r = (int) _revMap.GetAt(nf_._index); + + bool includeRow = r >= 0; + if (!pass) + if (nf_._type == c4_Notifier::kSetAt) + { + d4_assert(nf_._cursor != 0); + includeRow = Match(nf_._cursor->_index, + *nf_._cursor->_seq); + } + else // set just one property, and it's not in a row yet + includeRow = MatchOne(nf_._propId, *nf_._bytes); + + if (r >= 0 && !includeRow) + chg->StartRemoveAt(r, 1); + else if (r < 0 && includeRow) + chg->StartInsertAt(PosInMap(nf_._index), *nf_._cursor, 1); + else if (includeRow) + { + d4_assert(r >= 0); + + if (nf_._type == c4_Notifier::kSetAt) + chg->StartSetAt(r, *nf_._cursor); + else + chg->StartSet(r, nf_._propId, *nf_._bytes); + } + } + break; + + case c4_Notifier::kInsertAt: + { + int i = PosInMap(nf_._index); + + d4_assert(nf_._cursor != 0); + if (Match(nf_._cursor->_index, *nf_._cursor->_seq)) + chg->StartInsertAt(i, *nf_._cursor, nf_._count); + } + break; + + case c4_Notifier::kRemoveAt: + { + int i = PosInMap(nf_._index); + int j = PosInMap(nf_._index + nf_._count); + d4_assert(j >= i); + + if (j > i) + chg->StartRemoveAt(i, j - i); + } + break; + + case c4_Notifier::kMove: + { + int i = PosInMap(nf_._index); + bool inMap = i < NumRows() && (int) _rowMap.GetAt(i) == nf_._index; + + if (inMap && nf_._index != nf_._count) + chg->StartMove(i, PosInMap(nf_._count)); + } + break; + } + + return chg; +} + +void c4_FilterSeq::PostChange(c4_Notifier& nf_) +{ + bool pass = false; + + switch (nf_._type) + { + case c4_Notifier::kSet: + pass = nf_._propId >= _rowIds.Size() || + _rowIds.Contents()[nf_._propId] == 0; + // fall through... + + case c4_Notifier::kSetAt: + { + int r = (int) _revMap.GetAt(nf_._index); + + bool includeRow = r >= 0; + if (!pass) + if (nf_._type == c4_Notifier::kSetAt) + { + d4_assert(nf_._cursor != 0); + includeRow = Match(nf_._cursor->_index, + *nf_._cursor->_seq); + } + else // set just one property, and it's not in a row yet + includeRow = MatchOne(nf_._propId, *nf_._bytes); + + if (r >= 0 && !includeRow) + _rowMap.RemoveAt(r); + else if (r < 0 && includeRow) + _rowMap.InsertAt(PosInMap(nf_._index), nf_._index); + else + break; + + FixupReverseMap(); + } + break; + + case c4_Notifier::kInsertAt: + { + int i = PosInMap(nf_._index); + + if (Match(nf_._index, _seq)) + { + _rowMap.InsertAt(i, 0, nf_._count); + + for (int j = 0; j < nf_._count; ++j) + _rowMap.SetAt(i++, nf_._index + j); + } + + while (i < NumRows()) + _rowMap.ElementAt(i++) += nf_._count; + + FixupReverseMap(); + } + break; + + case c4_Notifier::kRemoveAt: + { + int i = PosInMap(nf_._index); + int j = PosInMap(nf_._index + nf_._count); + d4_assert(j >= i); + + if (j > i) + _rowMap.RemoveAt(i, j - i); + + while (i < NumRows()) + _rowMap.ElementAt(i++) -= nf_._count; + + FixupReverseMap(); + } + break; + + case c4_Notifier::kMove: + { + int i = PosInMap(nf_._index); + bool inMap = i < NumRows() && (int) _rowMap.GetAt(i) == nf_._index; + + if (inMap && nf_._index != nf_._count) + { + int j = PosInMap(nf_._count); + + _rowMap.RemoveAt(i); + + if (j > i) + --j; + + _rowMap.InsertAt(j, nf_._count); + + FixupReverseMap(); + } + } + break; + } +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_SortSeq : public c4_FilterSeq +{ +public: + typedef t4_i32 T; + + c4_SortSeq (c4_Sequence& seq_, c4_Sequence* down_); + virtual ~c4_SortSeq (); + + virtual c4_Notifier* PreChange(c4_Notifier& nf_); + virtual void PostChange(c4_Notifier& nf_); + +private: + struct c4_SortInfo + { + c4_Handler* _handler; + const c4_Sequence* _context; + c4_Bytes _buffer; + + int CompareOne(c4_Sequence& seq_, T a, T b) + { + _handler->GetBytes(seq_.RemapIndex((int) b, _context), _buffer, true); + return _handler->Compare(seq_.RemapIndex((int) a, _context), _buffer); + } + }; + + bool LessThan(T a, T b); + bool TestSwap( T& first , T& second ); + void MergeSortThis( T* ar, int size , T scratch[] ); + void MergeSort( T ar[] , int size ); + + virtual int Compare(int, c4_Cursor) const; + int PosInMap(c4_Cursor cursor_) const; + + c4_SortInfo* _info; + c4_Bytes _down; + int _width; +}; + +///////////////////////////////////////////////////////////////////////////// + +bool c4_SortSeq::LessThan(T a, T b) +{ + if (a == b) + return false; + + // go through each of the columns and compare values, but since + // handler access is used, we must be careful to remap indices + + c4_SortInfo* info; + + for (info = _info; info->_handler; ++info) + { + int f = info->CompareOne(_seq, a, b); + if (f) + { + int n = info - _info; + if (_width < n) + _width = n; + + return (_down.Contents()[n] ? -f : f) < 0; + } + } + + _width = info - _info; + return a < b; +} + +inline bool c4_SortSeq::TestSwap( T& first , T& second ) +{ + if ( LessThan( second , first ) ) + { + T temp = first; first = second; second = temp; + return true; + } + + return false; +} + +void c4_SortSeq::MergeSortThis( T* ar, int size , T scratch[] ) +{ + switch( size ) + { + //Handle the special cases for speed: + case 2: + TestSwap( ar[ 0 ] , ar[ 1 ] ); + break; + + case 3: + TestSwap( ar[ 0 ] , ar[ 1 ] ); + if ( TestSwap( ar[ 1 ] , ar[ 2 ] ) ) + TestSwap( ar[ 0 ] , ar[ 1 ] ); + break; + + case 4: + //Gotta optimize this.... + TestSwap( ar[ 0 ] , ar[ 1 ] ); + TestSwap( ar[ 2 ] , ar[ 3 ] ); + TestSwap( ar[ 0 ] , ar[ 2 ] ); + TestSwap( ar[ 1 ] , ar[ 3 ] ); + TestSwap( ar[ 1 ] , ar[ 2 ] ); + break; + + //Gotta do special case for list of five. + + default: + //Subdivide the list, recurse, and merge + { + int s1 = size / 2; + int s2 = size - s1; + T* from1_ = scratch; + T* from2_ = scratch + s1; + MergeSortThis( from1_ , s1 , ar ); + MergeSortThis( from2_ , s2 , ar + s1 ); + + T* to1_ = from1_ + s1; + T* to2_ = from2_ + s2; + + for (;;) + { + if ( LessThan( *from1_, *from2_) ) + { + *ar++ = *from1_++; + + if (from1_ >= to1_) + { + while( from2_ < to2_ ) + *ar++ = *from2_++; + break; + } + } + else + { + *ar++ = *from2_++; + + if (from2_ >= to2_) + { + while( from1_ < to1_ ) + *ar++ = *from1_++; + break; + } + } + } + } + } +} + +void c4_SortSeq::MergeSort( T ar[] , int size ) +{ + if ( size > 1 ) + { + T* scratch = d4_new T [size]; + memcpy(scratch , ar , size * sizeof (T)); + MergeSortThis(ar , size , scratch); + delete [] scratch; + } +} + +c4_SortSeq::c4_SortSeq (c4_Sequence& seq_, c4_Sequence* down_) + : c4_FilterSeq (seq_), _info (0), _width (-1) +{ + d4_assert(NumRows() == seq_.NumRows()); + + if (NumRows() > 0) + { + // down is a vector of flags, true to sort in reverse order + char* down = (char*) _down.SetBufferClear(NumHandlers()); + + // set the down flag for all properties to be sorted in reverse + if (down_) + for (int i = 0; i < NumHandlers(); ++i) + if (down_->PropIndex(NthPropId(i)) >= 0) + down[i] = 1; + + _width = -1; + int n = NumHandlers() + 1; + _info = d4_new c4_SortInfo [n]; + + int j; + + for (j = 0; j < NumHandlers(); ++j) + { + _info[j]._handler = & _seq.NthHandler(j); + _info[j]._context = _seq.HandlerContext(j); + } + + _info[j]._handler = 0; + + // everything is ready, go sort the row index vector + MergeSort((T*) &_rowMap.ElementAt(0), NumRows()); + + delete [] _info; + _info = 0; + + FixupReverseMap(); + } +} + +c4_SortSeq::~c4_SortSeq () +{ + d4_assert(!_info); +} + +int c4_SortSeq::Compare(int index_, c4_Cursor cursor_) const +{ + d4_assert(cursor_._seq != 0); + + const char* down = (const char*) _down.Contents(); + d4_assert(_down.Size() <= NumHandlers()); + + c4_Bytes data; + + for (int colNum = 0; colNum < NumHandlers(); ++colNum) + { + c4_Handler& h = NthHandler(colNum); + const c4_Sequence* hc = HandlerContext(colNum); + + if (!cursor_._seq->Get(cursor_._index, h.PropId(), data)) + h.ClearBytes(data); + + int f = h.Compare(RemapIndex(index_, hc), data); + if (f != 0) + return colNum < _down.Size() && down[colNum] ? -f : +f; + } + + return 0; +} + +int c4_SortSeq::PosInMap(c4_Cursor cursor_) const +{ + int i = 0; + + while (i < NumRows()) + if (Compare(i, cursor_) >= 0) + break; + else + ++i; + + d4_assert(i == NumRows() || Compare(i, cursor_) >= 0); + return i; +} + +c4_Notifier* c4_SortSeq::PreChange(c4_Notifier& /*nf_*/) +{ + if (!GetDependencies()) + return 0; + +#if 0 + c4_Notifier* chg = d4_new c4_Notifier (this); + + switch (nf_._type) + { + case c4_Notifier::kSetAt: + case c4_Notifier::kSet: + { + d4_assert(0); // also needs nested propagation + + /* + change can require a move *and* a change of contents + */ + } + break; + + case c4_Notifier::kInsertAt: + { + d4_assert(0); // this case isn't really difficult + } + break; + + case c4_Notifier::kRemoveAt: + { + d4_assert(0); // nested propagation is too difficult for now + // i.e. can only use sort as last derived view + /* + possible solution: + + if 1 row, simple + else if contig in map, also simple + else propagate reorder first, then delete contig + + it can be done here, as multiple notifications, + by simulating n-1 SetAt's of first row in others + needs some map juggling, allow temp dup entries? + + or perhaps more consistent with n separate removes + */ + } + break; + + case c4_Notifier::kMove: + { + // incorrect: may need to move if recnum matters (recs same) + } + break; + } + + return chg; +#endif + +// d4_assert(0); // fail, cannot handle a view dependent on this one yet + return 0; +} + +void c4_SortSeq::PostChange(c4_Notifier& nf_) +{ + switch (nf_._type) + { + case c4_Notifier::kSet: + if (_seq.PropIndex(nf_._propId) > _width) + break; // cannot affect sort order, valuable optimization + + case c4_Notifier::kSetAt: + { + int oi = (int) _revMap.GetAt(nf_._index); + d4_assert(oi >= 0); + + c4_Cursor cursor (_seq, nf_._index); + + // move the entry if the sort order has been disrupted + if ((oi > 0 && Compare(oi - 1, cursor) > 0) || + (oi+1 < NumRows() && Compare(oi+1, cursor) < 0)) + { + _rowMap.RemoveAt(oi); + _rowMap.InsertAt(PosInMap(cursor), nf_._index); + + FixupReverseMap(); + } + + _width = NumHandlers(); // sorry, no more optimization + } + break; + + case c4_Notifier::kInsertAt: + { + // if cursor was not set, it started out as a single Set + c4_Cursor cursor (_seq, nf_._index); + if (nf_._cursor) + cursor = *nf_._cursor; + + for (int n = 0; n < NumRows(); ++n) + if ((int) _rowMap.GetAt(n) >= nf_._index) + _rowMap.ElementAt(n) += nf_._count; + + int i = PosInMap(cursor); + _rowMap.InsertAt(i, 0, nf_._count); + + for (int j = 0; j < nf_._count; ++j) + _rowMap.SetAt(i++, nf_._index + j); + + FixupReverseMap(); + + _width = NumHandlers(); // sorry, no more optimization + } + break; + + case c4_Notifier::kRemoveAt: + { + int lo = nf_._index; + int hi = nf_._index + nf_._count; + + int j = 0; + for (int i = 0; i < NumRows(); ++i) + { + int n = (int) _rowMap.GetAt(i); + + if (n >= hi) + _rowMap.ElementAt(i) -= nf_._count; + + if (!(lo <= n && n < hi)) + _rowMap.SetAt(j++, _rowMap.GetAt(i)); + } + + d4_assert(j + nf_._count == NumRows()); + _rowMap.SetSize(j); + + FixupReverseMap(); + + _width = NumHandlers(); // sorry, no more optimization + } + break; + + case c4_Notifier::kMove: + { + // incorrect: may need to move if recnum matters (recs same) + } + break; + } +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_ProjectSeq : public c4_DerivedSeq +{ + c4_DWordArray _colMap; // a bit large, but bytes would be too small + bool _frozen; + int _omitCount; // if > 0 then this is a dynamic "project without" + +public: + c4_ProjectSeq (c4_Sequence& seq_, c4_Sequence& in_, bool, c4_Sequence* out_); + virtual ~c4_ProjectSeq (); + + virtual int NumHandlers() const; + virtual c4_Handler& NthHandler(int) const; + virtual const c4_Sequence* HandlerContext(int) const; + virtual int AddHandler(c4_Handler*); + + virtual bool Get(int, int, c4_Bytes&); + virtual void Set(int, const c4_Property&, const c4_Bytes&); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_ProjectSeq::c4_ProjectSeq (c4_Sequence& seq_, c4_Sequence& in_, + bool reorder_, c4_Sequence* out_) + : c4_DerivedSeq (seq_), _frozen (!reorder_ && !out_), _omitCount (0) +{ + // build the array with column indexes + for (int j = 0; j < in_.NumHandlers(); ++j) + { + int propId = in_.NthPropId(j); + int idx = _seq.PropIndex(propId); + + // if the j'th property is in the sequence, add it + if (idx >= 0) + { + // but only if it's not in the out_ view + if (out_ && out_->PropIndex(propId) >= 0) + ++_omitCount; + else + _colMap.Add(idx); + } + } + + // if only reordering, append remaining columns from original view + if (reorder_) + { + for (int i = 0; i < _seq.NumHandlers(); ++i) + { + int propId = _seq.NthPropId(i); + + // only consider properties we did not deal with before + if (in_.PropIndex(propId) < 0) + _colMap.Add(i); + } + + d4_assert(_colMap.GetSize() == _seq.NumHandlers()); + } +} + +c4_ProjectSeq::~c4_ProjectSeq () +{ +} + +int c4_ProjectSeq::NumHandlers() const +{ + return _frozen ? _colMap.GetSize() : _seq.NumHandlers() - _omitCount; +} + +c4_Handler& c4_ProjectSeq::NthHandler(int colNum_) const +{ + int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_) : colNum_; + return _seq.NthHandler(n); +} + +const c4_Sequence* c4_ProjectSeq::HandlerContext(int colNum_) const +{ + int n = colNum_ < _colMap.GetSize() ? _colMap.GetAt(colNum_) : colNum_; + return _seq.HandlerContext(n); +} + +int c4_ProjectSeq::AddHandler(c4_Handler* handler_) +{ + int n = _seq.AddHandler(handler_); + return _frozen ? _colMap.Add(n) : n - _omitCount; +} + +bool c4_ProjectSeq::Get(int index_, int propId_, c4_Bytes& buf_) +{ + // fixed in 1.8: check that the property is visible + return PropIndex(propId_) >= 0 && _seq.Get(index_, propId_, buf_); +} + +void c4_ProjectSeq::Set(int index_, const c4_Property& prop_, const c4_Bytes& bytes_) +{ + int n = _seq.NumHandlers(); + _seq.Set(index_, prop_, bytes_); + + // if the number of handlers changed, then one must have been added + if (n != _seq.NumHandlers()) + { + d4_assert(n == _seq.NumHandlers() - 1); + + if (_frozen) + _colMap.Add(n); + } +} + +///////////////////////////////////////////////////////////////////////////// + +c4_Sequence* f4_CreateFilter(c4_Sequence& seq_, c4_Cursor l_, c4_Cursor h_) +{ + return d4_new c4_FilterSeq (seq_, l_, h_); +} + +c4_Sequence* f4_CreateSort(c4_Sequence& seq_, c4_Sequence* down_) +{ + return d4_new c4_SortSeq (seq_, down_); +} + +c4_Sequence* f4_CreateProject(c4_Sequence& seq_, c4_Sequence& in_, + bool reorder_, c4_Sequence* out_) +{ + return d4_new c4_ProjectSeq (seq_, in_, reorder_, out_); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/derived.h b/akregator/src/mk4storage/metakit/src/derived.h new file mode 100644 index 000000000..8bd934fc9 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/derived.h @@ -0,0 +1,25 @@ +// derived.h -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Encapsulation of derived view classes + */ + +#ifndef __DERIVED_H__ +#define __DERIVED_H__ + +///////////////////////////////////////////////////////////////////////////// +// Declarations in this file + + class c4_Cursor; // not defined here + class c4_Sequence; // not defined here + + extern c4_Sequence* f4_CreateFilter(c4_Sequence&, c4_Cursor, c4_Cursor); + extern c4_Sequence* f4_CreateSort(c4_Sequence&, c4_Sequence* =0); + extern c4_Sequence* f4_CreateProject(c4_Sequence&, c4_Sequence&, + bool, c4_Sequence* =0); + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/akregator/src/mk4storage/metakit/src/field.cpp b/akregator/src/mk4storage/metakit/src/field.cpp new file mode 100644 index 000000000..3867d6b5c --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/field.cpp @@ -0,0 +1,119 @@ +// field.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Implementation of the field structure tree + */ + +#include "header.h" +#include "field.h" + +#include <stdlib.h> // strtol + +#if !q4_INLINE +#include "field.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// +// Implemented in this file + + class c4_Field; + +///////////////////////////////////////////////////////////////////////////// +// c4_Field + +c4_Field::c4_Field (const char*& description_, c4_Field* parent_) + : _type (0) +{ + _indirect = this; + + size_t n = strcspn(description_, ",[]"); + const char* p = strchr(description_, ':'); + + if (p != 0 && p < description_ + n) { + _name = c4_String (description_, p - description_); + _type = p[1] & ~0x20; // force to upper case + } else { + _name = c4_String (description_, n); + _type = 'S'; + } + + description_ += n; + + if (*description_ == '[') { + ++description_; + _type = 'V'; + + if (*description_ == '^') { + ++description_; + _indirect = parent_; + d4_assert(*description_ == ']'); + } + + if (*description_ == ']') + ++description_; + else + do { + // 2004-01-20 ignore duplicate property names + // (since there is no good way to report errors at this point) + c4_Field* sf = d4_new c4_Field (description_, this); + for (int i = 0; i < NumSubFields(); ++i) + if (SubField(i).Name().CompareNoCase(sf->Name()) == 0) { + delete sf; + sf = 0; + break; + } + if (sf != 0) + _subFields.Add(sf); + } while (*description_++ == ','); + } +} + +c4_Field::~c4_Field () +{ + if (_indirect == this) { + //better? for (int i = NumSubFields(); --i >= 0 ;) + for (int i = 0; i < NumSubFields(); ++i) { + c4_Field* sf = & SubField(i); + if (sf != this) // careful with recursive subfields + delete sf; + } + } +} + +c4_String c4_Field::Description(bool anonymous_) const +{ + c4_String s = anonymous_ ? "?" : (const char*) Name(); + + if (Type() == 'V') + s += "[" + DescribeSubFields(anonymous_) + "]"; + else { + s += ":"; + s += (c4_String) Type(); + } + + return s; +} + +c4_String c4_Field::DescribeSubFields(bool) const +{ + d4_assert(Type() == 'V'); + + if (_indirect != this) + return "^"; + + c4_String s; + char c = 0; + + for (int i = 0; i < NumSubFields(); ++i) { + if (c != 0) + s += (c4_String) c; + s += SubField(i).Description(); + c = ','; + } + + return s; +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/field.h b/akregator/src/mk4storage/metakit/src/field.h new file mode 100644 index 000000000..5dfc25736 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/field.h @@ -0,0 +1,64 @@ +// field.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Core class to represent fields + */ + +#ifndef __FIELD_H__ +#define __FIELD_H__ + +#ifndef __K4CONF_H__ +#error Please include "k4conf.h" before this header file +#endif + +///////////////////////////////////////////////////////////////////////////// + +class c4_Field +{ + c4_PtrArray _subFields; + c4_String _name; + char _type; + c4_Field* _indirect; + +public: +/* Construction / destruction */ + c4_Field (const char*&, c4_Field* =0); + //: Constructs a new field. + ~c4_Field (); + +/* Repeating and compound fields */ + int NumSubFields() const; + //: Returns the number of subfields. + c4_Field& SubField(int) const; + //: Returns the description of each subfield. + bool IsRepeating() const; + //: Returns true if this field contains subtables. + +/* Field name and description */ + const c4_String& Name() const; + //: Returns name of this field. + char Type() const; + //: Returns the type description of this field, if any. + char OrigType() const; + //: Similar, but report types which were originall 'M' as well. + c4_String Description(bool anonymous_ =false) const; + //: Describes the structure, omit names if anonymous. + c4_String DescribeSubFields(bool anonymous_ =false) const; + //: Describes just the subfields, omit names if anonymous. + +private: + c4_Field (const c4_Field&); // not implemented + void operator= (const c4_Field&); // not implemented +}; + +///////////////////////////////////////////////////////////////////////////// + +#if q4_INLINE +#include "field.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/akregator/src/mk4storage/metakit/src/field.inl b/akregator/src/mk4storage/metakit/src/field.inl new file mode 100644 index 000000000..823e626e3 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/field.inl @@ -0,0 +1,37 @@ +// field.inl -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Inlined members of the field class + */ + +d4_inline bool c4_Field::IsRepeating() const +{ + return _type == 'V'; +} + +d4_inline int c4_Field::NumSubFields() const +{ + return _indirect->_subFields.GetSize(); +} + +d4_inline c4_Field& c4_Field::SubField(int index_) const +{ + return *(c4_Field*) _indirect->_subFields.GetAt(index_); +} + +d4_inline const c4_String& c4_Field::Name() const +{ + return _name; +} + +d4_inline char c4_Field::OrigType() const +{ + return _type; +} + +d4_inline char c4_Field::Type() const +{ + return _type == 'M' ? 'B' : _type; +} diff --git a/akregator/src/mk4storage/metakit/src/fileio.cpp b/akregator/src/mk4storage/metakit/src/fileio.cpp new file mode 100644 index 000000000..28ff7dca0 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/fileio.cpp @@ -0,0 +1,434 @@ +// fileio.cpp -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Implementation of c4_FileStrategy and c4_FileStream + */ + +#include "header.h" +#include "mk4io.h" + +#if q4_WIN32 +#if q4_MSVC && !q4_STRICT +#pragma warning(disable: 4201) // nonstandard extension used : ... +#endif +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#if !defined (q4_WINCE) +#include <io.h> +#include <fcntl.h> +#include <sys/stat.h> +#endif +#endif + +#if q4_UNIX && HAVE_MMAP +#include <sys/types.h> +#include <sys/mman.h> +#endif + +#if q4_UNIX +#include <unistd.h> +#include <fcntl.h> +#endif + +#if q4_WINCE +#define _get_osfhandle(x) x +#endif + +#ifndef _O_NOINHERIT +#define _O_NOINHERIT 0 +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// The "Carbon" version of a build on Macintosh supports running under +// either MacOS 7..9 (which has no mmap), or MacOS X (which has mmap). +// The logic below was adapted from a contribution by Paul Snively, it +// decides at run time which case it is, and switches I/O calls to match. + +#if defined (q4_CARBON) && q4_CARBON +//#if q4_MAC && !defined (__MACH__) && (!q4_MWCW || __MWERKS__ >= 0x3000) +#undef HAVE_MMAP +#define HAVE_MMAP 1 + +#include <CFBundle.h> +#include <Folders.h> + +#define PROT_NONE 0x00 +#define PROT_READ 0x01 +#define PROT_WRITE 0x02 +#define PROT_EXEC 0x04 + +#define MAP_SHARED 0x0001 +#define MAP_PRIVATE 0x0002 + +#define MAP_FIXED 0x0010 +#define MAP_RENAME 0x0020 +#define MAP_NORESERVE 0x0040 +#define MAP_INHERIT 0x0080 +#define MAP_NOEXTEND 0x0100 +#define MAP_HASSEMAPHORE 0x0200 + +typedef unsigned long t4_u32; + +static t4_u32 sfwRefCount = 0; +static CFBundleRef systemFramework = NULL; + +static char* fake_mmap(char*, t4_u32, int, int, int, long long) + { return (char*) -1L; } +static int fake_munmap(char*, t4_u32) + { return 0; } + +static FILE* (*my_fopen)(const char*,const char*) = fopen; +static int (*my_fclose)(FILE*) = fclose; +static long (*my_ftell)(FILE*) = ftell; +static int (*my_fseek)(FILE*,long,int) = fseek; +static t4_u32 (*my_fread)(void* ptr,t4_u32,t4_u32,FILE*) = fread; +static t4_u32 (*my_fwrite)(const void* ptr,t4_u32,t4_u32,FILE*) = fwrite; +static int (*my_ferror)(FILE*) = ferror; +static int (*my_fflush)(FILE*) = fflush; +static int (*my_fileno)(FILE*) = fileno; +static char* (*my_mmap)(char*,t4_u32,int,int,int,long long) = fake_mmap; +static int (*my_munmap)(char*,t4_u32) = fake_munmap; + +static void InitializeIO() +{ + if (sfwRefCount++) return; // race condition, infinitesimal risk + + FSRef theRef; + if (FSFindFolder(kOnAppropriateDisk, kFrameworksFolderType, + false, &theRef) == noErr) { + CFURLRef fw = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &theRef); + if (fw) { + CFURLRef bd = + CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, + fw, CFSTR("System.framework"), false); + CFRelease(fw); + if (bd) { + systemFramework = CFBundleCreate(kCFAllocatorSystemDefault, bd); + CFRelease(bd); + } + } + if (!systemFramework || !CFBundleLoadExecutable(systemFramework)) + return; +#define F(x) CFBundleGetFunctionPointerForName(systemFramework, CFSTR(#x)) + my_fopen = (FILE* (*)(const char*,const char*)) F(fopen); + my_fclose = (int (*)(FILE*)) F(fclose); + my_ftell = (long (*)(FILE*)) F(ftell); + my_fseek = (int (*)(FILE*,long,int)) F(fseek); + my_fread = (t4_u32 (*)(void* ptr,t4_u32,t4_u32,FILE*)) F(fread); + my_fwrite = (t4_u32 (*)(const void* ptr,t4_u32,t4_u32,FILE*)) F(fwrite); + my_ferror = (int (*)(FILE*)) F(ferror); + my_fflush = (int (*)(FILE*)) F(fflush); + my_fileno = (int (*)(FILE*)) F(fileno); + my_mmap = (char* (*)(char*,t4_u32,int,int,int,long long)) F(mmap); + my_munmap = (int (*)(char*,t4_u32)) F(munmap); +#undef F + d4_assert(my_fopen && my_fclose && my_ftell && my_fseek && + my_fread && my_fwrite && my_ferror && my_fflush && + my_fileno && my_mmap && my_munmap); + } +} + +static void FinalizeIO() +{ + if (--sfwRefCount) return; // race condition, infinitesimal risk + + if (systemFramework) { + CFBundleUnloadExecutable(systemFramework); + CFRelease(systemFramework); + systemFramework = 0; + } +} + +#define fopen my_fopen +#define fclose my_fclose +#define ftell my_ftell +#define fseek my_fseek +#define fread my_fread +#define fwrite my_fwrite +#define ferror my_ferror +#define fflush my_fflush +#define fileno my_fileno +#define mmap my_mmap +#define munmap my_munmap + +#else + +#define InitializeIO() +#define FinalizeIO() + +#endif + +///////////////////////////////////////////////////////////////////////////// + +#if q4_CHECK +#include <stdlib.h> + +void f4_AssertionFailed(const char* cond_, const char* file_, int line_) +{ + fprintf(stderr, "Assertion failed: %s (file %s, line %d)\n", + cond_, file_, line_); + abort(); +} + +#endif //q4_CHECK + +///////////////////////////////////////////////////////////////////////////// +// c4_FileStream + +c4_FileStream::c4_FileStream (FILE* stream_, bool owned_) + : _stream (stream_), _owned (owned_) +{ +} + +c4_FileStream::~c4_FileStream () +{ + if (_owned) + fclose(_stream); +} + +int c4_FileStream::Read(void* buffer_, int length_) +{ + d4_assert(_stream != 0); + + return (int) fread(buffer_, 1, length_, _stream); +} + +bool c4_FileStream::Write(const void* buffer_, int length_) +{ + d4_assert(_stream != 0); + + return (int) fwrite(buffer_, 1, length_, _stream) == length_; +} + +///////////////////////////////////////////////////////////////////////////// +// c4_FileStrategy + +c4_FileStrategy::c4_FileStrategy (FILE* file_) + : _file (file_), _cleanup (0) +{ + InitializeIO(); + ResetFileMapping(); +} + +c4_FileStrategy::~c4_FileStrategy () +{ + _file = 0; + ResetFileMapping(); + + if (_cleanup) + fclose(_cleanup); + + d4_assert(_mapStart == 0); + FinalizeIO(); +} + +bool c4_FileStrategy::IsValid() const +{ + return _file != 0; +} + +t4_i32 c4_FileStrategy::FileSize() +{ + d4_assert(_file != 0); + + long size = -1; + + long old = ftell(_file); + if (old >= 0 && fseek(_file, 0, 2) == 0) { + long pos = ftell(_file); + if (fseek(_file, old, 0) == 0) + size = pos; + } + + if (size < 0) + _failure = ferror(_file); + + return size; +} + +t4_i32 c4_FileStrategy::FreshGeneration() +{ + d4_assert(false); + return 0; +} + +void c4_FileStrategy::ResetFileMapping() +{ +#if q4_WIN32 + if (_mapStart != 0) { + _mapStart -= _baseOffset; + d4_dbgdef(BOOL g =) + ::UnmapViewOfFile((char*) _mapStart); + d4_assert(g); + _mapStart = 0; + _dataSize = 0; + } + + if (_file != 0) { + t4_i32 len = FileSize(); + + if (len > 0) { + FlushFileBuffers((HANDLE) _get_osfhandle(_fileno(_file))); + HANDLE h = ::CreateFileMapping((HANDLE) _get_osfhandle(_fileno(_file)), + 0, PAGE_READONLY, 0, len, 0); + d4_assert(h); // check for errors, but can continue without mapping + + if (h) { + _mapStart = (t4_byte*) ::MapViewOfFile(h, FILE_MAP_READ, 0, 0, len); + d4_assert(_mapStart != 0); + + if (_mapStart != 0) { + _mapStart += _baseOffset; + _dataSize = len - _baseOffset; + } + + d4_dbgdef(BOOL f =) + ::CloseHandle(h); + d4_assert(f); + } + } + } +#elif HAVE_MMAP + if (_mapStart != 0) { + _mapStart -= _baseOffset; + munmap((char*) _mapStart, _baseOffset + _dataSize); // also loses const + _mapStart = 0; + _dataSize = 0; + } + + if (_file != 0) { + t4_i32 len = FileSize(); + + if (len > 0) { + _mapStart = (const t4_byte*) mmap(0, len, PROT_READ, MAP_SHARED, + fileno(_file), 0); + if (_mapStart != (void*) -1L) { + _mapStart += _baseOffset; + _dataSize = len - _baseOffset; + } else + _mapStart = 0; + } + } +#endif +} + +bool c4_FileStrategy::DataOpen(const char* fname_, int mode_) +{ + d4_assert(!_file); + +#if q4_WIN32 && !q4_BORC && !q4_WINCE + int flags = _O_BINARY | _O_NOINHERIT | (mode_ > 0 ? _O_RDWR : _O_RDONLY); + int fd = _open(fname_, flags); + if (fd != -1) + _cleanup = _file = _fdopen(fd, mode_ > 0 ? "r+b" : "rb"); +#else + _cleanup = _file = fopen(fname_, mode_ > 0 ? "r+b" : "rb"); +#if q4_UNIX + if (_file != 0) + fcntl(fileno(_file), F_SETFD, FD_CLOEXEC); +#endif //q4_UNIX +#endif //q4_WIN32 && !q4_BORC && !q4_WINCE + + if (_file != 0) { + ResetFileMapping(); + return true; + } + + if (mode_ > 0) { +#if q4_WIN32 && !q4_BORC && !q4_WINCE + fd = _open(fname_, flags | _O_CREAT, _S_IREAD | _S_IWRITE); + if (fd != -1) + _cleanup = _file = _fdopen(fd, "w+b"); +#else + _cleanup = _file = fopen(fname_, "w+b"); +#if q4_UNIX + if (_file != 0) + fcntl(fileno(_file), F_SETFD, FD_CLOEXEC); +#endif //q4_UNIX +#endif //q4_WIN32 && !q4_BORC && !q4_WINCE + } + + //d4_assert(_file != 0); + return false; +} + +int c4_FileStrategy::DataRead(t4_i32 pos_, void* buf_, int len_) +{ + d4_assert(_baseOffset + pos_ >= 0); + d4_assert(_file != 0); + + //printf("DataRead at %d len %d\n", pos_, len_); + return fseek(_file, _baseOffset + pos_, 0) != 0 ? -1 : + (int) fread(buf_, 1, len_, _file); +} + +void c4_FileStrategy::DataWrite(t4_i32 pos_, const void* buf_, int len_) +{ + d4_assert(_baseOffset + pos_ >= 0); + d4_assert(_file != 0); +#if 0 + if (_mapStart <= buf_ && buf_ < _mapStart + _dataSize) { + printf("DataWrite %08x at %d len %d (map %d)\n", buf_, pos_, len_, + (const t4_byte*) buf_ - _mapStart + _baseOffset); + } else { + printf("DataWrite %08x at %d len %d\n", buf_, pos_, len_); + } + fprintf(stderr, " _mapStart %08x _dataSize %d buf_ %08x len_ %d _baseOffset %d\n", + _mapStart, _dataSize, buf_, len_, _baseOffset); + printf(" _mapStart %08x _dataSize %d buf_ %08x len_ %d _baseOffset %d\n", + _mapStart, _dataSize, buf_, len_, _baseOffset); + fflush(stdout); +#endif + +#if q4_WIN32 || __hpux || __MACH__ +// if (buf_ >= _mapStart && buf_ <= _mapLimit - len_) + + // a horrendous hack to allow file mapping for Win95 on network drive + // must use a temp buf to avoid write from mapped file to same file + // + // 6-Feb-1999 -- this workaround is not thread safe + // 30-Nov-2001 -- changed to use the stack so now it is + // 28-Oct-2002 -- added HP/UX to the mix, to avoid hard lockup + char tempBuf [4096]; + d4_assert(len_ <= sizeof tempBuf); + buf_ = memcpy(tempBuf, buf_, len_); +#endif + + if (fseek(_file, _baseOffset + pos_, 0) != 0 || + (int) fwrite(buf_, 1, len_, _file) != len_) { + _failure = ferror(_file); + d4_assert(_failure != 0); + d4_assert(true); // always force an assertion failure in debug mode + } +} + +void c4_FileStrategy::DataCommit(t4_i32 limit_) +{ + d4_assert(_file != 0); + + if (fflush(_file) < 0) { + _failure = ferror(_file); + d4_assert(_failure != 0); + d4_assert(true); // always force an assertion failure in debug mode + return; + } + + if (limit_ > 0) { +#if 0 // can't truncate file in a portable way! + // unmap the file first, WinNT is more picky about this than Win95 + FILE* save = _file; + + _file = 0; + ResetFileMapping(); + _file = save; + + _file->SetLength(limit_); // now we can resize the file +#endif + ResetFileMapping(); // remap, since file length may have changed + } +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/format.cpp b/akregator/src/mk4storage/metakit/src/format.cpp new file mode 100644 index 000000000..aa23e7391 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/format.cpp @@ -0,0 +1,1341 @@ +// format.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Format handlers deal with the representation of data + */ + +#include "header.h" +#include "handler.h" +#include "column.h" +#include "format.h" +#include "persist.h" + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatHandler : public c4_Handler +{ + c4_HandlerSeq& _owner; + +public: + c4_FormatHandler (const c4_Property& prop_, c4_HandlerSeq& owner_); + virtual ~c4_FormatHandler (); + + virtual bool IsPersistent() const; + +protected: + c4_HandlerSeq& Owner() const; +}; + +///////////////////////////////////////////////////////////////////////////// +// c4_FormatHandler + +c4_FormatHandler::c4_FormatHandler (const c4_Property& prop_, c4_HandlerSeq& owner_) + : c4_Handler (prop_), _owner (owner_) +{ +} + +c4_FormatHandler::~c4_FormatHandler () +{ +} + +d4_inline c4_HandlerSeq& c4_FormatHandler::Owner() const +{ + return _owner; +} + +bool c4_FormatHandler::IsPersistent() const +{ + return _owner.Persist() != 0; +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatX : public c4_FormatHandler +{ +public: + c4_FormatX (const c4_Property& prop_, c4_HandlerSeq& seq_, + int width_ =sizeof (t4_i32)); + + virtual void Define(int, const t4_byte**); + virtual void OldDefine(char type_, c4_Persist&); + virtual void FlipBytes(); + + virtual int ItemSize(int index_); + virtual const void* Get(int index_, int& length_); + virtual void Set(int index_, const c4_Bytes& buf_); + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_); + virtual void Remove(int index_, int count_); + + virtual void Commit(c4_SaveContext& ar_); + + virtual void Unmapped(); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); + +protected: + c4_ColOfInts _data; +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatX::c4_FormatX (const c4_Property& p_, c4_HandlerSeq& s_, int w_) + : c4_FormatHandler (p_, s_), _data (s_.Persist(), w_) +{ +} + +int c4_FormatX::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + return c4_ColOfInts::DoCompare(b1_, b2_); +} + +void c4_FormatX::Commit(c4_SaveContext& ar_) +{ + _data.FixSize(true); + ar_.CommitColumn(_data); + //_data.FixSize(false); +} + +void c4_FormatX::Define(int rows_, const t4_byte** ptr_) +{ + if (ptr_ != 0) + _data.PullLocation(*ptr_); + + _data.SetRowCount(rows_); +} + +void c4_FormatX::OldDefine(char, c4_Persist& pers_) +{ + pers_.FetchOldLocation(_data); + _data.SetRowCount(Owner().NumRows()); +} + +void c4_FormatX::FlipBytes() +{ + _data.FlipBytes(); +} + +int c4_FormatX::ItemSize(int index_) +{ + return _data.ItemSize(index_); +} + +const void* c4_FormatX::Get(int index_, int& length_) +{ + return _data.Get(index_, length_); +} + +void c4_FormatX::Set(int index_, const c4_Bytes& buf_) +{ + _data.Set(index_, buf_); +} + +void c4_FormatX::Insert(int index_, const c4_Bytes& buf_, int count_) +{ + _data.Insert(index_, buf_, count_); +} + +void c4_FormatX::Remove(int index_, int count_) +{ + _data.Remove(index_, count_); +} + +void c4_FormatX::Unmapped() +{ + _data.ReleaseAllSegments(); +} + +///////////////////////////////////////////////////////////////////////////// +#if !q4_TINY +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatL : public c4_FormatX +{ +public: + c4_FormatL (const c4_Property& prop_, c4_HandlerSeq& seq_); + + virtual void Define(int, const t4_byte**); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatL::c4_FormatL (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatX (prop_, seq_, sizeof (t4_i64)) +{ + // force maximum size, autosizing more than 32 bits won't work + _data.SetAccessWidth(8 * sizeof (t4_i64)); +} + +int c4_FormatL::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + d4_assert(b1_.Size() == sizeof (t4_i64)); + d4_assert(b2_.Size() == sizeof (t4_i64)); + + t4_i64 v1 = *(const t4_i64*) b1_.Contents(); + t4_i64 v2 = *(const t4_i64*) b2_.Contents(); + + return v1 == v2 ? 0 : v1 < v2 ? -1 : +1; +} + +void c4_FormatL::Define(int rows_, const t4_byte** ptr_) +{ + if (ptr_ == 0 && rows_ > 0) { + d4_assert(_data.ColSize() == 0); + _data.InsertData(0, rows_ * sizeof (t4_i64), true); + } + + c4_FormatX::Define(rows_, ptr_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatF : public c4_FormatX +{ +public: + c4_FormatF (const c4_Property& prop_, c4_HandlerSeq& seq_); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatF::c4_FormatF (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatX (prop_, seq_, sizeof (float)) +{ +} + +int c4_FormatF::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + d4_assert(b1_.Size() == sizeof (float)); + d4_assert(b2_.Size() == sizeof (float)); + + float v1 = *(const float*) b1_.Contents(); + float v2 = *(const float*) b2_.Contents(); + + return v1 == v2 ? 0 : v1 < v2 ? -1 : +1; +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatD : public c4_FormatX +{ +public: + c4_FormatD (const c4_Property& prop_, c4_HandlerSeq& seq_); + + virtual void Define(int, const t4_byte**); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatD::c4_FormatD (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatX (prop_, seq_, sizeof (double)) +{ + // force maximum size, autosizing more than 32 bits won't work + _data.SetAccessWidth(8 * sizeof (double)); +} + +int c4_FormatD::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + d4_assert(b1_.Size() == sizeof (double)); + d4_assert(b2_.Size() == sizeof (double)); + + double v1 = *(const double*) b1_.Contents(); + double v2 = *(const double*) b2_.Contents(); + + return v1 == v2 ? 0 : v1 < v2 ? -1 : +1; +} + +void c4_FormatD::Define(int rows_, const t4_byte** ptr_) +{ + if (ptr_ == 0 && rows_ > 0) { + d4_assert(_data.ColSize() == 0); + _data.InsertData(0, rows_ * sizeof (double), true); + } + + c4_FormatX::Define(rows_, ptr_); +} + +///////////////////////////////////////////////////////////////////////////// +#endif // !q4_TINY +///////////////////////////////////////////////////////////////////////////// + +/* + Byte properties are used for raw bytes and for indirect (memo) data. + + There are two columns: the actual data and the item sizes. If the data + is indirect, then the size is stored as a negative value. + + In addition, there is an in-memory-only vector of columns (_memos). + Columns are created when asked for, and stay around until released with + a commit call. If the column object exists and is not dirty, then it + is either a real column (size < 0), or simply a duplicate of the data + stored inline as bytes. +*/ + +class c4_FormatB : public c4_FormatHandler +{ +public: + c4_FormatB (const c4_Property& prop_, c4_HandlerSeq& seq_); + virtual ~c4_FormatB (); + + virtual void Define(int, const t4_byte**); + virtual void OldDefine(char type_, c4_Persist&); + virtual void Commit(c4_SaveContext& ar_); + + virtual int ItemSize(int index_); + virtual const void* Get(int index_, int& length_); + virtual void Set(int index_, const c4_Bytes& buf_); + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_); + virtual void Remove(int index_, int count_); + + virtual c4_Column* GetNthMemoCol(int index_, bool alloc_); + + virtual void Unmapped(); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); + +protected: + const void* GetOne(int index_, int& length_); + void SetOne(int index_, const c4_Bytes& buf_, bool ignoreMemos_ =false); + +private: + t4_i32 Offset(int index_) const; + bool ShouldBeMemo(int length_) const; + int ItemLenOffCol(int index_, t4_i32& off_, c4_Column*& col_); + bool CommitItem(c4_SaveContext& ar_, int index_); + void InitOffsets(c4_ColOfInts& sizes_); + + c4_Column _data; + c4_ColOfInts _sizeCol; // 2001-11-27: keep, to track position on disk + c4_Column _memoCol; // 2001-11-27: keep, to track position on disk + c4_DWordArray _offsets; + c4_PtrArray _memos; + bool _recalc; // 2001-11-27: remember when to redo _{size,memo}Col +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatB::c4_FormatB (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatHandler (prop_, seq_), _data (seq_.Persist()), + _sizeCol (seq_.Persist()), _memoCol (seq_.Persist()), _recalc (false) +{ + _offsets.SetSize(1, 100); + _offsets.SetAt(0, 0); +} + +c4_FormatB::~c4_FormatB () +{ + // cleanup allocated columns + //better? for (int i = _memos.GetSize(); --i >= 0 ;) + for (int i = 0; i < _memos.GetSize(); ++i) + delete (c4_Column*) _memos.GetAt(i); +} + +d4_inline t4_i32 c4_FormatB::Offset(int index_) const +{ + d4_assert((t4_i32) _offsets.GetAt(_offsets.GetSize() - 1) == _data.ColSize()); + d4_assert(_offsets.GetSize() == _memos.GetSize() + 1); + d4_assert(index_ < _offsets.GetSize()); + + // extend offset vectors for missing empty entries at end + int n = _offsets.GetSize(); + d4_assert(n > 0); + + if (index_ >= n) + index_ = n - 1; + + return _offsets.GetAt(index_); +} + +d4_inline bool c4_FormatB::ShouldBeMemo(int length_) const +{ + // items over 10000 bytes are always memos + // items up to 100 bytes are never memos + // + // else, memo only if the column would be under 1 Mb + // (assuming all items had the same size as this one) + // + // the effect is that as the number of rows increases, + // smaller and smaller items get turned into memos + // + // note that items which are no memo right now stay + // as is, and so do memos which have not been modified + + int rows = _memos.GetSize() + 1; // avoids divide by zero + return length_ > 10000 || length_ > 100 && length_ > 1000000 / rows; +} + +int c4_FormatB::ItemLenOffCol(int index_, t4_i32& off_, c4_Column*& col_) +{ + col_ = (c4_Column*) _memos.GetAt(index_); + if (col_ != 0) { + off_ = 0; + return col_->ColSize(); + } + + col_ = &_data; + off_ = Offset(index_); + return Offset(index_ + 1) - off_; +} + +c4_Column* c4_FormatB::GetNthMemoCol(int index_, bool alloc_) +{ + t4_i32 start; + c4_Column* col; + int n = ItemLenOffCol(index_, start, col); + + if (col == &_data && alloc_) { + col = d4_new c4_Column (_data.Persist()); + _memos.SetAt(index_, col); + + if (n > 0) + if (_data.IsDirty()) { + c4_Bytes temp; + _data.FetchBytes(start, n, temp, true); + col->SetBuffer(n); + col->StoreBytes(0, temp); + } + else + col->SetLocation(_data.Position() + start, n); + } + + return col; +} + +void c4_FormatB::Unmapped() +{ + _data.ReleaseAllSegments(); + _sizeCol.ReleaseAllSegments(); + _memoCol.ReleaseAllSegments(); + + for (int i = 0; i < _memos.GetSize(); ++i) { + c4_Column* cp = (c4_Column*) _memos.GetAt(i); + if (cp != 0) + cp->ReleaseAllSegments(); + } +} + +void c4_FormatB::Define(int, const t4_byte** ptr_) +{ + d4_assert(_memos.GetSize() == 0); + + if (ptr_ != 0) { + _data.PullLocation(*ptr_); + if (_data.ColSize() > 0) + _sizeCol.PullLocation(*ptr_); + _memoCol.PullLocation(*ptr_); + } + + // everything below this point could be delayed until use + // in that case, watch out that column space use is properly tracked + + InitOffsets(_sizeCol); + + if (_memoCol.ColSize() > 0) { + c4_Bytes walk; + _memoCol.FetchBytes(0, _memoCol.ColSize(), walk, true); + + const t4_byte* p = walk.Contents(); + + for (int row = 0; p < walk.Contents() + walk.Size(); ++row) { + row += c4_Column::PullValue(p); + d4_assert(row < _memos.GetSize()); + + c4_Column* mc = d4_new c4_Column (_data.Persist()); + d4_assert(mc != 0); + _memos.SetAt(row, mc); + + mc->PullLocation(p); + } + + d4_assert(p == walk.Contents() + walk.Size()); + } +} + +void c4_FormatB::OldDefine(char type_, c4_Persist& pers_) +{ + int rows = Owner().NumRows(); + + c4_ColOfInts sizes (_data.Persist()); + + if (type_ == 'M') { + InitOffsets(sizes); + + c4_ColOfInts szVec (_data.Persist()); + pers_.FetchOldLocation(szVec); + szVec.SetRowCount(rows); + + c4_ColOfInts posVec (_data.Persist()); + pers_.FetchOldLocation(posVec); + posVec.SetRowCount(rows); + + for (int r = 0; r < rows; ++r) { + t4_i32 sz = szVec.GetInt(r); + if (sz > 0) { + c4_Column* mc = d4_new c4_Column (_data.Persist()); + d4_assert(mc != 0); + _memos.SetAt(r, mc); + + mc->SetLocation(posVec.GetInt(r), sz); + } + } + } else { + pers_.FetchOldLocation(_data); + + if (type_ == 'B') { + pers_.FetchOldLocation(sizes); + +#if !q4_OLD_IS_ALWAYS_V2 + + // WARNING - HUGE HACK AHEAD - THIS IS NOT 100% FULLPROOF! + // + // The above is correct for MK versions 2.0 and up, but *NOT* + // for MK 1.8.6 datafiles, which store sizes first (OUCH!!!). + // This means that there is not a 100% safe way to auto-convert + // both 1.8.6 and 2.0 files - since there is no way to detect + // unambiguously which version a datafile is. All we can do, + // is to carefully check both vectors, and *hope* that only one + // of them is valid as sizes vector. This problem applies to + // the 'B' (bytes) property type only, and only pre 2.0 files. + // + // To build a version which *always* converts assuming 1.8.6, + // add flag "-Dq4_OLD_IS_PRE_V2" to the compiler command line. + // Conversely, "-Dq4_OLD_IS_ALWAYS_V2" forces 2.0 conversion. + + if (rows > 0) { + t4_i32 s1 = sizes.ColSize(); + t4_i32 s2 = _data.ColSize(); + +#if !q4_OLD_IS_PRE_V2 + // if the size vector is clearly impossible, swap vectors + bool fix = c4_ColOfInts::CalcAccessWidth(rows, s1) < 0; + + // if the other vector might be valid as well, check further + if (!fix && c4_ColOfInts::CalcAccessWidth(rows, s2) >= 0) { + sizes.SetRowCount(rows); + t4_i32 total = 0; + for (int i = 0; i < rows; ++i) { + t4_i32 w = sizes.GetInt(i); + if (w < 0 || total > s2) { + total = -1; + break; + } + total += w; + } + + // if the sizes don't add up, swap vectors + fix = total != s2; + } + + if (fix) +#endif + { + t4_i32 p1 = sizes.Position(); + t4_i32 p2 = _data.Position(); + _data.SetLocation(p1, s1); + sizes.SetLocation(p2, s2); + } + } +#endif + InitOffsets(sizes); + } else { + d4_assert(type_ == 'S'); + + sizes.SetRowCount(rows); + + t4_i32 pos = 0; + t4_i32 lastEnd = 0; + int k = 0; + + c4_ColIter iter (_data, 0, _data.ColSize()); + while (iter.Next()) { + const t4_byte* p = iter.BufLoad(); + for (int j = 0; j < iter.BufLen(); ++j) + if (!p[j]) { + sizes.SetInt(k++, pos + j + 1 - lastEnd); + lastEnd = pos + j + 1; + } + + pos += iter.BufLen(); + } + + d4_assert(pos == _data.ColSize()); + + if (lastEnd < pos) { // last entry had no zero byte + _data.InsertData(pos++, 1, true); + sizes.SetInt(k, pos - lastEnd); + } + + InitOffsets(sizes); + + // get rid of entries with just a null byte + for (int r = 0; r < rows; ++r) + if (c4_FormatB::ItemSize(r) == 1) + SetOne(r, c4_Bytes ()); + } + } +} + +void c4_FormatB::InitOffsets(c4_ColOfInts& sizes_) +{ + int rows = Owner().NumRows(); + + if (sizes_.RowCount() != rows) { + sizes_.SetRowCount(rows); + } + + _memos.SetSize(rows); + _offsets.SetSize(rows + 1); + + if (_data.ColSize() > 0) { + t4_i32 total = 0; + + for (int r = 0; r < rows; ++r) { + int n = sizes_.GetInt(r); + d4_assert(n >= 0); + total += n; + _offsets.SetAt(r + 1, total); + } + + d4_assert(total == _data.ColSize()); + } + +} + +int c4_FormatB::ItemSize(int index_) +{ + t4_i32 start; + c4_Column* col; + return ItemLenOffCol(index_, start, col); +} + +const void* c4_FormatB::GetOne(int index_, int& length_) +{ + t4_i32 start; + c4_Column* cp; + length_ = ItemLenOffCol(index_, start, cp); + d4_assert(length_ >= 0); + + if (length_ == 0) + return ""; + + return cp->FetchBytes(start, length_, Owner().Buffer(), false); +} + +const void* c4_FormatB::Get(int index_, int& length_) +{ + return GetOne(index_, length_); +} + +void c4_FormatB::SetOne(int index_, const c4_Bytes& xbuf_, bool ignoreMemos_) +{ + // this fixes bug in 2.4.0 when copying string from higher row + // TODO: this fix is very conservative, figure out when to copy + // (can probably look at pointer to see whether it's from us) + int sz = xbuf_.Size(); + c4_Bytes buf_ (xbuf_.Contents(), sz, 0 < sz && sz <= c4_Column::kSegMax); + + c4_Column* cp = &_data; + t4_i32 start = Offset(index_); + int len = Offset(index_ + 1) - start; + + if (!ignoreMemos_ && _memos.GetAt(index_) != 0) + len = ItemLenOffCol(index_, start, cp); + + int m = buf_.Size(); + int n = m - len; + + if (n > 0) + cp->Grow(start, n); + else if (n < 0) + cp->Shrink(start, - n); + else if (m == 0) + return; // no size change and no contents + + _recalc = true; + + cp->StoreBytes(start, buf_); + + if (n && cp == &_data) { // if size has changed + int k = _offsets.GetSize() - 1; + + // if filling in an empty entry at end: extend offsets first + if (m > 0 && index_ >= k) { + _offsets.InsertAt(k, _offsets.GetAt(k), index_ - k + 1); + + k = index_ + 1; + d4_assert(k == _offsets.GetSize() - 1); + } + + // adjust following entry offsets + while (++index_ <= k) + _offsets.ElementAt(index_) += n; + } + + d4_assert((t4_i32) _offsets.GetAt(_offsets.GetSize() - 1) == _data.ColSize()); +} + +void c4_FormatB::Set(int index_, const c4_Bytes& buf_) +{ + SetOne(index_, buf_); +} + +int c4_FormatB::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + int n = b1_.Size(); + if (n > b2_.Size()) + n = b2_.Size(); + + int f = memcmp(b1_.Contents(), b2_.Contents(), n); + return f ? f : b1_.Size() - b2_.Size(); +} + +void c4_FormatB::Insert(int index_, const c4_Bytes& buf_, int count_) +{ + d4_assert(count_ > 0); + + _recalc = true; + + int m = buf_.Size(); + t4_i32 off = Offset(index_); + + _memos.InsertAt(index_, 0, count_); + + // insert the appropriate number of bytes + t4_i32 n = count_ * (t4_i32) m; + if (n > 0) { + _data.Grow(off, n); + + // store as many copies as needed, but may have to do it in chunks + int spos = 0; + + c4_ColIter iter (_data, off, off + n); + while (iter.Next(m - spos)) { + memcpy(iter.BufSave(), buf_.Contents() + spos, iter.BufLen()); + + spos += iter.BufLen(); + if (spos >= m) + spos = 0; + } + + d4_assert(spos == 0); // must have copied an exact multiple of the data + } + + // define offsets of the new entries + _offsets.InsertAt(index_, 0, count_); + d4_assert(_offsets.GetSize() <= _memos.GetSize() + 1); + + while (--count_ >= 0) { + _offsets.SetAt(index_++, off); + off += m; + } + + d4_assert(index_ < _offsets.GetSize()); + + // adjust all following entries + while (index_ < _offsets.GetSize()) + _offsets.ElementAt(index_++) += n; + + d4_assert((t4_i32) _offsets.GetAt(index_ - 1) == _data.ColSize()); + d4_assert(index_ <= _memos.GetSize() + 1); +} + +void c4_FormatB::Remove(int index_, int count_) +{ + _recalc = true; + + t4_i32 off = Offset(index_); + t4_i32 n = Offset(index_ + count_) - off; + d4_assert(n >= 0); + + // remove the columns, if present + for (int i = 0; i < count_; ++i) + delete (c4_Column*) _memos.GetAt(index_ + i); + _memos.RemoveAt(index_, count_); + + if (n > 0) + _data.Shrink(off, n); + + _offsets.RemoveAt(index_, count_); + + d4_assert(index_ < _offsets.GetSize()); + + // adjust all following entries + while (index_ < _offsets.GetSize()) + _offsets.ElementAt(index_++) -= n; + + d4_assert((t4_i32) _offsets.GetAt(index_ - 1) == _data.ColSize()); + d4_assert(index_ <= _memos.GetSize() + 1); +} + +void c4_FormatB::Commit(c4_SaveContext& ar_) +{ + int rows = _memos.GetSize(); + d4_assert(rows > 0); + + bool full = _recalc || ar_.Serializing(); + + if (!full) + for (int i = 0; i < rows; ++i) { + c4_Column* col = (c4_Column*) _memos.GetAt(i); + if (col != 0) { + full = true; + break; + } + } + d4_assert(_recalc || _sizeCol.RowCount() == rows); + + if (full) { + _memoCol.SetBuffer(0); + _sizeCol.SetBuffer(0); + _sizeCol.SetAccessWidth(0); + _sizeCol.SetRowCount(rows); + + int skip = 0; + + c4_Column* saved = ar_.SetWalkBuffer(&_memoCol); + + for (int r = 0; r < rows; ++r) { + ++skip; + + t4_i32 start; + c4_Column* col; + int len = ItemLenOffCol(r, start, col); + + bool oldMemo = col != &_data; + bool newMemo = ShouldBeMemo(len); + + if (!oldMemo && newMemo) { + col = GetNthMemoCol(r, true); + d4_assert(col != &_data); + //? start = 0; + } + + c4_Bytes temp; + + if (newMemo) { // it now is a memo, inlined data will be empty + ar_.StoreValue(skip - 1); + skip = 0; + ar_.CommitColumn(*col); + } else if (!oldMemo) { // it was no memo, done if it hasn't become one + _sizeCol.SetInt(r, len); + continue; + } else { // it was a memo, but it no longer is + d4_assert(start == 0); + if (len > 0) + { + _sizeCol.SetInt(r, len); + col->FetchBytes(start, len, temp, true); + delete (c4_Column*) _memos.GetAt(r); // 28-11-2001: fix mem leak + _memos.SetAt(r, 0); // 02-11-2001: fix for use after commit + } + } + + SetOne(r, temp, true); // bypass current memo pointer + } + + ar_.SetWalkBuffer(saved); + } + + ar_.CommitColumn(_data); + + if (_data.ColSize() > 0) { + _sizeCol.FixSize(true); + ar_.CommitColumn(_sizeCol); + //_sizeCol.FixSize(false); + } + + ar_.CommitColumn(_memoCol); + + // need a way to find out when the data has been committed (on 2nd pass) + // both _sizeCol and _memoCol will be clean again when it has + // but be careful because dirty flag is only useful if size is nonzero + if (_recalc && !ar_.Serializing()) + _recalc = _sizeCol.ColSize() > 0 && _sizeCol.IsDirty() || + _memoCol.ColSize() > 0 && _memoCol.IsDirty(); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatS : public c4_FormatB +{ +public: + c4_FormatS (const c4_Property& prop_, c4_HandlerSeq& seq_); + + virtual int ItemSize(int index_); + virtual const void* Get(int index_, int& length_); + virtual void Set(int index_, const c4_Bytes& buf_); + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatS::c4_FormatS (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatB (prop_, seq_) +{ +} + +int c4_FormatS::ItemSize(int index_) +{ + int n = c4_FormatB::ItemSize(index_) - 1; + return n >= 0 ? n : 0; +} + +const void* c4_FormatS::Get(int index_, int& length_) +{ + const void* ptr = GetOne(index_, length_); + + if (length_ == 0) { + length_ = 1; + ptr = ""; + } + + d4_assert(((const char*) ptr)[length_-1] == 0); + return ptr; +} + +void c4_FormatS::Set(int index_, const c4_Bytes& buf_) +{ + int m = buf_.Size(); + if (--m >= 0) { + d4_assert(buf_.Contents()[m] == 0); + if (m == 0) { + SetOne(index_, c4_Bytes ()); // don't store data for empty strings + return; + } + } + + SetOne(index_, buf_); +} + +int c4_FormatS::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + c4_String v1 ((const char*) b1_.Contents(), b1_.Size()); + c4_String v2 ((const char*) b2_.Contents(), b2_.Size()); + + return v1.CompareNoCase(v2); +} + +void c4_FormatS::Insert(int index_, const c4_Bytes& buf_, int count_) +{ + d4_assert(count_ > 0); + + int m = buf_.Size(); + if (--m >= 0) { + d4_assert(buf_.Contents()[m] == 0); + if (m == 0) { + c4_FormatB::Insert(index_, c4_Bytes (), count_); + return; + } + } + + c4_FormatB::Insert(index_, buf_, count_); +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_FormatV : public c4_FormatHandler +{ +public: + c4_FormatV (const c4_Property& prop_, c4_HandlerSeq& seq_); + virtual ~c4_FormatV (); + + virtual void Define(int rows_, const t4_byte** ptr_); + virtual void OldDefine(char type_, c4_Persist&); + virtual void Commit(c4_SaveContext& ar_); + + virtual void FlipBytes(); + + virtual int ItemSize(int index_); + virtual const void* Get(int index_, int& length_); + virtual void Set(int index_, const c4_Bytes& buf_); + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_); + virtual void Remove(int index_, int count_); + + virtual void Unmapped(); + virtual bool HasSubview(int index_); + + static int DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_); + +private: + c4_HandlerSeq& At(int index_); + void Replace(int index_, c4_HandlerSeq* seq_); + void SetupAllSubviews(); + void ForgetSubview(int index_); + + c4_Column _data; + c4_PtrArray _subSeqs; + bool _inited; +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FormatV::c4_FormatV (const c4_Property& prop_, c4_HandlerSeq& seq_) + : c4_FormatHandler (prop_, seq_), _data (seq_.Persist()), _inited (false) +{ +} + +c4_FormatV::~c4_FormatV () +{ + for (int i = 0; i < _subSeqs.GetSize(); ++i) + ForgetSubview(i); +} + +c4_HandlerSeq& c4_FormatV::At(int index_) +{ + d4_assert(_inited); + + c4_HandlerSeq*& hs = (c4_HandlerSeq*&) _subSeqs.ElementAt(index_); + if (hs == 0) { + hs = d4_new c4_HandlerSeq (Owner(), this); + hs->IncRef(); + } + + return *hs; +} + +void c4_FormatV::SetupAllSubviews() +{ + d4_assert(!_inited); + _inited = true; + + if (_data.ColSize() > 0) { + c4_Bytes temp; + _data.FetchBytes(0, _data.ColSize(), temp, true); + const t4_byte* ptr = temp.Contents(); + + for (int r = 0; r < _subSeqs.GetSize(); ++r) { + // don't materialize subview if it is empty + // duplicates code which is in c4_HandlerSeq::Prepare + const t4_byte* p2 = ptr; + d4_dbgdef(t4_i32 sias =) + c4_Column::PullValue(p2); + d4_assert(sias == 0); // not yet + + if (c4_Column::PullValue(p2) > 0) + At(r).Prepare(&ptr, false); + else + ptr = p2; + } + + d4_assert(ptr == temp.Contents() + temp.Size()); + } +} + +void c4_FormatV::Define(int rows_, const t4_byte** ptr_) +{ + if (_inited) { + // big oops: a root handler already contains data + + for (int i = 0; i < _subSeqs.GetSize(); ++i) + ForgetSubview(i); + + _inited = false; + } + + _subSeqs.SetSize(rows_); + if (ptr_ != 0) + _data.PullLocation(*ptr_); +} + +void c4_FormatV::OldDefine(char, c4_Persist& pers_) +{ + int rows = Owner().NumRows(); + _subSeqs.SetSize(rows); + + for (int i = 0; i < rows; ++i) { + int n = pers_.FetchOldValue(); + if (n) { + // 14-11-2000: do not create again (this causes a mem leak) + // 04-12-2000: but do create if absent (fixes occasional crash) + c4_HandlerSeq* hs = (c4_HandlerSeq*) _subSeqs.GetAt(i); + if (hs == 0) { + hs = d4_new c4_HandlerSeq (Owner(), this); + _subSeqs.SetAt(i, hs); + hs->IncRef(); + } + hs->SetNumRows(n); + hs->OldPrepare(); + } + } +} + +void c4_FormatV::FlipBytes() +{ + if (!_inited) + SetupAllSubviews(); + + for (int i = 0; i < _subSeqs.GetSize(); ++i) + At(i).FlipAllBytes(); +} + +int c4_FormatV::ItemSize(int index_) +{ + if (!_inited) + SetupAllSubviews(); + + // 06-02-2002: avoid creating empty subview + c4_HandlerSeq* hs = (c4_HandlerSeq*&) _subSeqs.ElementAt(index_); + return hs == 0 ? 0 : hs->NumRows(); +} + +const void* c4_FormatV::Get(int index_, int& length_) +{ + if (!_inited) + SetupAllSubviews(); + + At(index_); // forces existence of a real entry + c4_HandlerSeq*& e = (c4_HandlerSeq*&) _subSeqs.ElementAt(index_); + + length_ = sizeof (c4_HandlerSeq**); + return &e; +} + +void c4_FormatV::Set(int index_, const c4_Bytes& buf_) +{ + d4_assert(buf_.Size() == sizeof (c4_Sequence*)); + + if (!_inited) + SetupAllSubviews(); + + c4_HandlerSeq* value = *(c4_HandlerSeq* const*) buf_.Contents(); + + if (value != & At(index_)) + Replace(index_, value); +} + +void c4_FormatV::Replace(int index_, c4_HandlerSeq* seq_) +{ + if (!_inited) + SetupAllSubviews(); + + c4_HandlerSeq*& curr = (c4_HandlerSeq*&) _subSeqs.ElementAt(index_); + if (seq_ == curr) + return; + + if (curr != 0) { + d4_assert(&curr->Parent() == &Owner()); + curr->DetachFromParent(); + curr->DetachFromStorage(true); + + curr->DecRef(); + curr = 0; + } + + if (seq_) { + int n = seq_->NumRows(); + + c4_HandlerSeq& t = At(index_); + d4_assert(t.NumRows() == 0); + + t.Resize(n); + + c4_Bytes data; + + // this dest seq has only the persistent handlers + // and maybe in a different order + // create any others we need as temporary properties + for (int i = 0; i < seq_->NumHandlers(); ++i) { + c4_Handler& h1 = seq_->NthHandler(i); + + int j = t.PropIndex(h1.Property()); + d4_assert(j >= 0); + + c4_Handler& h2 = t.NthHandler(j); + + for (int k = 0; k < n; ++k) + if (seq_->Get(k, h1.PropId(), data)) + h2.Set(k, data); + } + } +} + +int c4_FormatV::DoCompare(const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + d4_assert(b1_.Size() == sizeof (c4_Sequence*)); + d4_assert(b2_.Size() == sizeof (c4_Sequence*)); + + c4_View v1 = *(c4_Sequence* const*) b1_.Contents(); + c4_View v2 = *(c4_Sequence* const*) b2_.Contents(); + + return v1.Compare(v2); +} + +void c4_FormatV::Insert(int index_, const c4_Bytes& buf_, int count_) +{ + d4_assert(buf_.Size() == sizeof (c4_Sequence*)); + d4_assert(count_ > 0); + + // can only insert an empty entry! + d4_assert(*(c4_Sequence* const*) buf_.Contents() == 0); + + if (!_inited) + SetupAllSubviews(); + + _subSeqs.InsertAt(index_, 0, count_); + _data.SetBuffer(0); // 2004-01-18 force dirty +} + +void c4_FormatV::Remove(int index_, int count_) +{ + d4_assert(count_ > 0); + + if (!_inited) + SetupAllSubviews(); + + for (int i = 0; i < count_; ++i) + ForgetSubview(index_ + i); + + _subSeqs.RemoveAt(index_, count_); + _data.SetBuffer(0); // 2004-01-18 force dirty +} + +void c4_FormatV::Unmapped() +{ + if (_inited) + for (int i = 0; i < _subSeqs.GetSize(); ++i) + if (HasSubview(i)) { + c4_HandlerSeq& hs = At(i); + hs.UnmappedAll(); + if (hs.NumRefs() == 1 && hs.NumRows() == 0) + ForgetSubview(i); + } + + _data.ReleaseAllSegments(); +} + +bool c4_FormatV::HasSubview(int index_) +{ + if (!_inited) + SetupAllSubviews(); + + return _subSeqs.ElementAt(index_) != 0; +} + +void c4_FormatV::ForgetSubview(int index_) +{ + c4_HandlerSeq*& seq = (c4_HandlerSeq*&) _subSeqs.ElementAt(index_); + if (seq != 0) { + d4_assert(&seq->Parent() == &Owner()); + seq->DetachFromParent(); + seq->DetachFromStorage(true); + seq->UnmappedAll(); + seq->DecRef(); + seq = 0; + } +} + +void c4_FormatV::Commit(c4_SaveContext& ar_) +{ + if (!_inited) + SetupAllSubviews(); + + int rows = _subSeqs.GetSize(); + d4_assert(rows > 0); + + c4_Column temp (0); + c4_Column* saved = ar_.SetWalkBuffer(&temp); + + for (int r = 0; r < rows; ++r) + if (HasSubview(r)) { + c4_HandlerSeq& hs = At(r); + ar_.CommitSequence(hs, false); + if (hs.NumRefs() == 1 && hs.NumRows() == 0) + ForgetSubview(r); + } else { + ar_.StoreValue(0); // sias + ar_.StoreValue(0); // row count + } + + ar_.SetWalkBuffer(saved); + + c4_Bytes buf; + temp.FetchBytes(0, temp.ColSize(), buf, true); + + bool changed = temp.ColSize() != _data.ColSize(); + + if (!changed) { + c4_Bytes buf2; + _data.FetchBytes(0, _data.ColSize(), buf2, true); + changed = buf != buf2; + } + + if (changed) { + _data.SetBuffer(buf.Size()); + _data.StoreBytes(0, buf); + } + + ar_.CommitColumn(_data); +} + +///////////////////////////////////////////////////////////////////////////// + +c4_Handler* f4_CreateFormat(const c4_Property& prop_, c4_HandlerSeq& seq_) +{ + switch (prop_.Type()) { + case 'I': return d4_new c4_FormatX (prop_, seq_); +#if !q4_TINY + case 'L': return d4_new c4_FormatL (prop_, seq_); + case 'F': return d4_new c4_FormatF (prop_, seq_); + case 'D': return d4_new c4_FormatD (prop_, seq_); +#endif + case 'B': return d4_new c4_FormatB (prop_, seq_); + case 'S': return d4_new c4_FormatS (prop_, seq_); + case 'V': return d4_new c4_FormatV (prop_, seq_); + } + + d4_assert(0); + // 2004-01-16 turn bad definition type into an int property to avoid crash + return d4_new c4_FormatX (c4_IntProp (prop_.Name()), seq_); +} + +///////////////////////////////////////////////////////////////////////////// + +int f4_ClearFormat(char type_) +{ + switch (type_) { + case 'I': return sizeof (t4_i32); +#if !q4_TINY + case 'L': return sizeof (t4_i64); + case 'F': return sizeof (float); + case 'D': return sizeof (double); +#endif + case 'S': return 1; + case 'V': return sizeof (c4_Sequence*); + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////// + +int f4_CompareFormat(char type_, const c4_Bytes& b1_, const c4_Bytes& b2_) +{ + switch (type_) { + case 'I': return c4_FormatX::DoCompare(b1_, b2_); +#if !q4_TINY + case 'L': return c4_FormatL::DoCompare(b1_, b2_); + case 'F': return c4_FormatF::DoCompare(b1_, b2_); + case 'D': return c4_FormatD::DoCompare(b1_, b2_); +#endif + case 'B': return c4_FormatB::DoCompare(b1_, b2_); + case 'S': return c4_FormatS::DoCompare(b1_, b2_); + case 'V': return c4_FormatV::DoCompare(b1_, b2_); + } + + d4_assert(0); + return 0; +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/format.h b/akregator/src/mk4storage/metakit/src/format.h new file mode 100644 index 000000000..721e55484 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/format.h @@ -0,0 +1,23 @@ +// format.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Encapsulation of all format handlers + */ + +#ifndef __FORMAT_H__ +#define __FORMAT_H__ + +///////////////////////////////////////////////////////////////////////////// +// Declarations in this file + + class c4_Handler; // not defined here + + extern c4_Handler* f4_CreateFormat(const c4_Property&, c4_HandlerSeq&); + extern int f4_ClearFormat(char); + extern int f4_CompareFormat(char, const c4_Bytes&, const c4_Bytes&); + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/akregator/src/mk4storage/metakit/src/gnuc.h b/akregator/src/mk4storage/metakit/src/gnuc.h new file mode 100644 index 000000000..da112ac6a --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/gnuc.h @@ -0,0 +1,13 @@ +// gnuc.h -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Configuration header for GNU C++ + */ + +#define q4_GNUC 1 + +#if !defined (q4_BOOL) +#define q4_BOOL 1 +#endif diff --git a/akregator/src/mk4storage/metakit/src/handler.cpp b/akregator/src/mk4storage/metakit/src/handler.cpp new file mode 100644 index 000000000..6c68c5c3f --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/handler.cpp @@ -0,0 +1,500 @@ +// handler.cpp -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Handlers store data in column-wise format + */ + +#include "header.h" +#include "handler.h" +#include "format.h" +#include "field.h" +#include "column.h" +#include "persist.h" + +#if !q4_INLINE +#include "handler.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// +// c4_Handler + +void c4_Handler::ClearBytes(c4_Bytes& buf_) const +{ + static char zeros[8]; + + int n = f4_ClearFormat(Property().Type()); + d4_assert(n <= sizeof zeros); + + buf_ = c4_Bytes (zeros, n); +} + +int c4_Handler::Compare(int index_, const c4_Bytes& buf_) +{ + // create a copy for small data, since ints use a common _item buffer + c4_Bytes copy (buf_.Contents(), buf_.Size(), buf_.Size() <= 8); + + c4_Bytes data; + GetBytes(index_, data); + + return f4_CompareFormat(Property().Type(), data, copy); +} + +void c4_Handler::Commit(c4_SaveContext&) +{ + d4_assert(0); +} + +void c4_Handler::OldDefine(char, c4_Persist&) +{ + d4_assert(0); +} + + // this is how the old "Get" was, keep it until no longer needed +void c4_Handler::GetBytes(int index_, c4_Bytes& buf_, bool copySmall_) +{ + int n; + const void* p = Get(index_, n); + buf_ = c4_Bytes (p, n, copySmall_ && n <= 8); +} + +void c4_Handler::Move(int from_, int to_) +{ + if (from_ != to_) { + c4_Bytes data; + GetBytes(from_, data); + + Remove(from_, 1); + + if (to_ > from_) + --to_; + + Insert(to_, data, 1); + } +} + +///////////////////////////////////////////////////////////////////////////// +// c4_HandlerSeq + +c4_HandlerSeq::c4_HandlerSeq (c4_Persist* persist_) + : _persist (persist_), _field (0), _parent (0), _numRows (0) +{ +} + +c4_HandlerSeq::c4_HandlerSeq (c4_HandlerSeq& owner_, c4_Handler* handler_) + : _persist (owner_.Persist()), _field (owner_.FindField(handler_)), + _parent (&owner_), _numRows (0) +{ + for (int i = 0; i < NumFields(); ++i) { + c4_Field& field = Field(i); + c4_Property prop (field.Type(), field.Name()); + + d4_dbgdef(int n =) + AddHandler(f4_CreateFormat(prop, *this)); + d4_assert(n == i); + } +} + +c4_HandlerSeq::~c4_HandlerSeq () +{ + const bool rootLevel = _parent == this; + c4_Persist* pers = _persist; + + if (rootLevel && pers != 0) + pers->DoAutoCommit(); + + DetachFromParent(); + DetachFromStorage(true); + + for (int i = 0; i < NumHandlers(); ++i) + delete & NthHandler(i); + _handlers.SetSize(0); + + ClearCache(); + + if (rootLevel) { + delete _field; + + d4_assert(pers != 0); + delete pers; + } +} + +c4_Persist* c4_HandlerSeq::Persist() const +{ + return _persist; +} + +void c4_HandlerSeq::DefineRoot() +{ + d4_assert(_field == 0); + d4_assert(_parent == 0); + + SetNumRows(1); + + const char* desc = "[]"; + _field = d4_new c4_Field (desc); + d4_assert(!*desc); + + _parent = this; +} + +c4_Handler* c4_HandlerSeq::CreateHandler(const c4_Property& prop_) +{ + return f4_CreateFormat(prop_, *this); +} + +c4_Field& c4_HandlerSeq::Definition() const +{ + d4_assert(_field != 0); + + return *_field; +} + +void c4_HandlerSeq::DetachFromParent() +{ + if (_field != 0) { + const char* desc = "[]"; + c4_Field f (desc); + d4_assert(!*desc); + Restructure(f, false); + _field = 0; + } + + _parent = 0; +} + +void c4_HandlerSeq::DetachFromStorage(bool full_) +{ + if (_persist != 0) { + int limit = full_ ? 0 : NumFields(); + + // get rid of all handlers which might do I/O + for (int c = NumHandlers(); --c >= 0; ) { + c4_Handler& h = NthHandler(c); + + // all nested fields are detached recursively + if (IsNested(c)) + for (int r = 0; r < NumRows(); ++r) + if (h.HasSubview(r)) + SubEntry(c, r).DetachFromStorage(full_); + + if (c >= limit) { + if (h.IsPersistent()) { + delete &h; + _handlers.RemoveAt(c); + ClearCache(); + } + } + } + + if (full_) { + //UnmappedAll(); + _persist = 0; + } + } +} + +void c4_HandlerSeq::DetermineSpaceUsage() +{ + for (int c = 0; c < NumFields(); ++c) + if (IsNested(c)) { + c4_Handler& h = NthHandler(c); + for (int r = 0; r < NumRows(); ++r) + if (h.HasSubview(r)) + SubEntry(c, r).DetermineSpaceUsage(); + } +} + +void c4_HandlerSeq::SetNumRows(int numRows_) +{ + d4_assert(_numRows >= 0); + + _numRows = numRows_; +} + +int c4_HandlerSeq::AddHandler(c4_Handler* handler_) +{ + d4_assert(handler_ != 0); + + return _handlers.Add(handler_); +} + +const char* c4_HandlerSeq::Description() +{ + // 19-01-2003: avoid too dense code, since Sun CC seems to choke on it + //return _field != 0 ? UseTempBuffer(Definition().DescribeSubFields()) : 0; + if (_field == 0) + return 0; + c4_String s = _field->DescribeSubFields(); + return UseTempBuffer(s); +} + +void c4_HandlerSeq::Restructure(c4_Field& field_, bool remove_) +{ + //d4_assert(_field != 0); + + // all nested fields must be set up, before we shuffle them around + for (int k = 0; k < NumHandlers(); ++k) + if (IsNested(k)) { + c4_Handler& h = NthHandler(k); + for (int n = 0; n < NumRows(); ++n) + if (h.HasSubview(n)) + SubEntry(k, n); + } + + for (int i = 0; i < field_.NumSubFields(); ++i) { + c4_Field& nf = field_.SubField(i); + c4_Property prop (nf.Type(), nf.Name()); + + int n = PropIndex(prop.GetId()); + if (n == i) + continue; + + if (n < 0) { + _handlers.InsertAt(i, f4_CreateFormat(prop, *this)); + NthHandler(i).Define(NumRows(), 0); + } else { + // move the handler to the front + d4_assert(n > i); + _handlers.InsertAt(i, _handlers.GetAt(n)); + _handlers.RemoveAt(++n); + } + + ClearCache(); // we mess with the order of handler, keep clearing it + + d4_assert(PropIndex(prop.GetId()) == i); + } + + c4_Field* ofld = _field; + // special case if we're "restructuring a view out of persistence", see below + + _field = remove_ ? 0 : &field_; + + // let handler do additional init once all have been prepared + //for (int n = 0; n < NumHandlers(); ++n) + // NthHandler(n).Define(NumRows(), 0); + + const char* desc = "[]"; + c4_Field temp (desc); + + // all nested fields are restructured recursively + for (int j = 0; j < NumHandlers(); ++j) + if (IsNested(j)) { + c4_Handler& h = NthHandler(j); + for (int n = 0; n < NumRows(); ++n) + if (h.HasSubview(n)) { + c4_HandlerSeq& seq = SubEntry(j, n); + if (j < NumFields()) + seq.Restructure(field_.SubField(j), false); + else if (seq._field != 0) + seq.Restructure(temp, true); + } + } + + if (_parent == this) + delete ofld; // the root table owns its field structure tree +} + +int c4_HandlerSeq::NumFields() const +{ + return _field != 0 ? _field->NumSubFields() : 0; +} + +char c4_HandlerSeq::ColumnType(int index_) const +{ + return NthHandler(index_).Property().Type(); +} + +bool c4_HandlerSeq::IsNested(int index_) const +{ + return ColumnType(index_) == 'V'; +} + +c4_Field& c4_HandlerSeq::Field(int index_) const +{ + d4_assert(_field != 0); + + return _field->SubField(index_); +} + +void c4_HandlerSeq::Prepare(const t4_byte** ptr_, bool selfDesc_) +{ + if (ptr_ != 0) { + d4_dbgdef(t4_i32 sias =) + c4_Column::PullValue(*ptr_); + d4_assert(sias == 0); // not yet + + if (selfDesc_) { + t4_i32 n = c4_Column::PullValue(*ptr_); + if (n > 0) { + c4_String s = "[" + c4_String ((const char*) *ptr_, n) + "]"; + const char* desc = s; + + c4_Field* f = d4_new c4_Field (desc); + d4_assert(!*desc); + + Restructure(*f, false); + *ptr_ += n; + } + } + + int rows = (int) c4_Column::PullValue(*ptr_); + if (rows > 0) { + SetNumRows(rows); + + for (int i = 0; i < NumFields(); ++i) + NthHandler(i).Define(rows, ptr_); + } + } +} + +void c4_HandlerSeq::OldPrepare() +{ + d4_assert(_persist != 0); + + for (int i = 0; i < NumFields(); ++i) { + char origType = _field->SubField(i).OrigType(); + NthHandler(i).OldDefine(origType, *_persist); + } +} + +void c4_HandlerSeq::FlipAllBytes() +{ + for (int i = 0; i < NumHandlers(); ++i) { + c4_Handler& h = NthHandler(i); + h.FlipBytes(); + } +} + + // New 19990903: swap rows in tables without touching the memo fields + // or subviews on disk. This is used by the new c4_View::RelocateRows. + +void c4_HandlerSeq::ExchangeEntries(int srcPos_, c4_HandlerSeq& dst_, int dstPos_) +{ + d4_assert(NumHandlers() == dst_.NumHandlers()); + + c4_Bytes t1, t2; + + for (int col = 0; col < NumHandlers(); ++col) + { + if (IsNested(col)) + { + d4_assert(dst_.IsNested(col)); + + int n; + c4_HandlerSeq** e1 = (c4_HandlerSeq**) NthHandler(col).Get(srcPos_, n); + c4_HandlerSeq** e2 = (c4_HandlerSeq**) dst_.NthHandler(col).Get(dstPos_, n); + d4_assert(*e1 != 0 && *e2 != 0); + + // swap the two entries + c4_HandlerSeq* e = *e1; + *e1 = *e2; + *e2 = e; + + // shorthand, *after* the swap + c4_HandlerSeq& t1 = SubEntry(col, srcPos_); + c4_HandlerSeq& t2 = dst_.SubEntry(col, dstPos_); + + // adjust the parents + t1._parent = this; + t2._parent = &dst_; + + // reattach the proper field structures + t1.Restructure(Field(col), false); + t2.Restructure(dst_.Field(col), false); + } + else + { + d4_assert(ColumnType(col) == dst_.ColumnType(col)); + + c4_Handler& h1 = NthHandler(col); + c4_Handler& h2 = dst_.NthHandler(col); + +#if 0 // memo's are 'B' now, but tricky to deal with, so copy them for now + if (ColumnType(col) == 'M') + { + c4_Column* c1 = h1.GetNthMemoCol(srcPos_, true); + c4_Column* c2 = h2.GetNthMemoCol(dstPos_, true); + + t4_i32 p1 = c1 ? c1->Position() : 0; + t4_i32 p2 = c2 ? c2->Position() : 0; + + t4_i32 s1 = c1 ? c1->ColSize() : 0; + t4_i32 s2 = c2 ? c2->ColSize() : 0; + + d4_assert(false); // broken + //!h1.SetNthMemoPos(srcPos_, p2, s2, c2); + //!h2.SetNthMemoPos(dstPos_, p1, s1, c1); + } +#endif + // 10-4-2002: Need to use copies in case either item points into + // memory that could move, or if access re-uses a shared buffer. + // The special cases are sufficiently tricky that it's NOT being + // optimized for now (temp bufs, mmap ptrs, c4_Bytes buffering). + + int n1, n2; + const void* p1 = h1.Get(srcPos_, n1); + const void* p2 = h2.Get(dstPos_, n2); + + c4_Bytes t1 (p1, n1, true); + c4_Bytes t2 (p2, n2, true); + + h1.Set(srcPos_, t2); + h2.Set(dstPos_, t1); + } + } +} + +c4_HandlerSeq& c4_HandlerSeq::SubEntry(int col_, int row_) const +{ + d4_assert(IsNested(col_)); + + c4_Bytes temp; + NthHandler(col_).GetBytes(row_, temp); + + d4_assert(temp.Size() == sizeof (c4_HandlerSeq**)); + c4_HandlerSeq** p = (c4_HandlerSeq**) temp.Contents(); // loses const + + d4_assert(p != 0 && *p != 0); + + return **p; +} + +c4_Field* c4_HandlerSeq::FindField(const c4_Handler* handler_) +{ + for (int i = 0; i < NumFields(); ++i) + if (handler_ == &NthHandler(i)) + return &Field(i); + return 0; +} + +void c4_HandlerSeq::UnmappedAll() +{ + for (int i = 0; i < NumFields(); ++i) + NthHandler(i).Unmapped(); +} + + // construct meta view from a pre-parsed field tree structure + // this will one day be converted to directly parse the description string +void c4_HandlerSeq::BuildMeta(int parent_, int colnum_, c4_View& meta_, + const c4_Field& field_) +{ + c4_IntProp pP ("P"), pC ("C"); + c4_ViewProp pF ("F"); + c4_StringProp pN ("N"), pT ("T"); + + int n = meta_.Add(pP [parent_] + pC [colnum_]); + c4_View fields = pF (meta_[n]); + + for (int i = 0; i < field_.NumSubFields(); ++i) { + const c4_Field& f = field_.SubField(i); + char type = f.Type(); + fields.Add(pN [f.Name()] + pT [c4_String (&type, 1)]); + if (type == 'V') + BuildMeta(n, i, meta_, f); + } +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/handler.h b/akregator/src/mk4storage/metakit/src/handler.h new file mode 100644 index 000000000..6003f625d --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/handler.h @@ -0,0 +1,150 @@ +// handler.h -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Definition of the main handler classes + */ + +#ifndef __HANDLER_H__ +#define __HANDLER_H__ + +///////////////////////////////////////////////////////////////////////////// +// Declarations in this file + + class c4_Handler; // data representation handler + +// class c4_Sequence; + class c4_HandlerSeq; // a sequence built from handlers + + class c4_Column; // not defined here + class c4_Field; // not defined here + class c4_Persist; // not defined here + class c4_SaveContext; // not defined here + +///////////////////////////////////////////////////////////////////////////// + +class c4_Handler +{ + c4_Property _property; + +public: + c4_Handler (const c4_Property& _prop); + //: Constructor (this is an abstract base class). + virtual ~c4_Handler (); + + virtual void Define(int, const t4_byte**); + //: Called when the corresponding table has been fully defined. + virtual void FlipBytes(); + //: Called to reverse the internal byte order of foreign data. + virtual void Commit(c4_SaveContext& ar_); + //: Commit the associated column(s) to file. + virtual void OldDefine(char, c4_Persist&); + + const c4_Property& Property() const; + //: Returns the property associated with this handler. + int PropId() const; + //: Returns the id of the property associated with this handler. + + void ClearBytes(c4_Bytes& buf_) const; + //: Returns the default value for items of this type. + + virtual int ItemSize(int index_) = 0; + //: Return width of specified data item. + void GetBytes(int index_, c4_Bytes& buf_, bool copySmall_ =false); + //: Used for backward compatibility, should probably be replaced. + virtual const void* Get(int index_, int& length_) = 0; + //: Retrieves the data item at the specified index. + virtual void Set(int index_, const c4_Bytes& buf_) = 0; + //: Stores a new data item at the specified index. + + int Compare(int index_, const c4_Bytes& buf_); + //: Compares an entry with a specified data item. + + virtual void Insert(int index_, const c4_Bytes& buf_, int count_) = 0; + //: Inserts 1 or more data items at the specified index. + virtual void Remove(int index_, int count_) = 0; + //: Removes 1 or more data items at the specified index. + void Move(int from_, int to_); + //: Move a data item to another position. + + virtual c4_Column* GetNthMemoCol(int index_, bool alloc_ =false); + //: Special access to underlying data of memo entries + + virtual bool IsPersistent() const; + //: True if this handler might do I/O to satisfy fetches + + virtual void Unmapped(); + //: Make sure this handler stops using file mappings + + virtual bool HasSubview(int index_); + //: True if this subview has materialized into an object +}; + +///////////////////////////////////////////////////////////////////////////// + +class c4_HandlerSeq : public c4_Sequence +{ + c4_PtrArray _handlers; + c4_Persist* _persist; + c4_Field* _field; + c4_HandlerSeq* _parent; + int _numRows; + +public: + c4_HandlerSeq (c4_Persist*); + c4_HandlerSeq (c4_HandlerSeq& owner_, c4_Handler* handler_); + + virtual int NumRows() const; + virtual void SetNumRows(int); + + virtual int NumHandlers() const; + virtual c4_Handler& NthHandler(int) const; + virtual const c4_Sequence* HandlerContext(int) const; + virtual int AddHandler(c4_Handler*); + + void DefineRoot(); + void Restructure(c4_Field&, bool remove_); + void DetachFromParent(); + void DetachFromStorage(bool full_); + void DetermineSpaceUsage(); + + c4_Field& Definition() const; + const char* Description(); + c4_HandlerSeq& Parent() const; + virtual c4_Persist* Persist() const; + + c4_Field& Field(int) const; + int NumFields() const; + char ColumnType(int index_) const; + bool IsNested(int) const; + + void Prepare(const t4_byte** ptr_, bool selfDesc_); + void OldPrepare(); + + void FlipAllBytes(); + void ExchangeEntries(int srcPos_, c4_HandlerSeq& dst_, int dstPos_); + + c4_HandlerSeq& SubEntry(int, int) const; + + c4_Field* FindField(const c4_Handler* handler_); + + void UnmappedAll(); + + static void BuildMeta(int, int, c4_View&, const c4_Field&); + +protected: + virtual c4_Handler* CreateHandler(const c4_Property&); + + virtual ~c4_HandlerSeq (); +}; + +///////////////////////////////////////////////////////////////////////////// + +#if q4_INLINE +#include "handler.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/akregator/src/mk4storage/metakit/src/handler.inl b/akregator/src/mk4storage/metakit/src/handler.inl new file mode 100644 index 000000000..75321f9c7 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/handler.inl @@ -0,0 +1,90 @@ +// handler.inl -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Inlined members of the handler classes + */ + +///////////////////////////////////////////////////////////////////////////// +// c4_Handler + +d4_inline c4_Handler::c4_Handler (const c4_Property& prop_) + : _property (prop_) +{ +} + +d4_inline c4_Handler::~c4_Handler () +{ +} + +d4_inline void c4_Handler::Define(int, const t4_byte**) +{ +} + +d4_inline void c4_Handler::FlipBytes() +{ +} + +d4_inline const c4_Property& c4_Handler::Property() const +{ + return _property; +} + +d4_inline int c4_Handler::PropId() const +{ + return _property.GetId(); +} + +d4_inline c4_Column* c4_Handler::GetNthMemoCol(int, bool alloc_) +{ + return 0; +} + +d4_inline bool c4_Handler::IsPersistent() const +{ + return false; +} + +d4_inline void c4_Handler::Unmapped() +{ +} + +d4_inline bool c4_Handler::HasSubview(int) +{ + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// c4_HandlerSeq + +d4_inline int c4_HandlerSeq::NumRows() const +{ + d4_assert(_numRows >= 0); + + return _numRows; +} + +d4_inline int c4_HandlerSeq::NumHandlers() const +{ + return _handlers.GetSize(); +} + +d4_inline c4_Handler& c4_HandlerSeq::NthHandler(int index_) const +{ + d4_assert(_handlers.GetAt(index_) != 0); + + return *(c4_Handler*) _handlers.GetAt(index_); +} + +d4_inline const c4_Sequence* c4_HandlerSeq::HandlerContext(int) const +{ + return this; +} + +d4_inline c4_HandlerSeq& c4_HandlerSeq::Parent() const +{ + return *_parent; +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/header.h b/akregator/src/mk4storage/metakit/src/header.h new file mode 100644 index 000000000..2f8648b8f --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/header.h @@ -0,0 +1,215 @@ +// header.h -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * The internal header included in all source files + */ + +#ifndef __HEADER_H__ +#define __HEADER_H__ + +///////////////////////////////////////////////////////////////////////////// + +#include "config.h" + +///////////////////////////////////////////////////////////////////////////// +// A number of preprocessor options are used in the source code +// +// q4_DOS MS-DOS real-mode OS +// q4_MAC Apple Macintosh OS +// q4_UNIX Unix, any flavor +// q4_VMS DEC OpenVMS OS +// q4_WIN Microsoft Windows OS, any flavor +// q4_WIN32 Microsoft Windows OS, 32-bit +// q4_WINCE Microsoft Windows OS, embedded +// +// q4_MFC Microsoft MFC framework +// q4_STD Standard STL version +// q4_UNIV Universal version +// +// q4_BOOL compiler supports bool datatype +// q4_CHECK enable assertion checks +// q4_FIX manual header fix (see above) +// q4_INLINE enable inline expansion +// q4_KITDLL compile as DLL (shared library) +// q4_MULTI compile for multi-threading +// q4_NOLIB do not add automatic lib linkage (MSVC5) +// q4_NO_NS don't use namespaces for STL +// q4_OK assume all software is perfect +// q4_STRICT do not disable any compiler warnings +// q4_TINY small version, no floating point +// +///////////////////////////////////////////////////////////////////////////// + +#define __K4CONF_H__ // skip section in "mk4.h", since we use "header.h" + + // if neither MFC nor STD are specified, default to Universal version +#if !q4_MFC && !q4_STD && !defined (q4_UNIV) +#define q4_UNIV 1 +#endif + +///////////////////////////////////////////////////////////////////////////// +// You can either use '#define q4_xxx 1' to flag the choice of an OS, or +// use a '#define d4_OS_H "file.h"' to force inclusion of a header later. + +#if defined (__MINGW32__) +#define d4_OS_H "win.h" +#elif defined (MSDOS) && defined (__GNUC__) +#define q4_DOS 1 +#elif defined(unix) || defined(__unix__) || defined(__GNUC__) || \ + defined(_AIX) || defined(__hpux) +#define q4_UNIX 1 +#elif defined (__VMS) +#define q4_VMS 1 +#elif defined (macintosh) +#define q4_MAC 1 +#elif !defined (d4_OS_H) +#define d4_OS_H "win.h" +#endif + +///////////////////////////////////////////////////////////////////////////// +// Use '#define q4_xxx 1' to flag the choice of a CPU. + +#if defined (_M_I86) || defined (_M_IX86) || defined (i386) +#define q4_I86 1 +#if defined (_M_I86SM) +#define q4_TINY 1 +#endif +#elif defined (__powerc) +#define q4_PPC 1 +#elif defined (__alpha) +#define q4_AXP 1 +#define q4_LONG64 1 +#elif defined (__VMS) +#define q4_VAX 1 +#else +#define q4_M68K 1 +#endif + +///////////////////////////////////////////////////////////////////////////// +// Use '#define q4_xxx 1' to flag the choice of an IDE, and optionally also +// add '#include "file.h"' to force inclusion of a header file right here. + +#if defined (__BORLANDC__) // Borland C++ +#include "borc.h" +#elif defined (__DECCXX) // DEC C++ +#define q4_DECC 1 +#elif defined (__GNUC__) // GNU C++ +#include "gnuc.h" +#elif defined (__MWERKS__) // Metrowerks CodeWarrior C++ +#include "mwcw.h" +#elif defined (_MSC_VER) // Microsoft Visual C++ +#include "msvc.h" +#elif defined (__SC__) // Symantec C++ +#define q4_SYMC 1 +#elif defined (__WATCOMC__) // Watcom C++ +#define q4_WATC 1 +#endif + +///////////////////////////////////////////////////////////////////////////// +// Some of the options take precedence over others + +#if !q4_BOOL && !q4_STD // define a bool datatype +#define false 0 +#define true 1 +#define bool int +#endif + +#if !q4_CHECK // disable assertions +#undef d4_assert +#define d4_dbgdef(x) +#define d4_assert(x) +#endif + +#if q4_NO_NS // don't use namespaces +#define d4_std +#else +#define d4_std std +#endif + +#if HAVE_MEMMOVE +#define d4_memmove(d,s,n) memmove(d,s,n) +#elif HAVE_BCOPY +#define d4_memmove(d,s,n) bcopy(s,d,n) +#else +#define d4_memmove f4_memmove +extern void f4_memmove(void* d, const void* s, int n); +#endif + +typedef unsigned char t4_byte; // create typedefs for t4_byte, etc. + +///////////////////////////////////////////////////////////////////////////// +// Include header files which contain additional os/cpu/ide/fw specifics + +#ifdef d4_OS_H // operating system dependencies +#include d4_OS_H +#endif + +///////////////////////////////////////////////////////////////////////////// +// Several defines should always be set + +#ifndef d4_assert // assertion macro +#include <assert.h> +#define d4_assert assert +#endif + +#ifndef d4_dbgdef // conditionally compiled +#ifdef NDEBUG +#define d4_dbgdef(x) +#else +#define d4_dbgdef(x) x +#endif +#endif + +#ifndef d4_new // heap allocator +#define d4_new new +#endif + +#ifndef d4_reentrant // thread-local storage +#define d4_reentrant +#endif + +///////////////////////////////////////////////////////////////////////////// +// Debug logging option, called internally where properties are modified + +#if q4_LOGPROPMODS +void f4_DoLogProp(const c4_Handler*, int, const char*, int); +#else +#define f4_LogPropMods(a,b) 0 +#endif + +///////////////////////////////////////////////////////////////////////////// +// Public definitions, plus a few more framework-specific ones + +#include "mk4.h" + +#if q4_MFC +#include "mfc.h" +#elif q4_STD +#include "std.h" +#elif q4_UNIV +#include "univ.h" +#endif + +#ifdef _MSC_VER +#pragma warning(disable: 4100 4127 4135 4244 4511 4512 4514) +#endif + +#include <string.h> + +///////////////////////////////////////////////////////////////////////////// +// Report unexpected combinations of settings + +#if !q4_FIX +#if (q4_DOS+q4_MAC+q4_UNIX+q4_VMS+q4_WIN) != 1 +#error Exactly one operating system should have been defined +#endif +#if (q4_MFC+q4_STD+q4_UNIV) != 1 +#error Exactly one container library should have been defined +#endif +#endif + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/akregator/src/mk4storage/metakit/src/mfc.h b/akregator/src/mk4storage/metakit/src/mfc.h new file mode 100644 index 000000000..6e6d3e248 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/mfc.h @@ -0,0 +1,34 @@ +// mfc.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Configuration header for MFC-based builds + */ + +#define q4_MFC 1 + +#if q4_WIN && !q4_WIN32 +#include <afxwin.h> +#else +#include <afxcoll.h> +#endif + +#undef d4_assert +#define d4_assert ASSERT + +#undef d4_assertThis +#define d4_assertThis d4_assert(AfxIsValidAddress(this, sizeof *this, FALSE)) + +#undef d4_new +#define d4_new DEBUG_NEW + +typedef class CString c4_String; +typedef class CPtrArray c4_PtrArray; +typedef class CDWordArray c4_DWordArray; +typedef class CStringArray c4_StringArray; + + // MSVC 1.52 thinks a typedef has no constructor, so use a define instead +#if !q4_OK && q4_MSVC && _MSC_VER == 800 +#define c4_String CString +#endif diff --git a/akregator/src/mk4storage/metakit/src/msvc.h b/akregator/src/mk4storage/metakit/src/msvc.h new file mode 100644 index 000000000..a53e8b6dc --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/msvc.h @@ -0,0 +1,39 @@ +// msvc.h -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Configuration header for Microsoft Visual C++ + */ + +#define q4_MSVC 1 + + // get rid of several common warning messages +#if !q4_STRICT +//#pragma warning(disable: 4244) // conversion ..., possible loss of data +//#pragma warning(disable: 4135) // conversion between diff. integral types +//#pragma warning(disable: 4511) // copy constructor could not be generated +//#pragma warning(disable: 4512) // assignment op could not be generated +//#pragma warning(disable: 4514) // unreferenced inline removed +#pragma warning(disable: 4710) // function ... not inlined +#pragma warning(disable: 4711) // ... selected for automatic inline expansion +#pragma warning(disable: 4100) // unreferenced formal parameter +#endif + +#if _MSC_VER >= 1100 +#define q4_BOOL 1 // 5.0 supports the bool datatype +#else +#define q4_NO_NS 1 // 4.x doesn't use namespaces for STL +#endif + +#if defined (_MT) +#define q4_MULTI 1 // uses multi-threading +#endif + +#if defined (_DEBUG) && !defined (q4_CHECK) // use assertions in debug build +#define q4_CHECK 1 +#endif + +#if !q4_STD && !q4_UNIV && !defined (q4_MFC) +#define d4_FW_H "mfc.h" // default for MSVC is to use MFC +#endif diff --git a/akregator/src/mk4storage/metakit/src/mwcw.h b/akregator/src/mk4storage/metakit/src/mwcw.h new file mode 100644 index 000000000..1c863b967 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/mwcw.h @@ -0,0 +1,31 @@ +// mwcw.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Configuration header for Metrowerks CodeWarrior + */ + +#define q4_MWCW 1 + +///////////////////////////////////////////////////////////////////////////// + +#if q4_68K +#if !__option(IEEEdoubles) +#error Cannot build Metakit with 10-byte doubles +#endif +#endif + +#if __option(bool) +#define q4_BOOL 1 + // undo previous defaults, because q4_BOOL is not set early enough +#undef false +#undef true +#undef bool +#endif + +#undef _MSC_VER + +#pragma export on + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/persist.cpp b/akregator/src/mk4storage/metakit/src/persist.cpp new file mode 100644 index 000000000..65a9e94eb --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/persist.cpp @@ -0,0 +1,1185 @@ +// persist.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Implementation of the main file management classes + */ + +#include "header.h" +#include "column.h" +#include "persist.h" +#include "handler.h" +#include "store.h" +#include "field.h" + +///////////////////////////////////////////////////////////////////////////// + +class c4_FileMark +{ + enum { + kStorageFormat = 0x4C4A, // b0 = 'J', b1 = <4C> (on Intel) + kReverseFormat = 0x4A4C // b0 = <4C>, b1 = 'J' + }; + + t4_byte _data [8]; + +public: + c4_FileMark (); + c4_FileMark (t4_i32 pos_, bool flipped_, bool extend_); + c4_FileMark (t4_i32 pos_, int len_); + + t4_i32 Offset() const; + t4_i32 OldOffset() const; + + bool IsHeader() const; + bool IsOldHeader() const; + bool IsFlipped() const; +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_FileMark::c4_FileMark () +{ + d4_assert(sizeof *this == 8); +} + +c4_FileMark::c4_FileMark (t4_i32 pos_, bool flipped_, bool extend_) +{ + d4_assert(sizeof *this == 8); + *(short*) _data = flipped_ ? kReverseFormat : kStorageFormat; + _data[2] = extend_ ? 0x0A : 0x1A; + _data[3] = 0; + t4_byte* p = _data + 4; + for (int i = 24; i >= 0; i -= 8) + *p++ = (t4_byte) (pos_ >> i); + d4_assert(p == _data + sizeof _data); +} + +c4_FileMark::c4_FileMark (t4_i32 pos_, int len_) +{ + d4_assert(sizeof *this == 8); + t4_byte* p = _data; + *p++ = 0x80; + for (int j = 16; j >= 0; j -= 8) + *p++ = (t4_byte) (len_ >> j); + for (int i = 24; i >= 0; i -= 8) + *p++ = (t4_byte) (pos_ >> i); + d4_assert(p == _data + sizeof _data); +} + +t4_i32 c4_FileMark::Offset() const +{ + t4_i32 v = 0; + for (int i = 4; i < 8; ++i) + v = (v << 8) + _data[i]; + return v; +} + +t4_i32 c4_FileMark::OldOffset() const +{ + t4_i32 v = 0; + for (int i = 8; --i >= 4; ) + v = (v << 8) + _data[i]; + return v; +} + +bool c4_FileMark::IsHeader() const +{ + return (_data[0] == 'J' || _data[0] == 'L') && + (_data[0] ^ _data[1]) == ('J' ^ 'L') && _data[2] == 0x1A; +} + +bool c4_FileMark::IsOldHeader() const +{ + return IsHeader() && _data[3] == 0x80; +} + +bool c4_FileMark::IsFlipped() const +{ + return *(short*) _data == kReverseFormat; + +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_Allocator : public c4_DWordArray +{ +public: + c4_Allocator (); + + void Initialize(t4_i32 first_ =1); + + t4_i32 AllocationLimit() const; + + t4_i32 Allocate(t4_i32 len_); + void Occupy(t4_i32 pos_, t4_i32 len_); + void Release(t4_i32 pos_, t4_i32 len_); + void Dump(const char* str_); + +private: + int Locate(t4_i32 pos_) const; + void InsertPair(int i_, t4_i32 from_, t4_i32 to_); + t4_i32 ReduceFrags(int goal_, int sHi_, int sLo_); +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Allocation of blocks is maintained in a separate data structure. +// There is no allocation overhead in the allocation arena itself. +// +// A single vector of "walls" is maintained, sorted by position: +// +// * Each transition between free and allocated is a single entry. +// The number of entries is <num-free-ranges> + <num-used-ranges>. +// * By definition, free areas start at the positions indicated +// by the entries on even indices. Allocated ones use odd entries. +// * There is an extra <0,0> free slot at the very beginning. This +// simplifies boundary conditions at the start of the arena. +// * Position zero cannot be allocated, first slot starts at 1. +// +// Properties of this approach: +// +// * No allocation overhead for adjacent allocated areas. On the +// other hand, the allocator does not know the size of used slots. +// * Alternate function allows marking a specific range as occupied. +// * Allocator can be initialized as either all free or all in-use. +// * Allocation info contains only integers, it could be stored. +// * To extend allocated slots: "occupy" extra bytes at the end. +// * Generic: can be used for memory, disk files, and array entries. + +c4_Allocator::c4_Allocator () +{ + Initialize(); +} + +void c4_Allocator::Initialize(t4_i32 first_) +{ + SetSize(0, 1000); // empty, and growing in large chunks + Add(0); // fake block at start + Add(0); // ... only used to avoid merging + + // if occupied, add a tiny free slot at the end, else add entire range + const t4_i32 kMaxInt = 0x7fffffff; + if (first_ == 0) + first_ = kMaxInt; + + Add(first_); // start at a nicely aligned position + Add(kMaxInt); // ... there is no limit on file size +} + +t4_i32 c4_Allocator::Allocate(t4_i32 len_) +{ + // zero arg is ok, it simply returns first allocatable position + for (int i = 2; i < GetSize(); i += 2) + if (GetAt(i+1) >= GetAt(i) + len_) { + t4_i32 pos = GetAt(i); + if ((t4_i32) GetAt(i+1) > pos + len_) + ElementAt(i) += len_; + else + RemoveAt(i, 2); + return pos; + } + + d4_assert(0); + return 0; // not reached +} + +void c4_Allocator::Occupy(t4_i32 pos_, t4_i32 len_) +{ + d4_assert(pos_ > 0); + // note that zero size simply checks if there is any space to extend + + int i = Locate(pos_); + d4_assert(0 < i && i < GetSize()); + + if (i % 2) { // allocation is not at start of free block + d4_assert((t4_i32) GetAt(i-1) < pos_); + + if ((t4_i32) GetAt(i) == pos_ + len_) // allocate from end of free block + SetAt(i, pos_); + else // split free block in two + InsertPair(i, pos_, pos_ + len_); + } + else if ((t4_i32) GetAt(i) == pos_) +/* + This side of the if used to be unconditional, but that was + incorrect if ReduceFrags gets called (which only happens with + severely fragmented files) - there are cases when allocation + leads to an occupy request of which the free space list knows + nothing about because it dropped small segments. The solution + is to silently "allow" such allocations - fixed 29-02-2000 + Thanks to Andrew Kuchling for his help in chasing this bug. +*/ + { // else extend tail of allocated area + if ((t4_i32) GetAt(i+1) > pos_ + len_) + ElementAt(i) += len_; // move start of next free up + else + RemoveAt(i, 2); // remove this slot + } +} + +void c4_Allocator::Release(t4_i32 pos, t4_i32 len) +{ + int i = Locate(pos + len); + d4_assert(0 < i && i < GetSize()); + d4_assert(i % 2 == 0); // don't release inside a free block + + if ((t4_i32) GetAt(i) == pos) // move start of next free down + ElementAt(i) -= len; + else if ((t4_i32) GetAt(i-1) == pos) // move end of previous free up + ElementAt(i-1) += len; + else // insert a new entry + InsertPair(i, pos, pos + len); + + if (GetAt(i-1) == GetAt(i)) // merge if adjacent free + RemoveAt(i-1, 2); +} + +t4_i32 c4_Allocator::AllocationLimit() const +{ + d4_assert(GetSize() >= 2); + + return GetAt(GetSize() - 2); +} + +int c4_Allocator::Locate(t4_i32 pos) const +{ + int lo = 0, hi = GetSize() - 1; + + while (lo < hi) { + int i = (lo + hi) / 2; + if (pos < (t4_i32) GetAt(i)) + hi = i - 1; + else if (pos > (t4_i32) GetAt(i)) + lo = i + 1; + else + return i; + } + + return lo < GetSize() && pos > (t4_i32) GetAt(lo) ? lo + 1 : lo; +} + +void c4_Allocator::InsertPair(int i_, t4_i32 from_, t4_i32 to_) +{ + d4_assert(0 < i_); + d4_assert(i_ < GetSize()); + + d4_assert(from_ < to_); + d4_assert((t4_i32) GetAt(i_-1) < from_); + //!d4_assert(to_ < GetAt(i_)); + + if (to_ >= (t4_i32) GetAt(i_)) + return; // ignore 2nd allocation of used area + + InsertAt(i_, from_, 2); + SetAt(i_+1, to_); + + // it's ok to have arrays up to some 30000 bytes + if (GetSize() > 7500) + ReduceFrags(5000, 12, 6); +} + +t4_i32 c4_Allocator::ReduceFrags(int goal_, int sHi_, int sLo_) +{ + // drastic fail-safe measure: remove small gaps if vec gets too long + // this will cause some lost free space but avoids array overflow + // the lost space will most probably be re-used after the next commit + + int limit = GetSize() - 2; + t4_i32 loss = 0; + + // go through all entries and remove gaps under the given threshold + for (int shift = sHi_; shift >= sLo_; --shift) { + // the threshold is a fraction of the current size of the arena + t4_i32 threshold = AllocationLimit() >> shift; + if (threshold == 0) + continue; + + int n = 2; + for (int i = n; i < limit; i += 2) + if ((t4_i32) GetAt(i+1) - (t4_i32) GetAt(i) > threshold) { + SetAt(n++, GetAt(i)); + SetAt(n++, GetAt(i+1)); + } + else + loss += GetAt(i+1) - GetAt(i); + + limit = n; + + // if (GetSize() < goal_) - suboptimal, fixed 29-02-2000 + if (limit < goal_) + break; // got rid of enough entries, that's enough + } + + int n = GetSize() - 2; + SetAt(limit++, GetAt(n++)); + SetAt(limit++, GetAt(n)); + SetSize(limit); + + return loss; +} + +#if q4_CHECK +#include <stdio.h> + +void c4_Allocator::Dump(const char* str_) +{ + fprintf(stderr, "c4_Allocator::Dump, %d entries <%s>\n", GetSize(), str_); + for (int i = 2; i < GetSize(); i += 2) + fprintf(stderr, " %10ld .. %ld\n", GetAt(i-1), GetAt(i)); + fprintf(stderr, "END\n"); +} + +#else + +void c4_Allocator::Dump(const char* str_) { } + +#endif + +///////////////////////////////////////////////////////////////////////////// + +class c4_Differ +{ +public: + c4_Differ (c4_Storage& storage_); + ~c4_Differ (); + + int NewDiffID(); + void CreateDiff(int id_, c4_Column& col_); + t4_i32 BaseOfDiff(int id_); + void ApplyDiff(int id_, c4_Column& col_) const; + + void GetRoot(c4_Bytes& buffer_); + + c4_Storage _storage; + c4_View _diffs; + c4_View _temp; + +private: + void AddEntry(t4_i32, t4_i32, const c4_Bytes&); + + c4_ViewProp pCols; // column info: + c4_IntProp pOrig; // original position + c4_ViewProp pDiff; // difference chunks: + c4_IntProp pKeep; // offset + c4_IntProp pResize; // length + c4_BytesProp pBytes; // data +}; + +c4_Differ::c4_Differ (c4_Storage& storage_) + : _storage (storage_), pCols ("_C"), pOrig ("_O"), + pDiff ("_D"), pKeep ("_K"), pResize ("_R"), pBytes ("_B") +{ + // weird names, to avoid clashing with existing ones (capitalization!) + _diffs = _storage.GetAs("_C[_O:I,_D[_K:I,_R:I,_B:B]]"); +} + +c4_Differ::~c4_Differ () +{ + _diffs = c4_View (); +} + +void c4_Differ::AddEntry(t4_i32 off_, t4_i32 len_, const c4_Bytes& data_) +{ + int n = _temp.GetSize(); + _temp.SetSize(n + 1); + c4_RowRef r = _temp[n]; + + pKeep (r) = (t4_i32) off_; + pResize (r) = (t4_i32) len_; + pBytes (r).SetData(data_); +} + +int c4_Differ::NewDiffID() +{ + int n = _diffs.GetSize(); + _diffs.SetSize(n + 1); + return n; +} + +void c4_Differ::CreateDiff(int id_, c4_Column& col_) +{ + _temp.SetSize(0); +#if 0 + t4_i32 offset = 0; + t4_i32 savedOff = 0; + t4_i32 savedLen = 0; + + c4_Strategy* strat = col_.Persist() != 0 ? &col_.Strategy() : 0; + + c4_ColIter iter (col_, 0, col_.ColSize()); + while (iter.Next()) { + const t4_byte* p = iter.BufLoad(); + if (strat != 0 && strat->_mapStart != 0 && p >= strat->_mapStart && + p - strat->_mapStart < strat->_dataSize) + { + t4_i32 nextOff = p - strat->_mapStart; + if (savedLen == 0) + savedOff = nextOff; + if (nextOff == savedOff + savedLen) { + savedLen += iter.BufLen(); + continue; + } + + if (savedLen > 0) + AddEntry(savedOff, savedLen, c4_Bytes ()); + + savedOff = nextOff; + savedLen = iter.BufLen(); + } else { + AddEntry(savedOff, savedLen, c4_Bytes (p, iter.BufLen())); + savedLen = 0; + } + + offset += iter.BufLen(); + } + + c4_View diff = pDiff (_diffs[id_]); + if (_temp.GetSize() != diff.GetSize() || _temp != diff) +#else + c4_Bytes t1; + const t4_byte* p = col_.FetchBytes(0, col_.ColSize(), t1, false); + AddEntry(0, 0, c4_Bytes (p, col_.ColSize())); +#endif + pDiff (_diffs[id_]) = _temp; + + pOrig (_diffs[id_]) = col_.Position(); +} + +t4_i32 c4_Differ::BaseOfDiff(int id_) +{ + d4_assert(0 <= id_ && id_ < _diffs.GetSize()); + + return pOrig (_diffs[id_]); +} + +void c4_Differ::ApplyDiff(int id_, c4_Column& col_) const +{ + d4_assert(0 <= id_ && id_ < _diffs.GetSize()); + + c4_View diff = pDiff (_diffs[id_]); + t4_i32 offset = 0; + + for (int n = 0; n < diff.GetSize(); ++n) { + c4_RowRef row (diff[n]); + offset += pKeep (row); + + c4_Bytes data; + pBytes(row).GetData(data); + + // the following code is a lot like c4_MemoRef::Modify + const t4_i32 change = pResize (row); + if (change < 0) + col_.Shrink(offset, -change); + else if (change > 0) + col_.Grow(offset, change); + + col_.StoreBytes(offset, data); + offset += data.Size(); + } + + if (offset > col_.ColSize()) + col_.Shrink(offset, offset - col_.ColSize()); +} + +void c4_Differ::GetRoot(c4_Bytes& buffer_) +{ + int last = _diffs.GetSize() - 1; + if (last >= 0) { + c4_Bytes temp; + c4_View diff = pDiff (_diffs[last]); + if (diff.GetSize() > 0) + pBytes (diff[0]).GetData(buffer_); + } +} + +///////////////////////////////////////////////////////////////////////////// + +c4_SaveContext::c4_SaveContext (c4_Strategy& strategy_, bool fullScan_, + int mode_, c4_Differ* differ_, c4_Allocator* space_) + : _strategy (strategy_), _walk (0), _differ (differ_), _space (space_), + _cleanup (0), _nextSpace (0), _preflight (true), _fullScan (fullScan_), + _mode (mode_), _nextPosIndex (0), _bufPtr (_buffer), _curr (_buffer), + _limit (_buffer) +{ + if (_space == 0) + _space = _cleanup = d4_new c4_Allocator; + + _nextSpace = _mode == 1 ? d4_new c4_Allocator : _space; +} + +c4_SaveContext::~c4_SaveContext () +{ + delete _cleanup; + if (_nextSpace != _space) + delete _nextSpace; +} + +bool c4_SaveContext::IsFlipped() const +{ + return _strategy._bytesFlipped; +} + +bool c4_SaveContext::Serializing() const +{ + return _fullScan; +} + +void c4_SaveContext::AllocDump(const char* str_, bool next_) +{ + c4_Allocator* ap = next_ ? _nextSpace : _space; + if (ap != 0) + ap->Dump(str_); +} + +void c4_SaveContext::FlushBuffer() +{ + int n = _curr - _bufPtr; + if (_walk != 0 && n > 0) { + t4_i32 end = _walk->ColSize(); + _walk->Grow(end, n); + _walk->StoreBytes(end, c4_Bytes (_bufPtr, n)); + } + + _curr = _bufPtr = _buffer; + _limit = _buffer + sizeof _buffer; +} + +c4_Column* c4_SaveContext::SetWalkBuffer(c4_Column* col_) +{ + FlushBuffer(); + + c4_Column* prev = _walk; + _walk = col_; + return prev; +} + +void c4_SaveContext::Write(const void* buf_, int len_) +{ + // use buffering if possible + if (_curr + len_ <= _limit) { + memcpy(_curr, buf_, len_); + _curr += len_; + } else { + FlushBuffer(); + _bufPtr = (t4_byte*) buf_; // also loses const + _curr = _limit = _bufPtr + len_; + FlushBuffer(); + } +} + +void c4_SaveContext::StoreValue(t4_i32 v_) +{ + if (_walk == 0) + return; + + if (_curr + 10 >= _limit) + FlushBuffer(); + + d4_assert(_curr + 10 < _limit); + c4_Column::PushValue(_curr, v_); +} + +void c4_SaveContext::SaveIt(c4_HandlerSeq& root_, c4_Allocator** spacePtr_, + c4_Bytes& rootWalk_) +{ + d4_assert(_space != 0); + + const t4_i32 size = _strategy.FileSize(); + if (_strategy._failure != 0) + return; + + const t4_i32 end = _fullScan ? 0 : size - _strategy._baseOffset; + + if (_differ == 0) { + if (_mode != 1) + _space->Initialize(); + + // don't allocate anything inside the file in extend mode + if (_mode == 2 && end > 0) { + _space->Occupy(1, end - 1); + _nextSpace->Occupy(1, end - 1); + } + + // the header is always reserved + _space->Occupy(1, 7); + _nextSpace->Occupy(1, 7); + + if (end > 0) { + d4_assert(end >= 16); + _space->Occupy(end - 16, 16); + _nextSpace->Occupy(end - 16, 16); + _space->Occupy(end, 8); + _nextSpace->Occupy(end, 8); + } + } + + //AllocDump("a1", false); + //AllocDump("a2", true); + + // first pass allocates columns and constructs shallow walks + c4_Column walk (root_.Persist()); + SetWalkBuffer(&walk); + CommitSequence(root_, true); + SetWalkBuffer(0); + CommitColumn(walk); + + c4_Bytes tempWalk; + walk.FetchBytes(0, walk.ColSize(), tempWalk, true); + + t4_i32 limit = _nextSpace->AllocationLimit(); + d4_assert(limit >= 8 || _differ != 0); + + bool changed = _fullScan || tempWalk != rootWalk_; + + rootWalk_ = c4_Bytes (tempWalk.Contents(), tempWalk.Size(), true); + + _preflight = false; + + // special-case to avoid saving data if file is logically empty + // in that case, the data is 0x80 0x81 0x80 (plus the header) + if (!_fullScan && limit <= 11 && _differ == 0) { + _space->Initialize(); + _nextSpace->Initialize(); + changed = false; + } + + if (!changed) + return; + + //AllocDump("b1", false); + //AllocDump("b2", true); + + if (_differ != 0) { + int n = _differ->NewDiffID(); + _differ->CreateDiff(n, walk); + return; + } + + d4_assert(_mode != 0 || _fullScan); + + // this is the place where writing may start + + // figure out where the new file ends and write a skip tail there + t4_i32 end0 = end; + + // true if the file need not be extended due to internal free space + bool inPlace = end0 == limit - 8; + if (inPlace) { + d4_assert(!_fullScan); + _space->Release(end0, 8); + _nextSpace->Release(end0, 8); + end0 -= 16; // overwrite existing tail markers + } else { + c4_FileMark head (limit + 16 - end, _strategy._bytesFlipped, end > 0); + _strategy.DataWrite(end, &head, sizeof head); + + if (end0 < limit) + end0 = limit; // create a gap + } + + t4_i32 end1 = end0 + 8; + t4_i32 end2 = end1 + 8; + + if (!_fullScan && !inPlace) { + c4_FileMark mark1 (end0, 0); + _strategy.DataWrite(end0, &mark1, sizeof mark1); +#if q4_WIN32 + /* March 8, 2002 + * On at least NT4 with NTFS, extending a file can cause it to be + * rounded up further than expected. To prevent creating a bad + * file (since the file does then not end with a marker), the + * workaround it so simply accept the new end instead and rewrite. + * Note that between these two writes, the file is in a bad state. + */ + t4_i32 realend = _strategy.FileSize() - _strategy._baseOffset; + if (realend > end1) { + end0 = limit = realend - 8; + end1 = realend; + end2 = realend + 8; + c4_FileMark mark1a (end0, 0); + _strategy.DataWrite(end0, &mark1a, sizeof mark1a); + } +#endif + d4_assert(_strategy.FileSize() == _strategy._baseOffset + end1); + } + + _space->Occupy(end0, 16); + _nextSpace->Occupy(end0, 16); + + // strategy.DataCommit(0); // may be needed, need more info on how FS's work + // but this would need more work, since we can't adjust file-mapping here + + // second pass saves the columns and structure to disk + CommitSequence(root_, true); // writes changed columns + CommitColumn(walk); + + //! d4_assert(_curr == 0); + d4_assert(_nextPosIndex == _newPositions.GetSize()); + + if (_fullScan) { + c4_FileMark mark1 (limit, 0); + _strategy.DataWrite(_strategy.FileSize() - _strategy._baseOffset, + &mark1, sizeof mark1); + + c4_FileMark mark2 (limit - walk.ColSize(), walk.ColSize()); + _strategy.DataWrite(_strategy.FileSize() - _strategy._baseOffset, + &mark2, sizeof mark2); + + return; + } + + if (inPlace) + d4_assert(_strategy.FileSize() == _strategy._baseOffset + end2); + else { + // make sure the allocated size hasn't changed + d4_assert(_nextSpace->AllocationLimit() == limit + 16); + d4_assert(end0 >= limit); + d4_assert(_strategy.FileSize() - _strategy._baseOffset == end1); + } + + if (walk.Position() == 0 || _strategy._failure != 0) + return; + + _strategy.DataCommit(0); + + c4_FileMark mark2 (walk.Position(), walk.ColSize()); + _strategy.DataWrite(end1, &mark2, sizeof mark2); + d4_assert(_strategy.FileSize() - _strategy._baseOffset == end2); + + // do not alter the file header in extend mode, unless it is new + if (!_fullScan && (_mode == 1 || end == 0)) { + _strategy.DataCommit(0); + + c4_FileMark head (end2, _strategy._bytesFlipped, false); + d4_assert(head.IsHeader()); + _strategy.DataWrite(0, &head, sizeof head); + + // if the file became smaller, we could shrink it + if (limit + 16 < end0) { +/* + Not yet, this depends on the strategy class being able to truncate, but + there is no way to find out whether it does (the solution is to write tail + markers in such a way that the file won't grow unnecessarily if it doesn't). + + The logic will probably be: + + * write new skip + commit "tails" at limit (no visible effect on file) + * overwrite commit tail at end with a skip to this new one (equivalent) + * replace header with one pointing to that internal new one (equivalent) + * flush (now the file is valid both truncated and not-yet-truncated + + end = limit; +*/ + } + } + + // if using memory mapped files, make sure the map is no longer in use + if (_strategy._mapStart != 0) + root_.UnmappedAll(); + + // commit and tell strategy object what the new file size is, this + // may be smaller now, if old data at the end is no longer referenced + _strategy.DataCommit(end2); + + d4_assert(_strategy.FileSize() - _strategy._baseOffset == end2); + + if (spacePtr_ != 0 && _space != _nextSpace) { + d4_assert(*spacePtr_ == _space); + delete *spacePtr_; + *spacePtr_ = _nextSpace; + _nextSpace = 0; + } +} + +bool c4_SaveContext::CommitColumn(c4_Column& col_) +{ + bool changed = col_.IsDirty() || _fullScan; + + t4_i32 sz = col_.ColSize(); + StoreValue(sz); + if (sz > 0) { + t4_i32 pos = col_.Position(); + + if (_differ) { + if (changed) { + int n = pos < 0 ? ~pos : _differ->NewDiffID(); + _differ->CreateDiff(n, col_); + + d4_assert(n >= 0); + pos = ~n; + } + } else if (_preflight) { + if (changed) + pos = _space->Allocate(sz); + + _nextSpace->Occupy(pos, sz); + _newPositions.Add(pos); + } else { + pos = _newPositions.GetAt(_nextPosIndex++); + + if (changed) + col_.SaveNow(_strategy, pos); + + if (!_fullScan) + col_.SetLocation(pos, sz); + } + + StoreValue(pos); + } + + return changed; +} + +void c4_SaveContext::CommitSequence(c4_HandlerSeq& seq_, bool selfDesc_) +{ + StoreValue(0); // sias prefix + + if (selfDesc_) { + c4_String desc = seq_.Description(); + int k = desc.GetLength(); + StoreValue(k); + Write((const char*) desc, k); + } + + StoreValue(seq_.NumRows()); + if (seq_.NumRows() > 0) + for (int i = 0; i < seq_.NumFields(); ++i) + seq_.NthHandler(i).Commit(*this); +} + +///////////////////////////////////////////////////////////////////////////// + + // used for on-the-fly conversion of old-format datafiles + t4_byte* _oldBuf; + const t4_byte* _oldCurr; + const t4_byte* _oldLimit; + t4_i32 _oldSeek; + + +c4_Persist::c4_Persist (c4_Strategy& strategy_, bool owned_, int mode_) + : _space (0), _strategy (strategy_), _root (0), _differ (0), + _fCommit (0), _mode (mode_), _owned (owned_), _oldBuf (0), + _oldCurr (0), _oldLimit (0), _oldSeek (-1) +{ + if (_mode == 1) + _space = d4_new c4_Allocator; +} + +c4_Persist::~c4_Persist () +{ + delete _differ; + + if (_owned) { + if (_root != 0) + _root->UnmappedAll(); + delete &_strategy; + } + + delete _space; + + if (_oldBuf != 0) + delete [] _oldBuf; +} + +c4_HandlerSeq& c4_Persist::Root() const +{ + d4_assert(_root != 0); + return *_root; +} + +void c4_Persist::SetRoot(c4_HandlerSeq* root_) +{ + d4_assert(_root == 0); + _root = root_; +} + +c4_Strategy& c4_Persist::Strategy() const +{ + return _strategy; +} + +bool c4_Persist::AutoCommit(bool flag_) +{ + bool prev = _fCommit != 0; + if (flag_) + _fCommit = &c4_Persist::Commit; + else + _fCommit = 0; + return prev; +} + +void c4_Persist::DoAutoCommit() +{ + if (_fCommit != 0) + (this->*_fCommit)(false); +} + +bool c4_Persist::SetAside(c4_Storage& aside_) +{ + delete _differ; + _differ = d4_new c4_Differ (aside_); + Rollback(false); + return true; //! true if the generation matches +} + +c4_Storage* c4_Persist::GetAside() const +{ + return _differ != 0 ? &_differ->_storage : 0; +} + +bool c4_Persist::Commit(bool full_) +{ + // 1-Mar-1999, new semantics! return success status of commits + _strategy._failure = 0; + + if (!_strategy.IsValid()) + return false; + + if (_mode == 0 && (_differ == 0 || full_)) // can't commit to r/o file + return false; // note that _strategy._failure is *zero* in this case + + c4_SaveContext ar (_strategy, false, _mode, full_ ? 0 : _differ, _space); + + // get rid of temp properties which still use the datafile + if (_mode == 1) + _root->DetachFromStorage(false); + + // 30-3-2001: moved down, fixes "crash every 2nd call of mkdemo/dbg" + ar.SaveIt(*_root, &_space, _rootWalk); + return _strategy._failure == 0; +} + +bool c4_Persist::Rollback(bool full_) +{ + _root->DetachFromParent(); + _root->DetachFromStorage(true); + _root = 0; + + if (_space != 0) + _space->Initialize(); + + c4_HandlerSeq* seq = d4_new c4_HandlerSeq (this); + seq->DefineRoot(); + SetRoot(seq); + + if (full_) { + delete _differ; + _differ = 0; + } + + LoadAll(); + + return _strategy._failure == 0; +} + +bool c4_Persist::LoadIt(c4_Column& walk_) +{ + t4_i32 limit = _strategy.FileSize(); + if (_strategy._failure != 0) + return false; + + if (_strategy.EndOfData(limit) < 0) { + _strategy.SetBase(limit); + d4_assert(_strategy._failure == 0); // file is ok, but empty + return false; + } + + if (_strategy._rootLen > 0) + walk_.SetLocation(_strategy._rootPos, _strategy._rootLen); + + // if the file size has increased, we must remap + if (_strategy._mapStart != 0 && + _strategy.FileSize() > _strategy._baseOffset + _strategy._dataSize) + _strategy.ResetFileMapping(); + + return true; +} + +void c4_Persist::LoadAll() +{ + c4_Column walk (this); + if (!LoadIt(walk)) + return; + + if (_strategy._rootLen < 0) { + _oldSeek = _strategy._rootPos; + _oldBuf = d4_new t4_byte [512]; + _oldCurr = _oldLimit = _oldBuf; + + t4_i32 n = FetchOldValue(); + d4_assert(n == 0); + n = FetchOldValue(); + d4_assert(n > 0); + + c4_Bytes temp; + t4_byte* buf = temp.SetBuffer(n); + d4_dbgdef(int n2 =) + OldRead(buf, n); + d4_assert(n2 == n); + + c4_String s = "[" + c4_String ((const char*) buf, n) + "]"; + const char* desc = s; + + c4_Field* f = d4_new c4_Field (desc); + d4_assert(!*desc); + + //?_root->DefineRoot(); + _root->Restructure(*f, false); + + _root->OldPrepare(); + + // don't touch data inside while converting the file + if (_strategy.FileSize() >= 0) + OccupySpace(1, _strategy.FileSize()); + } else { + walk.FetchBytes(0, walk.ColSize(), _rootWalk, true); + if (_differ) + _differ->GetRoot(_rootWalk); + + // define and fill the root table + const t4_byte* ptr = _rootWalk.Contents(); + _root->Prepare(&ptr, true); + d4_assert(ptr == _rootWalk.Contents() + _rootWalk.Size()); + } +} + +t4_i32 c4_Persist::FetchOldValue() +{ + d4_assert(_oldSeek >= 0); + + if (_oldCurr == _oldLimit) { + int n = OldRead(_oldBuf, 500); + _oldLimit = _oldCurr + n; + _oldBuf[n] = 0x80; // to force end + } + + const t4_byte* p = _oldCurr; + t4_i32 value = c4_Column::PullValue(p); + + if (p > _oldLimit) { + int k = _oldLimit - _oldCurr; + d4_assert(0 < k && k < 10); + memcpy(_oldBuf, _oldCurr, k); + + int n = OldRead(_oldBuf + k, 500); + _oldCurr = _oldBuf + k; + _oldLimit = _oldCurr + n; + _oldBuf[n+k] = 0x80; // to force end + + p = _oldCurr; + value = c4_Column::PullValue(p); + d4_assert(p <= _oldLimit); + } + + _oldCurr = p; + return value; +} + +void c4_Persist::FetchOldLocation(c4_Column& col_) +{ + d4_assert(_oldSeek >= 0); + + t4_i32 sz = FetchOldValue(); + if (sz > 0) + col_.SetLocation(FetchOldValue(), sz); +} + +int c4_Persist::OldRead(t4_byte* buf_, int len_) +{ + d4_assert(_oldSeek >= 0); + + t4_i32 newSeek = _oldSeek + _oldCurr - _oldLimit; + int n = _strategy.DataRead(newSeek, buf_, len_); + d4_assert(n > 0); + _oldSeek = newSeek + n; + _oldCurr = _oldLimit = _oldBuf; + return n; +} + +c4_HandlerSeq* c4_Persist::Load(c4_Stream* stream_) +{ + d4_assert(stream_ != 0); + + c4_FileMark head; + if (stream_->Read(&head, sizeof head) != sizeof head || !head.IsHeader()) + return 0; // no data in file + + //_oldStyle = head._data[3] == 0x80; + d4_assert(!head.IsOldHeader()); + + t4_i32 limit = head.Offset(); + + c4_StreamStrategy* strat = d4_new c4_StreamStrategy (limit); + strat->_bytesFlipped = head.IsFlipped(); + strat->DataWrite(strat->FileSize() - strat->_baseOffset, &head, sizeof head); + + while (strat->FileSize() - strat->_baseOffset < limit) { + char buffer [4096]; + int n = stream_->Read(buffer, sizeof buffer); + d4_assert(n > 0); + strat->DataWrite(strat->FileSize() - strat->_baseOffset, buffer, n); + } + + c4_Persist* pers = d4_new c4_Persist (*strat, true, 0); + c4_HandlerSeq* seq = d4_new c4_HandlerSeq (pers); + seq->DefineRoot(); + pers->SetRoot(seq); + + c4_Column walk (pers); + if (!pers->LoadIt(walk)) { + seq->IncRef(); + seq->DecRef(); // a funny way to delete + return 0; + } + + c4_Bytes tempWalk; + walk.FetchBytes(0, walk.ColSize(), tempWalk, true); + + const t4_byte* ptr = tempWalk.Contents(); + seq->Prepare(&ptr, true); + d4_assert(ptr == tempWalk.Contents() + tempWalk.Size()); + + return seq; +} + +void c4_Persist::Save(c4_Stream* stream_, c4_HandlerSeq& root_) +{ + d4_assert(stream_ != 0); + + c4_StreamStrategy strat (stream_); + + // 31-01-2002: streaming must adopt byte order of origin datafile + c4_Persist* p = root_.Persist(); + if (p != 0) + strat._bytesFlipped = p->Strategy()._bytesFlipped; + + c4_SaveContext ar (strat, true, 0, 0, 0); + c4_Bytes tempWalk; + ar.SaveIt(root_, 0, tempWalk); +} + +t4_i32 c4_Persist::LookupAside(int id_) +{ + d4_assert(_differ != 0); + + return _differ->BaseOfDiff(id_); +} + +void c4_Persist::ApplyAside(int id_, c4_Column& col_) +{ + d4_assert(_differ != 0); + + _differ->ApplyDiff(id_, col_); +} + +void c4_Persist::OccupySpace(t4_i32 pos_, t4_i32 len_) +{ + d4_assert(_mode != 1 || _space != 0); + + if (_space != 0) + _space->Occupy(pos_, len_); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/persist.h b/akregator/src/mk4storage/metakit/src/persist.h new file mode 100644 index 000000000..61ef261ee --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/persist.h @@ -0,0 +1,127 @@ +// persist.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Definition of the core file management classes + */ + +#ifndef __PERSIST_H__ +#define __PERSIST_H__ + +///////////////////////////////////////////////////////////////////////////// +// Declarations in this file + + class c4_SaveContext; // wraps file commits + class c4_Persist; // persistent table storage + + class c4_Allocator; // not defined here + class c4_Column; // not defined here + class c4_Differ; // not defined here + class c4_FileMark; // not defined here + class c4_Strategy; // not defined here + class c4_HandlerSeq; // not defined here + +///////////////////////////////////////////////////////////////////////////// + +class c4_SaveContext +{ + c4_Strategy& _strategy; + c4_Column* _walk; + c4_Differ* _differ; + + c4_Allocator* _space; + c4_Allocator* _cleanup; + c4_Allocator* _nextSpace; + + bool _preflight; + bool _fullScan; + int _mode; + + c4_DWordArray _newPositions; + int _nextPosIndex; + + t4_byte* _bufPtr; + t4_byte* _curr; + t4_byte* _limit; + t4_byte _buffer [512]; + +public: + c4_SaveContext (c4_Strategy& strategy_, bool fullScan_, int mode_, + c4_Differ* differ_, c4_Allocator* space_); + ~c4_SaveContext (); + + void SaveIt(c4_HandlerSeq& root_, c4_Allocator** spacePtr_, + c4_Bytes& rootWalk_); + + void StoreValue(t4_i32 v_); + bool CommitColumn(c4_Column& col_); + void CommitSequence(c4_HandlerSeq& seq_, bool selfDesc_); + + c4_Column* SetWalkBuffer(c4_Column* walk_); + bool IsFlipped() const; + + bool Serializing() const; + void AllocDump(const char*, bool =false); + +private: + void FlushBuffer(); + void Write(const void* buf_, int len_); +}; + +///////////////////////////////////////////////////////////////////////////// + +class c4_Persist +{ + c4_Allocator* _space; + c4_Strategy& _strategy; + c4_HandlerSeq* _root; + c4_Differ* _differ; + c4_Bytes _rootWalk; + bool (c4_Persist::*_fCommit) (bool); + int _mode; + bool _owned; + + // used for on-the-fly conversion of old-format datafiles + t4_byte* _oldBuf; + const t4_byte* _oldCurr; + const t4_byte* _oldLimit; + t4_i32 _oldSeek; + + int OldRead(t4_byte* buf_, int len_); + +public: + c4_Persist (c4_Strategy&, bool owned_, int mode_); + ~c4_Persist (); + + c4_HandlerSeq& Root() const; + void SetRoot(c4_HandlerSeq* root_); + c4_Strategy& Strategy() const; + + bool AutoCommit(bool =true); + void DoAutoCommit(); + + bool SetAside(c4_Storage& aside_); + c4_Storage* GetAside() const; + + bool Commit(bool full_); + bool Rollback(bool full_); + + bool LoadIt(c4_Column& walk_); + void LoadAll(); + + t4_i32 LookupAside(int id_); + void ApplyAside(int id_, c4_Column& col_); + + void OccupySpace(t4_i32 pos_, t4_i32 len_); + + t4_i32 FetchOldValue(); + void FetchOldLocation(c4_Column& col_); + + static c4_HandlerSeq* Load(c4_Stream*); + static void Save(c4_Stream*, c4_HandlerSeq& root_); +}; + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/akregator/src/mk4storage/metakit/src/remap.cpp b/akregator/src/mk4storage/metakit/src/remap.cpp new file mode 100644 index 000000000..cc8175df2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/remap.cpp @@ -0,0 +1,1154 @@ +// remap.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file +* Mapping and remapping custom viewers +*/ + +#include "header.h" +#include "remap.h" +#include "handler.h" + +///////////////////////////////////////////////////////////////////////////// + +class c4_ReadOnlyViewer : public c4_CustomViewer +{ + c4_View _base; + +public: + c4_ReadOnlyViewer (c4_Sequence& seq_) : _base (&seq_) { } + virtual ~c4_ReadOnlyViewer () { } + + virtual c4_View GetTemplate() { return _base.Clone(); } + virtual int GetSize() { return _base.GetSize(); } + + virtual int Lookup(c4_Cursor key_, int& count_) + { int pos = 0; count_ = _base.GetSize(); + return _base.RestrictSearch(*key_, pos, count_); } + + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_) + { return _base.GetItem(row_, col_, buf_); } +}; + +///////////////////////////////////////////////////////////////////////////// + +class c4_HashViewer : public c4_CustomViewer +{ + c4_View _base; + c4_View _map; + int _numKeys; + + c4_IntProp _pHash; + c4_IntProp _pRow; + + bool KeySame(int row_, c4_Cursor cursor_) const; + t4_i32 CalcHash(c4_Cursor cursor_) const; + int LookDict(t4_i32 hash_, c4_Cursor cursor_) const; + void InsertDict(int row_); + void RemoveDict(int pos_); + bool DictResize(int minused); + + int Row(int i_) const { return _pRow (_map[i_]); } + int Hash(int i_) const { return _pHash (_map[i_]); } + + void SetRow(int i_, int v_) { _pRow (_map[i_]) = v_; } + void SetHash(int i_, int v_) { _pHash (_map[i_]) = v_; } + + bool IsUnused(int) const; + bool IsDummy(int) const; + bool IsActive(int i_) const { return Row(i_) >= 0; } + + int GetPoly() const; + void SetPoly(int v_); + int GetSpare() const; + void SetSpare(int v_); + +public: + c4_HashViewer (c4_Sequence& seq_, int numKeys_, + c4_Sequence* map_=0); + virtual ~c4_HashViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual int Lookup(c4_Cursor key_, int& count_); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_); + virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); + virtual bool RemoveRows(int pos_, int count_=1); +}; + +///////////////////////////////////////////////////////////////////////////// +// The following contains code derived froms Python's dictionaries, hence: +// Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, +// The Netherlands. +// Reduced and turned into a fast C++ class by Christian Tismer, hence: +// Copyright 1999 by Christian Tismer. +// Vectorized and reorganized further by Jean-Claude Wippler. +///////////////////////////////////////////////////////////////////////////// + +// Table of irreducible polynomials to efficiently cycle through +// GF(2^n)-{0}, 2<=n<=30. + +static long s_polys[] = { + 4 + 3, + 8 + 3, + 16 + 3, + 32 + 5, + 64 + 3, + 128 + 3, + 256 + 29, + 512 + 17, + 1024 + 9, + 2048 + 5, + 4096 + 83, + 8192 + 27, + 16384 + 43, + 32768 + 3, + 65536 + 45, + 131072 + 9, + 262144 + 39, + 524288 + 39, + 1048576 + 9, + 2097152 + 5, + 4194304 + 3, + 8388608 + 33, + 16777216 + 27, + 33554432 + 9, + 67108864 + 71, + 134217728 + 39, + 268435456 + 9, + 536870912 + 5, + 1073741824 + 83, + 0 +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_HashViewer::c4_HashViewer (c4_Sequence& seq_, int numKeys_, + c4_Sequence* map_) + : _base (&seq_), _map (map_), _numKeys (numKeys_), + _pHash ("_H"), _pRow ("_R") +{ + if (_map.GetSize() == 0) + _map.SetSize(1); + + int poly = GetPoly(); + if (poly == 0 || _map.GetSize() <= _base.GetSize()) + DictResize(_base.GetSize()); +} + +c4_HashViewer::~c4_HashViewer () +{ +} + +bool c4_HashViewer::IsUnused(int row_) const +{ + c4_RowRef r = _map[row_]; + return _pRow (r) < 0 && _pHash (r) == 0; +} + +bool c4_HashViewer::IsDummy(int row_) const +{ + c4_RowRef r = _map[row_]; + return _pRow (r) < 0 && _pHash (r) < 0; +} + +int c4_HashViewer::GetPoly() const +{ + return Hash(_map.GetSize()-1); +} + +void c4_HashViewer::SetPoly(int v_) +{ + SetHash(_map.GetSize()-1, v_); +} + +int c4_HashViewer::GetSpare() const +{ + return Row(_map.GetSize()-1); +} + +void c4_HashViewer::SetSpare(int v_) +{ + SetRow(_map.GetSize()-1, v_); +} + +bool c4_HashViewer::KeySame(int row_, c4_Cursor cursor_) const +{ + for (int i = 0; i < _numKeys; ++i) + { + c4_Bytes buffer; + _base.GetItem(row_, i, buffer); + + c4_Handler& h = cursor_._seq->NthHandler(i); + if (h.Compare(cursor_._index, buffer) != 0) + return false; + } + + return true; +} + +/// Create mapped view which is uses a second view for hashing +t4_i32 c4_HashViewer::CalcHash(c4_Cursor cursor_) const +{ + c4_Bytes buffer, buf2; + const t4_i32 endian = 0x03020100; + t4_i32 hash = 0; + + for (int i = 0; i < _numKeys; ++i) + { + c4_Handler& h = cursor_._seq->NthHandler(i); + cursor_._seq->Get(cursor_._index, h.PropId(), buffer); + + // this code borrows from Python's stringobject.c/string_hash() + int len = buffer.Size(); + if (len > 0) + { + const t4_byte* p = buffer.Contents(); + + // 20030218: careful to avoid endian-ness sensitivity + if (*(const t4_byte*) &endian) // true on big-endian systems + switch (h.Property().Type()) + { + case 'I': case 'L': case 'F': case 'D': + { + t4_byte* q = buf2.SetBuffer(len); + for (int j = 0; j < len; ++j) + q[len-j-1] = p[j]; + p = q; + } + } + + long x = *p << 7; + + // modifications are risky, this code avoid scanning huge blobs + if (len > 200) + len = 100; + + while (--len >= 0) + x = (1000003 * x) ^ *p++; + + if (buffer.Size() > 200) + { + len = 100; + p += buffer.Size() - 200; + while (--len >= 0) + x = (1000003 * x) ^ *p++; + } + + x ^= buffer.Size(); + hash ^= x ^ i; + } + } + + if (hash == 0) + hash = -1; + + return hash; +} + +/* + * Types of slots: + * Unused: row = -1, hash = 0 + * Dummy: row = -1, hash = -1 + * Active: row >= 0 + * There must be at least one Unused slot at all times. + */ + +int c4_HashViewer::LookDict(t4_i32 hash_, c4_Cursor cursor_) const +{ + const unsigned int mask = _map.GetSize() - 2; + /* We must come up with (i, incr) such that 0 <= i < _size + and 0 < incr < _size and both are a function of hash */ + int i = mask & ~hash_; + /* We use ~hash_ instead of hash_, as degenerate hash functions, such + as for ints <sigh>, can have lots of leading zeros. It's not + really a performance risk, but better safe than sorry. */ + if (IsUnused(i) || Hash(i) == hash_ && KeySame(Row(i), cursor_)) + return i; + + int freeslot = IsDummy(i) ? i : -1; + + /* Derive incr from hash_, just to make it more arbitrary. Note that + incr must not be 0, or we will get into an infinite loop.*/ + unsigned incr = (hash_ ^ ((unsigned long) hash_ >> 3)) & mask; + if (!incr) + incr = mask; + + int poly = GetPoly(); + for (;;) + { + i = (i+incr) & mask; + if (IsUnused(i)) + break; + if (Hash(i) == hash_ && KeySame(Row(i), cursor_)) + return i; + if (freeslot == -1 && IsDummy(i)) + freeslot = i; + /* Cycle through GF(2^n)-{0} */ + incr = incr << 1; + if (incr > mask) + incr ^= poly; /* This will implicitely clear the highest bit */ + } + + return freeslot != -1 ? freeslot : i; +} + +void c4_HashViewer::InsertDict(int row_) +{ + c4_Cursor cursor = &_base[row_]; + + t4_i32 hash = CalcHash(cursor); + int i = LookDict(hash, cursor); + + if (IsDummy(i)) + { + int n = GetSpare(); + d4_assert(n > 0); + SetSpare(n - 1); + } + + SetHash(i, hash); + SetRow(i, row_); +} + +void c4_HashViewer::RemoveDict(int pos_) +{ + c4_Cursor key = &_base[pos_]; + t4_i32 hash = CalcHash(key); + int i = LookDict(hash, key); + d4_assert(i >= 0); + + d4_assert(Row(i) == pos_); + + SetHash(i, -1); + SetRow(i, -1); + + SetSpare(GetSpare() + 1); +} + +bool c4_HashViewer::DictResize(int minused) +{ + int i, newsize, newpoly; + for (i = 0, newsize = 4; ; i++, newsize <<= 1) { + if (s_polys[i] == 0) + return false; + else if (newsize > minused) { + newpoly = s_polys[i]; + break; + } + } + + _map.SetSize(0); + + c4_Row empty; + _pRow (empty) = -1; + _map.InsertAt(0, empty, newsize + 1); + + SetPoly(newpoly); + SetSpare(0); + + for (int j = 0; j < _base.GetSize(); ++j) + InsertDict(j); + + return true; +} + +c4_View c4_HashViewer::GetTemplate() +{ + return _base.Clone(); +} + +int c4_HashViewer::GetSize() +{ + return _base.GetSize(); +} + +int c4_HashViewer::Lookup(c4_Cursor key_, int& count_) +{ + // can only use hashing if the properties match the query + // XXX it appears that this loop takes some 300 uS! + c4_View kv = (*key_).Container(); + for (int k = 0; k < _numKeys; ++k) + if (kv.FindProperty(_base.NthProperty(k).GetId()) < 0) + return -1; + + t4_i32 hash = CalcHash(key_); // TODO should combine with above loop + int i = LookDict(hash, key_); + + int row = Row(i); + count_ = row >= 0 && KeySame(row, key_) ? 1 : 0; + return count_ ? row : 0; // don't return -1, we *know* it's not there +} + +bool c4_HashViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + return _base.GetItem(row_, col_, buf_); +} + +bool c4_HashViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + if (col_ < _numKeys) + { + c4_Bytes temp; + _base.GetItem(row_, col_, temp); + if (buf_ == temp) + return true; // this call will have no effect, just ignore it + + RemoveDict(row_); + } + + _base.SetItem(row_, col_, buf_); + + if (col_ < _numKeys) + { + // careful if changing a key to one which is already present: + // in that case, delete the other row to preserve uniqueness + // + // Note: this is a tricky and confusing issue, because now the + // mere act of *setting* a property value can *delete* a row! + // + // The big problem here is that setting the rest of the values + // in a loop can end up *wrong*, if the row has moved down!!! + int n; + int i = Lookup(&_base[row_], n); + if (i >= 0 && n > 0) + { + RemoveRows(i, 1); + if (i < row_) + --row_; + } + + InsertDict(row_); + } + + return true; +} + +bool c4_HashViewer::InsertRows(int pos_, c4_Cursor value_, int count_) +{ + d4_assert(count_ > 0); + + int n; + int i = Lookup(value_, n); + if (i >= 0 && n > 0) + { + _base.SetAt(i, *value_); // replace existing + return true; + } + + // adjust row numbers if the insertion is not at the end + // + // TODO this could be optimized to go through the rows which + // were moved up, and then adjusting the map through a lookup + // (probably better than full scan if pos_ is relatively high) + if (pos_ < _base.GetSize()) + { + for (int r = 0; r < _map.GetSize() - 1; ++r) + { + int n2 = Row(r); + if (n2 >= pos_) + SetRow(r, n2 + 1); + } + } + + _base.InsertAt(pos_, *value_); + InsertDict(pos_); + + int used = _base.GetSize(); + int fill = used + GetSpare(); + if (fill * 3 >= (_map.GetSize() - 1) * 2 && !DictResize(used * 2)) + return false; // bail out + + d4_assert(_base.GetSize() + GetSpare() < _map.GetSize() - 1); + return true; +} + +bool c4_HashViewer::RemoveRows(int pos_, int count_) +{ + while (--count_ >= 0) + { + // since the map persists, be somewhat more aggressive than the + // original code in resizing down when the map is getting empty + if (_base.GetSize() * 3 < _map.GetSize() - 1 && + !DictResize(_base.GetSize())) + return false; // bail out + + RemoveDict(pos_); + + // move rows down for now + // + // optionally: consider replacing with last entry (much faster) + for (int r = 0; r < _map.GetSize() - 1; ++r) + { + int n = Row(r); + if (n > pos_) + SetRow(r, n - 1); + } + + _base.RemoveAt(pos_, 1); + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_BlockedViewer : public c4_CustomViewer +{ + enum { kLimit = 1000 }; + + c4_View _base; + + c4_ViewProp _pBlock; + c4_DWordArray _offsets; + + int Slot(int& pos_); + void Split(int block_, int row_); + void Merge(int block_); + void Validate() const; + +public: + c4_BlockedViewer (c4_Sequence& seq_); + virtual ~c4_BlockedViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_); + virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); + virtual bool RemoveRows(int pos_, int count_=1); +}; + +///////////////////////////////////////////////////////////////////////////// + +#if q4_CHECK + + // debugging version to verify that the internal data is consistent + void c4_BlockedViewer::Validate() const + { + d4_assert(_base.GetSize() >= 2); + + int n = _base.GetSize() - 1; + d4_assert(_offsets.GetSize() == n); + + int total = 0; + for (int i = 0; i < n; i++) + { + c4_View bv = _pBlock (_base[i]); + d4_assert(bv.GetSize() > 0 || i == 0); + total += bv.GetSize(); + d4_assert((int) _offsets.GetAt(i) == total++); + } + + c4_View be = _pBlock (_base[n]); + d4_assert(be.GetSize() == n - 1); + } + +#else + + // nothing, so inline this thing to avoid even the calling overhead + d4_inline void c4_BlockedViewer::Validate() const + { + } + +#endif + +c4_BlockedViewer::c4_BlockedViewer (c4_Sequence& seq_) + : _base (&seq_), _pBlock ("_B") +{ + if (_base.GetSize() < 2) + _base.SetSize(2); + + int n = _base.GetSize() - 1; + _offsets.SetSize(n); + + int total = 0; + for (int i = 0; i < n; i++) + { + c4_View bv = _pBlock (_base[i]); + total += bv.GetSize(); + _offsets.SetAt(i, total++); + } + Validate(); +} + +c4_BlockedViewer::~c4_BlockedViewer () +{ +} + +int c4_BlockedViewer::Slot(int& pos_) +{ + d4_assert(_offsets.GetSize() > 0); + d4_assert(pos_ <= (int) _offsets.GetAt(_offsets.GetSize() - 1)); + +#if 0 + const int n = _offsets.GetSize(); + + int h; + for (h = 0; h < n; ++h) + if (pos_ <= (t4_i32) _offsets.GetAt(h)) + break; +#else + // switch to binary search, adapted from code by Zhang Dehua, 28-3-2002 + // slows down some 5%, but said to be much better with 5 million rows + int l = 0, h = _offsets.GetSize() - 1; + while (l < h) { + int m = l + (h - l) / 2; + if ((t4_i32) _offsets.GetAt(m) < pos_) + l = m + 1; + else + h = m; + } +#endif + + if (h > 0) + pos_ -= _offsets.GetAt(h-1) + 1; + + return h; +} + +void c4_BlockedViewer::Split(int bno_, int row_) +{ + int z = _base.GetSize() - 1; + d4_assert(bno_ < z); + c4_View bz = _pBlock (_base[z]); + c4_View bv = _pBlock (_base[bno_]); + d4_assert(row_ < bv.GetSize()); + + _offsets.InsertAt(bno_, _offsets.GetAt(bno_) - bv.GetSize() + row_); + + _base.InsertAt(bno_+1, c4_Row ()); + c4_View bn = _pBlock (_base[bno_+1]); + + bv.RelocateRows(row_ + 1, -1, bn, 0); + bv.RelocateRows(row_, 1, bz, bno_); + + Validate(); +} + +void c4_BlockedViewer::Merge(int bno_) +{ + int z = _base.GetSize() - 1; + c4_View bz = _pBlock (_base[z]); + c4_View bv1 = _pBlock (_base[bno_]); + c4_View bv2 = _pBlock (_base[bno_+1]); + + _offsets.RemoveAt(bno_); + + bz.RelocateRows(bno_, 1, bv1, -1); + bv2.RelocateRows(0, -1, bv1, -1); + + _base.RemoveAt(bno_+1); + + Validate(); +} + +c4_View c4_BlockedViewer::GetTemplate() +{ + c4_View bv = _pBlock (_base[0]); + return bv.Clone(); +} + +int c4_BlockedViewer::GetSize() +{ + int n = _offsets.GetAt(_offsets.GetSize() - 1); + d4_assert(n >= 0); + return n; +} + +bool c4_BlockedViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + int orig = row_; + + int i = Slot(row_); + d4_assert(0 <= i && i < _base.GetSize() - 1); + + if ((t4_i32) _offsets.GetAt(i) == orig) + { + row_ = i; + i = _base.GetSize() - 1; + } + + c4_View bv = _pBlock (_base[i]); + return bv.GetItem(row_, col_, buf_); +} + +bool c4_BlockedViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + int orig = row_; + + int i = Slot(row_); + d4_assert(0 <= i && i < _base.GetSize() - 1); + + if ((t4_i32) _offsets.GetAt(i) == orig) + { + row_ = i; + i = _base.GetSize() - 1; + } + + c4_View bv = _pBlock (_base[i]); + bv.SetItem(row_, col_, buf_); + return true; +} + +bool c4_BlockedViewer::InsertRows(int pos_, c4_Cursor value_, int count_) +{ + d4_assert(count_ > 0); + + bool atEnd = pos_ == GetSize(); + + int z = _base.GetSize() - 1; + int i = Slot(pos_); + d4_assert(0 <= i && i < z); + + c4_View bv = _pBlock (_base[i]); + d4_assert(0 <= pos_ && pos_ <= bv.GetSize()); + + bv.InsertAt(pos_, *value_, count_); + for (int j = i; j < z; ++j) + _offsets.SetAt(j, _offsets.GetAt(j) + count_); + + // massive insertions are first split off + while (bv.GetSize() >= 2 * kLimit) + Split(i, bv.GetSize() - kLimit - 2); + + if (bv.GetSize() > kLimit ) + Split(i, atEnd ? kLimit - 1 : bv.GetSize() / 2); // 23-3-2002, from MB + + Validate(); + + return true; +} + +bool c4_BlockedViewer::RemoveRows(int pos_, int count_) +{ + d4_assert(count_ > 0); + d4_assert(pos_ + count_ <= GetSize()); + + int z = _base.GetSize() - 1; + int i = Slot(pos_); + d4_assert(0 <= i && i < z); + + c4_View bv = _pBlock (_base[i]); + d4_assert(0 <= pos_ && pos_ <= bv.GetSize()); + + int todo = count_; + + // optimize if the deletion goes past the end of this block... + int overshoot = pos_ + count_ - bv.GetSize(); + if (overshoot > 0) { + + // first, delete blocks which are going away completely + while (i+1 < _offsets.GetSize()) { + int nextsize = _offsets.GetAt(i+1) - _offsets.GetAt(i); + if (overshoot < nextsize) + break; + todo -= nextsize; + overshoot -= nextsize; + + // drop the block and forget it ever existed + for (int j = i+1; j < z; ++j) + _offsets.SetAt(j, _offsets.GetAt(j) - nextsize); + _offsets.RemoveAt(i+1); + + _base.RemoveAt(i+1); + --z; + c4_View bz = _pBlock (_base[z]); + bz.RemoveAt(i); + + Validate(); + } + + // delete before merging, to avoid useless copying + if (overshoot > 1) { + c4_View bv2 = _pBlock (_base[i+1]); + bv2.RemoveAt(0, overshoot - 1); + todo -= overshoot - 1; + + for (int j = i+1; j < z; ++j) + _offsets.SetAt(j, _offsets.GetAt(j) - (overshoot - 1)); + + // if the next block is filled enough, rotate the separator + // this avoids an expensive and unnecessary merge + split + if (bv2.GetSize() > kLimit / 2) { + c4_View bz = _pBlock (_base[z]); + bz[i] = bv2[0]; + bv2.RemoveAt(0); + --todo; + d4_assert(pos_ + todo <= bv.GetSize()); + d4_assert(i < _offsets.GetSize()); + + for (int j = i+1; j < z; ++j) + _offsets.SetAt(j, _offsets.GetAt(j) - 1); + } + } + + // merge into one block + if (pos_ + todo > bv.GetSize()) { + d4_assert(i < z - 1); + Merge(i); + --z; + } + } + d4_assert(pos_ + todo <= bv.GetSize()); + + // now remove the rows and adjust offsets + if (todo > 0) + bv.RemoveAt(pos_, todo); + + for (int j = i; j < z; ++j) + _offsets.SetAt(j, _offsets.GetAt(j) - todo); + + // if the block underflows, merge it + if (bv.GetSize() < kLimit / 2) { + if (i > 0) // merge with predecessor, preferably + bv = _pBlock (_base[--i]); + if (i >= z - 1) // unless there is no successor to merge with + return true; + Merge(i); + } + + // if the block overflows, split it + if (bv.GetSize() > kLimit ) + Split(i, bv.GetSize() / 2); + + Validate(); + + return true; +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_OrderedViewer : public c4_CustomViewer +{ + c4_View _base; + int _numKeys; + + int KeyCompare(int row_, c4_Cursor cursor_) const; + +public: + c4_OrderedViewer (c4_Sequence& seq_, int numKeys_); + virtual ~c4_OrderedViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual int Lookup(c4_Cursor key_, int& count_); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_); + virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); + virtual bool RemoveRows(int pos_, int count_=1); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_OrderedViewer::c4_OrderedViewer (c4_Sequence& seq_, int numKeys_) + : _base (&seq_), _numKeys (numKeys_) +{ +} + +c4_OrderedViewer::~c4_OrderedViewer () +{ +} + +int c4_OrderedViewer::KeyCompare(int row_, c4_Cursor cursor_) const +{ + for (int i = 0; i < _numKeys; ++i) + { + c4_Bytes buffer; + _base.GetItem(row_, i, buffer); + + c4_Handler& h = cursor_._seq->NthHandler(i); + int f = h.Compare(cursor_._index, buffer); + if (f != 0) + return f; + } + + return 0; +} + +c4_View c4_OrderedViewer::GetTemplate() +{ + return _base.Clone(); +} + +int c4_OrderedViewer::GetSize() +{ + return _base.GetSize(); +} + +int c4_OrderedViewer::Lookup(c4_Cursor key_, int& count_) +{ + // can only use bsearch if the properties match the query + // XXX with ord1.tcl (dict words), this loop takes 300 uS! + c4_View kv = (*key_).Container(); + for (int k = 0; k < _numKeys; ++k) + if (kv.FindProperty(_base.NthProperty(k).GetId()) < 0) + return -1; + +#if 0 // Locate gets the count wrong, it seems 2000-06-15 + int pos; + count_ = _base.Locate(*key_, &pos); +#else + int pos = _base.Search(*key_); + count_ = pos < _base.GetSize() && KeyCompare(pos, key_) == 0 ? 1 : 0; +#endif + return pos; +} + +bool c4_OrderedViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + return _base.GetItem(row_, col_, buf_); +} + +bool c4_OrderedViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + if (col_ < _numKeys) + { + c4_Bytes temp; + _base.GetItem(row_, col_, temp); + if (buf_ == temp) + return true; // this call will have no effect, just ignore it + } + + _base.SetItem(row_, col_, buf_); + + if (col_ < _numKeys) + { + c4_Row copy = _base[row_]; + // have to remove the row because it messes up searching + // it would be more efficient to search *around* this row + // or perhaps figure out new pos before changing any data + RemoveRows(row_); + InsertRows(0, ©); // position is ignored + } + + return true; +} + +bool c4_OrderedViewer::InsertRows(int, c4_Cursor value_, int count_) +{ + d4_assert(count_ > 0); + + int n; + int i = Lookup(value_, n); + + // XXX if the lookup does not work, then insert as first element (!?) + d4_assert(i >= 0); + if (i < 0) + i = 0; + + if (n == 0) + _base.InsertAt(i, *value_); + else + { + d4_assert(i < _base.GetSize()); + _base.SetAt(i, *value_); // replace existing + } + + return true; +} + +bool c4_OrderedViewer::RemoveRows(int pos_, int count_) +{ + _base.RemoveAt(pos_, count_); + return true; +} + +///////////////////////////////////////////////////////////////////////////// + +class c4_IndexedViewer : public c4_CustomViewer +{ + c4_View _base; + c4_View _map; + c4_View _props; + bool _unique; + c4_IntProp _mapProp; + + int KeyCompare(int row_, c4_Cursor cursor_) const; + +public: + c4_IndexedViewer (c4_Sequence& seq_, c4_Sequence& map_, + const c4_View& props_, bool unique_); + virtual ~c4_IndexedViewer (); + + virtual c4_View GetTemplate(); + virtual int GetSize(); + virtual int Lookup(c4_Cursor key_, int& count_); + virtual bool GetItem(int row_, int col_, c4_Bytes& buf_); + virtual bool SetItem(int row_, int col_, const c4_Bytes& buf_); + virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); + virtual bool RemoveRows(int pos_, int count_=1); +}; + +///////////////////////////////////////////////////////////////////////////// + +c4_IndexedViewer::c4_IndexedViewer (c4_Sequence& seq_, c4_Sequence& map_, + const c4_View& props_, bool unique_) + : _base (&seq_), _map (&map_), _props (props_), _unique (unique_), + _mapProp ((const c4_IntProp&) _map.NthProperty(0)) +{ + int n = _base.GetSize(); + if (_map.GetSize() != n) + { + c4_View sorted = _base.SortOn(_props); + + _map.SetSize(n); + for (int i = 0; i < n; ++i) + _mapProp (_map[i]) = _base.GetIndexOf(sorted[i]); + } +} + +c4_IndexedViewer::~c4_IndexedViewer () +{ +} + +int c4_IndexedViewer::KeyCompare(int row_, c4_Cursor cursor_) const +{ + int n = _props.NumProperties(); + for (int i = 0; i < n; ++i) + { + c4_Bytes buffer; + _base.GetItem(row_, i, buffer); + + c4_Handler& h = cursor_._seq->NthHandler(i); + int f = h.Compare(cursor_._index, buffer); + if (f != 0) + return f; + } + + return 0; +} + +c4_View c4_IndexedViewer::GetTemplate() +{ + return _base.Clone(); +} + +int c4_IndexedViewer::GetSize() +{ + return _base.GetSize(); +} + +int c4_IndexedViewer::Lookup(c4_Cursor key_, int& count_) +{ + // can only use bsearch if the properties match the query + // XXX with ord1.tcl (dict words), this loop takes 300 uS! + c4_View kv = (*key_).Container(); + int n = _props.NumProperties(); + for (int k = 0; k < n; ++k) + if (kv.FindProperty(_props.NthProperty(k).GetId()) < 0) + return -1; + +#if 0 // Locate gets the count wrong, it seems 2000-06-15 + int pos; + count_ = _base.Locate(*key_, &pos); +#else + int pos = _base.Search(*key_); + count_ = pos < _base.GetSize() && KeyCompare(pos, key_) == 0 ? 1 : 0; +#endif + return pos; +} + +bool c4_IndexedViewer::GetItem(int row_, int col_, c4_Bytes& buf_) +{ + return _base.GetItem(row_, col_, buf_); +} + +bool c4_IndexedViewer::SetItem(int row_, int col_, const c4_Bytes& buf_) +{ + const int id = _base.NthProperty(col_).GetId(); + const bool keyMod = _props.FindProperty(id) >= 0; + + if (keyMod) + { + c4_Bytes temp; + _base.GetItem(row_, col_, temp); + if (buf_ == temp) + return true; // this call will have no effect, just ignore it + } + + _base.SetItem(row_, col_, buf_); + + if (keyMod) + { + // TODO adjust index + } + + return true; +} + +bool c4_IndexedViewer::InsertRows(int, c4_Cursor value_, int count_) +{ + d4_assert(count_ > 0); + + if (_unique) + count_ = 1; + + int n; + int i = Lookup(value_, n); + + // XXX if the lookup does not work, then insert as first element (!?) + d4_assert(i >= 0); + if (i < 0) + i = 0; + + if (n == 0) + _base.InsertAt(i, *value_); + else + { + d4_assert(i < _base.GetSize()); + _base.SetAt(i, *value_); // replace existing + } + + return true; +} + +bool c4_IndexedViewer::RemoveRows(int pos_, int count_) +{ + _base.RemoveAt(pos_, count_); + + int n = _map.GetSize(); + while (--n >= 0) + { + int v = _mapProp (_map[n]); + if (v >= pos_) + if (v < pos_ + count_) + _map.RemoveAt(n); + else + _mapProp (_map[n]) = v - count_; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_CustomViewer* f4_CreateReadOnly(c4_Sequence& seq_) +{ + return d4_new c4_ReadOnlyViewer (seq_); +} + +c4_CustomViewer* f4_CreateHash(c4_Sequence& seq_, int nk_, c4_Sequence* map_) +{ + return d4_new c4_HashViewer (seq_, nk_, map_); +} + +c4_CustomViewer* f4_CreateBlocked(c4_Sequence& seq_) +{ + return d4_new c4_BlockedViewer (seq_); +} + +c4_CustomViewer* f4_CreateOrdered(c4_Sequence& seq_, int nk_) +{ + return d4_new c4_OrderedViewer (seq_, nk_); +} + +c4_CustomViewer* f4_CreateIndexed(c4_Sequence& seq_, c4_Sequence& map_, + const c4_View& props_, bool unique_) +{ + return d4_new c4_IndexedViewer (seq_, map_, props_, unique_); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/remap.h b/akregator/src/mk4storage/metakit/src/remap.h new file mode 100644 index 000000000..a435f853b --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/remap.h @@ -0,0 +1,26 @@ +// remap.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Encapsulation of the (re)mapping viewers + */ + +#ifndef __REMAP_H__ +#define __REMAP_H__ + +///////////////////////////////////////////////////////////////////////////// +// Declarations in this file + + class c4_Sequence; // not defined here + + extern c4_CustomViewer* f4_CreateReadOnly(c4_Sequence&); + extern c4_CustomViewer* f4_CreateHash(c4_Sequence&, int, c4_Sequence* =0); + extern c4_CustomViewer* f4_CreateBlocked(c4_Sequence&); + extern c4_CustomViewer* f4_CreateOrdered(c4_Sequence&, int); + extern c4_CustomViewer* f4_CreateIndexed(c4_Sequence&, c4_Sequence&, + const c4_View&, bool =false); + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/akregator/src/mk4storage/metakit/src/std.cpp b/akregator/src/mk4storage/metakit/src/std.cpp new file mode 100644 index 000000000..135fe7766 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/std.cpp @@ -0,0 +1,84 @@ +// std.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Implementation of STL-based strings and containers + */ + +#include "header.h" + +#if q4_STD // until end of source +///////////////////////////////////////////////////////////////////////////// + +#include "column.h" // c4_ColCache + +#if !q4_INLINE +static char _mk4stdInl[] = "mk4str.inl"; +#include "mk4str.inl" +#endif + +#if !q4_NO_NS +using namespace std; +#endif + +///////////////////////////////////////////////////////////////////////////// +// Implemented in this file + + class c4_String; + +///////////////////////////////////////////////////////////////////////////// + +#if !q4_MSVC && !q4_WATC + + // MS C/C++ has this handy stricmp: a case-insensitive version of strcmp + // This version only works with 7-bit ASCII characters 0x00 through 0x7F + + static int stricmp(const char* p1, const char* p2) + { + int c1, c2; + +#ifdef d4_USE_UNOPTIMIZED_CODE + do + { + c1 = tolower(*p1++); + c2 = tolower(*p2++); + } while (c1 != 0 && c1 == c2); +#else + do + { + c1 = *p1++; + c2 = *p2++; + } while (c1 != 0 && (c1 == c2 || tolower(c1) == tolower(c2))); + + c1 = tolower(c1); + c2 = tolower(c2); +#endif + + return c1 - c2; + } + +#endif + +///////////////////////////////////////////////////////////////////////////// +// c4_String + +c4_String c4_String::Mid(int nFirst_, int nCount_) const +{ + int n = length(); + if (nFirst_ > n) + nFirst_ = n; + if (nFirst_ + nCount_ > n) + nCount_ = n - nFirst_; + + return substr(nFirst_, nCount_); +} + +int c4_String::CompareNoCase(const char* str_) const +{ + // this is not very "standard library-ish" ... + return *(const string*) this == str_ ? 0 : stricmp(c_str(), str_); +} + +///////////////////////////////////////////////////////////////////////////// +#endif // q4_STD diff --git a/akregator/src/mk4storage/metakit/src/std.h b/akregator/src/mk4storage/metakit/src/std.h new file mode 100644 index 000000000..1d1937be3 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/std.h @@ -0,0 +1,63 @@ +// std.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Configuration header for STL-based builds + */ + +#define q4_STD 1 + +#include "mk4str.h" + +#include <vector> + +///////////////////////////////////////////////////////////////////////////// + +template<class T> +class c4_ArrayT +{ +#ifdef _MSC_VER + d4_std::vector< T, d4_std::allocator<T> > _vector; +#else + d4_std::vector< T, d4_std::alloc > _vector; +#endif + +public: + c4_ArrayT () { } + ~c4_ArrayT () { } + + int GetSize() const { return _vector.size(); } + void SetSize(int nNewSize, int =-1) { _vector.resize(nNewSize); } + + T GetAt(int nIndex) const { return _vector[nIndex]; } + T& ElementAt(int nIndex) { return _vector[nIndex]; } + + void SetAt(int nIndex, const T& newElement) + { + _vector[nIndex] = newElement; + } + + int Add(const T& newElement) + { + int n = _vector.size(); + _vector.push_back(newElement); + return n; + } + + void InsertAt(int nIndex, const T& newElement, int nCount =1) + { + _vector.insert(&_vector[nIndex], nCount, newElement); + } + + void RemoveAt(int nIndex, int nCount =1) + { + _vector.erase(&_vector[nIndex], &_vector[nIndex+nCount]); + } +}; + +typedef c4_ArrayT<t4_i32> c4_DWordArray; +typedef c4_ArrayT<void*> c4_PtrArray; +typedef c4_ArrayT<c4_String> c4_StringArray; + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/store.cpp b/akregator/src/mk4storage/metakit/src/store.cpp new file mode 100644 index 000000000..a32de665c --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/store.cpp @@ -0,0 +1,587 @@ +// store.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Storage management and several other loose ends + */ + +#include "header.h" +#include "handler.h" // 19990906 +#include "store.h" +#include "field.h" +#include "persist.h" +#include "format.h" // 19990906 + +#include "mk4io.h" // 19991104 + +#if !q4_INLINE +#include "store.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + +c4_Dependencies::c4_Dependencies () +{ + _refs.SetSize(0, 3); // a little optimization +} + +c4_Dependencies::~c4_Dependencies () +{ +} + +void c4_Dependencies::Add(c4_Sequence* seq_) +{ + for (int i = 0; i < _refs.GetSize(); ++i) + d4_assert(_refs.GetAt(i) != seq_); + + _refs.Add(seq_); +} + +bool c4_Dependencies::Remove(c4_Sequence* seq_) +{ + int n = _refs.GetSize() - 1; + d4_assert(n >= 0); + + for (int i = 0; i <= n; ++i) + if (_refs.GetAt(i) == seq_) { + _refs.SetAt(i, _refs.GetAt(n)); + _refs.SetSize(n); + return n > 0; + } + + d4_assert(0); // dependency not found + return true; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_Notifier::~c4_Notifier () +{ + if (_type > kNone && _origin->GetDependencies()) { + c4_PtrArray& refs = _origin->GetDependencies()->_refs; + + for (int i = 0; i < refs.GetSize(); ++i) { + c4_Sequence* seq = (c4_Sequence*) refs.GetAt(i); + d4_assert(seq != 0); + + seq->PostChange(*this); + + if (_chain && _chain->_origin == seq) { + c4_Notifier* next = _chain->_next; + _chain->_next = 0; + + delete _chain; + + _chain = next; + } + } + } + + d4_assert(!_chain); + d4_assert(!_next); +} + +void c4_Notifier::StartSetAt(int index_, c4_Cursor& cursor_) +{ + _type = kSetAt; + _index = index_; + _cursor = &cursor_; + + Notify(); +} + +void c4_Notifier::StartInsertAt(int i_, c4_Cursor& cursor_, int n_) +{ + _type = kInsertAt; + _index = i_; + _cursor = &cursor_; + _count = n_; + + Notify(); +} + +void c4_Notifier::StartRemoveAt(int index_, int count_) +{ + _type = kRemoveAt; + _index = index_; + _count = count_; + + Notify(); +} + +void c4_Notifier::StartMove(int from_, int to_) +{ + _type = kMove; + _index = from_; + _count = to_; + + Notify(); +} + +void c4_Notifier::StartSet(int i_, int propId_, const c4_Bytes& buf_) +{ + _type = kSet; + _index = i_; + _propId = propId_; + _bytes = &buf_; + + Notify(); +} + +void c4_Notifier::Notify() +{ + d4_assert(_origin->GetDependencies() != 0); + c4_PtrArray& refs = _origin->GetDependencies()->_refs; + + int n = refs.GetSize(); + d4_assert(n > 0); + + c4_Notifier** rover = &_chain; + + for (int i = 0; i < n; ++i) { + c4_Sequence* seq = (c4_Sequence*) refs.GetAt(i); + d4_assert(seq != 0); + + c4_Notifier* ptr = seq->PreChange(*this); + if (ptr) { + d4_assert(ptr->_origin == seq); + + d4_assert(!*rover); + *rover = ptr; + rover = &ptr->_next; + } + } +} + +///////////////////////////////////////////////////////////////////////////// + +/** @class c4_Storage + * + * Manager for persistent storage of view structures. + * + * The storage class uses a view, with additional functionality to be able + * to store and reload the data it contains (including nested subviews). + * + * By default, data is loaded on demand, i.e. whenever data which has + * not yet been referenced is used for the first time. Loading is limited + * to the lifetime of this storage object, since the storage object carries + * the file descriptor with it that is needed to access the data file. + * + * To save changes, call the Commit member. This is the only time + * that data is written to file - when using a read-only file simply avoid + * calling Commit. + * + * The LoadFromStream and SaveToStream members can be used to + * serialize the contents of this storage row using only sequential I/O + * (no seeking, only read or write calls). + * + * The data storage mechanism implementation provides fail-safe operation: + * if anything prevents Commit from completing its task, the last + * succesfully committed version of the saved data will be recovered on + * the next open. This also includes changes made to the table structure. + * + * The following code creates a view with 1 row and stores it on file: + * @code + * c4_StringProp pName ("Name"); + * c4_IntProp pAge ("Age"); + * + * c4_Storage storage ("myfile.dat", true); + * c4_View myView = storage.GetAs("Musicians[Name:S,Age:I]"); + * + * myView.Add(pName ["John Williams"] + pAge [43]); + * + * storage.Commit(); + * @endcode + */ + +c4_Storage::c4_Storage () +{ + // changed to r/o, now that commits don't crash on it anymore + Initialize(*d4_new c4_Strategy, true, 0); +} + +c4_Storage::c4_Storage (c4_Strategy& strategy_, bool owned_, int mode_) +{ + Initialize(strategy_, owned_, mode_); + Persist()->LoadAll(); +} + +c4_Storage::c4_Storage (const char* fname_, int mode_) +{ + c4_FileStrategy* strat = d4_new c4_FileStrategy; + strat->DataOpen(fname_, mode_); + + Initialize(*strat, true, mode_); + if (strat->IsValid()) + Persist()->LoadAll(); +} + +c4_Storage::c4_Storage (const c4_View& root_) +{ + if (root_.Persist() != 0) // only restore if view was indeed persistent + *(c4_View*) this = root_; + else // if this was not possible, start with a fresh empty storage + Initialize(*d4_new c4_Strategy, true, 0); +} + +c4_Storage::~c4_Storage () +{ + // cannot unmap here, because there may still be an autocommit pending + //((c4_HandlerSeq*) _seq)->UnmapAll(); +} + +void c4_Storage::Initialize(c4_Strategy& strategy_, bool owned_, int mode_) +{ + c4_Persist* pers = d4_new c4_Persist (strategy_, owned_, mode_); + c4_HandlerSeq* seq = d4_new c4_HandlerSeq (pers); + seq->DefineRoot(); + *(c4_View*) this = seq; + pers->SetRoot(seq); +} + + /// Get or set a named view in this storage object +c4_ViewRef c4_Storage::View(const char* name_) +{ + /* + The easy solution would seem to be: + + c4_ViewProp prop (name_); + return prop (Contents()); + + But this does not work, because the return value would point to + an object allocated on the stack. + + Instead, make sure the view *has* such a property, and use the + one inside the c4_Handler for it (since this will stay around). + */ + +// int n = _root->PropIndex(c4_ViewProp (name_)); + + c4_ViewProp prop (name_); + int n = AddProperty(prop); + d4_assert(n >= 0); + + // the following is an expression of the form "property (rowref)" + return NthProperty(n) (GetAt(0)); +} + + /// Get a named view, redefining it to match the given structure +c4_View c4_Storage::GetAs(const char* description_) +{ + d4_assert(description_ != 0); + + // Dec 2001: now that GetAs is being used so much more frequently, + // add a quick check to see whether restructuring is needed at all + const char* q = strchr(description_, '['); + if (q != 0) { + c4_String vname (description_, q - description_); + const char* d = Description(vname); + if (d != 0) { + c4_String desc (d); + if (("[" + desc + "]").CompareNoCase(q) == 0) + return View(vname); + } + } + + c4_Field* field = d4_new c4_Field (description_); + d4_assert(field != 0); + + d4_assert(!*description_); + + c4_String name = field->Name(); + d4_assert(!name.IsEmpty()); + + c4_Field& curr = Persist()->Root().Definition(); + + c4_String newField = "," + field->Description(); + bool keep = newField.Find('[') >= 0; + + c4_String newDef; + + // go through all subfields + for (int i = 0; i < curr.NumSubFields(); ++i) { + c4_Field& of = curr.SubField(i); + if (of.Name().CompareNoCase(name) == 0) { + if (field->IsRepeating()) + newDef += newField; + // else new is not a repeating entry, so drop this entire field + + newField.Empty(); // don't append it later on + continue; + } + + newDef += "," + of.Description(); // keep original field + } + + if (keep) // added 19990824 ignore if deletion + newDef += newField; // appends new definition if not found earlier + + delete field; + + const char* p = newDef; + SetStructure(*p ? ++p : p); // skip the leading comma + + if (!keep) // 19990916: avoid adding an empty view again + return c4_View (); + + return View(name); +} + + /// Define the complete view structure of the storage +void c4_Storage::SetStructure(const char* description_) +{ + d4_assert(description_ != 0); + + if (description_ != Description()) { + c4_String s = "[" + c4_String (description_) + "]"; + description_ = s; + + c4_Field* field = d4_new c4_Field (description_); + d4_assert(!*description_); + + d4_assert(field != 0); + Persist()->Root().Restructure(*field, false); + } +} + + /// Return the strategy object associated with this storage +c4_Strategy& c4_Storage::Strategy() const +{ + return Persist()->Strategy(); +} + + /// Return a description of the view structure (default is all) +const char* c4_Storage::Description(const char* name_) +{ + if (name_ == 0 || *name_ == 0) + return c4_View::Description(); + + c4_View v = View(name_); + return v.Description(); +} + + /// Define the storage to use for differential commits +bool c4_Storage::SetAside(c4_Storage& aside_) +{ + c4_Persist* pers = Persist(); + bool f = pers->SetAside(aside_); + // adjust our copy when the root view has been replaced + *(c4_View*) this = &pers->Root(); + return f; +} + + /// Return storage used for differential commits, or null +c4_Storage* c4_Storage::GetAside() const +{ + return Persist()->GetAside(); +} + + /// Flush pending changes to file right now +bool c4_Storage::Commit(bool full_) +{ + return Strategy().IsValid() && Persist()->Commit(full_); +} + +/** (Re)initialize for on-demand loading + * + * Calling Rollback will cancel all uncommitted changes. + */ +bool c4_Storage::Rollback(bool full_) +{ + c4_Persist* pers = Persist(); + bool f = Strategy().IsValid() && pers->Rollback(full_); + // adjust our copy when the root view has been replaced + *(c4_View*) this = &pers->Root(); + return f; +} + + /// Set storage up to always call Commit in the destructor +bool c4_Storage::AutoCommit(bool flag_) +{ + return Persist()->AutoCommit(flag_); +} + + /// Load contents from the specified input stream +bool c4_Storage::LoadFrom(c4_Stream& stream_) +{ + c4_HandlerSeq* newRoot = c4_Persist::Load(&stream_); + if (newRoot == 0) + return false; + + // fix commit-after-load bug, by using a full view copy + // this is inefficient, but avoids mapping/strategy problems + c4_View temp (newRoot); + + SetSize(0); + SetStructure(temp.Description()); + InsertAt(0, temp); + + return true; +} + + /// Save contents to the specified output stream +void c4_Storage::SaveTo(c4_Stream& stream_) +{ + c4_Persist::Save(&stream_, Persist()->Root()); +} + +///////////////////////////////////////////////////////////////////////////// + +c4_DerivedSeq::c4_DerivedSeq (c4_Sequence& seq_) + : _seq (seq_) +{ + _seq.Attach(this); +} + +c4_DerivedSeq::~c4_DerivedSeq () +{ + _seq.Detach(this); +} + +int c4_DerivedSeq::RemapIndex(int index_, const c4_Sequence* seq_) const +{ + return seq_ == this ? index_ : _seq.RemapIndex(index_, seq_); +} + +int c4_DerivedSeq::NumRows() const +{ + return _seq.NumRows(); +} + +int c4_DerivedSeq::NumHandlers() const +{ + return _seq.NumHandlers(); +} + +c4_Handler& c4_DerivedSeq::NthHandler(int colNum_) const +{ + return _seq.NthHandler(colNum_); +} + +const c4_Sequence* c4_DerivedSeq::HandlerContext(int colNum_) const +{ + return _seq.HandlerContext(colNum_); +} + +int c4_DerivedSeq::AddHandler(c4_Handler* handler_) +{ + return _seq.AddHandler(handler_); +} + +c4_Handler* c4_DerivedSeq::CreateHandler(const c4_Property& prop_) +{ + return _seq.CreateHandler(prop_); +} + +void c4_DerivedSeq::SetNumRows(int size_) +{ + _seq.SetNumRows(size_); +} + +c4_Notifier* c4_DerivedSeq::PreChange(c4_Notifier& nf_) +{ + if (!GetDependencies()) + return 0; + + c4_Notifier* chg = d4_new c4_Notifier (this); + + switch (nf_._type) + { + case c4_Notifier::kSetAt: + chg->StartSetAt(nf_._index, *nf_._cursor); + break; + + case c4_Notifier::kSet: + chg->StartSet(nf_._index, nf_._propId, *nf_._bytes); + break; + + case c4_Notifier::kInsertAt: + chg->StartInsertAt(nf_._index, *nf_._cursor, nf_._count); + break; + + case c4_Notifier::kRemoveAt: + chg->StartRemoveAt(nf_._index, nf_._count); + break; + + case c4_Notifier::kMove: + chg->StartMove(nf_._index, nf_._count); + break; + } + + return chg; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_StreamStrategy::c4_StreamStrategy (t4_i32 buflen_) + : _stream (0), _buffer (d4_new t4_byte [buflen_]), _buflen (buflen_), _position (0) +{ + _mapStart = _buffer; + _dataSize = buflen_; +} + +c4_StreamStrategy::c4_StreamStrategy (c4_Stream* stream_) + : _stream (stream_), _buffer (0), _buflen (0), _position (0) +{ +} + +c4_StreamStrategy::~c4_StreamStrategy () +{ + _mapStart = 0; + _dataSize = 0; + + if (_buffer != 0) + delete [] _buffer; +} + +bool c4_StreamStrategy::IsValid() const +{ + return true; +} + +int c4_StreamStrategy::DataRead(t4_i32 pos_, void* buffer_, int length_) +{ + if (_buffer != 0) { + d4_assert(pos_ <= _buflen); + _position = pos_ + _baseOffset; + + if (length_ > _buflen - _position) + length_ = _buflen - _position; + if (length_ > 0) + memcpy(buffer_, _buffer + _position, length_); + } else { + d4_assert(_position == pos_ + _baseOffset); + length_ = _stream != 0 ? _stream->Read(buffer_, length_) : 0; + } + + _position += length_; + return length_; +} + +void c4_StreamStrategy::DataWrite(t4_i32 pos_, const void* buffer_, int length_) +{ + if (_buffer != 0) { + d4_assert(pos_ <= _buflen); + _position = pos_ + _baseOffset; + + int n = length_; + if (n > _buflen - _position) + n = _buflen - _position; + if (n > 0) + memcpy(_buffer + _position, buffer_, n); + } else { + d4_assert(_position == pos_ + _baseOffset); + if (_stream != 0 && !_stream->Write(buffer_, length_)) + ++_failure; + } + + _position += length_; +} + +t4_i32 c4_StreamStrategy::FileSize() +{ + return _position; +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/store.h b/akregator/src/mk4storage/metakit/src/store.h new file mode 100644 index 000000000..c45d258ea --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/store.h @@ -0,0 +1,114 @@ +// store.h -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Definition of auxiliary storage management classes + */ + +#ifndef __STORE_H__ +#define __STORE_H__ + +///////////////////////////////////////////////////////////////////////////// + +class c4_Dependencies +{ + c4_PtrArray _refs; + +public: + c4_Dependencies (); + ~c4_Dependencies (); + + void Add(c4_Sequence* seq_); + bool Remove(c4_Sequence* seq_); + + friend class c4_Notifier; +}; + +///////////////////////////////////////////////////////////////////////////// + +class c4_Notifier +{ + c4_Sequence* _origin; + c4_Notifier* _chain; + c4_Notifier* _next; + +public: + enum { kNone, kSetAt, kInsertAt, kRemoveAt, kMove, kSet, kLimit }; + + c4_Notifier (c4_Sequence* origin_); + ~c4_Notifier (); + + bool HasDependents() const; + + void StartSetAt(int index_, c4_Cursor& cursor_); + void StartInsertAt(int index_, c4_Cursor& cursor_, int count_); + void StartRemoveAt(int index_, int count_); + void StartMove(int from_, int to_); + void StartSet(int index_, int propId_, const c4_Bytes& buf_); + + int _type; + int _index; + int _propId; + int _count; + c4_Cursor* _cursor; + const c4_Bytes* _bytes; + +private: + void Notify(); +}; + +///////////////////////////////////////////////////////////////////////////// + +class c4_DerivedSeq : public c4_Sequence +{ +protected: + c4_Sequence& _seq; + +protected: + c4_DerivedSeq (c4_Sequence& seq_); + virtual ~c4_DerivedSeq (); + +public: + virtual int RemapIndex(int, const c4_Sequence*) const; + + virtual int NumRows() const; + virtual void SetNumRows(int size_); + + virtual int NumHandlers() const; + virtual c4_Handler& NthHandler(int) const; + virtual const c4_Sequence* HandlerContext(int) const; + virtual int AddHandler(c4_Handler*); + virtual c4_Handler* CreateHandler(const c4_Property&); + + virtual c4_Notifier* PreChange(c4_Notifier& nf_); +}; + +///////////////////////////////////////////////////////////////////////////// + +class c4_StreamStrategy : public c4_Strategy +{ + c4_Stream* _stream; + t4_byte* _buffer; + t4_i32 _buflen; + t4_i32 _position; +public: + c4_StreamStrategy (t4_i32 buflen_); + c4_StreamStrategy (c4_Stream* stream_); + virtual ~c4_StreamStrategy (); + + virtual bool IsValid() const; + virtual int DataRead(t4_i32 pos_, void* buffer_, int length_); + virtual void DataWrite(t4_i32 pos_, const void* buffer_, int length_); + virtual t4_i32 FileSize(); +}; + +///////////////////////////////////////////////////////////////////////////// + +#if q4_INLINE +#include "store.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/akregator/src/mk4storage/metakit/src/store.inl b/akregator/src/mk4storage/metakit/src/store.inl new file mode 100644 index 000000000..191ee7de7 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/store.inl @@ -0,0 +1,20 @@ +// store.inl -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Inlined members of the storage management classes + */ + +///////////////////////////////////////////////////////////////////////////// +// c4_Notifier + +d4_inline c4_Notifier::c4_Notifier (c4_Sequence* origin_) + : _origin (origin_), _chain (0), _next (0), + _type (kNone), _index (0), _propId (0), _count (0), + _cursor (0), _bytes (0) +{ + d4_assert(_origin != 0); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/string.cpp b/akregator/src/mk4storage/metakit/src/string.cpp new file mode 100644 index 000000000..33c8836c8 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/string.cpp @@ -0,0 +1,304 @@ +// string.cpp -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * yet another string implementation + */ + +#include "header.h" + +/* these definitions could be used instead of header.h ... + #define q4_UNIV 1 + #define d4_inline + #include "mk4str.h" + #define d4_reentrant + #define d4_assert(x) +*/ + +#if q4_UNIV // until end of source +///////////////////////////////////////////////////////////////////////////// + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef _AIX +#include <strings.h> +#endif + +#if !q4_INLINE +#include "mk4str.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + +#if q4_MSVC || q4_WATC || q4_BORC || (q4_MWCW && __MWERKS__ < 0x3000) +#define strcasecmp stricmp +#elif q4_WINCE + + // MS C/C++ has this handy stricmp: a case-insensitive version of strcmp + // This version only works with 7-bit ASCII characters 0x00 through 0x7F + + static int stricmp(const char* p1, const char* p2) + { + int c1, c2; + +#ifdef d4_USE_UNOPTIMIZED_CODE + do + { + c1 = tolower(*p1++); + c2 = tolower(*p2++); + } while (c1 != 0 && c1 == c2); +#else + do + { + c1 = *p1++; + c2 = *p2++; + } while (c1 != 0 && (c1 == c2 || tolower(c1) == tolower(c2))); + + c1 = tolower(c1); + c2 = tolower(c2); +#endif + + return c1 - c2; + } + +#endif + +#if q4_WINCE + const char* strrchr(const char* p, char ch) + { + const char* q = 0; + while (*p) + if (*p++ == ch) + q = p; + return q; + } +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// This string class implement functionality which is very similar to that +// provided by the CString class of the Microsoft Framework Classes (MFC). +// +// There are also several major differences: +// +// 1) This class uses reference counting to avoid massive copying. +// Consequently, function return as well as assignment is very fast. +// 2) Strings of up to 255 bytes can contain any data, even null bytes. +// Longer strings can not contain any null bytes past position 255. +// 3) This class can produce a "const char*" without overhead, but it +// can also cast to the byte-counted "const unsigned char*" used +// everywhere in Macintosh applications (as StringPtr, Str255, etc). +// 4) This source code is not derived from Microsoft's code in any way. +// +// A good way to use this class, is to always use c4_String for function +// return values and "const [unsigned] char*" for all parameters. Together, +// these two choices will remove the need for nearly any messy casts. +// +// Note: MFC 4.0 has now adopted refcounts, and is a good alternative to +// this code (but a bit bulkier, it also has Unicode support). + + // 2001-11-27, stop releasing nullvec, to allow MT use + d4_reentrant static unsigned char* nullVec = 0; + +static int fInc(unsigned char* p) +{ + ++*p; + if (*p) + return 1; + + --*p; + return 0; +} + +inline static void fDec(unsigned char* p) +{ + --*p; + if (!*p && p != nullVec) + delete [] p; +} + +c4_String::c4_String (char ch, int n /* =1 */) +{ + if (n < 0) + n = 0; + + _value = new unsigned char [n + 3]; + + _value[0] = 1; // see Init() member + memset(_value + 2, ch, n); + _value[1] = (unsigned char) (n <= 255 ? n : 255); + _value[n+2] = 0; +} + +c4_String::c4_String (const char* p) +{ + Init(p, p != 0 ? strlen(p) : 0); +} + +c4_String::c4_String (const c4_String& s) +{ + if (fInc(s._value)) + _value = s._value; + else + Init(s.Data(), s.GetLength()); +} + +c4_String::~c4_String () +{ + fDec(_value); +} + +const c4_String& c4_String::operator= (const c4_String& s) +{ + unsigned char* oldVal = _value; + if (fInc(s._value)) + _value = s._value; + else + Init(s.Data(), s.GetLength()); + fDec(oldVal); + + return *this; +} + +c4_String operator+ (const c4_String& a, const c4_String& b) +{ + const int aCnt = a.GetLength(); + int sum = aCnt + b.GetLength(); + + c4_String result ('\0', sum); // set up correct size, then fix contents + memcpy(result._value + 2, a.Data(), aCnt); + memcpy(result._value + 2 + aCnt, b.Data(), sum - aCnt); + + return result; +} + +void c4_String::Init(const void* p, int n) +{ + if (p == NULL || n <= 0) + { + // Optimization to significantly speed-up init of empty strings: + // share a common entry, which avoids *LOTS* of tiny mem allocs. + // + // Especially "new [...] c4_String" will benefit a lot, as well as: + // + // c4_String s; // this would have caused a new allocation + // s = ... // then immediately drops the count back + // + // 2001/11/27: changed to never release this empty vector, for MT use + // the new logic is to completely ignore its ref count + + if (!nullVec) + { + // obtain a valid new empty string buffer to keep around + unsigned char* nv = new unsigned char [3]; + nv[0] = nv[1] = nv[2] = 0; + // only set static value after item is fully inited (avoid MT race) + nullVec = nv; + } + + _value = nullVec; // use this buffer as our empty string + return; // done... that was quick, wasn't it? + } + + _value = new unsigned char [n + 3]; + + _value[0] = 1; // many assumptions here: set the reference count to 1 + + if (n > 0) + memcpy(_value + 2, p, n); + _value[1] = (unsigned char) (n <= 255 ? n : 255); + _value[n+2] = 0; +} + +int c4_String::FullLength() const +{ + int n = _value[1]; + return n < 255 ? n : n + strlen((const char*) _value + 2 + 255); +} + +c4_String c4_String::Mid(int nFirst, int nCount) const +{ + if (nFirst >= GetLength()) + return c4_String (); + + if (nFirst + nCount > GetLength()) + nCount = GetLength() - nFirst; + + if (nFirst == 0 && nCount == GetLength()) + return *this; + + return c4_String (Data() + nFirst, nCount); +} + +c4_String c4_String::Left(int nCount) const +{ + if (nCount >= GetLength()) + return *this; + + return c4_String (Data(), nCount); +} + +c4_String c4_String::Right(int nCount) const +{ + if (nCount >= GetLength()) + return *this; + + return c4_String (Data() + GetLength() - nCount, nCount); +} + +bool operator== (const c4_String& a, const c4_String& b) +{ + return a._value == b._value || a.GetLength() == b.GetLength() && + memcmp(a.Data(), b.Data(), a.GetLength()) == 0; +} + +int c4_String::Compare(const char* str) const +{ + return Data() == str ? 0 : strcmp(Data(), str); +} + +int c4_String::CompareNoCase(const char* str) const +{ + return Data() == str ? 0 : strcasecmp(Data(), str); +} + +int c4_String::Find(char ch) const +{ + const char* p = strchr(Data(), ch); + return p != 0 ? p - Data() : -1; +} + +int c4_String::ReverseFind(char ch) const +{ + const char* p = strrchr(Data(), ch); + return p != 0 ? p - Data() : -1; +} + +int c4_String::FindOneOf(const char* set) const +{ + const char* p = strpbrk(Data(), set); + return p != 0 ? p - Data() : -1; +} + +int c4_String::Find(const char* sub) const +{ + const char* p = strstr(Data(), sub); + return p != 0 ? p - Data() : -1; +} + +c4_String c4_String::SpanIncluding(const char* set) const +{ + return Left(strspn(Data(), set)); +} + +c4_String c4_String::SpanExcluding(const char* set) const +{ + return Left(strcspn(Data(), set)); +} + +///////////////////////////////////////////////////////////////////////////// +#endif // q4_UNIV diff --git a/akregator/src/mk4storage/metakit/src/table.cpp b/akregator/src/mk4storage/metakit/src/table.cpp new file mode 100644 index 000000000..a857cee09 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/table.cpp @@ -0,0 +1,160 @@ +// table.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Loose ends, these should be moved + */ + +#include "header.h" +#include "handler.h" +#include "store.h" +#include "field.h" +#include "format.h" +#include "persist.h" + +///////////////////////////////////////////////////////////////////////////// +// Implemented in this file + + class c4_Bytes; + class c4_HandlerSeq; + +///////////////////////////////////////////////////////////////////////////// + +/** @class c4_Bytes + * + * Generic data buffer, with optional automatic clean up. + * + * These objects are used to pass around untyped data without concern about + * clean-up. They know whether the bytes need to be de-allocated when these + * objects go out of scope. Small amounts of data are stored in the object. + * + * Objects of this class are used a lot within Metakit to manipulate its own + * data items generically. The c4_BytesProp class allows storing binary + * data explicitly in a file. If such data files must be portable, then the + * application itself must define a generic format to deal with byte order. + * + * How to store an object in binary form in a row (this is not portable): + * @code + * struct MyStruct { ... }; + * MyStruct something; + * + * c4_BytesProp pData ("Data"); + * c4_Row row; + * + * pData (row) = c4_Bytes (&something, sizeof something); + * @endcode + */ + + /// Construct an object with contents, optionally as a copy +c4_Bytes::c4_Bytes (const void* buf_, int len_, bool copy_) + : _size (len_), _copy (copy_) +{ + _contents = (t4_byte*) buf_; // moved out of intializers for DEC CXX 5.7 + if (_copy) + _MakeCopy(); +} + + /// Copy constructor +c4_Bytes::c4_Bytes (const c4_Bytes& src_) + : _size (src_._size), _copy (src_._copy) +{ + _contents = src_._contents; // moved out of intializers for DEC CXX 5.7 + if (_copy || _contents == src_._buffer) + _MakeCopy(); +} + + /// Assignment, this may make a private copy of contents +c4_Bytes& c4_Bytes::operator= (const c4_Bytes& src_) +{ + if (&src_ != this) + { + _LoseCopy(); + + _contents = src_._contents; + _size = src_._size; + _copy = src_._copy; + + if (_copy || _contents == src_._buffer) + _MakeCopy(); + } + + return *this; +} + + /// Swap the contents and ownership of two byte objects +void c4_Bytes::Swap(c4_Bytes& bytes_) +{ + t4_byte* p = _contents; + int s = _size; + bool c = _copy; + + _contents = bytes_._contents; + _size = bytes_._size; + _copy = bytes_._copy; + + bytes_._contents = p; + bytes_._size = s; + bytes_._copy = c; + + // if either one is using its local buffer, swap those too + if (_contents == bytes_._buffer || p == _buffer) + { + t4_byte t [sizeof _buffer]; + + memcpy(t, _buffer, sizeof _buffer); + memcpy(_buffer, bytes_._buffer, sizeof _buffer); + memcpy(bytes_._buffer, t, sizeof _buffer); + + if (_contents == bytes_._buffer) + _contents = _buffer; + + if (bytes_._contents == _buffer) + bytes_._contents = bytes_._buffer; + } +} + +/// Define contents as a freshly allocated buffer of given size +t4_byte* c4_Bytes::SetBuffer(int length_) +{ +/* No substantial improvement measured: + Perhaps keep a correctly sized c4_Bytes object in each property? + It means c4_...Ref objects would need to store a pointer, not an id. + + if (length_ == _size) + return _contents; // no work needed, get out fast +*/ + _LoseCopy(); + + _size = length_; + _copy = _size > (int) sizeof _buffer; + + return _contents = _copy ? d4_new t4_byte [_size] : _buffer; +} + +/// Allocate a buffer and fills its contents with zero bytes +t4_byte* c4_Bytes::SetBufferClear(int length_) +{ + return (t4_byte*) memset(SetBuffer(length_), 0, length_); +} + +void c4_Bytes::_MakeCopy() +{ + d4_assert(_contents != 0); + + _copy = _size > (int) sizeof _buffer; + + if (_size > 0) + _contents = (t4_byte*) memcpy(_copy ? d4_new t4_byte [_size] + : _buffer, _contents, _size); +} + +/// Return true if the contents of both objects are equal +bool operator== (const c4_Bytes& a_, const c4_Bytes& b_) +{ + return a_._contents == b_._contents || + (a_._size == b_._size && + memcmp(a_._contents, b_._contents, a_._size) == 0); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/univ.cpp b/akregator/src/mk4storage/metakit/src/univ.cpp new file mode 100644 index 000000000..81a73015a --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/univ.cpp @@ -0,0 +1,227 @@ +// univ.cpp -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * A simple implementation of dynamic arrays + */ + +#include "header.h" + +#if q4_UNIV // until end of source +///////////////////////////////////////////////////////////////////////////// + +#include <stdlib.h> // malloc + +#if !q4_INLINE +#include "univ.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + +#if q4_UNIX || __MINGW32__ +#define _strdup strdup +#elif !q4_BORC && !q4_MSVC && !q4_WATC && !(q4_MWCW && defined(_WIN32)) && \ + !(q4_MWCW && __MWERKS__ >= 0x3000) + + static char* _strdup(const char* p) + { + if (!p) + return 0; + + char* s = (char*) malloc(strlen(p) + 1); + return strcpy(s, p); + } + +#endif + + // The Borland C++ RTL does not want file handle objects to cross + // DLL boundaries, so we add special fopen/fclose hooks to this DLL. + +#if q4_BORC + #include <stdio.h> + +#if q4_WIN32 + __declspec(dllexport) FILE* +#else + FILE* __export +#endif + f4_FileOpenInDLL(const char* name_, const char* mode_) + { + return fopen(name_, mode_); + } + +#if q4_WIN32 + __declspec(dllexport) +#else + int __export +#endif + f4_FileCloseInDLL(FILE* file_) + { + return fclose(file_); + } + +#endif + +///////////////////////////////////////////////////////////////////////////// +// c4_BaseArray + +c4_BaseArray::c4_BaseArray () + : _data (0), _size (0) +{ +} + +c4_BaseArray::~c4_BaseArray () +{ + SetLength(0); +} + +void c4_BaseArray::SetLength(int nNewSize) +{ + // 2001-11-25: use more granular allocation, as optimization + const int bits = 6; + + if (((_size - 1) ^ (nNewSize - 1)) >> bits) { + const int n = (nNewSize + (1<<bits) - 1) & -(1<<bits); + _data = _data == 0 ? n == 0 ? (char*) 0 + : (char*) malloc(n) + : n == 0 ? (free(_data), (char*) 0) + : (char*) realloc(_data, n); + } + + d4_assert(_data != 0 || nNewSize == 0); + + int n = _size; + _size = nNewSize; + + if (nNewSize > n) + memset(GetData(n), 0, nNewSize - n); +} + +void c4_BaseArray::Grow(int nIndex) +{ + if (nIndex > _size) + SetLength(nIndex); +} + +void c4_BaseArray::InsertAt(int nIndex, int nCount) +{ + SetLength(_size + nCount); + + int to = nIndex + nCount; + if (_size > to) + d4_memmove(GetData(to), GetData(nIndex), _size - to); +} + +void c4_BaseArray::RemoveAt(int nIndex, int nCount) +{ + int from = nIndex + nCount; + if (_size > from) + d4_memmove(GetData(nIndex), GetData(from), _size - from); + + SetLength(_size - nCount); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_DWordArray + +int c4_DWordArray::Add(t4_i32 newElement) +{ + int n = GetSize(); + _vector.Grow(Off(n + 1)); + SetAt(n, newElement); + return n; +} + +void c4_DWordArray::InsertAt(int nIndex, t4_i32 newElement, int nCount) +{ + _vector.InsertAt(Off(nIndex), nCount * sizeof (t4_i32)); + + while (--nCount >= 0) + SetAt(nIndex++, newElement); +} + +void c4_DWordArray::RemoveAt(int nIndex, int nCount) +{ + _vector.RemoveAt(Off(nIndex), nCount * sizeof (t4_i32)); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_PtrArray + +int c4_PtrArray::Add(void* newElement) +{ + int n = GetSize(); + _vector.Grow(Off(n + 1)); + SetAt(n, newElement); + return n; +} + +void c4_PtrArray::InsertAt(int nIndex, void* newElement, int nCount) +{ + _vector.InsertAt(Off(nIndex), nCount * sizeof (void*)); + + while (--nCount >= 0) + SetAt(nIndex++, newElement); +} + +void c4_PtrArray::RemoveAt(int nIndex, int nCount) +{ + _vector.RemoveAt(Off(nIndex), nCount * sizeof (void*)); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_StringArray + +c4_StringArray::~c4_StringArray() +{ + SetSize(0); +} + +void c4_StringArray::SetSize(int nNewSize, int) +{ + int i = nNewSize; + + while (i < GetSize()) + SetAt(i++, 0); + + _ptrs.SetSize(nNewSize); + + while (i < GetSize()) + _ptrs.SetAt(i++, ""); +} + +void c4_StringArray::SetAt(int nIndex, const char* newElement) +{ + char* s = (char*) _ptrs.GetAt(nIndex); + if (s && *s) + free(s); + + _ptrs.SetAt(nIndex, newElement && *newElement? _strdup(newElement) : ""); +} + +int c4_StringArray::Add(const char* newElement) +{ + int n = _ptrs.Add(0); + SetAt(n, newElement); + return n; +} + +void c4_StringArray::InsertAt(int nIndex, const char* newElement, int nCount) +{ + _ptrs.InsertAt(nIndex, 0, nCount); + + while (--nCount >= 0) + SetAt(nIndex++, newElement); +} + +void c4_StringArray::RemoveAt(int nIndex, int nCount) +{ + for (int i = 0; i < nCount; ++i) + SetAt(nIndex + i, 0); + + _ptrs.RemoveAt(nIndex, nCount); +} + +///////////////////////////////////////////////////////////////////////////// +#endif // q4_UNIV diff --git a/akregator/src/mk4storage/metakit/src/univ.h b/akregator/src/mk4storage/metakit/src/univ.h new file mode 100644 index 000000000..5558e8a4c --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/univ.h @@ -0,0 +1,115 @@ +// univ.h -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Definition of the container classes + */ + +#define q4_UNIV 1 + +#include "mk4str.h" + +///////////////////////////////////////////////////////////////////////////// + +class c4_BaseArray +{ +public: + c4_BaseArray (); + ~c4_BaseArray (); + + int GetLength() const; + void SetLength(int nNewSize); + + const void* GetData(int nIndex) const; + void* GetData(int nIndex); + + void Grow(int nIndex); + + void InsertAt(int nIndex, int nCount); + void RemoveAt(int nIndex, int nCount); + +private: + char* _data; + int _size; +// char _buffer[4]; +}; + +class c4_PtrArray +{ +public: + c4_PtrArray (); + ~c4_PtrArray (); + + int GetSize() const; + void SetSize(int nNewSize, int nGrowBy = -1); + + void* GetAt(int nIndex) const; + void SetAt(int nIndex, const void* newElement); + void*& ElementAt(int nIndex); + + int Add(void* newElement); + + void InsertAt(int nIndex, void* newElement, int nCount = 1); + void RemoveAt(int nIndex, int nCount = 1); + +private: + static int Off(int n_); + + c4_BaseArray _vector; +}; + +class c4_DWordArray +{ +public: + c4_DWordArray (); + ~c4_DWordArray (); + + int GetSize() const; + void SetSize(int nNewSize, int nGrowBy = -1); + + t4_i32 GetAt(int nIndex) const; + void SetAt(int nIndex, t4_i32 newElement); + t4_i32& ElementAt(int nIndex); + + int Add(t4_i32 newElement); + + void InsertAt(int nIndex, t4_i32 newElement, int nCount = 1); + void RemoveAt(int nIndex, int nCount = 1); + +private: + static int Off(int n_); + + c4_BaseArray _vector; +}; + +class c4_StringArray +{ +public: + c4_StringArray (); + ~c4_StringArray (); + + int GetSize() const; + void SetSize(int nNewSize, int nGrowBy = -1); + + const char* GetAt(int nIndex) const; + void SetAt(int nIndex, const char* newElement); +// c4_String& ElementAt(int nIndex); + + int Add(const char* newElement); + + void InsertAt(int nIndex, const char* newElement, int nCount = 1); + void RemoveAt(int nIndex, int nCount = 1); + +private: + c4_PtrArray _ptrs; +}; + +///////////////////////////////////////////////////////////////////////////// + +#if q4_INLINE + #include "univ.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// + diff --git a/akregator/src/mk4storage/metakit/src/univ.inl b/akregator/src/mk4storage/metakit/src/univ.inl new file mode 100644 index 000000000..45b086fb2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/univ.inl @@ -0,0 +1,126 @@ +// univ.inl -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Inlined members of the container classes + */ + +///////////////////////////////////////////////////////////////////////////// +// c4_BaseArray + +d4_inline int c4_BaseArray::GetLength() const +{ + return _size; +} + +d4_inline const void* c4_BaseArray::GetData(int nIndex) const +{ + return _data + nIndex; +} + +d4_inline void* c4_BaseArray::GetData(int nIndex) +{ + return _data + nIndex; +} + +///////////////////////////////////////////////////////////////////////////// +// c4_PtrArray + +d4_inline c4_PtrArray::c4_PtrArray () +{ +} + +d4_inline c4_PtrArray::~c4_PtrArray () +{ +} + +d4_inline int c4_PtrArray::Off(int n_) +{ + return n_ * sizeof (void*); +} + +d4_inline int c4_PtrArray::GetSize() const +{ + return _vector.GetLength() / sizeof (void*); +} + +d4_inline void c4_PtrArray::SetSize(int nNewSize, int) +{ + _vector.SetLength(Off(nNewSize)); +} + +d4_inline void* c4_PtrArray::GetAt(int nIndex) const +{ + return *(void* const*) _vector.GetData(Off(nIndex)); +} + +d4_inline void c4_PtrArray::SetAt(int nIndex, const void* newElement) +{ + *(const void**) _vector.GetData(Off(nIndex)) = newElement; +} + +d4_inline void*& c4_PtrArray::ElementAt(int nIndex) +{ + return *(void**) _vector.GetData(Off(nIndex)); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_DWordArray + +d4_inline c4_DWordArray::c4_DWordArray () +{ +} + +d4_inline c4_DWordArray::~c4_DWordArray () +{ +} + +d4_inline int c4_DWordArray::Off(int n_) +{ + return n_ * sizeof (t4_i32); +} + +d4_inline int c4_DWordArray::GetSize() const +{ + return _vector.GetLength() / sizeof (t4_i32); +} + +d4_inline void c4_DWordArray::SetSize(int nNewSize, int) +{ + _vector.SetLength(Off(nNewSize)); +} + +d4_inline t4_i32 c4_DWordArray::GetAt(int nIndex) const +{ + return *(const t4_i32*) _vector.GetData(Off(nIndex)); +} + +d4_inline void c4_DWordArray::SetAt(int nIndex, t4_i32 newElement) +{ + *(t4_i32*) _vector.GetData(Off(nIndex)) = newElement; +} + +d4_inline t4_i32& c4_DWordArray::ElementAt(int nIndex) +{ + return *(t4_i32*) _vector.GetData(Off(nIndex)); +} + +///////////////////////////////////////////////////////////////////////////// +// c4_StringArray + +d4_inline c4_StringArray::c4_StringArray () +{ +} + +d4_inline int c4_StringArray::GetSize() const +{ + return _ptrs.GetSize(); +} + +d4_inline const char* c4_StringArray::GetAt(int nIndex) const +{ + return (const char*) _ptrs.GetAt(nIndex); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/view.cpp b/akregator/src/mk4storage/metakit/src/view.cpp new file mode 100644 index 000000000..af2fc9fa2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/view.cpp @@ -0,0 +1,1271 @@ +// view.cpp -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Implementation of main classes not involved in persistence + */ + +#include "header.h" +#include "derived.h" +#include "custom.h" +#include "store.h" // for RelocateRows +#include "field.h" // for RelocateRows +#include "persist.h" +#include "remap.h" + +#if !q4_INLINE +#include "mk4.inl" +#endif + +///////////////////////////////////////////////////////////////////////////// +// c4_ThreadLock + +class c4_ThreadLock +{ +public: + c4_ThreadLock (); + + class Hold + { + public: + Hold (); + ~Hold (); + }; +}; + +///////////////////////////////////////////////////////////////////////////// + +#if q4_MULTI + +#if q4_WIN32 + +/* + * On Win32, use a critical section to protect the global symbol table. + * Also uses special thread-safe calls to inc/dec all reference counts. + * + * This implementation replaces the previous use of TLS, which cannot + * be used without special tricks in dynamically loaded DLL's, as is + * required for OCX/ActiveX use (which uses LoadLibrary). + * + * Note: Could have used MFC's CCriticalSection and CSingleLock classes, + * but the code below is so trivial that it hardly matters. + */ + +#if q4_MSVC && !q4_STRICT +#pragma warning(disable: 4201) // nonstandard extension used : ... +#endif +#include <windows.h> + + static CRITICAL_SECTION gCritSect; + + c4_ThreadLock::c4_ThreadLock () + { + InitializeCriticalSection(&gCritSect); + } + + c4_ThreadLock::Hold::Hold () + { + EnterCriticalSection(&gCritSect); + } + + c4_ThreadLock::Hold::~Hold () + { + LeaveCriticalSection(&gCritSect); + } + +#else /* q4_WIN32 */ + +#include <pthread.h> + + static pthread_mutex_t gMutex; + + d4_inline c4_ThreadLock::c4_ThreadLock () + { + pthread_mutex_init(&gMutex, 0); + } + + d4_inline c4_ThreadLock::Hold::Hold () + { + d4_dbgdef(int r =) + pthread_mutex_lock(&gMutex); + d4_assert(r == 0); + } + + d4_inline c4_ThreadLock::Hold::~Hold () + { + d4_dbgdef(int r =) + pthread_mutex_unlock(&gMutex); + d4_assert(r == 0); + } + +#endif /* q4_WIN32 */ + +#else /* q4_MULTI */ + +// All other implementations revert to the simple "thread-less" case. + + d4_inline c4_ThreadLock::c4_ThreadLock () + { + } + + d4_inline c4_ThreadLock::Hold::Hold () + { + } + + d4_inline c4_ThreadLock::Hold::~Hold () + { + } + +#endif + +///////////////////////////////////////////////////////////////////////////// + +#if q4_LOGPROPMODS + + static FILE* sPropModsFile = 0; + static int sPropModsProp = -1; + +FILE* f4_LogPropMods(FILE* fp_, int propId_) +{ + FILE* prevfp = sPropModsFile; + sPropModsFile = fp_; + sPropModsProp = propId_; + return prevfp; +} + +void f4_DoLogProp(const c4_Handler* hp_, int id_, const char* fmt_, int arg_) +{ + if (sPropModsFile != 0 && (sPropModsProp < 0 || sPropModsProp == id_)) { + fprintf(sPropModsFile, "handler 0x%x id %d: ", hp_, id_); + fprintf(sPropModsFile, fmt_, arg_); + } +} + +#endif + +///////////////////////////////////////////////////////////////////////////// + +/** @class c4_View + * + * A collection of data rows. This is the central public data structure of + * Metakit (often called "table", "array", or "relation" in other systems). + * + * Views are smart pointers to the actual collections, setting a view to a new + * value does not alter the collection to which this view pointed previously. + * + * The elements of views can be referred to by their 0-based index, which + * produces a row-reference of type c4_RowRef. These row references can + * be copied, used to get or set properties, or dereferenced (in which case + * an object of class c4_Row is returned). Taking the address of a row + * reference produces a c4_Cursor, which acts very much like a pointer. + * + * The following code creates a view with 1 row and 2 properties: + * @code + * c4_StringProp pName ("name"); + * c4_IntProp pAge ("age"); + * + * c4_Row data; + * pName (data) = "John Williams"; + * pAge (data) = 43; + * + * c4_View myView; + * myView.Add(row); + * @endcode + */ + +/// Construct a view based on a sequence +c4_View::c4_View (c4_Sequence* seq_) + : _seq (seq_) +{ + if (!_seq) + _seq = d4_new c4_HandlerSeq (0); + + _IncSeqRef(); +} + +/// Construct a view based on a custom viewer +c4_View::c4_View (c4_CustomViewer* viewer_) + : _seq (0) +{ + d4_assert(viewer_); + + _seq = d4_new c4_CustomSeq (viewer_); + + _IncSeqRef(); +} + +/// Construct a view based on an input stream +c4_View::c4_View (c4_Stream* stream_) + : _seq (c4_Persist::Load(stream_)) +{ + if (_seq == 0) + _seq = d4_new c4_HandlerSeq (0); + + _IncSeqRef(); +} + +/// Construct an empty view with one property +c4_View::c4_View (const c4_Property& prop_) + : _seq (d4_new c4_HandlerSeq (0)) +{ + _IncSeqRef(); + + _seq->PropIndex(prop_); +} + +/// Copy constructor +c4_View::c4_View (const c4_View& view_) + : _seq (view_._seq) +{ + _IncSeqRef(); +} + +/// Makes this view the same as another one. +c4_View& c4_View::operator= (const c4_View& view_) +{ + if (_seq != view_._seq) { + _DecSeqRef(); + _seq = view_._seq; + _IncSeqRef(); + } + return *this; +} + +/** Get a single data item in a generic way + * + * This can be used to access view data in a generalized way. + * Useful for c4_CustomViewers which are based on other views. + * @return true if the item is non-empty + */ +bool c4_View::GetItem(int row_, int col_, c4_Bytes& buf_) const +{ + const c4_Property& prop = NthProperty(col_); + return prop (GetAt(row_)).GetData(buf_); +} + +/// Set a single data item in a generic way +void c4_View::SetItem(int row_, int col_, const c4_Bytes& buf_) const +{ + const c4_Property& prop = NthProperty(col_); + prop (GetAt(row_)).SetData(buf_); +} + +/// Set an entry, growing the view if needed +void c4_View::SetAtGrow(int index_, const c4_RowRef& newElem_) +{ + if (index_ >= GetSize()) + SetSize(index_ + 1); + + _seq->SetAt(index_, &newElem_); +} + +/** Add a new entry, same as "SetAtGrow(GetSize(), ...)" + * @return the index of the newly added row + */ +int c4_View::Add(const c4_RowRef& newElem_) +{ + int i = GetSize(); + InsertAt(i, newElem_); + return i; +} + +/** Construct a new view with a copy of the data + * + * The copy is a deep copy, because subviews are always copied in full. + */ +c4_View c4_View::Duplicate() const +{ + // insert all rows, sharing any subviews as needed + c4_View result = Clone(); + result.InsertAt(0, _seq); + return result; +} + +/** Constructs a new view with the same structure but no data + * + * Structural information can only be maintain for the top level, + * subviews will be included but without any properties themselves. + */ +c4_View c4_View::Clone() const +{ + c4_View view; + + for (int i = 0; i < NumProperties(); ++i) + view._seq->PropIndex(NthProperty(i)); + + return view; +} + +/** Adds a property column to a view if not already present + * @return 0-based column position of the property + */ +int c4_View::AddProperty(const c4_Property& prop_) +{ + return _seq->PropIndex(prop_); +} + +/** Returns the N-th property (using zero-based indexing) + * @return reference to the specified property + */ +const c4_Property& c4_View::NthProperty( + int index_ ///< the zero-based property index + ) const +{ + return _seq->NthHandler(index_).Property(); +} + +/** Find the index of a property, given its name + * @return 0-based column index + * @retval -1 property not present in this view + */ +int c4_View::FindPropIndexByName( + const char* name_ ///< property name (case insensitive) + ) const +{ + // use a slow linear scan to find the untyped property by name + for (int i = 0; i < NumProperties(); ++i) { + c4_String s = NthProperty(i).Name(); + if (s.CompareNoCase(name_) == 0) + return i; + } + + return -1; +} + +/** Defines a column for a property. + * + * The following code defines an empty view with three properties: + * @code + * c4_IntProp p1, p2, p3; + * c4_View myView = (p1, p2, p3); + * @endcode + * @return the new view object (without any data rows) + * @sa c4_Property + */ +c4_View c4_View::operator, (const c4_Property& prop_) const +{ + c4_View view = Clone(); + view.AddProperty(prop_); + return view; +} + +/// Insert copies of all rows of the specified view +void c4_View::InsertAt(int index_, const c4_View& view_) +{ + int n = view_.GetSize(); + if (n > 0) { + c4_Row empty; + + InsertAt(index_, empty, n); + + for (int i = 0; i < n; ++i) + SetAt(index_ + i, view_[i]); + } +} + +bool c4_View::IsCompatibleWith(const c4_View& dest_) const +{ + // can't determine table without handlers (and can't be a table) + if (NumProperties() == 0 || dest_.NumProperties() == 0) + return false; + + c4_Sequence* s1 = _seq; + c4_Sequence* s2 = dest_._seq; + c4_HandlerSeq* h1 = (c4_HandlerSeq*) s1->HandlerContext(0); + c4_HandlerSeq* h2 = (c4_HandlerSeq*) s2->HandlerContext(0); + + // both must be real handler views, not derived ones + if (h1 != s1 || h2 != s2) + return false; + + // both must not contain any temporary handlers + if (s1->NumHandlers() != h1->NumFields() || + s2->NumHandlers() != h2->NumFields()) + return false; + + // both must be in the same storage + if (h1->Persist() == 0 || h1->Persist() != h2->Persist()) + return false; + + // both must have the same structure (is this expensive?) + c4_String d1 = h1->Definition().Description(true); + c4_String d2 = h1->Definition().Description(true); + return d1 == d2; // ignores all names +} + +/** Move attached rows to somewhere else in same storage + * + * There is a lot of trickery going on here. The whole point of this + * code is that moving rows between (compatible!) subviews should not + * use copying when potentially large memo's and subviews are involved. + * In that case, the best solution is really to move pointers, not data. + */ +void c4_View::RelocateRows(int from_, int count_, c4_View& dest_, int pos_) +{ + if (count_ < 0) + count_ = GetSize() - from_; + if (pos_ < 0) + pos_ = dest_.GetSize(); + + d4_assert(0 <= from_ && from_ <= GetSize()); + d4_assert(0 <= count_ && from_ + count_ <= GetSize()); + d4_assert(0 <= pos_ && pos_ <= dest_.GetSize()); + + if (count_ > 0) { + // the destination must not be inside the source rows + d4_assert(&dest_ != this || from_ > pos_ || pos_ >= from_ + count_); + + // this test is slow, so do it only as a debug check + d4_assert(IsCompatibleWith(dest_)); + + // make space, swap rows, drop originals + c4_Row empty; + dest_.InsertAt(pos_, empty, count_); + + // careful if insert moves origin + if (&dest_ == this && pos_ <= from_) + from_ += count_; + + for (int i = 0; i < count_; ++i) + ((c4_HandlerSeq*) _seq)->ExchangeEntries(from_ + i, + *(c4_HandlerSeq*) dest_._seq, pos_ + i); + + RemoveAt(from_, count_); + } +} + +/** Create view with all rows in natural (property-wise) order + * + * The result is virtual, it merely maintains a permutation to access the + * underlying view. This "derived" view uses change notification to track + * changes to the underlying view, but unfortunately there are some major + * limitations with this scheme - one of them being that deriving another + * view from this sorted one will not properly track changes. + */ +c4_View c4_View::Sort() const +{ + return f4_CreateSort(*_seq); +} + +/** Create view sorted according to the specified properties + * + * The result is virtual, it merely maintains a permutation to access the + * underlying view. This "derived" view uses change notification to track + * changes to the underlying view, but unfortunately there are some major + * limitations with this scheme - one of them being that deriving another + * view from this sorted one will not properly track changes. + */ +c4_View c4_View::SortOn(const c4_View& up_) const +{ + c4_Sequence* seq = f4_CreateProject(*_seq, *up_._seq, true); + + return f4_CreateSort(*seq); +} + +/** Create sorted view, with some properties sorted in reverse + * + * The result is virtual, it merely maintains a permutation to access the + * underlying view. This "derived" view uses change notification to track + * changes to the underlying view, but unfortunately there are some major + * limitations with this scheme - one of them being that deriving another + * view from this sorted one will not properly track changes. + */ +c4_View c4_View::SortOnReverse( + const c4_View& up_, ///< the view which defines the sort order + const c4_View& down_ ///< subset of up_, defines reverse order + ) const +{ + c4_Sequence* seq = f4_CreateProject(*_seq, *up_._seq, true); + + return f4_CreateSort(*seq, down_._seq); +} + +/** Create view with rows matching the specified value + * + * The result is virtual, it merely maintains a permutation to access the + * underlying view. This "derived" view uses change notification to track + * changes to the underlying view, but this only works when based on views + * which properly generate change notifications (.e. raw views, other + * selections, and projections). + */ +c4_View c4_View::Select(const c4_RowRef& crit_) const +{ + return f4_CreateFilter(*_seq, &crit_, &crit_); +} + +/** Create view with row values within the specified range + * + * The result is virtual, it merely maintains a permutation to access the + * underlying view. This "derived" view uses change notification to track + * changes to the underlying view, but this only works when based on views + * which properly generate change notifications (.e. raw views, other + * selections, and projections). + */ +c4_View c4_View::SelectRange( + const c4_RowRef& low_, ///< values of the lower bounds (inclusive) + const c4_RowRef& high_ ///< values of the upper bounds (inclusive) + ) const +{ + return f4_CreateFilter(*_seq, &low_, &high_); +} + +/** Create view with the specified property arrangement + * + * The result is virtual, it merely maintains a permutation to access the + * underlying view. This "derived" view uses change notification to track + * changes to the underlying view, but this only works when based on views + * which properly generate change notifications (.e. raw views, selections, + * and other projections). + */ +c4_View c4_View::Project(const c4_View& in_) const +{ + return f4_CreateProject(*_seq, *in_._seq, false); +} + +/** Create derived view with some properties omitted + * + * The result is virtual, it merely maintains a permutation to access the + * underlying view. This "derived" view uses change notification to track + * changes to the underlying view, but this only works when based on views + * which properly generate change notifications (.e. raw views, selections, + * and other projections). + */ +c4_View c4_View::ProjectWithout(const c4_View& out_) const +{ + return f4_CreateProject(*_seq, *_seq, false, out_._seq); +} + +/** Create view which is a segment/slice (default is up to end) + * + * Returns a view which is a subset, either a contiguous range, or + * a "slice" with element taken from every step_ entries. If the + * step is negative, the same entries are returned, but in reverse + * order (start_ is still lower index, it'll then be returned last). + * + * This view operation is based on a custom viewer and is modifiable. + */ +c4_View c4_View::Slice(int first_, int limit_, int step_) const +{ + return f4_CustSlice(*_seq, first_, limit_, step_); +} + +/** Create view which is the cartesian product with given view + * + * The cartesian product is defined as every combination of rows + * in both views. The number of entries is the product of the + * number of entries in the two views, properties which are present + * in both views will use the values defined in this view. + * + * This view operation is based on a read-only custom viewer. + */ +c4_View c4_View::Product(const c4_View& view_) const +{ + return f4_CustProduct(*_seq, view_); +} + +/** Create view which remaps another given view + * + * Remapping constructs a view with the rows indicated by another + * view. The first property in the order_ view must be an int + * property with index values referring to this one. The size of + * the resulting view is determined by the order_ view and can + * differ, for example to act as a subset selection (if smaller). + * + * This view operation is based on a custom viewer and is modifiable. + */ +c4_View c4_View::RemapWith(const c4_View& view_) const +{ + return f4_CustRemapWith(*_seq, view_); +} + +/** Create view which pairs each row with corresponding row + * + * This is like a row-by-row concatenation. Both views must have + * the same number of rows, the result has all properties from + * this view plus any other properties from the other view. + * + * This view operation is based on a custom viewer and is modifiable. + */ +c4_View c4_View::Pair(const c4_View& view_) const +{ + return f4_CustPair(*_seq, view_); +} + +/** Create view with rows from another view appended + * + * Constructs a view which has all rows of this view, and all rows + * of the second view appended. The structure of the second view + * is assumed to be identical to this one. This operation is a bit + * similar to appending all rows from the second view, but it does + * not actually store the result anywhere, it just looks like it. + * + * This view operation is based on a custom viewer and is modifiable. + */ +c4_View c4_View::Concat(const c4_View& view_) const +{ + return f4_CustConcat(*_seq, view_); +} + +/** Create view with one property renamed (must be of same type) + * + * This view operation is based on a custom viewer and is modifiable. + */ +c4_View c4_View::Rename(const c4_Property& old_, const c4_Property& new_) const +{ + return f4_CustRename(*_seq, old_, new_); +} + +/** Create view with a subview, grouped by the specified properties + * + * This operation is similar to the SQL 'GROUP BY', but it takes + * advantage of the fact that Metakit supports nested views. The + * view returned from this member has one row per distinct group, + * with an extra view property holding the remaining properties. + * If there are N rows in the original view matching key X, then + * the result is a row for key X, with a subview of N rows. The + * properties of the subview are all the properties not in the key. + * + * This view operation is based on a read-only custom viewer. + */ +c4_View c4_View::GroupBy( + const c4_View& keys_, ///< properties in this view determine grouping + const c4_ViewProp& result_ ///< name of new subview defined in result + ) const +{ + return f4_CustGroupBy(*_seq, keys_, result_); +} + +/** Create view with count of duplicates, when grouped by key + * + * This is similar to c4_View::GroupBy, but it determines only the + * number of rows in each group and does not create a nested view. + * + * This view operation is based on a read-only custom viewer. + */ +c4_View c4_View::Counts( + const c4_View& keys_, ///< properties in this view determine grouping + const c4_IntProp& result_ ///< new count property defined in result + ) const +{ + return f4_CustGroupBy(*_seq, keys_, result_); // third arg is c4_IntProp +} + +/** Create view with all duplicate rows omitted + * + * This view operation is based on a read-only custom viewer. + */ +c4_View c4_View::Unique() const +{ + c4_IntProp count ("#N#"); + return Counts(Clone(), count).ProjectWithout(count); +} + +/** Create view which is the set union (assumes no duplicate rows) + * + * Calculates the set union. This will only work if both input + * views are sets, i.e. they have no duplicate rows in them. + * + * This view operation is based on a read-only custom viewer. + */ +c4_View c4_View::Union(const c4_View& view_) const +{ + return Concat(view_).Unique(); +} + +/** Create view with all rows also in the given view (no dups) + * + * Calculates the set intersection. This will only work if both + * input views are sets, i.e. they have no duplicate rows in them. + * + * This view operation is based on a read-only custom viewer. + */ +c4_View c4_View::Intersect(const c4_View& view_) const +{ + c4_View v = Concat(view_); + + // assume neither view has any duplicates + c4_IntProp count ("#N#"); + return v.Counts(Clone(), count).Select(count [2]).ProjectWithout(count); +} + +/** Create view with all rows not in both views (no dups) + * + * Calculates the "XOR" of two sets. This will only work if both + * input views are sets, i.e. they have no duplicate rows in them. + * + * This view operation is based on a read-only custom viewer. + */ +c4_View c4_View::Different(const c4_View& view_) const +{ + c4_View v = Concat(view_); + + // assume neither view has any duplicates + c4_IntProp count ("#N#"); + return v.Counts(Clone(), count).Select(count [1]).ProjectWithout(count); +} + +/** Create view with all rows not in the given view (no dups) + * + * Calculates set-difference of this view minus arg view. Result + * is a subset, unlike c4_View::Different. Will only work if both + * input views are sets, i.e. they have no duplicate rows in them. + * + * This view operation is based on a read-only custom viewer. + */ +c4_View c4_View::Minus( + const c4_View& view_ ///< the second view + ) const +{ + // inefficient: calculate difference, then keep only those in self + return Intersect(Different(view_)); +} + +/** Create view with a specific subview expanded, like a join + * + * This operation is the inverse of c4_View::GroupBy, expanding + * all rows in specified subview and returning a view which looks + * as if the rows in each subview were "expanded in place". + * + * This view operation is based on a read-only custom viewer. + */ +c4_View c4_View::JoinProp( + const c4_ViewProp& sub_, ///< name of the subview to expand + bool outer_ ///< true: keep rows with empty subviews + ) const +{ + return f4_CustJoinProp(*_seq, sub_, outer_); +} + +/** Create view which is the relational join on the given keys + * + * This view operation is based on a read-only custom viewer. + */ +c4_View c4_View::Join( + const c4_View& keys_, ///< properties in this view determine the join + const c4_View& view_, ///< second view participating in the join + bool outer_ ///< true: keep rows with no match in second view + ) const +{ + // inefficient: calculate difference, then keep only those in self + return f4_CustJoin(*_seq, keys_, view_, outer_); +} + +/** Create an identity view which only allows reading + * + * This view operation is based on a custom viewer. + */ +c4_View c4_View::ReadOnly() const +{ + return f4_CreateReadOnly(*_seq); +} + +/** Create mapped view which adds a hash lookup layer + * + * This view creates and manages a special hash map view, to implement a + * fast find on the key. The key is defined to consist of the first + * numKeys_ properties of the underlying view. + * + * The map_ view must be empty the first time this hash view is used, so + * that Metakit can fill it based on whatever rows are already present in + * the underlying view. After that, neither the underlying view nor the + * map view may be modified other than through this hash mapping layer. + * The defined structure of the map view must be "_H:I,_R:I". + * + * This view is modifiable. Insertions and changes to key field properties + * can cause rows to be repositioned to maintain hash uniqueness. Careful: + * when a row is changed in such a way that its key is the same as in another + * row, that other row will be deleted from the view. + * + * Example of use: + * @code + * c4_View data = storage.GetAs("people[name:S,age:I]"); + * c4_View datah = storage.GetAs("people_H[_H:I,_R:I]"); + * c4_View hash = raw.Hash(datah, 1); + * ... hash.GetSize() ... + * hash.Add(...) + * @endcode + */ +c4_View c4_View::Hash(const c4_View& map_, int numKeys_) const +{ + return f4_CreateHash(*_seq, numKeys_, map_._seq); +} + +/** Create mapped view which blocks its rows in two levels + * + * This view acts like a large flat view, even though the actual rows are + * stored in blocks, which are rebalanced automatically to maintain a good + * trade-off between block size and number of blocks. + * + * The underlying view must be defined with a single view property, with + * the structure of the subview being as needed. An example of a blocked + * view definition which will act like a single one containing 2 properties: + * @code + * c4_View raw = storage.GetAs("people[_B[name:S,age:I]]"); + * c4_View flat = raw.Blocked(); + * ... flat.GetSize() ... + * flat.InsertAt(...) + * @endcode + * + * This view operation is based on a custom viewer and is modifiable. + */ +c4_View c4_View::Blocked() const +{ + return f4_CreateBlocked(*_seq); +} + +/** Create mapped view which keeps its rows ordered + * + * This is an identity view, which has as only use to inform Metakit that + * the underlying view can be considered to be sorted on its first numKeys_ + * properties. The effect is that c4_View::Find will try to use binary + * search when the search includes key properties (results will be identical + * to unordered views, the find will just be more efficient). + * + * This view is modifiable. Insertions and changes to key field properties + * can cause rows to be repositioned to maintain the sort order. Careful: + * when a row is changed in such a way that its key is the same as in another + * row, that other row will be deleted from the view. + * + * This view can be combined with c4_View::Blocked, to create a 2-level + * btree structure. + */ +c4_View c4_View::Ordered(int numKeys_) const +{ + return f4_CreateOrdered(*_seq, numKeys_); +} + +/** Create mapped view which maintains an index permutation + * + * This is an identity view which somewhat resembles the ordered view, it + * maintains a secondary "map" view to contain the permutation to act as + * an index. The indexed view presents the same order of rows as the + * underlying view, but the index map is set up in such a way that binary + * search is possible on the keys specified. When the "unique" parameter + * is true, insertions which would create a duplicate key are ignored. + * + * This view is modifiable. Careful: when a row is changed in such a way + * that its key is the same as in another row, that other row will be + * deleted from the view. + */ +c4_View c4_View::Indexed(const c4_View& map_, const c4_View& props_, + bool unique_) const +{ + return f4_CreateIndexed(*_seq, *map_._seq, props_, unique_); +} + +/** Return the index of the specified row in this view (or -1) + * + * This function can be used to "unmap" an index of a derived view back + * to the original underlying view. + */ +int c4_View::GetIndexOf(const c4_RowRef& row_) const +{ + c4_Cursor cursor = &row_; + + return cursor._seq->RemapIndex(cursor._index, _seq); +} + +/// Restrict the search range for rows +int c4_View::RestrictSearch(const c4_RowRef& c_, int& pos_, int& count_) +{ + return _seq->RestrictSearch(&c_, pos_, count_) ? 0 : ~0; +} + +/** Find index of the the next entry matching the specified key. + * + * Defaults to linear search, but hash- and ordered-views will use a better + * algorithm if possible. Only the properties present in the search key + * are used to determine whether a row matches the key. + * @return position where match occurred + * @retval -1 if not found + */ +int c4_View::Find( + const c4_RowRef& crit_, ///< the value to look for + int start_ ///< the index to start with + ) const +{ + d4_assert(start_ >= 0); + + c4_Row copy = crit_; // the lazy (and slow) solution: make a copy + + int count = GetSize() - start_; + if (_seq->RestrictSearch(©, start_, count)) { + c4_View refView = copy.Container(); + c4_Sequence* refSeq = refView._seq; + d4_assert(refSeq != 0); + + c4_Bytes data; + + for (int j = 0; j < count; ++j) { + int i; + + for (i = 0; i < refSeq->NumHandlers(); ++i) { + c4_Handler& h = refSeq->NthHandler(i); // no context issues + + if (!_seq->Get(start_ + j, h.PropId(), data)) + h.ClearBytes(data); + + if (h.Compare(0, data) != 0) // always row 0 + break; + } + + if (i == refSeq->NumHandlers()) + return start_ + j; + } + } + + return -1; +} + +/** Search for a key, using the native sort order of the view + * @return position where found, or where it may be inserted, + * this position can also be just past the last row + */ +int c4_View::Search(const c4_RowRef& crit_) const +{ + int l = -1, u = GetSize(); + while (l + 1 != u) { + const int m = (l + u) >> 1; + if (_seq->Compare(m, &crit_) < 0) + //if (crit_ > (*this)[m]) // Dec 2001: see comments below + l = m; + else + u = m; + } + + return u; +} + +/// Return number of matching keys, and pos of first one as arg +int c4_View::Locate(const c4_RowRef& crit_, int* pos_) const +{ + // Dec 2001: fixed a problem with searching of partial rows. + // + // There is an *extremely* tricky issue in here, in that the + // comparison operator for rows is not symmetric. So in the + // general case, "a == b" is not euivalent to "b == a". This + // is without doubt a design mistake (and should have at least + // been named differently). + // + // The reason is that the number of properties in both rowrefs + // need not be the same. Only the properties of the leftmost + // rowref are compared against the other one. This also applies + // to the other comparisons, i.e. !=, <, >, <=, and >=. + // + // All Compare calls below have been changed to use comparisons + // in the proper order and now use "rowref <op> rowref" syntax. + + c4_Cursor curr (*(c4_Sequence*) _seq, 0); // loses const + + int l = -1, u = GetSize(); + while (l + 1 != u) { + curr._index = (l + u) >> 1; + if (crit_ > *curr) + l = curr._index; + else + u = curr._index; + } + + if (pos_ != 0) + *pos_ = u; + + // only look for more if the search hit an exact match + curr._index = u; + if (u == GetSize() || crit_ != *curr) + return 0; + + // as Jon Bentley wrote in DDJ Apr 2000, setting l2 to -1 is better than u + int l2 = -1, u2 = GetSize(); + while (l2 + 1 != u2) { + curr._index = (l2 + u2) >> 1; + if (crit_ >= *curr) + l2 = curr._index; + else + u2 = curr._index; + } + + return u2 - u; +} + +/// Compare two views lexicographically (rows 0..N-1). +int c4_View::Compare(const c4_View& view_) const +{ + if (_seq == view_._seq) + return 0; + + int na = GetSize(); + int nb = view_.GetSize(); + int i; + + for (i = 0; i < na && i < nb; ++i) + if (GetAt(i) != view_.GetAt(i)) + return GetAt(i) < view_.GetAt(i) ? -1 : +1; + + return na == nb ? 0 : i < na ? +1 : -1; +} + +///////////////////////////////////////////////////////////////////////////// + +/** @class c4_Cursor + * + * An iterator for collections of rows (views). + * + * Cursor objects can be used to point to specific entries in a view. + * A cursor acts very much like a pointer to a row in a view, and is + * returned when taking the address of a c4_RowRef. Dereferencing + * a cursor leads to the original row reference again. You can construct a + * cursor for a c4_Row, but since such rows are not part of a collection, + * incrementing or decrementing these cursors is meaningless (and wrong). + * + * The usual range of pointer operations can be applied to these objects: + * pre/post-increment and decrement, adding or subtracting integer offsets, + * as well as the full range of comparison operators. If two cursors + * point to entries in the same view, their difference can be calculated. + * + * As with regular pointers, care must be taken to avoid running off of + * either end of a view (the debug build includes assertions to check this). + */ + +/** @class c4_RowRef + * + * Reference to a data row, can be used on either side of an assignment. + * + * Row references are created when dereferencing a c4_Cursor or when + * indexing an element of a c4_View. Assignment will change the + * corresponding item. Rows (objects of type c4_Row) are a special + * case of row references, consisting of a view with exactly one item. + * + * Internally, row references are very similar to cursors, in fact they are + * little more than a wrapper around them. The essential difference is one + * of semantics: comparing row references compares contents, copying row + * references copies the contents, whereas cursor comparison and copying + * deals with the pointer to the row, not its contents. + */ + +///////////////////////////////////////////////////////////////////////////// +// c4_Row + +c4_Row::c4_Row () + : c4_RowRef (* Allocate()) +{ +} + +c4_Row::c4_Row (const c4_Row& row_) + : c4_RowRef (* Allocate()) +{ + operator= (row_); +} + +c4_Row::c4_Row (const c4_RowRef& rowRef_) + : c4_RowRef (* Allocate()) +{ + operator= (rowRef_); +} + +c4_Row::~c4_Row () +{ + Release(_cursor); +} + +c4_Row& c4_Row::operator= (const c4_Row& row_) +{ + return operator= ((const c4_RowRef&) row_); +} + +/// Assignment from a reference to a row. +c4_Row& c4_Row::operator= (const c4_RowRef& rowRef_) +{ + d4_assert(_cursor._seq != 0); + + if (_cursor != &rowRef_) { + d4_assert(_cursor._index == 0); + _cursor._seq->SetAt(0, &rowRef_); + } + + return *this; +} + +/// Adds all properties and values into this row. +void c4_Row::ConcatRow(const c4_RowRef& rowRef_) +{ + d4_assert(_cursor._seq != 0); + + c4_Cursor cursor = &rowRef_; // trick to access private rowRef_._cursor + d4_assert(cursor._seq != 0); + + c4_Sequence& rhSeq = * cursor._seq; + + c4_Bytes data; + + for (int i = 0; i < rhSeq.NumHandlers(); ++i) { + c4_Handler& h = rhSeq.NthHandler(i); + + h.GetBytes(cursor._index, data); + _cursor._seq->Set(_cursor._index, h.Property(), data); + } +} + +c4_Row operator+ (const c4_RowRef& a_, const c4_RowRef& b_) +{ + c4_Row row = a_; + row.ConcatRow(b_); + return row; +} + +c4_Cursor c4_Row::Allocate() +{ + c4_Sequence* seq = d4_new c4_HandlerSeq (0); + seq->IncRef(); + + seq->Resize(1); + + return c4_Cursor (*seq, 0); +} + +void c4_Row::Release(c4_Cursor row_) +{ + d4_assert(row_._seq != 0); + d4_assert(row_._index == 0); + + row_._seq->DecRef(); +} + +///////////////////////////////////////////////////////////////////////////// + +/** @class c4_Property + * + * Base class for the basic data types. + * + * Property objects exist independently of view, row, and storage objects. + * They have a name and type, and can appear in any number of views. + * You will normally only use derived classes, to maintain strong typing. + */ + + // This is a workaround for the fact that the initialization order of + // static objects is not always adequate (implementation dependent). + // Extremely messy solution, to allow statically declared properties. + // + // These are the only static variables in the entire Metakit core lib. + + static c4_ThreadLock* sThreadLock = 0; + static c4_StringArray* sPropNames = 0; + static c4_DWordArray* sPropCounts = 0; + + /// Call this to get rid of some internal datastructues (on exit) + void c4_Property::CleanupInternalData() + { + delete sPropNames; + sPropNames = 0; // race + + delete sPropCounts; + sPropCounts = 0; // race + + delete sThreadLock; + sThreadLock = 0; // race + } + +c4_Property::c4_Property (char type_, const char* name_) + : _type (type_) +{ + if (sThreadLock == 0) + sThreadLock = d4_new c4_ThreadLock; + + c4_ThreadLock::Hold lock; // grabs the lock until end of scope + + if (sPropNames == 0) + sPropNames = d4_new c4_StringArray; + + if (sPropCounts == 0) + sPropCounts = d4_new c4_DWordArray; + + c4_String temp = name_; + + _id = sPropNames->GetSize(); + while (-- _id >= 0) { + const char* p = sPropNames->GetAt(_id); + // optimize for first char case-insensitive match + if (((*p ^ *name_) & ~0x20) == 0 && temp.CompareNoCase(p) == 0) + break; + } + + if (_id < 0) { + int size = sPropCounts->GetSize(); + + for (_id = 0; _id < size; ++_id) + if (sPropCounts->GetAt(_id) == 0) + break; + + if (_id >= size) { + sPropCounts->SetSize(_id + 1); + sPropNames->SetSize(_id + 1); + } + + sPropCounts->SetAt(_id, 0); + sPropNames->SetAt(_id, name_); + } + + Refs(+1); +} + +c4_Property::c4_Property (const c4_Property& prop_) + : _id (prop_.GetId()), _type (prop_.Type()) +{ + c4_ThreadLock::Hold lock; + + d4_assert(sPropCounts != 0); + d4_assert(sPropCounts->GetAt(_id) > 0); + + Refs(+1); +} + +c4_Property::~c4_Property () +{ + c4_ThreadLock::Hold lock; + + Refs(-1); +} + +void c4_Property::operator= (const c4_Property& prop_) +{ + c4_ThreadLock::Hold lock; + + prop_.Refs(+1); + Refs(-1); + + _id = prop_.GetId(); + _type = prop_.Type(); +} + + /// Return the name of this property +const char* c4_Property::Name() const +{ + c4_ThreadLock::Hold lock; + + d4_assert(sPropNames != 0); + return sPropNames->GetAt(_id); +} + +/** Adjust the reference count + * + * This is part of the implementation and shouldn't normally be called. + * This code is only called with the lock held, and always thread-safe. + */ +void c4_Property::Refs(int diff_) const +{ + d4_assert(diff_ == -1 || diff_ == +1); + + d4_assert(sPropCounts != 0); + sPropCounts->ElementAt(_id) += diff_; + +#if q4_CHECK + // get rid of the cache when the last property goes away + static t4_i32 sPropTotals; + + sPropTotals += diff_; + if (sPropTotals == 0) + CleanupInternalData(); +#endif +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/viewx.cpp b/akregator/src/mk4storage/metakit/src/viewx.cpp new file mode 100644 index 000000000..db30d14e5 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/viewx.cpp @@ -0,0 +1,857 @@ +// viewx.cpp -- +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +/** @file + * Implements c4_Sequence, c4_Reference, and c4_...Ref + */ + +#include "header.h" +#include "handler.h" +#include "store.h" +#include "column.h" + +///////////////////////////////////////////////////////////////////////////// + +c4_Sequence::c4_Sequence () + : _refCount (0), _dependencies (0), _propertyLimit (0), _tempBuf (0) +{ +} + +c4_Sequence::~c4_Sequence () +{ + d4_assert(_refCount == 0); + + d4_assert(!_dependencies); // there can be no dependencies left + + ClearCache(); + + delete _tempBuf; +} + +c4_Persist* c4_Sequence::Persist() const +{ + return 0; +} + + /// Increment the reference count of this sequence +void c4_Sequence::IncRef() +{ + ++_refCount; + + d4_assert(_refCount != 0); +} + + /// Decrement the reference count, delete objects when last +void c4_Sequence::DecRef() +{ + d4_assert(_refCount != 0); + + if (--_refCount == 0) + delete this; +} + + /// Return the current reference count +int c4_Sequence::NumRefs() const +{ + return _refCount; +} + + /// Compare the specified row with another one +int c4_Sequence::Compare(int index_, c4_Cursor cursor_) const +{ + d4_assert(cursor_._seq != 0); + + c4_Bytes data; + + for (int colNum = 0; colNum < NumHandlers(); ++colNum) + { + c4_Handler& h = NthHandler(colNum); + + const c4_Sequence* hc = HandlerContext(colNum); + int i = RemapIndex(index_, hc); + + if (!cursor_._seq->Get(cursor_._index, h.PropId(), data)) + h.ClearBytes(data); + + int f = h.Compare(i, data); + if (f != 0) + return f; + } + + return 0; +} + + /// Restrict the search range for rows +bool c4_Sequence::RestrictSearch(c4_Cursor, int&, int&) +{ + return true; +} + + /// Replace the contents of a specified row +void c4_Sequence::SetAt(int index_, c4_Cursor newElem_) +{ + d4_assert(newElem_._seq != 0); + + c4_Bytes data; + + c4_Notifier change (this); + if (GetDependencies()) + change.StartSetAt(index_, newElem_); + + for (int i = 0; i < newElem_._seq->NumHandlers(); ++i) + { + c4_Handler& h = newElem_._seq->NthHandler(i); + + // added 06-12-1999 to do index remapping for derived seq's + const c4_Sequence* hc = newElem_._seq->HandlerContext(i); + int ri = newElem_._seq->RemapIndex(newElem_._index, hc); + + h.GetBytes(ri, data); + +// Set(index_, cursor._seq->NthProperty(i), data); + int colNum = PropIndex(h.Property()); + d4_assert(colNum >= 0); + + NthHandler(colNum).Set(index_, data); + } + + // if number of props in dest is larger after adding, clear the rest + // this way, new props get copied and undefined props get cleared + if (newElem_._seq->NumHandlers() < NumHandlers()) + { + for (int j = 0; j < NumHandlers(); ++j) + { + c4_Handler& h = NthHandler(j); + + // if the property does not appear in the source + if (newElem_._seq->PropIndex(h.PropId()) < 0) + { + h.ClearBytes(data); + h.Set(index_, data); + } + } + } +} + + /// Remap the index to an underlying view +int c4_Sequence::RemapIndex(int index_, const c4_Sequence* seq_) const +{ + return seq_ == this ? index_ : -1; +} + + /// Gives access to a general purpose temporary buffer +c4_Bytes& c4_Sequence::Buffer() +{ + if (_tempBuf == 0) + _tempBuf = d4_new c4_Bytes; + return *_tempBuf; +} + + // 1.8.5: extra buffer to hold returned description strings +const char* c4_Sequence::UseTempBuffer(const char* str_) +{ + return strcpy((char*) Buffer().SetBuffer(strlen(str_) + 1), str_); +} + + /// Change number of rows, either by inserting or removing them +void c4_Sequence::Resize(int newSize_, int) +{ + if (NumHandlers() > 0) + { + int diff = newSize_ - NumRows(); + + if (diff > 0) + { + c4_Row empty; // make sure this doesn't recurse, see below + InsertAt(NumRows(), &empty, diff); + } + else if (diff < 0) + RemoveAt(newSize_, - diff); + } + else // need special case to avoid recursion for c4_Row allocations + SetNumRows(newSize_); +} + + /// Insert one or more rows into this sequence +void c4_Sequence::InsertAt(int index_, c4_Cursor newElem_, int count_) +{ + d4_assert(newElem_._seq != 0); + + c4_Notifier change (this); + if (GetDependencies()) + change.StartInsertAt(index_, newElem_, count_); + + SetNumRows(NumRows() + count_); + + c4_Bytes data; + + for (int i = 0; i < newElem_._seq->NumHandlers(); ++i) + { + c4_Handler& h = newElem_._seq->NthHandler(i); + + // added 06-12-1999 to do index remapping for derived seq's + const c4_Sequence* hc = newElem_._seq->HandlerContext(i); + int ri = newElem_._seq->RemapIndex(newElem_._index, hc); + + h.GetBytes(ri, data); + + int colNum = PropIndex(h.Property()); + d4_assert(colNum >= 0); + + if (h.Property().Type() == 'V') + { + // special treatment for subviews, insert empty, then overwrite + // changed 19990904 - probably fixes a long-standing limitation + c4_Bytes temp; + h.ClearBytes(temp); + + c4_Handler& h2 = NthHandler(colNum); + h2.Insert(index_, temp, count_); + for (int j = 0; j < count_; ++j) + h2.Set(index_ + j, data); + } + else + NthHandler(colNum).Insert(index_, data, count_); + } + + // if number of props in dest is larger after adding, clear the rest + // this way, new props get copied and undefined props get cleared + if (newElem_._seq->NumHandlers() < NumHandlers()) + { + for (int j = 0; j < NumHandlers(); ++j) + { + c4_Handler& h = NthHandler(j); + + // if the property does not appear in the source + if (newElem_._seq->PropIndex(h.PropId()) < 0) + { + h.ClearBytes(data); + h.Insert(index_, data, count_); + } + } + } +} + + /// Remove one or more rows from this sequence +void c4_Sequence::RemoveAt(int index_, int count_) +{ + c4_Notifier change (this); + if (GetDependencies()) + change.StartRemoveAt(index_, count_); + + SetNumRows(NumRows() - count_); + + //! careful, this does no index remapping, wrong for derived seq's + for (int i = 0; i < NumHandlers(); ++i) + NthHandler(i).Remove(index_, count_); +} + + /// Move a row to another position +void c4_Sequence::Move(int from_, int to_) +{ + c4_Notifier change (this); + if (GetDependencies()) + change.StartMove(from_, to_); + + //! careful, this does no index remapping, wrong for derived seq's + for (int i = 0; i < NumHandlers(); ++i) + NthHandler(i).Move(from_, to_); +} + + /// Return the id of the N-th property +int c4_Sequence::NthPropId(int index_) const +{ + return NthHandler(index_).PropId(); +} + +void c4_Sequence::ClearCache() +{ + if (_propertyLimit > 0) + { + delete [] _propertyMap; // property indexes may change + _propertyLimit = 0; + } +} + + /// Find the index of a property by its id +int c4_Sequence::PropIndex(int propId_) +{ +//! CACHING NOTE: derived views will fail if underlying view is restructured +// still, this cache is kept, since sort will fail anyway... +// The only safe change in these cases is adding new properties at the end. + + // use the map for the fastest result once known + if (propId_ < _propertyLimit && _propertyMap[propId_] >= 0) + return _propertyMap[propId_]; + + // locate the property using a linear search, return if not present + int n = NumHandlers(); + do + if (--n < 0) + return -1; + while (NthPropId(n) != propId_); + + // if the map is too small, resize it (with a little slack) + if (propId_ >= _propertyLimit) + { + int round = (propId_ + 8) & ~0x07; + short* vec = d4_new short [round]; + + for (int i = 0; i < round; ++i) + vec[i] = i < _propertyLimit ? _propertyMap[i] : -1; + + if (_propertyLimit > 0) + delete [] _propertyMap; + + _propertyMap = vec; + _propertyLimit = round; + } + + // we have a map, adjust the entry and return + return _propertyMap[propId_] = n; +} + + /// Find the index of a property, or create a new entry +int c4_Sequence::PropIndex(const c4_Property& prop_) +{ + int pos = PropIndex(prop_.GetId()); + if (pos >= 0) + { + d4_assert(NthHandler(pos).Property().Type() == prop_.Type()); + return pos; + } + + c4_Handler* h = CreateHandler(prop_); + d4_assert(h != 0); + + int i = AddHandler(h); + if (i >= 0 && NumRows() > 0) + { + c4_Bytes data; + h->ClearBytes(data); + h->Insert(0, data, NumRows()); + } + + return i; +} + +const char* c4_Sequence::Description() +{ + return 0; +} + +int c4_Sequence::ItemSize(int index_, int propId_) +{ + int colNum = PropIndex(propId_); + return colNum >= 0 ? NthHandler(colNum).ItemSize(index_) : -1; +} + +bool c4_Sequence::Get(int index_, int propId_, c4_Bytes& buf_) +{ + int colNum = PropIndex(propId_); + if (colNum < 0) + return false; + + NthHandler(colNum).GetBytes(index_, buf_); + return true; +} + +void c4_Sequence::Set(int index_, const c4_Property& prop_, const c4_Bytes& buf_) +{ + int colNum = PropIndex(prop_); + d4_assert(colNum >= 0); + + c4_Handler& h = NthHandler(colNum); + + c4_Notifier change (this); + if (GetDependencies()) + change.StartSet(index_, prop_.GetId(), buf_); + + if (buf_.Size()) + h.Set(index_, buf_); + else + { + c4_Bytes empty; + h.ClearBytes(empty); + h.Set(index_, empty); + } +} + + /// Register a sequence to receive change notifications +void c4_Sequence::Attach(c4_Sequence* child_) +{ + IncRef(); + + if (!_dependencies) + _dependencies = d4_new c4_Dependencies; + + _dependencies->Add(child_); +} + + /// Unregister a sequence which received change notifications +void c4_Sequence::Detach(c4_Sequence* child_) +{ + d4_assert(_dependencies != 0); + + if (!_dependencies->Remove(child_)) + { + delete _dependencies; + _dependencies = 0; + } + + DecRef(); +} + + /// Called just before a change is made to the sequence +c4_Notifier* c4_Sequence::PreChange(c4_Notifier&) +{ + d4_assert(0); // should not be called, because it should not attach + return 0; +} + + /// Called after changes have been made to the sequence +void c4_Sequence::PostChange(c4_Notifier&) +{ +} + +///////////////////////////////////////////////////////////////////////////// + +c4_Reference& c4_Reference::operator= (const c4_Reference& value_) +{ + c4_Bytes result; + value_.GetData(result); + SetData(result); + + return *this; +} + +bool operator== (const c4_Reference& a_, const c4_Reference& b_) +{ + c4_Bytes buf1; + bool f1 = a_.GetData(buf1); + + c4_Bytes buf2; + bool f2 = b_.GetData(buf2); + + // if absent, fill either with zero bytes to match length + if (!f1) + buf1.SetBufferClear(buf2.Size()); + if (!f2) + buf2.SetBufferClear(buf1.Size()); + + return buf1 == buf2; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_IntRef::operator t4_i32 () const +{ + c4_Bytes result; + if (!GetData(result)) + return 0; + + d4_assert(result.Size() == sizeof (t4_i32)); + return *(const t4_i32*) result.Contents(); +} + +c4_IntRef& c4_IntRef::operator= (t4_i32 value_) +{ + SetData(c4_Bytes (&value_, sizeof value_)); + return *this; +} + +///////////////////////////////////////////////////////////////////////////// +#if !q4_TINY +///////////////////////////////////////////////////////////////////////////// + +c4_LongRef::operator t4_i64 () const +{ + c4_Bytes result; + if (!GetData(result)) + { + static t4_i64 zero; + return zero; + } + + d4_assert(result.Size() == sizeof (t4_i64)); + return *(const t4_i64*) result.Contents(); +} + +c4_LongRef& c4_LongRef::operator= (t4_i64 value_) +{ + SetData(c4_Bytes (&value_, sizeof value_)); + return *this; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_FloatRef::operator double () const +{ + c4_Bytes result; + if (!GetData(result)) + return 0; + + d4_assert(result.Size() == sizeof (float)); + return *(const float*) result.Contents(); +} + +c4_FloatRef& c4_FloatRef::operator= (double value_) +{ + float v = (float) value_; // loses precision + SetData(c4_Bytes (&v, sizeof v)); + return *this; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_DoubleRef::operator double () const +{ + c4_Bytes result; + if (!GetData(result)) + return 0; + + d4_assert(result.Size() == sizeof (double)); + return *(const double*) result.Contents(); +} + +c4_DoubleRef& c4_DoubleRef::operator= (double value_) +{ + SetData(c4_Bytes (&value_, sizeof value_)); + return *this; +} + +///////////////////////////////////////////////////////////////////////////// +#endif // !q4_TINY +///////////////////////////////////////////////////////////////////////////// + +c4_BytesRef::operator c4_Bytes () const +{ + c4_Bytes result; + GetData(result); + + // the result must immediately be used, its lifetime may be limited + return result; +} + +c4_BytesRef& c4_BytesRef::operator= (const c4_Bytes& value_) +{ + SetData(value_); + return *this; +} + +c4_Bytes c4_BytesRef::Access(t4_i32 off_, int len_) const +{ + c4_Bytes& buffer = _cursor._seq->Buffer(); + + int colNum = _cursor._seq->PropIndex(_property.GetId()); + if (colNum >= 0) + { + c4_Handler& h = _cursor._seq->NthHandler(colNum); + int sz = h.ItemSize(_cursor._index); + if (len_ == 0 || off_ + len_ > sz) + len_ = sz - off_; + + c4_Column* col = h.GetNthMemoCol(_cursor._index, true); + if (col != 0) + { + + if (len_ > 0) { + col->FetchBytes(off_, len_, buffer, true); + return buffer; + } + } + else // do it the hard way for custom/mapped views (2002-03-13) + { + c4_Bytes result; + GetData(result); + d4_assert(off_ + len_ <= result.Size()); + return c4_Bytes (result.Contents() + off_, len_, true); + } + } + + return c4_Bytes (); +} + +bool c4_BytesRef::Modify(const c4_Bytes& buf_, t4_i32 off_, int diff_) const +{ + int colNum = _cursor._seq->PropIndex(_property.GetId()); + if (colNum >= 0) + { + c4_Handler& h = _cursor._seq->NthHandler(colNum); + const int n = buf_.Size(); + const t4_i32 limit = off_ + n; // past changed bytes + const t4_i32 overshoot = limit - h.ItemSize(_cursor._index); + + if (diff_ < overshoot) + diff_ = overshoot; + + c4_Column* col = h.GetNthMemoCol(_cursor._index, true); + if (col != 0) + { + if (diff_ < 0) + col->Shrink(limit, - diff_); + else if (diff_ > 0) + // insert bytes in the highest possible spot + // if a gap is created, it will contain garbage + col->Grow(overshoot > 0 ? col->ColSize() : + diff_ > n ? off_ : limit - diff_, diff_); + + col->StoreBytes(off_, buf_); + } + else // do it the hard way for custom/mapped views (2002-03-13) + { + c4_Bytes orig; + GetData(orig); + + c4_Bytes result; + t4_byte* ptr = result.SetBuffer(orig.Size() + diff_); + + memcpy(ptr, orig.Contents(), off_); + memcpy(ptr + off_, buf_.Contents(), n); + memcpy(ptr + off_ + n, orig.Contents() + off_, orig.Size() - off_); + + SetData(result); + } + return true; + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_StringRef::operator const char* () const +{ + c4_Bytes result; + GetData(result); + + return result.Size() > 0 ? (const char*) result.Contents() : ""; +} + +c4_StringRef& c4_StringRef::operator= (const char* value_) +{ + SetData(c4_Bytes (value_, strlen(value_) + 1)); + return *this; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_ViewRef::operator c4_View () const +{ + c4_Bytes result; + if (!GetData(result)) + return (c4_Sequence*) 0; // resolve ambiguity + + d4_assert(result.Size() == sizeof (c4_Sequence*)); + return *(c4_Sequence* const*) result.Contents(); +} + +c4_ViewRef& c4_ViewRef::operator= (const c4_View& value_) +{ + SetData(c4_Bytes (&value_._seq, sizeof value_._seq)); + return *this; +} + +///////////////////////////////////////////////////////////////////////////// + +c4_Stream::~c4_Stream () +{ +} + +///////////////////////////////////////////////////////////////////////////// + +c4_Strategy::c4_Strategy () + : _bytesFlipped (false), _failure (0), + _mapStart (0), _dataSize (0), _baseOffset (0), _rootPos (-1), _rootLen (-1) +{ +} + +c4_Strategy::~c4_Strategy () +{ + d4_assert(_mapStart == 0); +} + + /// Read a number of bytes +int c4_Strategy::DataRead(t4_i32, void*, int) +{ +/* + if (_mapStart != 0 && pos_ + length_ <= _dataSize) + { + memcpy(buffer_, _mapStart + pos_, length_); + return length_; + } +*/ + ++_failure; + return -1; +} + + /// Write a number of bytes, return true if successful +void c4_Strategy::DataWrite(t4_i32, const void*, int) +{ + ++_failure; +} + + /// Flush and truncate file +void c4_Strategy::DataCommit(t4_i32) +{ +} + + /// Override to support memory-mapped files +void c4_Strategy::ResetFileMapping() +{ +} + + /// Report total size of the datafile +t4_i32 c4_Strategy::FileSize() +{ + return _dataSize; +} + + /// Return a value to use as fresh generation counter +t4_i32 c4_Strategy::FreshGeneration() +{ + return 1; +} + + /// Define the base offset where data is stored +void c4_Strategy::SetBase(t4_i32 base_) +{ + t4_i32 off = base_ - _baseOffset; + _baseOffset = base_; + _dataSize -= off; + if (_mapStart != 0) + _mapStart += off; +} + +/* + end_ is file position to start from (0 defaults to FileSize()) + + result is the logical end of the datafile (or -1 if no data) + + This code uses a tiny state machine so all the code to read and decode + file marks is in one place within the loop. +*/ + + /// Scan datafile head/tail markers, return logical end of data +t4_i32 c4_Strategy::EndOfData(t4_i32 end_) +{ + enum { kStateAtEnd, kStateCommit, kStateHead, kStateOld, kStateDone }; + + t4_i32 pos = (end_ >= 0 ? end_ : FileSize()) - _baseOffset; + t4_i32 last = pos; + t4_i32 rootPos = 0; + t4_i32 rootLen = -1; // impossible value, flags old-style header + t4_byte mark [8]; + + for (int state = kStateAtEnd; state != kStateDone; ) + { + pos -= 8; + if (pos + _baseOffset < 0 && state != kStateOld) + { + // bad offset, try old-style header from start of file + pos = - _baseOffset; + state = kStateOld; + } + + if (DataRead(pos, &mark, sizeof mark) != sizeof mark) + return -1; + + t4_i32 count = 0; + for (int i = 1; i < 4; ++i) + count = (count << 8) + mark[i]; + + t4_i32 offset = 0; + for (int j = 4; j < 8; ++j) + offset = (offset << 8) + mark[j]; + + const bool isSkipTail = mark[0] == 0x80 && count == 0 && offset > 0; + const bool isCommitTail = mark[0] == 0x80 && count > 0 && offset > 0; + const bool isHeader = (mark[0] == 'J' || mark[0] == 'L') && + (mark[0] ^ mark[1]) == ('J' ^ 'L') && + mark[2] == 0x1A; + switch (state) + { + case kStateAtEnd: // no commit tail found yet + + if (isSkipTail) + { + pos -= offset; + last = pos; + } + else if (isCommitTail) + { + rootPos = offset; + rootLen = count; + state = kStateCommit; + } + else + { + pos = 8; + state = kStateOld; + } + break; + + case kStateCommit: // commit tail must be preceded by skip tail + + if (!isSkipTail) + return -1; + pos -= offset - 8; + state = kStateHead; + break; + + case kStateHead: // fetch the header + + if (!isHeader) + { + pos = 8; + state = kStateOld; + } + else + { + state = kStateDone; + } + break; + + case kStateOld: // old format, look for header in first 4 Kb + + if (isHeader && mark[3] == 0x80) + { + d4_assert(rootPos == 0); + for (int k = 8; --k >= 4; ) // old header is little-endian + rootPos = (rootPos << 8) + mark[k]; + state = kStateDone; + } + else + { + pos += 16; + if (pos > 4096) + return -1; + } + break; + } + } + + last += _baseOffset; // all seeks were relative to current offset + + if (end_ >= 0) // if end was specified, then adjust this strategy object + { + _baseOffset += pos; + d4_assert(_baseOffset >= 0); + if (_mapStart != 0) + { + _mapStart += pos; + _dataSize -= pos; + } + + _rootPos = rootPos; + _rootLen = rootLen; + } + + d4_assert(mark[0] == 'J' || mark[1] == 'J'); + _bytesFlipped = (char) *(const short*) mark != 'J'; + + return last; +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/akregator/src/mk4storage/metakit/src/win.h b/akregator/src/mk4storage/metakit/src/win.h new file mode 100644 index 000000000..d27eb2445 --- /dev/null +++ b/akregator/src/mk4storage/metakit/src/win.h @@ -0,0 +1,33 @@ +// win.h -- +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +/** @file + * Configuration header for Windows builds + */ + +#if defined (_MSDOS) +#define q4_DOS 1 +#endif + +#if defined (_WINDOWS) +#define q4_WIN 1 +#endif + +#if defined (_WIN32) +#define q4_WIN32 1 +#endif + +#if defined (_WIN32_WCE) // check for Win CE +#define q4_WINCE 1 +#define q4_WIN32 1 +#endif + +#if q4_WIN32 // WIN32 implies WIN +#undef q4_WIN +#define q4_WIN 1 +#endif + +#if q4_WIN // WIN implies not DOS, even for Win3 +#undef q4_DOS +#endif diff --git a/akregator/src/mk4storage/metakit/tests/ok/b00.txt b/akregator/src/mk4storage/metakit/tests/ok/b00.txt new file mode 100644 index 000000000..6a37cfb5c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b00.txt @@ -0,0 +1,3 @@ +>>> Should fail +*** Failed: A(false) *** +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b01.txt b/akregator/src/mk4storage/metakit/tests/ok/b01.txt new file mode 100644 index 000000000..814999fb1 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b01.txt @@ -0,0 +1,2 @@ +>>> Should succeed +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b02.txt b/akregator/src/mk4storage/metakit/tests/ok/b02.txt new file mode 100644 index 000000000..ac0e86f67 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b02.txt @@ -0,0 +1,2 @@ +>>> Int property +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b03.txt b/akregator/src/mk4storage/metakit/tests/ok/b03.txt new file mode 100644 index 000000000..7f337457e --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b03.txt @@ -0,0 +1,2 @@ +>>> Float property +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b04.txt b/akregator/src/mk4storage/metakit/tests/ok/b04.txt new file mode 100644 index 000000000..741c5e87d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b04.txt @@ -0,0 +1,2 @@ +>>> String property +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b05.txt b/akregator/src/mk4storage/metakit/tests/ok/b05.txt new file mode 100644 index 000000000..3abc4fc98 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b05.txt @@ -0,0 +1,2 @@ +>>> View property +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b06.txt b/akregator/src/mk4storage/metakit/tests/ok/b06.txt new file mode 100644 index 000000000..67a3a4402 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b06.txt @@ -0,0 +1,2 @@ +>>> View construction +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b07.txt b/akregator/src/mk4storage/metakit/tests/ok/b07.txt new file mode 100644 index 000000000..05bd95f60 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b07.txt @@ -0,0 +1,2 @@ +>>> Row manipulation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b08.txt b/akregator/src/mk4storage/metakit/tests/ok/b08.txt new file mode 100644 index 000000000..1d6cd6e10 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b08.txt @@ -0,0 +1,2 @@ +>>> Row expressions +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b09.txt b/akregator/src/mk4storage/metakit/tests/ok/b09.txt new file mode 100644 index 000000000..6f3c20ddb --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b09.txt @@ -0,0 +1,2 @@ +>>> View manipulation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b10.txt b/akregator/src/mk4storage/metakit/tests/ok/b10.txt new file mode 100644 index 000000000..55c9a3aac --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b10.txt @@ -0,0 +1,2 @@ +>>> View sorting +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b11.txt b/akregator/src/mk4storage/metakit/tests/ok/b11.txt new file mode 100644 index 000000000..16c5a04a1 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b11.txt @@ -0,0 +1,2 @@ +>>> View selection +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b12.txt b/akregator/src/mk4storage/metakit/tests/ok/b12.txt new file mode 100644 index 000000000..93e30ce6f --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b12.txt @@ -0,0 +1,2 @@ +>>> Add after remove +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b13.txt b/akregator/src/mk4storage/metakit/tests/ok/b13.txt new file mode 100644 index 000000000..d1a9a9b8e --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b13.txt @@ -0,0 +1,2 @@ +>>> Clear view entry +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b14.txt b/akregator/src/mk4storage/metakit/tests/ok/b14.txt new file mode 100644 index 000000000..f166c430c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b14.txt @@ -0,0 +1,2 @@ +>>> Empty view outlives temp storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b15.txt b/akregator/src/mk4storage/metakit/tests/ok/b15.txt new file mode 100644 index 000000000..595ca0eb1 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b15.txt @@ -0,0 +1,2 @@ +>>> View outlives temp storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b16.txt b/akregator/src/mk4storage/metakit/tests/ok/b16.txt new file mode 100644 index 000000000..fb43bbf9d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b16.txt @@ -0,0 +1,2 @@ +>>> View outlives cleared temp storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b17.txt b/akregator/src/mk4storage/metakit/tests/ok/b17.txt new file mode 100644 index 000000000..0c61591bb --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b17.txt @@ -0,0 +1,2 @@ +>>> Double property +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b18.txt b/akregator/src/mk4storage/metakit/tests/ok/b18.txt new file mode 100644 index 000000000..e9ff0b495 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b18.txt @@ -0,0 +1,2 @@ +>>> SetAtGrow usage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b19.txt b/akregator/src/mk4storage/metakit/tests/ok/b19.txt new file mode 100644 index 000000000..99c176c63 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b19.txt @@ -0,0 +1,2 @@ +>>> Bytes property +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b20.txt b/akregator/src/mk4storage/metakit/tests/ok/b20.txt new file mode 100644 index 000000000..849ebf898 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b20.txt @@ -0,0 +1,2 @@ +>>> Search sorted view +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b21.txt b/akregator/src/mk4storage/metakit/tests/ok/b21.txt new file mode 100644 index 000000000..33cc19d26 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b21.txt @@ -0,0 +1,2 @@ +>>> Memo property +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b22.txt b/akregator/src/mk4storage/metakit/tests/ok/b22.txt new file mode 100644 index 000000000..c7612a0b9 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b22.txt @@ -0,0 +1,2 @@ +>>> Stored view references +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b23.txt b/akregator/src/mk4storage/metakit/tests/ok/b23.txt new file mode 100644 index 000000000..d8967bf62 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b23.txt @@ -0,0 +1,2 @@ +>>> Sort comparison fix +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b24.txt b/akregator/src/mk4storage/metakit/tests/ok/b24.txt new file mode 100644 index 000000000..dbf75a5c9 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b24.txt @@ -0,0 +1,2 @@ +>>> Custom view comparisons +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b25.txt b/akregator/src/mk4storage/metakit/tests/ok/b25.txt new file mode 100644 index 000000000..ab01b4fcc --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b25.txt @@ -0,0 +1,2 @@ +>>> Copy row from derived +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b26.txt b/akregator/src/mk4storage/metakit/tests/ok/b26.txt new file mode 100644 index 000000000..7fd6a5d37 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b26.txt @@ -0,0 +1,2 @@ +>>> Partial memo field access +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/b27.txt b/akregator/src/mk4storage/metakit/tests/ok/b27.txt new file mode 100644 index 000000000..156a2c92c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/b27.txt @@ -0,0 +1,2 @@ +>>> Copy value to another row +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c01.txt b/akregator/src/mk4storage/metakit/tests/ok/c01.txt new file mode 100644 index 000000000..ce7ebb77c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c01.txt @@ -0,0 +1,2 @@ +>>> Slice forward +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c02.txt b/akregator/src/mk4storage/metakit/tests/ok/c02.txt new file mode 100644 index 000000000..50b88f0f2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c02.txt @@ -0,0 +1,2 @@ +>>> Slice backward +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c03.txt b/akregator/src/mk4storage/metakit/tests/ok/c03.txt new file mode 100644 index 000000000..4feea348b --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c03.txt @@ -0,0 +1,2 @@ +>>> Slice reverse +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c04.txt b/akregator/src/mk4storage/metakit/tests/ok/c04.txt new file mode 100644 index 000000000..64bf550a2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c04.txt @@ -0,0 +1,2 @@ +>>> Cartesian product +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c05.txt b/akregator/src/mk4storage/metakit/tests/ok/c05.txt new file mode 100644 index 000000000..7fae13ec4 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c05.txt @@ -0,0 +1,2 @@ +>>> Remapping +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c06.txt b/akregator/src/mk4storage/metakit/tests/ok/c06.txt new file mode 100644 index 000000000..520d9f71c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c06.txt @@ -0,0 +1,2 @@ +>>> Pairwise combination +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c07.txt b/akregator/src/mk4storage/metakit/tests/ok/c07.txt new file mode 100644 index 000000000..169aa5052 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c07.txt @@ -0,0 +1,2 @@ +>>> Concatenate views +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c08.txt b/akregator/src/mk4storage/metakit/tests/ok/c08.txt new file mode 100644 index 000000000..773e1e52a --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c08.txt @@ -0,0 +1,2 @@ +>>> Rename property +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c09.txt b/akregator/src/mk4storage/metakit/tests/ok/c09.txt new file mode 100644 index 000000000..2d577394d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c09.txt @@ -0,0 +1,2 @@ +>>> GroupBy operation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c10.txt b/akregator/src/mk4storage/metakit/tests/ok/c10.txt new file mode 100644 index 000000000..ddb98fef9 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c10.txt @@ -0,0 +1,2 @@ +>>> Counts operation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c11.txt b/akregator/src/mk4storage/metakit/tests/ok/c11.txt new file mode 100644 index 000000000..6d70cd878 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c11.txt @@ -0,0 +1,2 @@ +>>> Unique operation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c12.txt b/akregator/src/mk4storage/metakit/tests/ok/c12.txt new file mode 100644 index 000000000..d66e764c9 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c12.txt @@ -0,0 +1,2 @@ +>>> Union operation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c13.txt b/akregator/src/mk4storage/metakit/tests/ok/c13.txt new file mode 100644 index 000000000..14f1e9721 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c13.txt @@ -0,0 +1,2 @@ +>>> Intersect operation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c14.txt b/akregator/src/mk4storage/metakit/tests/ok/c14.txt new file mode 100644 index 000000000..3edcec0b4 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c14.txt @@ -0,0 +1,2 @@ +>>> Different operation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c15.txt b/akregator/src/mk4storage/metakit/tests/ok/c15.txt new file mode 100644 index 000000000..c827c757a --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c15.txt @@ -0,0 +1,2 @@ +>>> Minus operation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c16.txt b/akregator/src/mk4storage/metakit/tests/ok/c16.txt new file mode 100644 index 000000000..55d9f6990 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c16.txt @@ -0,0 +1,2 @@ +>>> View comparisons +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c17.txt b/akregator/src/mk4storage/metakit/tests/ok/c17.txt new file mode 100644 index 000000000..cd2ac75e4 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c17.txt @@ -0,0 +1,2 @@ +>>> Join operation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c18.txt b/akregator/src/mk4storage/metakit/tests/ok/c18.txt new file mode 100644 index 000000000..dfdd45a89 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c18.txt @@ -0,0 +1,2 @@ +>>> Groupby sort fix +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c19.txt b/akregator/src/mk4storage/metakit/tests/ok/c19.txt new file mode 100644 index 000000000..469df00aa --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c19.txt @@ -0,0 +1,2 @@ +>>> JoinProp operation +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c20.txt b/akregator/src/mk4storage/metakit/tests/ok/c20.txt new file mode 100644 index 000000000..cea0b6516 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c20.txt @@ -0,0 +1,2 @@ +>>> Wide cartesian product +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c21.txt b/akregator/src/mk4storage/metakit/tests/ok/c21.txt new file mode 100644 index 000000000..679055b9c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c21.txt @@ -0,0 +1,2 @@ +>>> Join on compound key +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/c22.txt b/akregator/src/mk4storage/metakit/tests/ok/c22.txt new file mode 100644 index 000000000..1646d1a5d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/c22.txt @@ -0,0 +1,2 @@ +>>> Groupby with selection +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/d01.txt b/akregator/src/mk4storage/metakit/tests/ok/d01.txt new file mode 100644 index 000000000..46092d8c7 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/d01.txt @@ -0,0 +1,2 @@ +>>> Commit aside +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/d01a.txt b/akregator/src/mk4storage/metakit/tests/ok/d01a.txt new file mode 100644 index 000000000..fb8a3f195 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/d01a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 123 diff --git a/akregator/src/mk4storage/metakit/tests/ok/d01b.txt b/akregator/src/mk4storage/metakit/tests/ok/d01b.txt new file mode 100644 index 000000000..1440d4ebb --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/d01b.txt @@ -0,0 +1,19 @@ + VIEW 1 rows = _C:V + 0: subview '_C' + VIEW 4 rows = _O:I _D:V + 0: 8 + 0: subview '_D' + VIEW 1 rows = _K:I _R:I _B:B + 0: 0 0 (4b) + 1: 0 + 1: subview '_D' + VIEW 1 rows = _K:I _R:I _B:B + 0: 0 0 (5b) + 2: 0 + 2: subview '_D' + VIEW 1 rows = _K:I _R:I _B:B + 0: 0 0 (13b) + 3: 0 + 3: subview '_D' + VIEW 1 rows = _K:I _R:I _B:B + 0: 0 0 (13b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/e01.txt b/akregator/src/mk4storage/metakit/tests/ok/e01.txt new file mode 100644 index 000000000..665b9367d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e01.txt @@ -0,0 +1,2 @@ +>>> Extend new file +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/e01a.txt b/akregator/src/mk4storage/metakit/tests/ok/e01a.txt new file mode 100644 index 000000000..4f2b75602 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e01a.txt @@ -0,0 +1,5 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 2 rows = p1:I + 0: 123 + 1: 456 diff --git a/akregator/src/mk4storage/metakit/tests/ok/e02.txt b/akregator/src/mk4storage/metakit/tests/ok/e02.txt new file mode 100644 index 000000000..6dd7e4e44 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e02.txt @@ -0,0 +1,2 @@ +>>> Extend committing twice +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/e02a.txt b/akregator/src/mk4storage/metakit/tests/ok/e02a.txt new file mode 100644 index 000000000..4f2b75602 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e02a.txt @@ -0,0 +1,5 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 2 rows = p1:I + 0: 123 + 1: 456 diff --git a/akregator/src/mk4storage/metakit/tests/ok/e03.txt b/akregator/src/mk4storage/metakit/tests/ok/e03.txt new file mode 100644 index 000000000..8d58f9062 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e03.txt @@ -0,0 +1,2 @@ +>>> Read during extend +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/e03a.txt b/akregator/src/mk4storage/metakit/tests/ok/e03a.txt new file mode 100644 index 000000000..4f2b75602 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e03a.txt @@ -0,0 +1,5 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 2 rows = p1:I + 0: 123 + 1: 456 diff --git a/akregator/src/mk4storage/metakit/tests/ok/e04.txt b/akregator/src/mk4storage/metakit/tests/ok/e04.txt new file mode 100644 index 000000000..c5ab0c727 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e04.txt @@ -0,0 +1,2 @@ +>>> Extend during read +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/e04a.txt b/akregator/src/mk4storage/metakit/tests/ok/e04a.txt new file mode 100644 index 000000000..2f613d763 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e04a.txt @@ -0,0 +1,5 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 2 rows = p1:I + 0: 123 + 1: 123 diff --git a/akregator/src/mk4storage/metakit/tests/ok/e05.txt b/akregator/src/mk4storage/metakit/tests/ok/e05.txt new file mode 100644 index 000000000..9314db303 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e05.txt @@ -0,0 +1,2 @@ +>>> Test memory mapping +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/e05a.txt b/akregator/src/mk4storage/metakit/tests/ok/e05a.txt new file mode 100644 index 000000000..a28a7cae2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e05a.txt @@ -0,0 +1 @@ + VIEW 1 rows = diff --git a/akregator/src/mk4storage/metakit/tests/ok/e06.txt b/akregator/src/mk4storage/metakit/tests/ok/e06.txt new file mode 100644 index 000000000..62dc3218f --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e06.txt @@ -0,0 +1,2 @@ +>>> Rollback during extend +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/e06a.txt b/akregator/src/mk4storage/metakit/tests/ok/e06a.txt new file mode 100644 index 000000000..4f2b75602 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/e06a.txt @@ -0,0 +1,5 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 2 rows = p1:I + 0: 123 + 1: 456 diff --git a/akregator/src/mk4storage/metakit/tests/ok/f01.txt b/akregator/src/mk4storage/metakit/tests/ok/f01.txt new file mode 100644 index 000000000..412c99b76 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f01.txt @@ -0,0 +1,2 @@ +>>> Add view to format +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f01a.txt b/akregator/src/mk4storage/metakit/tests/ok/f01a.txt new file mode 100644 index 000000000..acb76114d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f01a.txt @@ -0,0 +1,8 @@ + VIEW 1 rows = a:V b:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 123 + 0: subview 'b' + VIEW 2 rows = p2:I + 0: 345 + 1: 567 diff --git a/akregator/src/mk4storage/metakit/tests/ok/f02.txt b/akregator/src/mk4storage/metakit/tests/ok/f02.txt new file mode 100644 index 000000000..8ed593070 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f02.txt @@ -0,0 +1,2 @@ +>>> Remove view from format +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f02a.txt b/akregator/src/mk4storage/metakit/tests/ok/f02a.txt new file mode 100644 index 000000000..700609bef --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f02a.txt @@ -0,0 +1,5 @@ + VIEW 1 rows = b:V + 0: subview 'b' + VIEW 2 rows = p2:I + 0: 345 + 1: 567 diff --git a/akregator/src/mk4storage/metakit/tests/ok/f03.txt b/akregator/src/mk4storage/metakit/tests/ok/f03.txt new file mode 100644 index 000000000..461a5b7dc --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f03.txt @@ -0,0 +1,2 @@ +>>> Rollback format change +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f03a.txt b/akregator/src/mk4storage/metakit/tests/ok/f03a.txt new file mode 100644 index 000000000..fb8a3f195 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f03a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 123 diff --git a/akregator/src/mk4storage/metakit/tests/ok/f04.txt b/akregator/src/mk4storage/metakit/tests/ok/f04.txt new file mode 100644 index 000000000..237a42f23 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f04.txt @@ -0,0 +1,2 @@ +>>> Rearrange format +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f04a.txt b/akregator/src/mk4storage/metakit/tests/ok/f04a.txt new file mode 100644 index 000000000..08b06c501 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f04a.txt @@ -0,0 +1,8 @@ + VIEW 1 rows = b:V a:V + 0: subview 'b' + VIEW 2 rows = p2:I + 0: 345 + 1: 567 + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 123 diff --git a/akregator/src/mk4storage/metakit/tests/ok/f05.txt b/akregator/src/mk4storage/metakit/tests/ok/f05.txt new file mode 100644 index 000000000..f9fac025e --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f05.txt @@ -0,0 +1,2 @@ +>>> Nested reformat +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f05a.txt b/akregator/src/mk4storage/metakit/tests/ok/f05a.txt new file mode 100644 index 000000000..2f3891813 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f05a.txt @@ -0,0 +1,8 @@ + VIEW 1 rows = a:V b:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 123 + 0: subview 'b' + VIEW 2 rows = p1:I p2:I + 0: 543 345 + 1: 765 567 diff --git a/akregator/src/mk4storage/metakit/tests/ok/f06.txt b/akregator/src/mk4storage/metakit/tests/ok/f06.txt new file mode 100644 index 000000000..039caebe6 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f06.txt @@ -0,0 +1,2 @@ +>>> Flip foreign data +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f07.txt b/akregator/src/mk4storage/metakit/tests/ok/f07.txt new file mode 100644 index 000000000..bf52b37b8 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f07.txt @@ -0,0 +1,2 @@ +>>> Automatic structure info (obsolete) +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f07a.txt b/akregator/src/mk4storage/metakit/tests/ok/f07a.txt new file mode 100644 index 000000000..79963d7b4 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f07a.txt @@ -0,0 +1,3 @@ + VIEW 1 rows = dict:V + 0: subview 'dict' + VIEW 0 rows = parent:I index:I view:V diff --git a/akregator/src/mk4storage/metakit/tests/ok/f08.txt b/akregator/src/mk4storage/metakit/tests/ok/f08.txt new file mode 100644 index 000000000..d4e88f89a --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f08.txt @@ -0,0 +1,2 @@ +>>> Automatic storage format +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f08a.txt b/akregator/src/mk4storage/metakit/tests/ok/f08a.txt new file mode 100644 index 000000000..7a56c7fcf --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f08a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = dict:V + 0: subview 'dict' + VIEW 3 rows = p1:S p2:S + 0: 'One' 'Two' + 1: '' '' + 2: 'One' 'Two' diff --git a/akregator/src/mk4storage/metakit/tests/ok/f09.txt b/akregator/src/mk4storage/metakit/tests/ok/f09.txt new file mode 100644 index 000000000..d1040e7ff --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f09.txt @@ -0,0 +1,2 @@ +>>> Partial restructuring +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f09a.txt b/akregator/src/mk4storage/metakit/tests/ok/f09a.txt new file mode 100644 index 000000000..c26f86755 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f09a.txt @@ -0,0 +1,13 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 10 rows = p1:I p2:I p3:I + 0: 1000 2000 3000 + 1: 1001 0 0 + 2: 1002 2002 0 + 3: 1003 0 3003 + 4: 1004 2004 0 + 5: 1005 0 0 + 6: 1006 2006 3006 + 7: 1007 0 0 + 8: 1008 2008 0 + 9: 1009 0 3009 diff --git a/akregator/src/mk4storage/metakit/tests/ok/f10.txt b/akregator/src/mk4storage/metakit/tests/ok/f10.txt new file mode 100644 index 000000000..848dbdddc --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f10.txt @@ -0,0 +1,2 @@ +>>> Committed restructuring +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f10a.txt b/akregator/src/mk4storage/metakit/tests/ok/f10a.txt new file mode 100644 index 000000000..c26f86755 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f10a.txt @@ -0,0 +1,13 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 10 rows = p1:I p2:I p3:I + 0: 1000 2000 3000 + 1: 1001 0 0 + 2: 1002 2002 0 + 3: 1003 0 3003 + 4: 1004 2004 0 + 5: 1005 0 0 + 6: 1006 2006 3006 + 7: 1007 0 0 + 8: 1008 2008 0 + 9: 1009 0 3009 diff --git a/akregator/src/mk4storage/metakit/tests/ok/f11.txt b/akregator/src/mk4storage/metakit/tests/ok/f11.txt new file mode 100644 index 000000000..583ec58ae --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f11.txt @@ -0,0 +1,2 @@ +>>> Delete missing view +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/f11a.txt b/akregator/src/mk4storage/metakit/tests/ok/f11a.txt new file mode 100644 index 000000000..a28a7cae2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/f11a.txt @@ -0,0 +1 @@ + VIEW 1 rows = diff --git a/akregator/src/mk4storage/metakit/tests/ok/l00.txt b/akregator/src/mk4storage/metakit/tests/ok/l00.txt new file mode 100644 index 000000000..b94a9a630 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l00.txt @@ -0,0 +1,2 @@ +>>> Lots of properties +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/l00a.txt b/akregator/src/mk4storage/metakit/tests/ok/l00a.txt new file mode 100644 index 000000000..bb0f5c661 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l00a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I p2:I p3:I p4:I p5:I p6:I p7:I p8:I p9:I p10:I p11:I p12:I p13:I p14:I p15:I p16:I p17:I p18:I p19:I p20:I p21:I p22:I p23:I p24:I p25:I p26:I p27:I p28:I p29:I p30:I p31:I p32:I p33:I p34:I p35:I p36:I p37:I p38:I p39:I p40:I p41:I p42:I p43:I p44:I p45:I p46:I p47:I p48:I p49:I p50:I p51:I p52:I p53:I p54:I p55:I p56:I p57:I p58:I p59:I p60:I p61:I p62:I p63:I p64:I p65:I p66:I p67:I p68:I p69:I p70:I p71:I p72:I p73:I p74:I p75:I p76:I p77:I p78:I p79:I p80:I p81:I p82:I p83:I p84:I p85:I p86:I p87:I p88:I p89:I p90:I p91:I p92:I p93:I p94:I p95:I p96:I p97:I p98:I p99:I p100:I p101:I p102:I p103:I p104:I p105:I p106:I p107:I p108:I p109:I p110:I p111:I p112:I p113:I p114:I p115:I p116:I p117:I p118:I p119:I p120:I p121:I p122:I p123:I p124:I p125:I p126:I p127:I p128:I p129:I p130:I p131:I p132:I p133:I p134:I p135:I p136:I p137:I p138:I p139:I p140:I p141:I p142:I p143:I p144:I p145:I p146:I p147:I p148:I p149:I + 0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 123 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/akregator/src/mk4storage/metakit/tests/ok/l01.txt b/akregator/src/mk4storage/metakit/tests/ok/l01.txt new file mode 100644 index 000000000..842c943de --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l01.txt @@ -0,0 +1,2 @@ +>>> Over 32 Kb of integers +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/l01a.txt b/akregator/src/mk4storage/metakit/tests/ok/l01a.txt new file mode 100644 index 000000000..69cad2436 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l01a.txt @@ -0,0 +1,9003 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 9000 rows = p1:I + 0: 1000000 + 1: 1000001 + 2: 1000002 + 3: 1000003 + 4: 1000004 + 5: 1000005 + 6: 1000006 + 7: 1000007 + 8: 1000008 + 9: 1000009 + 10: 1000010 + 11: 1000011 + 12: 1000012 + 13: 1000013 + 14: 1000014 + 15: 1000015 + 16: 1000016 + 17: 1000017 + 18: 1000018 + 19: 1000019 + 20: 1000020 + 21: 1000021 + 22: 1000022 + 23: 1000023 + 24: 1000024 + 25: 1000025 + 26: 1000026 + 27: 1000027 + 28: 1000028 + 29: 1000029 + 30: 1000030 + 31: 1000031 + 32: 1000032 + 33: 1000033 + 34: 1000034 + 35: 1000035 + 36: 1000036 + 37: 1000037 + 38: 1000038 + 39: 1000039 + 40: 1000040 + 41: 1000041 + 42: 1000042 + 43: 1000043 + 44: 1000044 + 45: 1000045 + 46: 1000046 + 47: 1000047 + 48: 1000048 + 49: 1000049 + 50: 1000050 + 51: 1000051 + 52: 1000052 + 53: 1000053 + 54: 1000054 + 55: 1000055 + 56: 1000056 + 57: 1000057 + 58: 1000058 + 59: 1000059 + 60: 1000060 + 61: 1000061 + 62: 1000062 + 63: 1000063 + 64: 1000064 + 65: 1000065 + 66: 1000066 + 67: 1000067 + 68: 1000068 + 69: 1000069 + 70: 1000070 + 71: 1000071 + 72: 1000072 + 73: 1000073 + 74: 1000074 + 75: 1000075 + 76: 1000076 + 77: 1000077 + 78: 1000078 + 79: 1000079 + 80: 1000080 + 81: 1000081 + 82: 1000082 + 83: 1000083 + 84: 1000084 + 85: 1000085 + 86: 1000086 + 87: 1000087 + 88: 1000088 + 89: 1000089 + 90: 1000090 + 91: 1000091 + 92: 1000092 + 93: 1000093 + 94: 1000094 + 95: 1000095 + 96: 1000096 + 97: 1000097 + 98: 1000098 + 99: 1000099 + 100: 1000100 + 101: 1000101 + 102: 1000102 + 103: 1000103 + 104: 1000104 + 105: 1000105 + 106: 1000106 + 107: 1000107 + 108: 1000108 + 109: 1000109 + 110: 1000110 + 111: 1000111 + 112: 1000112 + 113: 1000113 + 114: 1000114 + 115: 1000115 + 116: 1000116 + 117: 1000117 + 118: 1000118 + 119: 1000119 + 120: 1000120 + 121: 1000121 + 122: 1000122 + 123: 1000123 + 124: 1000124 + 125: 1000125 + 126: 1000126 + 127: 1000127 + 128: 1000128 + 129: 1000129 + 130: 1000130 + 131: 1000131 + 132: 1000132 + 133: 1000133 + 134: 1000134 + 135: 1000135 + 136: 1000136 + 137: 1000137 + 138: 1000138 + 139: 1000139 + 140: 1000140 + 141: 1000141 + 142: 1000142 + 143: 1000143 + 144: 1000144 + 145: 1000145 + 146: 1000146 + 147: 1000147 + 148: 1000148 + 149: 1000149 + 150: 1000150 + 151: 1000151 + 152: 1000152 + 153: 1000153 + 154: 1000154 + 155: 1000155 + 156: 1000156 + 157: 1000157 + 158: 1000158 + 159: 1000159 + 160: 1000160 + 161: 1000161 + 162: 1000162 + 163: 1000163 + 164: 1000164 + 165: 1000165 + 166: 1000166 + 167: 1000167 + 168: 1000168 + 169: 1000169 + 170: 1000170 + 171: 1000171 + 172: 1000172 + 173: 1000173 + 174: 1000174 + 175: 1000175 + 176: 1000176 + 177: 1000177 + 178: 1000178 + 179: 1000179 + 180: 1000180 + 181: 1000181 + 182: 1000182 + 183: 1000183 + 184: 1000184 + 185: 1000185 + 186: 1000186 + 187: 1000187 + 188: 1000188 + 189: 1000189 + 190: 1000190 + 191: 1000191 + 192: 1000192 + 193: 1000193 + 194: 1000194 + 195: 1000195 + 196: 1000196 + 197: 1000197 + 198: 1000198 + 199: 1000199 + 200: 1000200 + 201: 1000201 + 202: 1000202 + 203: 1000203 + 204: 1000204 + 205: 1000205 + 206: 1000206 + 207: 1000207 + 208: 1000208 + 209: 1000209 + 210: 1000210 + 211: 1000211 + 212: 1000212 + 213: 1000213 + 214: 1000214 + 215: 1000215 + 216: 1000216 + 217: 1000217 + 218: 1000218 + 219: 1000219 + 220: 1000220 + 221: 1000221 + 222: 1000222 + 223: 1000223 + 224: 1000224 + 225: 1000225 + 226: 1000226 + 227: 1000227 + 228: 1000228 + 229: 1000229 + 230: 1000230 + 231: 1000231 + 232: 1000232 + 233: 1000233 + 234: 1000234 + 235: 1000235 + 236: 1000236 + 237: 1000237 + 238: 1000238 + 239: 1000239 + 240: 1000240 + 241: 1000241 + 242: 1000242 + 243: 1000243 + 244: 1000244 + 245: 1000245 + 246: 1000246 + 247: 1000247 + 248: 1000248 + 249: 1000249 + 250: 1000250 + 251: 1000251 + 252: 1000252 + 253: 1000253 + 254: 1000254 + 255: 1000255 + 256: 1000256 + 257: 1000257 + 258: 1000258 + 259: 1000259 + 260: 1000260 + 261: 1000261 + 262: 1000262 + 263: 1000263 + 264: 1000264 + 265: 1000265 + 266: 1000266 + 267: 1000267 + 268: 1000268 + 269: 1000269 + 270: 1000270 + 271: 1000271 + 272: 1000272 + 273: 1000273 + 274: 1000274 + 275: 1000275 + 276: 1000276 + 277: 1000277 + 278: 1000278 + 279: 1000279 + 280: 1000280 + 281: 1000281 + 282: 1000282 + 283: 1000283 + 284: 1000284 + 285: 1000285 + 286: 1000286 + 287: 1000287 + 288: 1000288 + 289: 1000289 + 290: 1000290 + 291: 1000291 + 292: 1000292 + 293: 1000293 + 294: 1000294 + 295: 1000295 + 296: 1000296 + 297: 1000297 + 298: 1000298 + 299: 1000299 + 300: 1000300 + 301: 1000301 + 302: 1000302 + 303: 1000303 + 304: 1000304 + 305: 1000305 + 306: 1000306 + 307: 1000307 + 308: 1000308 + 309: 1000309 + 310: 1000310 + 311: 1000311 + 312: 1000312 + 313: 1000313 + 314: 1000314 + 315: 1000315 + 316: 1000316 + 317: 1000317 + 318: 1000318 + 319: 1000319 + 320: 1000320 + 321: 1000321 + 322: 1000322 + 323: 1000323 + 324: 1000324 + 325: 1000325 + 326: 1000326 + 327: 1000327 + 328: 1000328 + 329: 1000329 + 330: 1000330 + 331: 1000331 + 332: 1000332 + 333: 1000333 + 334: 1000334 + 335: 1000335 + 336: 1000336 + 337: 1000337 + 338: 1000338 + 339: 1000339 + 340: 1000340 + 341: 1000341 + 342: 1000342 + 343: 1000343 + 344: 1000344 + 345: 1000345 + 346: 1000346 + 347: 1000347 + 348: 1000348 + 349: 1000349 + 350: 1000350 + 351: 1000351 + 352: 1000352 + 353: 1000353 + 354: 1000354 + 355: 1000355 + 356: 1000356 + 357: 1000357 + 358: 1000358 + 359: 1000359 + 360: 1000360 + 361: 1000361 + 362: 1000362 + 363: 1000363 + 364: 1000364 + 365: 1000365 + 366: 1000366 + 367: 1000367 + 368: 1000368 + 369: 1000369 + 370: 1000370 + 371: 1000371 + 372: 1000372 + 373: 1000373 + 374: 1000374 + 375: 1000375 + 376: 1000376 + 377: 1000377 + 378: 1000378 + 379: 1000379 + 380: 1000380 + 381: 1000381 + 382: 1000382 + 383: 1000383 + 384: 1000384 + 385: 1000385 + 386: 1000386 + 387: 1000387 + 388: 1000388 + 389: 1000389 + 390: 1000390 + 391: 1000391 + 392: 1000392 + 393: 1000393 + 394: 1000394 + 395: 1000395 + 396: 1000396 + 397: 1000397 + 398: 1000398 + 399: 1000399 + 400: 1000400 + 401: 1000401 + 402: 1000402 + 403: 1000403 + 404: 1000404 + 405: 1000405 + 406: 1000406 + 407: 1000407 + 408: 1000408 + 409: 1000409 + 410: 1000410 + 411: 1000411 + 412: 1000412 + 413: 1000413 + 414: 1000414 + 415: 1000415 + 416: 1000416 + 417: 1000417 + 418: 1000418 + 419: 1000419 + 420: 1000420 + 421: 1000421 + 422: 1000422 + 423: 1000423 + 424: 1000424 + 425: 1000425 + 426: 1000426 + 427: 1000427 + 428: 1000428 + 429: 1000429 + 430: 1000430 + 431: 1000431 + 432: 1000432 + 433: 1000433 + 434: 1000434 + 435: 1000435 + 436: 1000436 + 437: 1000437 + 438: 1000438 + 439: 1000439 + 440: 1000440 + 441: 1000441 + 442: 1000442 + 443: 1000443 + 444: 1000444 + 445: 1000445 + 446: 1000446 + 447: 1000447 + 448: 1000448 + 449: 1000449 + 450: 1000450 + 451: 1000451 + 452: 1000452 + 453: 1000453 + 454: 1000454 + 455: 1000455 + 456: 1000456 + 457: 1000457 + 458: 1000458 + 459: 1000459 + 460: 1000460 + 461: 1000461 + 462: 1000462 + 463: 1000463 + 464: 1000464 + 465: 1000465 + 466: 1000466 + 467: 1000467 + 468: 1000468 + 469: 1000469 + 470: 1000470 + 471: 1000471 + 472: 1000472 + 473: 1000473 + 474: 1000474 + 475: 1000475 + 476: 1000476 + 477: 1000477 + 478: 1000478 + 479: 1000479 + 480: 1000480 + 481: 1000481 + 482: 1000482 + 483: 1000483 + 484: 1000484 + 485: 1000485 + 486: 1000486 + 487: 1000487 + 488: 1000488 + 489: 1000489 + 490: 1000490 + 491: 1000491 + 492: 1000492 + 493: 1000493 + 494: 1000494 + 495: 1000495 + 496: 1000496 + 497: 1000497 + 498: 1000498 + 499: 1000499 + 500: 1000500 + 501: 1000501 + 502: 1000502 + 503: 1000503 + 504: 1000504 + 505: 1000505 + 506: 1000506 + 507: 1000507 + 508: 1000508 + 509: 1000509 + 510: 1000510 + 511: 1000511 + 512: 1000512 + 513: 1000513 + 514: 1000514 + 515: 1000515 + 516: 1000516 + 517: 1000517 + 518: 1000518 + 519: 1000519 + 520: 1000520 + 521: 1000521 + 522: 1000522 + 523: 1000523 + 524: 1000524 + 525: 1000525 + 526: 1000526 + 527: 1000527 + 528: 1000528 + 529: 1000529 + 530: 1000530 + 531: 1000531 + 532: 1000532 + 533: 1000533 + 534: 1000534 + 535: 1000535 + 536: 1000536 + 537: 1000537 + 538: 1000538 + 539: 1000539 + 540: 1000540 + 541: 1000541 + 542: 1000542 + 543: 1000543 + 544: 1000544 + 545: 1000545 + 546: 1000546 + 547: 1000547 + 548: 1000548 + 549: 1000549 + 550: 1000550 + 551: 1000551 + 552: 1000552 + 553: 1000553 + 554: 1000554 + 555: 1000555 + 556: 1000556 + 557: 1000557 + 558: 1000558 + 559: 1000559 + 560: 1000560 + 561: 1000561 + 562: 1000562 + 563: 1000563 + 564: 1000564 + 565: 1000565 + 566: 1000566 + 567: 1000567 + 568: 1000568 + 569: 1000569 + 570: 1000570 + 571: 1000571 + 572: 1000572 + 573: 1000573 + 574: 1000574 + 575: 1000575 + 576: 1000576 + 577: 1000577 + 578: 1000578 + 579: 1000579 + 580: 1000580 + 581: 1000581 + 582: 1000582 + 583: 1000583 + 584: 1000584 + 585: 1000585 + 586: 1000586 + 587: 1000587 + 588: 1000588 + 589: 1000589 + 590: 1000590 + 591: 1000591 + 592: 1000592 + 593: 1000593 + 594: 1000594 + 595: 1000595 + 596: 1000596 + 597: 1000597 + 598: 1000598 + 599: 1000599 + 600: 1000600 + 601: 1000601 + 602: 1000602 + 603: 1000603 + 604: 1000604 + 605: 1000605 + 606: 1000606 + 607: 1000607 + 608: 1000608 + 609: 1000609 + 610: 1000610 + 611: 1000611 + 612: 1000612 + 613: 1000613 + 614: 1000614 + 615: 1000615 + 616: 1000616 + 617: 1000617 + 618: 1000618 + 619: 1000619 + 620: 1000620 + 621: 1000621 + 622: 1000622 + 623: 1000623 + 624: 1000624 + 625: 1000625 + 626: 1000626 + 627: 1000627 + 628: 1000628 + 629: 1000629 + 630: 1000630 + 631: 1000631 + 632: 1000632 + 633: 1000633 + 634: 1000634 + 635: 1000635 + 636: 1000636 + 637: 1000637 + 638: 1000638 + 639: 1000639 + 640: 1000640 + 641: 1000641 + 642: 1000642 + 643: 1000643 + 644: 1000644 + 645: 1000645 + 646: 1000646 + 647: 1000647 + 648: 1000648 + 649: 1000649 + 650: 1000650 + 651: 1000651 + 652: 1000652 + 653: 1000653 + 654: 1000654 + 655: 1000655 + 656: 1000656 + 657: 1000657 + 658: 1000658 + 659: 1000659 + 660: 1000660 + 661: 1000661 + 662: 1000662 + 663: 1000663 + 664: 1000664 + 665: 1000665 + 666: 1000666 + 667: 1000667 + 668: 1000668 + 669: 1000669 + 670: 1000670 + 671: 1000671 + 672: 1000672 + 673: 1000673 + 674: 1000674 + 675: 1000675 + 676: 1000676 + 677: 1000677 + 678: 1000678 + 679: 1000679 + 680: 1000680 + 681: 1000681 + 682: 1000682 + 683: 1000683 + 684: 1000684 + 685: 1000685 + 686: 1000686 + 687: 1000687 + 688: 1000688 + 689: 1000689 + 690: 1000690 + 691: 1000691 + 692: 1000692 + 693: 1000693 + 694: 1000694 + 695: 1000695 + 696: 1000696 + 697: 1000697 + 698: 1000698 + 699: 1000699 + 700: 1000700 + 701: 1000701 + 702: 1000702 + 703: 1000703 + 704: 1000704 + 705: 1000705 + 706: 1000706 + 707: 1000707 + 708: 1000708 + 709: 1000709 + 710: 1000710 + 711: 1000711 + 712: 1000712 + 713: 1000713 + 714: 1000714 + 715: 1000715 + 716: 1000716 + 717: 1000717 + 718: 1000718 + 719: 1000719 + 720: 1000720 + 721: 1000721 + 722: 1000722 + 723: 1000723 + 724: 1000724 + 725: 1000725 + 726: 1000726 + 727: 1000727 + 728: 1000728 + 729: 1000729 + 730: 1000730 + 731: 1000731 + 732: 1000732 + 733: 1000733 + 734: 1000734 + 735: 1000735 + 736: 1000736 + 737: 1000737 + 738: 1000738 + 739: 1000739 + 740: 1000740 + 741: 1000741 + 742: 1000742 + 743: 1000743 + 744: 1000744 + 745: 1000745 + 746: 1000746 + 747: 1000747 + 748: 1000748 + 749: 1000749 + 750: 1000750 + 751: 1000751 + 752: 1000752 + 753: 1000753 + 754: 1000754 + 755: 1000755 + 756: 1000756 + 757: 1000757 + 758: 1000758 + 759: 1000759 + 760: 1000760 + 761: 1000761 + 762: 1000762 + 763: 1000763 + 764: 1000764 + 765: 1000765 + 766: 1000766 + 767: 1000767 + 768: 1000768 + 769: 1000769 + 770: 1000770 + 771: 1000771 + 772: 1000772 + 773: 1000773 + 774: 1000774 + 775: 1000775 + 776: 1000776 + 777: 1000777 + 778: 1000778 + 779: 1000779 + 780: 1000780 + 781: 1000781 + 782: 1000782 + 783: 1000783 + 784: 1000784 + 785: 1000785 + 786: 1000786 + 787: 1000787 + 788: 1000788 + 789: 1000789 + 790: 1000790 + 791: 1000791 + 792: 1000792 + 793: 1000793 + 794: 1000794 + 795: 1000795 + 796: 1000796 + 797: 1000797 + 798: 1000798 + 799: 1000799 + 800: 1000800 + 801: 1000801 + 802: 1000802 + 803: 1000803 + 804: 1000804 + 805: 1000805 + 806: 1000806 + 807: 1000807 + 808: 1000808 + 809: 1000809 + 810: 1000810 + 811: 1000811 + 812: 1000812 + 813: 1000813 + 814: 1000814 + 815: 1000815 + 816: 1000816 + 817: 1000817 + 818: 1000818 + 819: 1000819 + 820: 1000820 + 821: 1000821 + 822: 1000822 + 823: 1000823 + 824: 1000824 + 825: 1000825 + 826: 1000826 + 827: 1000827 + 828: 1000828 + 829: 1000829 + 830: 1000830 + 831: 1000831 + 832: 1000832 + 833: 1000833 + 834: 1000834 + 835: 1000835 + 836: 1000836 + 837: 1000837 + 838: 1000838 + 839: 1000839 + 840: 1000840 + 841: 1000841 + 842: 1000842 + 843: 1000843 + 844: 1000844 + 845: 1000845 + 846: 1000846 + 847: 1000847 + 848: 1000848 + 849: 1000849 + 850: 1000850 + 851: 1000851 + 852: 1000852 + 853: 1000853 + 854: 1000854 + 855: 1000855 + 856: 1000856 + 857: 1000857 + 858: 1000858 + 859: 1000859 + 860: 1000860 + 861: 1000861 + 862: 1000862 + 863: 1000863 + 864: 1000864 + 865: 1000865 + 866: 1000866 + 867: 1000867 + 868: 1000868 + 869: 1000869 + 870: 1000870 + 871: 1000871 + 872: 1000872 + 873: 1000873 + 874: 1000874 + 875: 1000875 + 876: 1000876 + 877: 1000877 + 878: 1000878 + 879: 1000879 + 880: 1000880 + 881: 1000881 + 882: 1000882 + 883: 1000883 + 884: 1000884 + 885: 1000885 + 886: 1000886 + 887: 1000887 + 888: 1000888 + 889: 1000889 + 890: 1000890 + 891: 1000891 + 892: 1000892 + 893: 1000893 + 894: 1000894 + 895: 1000895 + 896: 1000896 + 897: 1000897 + 898: 1000898 + 899: 1000899 + 900: 1000900 + 901: 1000901 + 902: 1000902 + 903: 1000903 + 904: 1000904 + 905: 1000905 + 906: 1000906 + 907: 1000907 + 908: 1000908 + 909: 1000909 + 910: 1000910 + 911: 1000911 + 912: 1000912 + 913: 1000913 + 914: 1000914 + 915: 1000915 + 916: 1000916 + 917: 1000917 + 918: 1000918 + 919: 1000919 + 920: 1000920 + 921: 1000921 + 922: 1000922 + 923: 1000923 + 924: 1000924 + 925: 1000925 + 926: 1000926 + 927: 1000927 + 928: 1000928 + 929: 1000929 + 930: 1000930 + 931: 1000931 + 932: 1000932 + 933: 1000933 + 934: 1000934 + 935: 1000935 + 936: 1000936 + 937: 1000937 + 938: 1000938 + 939: 1000939 + 940: 1000940 + 941: 1000941 + 942: 1000942 + 943: 1000943 + 944: 1000944 + 945: 1000945 + 946: 1000946 + 947: 1000947 + 948: 1000948 + 949: 1000949 + 950: 1000950 + 951: 1000951 + 952: 1000952 + 953: 1000953 + 954: 1000954 + 955: 1000955 + 956: 1000956 + 957: 1000957 + 958: 1000958 + 959: 1000959 + 960: 1000960 + 961: 1000961 + 962: 1000962 + 963: 1000963 + 964: 1000964 + 965: 1000965 + 966: 1000966 + 967: 1000967 + 968: 1000968 + 969: 1000969 + 970: 1000970 + 971: 1000971 + 972: 1000972 + 973: 1000973 + 974: 1000974 + 975: 1000975 + 976: 1000976 + 977: 1000977 + 978: 1000978 + 979: 1000979 + 980: 1000980 + 981: 1000981 + 982: 1000982 + 983: 1000983 + 984: 1000984 + 985: 1000985 + 986: 1000986 + 987: 1000987 + 988: 1000988 + 989: 1000989 + 990: 1000990 + 991: 1000991 + 992: 1000992 + 993: 1000993 + 994: 1000994 + 995: 1000995 + 996: 1000996 + 997: 1000997 + 998: 1000998 + 999: 1000999 + 1000: 1001000 + 1001: 1001001 + 1002: 1001002 + 1003: 1001003 + 1004: 1001004 + 1005: 1001005 + 1006: 1001006 + 1007: 1001007 + 1008: 1001008 + 1009: 1001009 + 1010: 1001010 + 1011: 1001011 + 1012: 1001012 + 1013: 1001013 + 1014: 1001014 + 1015: 1001015 + 1016: 1001016 + 1017: 1001017 + 1018: 1001018 + 1019: 1001019 + 1020: 1001020 + 1021: 1001021 + 1022: 1001022 + 1023: 1001023 + 1024: 1001024 + 1025: 1001025 + 1026: 1001026 + 1027: 1001027 + 1028: 1001028 + 1029: 1001029 + 1030: 1001030 + 1031: 1001031 + 1032: 1001032 + 1033: 1001033 + 1034: 1001034 + 1035: 1001035 + 1036: 1001036 + 1037: 1001037 + 1038: 1001038 + 1039: 1001039 + 1040: 1001040 + 1041: 1001041 + 1042: 1001042 + 1043: 1001043 + 1044: 1001044 + 1045: 1001045 + 1046: 1001046 + 1047: 1001047 + 1048: 1001048 + 1049: 1001049 + 1050: 1001050 + 1051: 1001051 + 1052: 1001052 + 1053: 1001053 + 1054: 1001054 + 1055: 1001055 + 1056: 1001056 + 1057: 1001057 + 1058: 1001058 + 1059: 1001059 + 1060: 1001060 + 1061: 1001061 + 1062: 1001062 + 1063: 1001063 + 1064: 1001064 + 1065: 1001065 + 1066: 1001066 + 1067: 1001067 + 1068: 1001068 + 1069: 1001069 + 1070: 1001070 + 1071: 1001071 + 1072: 1001072 + 1073: 1001073 + 1074: 1001074 + 1075: 1001075 + 1076: 1001076 + 1077: 1001077 + 1078: 1001078 + 1079: 1001079 + 1080: 1001080 + 1081: 1001081 + 1082: 1001082 + 1083: 1001083 + 1084: 1001084 + 1085: 1001085 + 1086: 1001086 + 1087: 1001087 + 1088: 1001088 + 1089: 1001089 + 1090: 1001090 + 1091: 1001091 + 1092: 1001092 + 1093: 1001093 + 1094: 1001094 + 1095: 1001095 + 1096: 1001096 + 1097: 1001097 + 1098: 1001098 + 1099: 1001099 + 1100: 1001100 + 1101: 1001101 + 1102: 1001102 + 1103: 1001103 + 1104: 1001104 + 1105: 1001105 + 1106: 1001106 + 1107: 1001107 + 1108: 1001108 + 1109: 1001109 + 1110: 1001110 + 1111: 1001111 + 1112: 1001112 + 1113: 1001113 + 1114: 1001114 + 1115: 1001115 + 1116: 1001116 + 1117: 1001117 + 1118: 1001118 + 1119: 1001119 + 1120: 1001120 + 1121: 1001121 + 1122: 1001122 + 1123: 1001123 + 1124: 1001124 + 1125: 1001125 + 1126: 1001126 + 1127: 1001127 + 1128: 1001128 + 1129: 1001129 + 1130: 1001130 + 1131: 1001131 + 1132: 1001132 + 1133: 1001133 + 1134: 1001134 + 1135: 1001135 + 1136: 1001136 + 1137: 1001137 + 1138: 1001138 + 1139: 1001139 + 1140: 1001140 + 1141: 1001141 + 1142: 1001142 + 1143: 1001143 + 1144: 1001144 + 1145: 1001145 + 1146: 1001146 + 1147: 1001147 + 1148: 1001148 + 1149: 1001149 + 1150: 1001150 + 1151: 1001151 + 1152: 1001152 + 1153: 1001153 + 1154: 1001154 + 1155: 1001155 + 1156: 1001156 + 1157: 1001157 + 1158: 1001158 + 1159: 1001159 + 1160: 1001160 + 1161: 1001161 + 1162: 1001162 + 1163: 1001163 + 1164: 1001164 + 1165: 1001165 + 1166: 1001166 + 1167: 1001167 + 1168: 1001168 + 1169: 1001169 + 1170: 1001170 + 1171: 1001171 + 1172: 1001172 + 1173: 1001173 + 1174: 1001174 + 1175: 1001175 + 1176: 1001176 + 1177: 1001177 + 1178: 1001178 + 1179: 1001179 + 1180: 1001180 + 1181: 1001181 + 1182: 1001182 + 1183: 1001183 + 1184: 1001184 + 1185: 1001185 + 1186: 1001186 + 1187: 1001187 + 1188: 1001188 + 1189: 1001189 + 1190: 1001190 + 1191: 1001191 + 1192: 1001192 + 1193: 1001193 + 1194: 1001194 + 1195: 1001195 + 1196: 1001196 + 1197: 1001197 + 1198: 1001198 + 1199: 1001199 + 1200: 1001200 + 1201: 1001201 + 1202: 1001202 + 1203: 1001203 + 1204: 1001204 + 1205: 1001205 + 1206: 1001206 + 1207: 1001207 + 1208: 1001208 + 1209: 1001209 + 1210: 1001210 + 1211: 1001211 + 1212: 1001212 + 1213: 1001213 + 1214: 1001214 + 1215: 1001215 + 1216: 1001216 + 1217: 1001217 + 1218: 1001218 + 1219: 1001219 + 1220: 1001220 + 1221: 1001221 + 1222: 1001222 + 1223: 1001223 + 1224: 1001224 + 1225: 1001225 + 1226: 1001226 + 1227: 1001227 + 1228: 1001228 + 1229: 1001229 + 1230: 1001230 + 1231: 1001231 + 1232: 1001232 + 1233: 1001233 + 1234: 1001234 + 1235: 1001235 + 1236: 1001236 + 1237: 1001237 + 1238: 1001238 + 1239: 1001239 + 1240: 1001240 + 1241: 1001241 + 1242: 1001242 + 1243: 1001243 + 1244: 1001244 + 1245: 1001245 + 1246: 1001246 + 1247: 1001247 + 1248: 1001248 + 1249: 1001249 + 1250: 1001250 + 1251: 1001251 + 1252: 1001252 + 1253: 1001253 + 1254: 1001254 + 1255: 1001255 + 1256: 1001256 + 1257: 1001257 + 1258: 1001258 + 1259: 1001259 + 1260: 1001260 + 1261: 1001261 + 1262: 1001262 + 1263: 1001263 + 1264: 1001264 + 1265: 1001265 + 1266: 1001266 + 1267: 1001267 + 1268: 1001268 + 1269: 1001269 + 1270: 1001270 + 1271: 1001271 + 1272: 1001272 + 1273: 1001273 + 1274: 1001274 + 1275: 1001275 + 1276: 1001276 + 1277: 1001277 + 1278: 1001278 + 1279: 1001279 + 1280: 1001280 + 1281: 1001281 + 1282: 1001282 + 1283: 1001283 + 1284: 1001284 + 1285: 1001285 + 1286: 1001286 + 1287: 1001287 + 1288: 1001288 + 1289: 1001289 + 1290: 1001290 + 1291: 1001291 + 1292: 1001292 + 1293: 1001293 + 1294: 1001294 + 1295: 1001295 + 1296: 1001296 + 1297: 1001297 + 1298: 1001298 + 1299: 1001299 + 1300: 1001300 + 1301: 1001301 + 1302: 1001302 + 1303: 1001303 + 1304: 1001304 + 1305: 1001305 + 1306: 1001306 + 1307: 1001307 + 1308: 1001308 + 1309: 1001309 + 1310: 1001310 + 1311: 1001311 + 1312: 1001312 + 1313: 1001313 + 1314: 1001314 + 1315: 1001315 + 1316: 1001316 + 1317: 1001317 + 1318: 1001318 + 1319: 1001319 + 1320: 1001320 + 1321: 1001321 + 1322: 1001322 + 1323: 1001323 + 1324: 1001324 + 1325: 1001325 + 1326: 1001326 + 1327: 1001327 + 1328: 1001328 + 1329: 1001329 + 1330: 1001330 + 1331: 1001331 + 1332: 1001332 + 1333: 1001333 + 1334: 1001334 + 1335: 1001335 + 1336: 1001336 + 1337: 1001337 + 1338: 1001338 + 1339: 1001339 + 1340: 1001340 + 1341: 1001341 + 1342: 1001342 + 1343: 1001343 + 1344: 1001344 + 1345: 1001345 + 1346: 1001346 + 1347: 1001347 + 1348: 1001348 + 1349: 1001349 + 1350: 1001350 + 1351: 1001351 + 1352: 1001352 + 1353: 1001353 + 1354: 1001354 + 1355: 1001355 + 1356: 1001356 + 1357: 1001357 + 1358: 1001358 + 1359: 1001359 + 1360: 1001360 + 1361: 1001361 + 1362: 1001362 + 1363: 1001363 + 1364: 1001364 + 1365: 1001365 + 1366: 1001366 + 1367: 1001367 + 1368: 1001368 + 1369: 1001369 + 1370: 1001370 + 1371: 1001371 + 1372: 1001372 + 1373: 1001373 + 1374: 1001374 + 1375: 1001375 + 1376: 1001376 + 1377: 1001377 + 1378: 1001378 + 1379: 1001379 + 1380: 1001380 + 1381: 1001381 + 1382: 1001382 + 1383: 1001383 + 1384: 1001384 + 1385: 1001385 + 1386: 1001386 + 1387: 1001387 + 1388: 1001388 + 1389: 1001389 + 1390: 1001390 + 1391: 1001391 + 1392: 1001392 + 1393: 1001393 + 1394: 1001394 + 1395: 1001395 + 1396: 1001396 + 1397: 1001397 + 1398: 1001398 + 1399: 1001399 + 1400: 1001400 + 1401: 1001401 + 1402: 1001402 + 1403: 1001403 + 1404: 1001404 + 1405: 1001405 + 1406: 1001406 + 1407: 1001407 + 1408: 1001408 + 1409: 1001409 + 1410: 1001410 + 1411: 1001411 + 1412: 1001412 + 1413: 1001413 + 1414: 1001414 + 1415: 1001415 + 1416: 1001416 + 1417: 1001417 + 1418: 1001418 + 1419: 1001419 + 1420: 1001420 + 1421: 1001421 + 1422: 1001422 + 1423: 1001423 + 1424: 1001424 + 1425: 1001425 + 1426: 1001426 + 1427: 1001427 + 1428: 1001428 + 1429: 1001429 + 1430: 1001430 + 1431: 1001431 + 1432: 1001432 + 1433: 1001433 + 1434: 1001434 + 1435: 1001435 + 1436: 1001436 + 1437: 1001437 + 1438: 1001438 + 1439: 1001439 + 1440: 1001440 + 1441: 1001441 + 1442: 1001442 + 1443: 1001443 + 1444: 1001444 + 1445: 1001445 + 1446: 1001446 + 1447: 1001447 + 1448: 1001448 + 1449: 1001449 + 1450: 1001450 + 1451: 1001451 + 1452: 1001452 + 1453: 1001453 + 1454: 1001454 + 1455: 1001455 + 1456: 1001456 + 1457: 1001457 + 1458: 1001458 + 1459: 1001459 + 1460: 1001460 + 1461: 1001461 + 1462: 1001462 + 1463: 1001463 + 1464: 1001464 + 1465: 1001465 + 1466: 1001466 + 1467: 1001467 + 1468: 1001468 + 1469: 1001469 + 1470: 1001470 + 1471: 1001471 + 1472: 1001472 + 1473: 1001473 + 1474: 1001474 + 1475: 1001475 + 1476: 1001476 + 1477: 1001477 + 1478: 1001478 + 1479: 1001479 + 1480: 1001480 + 1481: 1001481 + 1482: 1001482 + 1483: 1001483 + 1484: 1001484 + 1485: 1001485 + 1486: 1001486 + 1487: 1001487 + 1488: 1001488 + 1489: 1001489 + 1490: 1001490 + 1491: 1001491 + 1492: 1001492 + 1493: 1001493 + 1494: 1001494 + 1495: 1001495 + 1496: 1001496 + 1497: 1001497 + 1498: 1001498 + 1499: 1001499 + 1500: 1001500 + 1501: 1001501 + 1502: 1001502 + 1503: 1001503 + 1504: 1001504 + 1505: 1001505 + 1506: 1001506 + 1507: 1001507 + 1508: 1001508 + 1509: 1001509 + 1510: 1001510 + 1511: 1001511 + 1512: 1001512 + 1513: 1001513 + 1514: 1001514 + 1515: 1001515 + 1516: 1001516 + 1517: 1001517 + 1518: 1001518 + 1519: 1001519 + 1520: 1001520 + 1521: 1001521 + 1522: 1001522 + 1523: 1001523 + 1524: 1001524 + 1525: 1001525 + 1526: 1001526 + 1527: 1001527 + 1528: 1001528 + 1529: 1001529 + 1530: 1001530 + 1531: 1001531 + 1532: 1001532 + 1533: 1001533 + 1534: 1001534 + 1535: 1001535 + 1536: 1001536 + 1537: 1001537 + 1538: 1001538 + 1539: 1001539 + 1540: 1001540 + 1541: 1001541 + 1542: 1001542 + 1543: 1001543 + 1544: 1001544 + 1545: 1001545 + 1546: 1001546 + 1547: 1001547 + 1548: 1001548 + 1549: 1001549 + 1550: 1001550 + 1551: 1001551 + 1552: 1001552 + 1553: 1001553 + 1554: 1001554 + 1555: 1001555 + 1556: 1001556 + 1557: 1001557 + 1558: 1001558 + 1559: 1001559 + 1560: 1001560 + 1561: 1001561 + 1562: 1001562 + 1563: 1001563 + 1564: 1001564 + 1565: 1001565 + 1566: 1001566 + 1567: 1001567 + 1568: 1001568 + 1569: 1001569 + 1570: 1001570 + 1571: 1001571 + 1572: 1001572 + 1573: 1001573 + 1574: 1001574 + 1575: 1001575 + 1576: 1001576 + 1577: 1001577 + 1578: 1001578 + 1579: 1001579 + 1580: 1001580 + 1581: 1001581 + 1582: 1001582 + 1583: 1001583 + 1584: 1001584 + 1585: 1001585 + 1586: 1001586 + 1587: 1001587 + 1588: 1001588 + 1589: 1001589 + 1590: 1001590 + 1591: 1001591 + 1592: 1001592 + 1593: 1001593 + 1594: 1001594 + 1595: 1001595 + 1596: 1001596 + 1597: 1001597 + 1598: 1001598 + 1599: 1001599 + 1600: 1001600 + 1601: 1001601 + 1602: 1001602 + 1603: 1001603 + 1604: 1001604 + 1605: 1001605 + 1606: 1001606 + 1607: 1001607 + 1608: 1001608 + 1609: 1001609 + 1610: 1001610 + 1611: 1001611 + 1612: 1001612 + 1613: 1001613 + 1614: 1001614 + 1615: 1001615 + 1616: 1001616 + 1617: 1001617 + 1618: 1001618 + 1619: 1001619 + 1620: 1001620 + 1621: 1001621 + 1622: 1001622 + 1623: 1001623 + 1624: 1001624 + 1625: 1001625 + 1626: 1001626 + 1627: 1001627 + 1628: 1001628 + 1629: 1001629 + 1630: 1001630 + 1631: 1001631 + 1632: 1001632 + 1633: 1001633 + 1634: 1001634 + 1635: 1001635 + 1636: 1001636 + 1637: 1001637 + 1638: 1001638 + 1639: 1001639 + 1640: 1001640 + 1641: 1001641 + 1642: 1001642 + 1643: 1001643 + 1644: 1001644 + 1645: 1001645 + 1646: 1001646 + 1647: 1001647 + 1648: 1001648 + 1649: 1001649 + 1650: 1001650 + 1651: 1001651 + 1652: 1001652 + 1653: 1001653 + 1654: 1001654 + 1655: 1001655 + 1656: 1001656 + 1657: 1001657 + 1658: 1001658 + 1659: 1001659 + 1660: 1001660 + 1661: 1001661 + 1662: 1001662 + 1663: 1001663 + 1664: 1001664 + 1665: 1001665 + 1666: 1001666 + 1667: 1001667 + 1668: 1001668 + 1669: 1001669 + 1670: 1001670 + 1671: 1001671 + 1672: 1001672 + 1673: 1001673 + 1674: 1001674 + 1675: 1001675 + 1676: 1001676 + 1677: 1001677 + 1678: 1001678 + 1679: 1001679 + 1680: 1001680 + 1681: 1001681 + 1682: 1001682 + 1683: 1001683 + 1684: 1001684 + 1685: 1001685 + 1686: 1001686 + 1687: 1001687 + 1688: 1001688 + 1689: 1001689 + 1690: 1001690 + 1691: 1001691 + 1692: 1001692 + 1693: 1001693 + 1694: 1001694 + 1695: 1001695 + 1696: 1001696 + 1697: 1001697 + 1698: 1001698 + 1699: 1001699 + 1700: 1001700 + 1701: 1001701 + 1702: 1001702 + 1703: 1001703 + 1704: 1001704 + 1705: 1001705 + 1706: 1001706 + 1707: 1001707 + 1708: 1001708 + 1709: 1001709 + 1710: 1001710 + 1711: 1001711 + 1712: 1001712 + 1713: 1001713 + 1714: 1001714 + 1715: 1001715 + 1716: 1001716 + 1717: 1001717 + 1718: 1001718 + 1719: 1001719 + 1720: 1001720 + 1721: 1001721 + 1722: 1001722 + 1723: 1001723 + 1724: 1001724 + 1725: 1001725 + 1726: 1001726 + 1727: 1001727 + 1728: 1001728 + 1729: 1001729 + 1730: 1001730 + 1731: 1001731 + 1732: 1001732 + 1733: 1001733 + 1734: 1001734 + 1735: 1001735 + 1736: 1001736 + 1737: 1001737 + 1738: 1001738 + 1739: 1001739 + 1740: 1001740 + 1741: 1001741 + 1742: 1001742 + 1743: 1001743 + 1744: 1001744 + 1745: 1001745 + 1746: 1001746 + 1747: 1001747 + 1748: 1001748 + 1749: 1001749 + 1750: 1001750 + 1751: 1001751 + 1752: 1001752 + 1753: 1001753 + 1754: 1001754 + 1755: 1001755 + 1756: 1001756 + 1757: 1001757 + 1758: 1001758 + 1759: 1001759 + 1760: 1001760 + 1761: 1001761 + 1762: 1001762 + 1763: 1001763 + 1764: 1001764 + 1765: 1001765 + 1766: 1001766 + 1767: 1001767 + 1768: 1001768 + 1769: 1001769 + 1770: 1001770 + 1771: 1001771 + 1772: 1001772 + 1773: 1001773 + 1774: 1001774 + 1775: 1001775 + 1776: 1001776 + 1777: 1001777 + 1778: 1001778 + 1779: 1001779 + 1780: 1001780 + 1781: 1001781 + 1782: 1001782 + 1783: 1001783 + 1784: 1001784 + 1785: 1001785 + 1786: 1001786 + 1787: 1001787 + 1788: 1001788 + 1789: 1001789 + 1790: 1001790 + 1791: 1001791 + 1792: 1001792 + 1793: 1001793 + 1794: 1001794 + 1795: 1001795 + 1796: 1001796 + 1797: 1001797 + 1798: 1001798 + 1799: 1001799 + 1800: 1001800 + 1801: 1001801 + 1802: 1001802 + 1803: 1001803 + 1804: 1001804 + 1805: 1001805 + 1806: 1001806 + 1807: 1001807 + 1808: 1001808 + 1809: 1001809 + 1810: 1001810 + 1811: 1001811 + 1812: 1001812 + 1813: 1001813 + 1814: 1001814 + 1815: 1001815 + 1816: 1001816 + 1817: 1001817 + 1818: 1001818 + 1819: 1001819 + 1820: 1001820 + 1821: 1001821 + 1822: 1001822 + 1823: 1001823 + 1824: 1001824 + 1825: 1001825 + 1826: 1001826 + 1827: 1001827 + 1828: 1001828 + 1829: 1001829 + 1830: 1001830 + 1831: 1001831 + 1832: 1001832 + 1833: 1001833 + 1834: 1001834 + 1835: 1001835 + 1836: 1001836 + 1837: 1001837 + 1838: 1001838 + 1839: 1001839 + 1840: 1001840 + 1841: 1001841 + 1842: 1001842 + 1843: 1001843 + 1844: 1001844 + 1845: 1001845 + 1846: 1001846 + 1847: 1001847 + 1848: 1001848 + 1849: 1001849 + 1850: 1001850 + 1851: 1001851 + 1852: 1001852 + 1853: 1001853 + 1854: 1001854 + 1855: 1001855 + 1856: 1001856 + 1857: 1001857 + 1858: 1001858 + 1859: 1001859 + 1860: 1001860 + 1861: 1001861 + 1862: 1001862 + 1863: 1001863 + 1864: 1001864 + 1865: 1001865 + 1866: 1001866 + 1867: 1001867 + 1868: 1001868 + 1869: 1001869 + 1870: 1001870 + 1871: 1001871 + 1872: 1001872 + 1873: 1001873 + 1874: 1001874 + 1875: 1001875 + 1876: 1001876 + 1877: 1001877 + 1878: 1001878 + 1879: 1001879 + 1880: 1001880 + 1881: 1001881 + 1882: 1001882 + 1883: 1001883 + 1884: 1001884 + 1885: 1001885 + 1886: 1001886 + 1887: 1001887 + 1888: 1001888 + 1889: 1001889 + 1890: 1001890 + 1891: 1001891 + 1892: 1001892 + 1893: 1001893 + 1894: 1001894 + 1895: 1001895 + 1896: 1001896 + 1897: 1001897 + 1898: 1001898 + 1899: 1001899 + 1900: 1001900 + 1901: 1001901 + 1902: 1001902 + 1903: 1001903 + 1904: 1001904 + 1905: 1001905 + 1906: 1001906 + 1907: 1001907 + 1908: 1001908 + 1909: 1001909 + 1910: 1001910 + 1911: 1001911 + 1912: 1001912 + 1913: 1001913 + 1914: 1001914 + 1915: 1001915 + 1916: 1001916 + 1917: 1001917 + 1918: 1001918 + 1919: 1001919 + 1920: 1001920 + 1921: 1001921 + 1922: 1001922 + 1923: 1001923 + 1924: 1001924 + 1925: 1001925 + 1926: 1001926 + 1927: 1001927 + 1928: 1001928 + 1929: 1001929 + 1930: 1001930 + 1931: 1001931 + 1932: 1001932 + 1933: 1001933 + 1934: 1001934 + 1935: 1001935 + 1936: 1001936 + 1937: 1001937 + 1938: 1001938 + 1939: 1001939 + 1940: 1001940 + 1941: 1001941 + 1942: 1001942 + 1943: 1001943 + 1944: 1001944 + 1945: 1001945 + 1946: 1001946 + 1947: 1001947 + 1948: 1001948 + 1949: 1001949 + 1950: 1001950 + 1951: 1001951 + 1952: 1001952 + 1953: 1001953 + 1954: 1001954 + 1955: 1001955 + 1956: 1001956 + 1957: 1001957 + 1958: 1001958 + 1959: 1001959 + 1960: 1001960 + 1961: 1001961 + 1962: 1001962 + 1963: 1001963 + 1964: 1001964 + 1965: 1001965 + 1966: 1001966 + 1967: 1001967 + 1968: 1001968 + 1969: 1001969 + 1970: 1001970 + 1971: 1001971 + 1972: 1001972 + 1973: 1001973 + 1974: 1001974 + 1975: 1001975 + 1976: 1001976 + 1977: 1001977 + 1978: 1001978 + 1979: 1001979 + 1980: 1001980 + 1981: 1001981 + 1982: 1001982 + 1983: 1001983 + 1984: 1001984 + 1985: 1001985 + 1986: 1001986 + 1987: 1001987 + 1988: 1001988 + 1989: 1001989 + 1990: 1001990 + 1991: 1001991 + 1992: 1001992 + 1993: 1001993 + 1994: 1001994 + 1995: 1001995 + 1996: 1001996 + 1997: 1001997 + 1998: 1001998 + 1999: 1001999 + 2000: 1002000 + 2001: 1002001 + 2002: 1002002 + 2003: 1002003 + 2004: 1002004 + 2005: 1002005 + 2006: 1002006 + 2007: 1002007 + 2008: 1002008 + 2009: 1002009 + 2010: 1002010 + 2011: 1002011 + 2012: 1002012 + 2013: 1002013 + 2014: 1002014 + 2015: 1002015 + 2016: 1002016 + 2017: 1002017 + 2018: 1002018 + 2019: 1002019 + 2020: 1002020 + 2021: 1002021 + 2022: 1002022 + 2023: 1002023 + 2024: 1002024 + 2025: 1002025 + 2026: 1002026 + 2027: 1002027 + 2028: 1002028 + 2029: 1002029 + 2030: 1002030 + 2031: 1002031 + 2032: 1002032 + 2033: 1002033 + 2034: 1002034 + 2035: 1002035 + 2036: 1002036 + 2037: 1002037 + 2038: 1002038 + 2039: 1002039 + 2040: 1002040 + 2041: 1002041 + 2042: 1002042 + 2043: 1002043 + 2044: 1002044 + 2045: 1002045 + 2046: 1002046 + 2047: 1002047 + 2048: 1002048 + 2049: 1002049 + 2050: 1002050 + 2051: 1002051 + 2052: 1002052 + 2053: 1002053 + 2054: 1002054 + 2055: 1002055 + 2056: 1002056 + 2057: 1002057 + 2058: 1002058 + 2059: 1002059 + 2060: 1002060 + 2061: 1002061 + 2062: 1002062 + 2063: 1002063 + 2064: 1002064 + 2065: 1002065 + 2066: 1002066 + 2067: 1002067 + 2068: 1002068 + 2069: 1002069 + 2070: 1002070 + 2071: 1002071 + 2072: 1002072 + 2073: 1002073 + 2074: 1002074 + 2075: 1002075 + 2076: 1002076 + 2077: 1002077 + 2078: 1002078 + 2079: 1002079 + 2080: 1002080 + 2081: 1002081 + 2082: 1002082 + 2083: 1002083 + 2084: 1002084 + 2085: 1002085 + 2086: 1002086 + 2087: 1002087 + 2088: 1002088 + 2089: 1002089 + 2090: 1002090 + 2091: 1002091 + 2092: 1002092 + 2093: 1002093 + 2094: 1002094 + 2095: 1002095 + 2096: 1002096 + 2097: 1002097 + 2098: 1002098 + 2099: 1002099 + 2100: 1002100 + 2101: 1002101 + 2102: 1002102 + 2103: 1002103 + 2104: 1002104 + 2105: 1002105 + 2106: 1002106 + 2107: 1002107 + 2108: 1002108 + 2109: 1002109 + 2110: 1002110 + 2111: 1002111 + 2112: 1002112 + 2113: 1002113 + 2114: 1002114 + 2115: 1002115 + 2116: 1002116 + 2117: 1002117 + 2118: 1002118 + 2119: 1002119 + 2120: 1002120 + 2121: 1002121 + 2122: 1002122 + 2123: 1002123 + 2124: 1002124 + 2125: 1002125 + 2126: 1002126 + 2127: 1002127 + 2128: 1002128 + 2129: 1002129 + 2130: 1002130 + 2131: 1002131 + 2132: 1002132 + 2133: 1002133 + 2134: 1002134 + 2135: 1002135 + 2136: 1002136 + 2137: 1002137 + 2138: 1002138 + 2139: 1002139 + 2140: 1002140 + 2141: 1002141 + 2142: 1002142 + 2143: 1002143 + 2144: 1002144 + 2145: 1002145 + 2146: 1002146 + 2147: 1002147 + 2148: 1002148 + 2149: 1002149 + 2150: 1002150 + 2151: 1002151 + 2152: 1002152 + 2153: 1002153 + 2154: 1002154 + 2155: 1002155 + 2156: 1002156 + 2157: 1002157 + 2158: 1002158 + 2159: 1002159 + 2160: 1002160 + 2161: 1002161 + 2162: 1002162 + 2163: 1002163 + 2164: 1002164 + 2165: 1002165 + 2166: 1002166 + 2167: 1002167 + 2168: 1002168 + 2169: 1002169 + 2170: 1002170 + 2171: 1002171 + 2172: 1002172 + 2173: 1002173 + 2174: 1002174 + 2175: 1002175 + 2176: 1002176 + 2177: 1002177 + 2178: 1002178 + 2179: 1002179 + 2180: 1002180 + 2181: 1002181 + 2182: 1002182 + 2183: 1002183 + 2184: 1002184 + 2185: 1002185 + 2186: 1002186 + 2187: 1002187 + 2188: 1002188 + 2189: 1002189 + 2190: 1002190 + 2191: 1002191 + 2192: 1002192 + 2193: 1002193 + 2194: 1002194 + 2195: 1002195 + 2196: 1002196 + 2197: 1002197 + 2198: 1002198 + 2199: 1002199 + 2200: 1002200 + 2201: 1002201 + 2202: 1002202 + 2203: 1002203 + 2204: 1002204 + 2205: 1002205 + 2206: 1002206 + 2207: 1002207 + 2208: 1002208 + 2209: 1002209 + 2210: 1002210 + 2211: 1002211 + 2212: 1002212 + 2213: 1002213 + 2214: 1002214 + 2215: 1002215 + 2216: 1002216 + 2217: 1002217 + 2218: 1002218 + 2219: 1002219 + 2220: 1002220 + 2221: 1002221 + 2222: 1002222 + 2223: 1002223 + 2224: 1002224 + 2225: 1002225 + 2226: 1002226 + 2227: 1002227 + 2228: 1002228 + 2229: 1002229 + 2230: 1002230 + 2231: 1002231 + 2232: 1002232 + 2233: 1002233 + 2234: 1002234 + 2235: 1002235 + 2236: 1002236 + 2237: 1002237 + 2238: 1002238 + 2239: 1002239 + 2240: 1002240 + 2241: 1002241 + 2242: 1002242 + 2243: 1002243 + 2244: 1002244 + 2245: 1002245 + 2246: 1002246 + 2247: 1002247 + 2248: 1002248 + 2249: 1002249 + 2250: 1002250 + 2251: 1002251 + 2252: 1002252 + 2253: 1002253 + 2254: 1002254 + 2255: 1002255 + 2256: 1002256 + 2257: 1002257 + 2258: 1002258 + 2259: 1002259 + 2260: 1002260 + 2261: 1002261 + 2262: 1002262 + 2263: 1002263 + 2264: 1002264 + 2265: 1002265 + 2266: 1002266 + 2267: 1002267 + 2268: 1002268 + 2269: 1002269 + 2270: 1002270 + 2271: 1002271 + 2272: 1002272 + 2273: 1002273 + 2274: 1002274 + 2275: 1002275 + 2276: 1002276 + 2277: 1002277 + 2278: 1002278 + 2279: 1002279 + 2280: 1002280 + 2281: 1002281 + 2282: 1002282 + 2283: 1002283 + 2284: 1002284 + 2285: 1002285 + 2286: 1002286 + 2287: 1002287 + 2288: 1002288 + 2289: 1002289 + 2290: 1002290 + 2291: 1002291 + 2292: 1002292 + 2293: 1002293 + 2294: 1002294 + 2295: 1002295 + 2296: 1002296 + 2297: 1002297 + 2298: 1002298 + 2299: 1002299 + 2300: 1002300 + 2301: 1002301 + 2302: 1002302 + 2303: 1002303 + 2304: 1002304 + 2305: 1002305 + 2306: 1002306 + 2307: 1002307 + 2308: 1002308 + 2309: 1002309 + 2310: 1002310 + 2311: 1002311 + 2312: 1002312 + 2313: 1002313 + 2314: 1002314 + 2315: 1002315 + 2316: 1002316 + 2317: 1002317 + 2318: 1002318 + 2319: 1002319 + 2320: 1002320 + 2321: 1002321 + 2322: 1002322 + 2323: 1002323 + 2324: 1002324 + 2325: 1002325 + 2326: 1002326 + 2327: 1002327 + 2328: 1002328 + 2329: 1002329 + 2330: 1002330 + 2331: 1002331 + 2332: 1002332 + 2333: 1002333 + 2334: 1002334 + 2335: 1002335 + 2336: 1002336 + 2337: 1002337 + 2338: 1002338 + 2339: 1002339 + 2340: 1002340 + 2341: 1002341 + 2342: 1002342 + 2343: 1002343 + 2344: 1002344 + 2345: 1002345 + 2346: 1002346 + 2347: 1002347 + 2348: 1002348 + 2349: 1002349 + 2350: 1002350 + 2351: 1002351 + 2352: 1002352 + 2353: 1002353 + 2354: 1002354 + 2355: 1002355 + 2356: 1002356 + 2357: 1002357 + 2358: 1002358 + 2359: 1002359 + 2360: 1002360 + 2361: 1002361 + 2362: 1002362 + 2363: 1002363 + 2364: 1002364 + 2365: 1002365 + 2366: 1002366 + 2367: 1002367 + 2368: 1002368 + 2369: 1002369 + 2370: 1002370 + 2371: 1002371 + 2372: 1002372 + 2373: 1002373 + 2374: 1002374 + 2375: 1002375 + 2376: 1002376 + 2377: 1002377 + 2378: 1002378 + 2379: 1002379 + 2380: 1002380 + 2381: 1002381 + 2382: 1002382 + 2383: 1002383 + 2384: 1002384 + 2385: 1002385 + 2386: 1002386 + 2387: 1002387 + 2388: 1002388 + 2389: 1002389 + 2390: 1002390 + 2391: 1002391 + 2392: 1002392 + 2393: 1002393 + 2394: 1002394 + 2395: 1002395 + 2396: 1002396 + 2397: 1002397 + 2398: 1002398 + 2399: 1002399 + 2400: 1002400 + 2401: 1002401 + 2402: 1002402 + 2403: 1002403 + 2404: 1002404 + 2405: 1002405 + 2406: 1002406 + 2407: 1002407 + 2408: 1002408 + 2409: 1002409 + 2410: 1002410 + 2411: 1002411 + 2412: 1002412 + 2413: 1002413 + 2414: 1002414 + 2415: 1002415 + 2416: 1002416 + 2417: 1002417 + 2418: 1002418 + 2419: 1002419 + 2420: 1002420 + 2421: 1002421 + 2422: 1002422 + 2423: 1002423 + 2424: 1002424 + 2425: 1002425 + 2426: 1002426 + 2427: 1002427 + 2428: 1002428 + 2429: 1002429 + 2430: 1002430 + 2431: 1002431 + 2432: 1002432 + 2433: 1002433 + 2434: 1002434 + 2435: 1002435 + 2436: 1002436 + 2437: 1002437 + 2438: 1002438 + 2439: 1002439 + 2440: 1002440 + 2441: 1002441 + 2442: 1002442 + 2443: 1002443 + 2444: 1002444 + 2445: 1002445 + 2446: 1002446 + 2447: 1002447 + 2448: 1002448 + 2449: 1002449 + 2450: 1002450 + 2451: 1002451 + 2452: 1002452 + 2453: 1002453 + 2454: 1002454 + 2455: 1002455 + 2456: 1002456 + 2457: 1002457 + 2458: 1002458 + 2459: 1002459 + 2460: 1002460 + 2461: 1002461 + 2462: 1002462 + 2463: 1002463 + 2464: 1002464 + 2465: 1002465 + 2466: 1002466 + 2467: 1002467 + 2468: 1002468 + 2469: 1002469 + 2470: 1002470 + 2471: 1002471 + 2472: 1002472 + 2473: 1002473 + 2474: 1002474 + 2475: 1002475 + 2476: 1002476 + 2477: 1002477 + 2478: 1002478 + 2479: 1002479 + 2480: 1002480 + 2481: 1002481 + 2482: 1002482 + 2483: 1002483 + 2484: 1002484 + 2485: 1002485 + 2486: 1002486 + 2487: 1002487 + 2488: 1002488 + 2489: 1002489 + 2490: 1002490 + 2491: 1002491 + 2492: 1002492 + 2493: 1002493 + 2494: 1002494 + 2495: 1002495 + 2496: 1002496 + 2497: 1002497 + 2498: 1002498 + 2499: 1002499 + 2500: 1002500 + 2501: 1002501 + 2502: 1002502 + 2503: 1002503 + 2504: 1002504 + 2505: 1002505 + 2506: 1002506 + 2507: 1002507 + 2508: 1002508 + 2509: 1002509 + 2510: 1002510 + 2511: 1002511 + 2512: 1002512 + 2513: 1002513 + 2514: 1002514 + 2515: 1002515 + 2516: 1002516 + 2517: 1002517 + 2518: 1002518 + 2519: 1002519 + 2520: 1002520 + 2521: 1002521 + 2522: 1002522 + 2523: 1002523 + 2524: 1002524 + 2525: 1002525 + 2526: 1002526 + 2527: 1002527 + 2528: 1002528 + 2529: 1002529 + 2530: 1002530 + 2531: 1002531 + 2532: 1002532 + 2533: 1002533 + 2534: 1002534 + 2535: 1002535 + 2536: 1002536 + 2537: 1002537 + 2538: 1002538 + 2539: 1002539 + 2540: 1002540 + 2541: 1002541 + 2542: 1002542 + 2543: 1002543 + 2544: 1002544 + 2545: 1002545 + 2546: 1002546 + 2547: 1002547 + 2548: 1002548 + 2549: 1002549 + 2550: 1002550 + 2551: 1002551 + 2552: 1002552 + 2553: 1002553 + 2554: 1002554 + 2555: 1002555 + 2556: 1002556 + 2557: 1002557 + 2558: 1002558 + 2559: 1002559 + 2560: 1002560 + 2561: 1002561 + 2562: 1002562 + 2563: 1002563 + 2564: 1002564 + 2565: 1002565 + 2566: 1002566 + 2567: 1002567 + 2568: 1002568 + 2569: 1002569 + 2570: 1002570 + 2571: 1002571 + 2572: 1002572 + 2573: 1002573 + 2574: 1002574 + 2575: 1002575 + 2576: 1002576 + 2577: 1002577 + 2578: 1002578 + 2579: 1002579 + 2580: 1002580 + 2581: 1002581 + 2582: 1002582 + 2583: 1002583 + 2584: 1002584 + 2585: 1002585 + 2586: 1002586 + 2587: 1002587 + 2588: 1002588 + 2589: 1002589 + 2590: 1002590 + 2591: 1002591 + 2592: 1002592 + 2593: 1002593 + 2594: 1002594 + 2595: 1002595 + 2596: 1002596 + 2597: 1002597 + 2598: 1002598 + 2599: 1002599 + 2600: 1002600 + 2601: 1002601 + 2602: 1002602 + 2603: 1002603 + 2604: 1002604 + 2605: 1002605 + 2606: 1002606 + 2607: 1002607 + 2608: 1002608 + 2609: 1002609 + 2610: 1002610 + 2611: 1002611 + 2612: 1002612 + 2613: 1002613 + 2614: 1002614 + 2615: 1002615 + 2616: 1002616 + 2617: 1002617 + 2618: 1002618 + 2619: 1002619 + 2620: 1002620 + 2621: 1002621 + 2622: 1002622 + 2623: 1002623 + 2624: 1002624 + 2625: 1002625 + 2626: 1002626 + 2627: 1002627 + 2628: 1002628 + 2629: 1002629 + 2630: 1002630 + 2631: 1002631 + 2632: 1002632 + 2633: 1002633 + 2634: 1002634 + 2635: 1002635 + 2636: 1002636 + 2637: 1002637 + 2638: 1002638 + 2639: 1002639 + 2640: 1002640 + 2641: 1002641 + 2642: 1002642 + 2643: 1002643 + 2644: 1002644 + 2645: 1002645 + 2646: 1002646 + 2647: 1002647 + 2648: 1002648 + 2649: 1002649 + 2650: 1002650 + 2651: 1002651 + 2652: 1002652 + 2653: 1002653 + 2654: 1002654 + 2655: 1002655 + 2656: 1002656 + 2657: 1002657 + 2658: 1002658 + 2659: 1002659 + 2660: 1002660 + 2661: 1002661 + 2662: 1002662 + 2663: 1002663 + 2664: 1002664 + 2665: 1002665 + 2666: 1002666 + 2667: 1002667 + 2668: 1002668 + 2669: 1002669 + 2670: 1002670 + 2671: 1002671 + 2672: 1002672 + 2673: 1002673 + 2674: 1002674 + 2675: 1002675 + 2676: 1002676 + 2677: 1002677 + 2678: 1002678 + 2679: 1002679 + 2680: 1002680 + 2681: 1002681 + 2682: 1002682 + 2683: 1002683 + 2684: 1002684 + 2685: 1002685 + 2686: 1002686 + 2687: 1002687 + 2688: 1002688 + 2689: 1002689 + 2690: 1002690 + 2691: 1002691 + 2692: 1002692 + 2693: 1002693 + 2694: 1002694 + 2695: 1002695 + 2696: 1002696 + 2697: 1002697 + 2698: 1002698 + 2699: 1002699 + 2700: 1002700 + 2701: 1002701 + 2702: 1002702 + 2703: 1002703 + 2704: 1002704 + 2705: 1002705 + 2706: 1002706 + 2707: 1002707 + 2708: 1002708 + 2709: 1002709 + 2710: 1002710 + 2711: 1002711 + 2712: 1002712 + 2713: 1002713 + 2714: 1002714 + 2715: 1002715 + 2716: 1002716 + 2717: 1002717 + 2718: 1002718 + 2719: 1002719 + 2720: 1002720 + 2721: 1002721 + 2722: 1002722 + 2723: 1002723 + 2724: 1002724 + 2725: 1002725 + 2726: 1002726 + 2727: 1002727 + 2728: 1002728 + 2729: 1002729 + 2730: 1002730 + 2731: 1002731 + 2732: 1002732 + 2733: 1002733 + 2734: 1002734 + 2735: 1002735 + 2736: 1002736 + 2737: 1002737 + 2738: 1002738 + 2739: 1002739 + 2740: 1002740 + 2741: 1002741 + 2742: 1002742 + 2743: 1002743 + 2744: 1002744 + 2745: 1002745 + 2746: 1002746 + 2747: 1002747 + 2748: 1002748 + 2749: 1002749 + 2750: 1002750 + 2751: 1002751 + 2752: 1002752 + 2753: 1002753 + 2754: 1002754 + 2755: 1002755 + 2756: 1002756 + 2757: 1002757 + 2758: 1002758 + 2759: 1002759 + 2760: 1002760 + 2761: 1002761 + 2762: 1002762 + 2763: 1002763 + 2764: 1002764 + 2765: 1002765 + 2766: 1002766 + 2767: 1002767 + 2768: 1002768 + 2769: 1002769 + 2770: 1002770 + 2771: 1002771 + 2772: 1002772 + 2773: 1002773 + 2774: 1002774 + 2775: 1002775 + 2776: 1002776 + 2777: 1002777 + 2778: 1002778 + 2779: 1002779 + 2780: 1002780 + 2781: 1002781 + 2782: 1002782 + 2783: 1002783 + 2784: 1002784 + 2785: 1002785 + 2786: 1002786 + 2787: 1002787 + 2788: 1002788 + 2789: 1002789 + 2790: 1002790 + 2791: 1002791 + 2792: 1002792 + 2793: 1002793 + 2794: 1002794 + 2795: 1002795 + 2796: 1002796 + 2797: 1002797 + 2798: 1002798 + 2799: 1002799 + 2800: 1002800 + 2801: 1002801 + 2802: 1002802 + 2803: 1002803 + 2804: 1002804 + 2805: 1002805 + 2806: 1002806 + 2807: 1002807 + 2808: 1002808 + 2809: 1002809 + 2810: 1002810 + 2811: 1002811 + 2812: 1002812 + 2813: 1002813 + 2814: 1002814 + 2815: 1002815 + 2816: 1002816 + 2817: 1002817 + 2818: 1002818 + 2819: 1002819 + 2820: 1002820 + 2821: 1002821 + 2822: 1002822 + 2823: 1002823 + 2824: 1002824 + 2825: 1002825 + 2826: 1002826 + 2827: 1002827 + 2828: 1002828 + 2829: 1002829 + 2830: 1002830 + 2831: 1002831 + 2832: 1002832 + 2833: 1002833 + 2834: 1002834 + 2835: 1002835 + 2836: 1002836 + 2837: 1002837 + 2838: 1002838 + 2839: 1002839 + 2840: 1002840 + 2841: 1002841 + 2842: 1002842 + 2843: 1002843 + 2844: 1002844 + 2845: 1002845 + 2846: 1002846 + 2847: 1002847 + 2848: 1002848 + 2849: 1002849 + 2850: 1002850 + 2851: 1002851 + 2852: 1002852 + 2853: 1002853 + 2854: 1002854 + 2855: 1002855 + 2856: 1002856 + 2857: 1002857 + 2858: 1002858 + 2859: 1002859 + 2860: 1002860 + 2861: 1002861 + 2862: 1002862 + 2863: 1002863 + 2864: 1002864 + 2865: 1002865 + 2866: 1002866 + 2867: 1002867 + 2868: 1002868 + 2869: 1002869 + 2870: 1002870 + 2871: 1002871 + 2872: 1002872 + 2873: 1002873 + 2874: 1002874 + 2875: 1002875 + 2876: 1002876 + 2877: 1002877 + 2878: 1002878 + 2879: 1002879 + 2880: 1002880 + 2881: 1002881 + 2882: 1002882 + 2883: 1002883 + 2884: 1002884 + 2885: 1002885 + 2886: 1002886 + 2887: 1002887 + 2888: 1002888 + 2889: 1002889 + 2890: 1002890 + 2891: 1002891 + 2892: 1002892 + 2893: 1002893 + 2894: 1002894 + 2895: 1002895 + 2896: 1002896 + 2897: 1002897 + 2898: 1002898 + 2899: 1002899 + 2900: 1002900 + 2901: 1002901 + 2902: 1002902 + 2903: 1002903 + 2904: 1002904 + 2905: 1002905 + 2906: 1002906 + 2907: 1002907 + 2908: 1002908 + 2909: 1002909 + 2910: 1002910 + 2911: 1002911 + 2912: 1002912 + 2913: 1002913 + 2914: 1002914 + 2915: 1002915 + 2916: 1002916 + 2917: 1002917 + 2918: 1002918 + 2919: 1002919 + 2920: 1002920 + 2921: 1002921 + 2922: 1002922 + 2923: 1002923 + 2924: 1002924 + 2925: 1002925 + 2926: 1002926 + 2927: 1002927 + 2928: 1002928 + 2929: 1002929 + 2930: 1002930 + 2931: 1002931 + 2932: 1002932 + 2933: 1002933 + 2934: 1002934 + 2935: 1002935 + 2936: 1002936 + 2937: 1002937 + 2938: 1002938 + 2939: 1002939 + 2940: 1002940 + 2941: 1002941 + 2942: 1002942 + 2943: 1002943 + 2944: 1002944 + 2945: 1002945 + 2946: 1002946 + 2947: 1002947 + 2948: 1002948 + 2949: 1002949 + 2950: 1002950 + 2951: 1002951 + 2952: 1002952 + 2953: 1002953 + 2954: 1002954 + 2955: 1002955 + 2956: 1002956 + 2957: 1002957 + 2958: 1002958 + 2959: 1002959 + 2960: 1002960 + 2961: 1002961 + 2962: 1002962 + 2963: 1002963 + 2964: 1002964 + 2965: 1002965 + 2966: 1002966 + 2967: 1002967 + 2968: 1002968 + 2969: 1002969 + 2970: 1002970 + 2971: 1002971 + 2972: 1002972 + 2973: 1002973 + 2974: 1002974 + 2975: 1002975 + 2976: 1002976 + 2977: 1002977 + 2978: 1002978 + 2979: 1002979 + 2980: 1002980 + 2981: 1002981 + 2982: 1002982 + 2983: 1002983 + 2984: 1002984 + 2985: 1002985 + 2986: 1002986 + 2987: 1002987 + 2988: 1002988 + 2989: 1002989 + 2990: 1002990 + 2991: 1002991 + 2992: 1002992 + 2993: 1002993 + 2994: 1002994 + 2995: 1002995 + 2996: 1002996 + 2997: 1002997 + 2998: 1002998 + 2999: 1002999 + 3000: 1003000 + 3001: 1003001 + 3002: 1003002 + 3003: 1003003 + 3004: 1003004 + 3005: 1003005 + 3006: 1003006 + 3007: 1003007 + 3008: 1003008 + 3009: 1003009 + 3010: 1003010 + 3011: 1003011 + 3012: 1003012 + 3013: 1003013 + 3014: 1003014 + 3015: 1003015 + 3016: 1003016 + 3017: 1003017 + 3018: 1003018 + 3019: 1003019 + 3020: 1003020 + 3021: 1003021 + 3022: 1003022 + 3023: 1003023 + 3024: 1003024 + 3025: 1003025 + 3026: 1003026 + 3027: 1003027 + 3028: 1003028 + 3029: 1003029 + 3030: 1003030 + 3031: 1003031 + 3032: 1003032 + 3033: 1003033 + 3034: 1003034 + 3035: 1003035 + 3036: 1003036 + 3037: 1003037 + 3038: 1003038 + 3039: 1003039 + 3040: 1003040 + 3041: 1003041 + 3042: 1003042 + 3043: 1003043 + 3044: 1003044 + 3045: 1003045 + 3046: 1003046 + 3047: 1003047 + 3048: 1003048 + 3049: 1003049 + 3050: 1003050 + 3051: 1003051 + 3052: 1003052 + 3053: 1003053 + 3054: 1003054 + 3055: 1003055 + 3056: 1003056 + 3057: 1003057 + 3058: 1003058 + 3059: 1003059 + 3060: 1003060 + 3061: 1003061 + 3062: 1003062 + 3063: 1003063 + 3064: 1003064 + 3065: 1003065 + 3066: 1003066 + 3067: 1003067 + 3068: 1003068 + 3069: 1003069 + 3070: 1003070 + 3071: 1003071 + 3072: 1003072 + 3073: 1003073 + 3074: 1003074 + 3075: 1003075 + 3076: 1003076 + 3077: 1003077 + 3078: 1003078 + 3079: 1003079 + 3080: 1003080 + 3081: 1003081 + 3082: 1003082 + 3083: 1003083 + 3084: 1003084 + 3085: 1003085 + 3086: 1003086 + 3087: 1003087 + 3088: 1003088 + 3089: 1003089 + 3090: 1003090 + 3091: 1003091 + 3092: 1003092 + 3093: 1003093 + 3094: 1003094 + 3095: 1003095 + 3096: 1003096 + 3097: 1003097 + 3098: 1003098 + 3099: 1003099 + 3100: 1003100 + 3101: 1003101 + 3102: 1003102 + 3103: 1003103 + 3104: 1003104 + 3105: 1003105 + 3106: 1003106 + 3107: 1003107 + 3108: 1003108 + 3109: 1003109 + 3110: 1003110 + 3111: 1003111 + 3112: 1003112 + 3113: 1003113 + 3114: 1003114 + 3115: 1003115 + 3116: 1003116 + 3117: 1003117 + 3118: 1003118 + 3119: 1003119 + 3120: 1003120 + 3121: 1003121 + 3122: 1003122 + 3123: 1003123 + 3124: 1003124 + 3125: 1003125 + 3126: 1003126 + 3127: 1003127 + 3128: 1003128 + 3129: 1003129 + 3130: 1003130 + 3131: 1003131 + 3132: 1003132 + 3133: 1003133 + 3134: 1003134 + 3135: 1003135 + 3136: 1003136 + 3137: 1003137 + 3138: 1003138 + 3139: 1003139 + 3140: 1003140 + 3141: 1003141 + 3142: 1003142 + 3143: 1003143 + 3144: 1003144 + 3145: 1003145 + 3146: 1003146 + 3147: 1003147 + 3148: 1003148 + 3149: 1003149 + 3150: 1003150 + 3151: 1003151 + 3152: 1003152 + 3153: 1003153 + 3154: 1003154 + 3155: 1003155 + 3156: 1003156 + 3157: 1003157 + 3158: 1003158 + 3159: 1003159 + 3160: 1003160 + 3161: 1003161 + 3162: 1003162 + 3163: 1003163 + 3164: 1003164 + 3165: 1003165 + 3166: 1003166 + 3167: 1003167 + 3168: 1003168 + 3169: 1003169 + 3170: 1003170 + 3171: 1003171 + 3172: 1003172 + 3173: 1003173 + 3174: 1003174 + 3175: 1003175 + 3176: 1003176 + 3177: 1003177 + 3178: 1003178 + 3179: 1003179 + 3180: 1003180 + 3181: 1003181 + 3182: 1003182 + 3183: 1003183 + 3184: 1003184 + 3185: 1003185 + 3186: 1003186 + 3187: 1003187 + 3188: 1003188 + 3189: 1003189 + 3190: 1003190 + 3191: 1003191 + 3192: 1003192 + 3193: 1003193 + 3194: 1003194 + 3195: 1003195 + 3196: 1003196 + 3197: 1003197 + 3198: 1003198 + 3199: 1003199 + 3200: 1003200 + 3201: 1003201 + 3202: 1003202 + 3203: 1003203 + 3204: 1003204 + 3205: 1003205 + 3206: 1003206 + 3207: 1003207 + 3208: 1003208 + 3209: 1003209 + 3210: 1003210 + 3211: 1003211 + 3212: 1003212 + 3213: 1003213 + 3214: 1003214 + 3215: 1003215 + 3216: 1003216 + 3217: 1003217 + 3218: 1003218 + 3219: 1003219 + 3220: 1003220 + 3221: 1003221 + 3222: 1003222 + 3223: 1003223 + 3224: 1003224 + 3225: 1003225 + 3226: 1003226 + 3227: 1003227 + 3228: 1003228 + 3229: 1003229 + 3230: 1003230 + 3231: 1003231 + 3232: 1003232 + 3233: 1003233 + 3234: 1003234 + 3235: 1003235 + 3236: 1003236 + 3237: 1003237 + 3238: 1003238 + 3239: 1003239 + 3240: 1003240 + 3241: 1003241 + 3242: 1003242 + 3243: 1003243 + 3244: 1003244 + 3245: 1003245 + 3246: 1003246 + 3247: 1003247 + 3248: 1003248 + 3249: 1003249 + 3250: 1003250 + 3251: 1003251 + 3252: 1003252 + 3253: 1003253 + 3254: 1003254 + 3255: 1003255 + 3256: 1003256 + 3257: 1003257 + 3258: 1003258 + 3259: 1003259 + 3260: 1003260 + 3261: 1003261 + 3262: 1003262 + 3263: 1003263 + 3264: 1003264 + 3265: 1003265 + 3266: 1003266 + 3267: 1003267 + 3268: 1003268 + 3269: 1003269 + 3270: 1003270 + 3271: 1003271 + 3272: 1003272 + 3273: 1003273 + 3274: 1003274 + 3275: 1003275 + 3276: 1003276 + 3277: 1003277 + 3278: 1003278 + 3279: 1003279 + 3280: 1003280 + 3281: 1003281 + 3282: 1003282 + 3283: 1003283 + 3284: 1003284 + 3285: 1003285 + 3286: 1003286 + 3287: 1003287 + 3288: 1003288 + 3289: 1003289 + 3290: 1003290 + 3291: 1003291 + 3292: 1003292 + 3293: 1003293 + 3294: 1003294 + 3295: 1003295 + 3296: 1003296 + 3297: 1003297 + 3298: 1003298 + 3299: 1003299 + 3300: 1003300 + 3301: 1003301 + 3302: 1003302 + 3303: 1003303 + 3304: 1003304 + 3305: 1003305 + 3306: 1003306 + 3307: 1003307 + 3308: 1003308 + 3309: 1003309 + 3310: 1003310 + 3311: 1003311 + 3312: 1003312 + 3313: 1003313 + 3314: 1003314 + 3315: 1003315 + 3316: 1003316 + 3317: 1003317 + 3318: 1003318 + 3319: 1003319 + 3320: 1003320 + 3321: 1003321 + 3322: 1003322 + 3323: 1003323 + 3324: 1003324 + 3325: 1003325 + 3326: 1003326 + 3327: 1003327 + 3328: 1003328 + 3329: 1003329 + 3330: 1003330 + 3331: 1003331 + 3332: 1003332 + 3333: 1003333 + 3334: 1003334 + 3335: 1003335 + 3336: 1003336 + 3337: 1003337 + 3338: 1003338 + 3339: 1003339 + 3340: 1003340 + 3341: 1003341 + 3342: 1003342 + 3343: 1003343 + 3344: 1003344 + 3345: 1003345 + 3346: 1003346 + 3347: 1003347 + 3348: 1003348 + 3349: 1003349 + 3350: 1003350 + 3351: 1003351 + 3352: 1003352 + 3353: 1003353 + 3354: 1003354 + 3355: 1003355 + 3356: 1003356 + 3357: 1003357 + 3358: 1003358 + 3359: 1003359 + 3360: 1003360 + 3361: 1003361 + 3362: 1003362 + 3363: 1003363 + 3364: 1003364 + 3365: 1003365 + 3366: 1003366 + 3367: 1003367 + 3368: 1003368 + 3369: 1003369 + 3370: 1003370 + 3371: 1003371 + 3372: 1003372 + 3373: 1003373 + 3374: 1003374 + 3375: 1003375 + 3376: 1003376 + 3377: 1003377 + 3378: 1003378 + 3379: 1003379 + 3380: 1003380 + 3381: 1003381 + 3382: 1003382 + 3383: 1003383 + 3384: 1003384 + 3385: 1003385 + 3386: 1003386 + 3387: 1003387 + 3388: 1003388 + 3389: 1003389 + 3390: 1003390 + 3391: 1003391 + 3392: 1003392 + 3393: 1003393 + 3394: 1003394 + 3395: 1003395 + 3396: 1003396 + 3397: 1003397 + 3398: 1003398 + 3399: 1003399 + 3400: 1003400 + 3401: 1003401 + 3402: 1003402 + 3403: 1003403 + 3404: 1003404 + 3405: 1003405 + 3406: 1003406 + 3407: 1003407 + 3408: 1003408 + 3409: 1003409 + 3410: 1003410 + 3411: 1003411 + 3412: 1003412 + 3413: 1003413 + 3414: 1003414 + 3415: 1003415 + 3416: 1003416 + 3417: 1003417 + 3418: 1003418 + 3419: 1003419 + 3420: 1003420 + 3421: 1003421 + 3422: 1003422 + 3423: 1003423 + 3424: 1003424 + 3425: 1003425 + 3426: 1003426 + 3427: 1003427 + 3428: 1003428 + 3429: 1003429 + 3430: 1003430 + 3431: 1003431 + 3432: 1003432 + 3433: 1003433 + 3434: 1003434 + 3435: 1003435 + 3436: 1003436 + 3437: 1003437 + 3438: 1003438 + 3439: 1003439 + 3440: 1003440 + 3441: 1003441 + 3442: 1003442 + 3443: 1003443 + 3444: 1003444 + 3445: 1003445 + 3446: 1003446 + 3447: 1003447 + 3448: 1003448 + 3449: 1003449 + 3450: 1003450 + 3451: 1003451 + 3452: 1003452 + 3453: 1003453 + 3454: 1003454 + 3455: 1003455 + 3456: 1003456 + 3457: 1003457 + 3458: 1003458 + 3459: 1003459 + 3460: 1003460 + 3461: 1003461 + 3462: 1003462 + 3463: 1003463 + 3464: 1003464 + 3465: 1003465 + 3466: 1003466 + 3467: 1003467 + 3468: 1003468 + 3469: 1003469 + 3470: 1003470 + 3471: 1003471 + 3472: 1003472 + 3473: 1003473 + 3474: 1003474 + 3475: 1003475 + 3476: 1003476 + 3477: 1003477 + 3478: 1003478 + 3479: 1003479 + 3480: 1003480 + 3481: 1003481 + 3482: 1003482 + 3483: 1003483 + 3484: 1003484 + 3485: 1003485 + 3486: 1003486 + 3487: 1003487 + 3488: 1003488 + 3489: 1003489 + 3490: 1003490 + 3491: 1003491 + 3492: 1003492 + 3493: 1003493 + 3494: 1003494 + 3495: 1003495 + 3496: 1003496 + 3497: 1003497 + 3498: 1003498 + 3499: 1003499 + 3500: 1003500 + 3501: 1003501 + 3502: 1003502 + 3503: 1003503 + 3504: 1003504 + 3505: 1003505 + 3506: 1003506 + 3507: 1003507 + 3508: 1003508 + 3509: 1003509 + 3510: 1003510 + 3511: 1003511 + 3512: 1003512 + 3513: 1003513 + 3514: 1003514 + 3515: 1003515 + 3516: 1003516 + 3517: 1003517 + 3518: 1003518 + 3519: 1003519 + 3520: 1003520 + 3521: 1003521 + 3522: 1003522 + 3523: 1003523 + 3524: 1003524 + 3525: 1003525 + 3526: 1003526 + 3527: 1003527 + 3528: 1003528 + 3529: 1003529 + 3530: 1003530 + 3531: 1003531 + 3532: 1003532 + 3533: 1003533 + 3534: 1003534 + 3535: 1003535 + 3536: 1003536 + 3537: 1003537 + 3538: 1003538 + 3539: 1003539 + 3540: 1003540 + 3541: 1003541 + 3542: 1003542 + 3543: 1003543 + 3544: 1003544 + 3545: 1003545 + 3546: 1003546 + 3547: 1003547 + 3548: 1003548 + 3549: 1003549 + 3550: 1003550 + 3551: 1003551 + 3552: 1003552 + 3553: 1003553 + 3554: 1003554 + 3555: 1003555 + 3556: 1003556 + 3557: 1003557 + 3558: 1003558 + 3559: 1003559 + 3560: 1003560 + 3561: 1003561 + 3562: 1003562 + 3563: 1003563 + 3564: 1003564 + 3565: 1003565 + 3566: 1003566 + 3567: 1003567 + 3568: 1003568 + 3569: 1003569 + 3570: 1003570 + 3571: 1003571 + 3572: 1003572 + 3573: 1003573 + 3574: 1003574 + 3575: 1003575 + 3576: 1003576 + 3577: 1003577 + 3578: 1003578 + 3579: 1003579 + 3580: 1003580 + 3581: 1003581 + 3582: 1003582 + 3583: 1003583 + 3584: 1003584 + 3585: 1003585 + 3586: 1003586 + 3587: 1003587 + 3588: 1003588 + 3589: 1003589 + 3590: 1003590 + 3591: 1003591 + 3592: 1003592 + 3593: 1003593 + 3594: 1003594 + 3595: 1003595 + 3596: 1003596 + 3597: 1003597 + 3598: 1003598 + 3599: 1003599 + 3600: 1003600 + 3601: 1003601 + 3602: 1003602 + 3603: 1003603 + 3604: 1003604 + 3605: 1003605 + 3606: 1003606 + 3607: 1003607 + 3608: 1003608 + 3609: 1003609 + 3610: 1003610 + 3611: 1003611 + 3612: 1003612 + 3613: 1003613 + 3614: 1003614 + 3615: 1003615 + 3616: 1003616 + 3617: 1003617 + 3618: 1003618 + 3619: 1003619 + 3620: 1003620 + 3621: 1003621 + 3622: 1003622 + 3623: 1003623 + 3624: 1003624 + 3625: 1003625 + 3626: 1003626 + 3627: 1003627 + 3628: 1003628 + 3629: 1003629 + 3630: 1003630 + 3631: 1003631 + 3632: 1003632 + 3633: 1003633 + 3634: 1003634 + 3635: 1003635 + 3636: 1003636 + 3637: 1003637 + 3638: 1003638 + 3639: 1003639 + 3640: 1003640 + 3641: 1003641 + 3642: 1003642 + 3643: 1003643 + 3644: 1003644 + 3645: 1003645 + 3646: 1003646 + 3647: 1003647 + 3648: 1003648 + 3649: 1003649 + 3650: 1003650 + 3651: 1003651 + 3652: 1003652 + 3653: 1003653 + 3654: 1003654 + 3655: 1003655 + 3656: 1003656 + 3657: 1003657 + 3658: 1003658 + 3659: 1003659 + 3660: 1003660 + 3661: 1003661 + 3662: 1003662 + 3663: 1003663 + 3664: 1003664 + 3665: 1003665 + 3666: 1003666 + 3667: 1003667 + 3668: 1003668 + 3669: 1003669 + 3670: 1003670 + 3671: 1003671 + 3672: 1003672 + 3673: 1003673 + 3674: 1003674 + 3675: 1003675 + 3676: 1003676 + 3677: 1003677 + 3678: 1003678 + 3679: 1003679 + 3680: 1003680 + 3681: 1003681 + 3682: 1003682 + 3683: 1003683 + 3684: 1003684 + 3685: 1003685 + 3686: 1003686 + 3687: 1003687 + 3688: 1003688 + 3689: 1003689 + 3690: 1003690 + 3691: 1003691 + 3692: 1003692 + 3693: 1003693 + 3694: 1003694 + 3695: 1003695 + 3696: 1003696 + 3697: 1003697 + 3698: 1003698 + 3699: 1003699 + 3700: 1003700 + 3701: 1003701 + 3702: 1003702 + 3703: 1003703 + 3704: 1003704 + 3705: 1003705 + 3706: 1003706 + 3707: 1003707 + 3708: 1003708 + 3709: 1003709 + 3710: 1003710 + 3711: 1003711 + 3712: 1003712 + 3713: 1003713 + 3714: 1003714 + 3715: 1003715 + 3716: 1003716 + 3717: 1003717 + 3718: 1003718 + 3719: 1003719 + 3720: 1003720 + 3721: 1003721 + 3722: 1003722 + 3723: 1003723 + 3724: 1003724 + 3725: 1003725 + 3726: 1003726 + 3727: 1003727 + 3728: 1003728 + 3729: 1003729 + 3730: 1003730 + 3731: 1003731 + 3732: 1003732 + 3733: 1003733 + 3734: 1003734 + 3735: 1003735 + 3736: 1003736 + 3737: 1003737 + 3738: 1003738 + 3739: 1003739 + 3740: 1003740 + 3741: 1003741 + 3742: 1003742 + 3743: 1003743 + 3744: 1003744 + 3745: 1003745 + 3746: 1003746 + 3747: 1003747 + 3748: 1003748 + 3749: 1003749 + 3750: 1003750 + 3751: 1003751 + 3752: 1003752 + 3753: 1003753 + 3754: 1003754 + 3755: 1003755 + 3756: 1003756 + 3757: 1003757 + 3758: 1003758 + 3759: 1003759 + 3760: 1003760 + 3761: 1003761 + 3762: 1003762 + 3763: 1003763 + 3764: 1003764 + 3765: 1003765 + 3766: 1003766 + 3767: 1003767 + 3768: 1003768 + 3769: 1003769 + 3770: 1003770 + 3771: 1003771 + 3772: 1003772 + 3773: 1003773 + 3774: 1003774 + 3775: 1003775 + 3776: 1003776 + 3777: 1003777 + 3778: 1003778 + 3779: 1003779 + 3780: 1003780 + 3781: 1003781 + 3782: 1003782 + 3783: 1003783 + 3784: 1003784 + 3785: 1003785 + 3786: 1003786 + 3787: 1003787 + 3788: 1003788 + 3789: 1003789 + 3790: 1003790 + 3791: 1003791 + 3792: 1003792 + 3793: 1003793 + 3794: 1003794 + 3795: 1003795 + 3796: 1003796 + 3797: 1003797 + 3798: 1003798 + 3799: 1003799 + 3800: 1003800 + 3801: 1003801 + 3802: 1003802 + 3803: 1003803 + 3804: 1003804 + 3805: 1003805 + 3806: 1003806 + 3807: 1003807 + 3808: 1003808 + 3809: 1003809 + 3810: 1003810 + 3811: 1003811 + 3812: 1003812 + 3813: 1003813 + 3814: 1003814 + 3815: 1003815 + 3816: 1003816 + 3817: 1003817 + 3818: 1003818 + 3819: 1003819 + 3820: 1003820 + 3821: 1003821 + 3822: 1003822 + 3823: 1003823 + 3824: 1003824 + 3825: 1003825 + 3826: 1003826 + 3827: 1003827 + 3828: 1003828 + 3829: 1003829 + 3830: 1003830 + 3831: 1003831 + 3832: 1003832 + 3833: 1003833 + 3834: 1003834 + 3835: 1003835 + 3836: 1003836 + 3837: 1003837 + 3838: 1003838 + 3839: 1003839 + 3840: 1003840 + 3841: 1003841 + 3842: 1003842 + 3843: 1003843 + 3844: 1003844 + 3845: 1003845 + 3846: 1003846 + 3847: 1003847 + 3848: 1003848 + 3849: 1003849 + 3850: 1003850 + 3851: 1003851 + 3852: 1003852 + 3853: 1003853 + 3854: 1003854 + 3855: 1003855 + 3856: 1003856 + 3857: 1003857 + 3858: 1003858 + 3859: 1003859 + 3860: 1003860 + 3861: 1003861 + 3862: 1003862 + 3863: 1003863 + 3864: 1003864 + 3865: 1003865 + 3866: 1003866 + 3867: 1003867 + 3868: 1003868 + 3869: 1003869 + 3870: 1003870 + 3871: 1003871 + 3872: 1003872 + 3873: 1003873 + 3874: 1003874 + 3875: 1003875 + 3876: 1003876 + 3877: 1003877 + 3878: 1003878 + 3879: 1003879 + 3880: 1003880 + 3881: 1003881 + 3882: 1003882 + 3883: 1003883 + 3884: 1003884 + 3885: 1003885 + 3886: 1003886 + 3887: 1003887 + 3888: 1003888 + 3889: 1003889 + 3890: 1003890 + 3891: 1003891 + 3892: 1003892 + 3893: 1003893 + 3894: 1003894 + 3895: 1003895 + 3896: 1003896 + 3897: 1003897 + 3898: 1003898 + 3899: 1003899 + 3900: 1003900 + 3901: 1003901 + 3902: 1003902 + 3903: 1003903 + 3904: 1003904 + 3905: 1003905 + 3906: 1003906 + 3907: 1003907 + 3908: 1003908 + 3909: 1003909 + 3910: 1003910 + 3911: 1003911 + 3912: 1003912 + 3913: 1003913 + 3914: 1003914 + 3915: 1003915 + 3916: 1003916 + 3917: 1003917 + 3918: 1003918 + 3919: 1003919 + 3920: 1003920 + 3921: 1003921 + 3922: 1003922 + 3923: 1003923 + 3924: 1003924 + 3925: 1003925 + 3926: 1003926 + 3927: 1003927 + 3928: 1003928 + 3929: 1003929 + 3930: 1003930 + 3931: 1003931 + 3932: 1003932 + 3933: 1003933 + 3934: 1003934 + 3935: 1003935 + 3936: 1003936 + 3937: 1003937 + 3938: 1003938 + 3939: 1003939 + 3940: 1003940 + 3941: 1003941 + 3942: 1003942 + 3943: 1003943 + 3944: 1003944 + 3945: 1003945 + 3946: 1003946 + 3947: 1003947 + 3948: 1003948 + 3949: 1003949 + 3950: 1003950 + 3951: 1003951 + 3952: 1003952 + 3953: 1003953 + 3954: 1003954 + 3955: 1003955 + 3956: 1003956 + 3957: 1003957 + 3958: 1003958 + 3959: 1003959 + 3960: 1003960 + 3961: 1003961 + 3962: 1003962 + 3963: 1003963 + 3964: 1003964 + 3965: 1003965 + 3966: 1003966 + 3967: 1003967 + 3968: 1003968 + 3969: 1003969 + 3970: 1003970 + 3971: 1003971 + 3972: 1003972 + 3973: 1003973 + 3974: 1003974 + 3975: 1003975 + 3976: 1003976 + 3977: 1003977 + 3978: 1003978 + 3979: 1003979 + 3980: 1003980 + 3981: 1003981 + 3982: 1003982 + 3983: 1003983 + 3984: 1003984 + 3985: 1003985 + 3986: 1003986 + 3987: 1003987 + 3988: 1003988 + 3989: 1003989 + 3990: 1003990 + 3991: 1003991 + 3992: 1003992 + 3993: 1003993 + 3994: 1003994 + 3995: 1003995 + 3996: 1003996 + 3997: 1003997 + 3998: 1003998 + 3999: 1003999 + 4000: 1004000 + 4001: 1004001 + 4002: 1004002 + 4003: 1004003 + 4004: 1004004 + 4005: 1004005 + 4006: 1004006 + 4007: 1004007 + 4008: 1004008 + 4009: 1004009 + 4010: 1004010 + 4011: 1004011 + 4012: 1004012 + 4013: 1004013 + 4014: 1004014 + 4015: 1004015 + 4016: 1004016 + 4017: 1004017 + 4018: 1004018 + 4019: 1004019 + 4020: 1004020 + 4021: 1004021 + 4022: 1004022 + 4023: 1004023 + 4024: 1004024 + 4025: 1004025 + 4026: 1004026 + 4027: 1004027 + 4028: 1004028 + 4029: 1004029 + 4030: 1004030 + 4031: 1004031 + 4032: 1004032 + 4033: 1004033 + 4034: 1004034 + 4035: 1004035 + 4036: 1004036 + 4037: 1004037 + 4038: 1004038 + 4039: 1004039 + 4040: 1004040 + 4041: 1004041 + 4042: 1004042 + 4043: 1004043 + 4044: 1004044 + 4045: 1004045 + 4046: 1004046 + 4047: 1004047 + 4048: 1004048 + 4049: 1004049 + 4050: 1004050 + 4051: 1004051 + 4052: 1004052 + 4053: 1004053 + 4054: 1004054 + 4055: 1004055 + 4056: 1004056 + 4057: 1004057 + 4058: 1004058 + 4059: 1004059 + 4060: 1004060 + 4061: 1004061 + 4062: 1004062 + 4063: 1004063 + 4064: 1004064 + 4065: 1004065 + 4066: 1004066 + 4067: 1004067 + 4068: 1004068 + 4069: 1004069 + 4070: 1004070 + 4071: 1004071 + 4072: 1004072 + 4073: 1004073 + 4074: 1004074 + 4075: 1004075 + 4076: 1004076 + 4077: 1004077 + 4078: 1004078 + 4079: 1004079 + 4080: 1004080 + 4081: 1004081 + 4082: 1004082 + 4083: 1004083 + 4084: 1004084 + 4085: 1004085 + 4086: 1004086 + 4087: 1004087 + 4088: 1004088 + 4089: 1004089 + 4090: 1004090 + 4091: 1004091 + 4092: 1004092 + 4093: 1004093 + 4094: 1004094 + 4095: 1004095 + 4096: 1004096 + 4097: 1004097 + 4098: 1004098 + 4099: 1004099 + 4100: 1004100 + 4101: 1004101 + 4102: 1004102 + 4103: 1004103 + 4104: 1004104 + 4105: 1004105 + 4106: 1004106 + 4107: 1004107 + 4108: 1004108 + 4109: 1004109 + 4110: 1004110 + 4111: 1004111 + 4112: 1004112 + 4113: 1004113 + 4114: 1004114 + 4115: 1004115 + 4116: 1004116 + 4117: 1004117 + 4118: 1004118 + 4119: 1004119 + 4120: 1004120 + 4121: 1004121 + 4122: 1004122 + 4123: 1004123 + 4124: 1004124 + 4125: 1004125 + 4126: 1004126 + 4127: 1004127 + 4128: 1004128 + 4129: 1004129 + 4130: 1004130 + 4131: 1004131 + 4132: 1004132 + 4133: 1004133 + 4134: 1004134 + 4135: 1004135 + 4136: 1004136 + 4137: 1004137 + 4138: 1004138 + 4139: 1004139 + 4140: 1004140 + 4141: 1004141 + 4142: 1004142 + 4143: 1004143 + 4144: 1004144 + 4145: 1004145 + 4146: 1004146 + 4147: 1004147 + 4148: 1004148 + 4149: 1004149 + 4150: 1004150 + 4151: 1004151 + 4152: 1004152 + 4153: 1004153 + 4154: 1004154 + 4155: 1004155 + 4156: 1004156 + 4157: 1004157 + 4158: 1004158 + 4159: 1004159 + 4160: 1004160 + 4161: 1004161 + 4162: 1004162 + 4163: 1004163 + 4164: 1004164 + 4165: 1004165 + 4166: 1004166 + 4167: 1004167 + 4168: 1004168 + 4169: 1004169 + 4170: 1004170 + 4171: 1004171 + 4172: 1004172 + 4173: 1004173 + 4174: 1004174 + 4175: 1004175 + 4176: 1004176 + 4177: 1004177 + 4178: 1004178 + 4179: 1004179 + 4180: 1004180 + 4181: 1004181 + 4182: 1004182 + 4183: 1004183 + 4184: 1004184 + 4185: 1004185 + 4186: 1004186 + 4187: 1004187 + 4188: 1004188 + 4189: 1004189 + 4190: 1004190 + 4191: 1004191 + 4192: 1004192 + 4193: 1004193 + 4194: 1004194 + 4195: 1004195 + 4196: 1004196 + 4197: 1004197 + 4198: 1004198 + 4199: 1004199 + 4200: 1004200 + 4201: 1004201 + 4202: 1004202 + 4203: 1004203 + 4204: 1004204 + 4205: 1004205 + 4206: 1004206 + 4207: 1004207 + 4208: 1004208 + 4209: 1004209 + 4210: 1004210 + 4211: 1004211 + 4212: 1004212 + 4213: 1004213 + 4214: 1004214 + 4215: 1004215 + 4216: 1004216 + 4217: 1004217 + 4218: 1004218 + 4219: 1004219 + 4220: 1004220 + 4221: 1004221 + 4222: 1004222 + 4223: 1004223 + 4224: 1004224 + 4225: 1004225 + 4226: 1004226 + 4227: 1004227 + 4228: 1004228 + 4229: 1004229 + 4230: 1004230 + 4231: 1004231 + 4232: 1004232 + 4233: 1004233 + 4234: 1004234 + 4235: 1004235 + 4236: 1004236 + 4237: 1004237 + 4238: 1004238 + 4239: 1004239 + 4240: 1004240 + 4241: 1004241 + 4242: 1004242 + 4243: 1004243 + 4244: 1004244 + 4245: 1004245 + 4246: 1004246 + 4247: 1004247 + 4248: 1004248 + 4249: 1004249 + 4250: 1004250 + 4251: 1004251 + 4252: 1004252 + 4253: 1004253 + 4254: 1004254 + 4255: 1004255 + 4256: 1004256 + 4257: 1004257 + 4258: 1004258 + 4259: 1004259 + 4260: 1004260 + 4261: 1004261 + 4262: 1004262 + 4263: 1004263 + 4264: 1004264 + 4265: 1004265 + 4266: 1004266 + 4267: 1004267 + 4268: 1004268 + 4269: 1004269 + 4270: 1004270 + 4271: 1004271 + 4272: 1004272 + 4273: 1004273 + 4274: 1004274 + 4275: 1004275 + 4276: 1004276 + 4277: 1004277 + 4278: 1004278 + 4279: 1004279 + 4280: 1004280 + 4281: 1004281 + 4282: 1004282 + 4283: 1004283 + 4284: 1004284 + 4285: 1004285 + 4286: 1004286 + 4287: 1004287 + 4288: 1004288 + 4289: 1004289 + 4290: 1004290 + 4291: 1004291 + 4292: 1004292 + 4293: 1004293 + 4294: 1004294 + 4295: 1004295 + 4296: 1004296 + 4297: 1004297 + 4298: 1004298 + 4299: 1004299 + 4300: 1004300 + 4301: 1004301 + 4302: 1004302 + 4303: 1004303 + 4304: 1004304 + 4305: 1004305 + 4306: 1004306 + 4307: 1004307 + 4308: 1004308 + 4309: 1004309 + 4310: 1004310 + 4311: 1004311 + 4312: 1004312 + 4313: 1004313 + 4314: 1004314 + 4315: 1004315 + 4316: 1004316 + 4317: 1004317 + 4318: 1004318 + 4319: 1004319 + 4320: 1004320 + 4321: 1004321 + 4322: 1004322 + 4323: 1004323 + 4324: 1004324 + 4325: 1004325 + 4326: 1004326 + 4327: 1004327 + 4328: 1004328 + 4329: 1004329 + 4330: 1004330 + 4331: 1004331 + 4332: 1004332 + 4333: 1004333 + 4334: 1004334 + 4335: 1004335 + 4336: 1004336 + 4337: 1004337 + 4338: 1004338 + 4339: 1004339 + 4340: 1004340 + 4341: 1004341 + 4342: 1004342 + 4343: 1004343 + 4344: 1004344 + 4345: 1004345 + 4346: 1004346 + 4347: 1004347 + 4348: 1004348 + 4349: 1004349 + 4350: 1004350 + 4351: 1004351 + 4352: 1004352 + 4353: 1004353 + 4354: 1004354 + 4355: 1004355 + 4356: 1004356 + 4357: 1004357 + 4358: 1004358 + 4359: 1004359 + 4360: 1004360 + 4361: 1004361 + 4362: 1004362 + 4363: 1004363 + 4364: 1004364 + 4365: 1004365 + 4366: 1004366 + 4367: 1004367 + 4368: 1004368 + 4369: 1004369 + 4370: 1004370 + 4371: 1004371 + 4372: 1004372 + 4373: 1004373 + 4374: 1004374 + 4375: 1004375 + 4376: 1004376 + 4377: 1004377 + 4378: 1004378 + 4379: 1004379 + 4380: 1004380 + 4381: 1004381 + 4382: 1004382 + 4383: 1004383 + 4384: 1004384 + 4385: 1004385 + 4386: 1004386 + 4387: 1004387 + 4388: 1004388 + 4389: 1004389 + 4390: 1004390 + 4391: 1004391 + 4392: 1004392 + 4393: 1004393 + 4394: 1004394 + 4395: 1004395 + 4396: 1004396 + 4397: 1004397 + 4398: 1004398 + 4399: 1004399 + 4400: 1004400 + 4401: 1004401 + 4402: 1004402 + 4403: 1004403 + 4404: 1004404 + 4405: 1004405 + 4406: 1004406 + 4407: 1004407 + 4408: 1004408 + 4409: 1004409 + 4410: 1004410 + 4411: 1004411 + 4412: 1004412 + 4413: 1004413 + 4414: 1004414 + 4415: 1004415 + 4416: 1004416 + 4417: 1004417 + 4418: 1004418 + 4419: 1004419 + 4420: 1004420 + 4421: 1004421 + 4422: 1004422 + 4423: 1004423 + 4424: 1004424 + 4425: 1004425 + 4426: 1004426 + 4427: 1004427 + 4428: 1004428 + 4429: 1004429 + 4430: 1004430 + 4431: 1004431 + 4432: 1004432 + 4433: 1004433 + 4434: 1004434 + 4435: 1004435 + 4436: 1004436 + 4437: 1004437 + 4438: 1004438 + 4439: 1004439 + 4440: 1004440 + 4441: 1004441 + 4442: 1004442 + 4443: 1004443 + 4444: 1004444 + 4445: 1004445 + 4446: 1004446 + 4447: 1004447 + 4448: 1004448 + 4449: 1004449 + 4450: 1004450 + 4451: 1004451 + 4452: 1004452 + 4453: 1004453 + 4454: 1004454 + 4455: 1004455 + 4456: 1004456 + 4457: 1004457 + 4458: 1004458 + 4459: 1004459 + 4460: 1004460 + 4461: 1004461 + 4462: 1004462 + 4463: 1004463 + 4464: 1004464 + 4465: 1004465 + 4466: 1004466 + 4467: 1004467 + 4468: 1004468 + 4469: 1004469 + 4470: 1004470 + 4471: 1004471 + 4472: 1004472 + 4473: 1004473 + 4474: 1004474 + 4475: 1004475 + 4476: 1004476 + 4477: 1004477 + 4478: 1004478 + 4479: 1004479 + 4480: 1004480 + 4481: 1004481 + 4482: 1004482 + 4483: 1004483 + 4484: 1004484 + 4485: 1004485 + 4486: 1004486 + 4487: 1004487 + 4488: 1004488 + 4489: 1004489 + 4490: 1004490 + 4491: 1004491 + 4492: 1004492 + 4493: 1004493 + 4494: 1004494 + 4495: 1004495 + 4496: 1004496 + 4497: 1004497 + 4498: 1004498 + 4499: 1004499 + 4500: 1004500 + 4501: 1004501 + 4502: 1004502 + 4503: 1004503 + 4504: 1004504 + 4505: 1004505 + 4506: 1004506 + 4507: 1004507 + 4508: 1004508 + 4509: 1004509 + 4510: 1004510 + 4511: 1004511 + 4512: 1004512 + 4513: 1004513 + 4514: 1004514 + 4515: 1004515 + 4516: 1004516 + 4517: 1004517 + 4518: 1004518 + 4519: 1004519 + 4520: 1004520 + 4521: 1004521 + 4522: 1004522 + 4523: 1004523 + 4524: 1004524 + 4525: 1004525 + 4526: 1004526 + 4527: 1004527 + 4528: 1004528 + 4529: 1004529 + 4530: 1004530 + 4531: 1004531 + 4532: 1004532 + 4533: 1004533 + 4534: 1004534 + 4535: 1004535 + 4536: 1004536 + 4537: 1004537 + 4538: 1004538 + 4539: 1004539 + 4540: 1004540 + 4541: 1004541 + 4542: 1004542 + 4543: 1004543 + 4544: 1004544 + 4545: 1004545 + 4546: 1004546 + 4547: 1004547 + 4548: 1004548 + 4549: 1004549 + 4550: 1004550 + 4551: 1004551 + 4552: 1004552 + 4553: 1004553 + 4554: 1004554 + 4555: 1004555 + 4556: 1004556 + 4557: 1004557 + 4558: 1004558 + 4559: 1004559 + 4560: 1004560 + 4561: 1004561 + 4562: 1004562 + 4563: 1004563 + 4564: 1004564 + 4565: 1004565 + 4566: 1004566 + 4567: 1004567 + 4568: 1004568 + 4569: 1004569 + 4570: 1004570 + 4571: 1004571 + 4572: 1004572 + 4573: 1004573 + 4574: 1004574 + 4575: 1004575 + 4576: 1004576 + 4577: 1004577 + 4578: 1004578 + 4579: 1004579 + 4580: 1004580 + 4581: 1004581 + 4582: 1004582 + 4583: 1004583 + 4584: 1004584 + 4585: 1004585 + 4586: 1004586 + 4587: 1004587 + 4588: 1004588 + 4589: 1004589 + 4590: 1004590 + 4591: 1004591 + 4592: 1004592 + 4593: 1004593 + 4594: 1004594 + 4595: 1004595 + 4596: 1004596 + 4597: 1004597 + 4598: 1004598 + 4599: 1004599 + 4600: 1004600 + 4601: 1004601 + 4602: 1004602 + 4603: 1004603 + 4604: 1004604 + 4605: 1004605 + 4606: 1004606 + 4607: 1004607 + 4608: 1004608 + 4609: 1004609 + 4610: 1004610 + 4611: 1004611 + 4612: 1004612 + 4613: 1004613 + 4614: 1004614 + 4615: 1004615 + 4616: 1004616 + 4617: 1004617 + 4618: 1004618 + 4619: 1004619 + 4620: 1004620 + 4621: 1004621 + 4622: 1004622 + 4623: 1004623 + 4624: 1004624 + 4625: 1004625 + 4626: 1004626 + 4627: 1004627 + 4628: 1004628 + 4629: 1004629 + 4630: 1004630 + 4631: 1004631 + 4632: 1004632 + 4633: 1004633 + 4634: 1004634 + 4635: 1004635 + 4636: 1004636 + 4637: 1004637 + 4638: 1004638 + 4639: 1004639 + 4640: 1004640 + 4641: 1004641 + 4642: 1004642 + 4643: 1004643 + 4644: 1004644 + 4645: 1004645 + 4646: 1004646 + 4647: 1004647 + 4648: 1004648 + 4649: 1004649 + 4650: 1004650 + 4651: 1004651 + 4652: 1004652 + 4653: 1004653 + 4654: 1004654 + 4655: 1004655 + 4656: 1004656 + 4657: 1004657 + 4658: 1004658 + 4659: 1004659 + 4660: 1004660 + 4661: 1004661 + 4662: 1004662 + 4663: 1004663 + 4664: 1004664 + 4665: 1004665 + 4666: 1004666 + 4667: 1004667 + 4668: 1004668 + 4669: 1004669 + 4670: 1004670 + 4671: 1004671 + 4672: 1004672 + 4673: 1004673 + 4674: 1004674 + 4675: 1004675 + 4676: 1004676 + 4677: 1004677 + 4678: 1004678 + 4679: 1004679 + 4680: 1004680 + 4681: 1004681 + 4682: 1004682 + 4683: 1004683 + 4684: 1004684 + 4685: 1004685 + 4686: 1004686 + 4687: 1004687 + 4688: 1004688 + 4689: 1004689 + 4690: 1004690 + 4691: 1004691 + 4692: 1004692 + 4693: 1004693 + 4694: 1004694 + 4695: 1004695 + 4696: 1004696 + 4697: 1004697 + 4698: 1004698 + 4699: 1004699 + 4700: 1004700 + 4701: 1004701 + 4702: 1004702 + 4703: 1004703 + 4704: 1004704 + 4705: 1004705 + 4706: 1004706 + 4707: 1004707 + 4708: 1004708 + 4709: 1004709 + 4710: 1004710 + 4711: 1004711 + 4712: 1004712 + 4713: 1004713 + 4714: 1004714 + 4715: 1004715 + 4716: 1004716 + 4717: 1004717 + 4718: 1004718 + 4719: 1004719 + 4720: 1004720 + 4721: 1004721 + 4722: 1004722 + 4723: 1004723 + 4724: 1004724 + 4725: 1004725 + 4726: 1004726 + 4727: 1004727 + 4728: 1004728 + 4729: 1004729 + 4730: 1004730 + 4731: 1004731 + 4732: 1004732 + 4733: 1004733 + 4734: 1004734 + 4735: 1004735 + 4736: 1004736 + 4737: 1004737 + 4738: 1004738 + 4739: 1004739 + 4740: 1004740 + 4741: 1004741 + 4742: 1004742 + 4743: 1004743 + 4744: 1004744 + 4745: 1004745 + 4746: 1004746 + 4747: 1004747 + 4748: 1004748 + 4749: 1004749 + 4750: 1004750 + 4751: 1004751 + 4752: 1004752 + 4753: 1004753 + 4754: 1004754 + 4755: 1004755 + 4756: 1004756 + 4757: 1004757 + 4758: 1004758 + 4759: 1004759 + 4760: 1004760 + 4761: 1004761 + 4762: 1004762 + 4763: 1004763 + 4764: 1004764 + 4765: 1004765 + 4766: 1004766 + 4767: 1004767 + 4768: 1004768 + 4769: 1004769 + 4770: 1004770 + 4771: 1004771 + 4772: 1004772 + 4773: 1004773 + 4774: 1004774 + 4775: 1004775 + 4776: 1004776 + 4777: 1004777 + 4778: 1004778 + 4779: 1004779 + 4780: 1004780 + 4781: 1004781 + 4782: 1004782 + 4783: 1004783 + 4784: 1004784 + 4785: 1004785 + 4786: 1004786 + 4787: 1004787 + 4788: 1004788 + 4789: 1004789 + 4790: 1004790 + 4791: 1004791 + 4792: 1004792 + 4793: 1004793 + 4794: 1004794 + 4795: 1004795 + 4796: 1004796 + 4797: 1004797 + 4798: 1004798 + 4799: 1004799 + 4800: 1004800 + 4801: 1004801 + 4802: 1004802 + 4803: 1004803 + 4804: 1004804 + 4805: 1004805 + 4806: 1004806 + 4807: 1004807 + 4808: 1004808 + 4809: 1004809 + 4810: 1004810 + 4811: 1004811 + 4812: 1004812 + 4813: 1004813 + 4814: 1004814 + 4815: 1004815 + 4816: 1004816 + 4817: 1004817 + 4818: 1004818 + 4819: 1004819 + 4820: 1004820 + 4821: 1004821 + 4822: 1004822 + 4823: 1004823 + 4824: 1004824 + 4825: 1004825 + 4826: 1004826 + 4827: 1004827 + 4828: 1004828 + 4829: 1004829 + 4830: 1004830 + 4831: 1004831 + 4832: 1004832 + 4833: 1004833 + 4834: 1004834 + 4835: 1004835 + 4836: 1004836 + 4837: 1004837 + 4838: 1004838 + 4839: 1004839 + 4840: 1004840 + 4841: 1004841 + 4842: 1004842 + 4843: 1004843 + 4844: 1004844 + 4845: 1004845 + 4846: 1004846 + 4847: 1004847 + 4848: 1004848 + 4849: 1004849 + 4850: 1004850 + 4851: 1004851 + 4852: 1004852 + 4853: 1004853 + 4854: 1004854 + 4855: 1004855 + 4856: 1004856 + 4857: 1004857 + 4858: 1004858 + 4859: 1004859 + 4860: 1004860 + 4861: 1004861 + 4862: 1004862 + 4863: 1004863 + 4864: 1004864 + 4865: 1004865 + 4866: 1004866 + 4867: 1004867 + 4868: 1004868 + 4869: 1004869 + 4870: 1004870 + 4871: 1004871 + 4872: 1004872 + 4873: 1004873 + 4874: 1004874 + 4875: 1004875 + 4876: 1004876 + 4877: 1004877 + 4878: 1004878 + 4879: 1004879 + 4880: 1004880 + 4881: 1004881 + 4882: 1004882 + 4883: 1004883 + 4884: 1004884 + 4885: 1004885 + 4886: 1004886 + 4887: 1004887 + 4888: 1004888 + 4889: 1004889 + 4890: 1004890 + 4891: 1004891 + 4892: 1004892 + 4893: 1004893 + 4894: 1004894 + 4895: 1004895 + 4896: 1004896 + 4897: 1004897 + 4898: 1004898 + 4899: 1004899 + 4900: 1004900 + 4901: 1004901 + 4902: 1004902 + 4903: 1004903 + 4904: 1004904 + 4905: 1004905 + 4906: 1004906 + 4907: 1004907 + 4908: 1004908 + 4909: 1004909 + 4910: 1004910 + 4911: 1004911 + 4912: 1004912 + 4913: 1004913 + 4914: 1004914 + 4915: 1004915 + 4916: 1004916 + 4917: 1004917 + 4918: 1004918 + 4919: 1004919 + 4920: 1004920 + 4921: 1004921 + 4922: 1004922 + 4923: 1004923 + 4924: 1004924 + 4925: 1004925 + 4926: 1004926 + 4927: 1004927 + 4928: 1004928 + 4929: 1004929 + 4930: 1004930 + 4931: 1004931 + 4932: 1004932 + 4933: 1004933 + 4934: 1004934 + 4935: 1004935 + 4936: 1004936 + 4937: 1004937 + 4938: 1004938 + 4939: 1004939 + 4940: 1004940 + 4941: 1004941 + 4942: 1004942 + 4943: 1004943 + 4944: 1004944 + 4945: 1004945 + 4946: 1004946 + 4947: 1004947 + 4948: 1004948 + 4949: 1004949 + 4950: 1004950 + 4951: 1004951 + 4952: 1004952 + 4953: 1004953 + 4954: 1004954 + 4955: 1004955 + 4956: 1004956 + 4957: 1004957 + 4958: 1004958 + 4959: 1004959 + 4960: 1004960 + 4961: 1004961 + 4962: 1004962 + 4963: 1004963 + 4964: 1004964 + 4965: 1004965 + 4966: 1004966 + 4967: 1004967 + 4968: 1004968 + 4969: 1004969 + 4970: 1004970 + 4971: 1004971 + 4972: 1004972 + 4973: 1004973 + 4974: 1004974 + 4975: 1004975 + 4976: 1004976 + 4977: 1004977 + 4978: 1004978 + 4979: 1004979 + 4980: 1004980 + 4981: 1004981 + 4982: 1004982 + 4983: 1004983 + 4984: 1004984 + 4985: 1004985 + 4986: 1004986 + 4987: 1004987 + 4988: 1004988 + 4989: 1004989 + 4990: 1004990 + 4991: 1004991 + 4992: 1004992 + 4993: 1004993 + 4994: 1004994 + 4995: 1004995 + 4996: 1004996 + 4997: 1004997 + 4998: 1004998 + 4999: 1004999 + 5000: 1005000 + 5001: 1005001 + 5002: 1005002 + 5003: 1005003 + 5004: 1005004 + 5005: 1005005 + 5006: 1005006 + 5007: 1005007 + 5008: 1005008 + 5009: 1005009 + 5010: 1005010 + 5011: 1005011 + 5012: 1005012 + 5013: 1005013 + 5014: 1005014 + 5015: 1005015 + 5016: 1005016 + 5017: 1005017 + 5018: 1005018 + 5019: 1005019 + 5020: 1005020 + 5021: 1005021 + 5022: 1005022 + 5023: 1005023 + 5024: 1005024 + 5025: 1005025 + 5026: 1005026 + 5027: 1005027 + 5028: 1005028 + 5029: 1005029 + 5030: 1005030 + 5031: 1005031 + 5032: 1005032 + 5033: 1005033 + 5034: 1005034 + 5035: 1005035 + 5036: 1005036 + 5037: 1005037 + 5038: 1005038 + 5039: 1005039 + 5040: 1005040 + 5041: 1005041 + 5042: 1005042 + 5043: 1005043 + 5044: 1005044 + 5045: 1005045 + 5046: 1005046 + 5047: 1005047 + 5048: 1005048 + 5049: 1005049 + 5050: 1005050 + 5051: 1005051 + 5052: 1005052 + 5053: 1005053 + 5054: 1005054 + 5055: 1005055 + 5056: 1005056 + 5057: 1005057 + 5058: 1005058 + 5059: 1005059 + 5060: 1005060 + 5061: 1005061 + 5062: 1005062 + 5063: 1005063 + 5064: 1005064 + 5065: 1005065 + 5066: 1005066 + 5067: 1005067 + 5068: 1005068 + 5069: 1005069 + 5070: 1005070 + 5071: 1005071 + 5072: 1005072 + 5073: 1005073 + 5074: 1005074 + 5075: 1005075 + 5076: 1005076 + 5077: 1005077 + 5078: 1005078 + 5079: 1005079 + 5080: 1005080 + 5081: 1005081 + 5082: 1005082 + 5083: 1005083 + 5084: 1005084 + 5085: 1005085 + 5086: 1005086 + 5087: 1005087 + 5088: 1005088 + 5089: 1005089 + 5090: 1005090 + 5091: 1005091 + 5092: 1005092 + 5093: 1005093 + 5094: 1005094 + 5095: 1005095 + 5096: 1005096 + 5097: 1005097 + 5098: 1005098 + 5099: 1005099 + 5100: 1005100 + 5101: 1005101 + 5102: 1005102 + 5103: 1005103 + 5104: 1005104 + 5105: 1005105 + 5106: 1005106 + 5107: 1005107 + 5108: 1005108 + 5109: 1005109 + 5110: 1005110 + 5111: 1005111 + 5112: 1005112 + 5113: 1005113 + 5114: 1005114 + 5115: 1005115 + 5116: 1005116 + 5117: 1005117 + 5118: 1005118 + 5119: 1005119 + 5120: 1005120 + 5121: 1005121 + 5122: 1005122 + 5123: 1005123 + 5124: 1005124 + 5125: 1005125 + 5126: 1005126 + 5127: 1005127 + 5128: 1005128 + 5129: 1005129 + 5130: 1005130 + 5131: 1005131 + 5132: 1005132 + 5133: 1005133 + 5134: 1005134 + 5135: 1005135 + 5136: 1005136 + 5137: 1005137 + 5138: 1005138 + 5139: 1005139 + 5140: 1005140 + 5141: 1005141 + 5142: 1005142 + 5143: 1005143 + 5144: 1005144 + 5145: 1005145 + 5146: 1005146 + 5147: 1005147 + 5148: 1005148 + 5149: 1005149 + 5150: 1005150 + 5151: 1005151 + 5152: 1005152 + 5153: 1005153 + 5154: 1005154 + 5155: 1005155 + 5156: 1005156 + 5157: 1005157 + 5158: 1005158 + 5159: 1005159 + 5160: 1005160 + 5161: 1005161 + 5162: 1005162 + 5163: 1005163 + 5164: 1005164 + 5165: 1005165 + 5166: 1005166 + 5167: 1005167 + 5168: 1005168 + 5169: 1005169 + 5170: 1005170 + 5171: 1005171 + 5172: 1005172 + 5173: 1005173 + 5174: 1005174 + 5175: 1005175 + 5176: 1005176 + 5177: 1005177 + 5178: 1005178 + 5179: 1005179 + 5180: 1005180 + 5181: 1005181 + 5182: 1005182 + 5183: 1005183 + 5184: 1005184 + 5185: 1005185 + 5186: 1005186 + 5187: 1005187 + 5188: 1005188 + 5189: 1005189 + 5190: 1005190 + 5191: 1005191 + 5192: 1005192 + 5193: 1005193 + 5194: 1005194 + 5195: 1005195 + 5196: 1005196 + 5197: 1005197 + 5198: 1005198 + 5199: 1005199 + 5200: 1005200 + 5201: 1005201 + 5202: 1005202 + 5203: 1005203 + 5204: 1005204 + 5205: 1005205 + 5206: 1005206 + 5207: 1005207 + 5208: 1005208 + 5209: 1005209 + 5210: 1005210 + 5211: 1005211 + 5212: 1005212 + 5213: 1005213 + 5214: 1005214 + 5215: 1005215 + 5216: 1005216 + 5217: 1005217 + 5218: 1005218 + 5219: 1005219 + 5220: 1005220 + 5221: 1005221 + 5222: 1005222 + 5223: 1005223 + 5224: 1005224 + 5225: 1005225 + 5226: 1005226 + 5227: 1005227 + 5228: 1005228 + 5229: 1005229 + 5230: 1005230 + 5231: 1005231 + 5232: 1005232 + 5233: 1005233 + 5234: 1005234 + 5235: 1005235 + 5236: 1005236 + 5237: 1005237 + 5238: 1005238 + 5239: 1005239 + 5240: 1005240 + 5241: 1005241 + 5242: 1005242 + 5243: 1005243 + 5244: 1005244 + 5245: 1005245 + 5246: 1005246 + 5247: 1005247 + 5248: 1005248 + 5249: 1005249 + 5250: 1005250 + 5251: 1005251 + 5252: 1005252 + 5253: 1005253 + 5254: 1005254 + 5255: 1005255 + 5256: 1005256 + 5257: 1005257 + 5258: 1005258 + 5259: 1005259 + 5260: 1005260 + 5261: 1005261 + 5262: 1005262 + 5263: 1005263 + 5264: 1005264 + 5265: 1005265 + 5266: 1005266 + 5267: 1005267 + 5268: 1005268 + 5269: 1005269 + 5270: 1005270 + 5271: 1005271 + 5272: 1005272 + 5273: 1005273 + 5274: 1005274 + 5275: 1005275 + 5276: 1005276 + 5277: 1005277 + 5278: 1005278 + 5279: 1005279 + 5280: 1005280 + 5281: 1005281 + 5282: 1005282 + 5283: 1005283 + 5284: 1005284 + 5285: 1005285 + 5286: 1005286 + 5287: 1005287 + 5288: 1005288 + 5289: 1005289 + 5290: 1005290 + 5291: 1005291 + 5292: 1005292 + 5293: 1005293 + 5294: 1005294 + 5295: 1005295 + 5296: 1005296 + 5297: 1005297 + 5298: 1005298 + 5299: 1005299 + 5300: 1005300 + 5301: 1005301 + 5302: 1005302 + 5303: 1005303 + 5304: 1005304 + 5305: 1005305 + 5306: 1005306 + 5307: 1005307 + 5308: 1005308 + 5309: 1005309 + 5310: 1005310 + 5311: 1005311 + 5312: 1005312 + 5313: 1005313 + 5314: 1005314 + 5315: 1005315 + 5316: 1005316 + 5317: 1005317 + 5318: 1005318 + 5319: 1005319 + 5320: 1005320 + 5321: 1005321 + 5322: 1005322 + 5323: 1005323 + 5324: 1005324 + 5325: 1005325 + 5326: 1005326 + 5327: 1005327 + 5328: 1005328 + 5329: 1005329 + 5330: 1005330 + 5331: 1005331 + 5332: 1005332 + 5333: 1005333 + 5334: 1005334 + 5335: 1005335 + 5336: 1005336 + 5337: 1005337 + 5338: 1005338 + 5339: 1005339 + 5340: 1005340 + 5341: 1005341 + 5342: 1005342 + 5343: 1005343 + 5344: 1005344 + 5345: 1005345 + 5346: 1005346 + 5347: 1005347 + 5348: 1005348 + 5349: 1005349 + 5350: 1005350 + 5351: 1005351 + 5352: 1005352 + 5353: 1005353 + 5354: 1005354 + 5355: 1005355 + 5356: 1005356 + 5357: 1005357 + 5358: 1005358 + 5359: 1005359 + 5360: 1005360 + 5361: 1005361 + 5362: 1005362 + 5363: 1005363 + 5364: 1005364 + 5365: 1005365 + 5366: 1005366 + 5367: 1005367 + 5368: 1005368 + 5369: 1005369 + 5370: 1005370 + 5371: 1005371 + 5372: 1005372 + 5373: 1005373 + 5374: 1005374 + 5375: 1005375 + 5376: 1005376 + 5377: 1005377 + 5378: 1005378 + 5379: 1005379 + 5380: 1005380 + 5381: 1005381 + 5382: 1005382 + 5383: 1005383 + 5384: 1005384 + 5385: 1005385 + 5386: 1005386 + 5387: 1005387 + 5388: 1005388 + 5389: 1005389 + 5390: 1005390 + 5391: 1005391 + 5392: 1005392 + 5393: 1005393 + 5394: 1005394 + 5395: 1005395 + 5396: 1005396 + 5397: 1005397 + 5398: 1005398 + 5399: 1005399 + 5400: 1005400 + 5401: 1005401 + 5402: 1005402 + 5403: 1005403 + 5404: 1005404 + 5405: 1005405 + 5406: 1005406 + 5407: 1005407 + 5408: 1005408 + 5409: 1005409 + 5410: 1005410 + 5411: 1005411 + 5412: 1005412 + 5413: 1005413 + 5414: 1005414 + 5415: 1005415 + 5416: 1005416 + 5417: 1005417 + 5418: 1005418 + 5419: 1005419 + 5420: 1005420 + 5421: 1005421 + 5422: 1005422 + 5423: 1005423 + 5424: 1005424 + 5425: 1005425 + 5426: 1005426 + 5427: 1005427 + 5428: 1005428 + 5429: 1005429 + 5430: 1005430 + 5431: 1005431 + 5432: 1005432 + 5433: 1005433 + 5434: 1005434 + 5435: 1005435 + 5436: 1005436 + 5437: 1005437 + 5438: 1005438 + 5439: 1005439 + 5440: 1005440 + 5441: 1005441 + 5442: 1005442 + 5443: 1005443 + 5444: 1005444 + 5445: 1005445 + 5446: 1005446 + 5447: 1005447 + 5448: 1005448 + 5449: 1005449 + 5450: 1005450 + 5451: 1005451 + 5452: 1005452 + 5453: 1005453 + 5454: 1005454 + 5455: 1005455 + 5456: 1005456 + 5457: 1005457 + 5458: 1005458 + 5459: 1005459 + 5460: 1005460 + 5461: 1005461 + 5462: 1005462 + 5463: 1005463 + 5464: 1005464 + 5465: 1005465 + 5466: 1005466 + 5467: 1005467 + 5468: 1005468 + 5469: 1005469 + 5470: 1005470 + 5471: 1005471 + 5472: 1005472 + 5473: 1005473 + 5474: 1005474 + 5475: 1005475 + 5476: 1005476 + 5477: 1005477 + 5478: 1005478 + 5479: 1005479 + 5480: 1005480 + 5481: 1005481 + 5482: 1005482 + 5483: 1005483 + 5484: 1005484 + 5485: 1005485 + 5486: 1005486 + 5487: 1005487 + 5488: 1005488 + 5489: 1005489 + 5490: 1005490 + 5491: 1005491 + 5492: 1005492 + 5493: 1005493 + 5494: 1005494 + 5495: 1005495 + 5496: 1005496 + 5497: 1005497 + 5498: 1005498 + 5499: 1005499 + 5500: 1005500 + 5501: 1005501 + 5502: 1005502 + 5503: 1005503 + 5504: 1005504 + 5505: 1005505 + 5506: 1005506 + 5507: 1005507 + 5508: 1005508 + 5509: 1005509 + 5510: 1005510 + 5511: 1005511 + 5512: 1005512 + 5513: 1005513 + 5514: 1005514 + 5515: 1005515 + 5516: 1005516 + 5517: 1005517 + 5518: 1005518 + 5519: 1005519 + 5520: 1005520 + 5521: 1005521 + 5522: 1005522 + 5523: 1005523 + 5524: 1005524 + 5525: 1005525 + 5526: 1005526 + 5527: 1005527 + 5528: 1005528 + 5529: 1005529 + 5530: 1005530 + 5531: 1005531 + 5532: 1005532 + 5533: 1005533 + 5534: 1005534 + 5535: 1005535 + 5536: 1005536 + 5537: 1005537 + 5538: 1005538 + 5539: 1005539 + 5540: 1005540 + 5541: 1005541 + 5542: 1005542 + 5543: 1005543 + 5544: 1005544 + 5545: 1005545 + 5546: 1005546 + 5547: 1005547 + 5548: 1005548 + 5549: 1005549 + 5550: 1005550 + 5551: 1005551 + 5552: 1005552 + 5553: 1005553 + 5554: 1005554 + 5555: 1005555 + 5556: 1005556 + 5557: 1005557 + 5558: 1005558 + 5559: 1005559 + 5560: 1005560 + 5561: 1005561 + 5562: 1005562 + 5563: 1005563 + 5564: 1005564 + 5565: 1005565 + 5566: 1005566 + 5567: 1005567 + 5568: 1005568 + 5569: 1005569 + 5570: 1005570 + 5571: 1005571 + 5572: 1005572 + 5573: 1005573 + 5574: 1005574 + 5575: 1005575 + 5576: 1005576 + 5577: 1005577 + 5578: 1005578 + 5579: 1005579 + 5580: 1005580 + 5581: 1005581 + 5582: 1005582 + 5583: 1005583 + 5584: 1005584 + 5585: 1005585 + 5586: 1005586 + 5587: 1005587 + 5588: 1005588 + 5589: 1005589 + 5590: 1005590 + 5591: 1005591 + 5592: 1005592 + 5593: 1005593 + 5594: 1005594 + 5595: 1005595 + 5596: 1005596 + 5597: 1005597 + 5598: 1005598 + 5599: 1005599 + 5600: 1005600 + 5601: 1005601 + 5602: 1005602 + 5603: 1005603 + 5604: 1005604 + 5605: 1005605 + 5606: 1005606 + 5607: 1005607 + 5608: 1005608 + 5609: 1005609 + 5610: 1005610 + 5611: 1005611 + 5612: 1005612 + 5613: 1005613 + 5614: 1005614 + 5615: 1005615 + 5616: 1005616 + 5617: 1005617 + 5618: 1005618 + 5619: 1005619 + 5620: 1005620 + 5621: 1005621 + 5622: 1005622 + 5623: 1005623 + 5624: 1005624 + 5625: 1005625 + 5626: 1005626 + 5627: 1005627 + 5628: 1005628 + 5629: 1005629 + 5630: 1005630 + 5631: 1005631 + 5632: 1005632 + 5633: 1005633 + 5634: 1005634 + 5635: 1005635 + 5636: 1005636 + 5637: 1005637 + 5638: 1005638 + 5639: 1005639 + 5640: 1005640 + 5641: 1005641 + 5642: 1005642 + 5643: 1005643 + 5644: 1005644 + 5645: 1005645 + 5646: 1005646 + 5647: 1005647 + 5648: 1005648 + 5649: 1005649 + 5650: 1005650 + 5651: 1005651 + 5652: 1005652 + 5653: 1005653 + 5654: 1005654 + 5655: 1005655 + 5656: 1005656 + 5657: 1005657 + 5658: 1005658 + 5659: 1005659 + 5660: 1005660 + 5661: 1005661 + 5662: 1005662 + 5663: 1005663 + 5664: 1005664 + 5665: 1005665 + 5666: 1005666 + 5667: 1005667 + 5668: 1005668 + 5669: 1005669 + 5670: 1005670 + 5671: 1005671 + 5672: 1005672 + 5673: 1005673 + 5674: 1005674 + 5675: 1005675 + 5676: 1005676 + 5677: 1005677 + 5678: 1005678 + 5679: 1005679 + 5680: 1005680 + 5681: 1005681 + 5682: 1005682 + 5683: 1005683 + 5684: 1005684 + 5685: 1005685 + 5686: 1005686 + 5687: 1005687 + 5688: 1005688 + 5689: 1005689 + 5690: 1005690 + 5691: 1005691 + 5692: 1005692 + 5693: 1005693 + 5694: 1005694 + 5695: 1005695 + 5696: 1005696 + 5697: 1005697 + 5698: 1005698 + 5699: 1005699 + 5700: 1005700 + 5701: 1005701 + 5702: 1005702 + 5703: 1005703 + 5704: 1005704 + 5705: 1005705 + 5706: 1005706 + 5707: 1005707 + 5708: 1005708 + 5709: 1005709 + 5710: 1005710 + 5711: 1005711 + 5712: 1005712 + 5713: 1005713 + 5714: 1005714 + 5715: 1005715 + 5716: 1005716 + 5717: 1005717 + 5718: 1005718 + 5719: 1005719 + 5720: 1005720 + 5721: 1005721 + 5722: 1005722 + 5723: 1005723 + 5724: 1005724 + 5725: 1005725 + 5726: 1005726 + 5727: 1005727 + 5728: 1005728 + 5729: 1005729 + 5730: 1005730 + 5731: 1005731 + 5732: 1005732 + 5733: 1005733 + 5734: 1005734 + 5735: 1005735 + 5736: 1005736 + 5737: 1005737 + 5738: 1005738 + 5739: 1005739 + 5740: 1005740 + 5741: 1005741 + 5742: 1005742 + 5743: 1005743 + 5744: 1005744 + 5745: 1005745 + 5746: 1005746 + 5747: 1005747 + 5748: 1005748 + 5749: 1005749 + 5750: 1005750 + 5751: 1005751 + 5752: 1005752 + 5753: 1005753 + 5754: 1005754 + 5755: 1005755 + 5756: 1005756 + 5757: 1005757 + 5758: 1005758 + 5759: 1005759 + 5760: 1005760 + 5761: 1005761 + 5762: 1005762 + 5763: 1005763 + 5764: 1005764 + 5765: 1005765 + 5766: 1005766 + 5767: 1005767 + 5768: 1005768 + 5769: 1005769 + 5770: 1005770 + 5771: 1005771 + 5772: 1005772 + 5773: 1005773 + 5774: 1005774 + 5775: 1005775 + 5776: 1005776 + 5777: 1005777 + 5778: 1005778 + 5779: 1005779 + 5780: 1005780 + 5781: 1005781 + 5782: 1005782 + 5783: 1005783 + 5784: 1005784 + 5785: 1005785 + 5786: 1005786 + 5787: 1005787 + 5788: 1005788 + 5789: 1005789 + 5790: 1005790 + 5791: 1005791 + 5792: 1005792 + 5793: 1005793 + 5794: 1005794 + 5795: 1005795 + 5796: 1005796 + 5797: 1005797 + 5798: 1005798 + 5799: 1005799 + 5800: 1005800 + 5801: 1005801 + 5802: 1005802 + 5803: 1005803 + 5804: 1005804 + 5805: 1005805 + 5806: 1005806 + 5807: 1005807 + 5808: 1005808 + 5809: 1005809 + 5810: 1005810 + 5811: 1005811 + 5812: 1005812 + 5813: 1005813 + 5814: 1005814 + 5815: 1005815 + 5816: 1005816 + 5817: 1005817 + 5818: 1005818 + 5819: 1005819 + 5820: 1005820 + 5821: 1005821 + 5822: 1005822 + 5823: 1005823 + 5824: 1005824 + 5825: 1005825 + 5826: 1005826 + 5827: 1005827 + 5828: 1005828 + 5829: 1005829 + 5830: 1005830 + 5831: 1005831 + 5832: 1005832 + 5833: 1005833 + 5834: 1005834 + 5835: 1005835 + 5836: 1005836 + 5837: 1005837 + 5838: 1005838 + 5839: 1005839 + 5840: 1005840 + 5841: 1005841 + 5842: 1005842 + 5843: 1005843 + 5844: 1005844 + 5845: 1005845 + 5846: 1005846 + 5847: 1005847 + 5848: 1005848 + 5849: 1005849 + 5850: 1005850 + 5851: 1005851 + 5852: 1005852 + 5853: 1005853 + 5854: 1005854 + 5855: 1005855 + 5856: 1005856 + 5857: 1005857 + 5858: 1005858 + 5859: 1005859 + 5860: 1005860 + 5861: 1005861 + 5862: 1005862 + 5863: 1005863 + 5864: 1005864 + 5865: 1005865 + 5866: 1005866 + 5867: 1005867 + 5868: 1005868 + 5869: 1005869 + 5870: 1005870 + 5871: 1005871 + 5872: 1005872 + 5873: 1005873 + 5874: 1005874 + 5875: 1005875 + 5876: 1005876 + 5877: 1005877 + 5878: 1005878 + 5879: 1005879 + 5880: 1005880 + 5881: 1005881 + 5882: 1005882 + 5883: 1005883 + 5884: 1005884 + 5885: 1005885 + 5886: 1005886 + 5887: 1005887 + 5888: 1005888 + 5889: 1005889 + 5890: 1005890 + 5891: 1005891 + 5892: 1005892 + 5893: 1005893 + 5894: 1005894 + 5895: 1005895 + 5896: 1005896 + 5897: 1005897 + 5898: 1005898 + 5899: 1005899 + 5900: 1005900 + 5901: 1005901 + 5902: 1005902 + 5903: 1005903 + 5904: 1005904 + 5905: 1005905 + 5906: 1005906 + 5907: 1005907 + 5908: 1005908 + 5909: 1005909 + 5910: 1005910 + 5911: 1005911 + 5912: 1005912 + 5913: 1005913 + 5914: 1005914 + 5915: 1005915 + 5916: 1005916 + 5917: 1005917 + 5918: 1005918 + 5919: 1005919 + 5920: 1005920 + 5921: 1005921 + 5922: 1005922 + 5923: 1005923 + 5924: 1005924 + 5925: 1005925 + 5926: 1005926 + 5927: 1005927 + 5928: 1005928 + 5929: 1005929 + 5930: 1005930 + 5931: 1005931 + 5932: 1005932 + 5933: 1005933 + 5934: 1005934 + 5935: 1005935 + 5936: 1005936 + 5937: 1005937 + 5938: 1005938 + 5939: 1005939 + 5940: 1005940 + 5941: 1005941 + 5942: 1005942 + 5943: 1005943 + 5944: 1005944 + 5945: 1005945 + 5946: 1005946 + 5947: 1005947 + 5948: 1005948 + 5949: 1005949 + 5950: 1005950 + 5951: 1005951 + 5952: 1005952 + 5953: 1005953 + 5954: 1005954 + 5955: 1005955 + 5956: 1005956 + 5957: 1005957 + 5958: 1005958 + 5959: 1005959 + 5960: 1005960 + 5961: 1005961 + 5962: 1005962 + 5963: 1005963 + 5964: 1005964 + 5965: 1005965 + 5966: 1005966 + 5967: 1005967 + 5968: 1005968 + 5969: 1005969 + 5970: 1005970 + 5971: 1005971 + 5972: 1005972 + 5973: 1005973 + 5974: 1005974 + 5975: 1005975 + 5976: 1005976 + 5977: 1005977 + 5978: 1005978 + 5979: 1005979 + 5980: 1005980 + 5981: 1005981 + 5982: 1005982 + 5983: 1005983 + 5984: 1005984 + 5985: 1005985 + 5986: 1005986 + 5987: 1005987 + 5988: 1005988 + 5989: 1005989 + 5990: 1005990 + 5991: 1005991 + 5992: 1005992 + 5993: 1005993 + 5994: 1005994 + 5995: 1005995 + 5996: 1005996 + 5997: 1005997 + 5998: 1005998 + 5999: 1005999 + 6000: 1006000 + 6001: 1006001 + 6002: 1006002 + 6003: 1006003 + 6004: 1006004 + 6005: 1006005 + 6006: 1006006 + 6007: 1006007 + 6008: 1006008 + 6009: 1006009 + 6010: 1006010 + 6011: 1006011 + 6012: 1006012 + 6013: 1006013 + 6014: 1006014 + 6015: 1006015 + 6016: 1006016 + 6017: 1006017 + 6018: 1006018 + 6019: 1006019 + 6020: 1006020 + 6021: 1006021 + 6022: 1006022 + 6023: 1006023 + 6024: 1006024 + 6025: 1006025 + 6026: 1006026 + 6027: 1006027 + 6028: 1006028 + 6029: 1006029 + 6030: 1006030 + 6031: 1006031 + 6032: 1006032 + 6033: 1006033 + 6034: 1006034 + 6035: 1006035 + 6036: 1006036 + 6037: 1006037 + 6038: 1006038 + 6039: 1006039 + 6040: 1006040 + 6041: 1006041 + 6042: 1006042 + 6043: 1006043 + 6044: 1006044 + 6045: 1006045 + 6046: 1006046 + 6047: 1006047 + 6048: 1006048 + 6049: 1006049 + 6050: 1006050 + 6051: 1006051 + 6052: 1006052 + 6053: 1006053 + 6054: 1006054 + 6055: 1006055 + 6056: 1006056 + 6057: 1006057 + 6058: 1006058 + 6059: 1006059 + 6060: 1006060 + 6061: 1006061 + 6062: 1006062 + 6063: 1006063 + 6064: 1006064 + 6065: 1006065 + 6066: 1006066 + 6067: 1006067 + 6068: 1006068 + 6069: 1006069 + 6070: 1006070 + 6071: 1006071 + 6072: 1006072 + 6073: 1006073 + 6074: 1006074 + 6075: 1006075 + 6076: 1006076 + 6077: 1006077 + 6078: 1006078 + 6079: 1006079 + 6080: 1006080 + 6081: 1006081 + 6082: 1006082 + 6083: 1006083 + 6084: 1006084 + 6085: 1006085 + 6086: 1006086 + 6087: 1006087 + 6088: 1006088 + 6089: 1006089 + 6090: 1006090 + 6091: 1006091 + 6092: 1006092 + 6093: 1006093 + 6094: 1006094 + 6095: 1006095 + 6096: 1006096 + 6097: 1006097 + 6098: 1006098 + 6099: 1006099 + 6100: 1006100 + 6101: 1006101 + 6102: 1006102 + 6103: 1006103 + 6104: 1006104 + 6105: 1006105 + 6106: 1006106 + 6107: 1006107 + 6108: 1006108 + 6109: 1006109 + 6110: 1006110 + 6111: 1006111 + 6112: 1006112 + 6113: 1006113 + 6114: 1006114 + 6115: 1006115 + 6116: 1006116 + 6117: 1006117 + 6118: 1006118 + 6119: 1006119 + 6120: 1006120 + 6121: 1006121 + 6122: 1006122 + 6123: 1006123 + 6124: 1006124 + 6125: 1006125 + 6126: 1006126 + 6127: 1006127 + 6128: 1006128 + 6129: 1006129 + 6130: 1006130 + 6131: 1006131 + 6132: 1006132 + 6133: 1006133 + 6134: 1006134 + 6135: 1006135 + 6136: 1006136 + 6137: 1006137 + 6138: 1006138 + 6139: 1006139 + 6140: 1006140 + 6141: 1006141 + 6142: 1006142 + 6143: 1006143 + 6144: 1006144 + 6145: 1006145 + 6146: 1006146 + 6147: 1006147 + 6148: 1006148 + 6149: 1006149 + 6150: 1006150 + 6151: 1006151 + 6152: 1006152 + 6153: 1006153 + 6154: 1006154 + 6155: 1006155 + 6156: 1006156 + 6157: 1006157 + 6158: 1006158 + 6159: 1006159 + 6160: 1006160 + 6161: 1006161 + 6162: 1006162 + 6163: 1006163 + 6164: 1006164 + 6165: 1006165 + 6166: 1006166 + 6167: 1006167 + 6168: 1006168 + 6169: 1006169 + 6170: 1006170 + 6171: 1006171 + 6172: 1006172 + 6173: 1006173 + 6174: 1006174 + 6175: 1006175 + 6176: 1006176 + 6177: 1006177 + 6178: 1006178 + 6179: 1006179 + 6180: 1006180 + 6181: 1006181 + 6182: 1006182 + 6183: 1006183 + 6184: 1006184 + 6185: 1006185 + 6186: 1006186 + 6187: 1006187 + 6188: 1006188 + 6189: 1006189 + 6190: 1006190 + 6191: 1006191 + 6192: 1006192 + 6193: 1006193 + 6194: 1006194 + 6195: 1006195 + 6196: 1006196 + 6197: 1006197 + 6198: 1006198 + 6199: 1006199 + 6200: 1006200 + 6201: 1006201 + 6202: 1006202 + 6203: 1006203 + 6204: 1006204 + 6205: 1006205 + 6206: 1006206 + 6207: 1006207 + 6208: 1006208 + 6209: 1006209 + 6210: 1006210 + 6211: 1006211 + 6212: 1006212 + 6213: 1006213 + 6214: 1006214 + 6215: 1006215 + 6216: 1006216 + 6217: 1006217 + 6218: 1006218 + 6219: 1006219 + 6220: 1006220 + 6221: 1006221 + 6222: 1006222 + 6223: 1006223 + 6224: 1006224 + 6225: 1006225 + 6226: 1006226 + 6227: 1006227 + 6228: 1006228 + 6229: 1006229 + 6230: 1006230 + 6231: 1006231 + 6232: 1006232 + 6233: 1006233 + 6234: 1006234 + 6235: 1006235 + 6236: 1006236 + 6237: 1006237 + 6238: 1006238 + 6239: 1006239 + 6240: 1006240 + 6241: 1006241 + 6242: 1006242 + 6243: 1006243 + 6244: 1006244 + 6245: 1006245 + 6246: 1006246 + 6247: 1006247 + 6248: 1006248 + 6249: 1006249 + 6250: 1006250 + 6251: 1006251 + 6252: 1006252 + 6253: 1006253 + 6254: 1006254 + 6255: 1006255 + 6256: 1006256 + 6257: 1006257 + 6258: 1006258 + 6259: 1006259 + 6260: 1006260 + 6261: 1006261 + 6262: 1006262 + 6263: 1006263 + 6264: 1006264 + 6265: 1006265 + 6266: 1006266 + 6267: 1006267 + 6268: 1006268 + 6269: 1006269 + 6270: 1006270 + 6271: 1006271 + 6272: 1006272 + 6273: 1006273 + 6274: 1006274 + 6275: 1006275 + 6276: 1006276 + 6277: 1006277 + 6278: 1006278 + 6279: 1006279 + 6280: 1006280 + 6281: 1006281 + 6282: 1006282 + 6283: 1006283 + 6284: 1006284 + 6285: 1006285 + 6286: 1006286 + 6287: 1006287 + 6288: 1006288 + 6289: 1006289 + 6290: 1006290 + 6291: 1006291 + 6292: 1006292 + 6293: 1006293 + 6294: 1006294 + 6295: 1006295 + 6296: 1006296 + 6297: 1006297 + 6298: 1006298 + 6299: 1006299 + 6300: 1006300 + 6301: 1006301 + 6302: 1006302 + 6303: 1006303 + 6304: 1006304 + 6305: 1006305 + 6306: 1006306 + 6307: 1006307 + 6308: 1006308 + 6309: 1006309 + 6310: 1006310 + 6311: 1006311 + 6312: 1006312 + 6313: 1006313 + 6314: 1006314 + 6315: 1006315 + 6316: 1006316 + 6317: 1006317 + 6318: 1006318 + 6319: 1006319 + 6320: 1006320 + 6321: 1006321 + 6322: 1006322 + 6323: 1006323 + 6324: 1006324 + 6325: 1006325 + 6326: 1006326 + 6327: 1006327 + 6328: 1006328 + 6329: 1006329 + 6330: 1006330 + 6331: 1006331 + 6332: 1006332 + 6333: 1006333 + 6334: 1006334 + 6335: 1006335 + 6336: 1006336 + 6337: 1006337 + 6338: 1006338 + 6339: 1006339 + 6340: 1006340 + 6341: 1006341 + 6342: 1006342 + 6343: 1006343 + 6344: 1006344 + 6345: 1006345 + 6346: 1006346 + 6347: 1006347 + 6348: 1006348 + 6349: 1006349 + 6350: 1006350 + 6351: 1006351 + 6352: 1006352 + 6353: 1006353 + 6354: 1006354 + 6355: 1006355 + 6356: 1006356 + 6357: 1006357 + 6358: 1006358 + 6359: 1006359 + 6360: 1006360 + 6361: 1006361 + 6362: 1006362 + 6363: 1006363 + 6364: 1006364 + 6365: 1006365 + 6366: 1006366 + 6367: 1006367 + 6368: 1006368 + 6369: 1006369 + 6370: 1006370 + 6371: 1006371 + 6372: 1006372 + 6373: 1006373 + 6374: 1006374 + 6375: 1006375 + 6376: 1006376 + 6377: 1006377 + 6378: 1006378 + 6379: 1006379 + 6380: 1006380 + 6381: 1006381 + 6382: 1006382 + 6383: 1006383 + 6384: 1006384 + 6385: 1006385 + 6386: 1006386 + 6387: 1006387 + 6388: 1006388 + 6389: 1006389 + 6390: 1006390 + 6391: 1006391 + 6392: 1006392 + 6393: 1006393 + 6394: 1006394 + 6395: 1006395 + 6396: 1006396 + 6397: 1006397 + 6398: 1006398 + 6399: 1006399 + 6400: 1006400 + 6401: 1006401 + 6402: 1006402 + 6403: 1006403 + 6404: 1006404 + 6405: 1006405 + 6406: 1006406 + 6407: 1006407 + 6408: 1006408 + 6409: 1006409 + 6410: 1006410 + 6411: 1006411 + 6412: 1006412 + 6413: 1006413 + 6414: 1006414 + 6415: 1006415 + 6416: 1006416 + 6417: 1006417 + 6418: 1006418 + 6419: 1006419 + 6420: 1006420 + 6421: 1006421 + 6422: 1006422 + 6423: 1006423 + 6424: 1006424 + 6425: 1006425 + 6426: 1006426 + 6427: 1006427 + 6428: 1006428 + 6429: 1006429 + 6430: 1006430 + 6431: 1006431 + 6432: 1006432 + 6433: 1006433 + 6434: 1006434 + 6435: 1006435 + 6436: 1006436 + 6437: 1006437 + 6438: 1006438 + 6439: 1006439 + 6440: 1006440 + 6441: 1006441 + 6442: 1006442 + 6443: 1006443 + 6444: 1006444 + 6445: 1006445 + 6446: 1006446 + 6447: 1006447 + 6448: 1006448 + 6449: 1006449 + 6450: 1006450 + 6451: 1006451 + 6452: 1006452 + 6453: 1006453 + 6454: 1006454 + 6455: 1006455 + 6456: 1006456 + 6457: 1006457 + 6458: 1006458 + 6459: 1006459 + 6460: 1006460 + 6461: 1006461 + 6462: 1006462 + 6463: 1006463 + 6464: 1006464 + 6465: 1006465 + 6466: 1006466 + 6467: 1006467 + 6468: 1006468 + 6469: 1006469 + 6470: 1006470 + 6471: 1006471 + 6472: 1006472 + 6473: 1006473 + 6474: 1006474 + 6475: 1006475 + 6476: 1006476 + 6477: 1006477 + 6478: 1006478 + 6479: 1006479 + 6480: 1006480 + 6481: 1006481 + 6482: 1006482 + 6483: 1006483 + 6484: 1006484 + 6485: 1006485 + 6486: 1006486 + 6487: 1006487 + 6488: 1006488 + 6489: 1006489 + 6490: 1006490 + 6491: 1006491 + 6492: 1006492 + 6493: 1006493 + 6494: 1006494 + 6495: 1006495 + 6496: 1006496 + 6497: 1006497 + 6498: 1006498 + 6499: 1006499 + 6500: 1006500 + 6501: 1006501 + 6502: 1006502 + 6503: 1006503 + 6504: 1006504 + 6505: 1006505 + 6506: 1006506 + 6507: 1006507 + 6508: 1006508 + 6509: 1006509 + 6510: 1006510 + 6511: 1006511 + 6512: 1006512 + 6513: 1006513 + 6514: 1006514 + 6515: 1006515 + 6516: 1006516 + 6517: 1006517 + 6518: 1006518 + 6519: 1006519 + 6520: 1006520 + 6521: 1006521 + 6522: 1006522 + 6523: 1006523 + 6524: 1006524 + 6525: 1006525 + 6526: 1006526 + 6527: 1006527 + 6528: 1006528 + 6529: 1006529 + 6530: 1006530 + 6531: 1006531 + 6532: 1006532 + 6533: 1006533 + 6534: 1006534 + 6535: 1006535 + 6536: 1006536 + 6537: 1006537 + 6538: 1006538 + 6539: 1006539 + 6540: 1006540 + 6541: 1006541 + 6542: 1006542 + 6543: 1006543 + 6544: 1006544 + 6545: 1006545 + 6546: 1006546 + 6547: 1006547 + 6548: 1006548 + 6549: 1006549 + 6550: 1006550 + 6551: 1006551 + 6552: 1006552 + 6553: 1006553 + 6554: 1006554 + 6555: 1006555 + 6556: 1006556 + 6557: 1006557 + 6558: 1006558 + 6559: 1006559 + 6560: 1006560 + 6561: 1006561 + 6562: 1006562 + 6563: 1006563 + 6564: 1006564 + 6565: 1006565 + 6566: 1006566 + 6567: 1006567 + 6568: 1006568 + 6569: 1006569 + 6570: 1006570 + 6571: 1006571 + 6572: 1006572 + 6573: 1006573 + 6574: 1006574 + 6575: 1006575 + 6576: 1006576 + 6577: 1006577 + 6578: 1006578 + 6579: 1006579 + 6580: 1006580 + 6581: 1006581 + 6582: 1006582 + 6583: 1006583 + 6584: 1006584 + 6585: 1006585 + 6586: 1006586 + 6587: 1006587 + 6588: 1006588 + 6589: 1006589 + 6590: 1006590 + 6591: 1006591 + 6592: 1006592 + 6593: 1006593 + 6594: 1006594 + 6595: 1006595 + 6596: 1006596 + 6597: 1006597 + 6598: 1006598 + 6599: 1006599 + 6600: 1006600 + 6601: 1006601 + 6602: 1006602 + 6603: 1006603 + 6604: 1006604 + 6605: 1006605 + 6606: 1006606 + 6607: 1006607 + 6608: 1006608 + 6609: 1006609 + 6610: 1006610 + 6611: 1006611 + 6612: 1006612 + 6613: 1006613 + 6614: 1006614 + 6615: 1006615 + 6616: 1006616 + 6617: 1006617 + 6618: 1006618 + 6619: 1006619 + 6620: 1006620 + 6621: 1006621 + 6622: 1006622 + 6623: 1006623 + 6624: 1006624 + 6625: 1006625 + 6626: 1006626 + 6627: 1006627 + 6628: 1006628 + 6629: 1006629 + 6630: 1006630 + 6631: 1006631 + 6632: 1006632 + 6633: 1006633 + 6634: 1006634 + 6635: 1006635 + 6636: 1006636 + 6637: 1006637 + 6638: 1006638 + 6639: 1006639 + 6640: 1006640 + 6641: 1006641 + 6642: 1006642 + 6643: 1006643 + 6644: 1006644 + 6645: 1006645 + 6646: 1006646 + 6647: 1006647 + 6648: 1006648 + 6649: 1006649 + 6650: 1006650 + 6651: 1006651 + 6652: 1006652 + 6653: 1006653 + 6654: 1006654 + 6655: 1006655 + 6656: 1006656 + 6657: 1006657 + 6658: 1006658 + 6659: 1006659 + 6660: 1006660 + 6661: 1006661 + 6662: 1006662 + 6663: 1006663 + 6664: 1006664 + 6665: 1006665 + 6666: 1006666 + 6667: 1006667 + 6668: 1006668 + 6669: 1006669 + 6670: 1006670 + 6671: 1006671 + 6672: 1006672 + 6673: 1006673 + 6674: 1006674 + 6675: 1006675 + 6676: 1006676 + 6677: 1006677 + 6678: 1006678 + 6679: 1006679 + 6680: 1006680 + 6681: 1006681 + 6682: 1006682 + 6683: 1006683 + 6684: 1006684 + 6685: 1006685 + 6686: 1006686 + 6687: 1006687 + 6688: 1006688 + 6689: 1006689 + 6690: 1006690 + 6691: 1006691 + 6692: 1006692 + 6693: 1006693 + 6694: 1006694 + 6695: 1006695 + 6696: 1006696 + 6697: 1006697 + 6698: 1006698 + 6699: 1006699 + 6700: 1006700 + 6701: 1006701 + 6702: 1006702 + 6703: 1006703 + 6704: 1006704 + 6705: 1006705 + 6706: 1006706 + 6707: 1006707 + 6708: 1006708 + 6709: 1006709 + 6710: 1006710 + 6711: 1006711 + 6712: 1006712 + 6713: 1006713 + 6714: 1006714 + 6715: 1006715 + 6716: 1006716 + 6717: 1006717 + 6718: 1006718 + 6719: 1006719 + 6720: 1006720 + 6721: 1006721 + 6722: 1006722 + 6723: 1006723 + 6724: 1006724 + 6725: 1006725 + 6726: 1006726 + 6727: 1006727 + 6728: 1006728 + 6729: 1006729 + 6730: 1006730 + 6731: 1006731 + 6732: 1006732 + 6733: 1006733 + 6734: 1006734 + 6735: 1006735 + 6736: 1006736 + 6737: 1006737 + 6738: 1006738 + 6739: 1006739 + 6740: 1006740 + 6741: 1006741 + 6742: 1006742 + 6743: 1006743 + 6744: 1006744 + 6745: 1006745 + 6746: 1006746 + 6747: 1006747 + 6748: 1006748 + 6749: 1006749 + 6750: 1006750 + 6751: 1006751 + 6752: 1006752 + 6753: 1006753 + 6754: 1006754 + 6755: 1006755 + 6756: 1006756 + 6757: 1006757 + 6758: 1006758 + 6759: 1006759 + 6760: 1006760 + 6761: 1006761 + 6762: 1006762 + 6763: 1006763 + 6764: 1006764 + 6765: 1006765 + 6766: 1006766 + 6767: 1006767 + 6768: 1006768 + 6769: 1006769 + 6770: 1006770 + 6771: 1006771 + 6772: 1006772 + 6773: 1006773 + 6774: 1006774 + 6775: 1006775 + 6776: 1006776 + 6777: 1006777 + 6778: 1006778 + 6779: 1006779 + 6780: 1006780 + 6781: 1006781 + 6782: 1006782 + 6783: 1006783 + 6784: 1006784 + 6785: 1006785 + 6786: 1006786 + 6787: 1006787 + 6788: 1006788 + 6789: 1006789 + 6790: 1006790 + 6791: 1006791 + 6792: 1006792 + 6793: 1006793 + 6794: 1006794 + 6795: 1006795 + 6796: 1006796 + 6797: 1006797 + 6798: 1006798 + 6799: 1006799 + 6800: 1006800 + 6801: 1006801 + 6802: 1006802 + 6803: 1006803 + 6804: 1006804 + 6805: 1006805 + 6806: 1006806 + 6807: 1006807 + 6808: 1006808 + 6809: 1006809 + 6810: 1006810 + 6811: 1006811 + 6812: 1006812 + 6813: 1006813 + 6814: 1006814 + 6815: 1006815 + 6816: 1006816 + 6817: 1006817 + 6818: 1006818 + 6819: 1006819 + 6820: 1006820 + 6821: 1006821 + 6822: 1006822 + 6823: 1006823 + 6824: 1006824 + 6825: 1006825 + 6826: 1006826 + 6827: 1006827 + 6828: 1006828 + 6829: 1006829 + 6830: 1006830 + 6831: 1006831 + 6832: 1006832 + 6833: 1006833 + 6834: 1006834 + 6835: 1006835 + 6836: 1006836 + 6837: 1006837 + 6838: 1006838 + 6839: 1006839 + 6840: 1006840 + 6841: 1006841 + 6842: 1006842 + 6843: 1006843 + 6844: 1006844 + 6845: 1006845 + 6846: 1006846 + 6847: 1006847 + 6848: 1006848 + 6849: 1006849 + 6850: 1006850 + 6851: 1006851 + 6852: 1006852 + 6853: 1006853 + 6854: 1006854 + 6855: 1006855 + 6856: 1006856 + 6857: 1006857 + 6858: 1006858 + 6859: 1006859 + 6860: 1006860 + 6861: 1006861 + 6862: 1006862 + 6863: 1006863 + 6864: 1006864 + 6865: 1006865 + 6866: 1006866 + 6867: 1006867 + 6868: 1006868 + 6869: 1006869 + 6870: 1006870 + 6871: 1006871 + 6872: 1006872 + 6873: 1006873 + 6874: 1006874 + 6875: 1006875 + 6876: 1006876 + 6877: 1006877 + 6878: 1006878 + 6879: 1006879 + 6880: 1006880 + 6881: 1006881 + 6882: 1006882 + 6883: 1006883 + 6884: 1006884 + 6885: 1006885 + 6886: 1006886 + 6887: 1006887 + 6888: 1006888 + 6889: 1006889 + 6890: 1006890 + 6891: 1006891 + 6892: 1006892 + 6893: 1006893 + 6894: 1006894 + 6895: 1006895 + 6896: 1006896 + 6897: 1006897 + 6898: 1006898 + 6899: 1006899 + 6900: 1006900 + 6901: 1006901 + 6902: 1006902 + 6903: 1006903 + 6904: 1006904 + 6905: 1006905 + 6906: 1006906 + 6907: 1006907 + 6908: 1006908 + 6909: 1006909 + 6910: 1006910 + 6911: 1006911 + 6912: 1006912 + 6913: 1006913 + 6914: 1006914 + 6915: 1006915 + 6916: 1006916 + 6917: 1006917 + 6918: 1006918 + 6919: 1006919 + 6920: 1006920 + 6921: 1006921 + 6922: 1006922 + 6923: 1006923 + 6924: 1006924 + 6925: 1006925 + 6926: 1006926 + 6927: 1006927 + 6928: 1006928 + 6929: 1006929 + 6930: 1006930 + 6931: 1006931 + 6932: 1006932 + 6933: 1006933 + 6934: 1006934 + 6935: 1006935 + 6936: 1006936 + 6937: 1006937 + 6938: 1006938 + 6939: 1006939 + 6940: 1006940 + 6941: 1006941 + 6942: 1006942 + 6943: 1006943 + 6944: 1006944 + 6945: 1006945 + 6946: 1006946 + 6947: 1006947 + 6948: 1006948 + 6949: 1006949 + 6950: 1006950 + 6951: 1006951 + 6952: 1006952 + 6953: 1006953 + 6954: 1006954 + 6955: 1006955 + 6956: 1006956 + 6957: 1006957 + 6958: 1006958 + 6959: 1006959 + 6960: 1006960 + 6961: 1006961 + 6962: 1006962 + 6963: 1006963 + 6964: 1006964 + 6965: 1006965 + 6966: 1006966 + 6967: 1006967 + 6968: 1006968 + 6969: 1006969 + 6970: 1006970 + 6971: 1006971 + 6972: 1006972 + 6973: 1006973 + 6974: 1006974 + 6975: 1006975 + 6976: 1006976 + 6977: 1006977 + 6978: 1006978 + 6979: 1006979 + 6980: 1006980 + 6981: 1006981 + 6982: 1006982 + 6983: 1006983 + 6984: 1006984 + 6985: 1006985 + 6986: 1006986 + 6987: 1006987 + 6988: 1006988 + 6989: 1006989 + 6990: 1006990 + 6991: 1006991 + 6992: 1006992 + 6993: 1006993 + 6994: 1006994 + 6995: 1006995 + 6996: 1006996 + 6997: 1006997 + 6998: 1006998 + 6999: 1006999 + 7000: 1007000 + 7001: 1007001 + 7002: 1007002 + 7003: 1007003 + 7004: 1007004 + 7005: 1007005 + 7006: 1007006 + 7007: 1007007 + 7008: 1007008 + 7009: 1007009 + 7010: 1007010 + 7011: 1007011 + 7012: 1007012 + 7013: 1007013 + 7014: 1007014 + 7015: 1007015 + 7016: 1007016 + 7017: 1007017 + 7018: 1007018 + 7019: 1007019 + 7020: 1007020 + 7021: 1007021 + 7022: 1007022 + 7023: 1007023 + 7024: 1007024 + 7025: 1007025 + 7026: 1007026 + 7027: 1007027 + 7028: 1007028 + 7029: 1007029 + 7030: 1007030 + 7031: 1007031 + 7032: 1007032 + 7033: 1007033 + 7034: 1007034 + 7035: 1007035 + 7036: 1007036 + 7037: 1007037 + 7038: 1007038 + 7039: 1007039 + 7040: 1007040 + 7041: 1007041 + 7042: 1007042 + 7043: 1007043 + 7044: 1007044 + 7045: 1007045 + 7046: 1007046 + 7047: 1007047 + 7048: 1007048 + 7049: 1007049 + 7050: 1007050 + 7051: 1007051 + 7052: 1007052 + 7053: 1007053 + 7054: 1007054 + 7055: 1007055 + 7056: 1007056 + 7057: 1007057 + 7058: 1007058 + 7059: 1007059 + 7060: 1007060 + 7061: 1007061 + 7062: 1007062 + 7063: 1007063 + 7064: 1007064 + 7065: 1007065 + 7066: 1007066 + 7067: 1007067 + 7068: 1007068 + 7069: 1007069 + 7070: 1007070 + 7071: 1007071 + 7072: 1007072 + 7073: 1007073 + 7074: 1007074 + 7075: 1007075 + 7076: 1007076 + 7077: 1007077 + 7078: 1007078 + 7079: 1007079 + 7080: 1007080 + 7081: 1007081 + 7082: 1007082 + 7083: 1007083 + 7084: 1007084 + 7085: 1007085 + 7086: 1007086 + 7087: 1007087 + 7088: 1007088 + 7089: 1007089 + 7090: 1007090 + 7091: 1007091 + 7092: 1007092 + 7093: 1007093 + 7094: 1007094 + 7095: 1007095 + 7096: 1007096 + 7097: 1007097 + 7098: 1007098 + 7099: 1007099 + 7100: 1007100 + 7101: 1007101 + 7102: 1007102 + 7103: 1007103 + 7104: 1007104 + 7105: 1007105 + 7106: 1007106 + 7107: 1007107 + 7108: 1007108 + 7109: 1007109 + 7110: 1007110 + 7111: 1007111 + 7112: 1007112 + 7113: 1007113 + 7114: 1007114 + 7115: 1007115 + 7116: 1007116 + 7117: 1007117 + 7118: 1007118 + 7119: 1007119 + 7120: 1007120 + 7121: 1007121 + 7122: 1007122 + 7123: 1007123 + 7124: 1007124 + 7125: 1007125 + 7126: 1007126 + 7127: 1007127 + 7128: 1007128 + 7129: 1007129 + 7130: 1007130 + 7131: 1007131 + 7132: 1007132 + 7133: 1007133 + 7134: 1007134 + 7135: 1007135 + 7136: 1007136 + 7137: 1007137 + 7138: 1007138 + 7139: 1007139 + 7140: 1007140 + 7141: 1007141 + 7142: 1007142 + 7143: 1007143 + 7144: 1007144 + 7145: 1007145 + 7146: 1007146 + 7147: 1007147 + 7148: 1007148 + 7149: 1007149 + 7150: 1007150 + 7151: 1007151 + 7152: 1007152 + 7153: 1007153 + 7154: 1007154 + 7155: 1007155 + 7156: 1007156 + 7157: 1007157 + 7158: 1007158 + 7159: 1007159 + 7160: 1007160 + 7161: 1007161 + 7162: 1007162 + 7163: 1007163 + 7164: 1007164 + 7165: 1007165 + 7166: 1007166 + 7167: 1007167 + 7168: 1007168 + 7169: 1007169 + 7170: 1007170 + 7171: 1007171 + 7172: 1007172 + 7173: 1007173 + 7174: 1007174 + 7175: 1007175 + 7176: 1007176 + 7177: 1007177 + 7178: 1007178 + 7179: 1007179 + 7180: 1007180 + 7181: 1007181 + 7182: 1007182 + 7183: 1007183 + 7184: 1007184 + 7185: 1007185 + 7186: 1007186 + 7187: 1007187 + 7188: 1007188 + 7189: 1007189 + 7190: 1007190 + 7191: 1007191 + 7192: 1007192 + 7193: 1007193 + 7194: 1007194 + 7195: 1007195 + 7196: 1007196 + 7197: 1007197 + 7198: 1007198 + 7199: 1007199 + 7200: 1007200 + 7201: 1007201 + 7202: 1007202 + 7203: 1007203 + 7204: 1007204 + 7205: 1007205 + 7206: 1007206 + 7207: 1007207 + 7208: 1007208 + 7209: 1007209 + 7210: 1007210 + 7211: 1007211 + 7212: 1007212 + 7213: 1007213 + 7214: 1007214 + 7215: 1007215 + 7216: 1007216 + 7217: 1007217 + 7218: 1007218 + 7219: 1007219 + 7220: 1007220 + 7221: 1007221 + 7222: 1007222 + 7223: 1007223 + 7224: 1007224 + 7225: 1007225 + 7226: 1007226 + 7227: 1007227 + 7228: 1007228 + 7229: 1007229 + 7230: 1007230 + 7231: 1007231 + 7232: 1007232 + 7233: 1007233 + 7234: 1007234 + 7235: 1007235 + 7236: 1007236 + 7237: 1007237 + 7238: 1007238 + 7239: 1007239 + 7240: 1007240 + 7241: 1007241 + 7242: 1007242 + 7243: 1007243 + 7244: 1007244 + 7245: 1007245 + 7246: 1007246 + 7247: 1007247 + 7248: 1007248 + 7249: 1007249 + 7250: 1007250 + 7251: 1007251 + 7252: 1007252 + 7253: 1007253 + 7254: 1007254 + 7255: 1007255 + 7256: 1007256 + 7257: 1007257 + 7258: 1007258 + 7259: 1007259 + 7260: 1007260 + 7261: 1007261 + 7262: 1007262 + 7263: 1007263 + 7264: 1007264 + 7265: 1007265 + 7266: 1007266 + 7267: 1007267 + 7268: 1007268 + 7269: 1007269 + 7270: 1007270 + 7271: 1007271 + 7272: 1007272 + 7273: 1007273 + 7274: 1007274 + 7275: 1007275 + 7276: 1007276 + 7277: 1007277 + 7278: 1007278 + 7279: 1007279 + 7280: 1007280 + 7281: 1007281 + 7282: 1007282 + 7283: 1007283 + 7284: 1007284 + 7285: 1007285 + 7286: 1007286 + 7287: 1007287 + 7288: 1007288 + 7289: 1007289 + 7290: 1007290 + 7291: 1007291 + 7292: 1007292 + 7293: 1007293 + 7294: 1007294 + 7295: 1007295 + 7296: 1007296 + 7297: 1007297 + 7298: 1007298 + 7299: 1007299 + 7300: 1007300 + 7301: 1007301 + 7302: 1007302 + 7303: 1007303 + 7304: 1007304 + 7305: 1007305 + 7306: 1007306 + 7307: 1007307 + 7308: 1007308 + 7309: 1007309 + 7310: 1007310 + 7311: 1007311 + 7312: 1007312 + 7313: 1007313 + 7314: 1007314 + 7315: 1007315 + 7316: 1007316 + 7317: 1007317 + 7318: 1007318 + 7319: 1007319 + 7320: 1007320 + 7321: 1007321 + 7322: 1007322 + 7323: 1007323 + 7324: 1007324 + 7325: 1007325 + 7326: 1007326 + 7327: 1007327 + 7328: 1007328 + 7329: 1007329 + 7330: 1007330 + 7331: 1007331 + 7332: 1007332 + 7333: 1007333 + 7334: 1007334 + 7335: 1007335 + 7336: 1007336 + 7337: 1007337 + 7338: 1007338 + 7339: 1007339 + 7340: 1007340 + 7341: 1007341 + 7342: 1007342 + 7343: 1007343 + 7344: 1007344 + 7345: 1007345 + 7346: 1007346 + 7347: 1007347 + 7348: 1007348 + 7349: 1007349 + 7350: 1007350 + 7351: 1007351 + 7352: 1007352 + 7353: 1007353 + 7354: 1007354 + 7355: 1007355 + 7356: 1007356 + 7357: 1007357 + 7358: 1007358 + 7359: 1007359 + 7360: 1007360 + 7361: 1007361 + 7362: 1007362 + 7363: 1007363 + 7364: 1007364 + 7365: 1007365 + 7366: 1007366 + 7367: 1007367 + 7368: 1007368 + 7369: 1007369 + 7370: 1007370 + 7371: 1007371 + 7372: 1007372 + 7373: 1007373 + 7374: 1007374 + 7375: 1007375 + 7376: 1007376 + 7377: 1007377 + 7378: 1007378 + 7379: 1007379 + 7380: 1007380 + 7381: 1007381 + 7382: 1007382 + 7383: 1007383 + 7384: 1007384 + 7385: 1007385 + 7386: 1007386 + 7387: 1007387 + 7388: 1007388 + 7389: 1007389 + 7390: 1007390 + 7391: 1007391 + 7392: 1007392 + 7393: 1007393 + 7394: 1007394 + 7395: 1007395 + 7396: 1007396 + 7397: 1007397 + 7398: 1007398 + 7399: 1007399 + 7400: 1007400 + 7401: 1007401 + 7402: 1007402 + 7403: 1007403 + 7404: 1007404 + 7405: 1007405 + 7406: 1007406 + 7407: 1007407 + 7408: 1007408 + 7409: 1007409 + 7410: 1007410 + 7411: 1007411 + 7412: 1007412 + 7413: 1007413 + 7414: 1007414 + 7415: 1007415 + 7416: 1007416 + 7417: 1007417 + 7418: 1007418 + 7419: 1007419 + 7420: 1007420 + 7421: 1007421 + 7422: 1007422 + 7423: 1007423 + 7424: 1007424 + 7425: 1007425 + 7426: 1007426 + 7427: 1007427 + 7428: 1007428 + 7429: 1007429 + 7430: 1007430 + 7431: 1007431 + 7432: 1007432 + 7433: 1007433 + 7434: 1007434 + 7435: 1007435 + 7436: 1007436 + 7437: 1007437 + 7438: 1007438 + 7439: 1007439 + 7440: 1007440 + 7441: 1007441 + 7442: 1007442 + 7443: 1007443 + 7444: 1007444 + 7445: 1007445 + 7446: 1007446 + 7447: 1007447 + 7448: 1007448 + 7449: 1007449 + 7450: 1007450 + 7451: 1007451 + 7452: 1007452 + 7453: 1007453 + 7454: 1007454 + 7455: 1007455 + 7456: 1007456 + 7457: 1007457 + 7458: 1007458 + 7459: 1007459 + 7460: 1007460 + 7461: 1007461 + 7462: 1007462 + 7463: 1007463 + 7464: 1007464 + 7465: 1007465 + 7466: 1007466 + 7467: 1007467 + 7468: 1007468 + 7469: 1007469 + 7470: 1007470 + 7471: 1007471 + 7472: 1007472 + 7473: 1007473 + 7474: 1007474 + 7475: 1007475 + 7476: 1007476 + 7477: 1007477 + 7478: 1007478 + 7479: 1007479 + 7480: 1007480 + 7481: 1007481 + 7482: 1007482 + 7483: 1007483 + 7484: 1007484 + 7485: 1007485 + 7486: 1007486 + 7487: 1007487 + 7488: 1007488 + 7489: 1007489 + 7490: 1007490 + 7491: 1007491 + 7492: 1007492 + 7493: 1007493 + 7494: 1007494 + 7495: 1007495 + 7496: 1007496 + 7497: 1007497 + 7498: 1007498 + 7499: 1007499 + 7500: 1007500 + 7501: 1007501 + 7502: 1007502 + 7503: 1007503 + 7504: 1007504 + 7505: 1007505 + 7506: 1007506 + 7507: 1007507 + 7508: 1007508 + 7509: 1007509 + 7510: 1007510 + 7511: 1007511 + 7512: 1007512 + 7513: 1007513 + 7514: 1007514 + 7515: 1007515 + 7516: 1007516 + 7517: 1007517 + 7518: 1007518 + 7519: 1007519 + 7520: 1007520 + 7521: 1007521 + 7522: 1007522 + 7523: 1007523 + 7524: 1007524 + 7525: 1007525 + 7526: 1007526 + 7527: 1007527 + 7528: 1007528 + 7529: 1007529 + 7530: 1007530 + 7531: 1007531 + 7532: 1007532 + 7533: 1007533 + 7534: 1007534 + 7535: 1007535 + 7536: 1007536 + 7537: 1007537 + 7538: 1007538 + 7539: 1007539 + 7540: 1007540 + 7541: 1007541 + 7542: 1007542 + 7543: 1007543 + 7544: 1007544 + 7545: 1007545 + 7546: 1007546 + 7547: 1007547 + 7548: 1007548 + 7549: 1007549 + 7550: 1007550 + 7551: 1007551 + 7552: 1007552 + 7553: 1007553 + 7554: 1007554 + 7555: 1007555 + 7556: 1007556 + 7557: 1007557 + 7558: 1007558 + 7559: 1007559 + 7560: 1007560 + 7561: 1007561 + 7562: 1007562 + 7563: 1007563 + 7564: 1007564 + 7565: 1007565 + 7566: 1007566 + 7567: 1007567 + 7568: 1007568 + 7569: 1007569 + 7570: 1007570 + 7571: 1007571 + 7572: 1007572 + 7573: 1007573 + 7574: 1007574 + 7575: 1007575 + 7576: 1007576 + 7577: 1007577 + 7578: 1007578 + 7579: 1007579 + 7580: 1007580 + 7581: 1007581 + 7582: 1007582 + 7583: 1007583 + 7584: 1007584 + 7585: 1007585 + 7586: 1007586 + 7587: 1007587 + 7588: 1007588 + 7589: 1007589 + 7590: 1007590 + 7591: 1007591 + 7592: 1007592 + 7593: 1007593 + 7594: 1007594 + 7595: 1007595 + 7596: 1007596 + 7597: 1007597 + 7598: 1007598 + 7599: 1007599 + 7600: 1007600 + 7601: 1007601 + 7602: 1007602 + 7603: 1007603 + 7604: 1007604 + 7605: 1007605 + 7606: 1007606 + 7607: 1007607 + 7608: 1007608 + 7609: 1007609 + 7610: 1007610 + 7611: 1007611 + 7612: 1007612 + 7613: 1007613 + 7614: 1007614 + 7615: 1007615 + 7616: 1007616 + 7617: 1007617 + 7618: 1007618 + 7619: 1007619 + 7620: 1007620 + 7621: 1007621 + 7622: 1007622 + 7623: 1007623 + 7624: 1007624 + 7625: 1007625 + 7626: 1007626 + 7627: 1007627 + 7628: 1007628 + 7629: 1007629 + 7630: 1007630 + 7631: 1007631 + 7632: 1007632 + 7633: 1007633 + 7634: 1007634 + 7635: 1007635 + 7636: 1007636 + 7637: 1007637 + 7638: 1007638 + 7639: 1007639 + 7640: 1007640 + 7641: 1007641 + 7642: 1007642 + 7643: 1007643 + 7644: 1007644 + 7645: 1007645 + 7646: 1007646 + 7647: 1007647 + 7648: 1007648 + 7649: 1007649 + 7650: 1007650 + 7651: 1007651 + 7652: 1007652 + 7653: 1007653 + 7654: 1007654 + 7655: 1007655 + 7656: 1007656 + 7657: 1007657 + 7658: 1007658 + 7659: 1007659 + 7660: 1007660 + 7661: 1007661 + 7662: 1007662 + 7663: 1007663 + 7664: 1007664 + 7665: 1007665 + 7666: 1007666 + 7667: 1007667 + 7668: 1007668 + 7669: 1007669 + 7670: 1007670 + 7671: 1007671 + 7672: 1007672 + 7673: 1007673 + 7674: 1007674 + 7675: 1007675 + 7676: 1007676 + 7677: 1007677 + 7678: 1007678 + 7679: 1007679 + 7680: 1007680 + 7681: 1007681 + 7682: 1007682 + 7683: 1007683 + 7684: 1007684 + 7685: 1007685 + 7686: 1007686 + 7687: 1007687 + 7688: 1007688 + 7689: 1007689 + 7690: 1007690 + 7691: 1007691 + 7692: 1007692 + 7693: 1007693 + 7694: 1007694 + 7695: 1007695 + 7696: 1007696 + 7697: 1007697 + 7698: 1007698 + 7699: 1007699 + 7700: 1007700 + 7701: 1007701 + 7702: 1007702 + 7703: 1007703 + 7704: 1007704 + 7705: 1007705 + 7706: 1007706 + 7707: 1007707 + 7708: 1007708 + 7709: 1007709 + 7710: 1007710 + 7711: 1007711 + 7712: 1007712 + 7713: 1007713 + 7714: 1007714 + 7715: 1007715 + 7716: 1007716 + 7717: 1007717 + 7718: 1007718 + 7719: 1007719 + 7720: 1007720 + 7721: 1007721 + 7722: 1007722 + 7723: 1007723 + 7724: 1007724 + 7725: 1007725 + 7726: 1007726 + 7727: 1007727 + 7728: 1007728 + 7729: 1007729 + 7730: 1007730 + 7731: 1007731 + 7732: 1007732 + 7733: 1007733 + 7734: 1007734 + 7735: 1007735 + 7736: 1007736 + 7737: 1007737 + 7738: 1007738 + 7739: 1007739 + 7740: 1007740 + 7741: 1007741 + 7742: 1007742 + 7743: 1007743 + 7744: 1007744 + 7745: 1007745 + 7746: 1007746 + 7747: 1007747 + 7748: 1007748 + 7749: 1007749 + 7750: 1007750 + 7751: 1007751 + 7752: 1007752 + 7753: 1007753 + 7754: 1007754 + 7755: 1007755 + 7756: 1007756 + 7757: 1007757 + 7758: 1007758 + 7759: 1007759 + 7760: 1007760 + 7761: 1007761 + 7762: 1007762 + 7763: 1007763 + 7764: 1007764 + 7765: 1007765 + 7766: 1007766 + 7767: 1007767 + 7768: 1007768 + 7769: 1007769 + 7770: 1007770 + 7771: 1007771 + 7772: 1007772 + 7773: 1007773 + 7774: 1007774 + 7775: 1007775 + 7776: 1007776 + 7777: 1007777 + 7778: 1007778 + 7779: 1007779 + 7780: 1007780 + 7781: 1007781 + 7782: 1007782 + 7783: 1007783 + 7784: 1007784 + 7785: 1007785 + 7786: 1007786 + 7787: 1007787 + 7788: 1007788 + 7789: 1007789 + 7790: 1007790 + 7791: 1007791 + 7792: 1007792 + 7793: 1007793 + 7794: 1007794 + 7795: 1007795 + 7796: 1007796 + 7797: 1007797 + 7798: 1007798 + 7799: 1007799 + 7800: 1007800 + 7801: 1007801 + 7802: 1007802 + 7803: 1007803 + 7804: 1007804 + 7805: 1007805 + 7806: 1007806 + 7807: 1007807 + 7808: 1007808 + 7809: 1007809 + 7810: 1007810 + 7811: 1007811 + 7812: 1007812 + 7813: 1007813 + 7814: 1007814 + 7815: 1007815 + 7816: 1007816 + 7817: 1007817 + 7818: 1007818 + 7819: 1007819 + 7820: 1007820 + 7821: 1007821 + 7822: 1007822 + 7823: 1007823 + 7824: 1007824 + 7825: 1007825 + 7826: 1007826 + 7827: 1007827 + 7828: 1007828 + 7829: 1007829 + 7830: 1007830 + 7831: 1007831 + 7832: 1007832 + 7833: 1007833 + 7834: 1007834 + 7835: 1007835 + 7836: 1007836 + 7837: 1007837 + 7838: 1007838 + 7839: 1007839 + 7840: 1007840 + 7841: 1007841 + 7842: 1007842 + 7843: 1007843 + 7844: 1007844 + 7845: 1007845 + 7846: 1007846 + 7847: 1007847 + 7848: 1007848 + 7849: 1007849 + 7850: 1007850 + 7851: 1007851 + 7852: 1007852 + 7853: 1007853 + 7854: 1007854 + 7855: 1007855 + 7856: 1007856 + 7857: 1007857 + 7858: 1007858 + 7859: 1007859 + 7860: 1007860 + 7861: 1007861 + 7862: 1007862 + 7863: 1007863 + 7864: 1007864 + 7865: 1007865 + 7866: 1007866 + 7867: 1007867 + 7868: 1007868 + 7869: 1007869 + 7870: 1007870 + 7871: 1007871 + 7872: 1007872 + 7873: 1007873 + 7874: 1007874 + 7875: 1007875 + 7876: 1007876 + 7877: 1007877 + 7878: 1007878 + 7879: 1007879 + 7880: 1007880 + 7881: 1007881 + 7882: 1007882 + 7883: 1007883 + 7884: 1007884 + 7885: 1007885 + 7886: 1007886 + 7887: 1007887 + 7888: 1007888 + 7889: 1007889 + 7890: 1007890 + 7891: 1007891 + 7892: 1007892 + 7893: 1007893 + 7894: 1007894 + 7895: 1007895 + 7896: 1007896 + 7897: 1007897 + 7898: 1007898 + 7899: 1007899 + 7900: 1007900 + 7901: 1007901 + 7902: 1007902 + 7903: 1007903 + 7904: 1007904 + 7905: 1007905 + 7906: 1007906 + 7907: 1007907 + 7908: 1007908 + 7909: 1007909 + 7910: 1007910 + 7911: 1007911 + 7912: 1007912 + 7913: 1007913 + 7914: 1007914 + 7915: 1007915 + 7916: 1007916 + 7917: 1007917 + 7918: 1007918 + 7919: 1007919 + 7920: 1007920 + 7921: 1007921 + 7922: 1007922 + 7923: 1007923 + 7924: 1007924 + 7925: 1007925 + 7926: 1007926 + 7927: 1007927 + 7928: 1007928 + 7929: 1007929 + 7930: 1007930 + 7931: 1007931 + 7932: 1007932 + 7933: 1007933 + 7934: 1007934 + 7935: 1007935 + 7936: 1007936 + 7937: 1007937 + 7938: 1007938 + 7939: 1007939 + 7940: 1007940 + 7941: 1007941 + 7942: 1007942 + 7943: 1007943 + 7944: 1007944 + 7945: 1007945 + 7946: 1007946 + 7947: 1007947 + 7948: 1007948 + 7949: 1007949 + 7950: 1007950 + 7951: 1007951 + 7952: 1007952 + 7953: 1007953 + 7954: 1007954 + 7955: 1007955 + 7956: 1007956 + 7957: 1007957 + 7958: 1007958 + 7959: 1007959 + 7960: 1007960 + 7961: 1007961 + 7962: 1007962 + 7963: 1007963 + 7964: 1007964 + 7965: 1007965 + 7966: 1007966 + 7967: 1007967 + 7968: 1007968 + 7969: 1007969 + 7970: 1007970 + 7971: 1007971 + 7972: 1007972 + 7973: 1007973 + 7974: 1007974 + 7975: 1007975 + 7976: 1007976 + 7977: 1007977 + 7978: 1007978 + 7979: 1007979 + 7980: 1007980 + 7981: 1007981 + 7982: 1007982 + 7983: 1007983 + 7984: 1007984 + 7985: 1007985 + 7986: 1007986 + 7987: 1007987 + 7988: 1007988 + 7989: 1007989 + 7990: 1007990 + 7991: 1007991 + 7992: 1007992 + 7993: 1007993 + 7994: 1007994 + 7995: 1007995 + 7996: 1007996 + 7997: 1007997 + 7998: 1007998 + 7999: 1007999 + 8000: 1008000 + 8001: 1008001 + 8002: 1008002 + 8003: 1008003 + 8004: 1008004 + 8005: 1008005 + 8006: 1008006 + 8007: 1008007 + 8008: 1008008 + 8009: 1008009 + 8010: 1008010 + 8011: 1008011 + 8012: 1008012 + 8013: 1008013 + 8014: 1008014 + 8015: 1008015 + 8016: 1008016 + 8017: 1008017 + 8018: 1008018 + 8019: 1008019 + 8020: 1008020 + 8021: 1008021 + 8022: 1008022 + 8023: 1008023 + 8024: 1008024 + 8025: 1008025 + 8026: 1008026 + 8027: 1008027 + 8028: 1008028 + 8029: 1008029 + 8030: 1008030 + 8031: 1008031 + 8032: 1008032 + 8033: 1008033 + 8034: 1008034 + 8035: 1008035 + 8036: 1008036 + 8037: 1008037 + 8038: 1008038 + 8039: 1008039 + 8040: 1008040 + 8041: 1008041 + 8042: 1008042 + 8043: 1008043 + 8044: 1008044 + 8045: 1008045 + 8046: 1008046 + 8047: 1008047 + 8048: 1008048 + 8049: 1008049 + 8050: 1008050 + 8051: 1008051 + 8052: 1008052 + 8053: 1008053 + 8054: 1008054 + 8055: 1008055 + 8056: 1008056 + 8057: 1008057 + 8058: 1008058 + 8059: 1008059 + 8060: 1008060 + 8061: 1008061 + 8062: 1008062 + 8063: 1008063 + 8064: 1008064 + 8065: 1008065 + 8066: 1008066 + 8067: 1008067 + 8068: 1008068 + 8069: 1008069 + 8070: 1008070 + 8071: 1008071 + 8072: 1008072 + 8073: 1008073 + 8074: 1008074 + 8075: 1008075 + 8076: 1008076 + 8077: 1008077 + 8078: 1008078 + 8079: 1008079 + 8080: 1008080 + 8081: 1008081 + 8082: 1008082 + 8083: 1008083 + 8084: 1008084 + 8085: 1008085 + 8086: 1008086 + 8087: 1008087 + 8088: 1008088 + 8089: 1008089 + 8090: 1008090 + 8091: 1008091 + 8092: 1008092 + 8093: 1008093 + 8094: 1008094 + 8095: 1008095 + 8096: 1008096 + 8097: 1008097 + 8098: 1008098 + 8099: 1008099 + 8100: 1008100 + 8101: 1008101 + 8102: 1008102 + 8103: 1008103 + 8104: 1008104 + 8105: 1008105 + 8106: 1008106 + 8107: 1008107 + 8108: 1008108 + 8109: 1008109 + 8110: 1008110 + 8111: 1008111 + 8112: 1008112 + 8113: 1008113 + 8114: 1008114 + 8115: 1008115 + 8116: 1008116 + 8117: 1008117 + 8118: 1008118 + 8119: 1008119 + 8120: 1008120 + 8121: 1008121 + 8122: 1008122 + 8123: 1008123 + 8124: 1008124 + 8125: 1008125 + 8126: 1008126 + 8127: 1008127 + 8128: 1008128 + 8129: 1008129 + 8130: 1008130 + 8131: 1008131 + 8132: 1008132 + 8133: 1008133 + 8134: 1008134 + 8135: 1008135 + 8136: 1008136 + 8137: 1008137 + 8138: 1008138 + 8139: 1008139 + 8140: 1008140 + 8141: 1008141 + 8142: 1008142 + 8143: 1008143 + 8144: 1008144 + 8145: 1008145 + 8146: 1008146 + 8147: 1008147 + 8148: 1008148 + 8149: 1008149 + 8150: 1008150 + 8151: 1008151 + 8152: 1008152 + 8153: 1008153 + 8154: 1008154 + 8155: 1008155 + 8156: 1008156 + 8157: 1008157 + 8158: 1008158 + 8159: 1008159 + 8160: 1008160 + 8161: 1008161 + 8162: 1008162 + 8163: 1008163 + 8164: 1008164 + 8165: 1008165 + 8166: 1008166 + 8167: 1008167 + 8168: 1008168 + 8169: 1008169 + 8170: 1008170 + 8171: 1008171 + 8172: 1008172 + 8173: 1008173 + 8174: 1008174 + 8175: 1008175 + 8176: 1008176 + 8177: 1008177 + 8178: 1008178 + 8179: 1008179 + 8180: 1008180 + 8181: 1008181 + 8182: 1008182 + 8183: 1008183 + 8184: 1008184 + 8185: 1008185 + 8186: 1008186 + 8187: 1008187 + 8188: 1008188 + 8189: 1008189 + 8190: 1008190 + 8191: 1008191 + 8192: 1008192 + 8193: 1008193 + 8194: 1008194 + 8195: 1008195 + 8196: 1008196 + 8197: 1008197 + 8198: 1008198 + 8199: 1008199 + 8200: 1008200 + 8201: 1008201 + 8202: 1008202 + 8203: 1008203 + 8204: 1008204 + 8205: 1008205 + 8206: 1008206 + 8207: 1008207 + 8208: 1008208 + 8209: 1008209 + 8210: 1008210 + 8211: 1008211 + 8212: 1008212 + 8213: 1008213 + 8214: 1008214 + 8215: 1008215 + 8216: 1008216 + 8217: 1008217 + 8218: 1008218 + 8219: 1008219 + 8220: 1008220 + 8221: 1008221 + 8222: 1008222 + 8223: 1008223 + 8224: 1008224 + 8225: 1008225 + 8226: 1008226 + 8227: 1008227 + 8228: 1008228 + 8229: 1008229 + 8230: 1008230 + 8231: 1008231 + 8232: 1008232 + 8233: 1008233 + 8234: 1008234 + 8235: 1008235 + 8236: 1008236 + 8237: 1008237 + 8238: 1008238 + 8239: 1008239 + 8240: 1008240 + 8241: 1008241 + 8242: 1008242 + 8243: 1008243 + 8244: 1008244 + 8245: 1008245 + 8246: 1008246 + 8247: 1008247 + 8248: 1008248 + 8249: 1008249 + 8250: 1008250 + 8251: 1008251 + 8252: 1008252 + 8253: 1008253 + 8254: 1008254 + 8255: 1008255 + 8256: 1008256 + 8257: 1008257 + 8258: 1008258 + 8259: 1008259 + 8260: 1008260 + 8261: 1008261 + 8262: 1008262 + 8263: 1008263 + 8264: 1008264 + 8265: 1008265 + 8266: 1008266 + 8267: 1008267 + 8268: 1008268 + 8269: 1008269 + 8270: 1008270 + 8271: 1008271 + 8272: 1008272 + 8273: 1008273 + 8274: 1008274 + 8275: 1008275 + 8276: 1008276 + 8277: 1008277 + 8278: 1008278 + 8279: 1008279 + 8280: 1008280 + 8281: 1008281 + 8282: 1008282 + 8283: 1008283 + 8284: 1008284 + 8285: 1008285 + 8286: 1008286 + 8287: 1008287 + 8288: 1008288 + 8289: 1008289 + 8290: 1008290 + 8291: 1008291 + 8292: 1008292 + 8293: 1008293 + 8294: 1008294 + 8295: 1008295 + 8296: 1008296 + 8297: 1008297 + 8298: 1008298 + 8299: 1008299 + 8300: 1008300 + 8301: 1008301 + 8302: 1008302 + 8303: 1008303 + 8304: 1008304 + 8305: 1008305 + 8306: 1008306 + 8307: 1008307 + 8308: 1008308 + 8309: 1008309 + 8310: 1008310 + 8311: 1008311 + 8312: 1008312 + 8313: 1008313 + 8314: 1008314 + 8315: 1008315 + 8316: 1008316 + 8317: 1008317 + 8318: 1008318 + 8319: 1008319 + 8320: 1008320 + 8321: 1008321 + 8322: 1008322 + 8323: 1008323 + 8324: 1008324 + 8325: 1008325 + 8326: 1008326 + 8327: 1008327 + 8328: 1008328 + 8329: 1008329 + 8330: 1008330 + 8331: 1008331 + 8332: 1008332 + 8333: 1008333 + 8334: 1008334 + 8335: 1008335 + 8336: 1008336 + 8337: 1008337 + 8338: 1008338 + 8339: 1008339 + 8340: 1008340 + 8341: 1008341 + 8342: 1008342 + 8343: 1008343 + 8344: 1008344 + 8345: 1008345 + 8346: 1008346 + 8347: 1008347 + 8348: 1008348 + 8349: 1008349 + 8350: 1008350 + 8351: 1008351 + 8352: 1008352 + 8353: 1008353 + 8354: 1008354 + 8355: 1008355 + 8356: 1008356 + 8357: 1008357 + 8358: 1008358 + 8359: 1008359 + 8360: 1008360 + 8361: 1008361 + 8362: 1008362 + 8363: 1008363 + 8364: 1008364 + 8365: 1008365 + 8366: 1008366 + 8367: 1008367 + 8368: 1008368 + 8369: 1008369 + 8370: 1008370 + 8371: 1008371 + 8372: 1008372 + 8373: 1008373 + 8374: 1008374 + 8375: 1008375 + 8376: 1008376 + 8377: 1008377 + 8378: 1008378 + 8379: 1008379 + 8380: 1008380 + 8381: 1008381 + 8382: 1008382 + 8383: 1008383 + 8384: 1008384 + 8385: 1008385 + 8386: 1008386 + 8387: 1008387 + 8388: 1008388 + 8389: 1008389 + 8390: 1008390 + 8391: 1008391 + 8392: 1008392 + 8393: 1008393 + 8394: 1008394 + 8395: 1008395 + 8396: 1008396 + 8397: 1008397 + 8398: 1008398 + 8399: 1008399 + 8400: 1008400 + 8401: 1008401 + 8402: 1008402 + 8403: 1008403 + 8404: 1008404 + 8405: 1008405 + 8406: 1008406 + 8407: 1008407 + 8408: 1008408 + 8409: 1008409 + 8410: 1008410 + 8411: 1008411 + 8412: 1008412 + 8413: 1008413 + 8414: 1008414 + 8415: 1008415 + 8416: 1008416 + 8417: 1008417 + 8418: 1008418 + 8419: 1008419 + 8420: 1008420 + 8421: 1008421 + 8422: 1008422 + 8423: 1008423 + 8424: 1008424 + 8425: 1008425 + 8426: 1008426 + 8427: 1008427 + 8428: 1008428 + 8429: 1008429 + 8430: 1008430 + 8431: 1008431 + 8432: 1008432 + 8433: 1008433 + 8434: 1008434 + 8435: 1008435 + 8436: 1008436 + 8437: 1008437 + 8438: 1008438 + 8439: 1008439 + 8440: 1008440 + 8441: 1008441 + 8442: 1008442 + 8443: 1008443 + 8444: 1008444 + 8445: 1008445 + 8446: 1008446 + 8447: 1008447 + 8448: 1008448 + 8449: 1008449 + 8450: 1008450 + 8451: 1008451 + 8452: 1008452 + 8453: 1008453 + 8454: 1008454 + 8455: 1008455 + 8456: 1008456 + 8457: 1008457 + 8458: 1008458 + 8459: 1008459 + 8460: 1008460 + 8461: 1008461 + 8462: 1008462 + 8463: 1008463 + 8464: 1008464 + 8465: 1008465 + 8466: 1008466 + 8467: 1008467 + 8468: 1008468 + 8469: 1008469 + 8470: 1008470 + 8471: 1008471 + 8472: 1008472 + 8473: 1008473 + 8474: 1008474 + 8475: 1008475 + 8476: 1008476 + 8477: 1008477 + 8478: 1008478 + 8479: 1008479 + 8480: 1008480 + 8481: 1008481 + 8482: 1008482 + 8483: 1008483 + 8484: 1008484 + 8485: 1008485 + 8486: 1008486 + 8487: 1008487 + 8488: 1008488 + 8489: 1008489 + 8490: 1008490 + 8491: 1008491 + 8492: 1008492 + 8493: 1008493 + 8494: 1008494 + 8495: 1008495 + 8496: 1008496 + 8497: 1008497 + 8498: 1008498 + 8499: 1008499 + 8500: 1008500 + 8501: 1008501 + 8502: 1008502 + 8503: 1008503 + 8504: 1008504 + 8505: 1008505 + 8506: 1008506 + 8507: 1008507 + 8508: 1008508 + 8509: 1008509 + 8510: 1008510 + 8511: 1008511 + 8512: 1008512 + 8513: 1008513 + 8514: 1008514 + 8515: 1008515 + 8516: 1008516 + 8517: 1008517 + 8518: 1008518 + 8519: 1008519 + 8520: 1008520 + 8521: 1008521 + 8522: 1008522 + 8523: 1008523 + 8524: 1008524 + 8525: 1008525 + 8526: 1008526 + 8527: 1008527 + 8528: 1008528 + 8529: 1008529 + 8530: 1008530 + 8531: 1008531 + 8532: 1008532 + 8533: 1008533 + 8534: 1008534 + 8535: 1008535 + 8536: 1008536 + 8537: 1008537 + 8538: 1008538 + 8539: 1008539 + 8540: 1008540 + 8541: 1008541 + 8542: 1008542 + 8543: 1008543 + 8544: 1008544 + 8545: 1008545 + 8546: 1008546 + 8547: 1008547 + 8548: 1008548 + 8549: 1008549 + 8550: 1008550 + 8551: 1008551 + 8552: 1008552 + 8553: 1008553 + 8554: 1008554 + 8555: 1008555 + 8556: 1008556 + 8557: 1008557 + 8558: 1008558 + 8559: 1008559 + 8560: 1008560 + 8561: 1008561 + 8562: 1008562 + 8563: 1008563 + 8564: 1008564 + 8565: 1008565 + 8566: 1008566 + 8567: 1008567 + 8568: 1008568 + 8569: 1008569 + 8570: 1008570 + 8571: 1008571 + 8572: 1008572 + 8573: 1008573 + 8574: 1008574 + 8575: 1008575 + 8576: 1008576 + 8577: 1008577 + 8578: 1008578 + 8579: 1008579 + 8580: 1008580 + 8581: 1008581 + 8582: 1008582 + 8583: 1008583 + 8584: 1008584 + 8585: 1008585 + 8586: 1008586 + 8587: 1008587 + 8588: 1008588 + 8589: 1008589 + 8590: 1008590 + 8591: 1008591 + 8592: 1008592 + 8593: 1008593 + 8594: 1008594 + 8595: 1008595 + 8596: 1008596 + 8597: 1008597 + 8598: 1008598 + 8599: 1008599 + 8600: 1008600 + 8601: 1008601 + 8602: 1008602 + 8603: 1008603 + 8604: 1008604 + 8605: 1008605 + 8606: 1008606 + 8607: 1008607 + 8608: 1008608 + 8609: 1008609 + 8610: 1008610 + 8611: 1008611 + 8612: 1008612 + 8613: 1008613 + 8614: 1008614 + 8615: 1008615 + 8616: 1008616 + 8617: 1008617 + 8618: 1008618 + 8619: 1008619 + 8620: 1008620 + 8621: 1008621 + 8622: 1008622 + 8623: 1008623 + 8624: 1008624 + 8625: 1008625 + 8626: 1008626 + 8627: 1008627 + 8628: 1008628 + 8629: 1008629 + 8630: 1008630 + 8631: 1008631 + 8632: 1008632 + 8633: 1008633 + 8634: 1008634 + 8635: 1008635 + 8636: 1008636 + 8637: 1008637 + 8638: 1008638 + 8639: 1008639 + 8640: 1008640 + 8641: 1008641 + 8642: 1008642 + 8643: 1008643 + 8644: 1008644 + 8645: 1008645 + 8646: 1008646 + 8647: 1008647 + 8648: 1008648 + 8649: 1008649 + 8650: 1008650 + 8651: 1008651 + 8652: 1008652 + 8653: 1008653 + 8654: 1008654 + 8655: 1008655 + 8656: 1008656 + 8657: 1008657 + 8658: 1008658 + 8659: 1008659 + 8660: 1008660 + 8661: 1008661 + 8662: 1008662 + 8663: 1008663 + 8664: 1008664 + 8665: 1008665 + 8666: 1008666 + 8667: 1008667 + 8668: 1008668 + 8669: 1008669 + 8670: 1008670 + 8671: 1008671 + 8672: 1008672 + 8673: 1008673 + 8674: 1008674 + 8675: 1008675 + 8676: 1008676 + 8677: 1008677 + 8678: 1008678 + 8679: 1008679 + 8680: 1008680 + 8681: 1008681 + 8682: 1008682 + 8683: 1008683 + 8684: 1008684 + 8685: 1008685 + 8686: 1008686 + 8687: 1008687 + 8688: 1008688 + 8689: 1008689 + 8690: 1008690 + 8691: 1008691 + 8692: 1008692 + 8693: 1008693 + 8694: 1008694 + 8695: 1008695 + 8696: 1008696 + 8697: 1008697 + 8698: 1008698 + 8699: 1008699 + 8700: 1008700 + 8701: 1008701 + 8702: 1008702 + 8703: 1008703 + 8704: 1008704 + 8705: 1008705 + 8706: 1008706 + 8707: 1008707 + 8708: 1008708 + 8709: 1008709 + 8710: 1008710 + 8711: 1008711 + 8712: 1008712 + 8713: 1008713 + 8714: 1008714 + 8715: 1008715 + 8716: 1008716 + 8717: 1008717 + 8718: 1008718 + 8719: 1008719 + 8720: 1008720 + 8721: 1008721 + 8722: 1008722 + 8723: 1008723 + 8724: 1008724 + 8725: 1008725 + 8726: 1008726 + 8727: 1008727 + 8728: 1008728 + 8729: 1008729 + 8730: 1008730 + 8731: 1008731 + 8732: 1008732 + 8733: 1008733 + 8734: 1008734 + 8735: 1008735 + 8736: 1008736 + 8737: 1008737 + 8738: 1008738 + 8739: 1008739 + 8740: 1008740 + 8741: 1008741 + 8742: 1008742 + 8743: 1008743 + 8744: 1008744 + 8745: 1008745 + 8746: 1008746 + 8747: 1008747 + 8748: 1008748 + 8749: 1008749 + 8750: 1008750 + 8751: 1008751 + 8752: 1008752 + 8753: 1008753 + 8754: 1008754 + 8755: 1008755 + 8756: 1008756 + 8757: 1008757 + 8758: 1008758 + 8759: 1008759 + 8760: 1008760 + 8761: 1008761 + 8762: 1008762 + 8763: 1008763 + 8764: 1008764 + 8765: 1008765 + 8766: 1008766 + 8767: 1008767 + 8768: 1008768 + 8769: 1008769 + 8770: 1008770 + 8771: 1008771 + 8772: 1008772 + 8773: 1008773 + 8774: 1008774 + 8775: 1008775 + 8776: 1008776 + 8777: 1008777 + 8778: 1008778 + 8779: 1008779 + 8780: 1008780 + 8781: 1008781 + 8782: 1008782 + 8783: 1008783 + 8784: 1008784 + 8785: 1008785 + 8786: 1008786 + 8787: 1008787 + 8788: 1008788 + 8789: 1008789 + 8790: 1008790 + 8791: 1008791 + 8792: 1008792 + 8793: 1008793 + 8794: 1008794 + 8795: 1008795 + 8796: 1008796 + 8797: 1008797 + 8798: 1008798 + 8799: 1008799 + 8800: 1008800 + 8801: 1008801 + 8802: 1008802 + 8803: 1008803 + 8804: 1008804 + 8805: 1008805 + 8806: 1008806 + 8807: 1008807 + 8808: 1008808 + 8809: 1008809 + 8810: 1008810 + 8811: 1008811 + 8812: 1008812 + 8813: 1008813 + 8814: 1008814 + 8815: 1008815 + 8816: 1008816 + 8817: 1008817 + 8818: 1008818 + 8819: 1008819 + 8820: 1008820 + 8821: 1008821 + 8822: 1008822 + 8823: 1008823 + 8824: 1008824 + 8825: 1008825 + 8826: 1008826 + 8827: 1008827 + 8828: 1008828 + 8829: 1008829 + 8830: 1008830 + 8831: 1008831 + 8832: 1008832 + 8833: 1008833 + 8834: 1008834 + 8835: 1008835 + 8836: 1008836 + 8837: 1008837 + 8838: 1008838 + 8839: 1008839 + 8840: 1008840 + 8841: 1008841 + 8842: 1008842 + 8843: 1008843 + 8844: 1008844 + 8845: 1008845 + 8846: 1008846 + 8847: 1008847 + 8848: 1008848 + 8849: 1008849 + 8850: 1008850 + 8851: 1008851 + 8852: 1008852 + 8853: 1008853 + 8854: 1008854 + 8855: 1008855 + 8856: 1008856 + 8857: 1008857 + 8858: 1008858 + 8859: 1008859 + 8860: 1008860 + 8861: 1008861 + 8862: 1008862 + 8863: 1008863 + 8864: 1008864 + 8865: 1008865 + 8866: 1008866 + 8867: 1008867 + 8868: 1008868 + 8869: 1008869 + 8870: 1008870 + 8871: 1008871 + 8872: 1008872 + 8873: 1008873 + 8874: 1008874 + 8875: 1008875 + 8876: 1008876 + 8877: 1008877 + 8878: 1008878 + 8879: 1008879 + 8880: 1008880 + 8881: 1008881 + 8882: 1008882 + 8883: 1008883 + 8884: 1008884 + 8885: 1008885 + 8886: 1008886 + 8887: 1008887 + 8888: 1008888 + 8889: 1008889 + 8890: 1008890 + 8891: 1008891 + 8892: 1008892 + 8893: 1008893 + 8894: 1008894 + 8895: 1008895 + 8896: 1008896 + 8897: 1008897 + 8898: 1008898 + 8899: 1008899 + 8900: 1008900 + 8901: 1008901 + 8902: 1008902 + 8903: 1008903 + 8904: 1008904 + 8905: 1008905 + 8906: 1008906 + 8907: 1008907 + 8908: 1008908 + 8909: 1008909 + 8910: 1008910 + 8911: 1008911 + 8912: 1008912 + 8913: 1008913 + 8914: 1008914 + 8915: 1008915 + 8916: 1008916 + 8917: 1008917 + 8918: 1008918 + 8919: 1008919 + 8920: 1008920 + 8921: 1008921 + 8922: 1008922 + 8923: 1008923 + 8924: 1008924 + 8925: 1008925 + 8926: 1008926 + 8927: 1008927 + 8928: 1008928 + 8929: 1008929 + 8930: 1008930 + 8931: 1008931 + 8932: 1008932 + 8933: 1008933 + 8934: 1008934 + 8935: 1008935 + 8936: 1008936 + 8937: 1008937 + 8938: 1008938 + 8939: 1008939 + 8940: 1008940 + 8941: 1008941 + 8942: 1008942 + 8943: 1008943 + 8944: 1008944 + 8945: 1008945 + 8946: 1008946 + 8947: 1008947 + 8948: 1008948 + 8949: 1008949 + 8950: 1008950 + 8951: 1008951 + 8952: 1008952 + 8953: 1008953 + 8954: 1008954 + 8955: 1008955 + 8956: 1008956 + 8957: 1008957 + 8958: 1008958 + 8959: 1008959 + 8960: 1008960 + 8961: 1008961 + 8962: 1008962 + 8963: 1008963 + 8964: 1008964 + 8965: 1008965 + 8966: 1008966 + 8967: 1008967 + 8968: 1008968 + 8969: 1008969 + 8970: 1008970 + 8971: 1008971 + 8972: 1008972 + 8973: 1008973 + 8974: 1008974 + 8975: 1008975 + 8976: 1008976 + 8977: 1008977 + 8978: 1008978 + 8979: 1008979 + 8980: 1008980 + 8981: 1008981 + 8982: 1008982 + 8983: 1008983 + 8984: 1008984 + 8985: 1008985 + 8986: 1008986 + 8987: 1008987 + 8988: 1008988 + 8989: 1008989 + 8990: 1008990 + 8991: 1008991 + 8992: 1008992 + 8993: 1008993 + 8994: 1008994 + 8995: 1008995 + 8996: 1008996 + 8997: 1008997 + 8998: 1008998 + 8999: 1008999 diff --git a/akregator/src/mk4storage/metakit/tests/ok/l02.txt b/akregator/src/mk4storage/metakit/tests/ok/l02.txt new file mode 100644 index 000000000..a1bd366e9 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l02.txt @@ -0,0 +1,2 @@ +>>> Over 64 Kb of strings +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/l02a.txt b/akregator/src/mk4storage/metakit/tests/ok/l02a.txt new file mode 100644 index 000000000..133662334 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l02a.txt @@ -0,0 +1,3503 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3500 rows = p1:S + 0: 'Alice in Wonderland' + 1: 'The wizard of Oz' + 2: 'I'm singin' in the rain' + 3: 'Alice in Wonderland' + 4: 'The wizard of Oz' + 5: 'I'm singin' in the rain' + 6: 'Alice in Wonderland' + 7: 'The wizard of Oz' + 8: 'I'm singin' in the rain' + 9: 'Alice in Wonderland' + 10: 'The wizard of Oz' + 11: 'I'm singin' in the rain' + 12: 'Alice in Wonderland' + 13: 'The wizard of Oz' + 14: 'I'm singin' in the rain' + 15: 'Alice in Wonderland' + 16: 'The wizard of Oz' + 17: 'I'm singin' in the rain' + 18: 'Alice in Wonderland' + 19: 'The wizard of Oz' + 20: 'I'm singin' in the rain' + 21: 'Alice in Wonderland' + 22: 'The wizard of Oz' + 23: 'I'm singin' in the rain' + 24: 'Alice in Wonderland' + 25: 'The wizard of Oz' + 26: 'I'm singin' in the rain' + 27: 'Alice in Wonderland' + 28: 'The wizard of Oz' + 29: 'I'm singin' in the rain' + 30: 'Alice in Wonderland' + 31: 'The wizard of Oz' + 32: 'I'm singin' in the rain' + 33: 'Alice in Wonderland' + 34: 'The wizard of Oz' + 35: 'I'm singin' in the rain' + 36: 'Alice in Wonderland' + 37: 'The wizard of Oz' + 38: 'I'm singin' in the rain' + 39: 'Alice in Wonderland' + 40: 'The wizard of Oz' + 41: 'I'm singin' in the rain' + 42: 'Alice in Wonderland' + 43: 'The wizard of Oz' + 44: 'I'm singin' in the rain' + 45: 'Alice in Wonderland' + 46: 'The wizard of Oz' + 47: 'I'm singin' in the rain' + 48: 'Alice in Wonderland' + 49: 'The wizard of Oz' + 50: 'I'm singin' in the rain' + 51: 'Alice in Wonderland' + 52: 'The wizard of Oz' + 53: 'I'm singin' in the rain' + 54: 'Alice in Wonderland' + 55: 'The wizard of Oz' + 56: 'I'm singin' in the rain' + 57: 'Alice in Wonderland' + 58: 'The wizard of Oz' + 59: 'I'm singin' in the rain' + 60: 'Alice in Wonderland' + 61: 'The wizard of Oz' + 62: 'I'm singin' in the rain' + 63: 'Alice in Wonderland' + 64: 'The wizard of Oz' + 65: 'I'm singin' in the rain' + 66: 'Alice in Wonderland' + 67: 'The wizard of Oz' + 68: 'I'm singin' in the rain' + 69: 'Alice in Wonderland' + 70: 'The wizard of Oz' + 71: 'I'm singin' in the rain' + 72: 'Alice in Wonderland' + 73: 'The wizard of Oz' + 74: 'I'm singin' in the rain' + 75: 'Alice in Wonderland' + 76: 'The wizard of Oz' + 77: 'I'm singin' in the rain' + 78: 'Alice in Wonderland' + 79: 'The wizard of Oz' + 80: 'I'm singin' in the rain' + 81: 'Alice in Wonderland' + 82: 'The wizard of Oz' + 83: 'I'm singin' in the rain' + 84: 'Alice in Wonderland' + 85: 'The wizard of Oz' + 86: 'I'm singin' in the rain' + 87: 'Alice in Wonderland' + 88: 'The wizard of Oz' + 89: 'I'm singin' in the rain' + 90: 'Alice in Wonderland' + 91: 'The wizard of Oz' + 92: 'I'm singin' in the rain' + 93: 'Alice in Wonderland' + 94: 'The wizard of Oz' + 95: 'I'm singin' in the rain' + 96: 'Alice in Wonderland' + 97: 'The wizard of Oz' + 98: 'I'm singin' in the rain' + 99: 'Alice in Wonderland' + 100: 'The wizard of Oz' + 101: 'I'm singin' in the rain' + 102: 'Alice in Wonderland' + 103: 'The wizard of Oz' + 104: 'I'm singin' in the rain' + 105: 'Alice in Wonderland' + 106: 'The wizard of Oz' + 107: 'I'm singin' in the rain' + 108: 'Alice in Wonderland' + 109: 'The wizard of Oz' + 110: 'I'm singin' in the rain' + 111: 'Alice in Wonderland' + 112: 'The wizard of Oz' + 113: 'I'm singin' in the rain' + 114: 'Alice in Wonderland' + 115: 'The wizard of Oz' + 116: 'I'm singin' in the rain' + 117: 'Alice in Wonderland' + 118: 'The wizard of Oz' + 119: 'I'm singin' in the rain' + 120: 'Alice in Wonderland' + 121: 'The wizard of Oz' + 122: 'I'm singin' in the rain' + 123: 'Alice in Wonderland' + 124: 'The wizard of Oz' + 125: 'I'm singin' in the rain' + 126: 'Alice in Wonderland' + 127: 'The wizard of Oz' + 128: 'I'm singin' in the rain' + 129: 'Alice in Wonderland' + 130: 'The wizard of Oz' + 131: 'I'm singin' in the rain' + 132: 'Alice in Wonderland' + 133: 'The wizard of Oz' + 134: 'I'm singin' in the rain' + 135: 'Alice in Wonderland' + 136: 'The wizard of Oz' + 137: 'I'm singin' in the rain' + 138: 'Alice in Wonderland' + 139: 'The wizard of Oz' + 140: 'I'm singin' in the rain' + 141: 'Alice in Wonderland' + 142: 'The wizard of Oz' + 143: 'I'm singin' in the rain' + 144: 'Alice in Wonderland' + 145: 'The wizard of Oz' + 146: 'I'm singin' in the rain' + 147: 'Alice in Wonderland' + 148: 'The wizard of Oz' + 149: 'I'm singin' in the rain' + 150: 'Alice in Wonderland' + 151: 'The wizard of Oz' + 152: 'I'm singin' in the rain' + 153: 'Alice in Wonderland' + 154: 'The wizard of Oz' + 155: 'I'm singin' in the rain' + 156: 'Alice in Wonderland' + 157: 'The wizard of Oz' + 158: 'I'm singin' in the rain' + 159: 'Alice in Wonderland' + 160: 'The wizard of Oz' + 161: 'I'm singin' in the rain' + 162: 'Alice in Wonderland' + 163: 'The wizard of Oz' + 164: 'I'm singin' in the rain' + 165: 'Alice in Wonderland' + 166: 'The wizard of Oz' + 167: 'I'm singin' in the rain' + 168: 'Alice in Wonderland' + 169: 'The wizard of Oz' + 170: 'I'm singin' in the rain' + 171: 'Alice in Wonderland' + 172: 'The wizard of Oz' + 173: 'I'm singin' in the rain' + 174: 'Alice in Wonderland' + 175: 'The wizard of Oz' + 176: 'I'm singin' in the rain' + 177: 'Alice in Wonderland' + 178: 'The wizard of Oz' + 179: 'I'm singin' in the rain' + 180: 'Alice in Wonderland' + 181: 'The wizard of Oz' + 182: 'I'm singin' in the rain' + 183: 'Alice in Wonderland' + 184: 'The wizard of Oz' + 185: 'I'm singin' in the rain' + 186: 'Alice in Wonderland' + 187: 'The wizard of Oz' + 188: 'I'm singin' in the rain' + 189: 'Alice in Wonderland' + 190: 'The wizard of Oz' + 191: 'I'm singin' in the rain' + 192: 'Alice in Wonderland' + 193: 'The wizard of Oz' + 194: 'I'm singin' in the rain' + 195: 'Alice in Wonderland' + 196: 'The wizard of Oz' + 197: 'I'm singin' in the rain' + 198: 'Alice in Wonderland' + 199: 'The wizard of Oz' + 200: 'I'm singin' in the rain' + 201: 'Alice in Wonderland' + 202: 'The wizard of Oz' + 203: 'I'm singin' in the rain' + 204: 'Alice in Wonderland' + 205: 'The wizard of Oz' + 206: 'I'm singin' in the rain' + 207: 'Alice in Wonderland' + 208: 'The wizard of Oz' + 209: 'I'm singin' in the rain' + 210: 'Alice in Wonderland' + 211: 'The wizard of Oz' + 212: 'I'm singin' in the rain' + 213: 'Alice in Wonderland' + 214: 'The wizard of Oz' + 215: 'I'm singin' in the rain' + 216: 'Alice in Wonderland' + 217: 'The wizard of Oz' + 218: 'I'm singin' in the rain' + 219: 'Alice in Wonderland' + 220: 'The wizard of Oz' + 221: 'I'm singin' in the rain' + 222: 'Alice in Wonderland' + 223: 'The wizard of Oz' + 224: 'I'm singin' in the rain' + 225: 'Alice in Wonderland' + 226: 'The wizard of Oz' + 227: 'I'm singin' in the rain' + 228: 'Alice in Wonderland' + 229: 'The wizard of Oz' + 230: 'I'm singin' in the rain' + 231: 'Alice in Wonderland' + 232: 'The wizard of Oz' + 233: 'I'm singin' in the rain' + 234: 'Alice in Wonderland' + 235: 'The wizard of Oz' + 236: 'I'm singin' in the rain' + 237: 'Alice in Wonderland' + 238: 'The wizard of Oz' + 239: 'I'm singin' in the rain' + 240: 'Alice in Wonderland' + 241: 'The wizard of Oz' + 242: 'I'm singin' in the rain' + 243: 'Alice in Wonderland' + 244: 'The wizard of Oz' + 245: 'I'm singin' in the rain' + 246: 'Alice in Wonderland' + 247: 'The wizard of Oz' + 248: 'I'm singin' in the rain' + 249: 'Alice in Wonderland' + 250: 'The wizard of Oz' + 251: 'I'm singin' in the rain' + 252: 'Alice in Wonderland' + 253: 'The wizard of Oz' + 254: 'I'm singin' in the rain' + 255: 'Alice in Wonderland' + 256: 'The wizard of Oz' + 257: 'I'm singin' in the rain' + 258: 'Alice in Wonderland' + 259: 'The wizard of Oz' + 260: 'I'm singin' in the rain' + 261: 'Alice in Wonderland' + 262: 'The wizard of Oz' + 263: 'I'm singin' in the rain' + 264: 'Alice in Wonderland' + 265: 'The wizard of Oz' + 266: 'I'm singin' in the rain' + 267: 'Alice in Wonderland' + 268: 'The wizard of Oz' + 269: 'I'm singin' in the rain' + 270: 'Alice in Wonderland' + 271: 'The wizard of Oz' + 272: 'I'm singin' in the rain' + 273: 'Alice in Wonderland' + 274: 'The wizard of Oz' + 275: 'I'm singin' in the rain' + 276: 'Alice in Wonderland' + 277: 'The wizard of Oz' + 278: 'I'm singin' in the rain' + 279: 'Alice in Wonderland' + 280: 'The wizard of Oz' + 281: 'I'm singin' in the rain' + 282: 'Alice in Wonderland' + 283: 'The wizard of Oz' + 284: 'I'm singin' in the rain' + 285: 'Alice in Wonderland' + 286: 'The wizard of Oz' + 287: 'I'm singin' in the rain' + 288: 'Alice in Wonderland' + 289: 'The wizard of Oz' + 290: 'I'm singin' in the rain' + 291: 'Alice in Wonderland' + 292: 'The wizard of Oz' + 293: 'I'm singin' in the rain' + 294: 'Alice in Wonderland' + 295: 'The wizard of Oz' + 296: 'I'm singin' in the rain' + 297: 'Alice in Wonderland' + 298: 'The wizard of Oz' + 299: 'I'm singin' in the rain' + 300: 'Alice in Wonderland' + 301: 'The wizard of Oz' + 302: 'I'm singin' in the rain' + 303: 'Alice in Wonderland' + 304: 'The wizard of Oz' + 305: 'I'm singin' in the rain' + 306: 'Alice in Wonderland' + 307: 'The wizard of Oz' + 308: 'I'm singin' in the rain' + 309: 'Alice in Wonderland' + 310: 'The wizard of Oz' + 311: 'I'm singin' in the rain' + 312: 'Alice in Wonderland' + 313: 'The wizard of Oz' + 314: 'I'm singin' in the rain' + 315: 'Alice in Wonderland' + 316: 'The wizard of Oz' + 317: 'I'm singin' in the rain' + 318: 'Alice in Wonderland' + 319: 'The wizard of Oz' + 320: 'I'm singin' in the rain' + 321: 'Alice in Wonderland' + 322: 'The wizard of Oz' + 323: 'I'm singin' in the rain' + 324: 'Alice in Wonderland' + 325: 'The wizard of Oz' + 326: 'I'm singin' in the rain' + 327: 'Alice in Wonderland' + 328: 'The wizard of Oz' + 329: 'I'm singin' in the rain' + 330: 'Alice in Wonderland' + 331: 'The wizard of Oz' + 332: 'I'm singin' in the rain' + 333: 'Alice in Wonderland' + 334: 'The wizard of Oz' + 335: 'I'm singin' in the rain' + 336: 'Alice in Wonderland' + 337: 'The wizard of Oz' + 338: 'I'm singin' in the rain' + 339: 'Alice in Wonderland' + 340: 'The wizard of Oz' + 341: 'I'm singin' in the rain' + 342: 'Alice in Wonderland' + 343: 'The wizard of Oz' + 344: 'I'm singin' in the rain' + 345: 'Alice in Wonderland' + 346: 'The wizard of Oz' + 347: 'I'm singin' in the rain' + 348: 'Alice in Wonderland' + 349: 'The wizard of Oz' + 350: 'I'm singin' in the rain' + 351: 'Alice in Wonderland' + 352: 'The wizard of Oz' + 353: 'I'm singin' in the rain' + 354: 'Alice in Wonderland' + 355: 'The wizard of Oz' + 356: 'I'm singin' in the rain' + 357: 'Alice in Wonderland' + 358: 'The wizard of Oz' + 359: 'I'm singin' in the rain' + 360: 'Alice in Wonderland' + 361: 'The wizard of Oz' + 362: 'I'm singin' in the rain' + 363: 'Alice in Wonderland' + 364: 'The wizard of Oz' + 365: 'I'm singin' in the rain' + 366: 'Alice in Wonderland' + 367: 'The wizard of Oz' + 368: 'I'm singin' in the rain' + 369: 'Alice in Wonderland' + 370: 'The wizard of Oz' + 371: 'I'm singin' in the rain' + 372: 'Alice in Wonderland' + 373: 'The wizard of Oz' + 374: 'I'm singin' in the rain' + 375: 'Alice in Wonderland' + 376: 'The wizard of Oz' + 377: 'I'm singin' in the rain' + 378: 'Alice in Wonderland' + 379: 'The wizard of Oz' + 380: 'I'm singin' in the rain' + 381: 'Alice in Wonderland' + 382: 'The wizard of Oz' + 383: 'I'm singin' in the rain' + 384: 'Alice in Wonderland' + 385: 'The wizard of Oz' + 386: 'I'm singin' in the rain' + 387: 'Alice in Wonderland' + 388: 'The wizard of Oz' + 389: 'I'm singin' in the rain' + 390: 'Alice in Wonderland' + 391: 'The wizard of Oz' + 392: 'I'm singin' in the rain' + 393: 'Alice in Wonderland' + 394: 'The wizard of Oz' + 395: 'I'm singin' in the rain' + 396: 'Alice in Wonderland' + 397: 'The wizard of Oz' + 398: 'I'm singin' in the rain' + 399: 'Alice in Wonderland' + 400: 'The wizard of Oz' + 401: 'I'm singin' in the rain' + 402: 'Alice in Wonderland' + 403: 'The wizard of Oz' + 404: 'I'm singin' in the rain' + 405: 'Alice in Wonderland' + 406: 'The wizard of Oz' + 407: 'I'm singin' in the rain' + 408: 'Alice in Wonderland' + 409: 'The wizard of Oz' + 410: 'I'm singin' in the rain' + 411: 'Alice in Wonderland' + 412: 'The wizard of Oz' + 413: 'I'm singin' in the rain' + 414: 'Alice in Wonderland' + 415: 'The wizard of Oz' + 416: 'I'm singin' in the rain' + 417: 'Alice in Wonderland' + 418: 'The wizard of Oz' + 419: 'I'm singin' in the rain' + 420: 'Alice in Wonderland' + 421: 'The wizard of Oz' + 422: 'I'm singin' in the rain' + 423: 'Alice in Wonderland' + 424: 'The wizard of Oz' + 425: 'I'm singin' in the rain' + 426: 'Alice in Wonderland' + 427: 'The wizard of Oz' + 428: 'I'm singin' in the rain' + 429: 'Alice in Wonderland' + 430: 'The wizard of Oz' + 431: 'I'm singin' in the rain' + 432: 'Alice in Wonderland' + 433: 'The wizard of Oz' + 434: 'I'm singin' in the rain' + 435: 'Alice in Wonderland' + 436: 'The wizard of Oz' + 437: 'I'm singin' in the rain' + 438: 'Alice in Wonderland' + 439: 'The wizard of Oz' + 440: 'I'm singin' in the rain' + 441: 'Alice in Wonderland' + 442: 'The wizard of Oz' + 443: 'I'm singin' in the rain' + 444: 'Alice in Wonderland' + 445: 'The wizard of Oz' + 446: 'I'm singin' in the rain' + 447: 'Alice in Wonderland' + 448: 'The wizard of Oz' + 449: 'I'm singin' in the rain' + 450: 'Alice in Wonderland' + 451: 'The wizard of Oz' + 452: 'I'm singin' in the rain' + 453: 'Alice in Wonderland' + 454: 'The wizard of Oz' + 455: 'I'm singin' in the rain' + 456: 'Alice in Wonderland' + 457: 'The wizard of Oz' + 458: 'I'm singin' in the rain' + 459: 'Alice in Wonderland' + 460: 'The wizard of Oz' + 461: 'I'm singin' in the rain' + 462: 'Alice in Wonderland' + 463: 'The wizard of Oz' + 464: 'I'm singin' in the rain' + 465: 'Alice in Wonderland' + 466: 'The wizard of Oz' + 467: 'I'm singin' in the rain' + 468: 'Alice in Wonderland' + 469: 'The wizard of Oz' + 470: 'I'm singin' in the rain' + 471: 'Alice in Wonderland' + 472: 'The wizard of Oz' + 473: 'I'm singin' in the rain' + 474: 'Alice in Wonderland' + 475: 'The wizard of Oz' + 476: 'I'm singin' in the rain' + 477: 'Alice in Wonderland' + 478: 'The wizard of Oz' + 479: 'I'm singin' in the rain' + 480: 'Alice in Wonderland' + 481: 'The wizard of Oz' + 482: 'I'm singin' in the rain' + 483: 'Alice in Wonderland' + 484: 'The wizard of Oz' + 485: 'I'm singin' in the rain' + 486: 'Alice in Wonderland' + 487: 'The wizard of Oz' + 488: 'I'm singin' in the rain' + 489: 'Alice in Wonderland' + 490: 'The wizard of Oz' + 491: 'I'm singin' in the rain' + 492: 'Alice in Wonderland' + 493: 'The wizard of Oz' + 494: 'I'm singin' in the rain' + 495: 'Alice in Wonderland' + 496: 'The wizard of Oz' + 497: 'I'm singin' in the rain' + 498: 'Alice in Wonderland' + 499: 'The wizard of Oz' + 500: 'I'm singin' in the rain' + 501: 'Alice in Wonderland' + 502: 'The wizard of Oz' + 503: 'I'm singin' in the rain' + 504: 'Alice in Wonderland' + 505: 'The wizard of Oz' + 506: 'I'm singin' in the rain' + 507: 'Alice in Wonderland' + 508: 'The wizard of Oz' + 509: 'I'm singin' in the rain' + 510: 'Alice in Wonderland' + 511: 'The wizard of Oz' + 512: 'I'm singin' in the rain' + 513: 'Alice in Wonderland' + 514: 'The wizard of Oz' + 515: 'I'm singin' in the rain' + 516: 'Alice in Wonderland' + 517: 'The wizard of Oz' + 518: 'I'm singin' in the rain' + 519: 'Alice in Wonderland' + 520: 'The wizard of Oz' + 521: 'I'm singin' in the rain' + 522: 'Alice in Wonderland' + 523: 'The wizard of Oz' + 524: 'I'm singin' in the rain' + 525: 'Alice in Wonderland' + 526: 'The wizard of Oz' + 527: 'I'm singin' in the rain' + 528: 'Alice in Wonderland' + 529: 'The wizard of Oz' + 530: 'I'm singin' in the rain' + 531: 'Alice in Wonderland' + 532: 'The wizard of Oz' + 533: 'I'm singin' in the rain' + 534: 'Alice in Wonderland' + 535: 'The wizard of Oz' + 536: 'I'm singin' in the rain' + 537: 'Alice in Wonderland' + 538: 'The wizard of Oz' + 539: 'I'm singin' in the rain' + 540: 'Alice in Wonderland' + 541: 'The wizard of Oz' + 542: 'I'm singin' in the rain' + 543: 'Alice in Wonderland' + 544: 'The wizard of Oz' + 545: 'I'm singin' in the rain' + 546: 'Alice in Wonderland' + 547: 'The wizard of Oz' + 548: 'I'm singin' in the rain' + 549: 'Alice in Wonderland' + 550: 'The wizard of Oz' + 551: 'I'm singin' in the rain' + 552: 'Alice in Wonderland' + 553: 'The wizard of Oz' + 554: 'I'm singin' in the rain' + 555: 'Alice in Wonderland' + 556: 'The wizard of Oz' + 557: 'I'm singin' in the rain' + 558: 'Alice in Wonderland' + 559: 'The wizard of Oz' + 560: 'I'm singin' in the rain' + 561: 'Alice in Wonderland' + 562: 'The wizard of Oz' + 563: 'I'm singin' in the rain' + 564: 'Alice in Wonderland' + 565: 'The wizard of Oz' + 566: 'I'm singin' in the rain' + 567: 'Alice in Wonderland' + 568: 'The wizard of Oz' + 569: 'I'm singin' in the rain' + 570: 'Alice in Wonderland' + 571: 'The wizard of Oz' + 572: 'I'm singin' in the rain' + 573: 'Alice in Wonderland' + 574: 'The wizard of Oz' + 575: 'I'm singin' in the rain' + 576: 'Alice in Wonderland' + 577: 'The wizard of Oz' + 578: 'I'm singin' in the rain' + 579: 'Alice in Wonderland' + 580: 'The wizard of Oz' + 581: 'I'm singin' in the rain' + 582: 'Alice in Wonderland' + 583: 'The wizard of Oz' + 584: 'I'm singin' in the rain' + 585: 'Alice in Wonderland' + 586: 'The wizard of Oz' + 587: 'I'm singin' in the rain' + 588: 'Alice in Wonderland' + 589: 'The wizard of Oz' + 590: 'I'm singin' in the rain' + 591: 'Alice in Wonderland' + 592: 'The wizard of Oz' + 593: 'I'm singin' in the rain' + 594: 'Alice in Wonderland' + 595: 'The wizard of Oz' + 596: 'I'm singin' in the rain' + 597: 'Alice in Wonderland' + 598: 'The wizard of Oz' + 599: 'I'm singin' in the rain' + 600: 'Alice in Wonderland' + 601: 'The wizard of Oz' + 602: 'I'm singin' in the rain' + 603: 'Alice in Wonderland' + 604: 'The wizard of Oz' + 605: 'I'm singin' in the rain' + 606: 'Alice in Wonderland' + 607: 'The wizard of Oz' + 608: 'I'm singin' in the rain' + 609: 'Alice in Wonderland' + 610: 'The wizard of Oz' + 611: 'I'm singin' in the rain' + 612: 'Alice in Wonderland' + 613: 'The wizard of Oz' + 614: 'I'm singin' in the rain' + 615: 'Alice in Wonderland' + 616: 'The wizard of Oz' + 617: 'I'm singin' in the rain' + 618: 'Alice in Wonderland' + 619: 'The wizard of Oz' + 620: 'I'm singin' in the rain' + 621: 'Alice in Wonderland' + 622: 'The wizard of Oz' + 623: 'I'm singin' in the rain' + 624: 'Alice in Wonderland' + 625: 'The wizard of Oz' + 626: 'I'm singin' in the rain' + 627: 'Alice in Wonderland' + 628: 'The wizard of Oz' + 629: 'I'm singin' in the rain' + 630: 'Alice in Wonderland' + 631: 'The wizard of Oz' + 632: 'I'm singin' in the rain' + 633: 'Alice in Wonderland' + 634: 'The wizard of Oz' + 635: 'I'm singin' in the rain' + 636: 'Alice in Wonderland' + 637: 'The wizard of Oz' + 638: 'I'm singin' in the rain' + 639: 'Alice in Wonderland' + 640: 'The wizard of Oz' + 641: 'I'm singin' in the rain' + 642: 'Alice in Wonderland' + 643: 'The wizard of Oz' + 644: 'I'm singin' in the rain' + 645: 'Alice in Wonderland' + 646: 'The wizard of Oz' + 647: 'I'm singin' in the rain' + 648: 'Alice in Wonderland' + 649: 'The wizard of Oz' + 650: 'I'm singin' in the rain' + 651: 'Alice in Wonderland' + 652: 'The wizard of Oz' + 653: 'I'm singin' in the rain' + 654: 'Alice in Wonderland' + 655: 'The wizard of Oz' + 656: 'I'm singin' in the rain' + 657: 'Alice in Wonderland' + 658: 'The wizard of Oz' + 659: 'I'm singin' in the rain' + 660: 'Alice in Wonderland' + 661: 'The wizard of Oz' + 662: 'I'm singin' in the rain' + 663: 'Alice in Wonderland' + 664: 'The wizard of Oz' + 665: 'I'm singin' in the rain' + 666: 'Alice in Wonderland' + 667: 'The wizard of Oz' + 668: 'I'm singin' in the rain' + 669: 'Alice in Wonderland' + 670: 'The wizard of Oz' + 671: 'I'm singin' in the rain' + 672: 'Alice in Wonderland' + 673: 'The wizard of Oz' + 674: 'I'm singin' in the rain' + 675: 'Alice in Wonderland' + 676: 'The wizard of Oz' + 677: 'I'm singin' in the rain' + 678: 'Alice in Wonderland' + 679: 'The wizard of Oz' + 680: 'I'm singin' in the rain' + 681: 'Alice in Wonderland' + 682: 'The wizard of Oz' + 683: 'I'm singin' in the rain' + 684: 'Alice in Wonderland' + 685: 'The wizard of Oz' + 686: 'I'm singin' in the rain' + 687: 'Alice in Wonderland' + 688: 'The wizard of Oz' + 689: 'I'm singin' in the rain' + 690: 'Alice in Wonderland' + 691: 'The wizard of Oz' + 692: 'I'm singin' in the rain' + 693: 'Alice in Wonderland' + 694: 'The wizard of Oz' + 695: 'I'm singin' in the rain' + 696: 'Alice in Wonderland' + 697: 'The wizard of Oz' + 698: 'I'm singin' in the rain' + 699: 'Alice in Wonderland' + 700: 'The wizard of Oz' + 701: 'I'm singin' in the rain' + 702: 'Alice in Wonderland' + 703: 'The wizard of Oz' + 704: 'I'm singin' in the rain' + 705: 'Alice in Wonderland' + 706: 'The wizard of Oz' + 707: 'I'm singin' in the rain' + 708: 'Alice in Wonderland' + 709: 'The wizard of Oz' + 710: 'I'm singin' in the rain' + 711: 'Alice in Wonderland' + 712: 'The wizard of Oz' + 713: 'I'm singin' in the rain' + 714: 'Alice in Wonderland' + 715: 'The wizard of Oz' + 716: 'I'm singin' in the rain' + 717: 'Alice in Wonderland' + 718: 'The wizard of Oz' + 719: 'I'm singin' in the rain' + 720: 'Alice in Wonderland' + 721: 'The wizard of Oz' + 722: 'I'm singin' in the rain' + 723: 'Alice in Wonderland' + 724: 'The wizard of Oz' + 725: 'I'm singin' in the rain' + 726: 'Alice in Wonderland' + 727: 'The wizard of Oz' + 728: 'I'm singin' in the rain' + 729: 'Alice in Wonderland' + 730: 'The wizard of Oz' + 731: 'I'm singin' in the rain' + 732: 'Alice in Wonderland' + 733: 'The wizard of Oz' + 734: 'I'm singin' in the rain' + 735: 'Alice in Wonderland' + 736: 'The wizard of Oz' + 737: 'I'm singin' in the rain' + 738: 'Alice in Wonderland' + 739: 'The wizard of Oz' + 740: 'I'm singin' in the rain' + 741: 'Alice in Wonderland' + 742: 'The wizard of Oz' + 743: 'I'm singin' in the rain' + 744: 'Alice in Wonderland' + 745: 'The wizard of Oz' + 746: 'I'm singin' in the rain' + 747: 'Alice in Wonderland' + 748: 'The wizard of Oz' + 749: 'I'm singin' in the rain' + 750: 'Alice in Wonderland' + 751: 'The wizard of Oz' + 752: 'I'm singin' in the rain' + 753: 'Alice in Wonderland' + 754: 'The wizard of Oz' + 755: 'I'm singin' in the rain' + 756: 'Alice in Wonderland' + 757: 'The wizard of Oz' + 758: 'I'm singin' in the rain' + 759: 'Alice in Wonderland' + 760: 'The wizard of Oz' + 761: 'I'm singin' in the rain' + 762: 'Alice in Wonderland' + 763: 'The wizard of Oz' + 764: 'I'm singin' in the rain' + 765: 'Alice in Wonderland' + 766: 'The wizard of Oz' + 767: 'I'm singin' in the rain' + 768: 'Alice in Wonderland' + 769: 'The wizard of Oz' + 770: 'I'm singin' in the rain' + 771: 'Alice in Wonderland' + 772: 'The wizard of Oz' + 773: 'I'm singin' in the rain' + 774: 'Alice in Wonderland' + 775: 'The wizard of Oz' + 776: 'I'm singin' in the rain' + 777: 'Alice in Wonderland' + 778: 'The wizard of Oz' + 779: 'I'm singin' in the rain' + 780: 'Alice in Wonderland' + 781: 'The wizard of Oz' + 782: 'I'm singin' in the rain' + 783: 'Alice in Wonderland' + 784: 'The wizard of Oz' + 785: 'I'm singin' in the rain' + 786: 'Alice in Wonderland' + 787: 'The wizard of Oz' + 788: 'I'm singin' in the rain' + 789: 'Alice in Wonderland' + 790: 'The wizard of Oz' + 791: 'I'm singin' in the rain' + 792: 'Alice in Wonderland' + 793: 'The wizard of Oz' + 794: 'I'm singin' in the rain' + 795: 'Alice in Wonderland' + 796: 'The wizard of Oz' + 797: 'I'm singin' in the rain' + 798: 'Alice in Wonderland' + 799: 'The wizard of Oz' + 800: 'I'm singin' in the rain' + 801: 'Alice in Wonderland' + 802: 'The wizard of Oz' + 803: 'I'm singin' in the rain' + 804: 'Alice in Wonderland' + 805: 'The wizard of Oz' + 806: 'I'm singin' in the rain' + 807: 'Alice in Wonderland' + 808: 'The wizard of Oz' + 809: 'I'm singin' in the rain' + 810: 'Alice in Wonderland' + 811: 'The wizard of Oz' + 812: 'I'm singin' in the rain' + 813: 'Alice in Wonderland' + 814: 'The wizard of Oz' + 815: 'I'm singin' in the rain' + 816: 'Alice in Wonderland' + 817: 'The wizard of Oz' + 818: 'I'm singin' in the rain' + 819: 'Alice in Wonderland' + 820: 'The wizard of Oz' + 821: 'I'm singin' in the rain' + 822: 'Alice in Wonderland' + 823: 'The wizard of Oz' + 824: 'I'm singin' in the rain' + 825: 'Alice in Wonderland' + 826: 'The wizard of Oz' + 827: 'I'm singin' in the rain' + 828: 'Alice in Wonderland' + 829: 'The wizard of Oz' + 830: 'I'm singin' in the rain' + 831: 'Alice in Wonderland' + 832: 'The wizard of Oz' + 833: 'I'm singin' in the rain' + 834: 'Alice in Wonderland' + 835: 'The wizard of Oz' + 836: 'I'm singin' in the rain' + 837: 'Alice in Wonderland' + 838: 'The wizard of Oz' + 839: 'I'm singin' in the rain' + 840: 'Alice in Wonderland' + 841: 'The wizard of Oz' + 842: 'I'm singin' in the rain' + 843: 'Alice in Wonderland' + 844: 'The wizard of Oz' + 845: 'I'm singin' in the rain' + 846: 'Alice in Wonderland' + 847: 'The wizard of Oz' + 848: 'I'm singin' in the rain' + 849: 'Alice in Wonderland' + 850: 'The wizard of Oz' + 851: 'I'm singin' in the rain' + 852: 'Alice in Wonderland' + 853: 'The wizard of Oz' + 854: 'I'm singin' in the rain' + 855: 'Alice in Wonderland' + 856: 'The wizard of Oz' + 857: 'I'm singin' in the rain' + 858: 'Alice in Wonderland' + 859: 'The wizard of Oz' + 860: 'I'm singin' in the rain' + 861: 'Alice in Wonderland' + 862: 'The wizard of Oz' + 863: 'I'm singin' in the rain' + 864: 'Alice in Wonderland' + 865: 'The wizard of Oz' + 866: 'I'm singin' in the rain' + 867: 'Alice in Wonderland' + 868: 'The wizard of Oz' + 869: 'I'm singin' in the rain' + 870: 'Alice in Wonderland' + 871: 'The wizard of Oz' + 872: 'I'm singin' in the rain' + 873: 'Alice in Wonderland' + 874: 'The wizard of Oz' + 875: 'I'm singin' in the rain' + 876: 'Alice in Wonderland' + 877: 'The wizard of Oz' + 878: 'I'm singin' in the rain' + 879: 'Alice in Wonderland' + 880: 'The wizard of Oz' + 881: 'I'm singin' in the rain' + 882: 'Alice in Wonderland' + 883: 'The wizard of Oz' + 884: 'I'm singin' in the rain' + 885: 'Alice in Wonderland' + 886: 'The wizard of Oz' + 887: 'I'm singin' in the rain' + 888: 'Alice in Wonderland' + 889: 'The wizard of Oz' + 890: 'I'm singin' in the rain' + 891: 'Alice in Wonderland' + 892: 'The wizard of Oz' + 893: 'I'm singin' in the rain' + 894: 'Alice in Wonderland' + 895: 'The wizard of Oz' + 896: 'I'm singin' in the rain' + 897: 'Alice in Wonderland' + 898: 'The wizard of Oz' + 899: 'I'm singin' in the rain' + 900: 'Alice in Wonderland' + 901: 'The wizard of Oz' + 902: 'I'm singin' in the rain' + 903: 'Alice in Wonderland' + 904: 'The wizard of Oz' + 905: 'I'm singin' in the rain' + 906: 'Alice in Wonderland' + 907: 'The wizard of Oz' + 908: 'I'm singin' in the rain' + 909: 'Alice in Wonderland' + 910: 'The wizard of Oz' + 911: 'I'm singin' in the rain' + 912: 'Alice in Wonderland' + 913: 'The wizard of Oz' + 914: 'I'm singin' in the rain' + 915: 'Alice in Wonderland' + 916: 'The wizard of Oz' + 917: 'I'm singin' in the rain' + 918: 'Alice in Wonderland' + 919: 'The wizard of Oz' + 920: 'I'm singin' in the rain' + 921: 'Alice in Wonderland' + 922: 'The wizard of Oz' + 923: 'I'm singin' in the rain' + 924: 'Alice in Wonderland' + 925: 'The wizard of Oz' + 926: 'I'm singin' in the rain' + 927: 'Alice in Wonderland' + 928: 'The wizard of Oz' + 929: 'I'm singin' in the rain' + 930: 'Alice in Wonderland' + 931: 'The wizard of Oz' + 932: 'I'm singin' in the rain' + 933: 'Alice in Wonderland' + 934: 'The wizard of Oz' + 935: 'I'm singin' in the rain' + 936: 'Alice in Wonderland' + 937: 'The wizard of Oz' + 938: 'I'm singin' in the rain' + 939: 'Alice in Wonderland' + 940: 'The wizard of Oz' + 941: 'I'm singin' in the rain' + 942: 'Alice in Wonderland' + 943: 'The wizard of Oz' + 944: 'I'm singin' in the rain' + 945: 'Alice in Wonderland' + 946: 'The wizard of Oz' + 947: 'I'm singin' in the rain' + 948: 'Alice in Wonderland' + 949: 'The wizard of Oz' + 950: 'I'm singin' in the rain' + 951: 'Alice in Wonderland' + 952: 'The wizard of Oz' + 953: 'I'm singin' in the rain' + 954: 'Alice in Wonderland' + 955: 'The wizard of Oz' + 956: 'I'm singin' in the rain' + 957: 'Alice in Wonderland' + 958: 'The wizard of Oz' + 959: 'I'm singin' in the rain' + 960: 'Alice in Wonderland' + 961: 'The wizard of Oz' + 962: 'I'm singin' in the rain' + 963: 'Alice in Wonderland' + 964: 'The wizard of Oz' + 965: 'I'm singin' in the rain' + 966: 'Alice in Wonderland' + 967: 'The wizard of Oz' + 968: 'I'm singin' in the rain' + 969: 'Alice in Wonderland' + 970: 'The wizard of Oz' + 971: 'I'm singin' in the rain' + 972: 'Alice in Wonderland' + 973: 'The wizard of Oz' + 974: 'I'm singin' in the rain' + 975: 'Alice in Wonderland' + 976: 'The wizard of Oz' + 977: 'I'm singin' in the rain' + 978: 'Alice in Wonderland' + 979: 'The wizard of Oz' + 980: 'I'm singin' in the rain' + 981: 'Alice in Wonderland' + 982: 'The wizard of Oz' + 983: 'I'm singin' in the rain' + 984: 'Alice in Wonderland' + 985: 'The wizard of Oz' + 986: 'I'm singin' in the rain' + 987: 'Alice in Wonderland' + 988: 'The wizard of Oz' + 989: 'I'm singin' in the rain' + 990: 'Alice in Wonderland' + 991: 'The wizard of Oz' + 992: 'I'm singin' in the rain' + 993: 'Alice in Wonderland' + 994: 'The wizard of Oz' + 995: 'I'm singin' in the rain' + 996: 'Alice in Wonderland' + 997: 'The wizard of Oz' + 998: 'I'm singin' in the rain' + 999: 'Alice in Wonderland' + 1000: 'The wizard of Oz' + 1001: 'I'm singin' in the rain' + 1002: 'Alice in Wonderland' + 1003: 'The wizard of Oz' + 1004: 'I'm singin' in the rain' + 1005: 'Alice in Wonderland' + 1006: 'The wizard of Oz' + 1007: 'I'm singin' in the rain' + 1008: 'Alice in Wonderland' + 1009: 'The wizard of Oz' + 1010: 'I'm singin' in the rain' + 1011: 'Alice in Wonderland' + 1012: 'The wizard of Oz' + 1013: 'I'm singin' in the rain' + 1014: 'Alice in Wonderland' + 1015: 'The wizard of Oz' + 1016: 'I'm singin' in the rain' + 1017: 'Alice in Wonderland' + 1018: 'The wizard of Oz' + 1019: 'I'm singin' in the rain' + 1020: 'Alice in Wonderland' + 1021: 'The wizard of Oz' + 1022: 'I'm singin' in the rain' + 1023: 'Alice in Wonderland' + 1024: 'The wizard of Oz' + 1025: 'I'm singin' in the rain' + 1026: 'Alice in Wonderland' + 1027: 'The wizard of Oz' + 1028: 'I'm singin' in the rain' + 1029: 'Alice in Wonderland' + 1030: 'The wizard of Oz' + 1031: 'I'm singin' in the rain' + 1032: 'Alice in Wonderland' + 1033: 'The wizard of Oz' + 1034: 'I'm singin' in the rain' + 1035: 'Alice in Wonderland' + 1036: 'The wizard of Oz' + 1037: 'I'm singin' in the rain' + 1038: 'Alice in Wonderland' + 1039: 'The wizard of Oz' + 1040: 'I'm singin' in the rain' + 1041: 'Alice in Wonderland' + 1042: 'The wizard of Oz' + 1043: 'I'm singin' in the rain' + 1044: 'Alice in Wonderland' + 1045: 'The wizard of Oz' + 1046: 'I'm singin' in the rain' + 1047: 'Alice in Wonderland' + 1048: 'The wizard of Oz' + 1049: 'I'm singin' in the rain' + 1050: 'Alice in Wonderland' + 1051: 'The wizard of Oz' + 1052: 'I'm singin' in the rain' + 1053: 'Alice in Wonderland' + 1054: 'The wizard of Oz' + 1055: 'I'm singin' in the rain' + 1056: 'Alice in Wonderland' + 1057: 'The wizard of Oz' + 1058: 'I'm singin' in the rain' + 1059: 'Alice in Wonderland' + 1060: 'The wizard of Oz' + 1061: 'I'm singin' in the rain' + 1062: 'Alice in Wonderland' + 1063: 'The wizard of Oz' + 1064: 'I'm singin' in the rain' + 1065: 'Alice in Wonderland' + 1066: 'The wizard of Oz' + 1067: 'I'm singin' in the rain' + 1068: 'Alice in Wonderland' + 1069: 'The wizard of Oz' + 1070: 'I'm singin' in the rain' + 1071: 'Alice in Wonderland' + 1072: 'The wizard of Oz' + 1073: 'I'm singin' in the rain' + 1074: 'Alice in Wonderland' + 1075: 'The wizard of Oz' + 1076: 'I'm singin' in the rain' + 1077: 'Alice in Wonderland' + 1078: 'The wizard of Oz' + 1079: 'I'm singin' in the rain' + 1080: 'Alice in Wonderland' + 1081: 'The wizard of Oz' + 1082: 'I'm singin' in the rain' + 1083: 'Alice in Wonderland' + 1084: 'The wizard of Oz' + 1085: 'I'm singin' in the rain' + 1086: 'Alice in Wonderland' + 1087: 'The wizard of Oz' + 1088: 'I'm singin' in the rain' + 1089: 'Alice in Wonderland' + 1090: 'The wizard of Oz' + 1091: 'I'm singin' in the rain' + 1092: 'Alice in Wonderland' + 1093: 'The wizard of Oz' + 1094: 'I'm singin' in the rain' + 1095: 'Alice in Wonderland' + 1096: 'The wizard of Oz' + 1097: 'I'm singin' in the rain' + 1098: 'Alice in Wonderland' + 1099: 'The wizard of Oz' + 1100: 'I'm singin' in the rain' + 1101: 'Alice in Wonderland' + 1102: 'The wizard of Oz' + 1103: 'I'm singin' in the rain' + 1104: 'Alice in Wonderland' + 1105: 'The wizard of Oz' + 1106: 'I'm singin' in the rain' + 1107: 'Alice in Wonderland' + 1108: 'The wizard of Oz' + 1109: 'I'm singin' in the rain' + 1110: 'Alice in Wonderland' + 1111: 'The wizard of Oz' + 1112: 'I'm singin' in the rain' + 1113: 'Alice in Wonderland' + 1114: 'The wizard of Oz' + 1115: 'I'm singin' in the rain' + 1116: 'Alice in Wonderland' + 1117: 'The wizard of Oz' + 1118: 'I'm singin' in the rain' + 1119: 'Alice in Wonderland' + 1120: 'The wizard of Oz' + 1121: 'I'm singin' in the rain' + 1122: 'Alice in Wonderland' + 1123: 'The wizard of Oz' + 1124: 'I'm singin' in the rain' + 1125: 'Alice in Wonderland' + 1126: 'The wizard of Oz' + 1127: 'I'm singin' in the rain' + 1128: 'Alice in Wonderland' + 1129: 'The wizard of Oz' + 1130: 'I'm singin' in the rain' + 1131: 'Alice in Wonderland' + 1132: 'The wizard of Oz' + 1133: 'I'm singin' in the rain' + 1134: 'Alice in Wonderland' + 1135: 'The wizard of Oz' + 1136: 'I'm singin' in the rain' + 1137: 'Alice in Wonderland' + 1138: 'The wizard of Oz' + 1139: 'I'm singin' in the rain' + 1140: 'Alice in Wonderland' + 1141: 'The wizard of Oz' + 1142: 'I'm singin' in the rain' + 1143: 'Alice in Wonderland' + 1144: 'The wizard of Oz' + 1145: 'I'm singin' in the rain' + 1146: 'Alice in Wonderland' + 1147: 'The wizard of Oz' + 1148: 'I'm singin' in the rain' + 1149: 'Alice in Wonderland' + 1150: 'The wizard of Oz' + 1151: 'I'm singin' in the rain' + 1152: 'Alice in Wonderland' + 1153: 'The wizard of Oz' + 1154: 'I'm singin' in the rain' + 1155: 'Alice in Wonderland' + 1156: 'The wizard of Oz' + 1157: 'I'm singin' in the rain' + 1158: 'Alice in Wonderland' + 1159: 'The wizard of Oz' + 1160: 'I'm singin' in the rain' + 1161: 'Alice in Wonderland' + 1162: 'The wizard of Oz' + 1163: 'I'm singin' in the rain' + 1164: 'Alice in Wonderland' + 1165: 'The wizard of Oz' + 1166: 'I'm singin' in the rain' + 1167: 'Alice in Wonderland' + 1168: 'The wizard of Oz' + 1169: 'I'm singin' in the rain' + 1170: 'Alice in Wonderland' + 1171: 'The wizard of Oz' + 1172: 'I'm singin' in the rain' + 1173: 'Alice in Wonderland' + 1174: 'The wizard of Oz' + 1175: 'I'm singin' in the rain' + 1176: 'Alice in Wonderland' + 1177: 'The wizard of Oz' + 1178: 'I'm singin' in the rain' + 1179: 'Alice in Wonderland' + 1180: 'The wizard of Oz' + 1181: 'I'm singin' in the rain' + 1182: 'Alice in Wonderland' + 1183: 'The wizard of Oz' + 1184: 'I'm singin' in the rain' + 1185: 'Alice in Wonderland' + 1186: 'The wizard of Oz' + 1187: 'I'm singin' in the rain' + 1188: 'Alice in Wonderland' + 1189: 'The wizard of Oz' + 1190: 'I'm singin' in the rain' + 1191: 'Alice in Wonderland' + 1192: 'The wizard of Oz' + 1193: 'I'm singin' in the rain' + 1194: 'Alice in Wonderland' + 1195: 'The wizard of Oz' + 1196: 'I'm singin' in the rain' + 1197: 'Alice in Wonderland' + 1198: 'The wizard of Oz' + 1199: 'I'm singin' in the rain' + 1200: 'Alice in Wonderland' + 1201: 'The wizard of Oz' + 1202: 'I'm singin' in the rain' + 1203: 'Alice in Wonderland' + 1204: 'The wizard of Oz' + 1205: 'I'm singin' in the rain' + 1206: 'Alice in Wonderland' + 1207: 'The wizard of Oz' + 1208: 'I'm singin' in the rain' + 1209: 'Alice in Wonderland' + 1210: 'The wizard of Oz' + 1211: 'I'm singin' in the rain' + 1212: 'Alice in Wonderland' + 1213: 'The wizard of Oz' + 1214: 'I'm singin' in the rain' + 1215: 'Alice in Wonderland' + 1216: 'The wizard of Oz' + 1217: 'I'm singin' in the rain' + 1218: 'Alice in Wonderland' + 1219: 'The wizard of Oz' + 1220: 'I'm singin' in the rain' + 1221: 'Alice in Wonderland' + 1222: 'The wizard of Oz' + 1223: 'I'm singin' in the rain' + 1224: 'Alice in Wonderland' + 1225: 'The wizard of Oz' + 1226: 'I'm singin' in the rain' + 1227: 'Alice in Wonderland' + 1228: 'The wizard of Oz' + 1229: 'I'm singin' in the rain' + 1230: 'Alice in Wonderland' + 1231: 'The wizard of Oz' + 1232: 'I'm singin' in the rain' + 1233: 'Alice in Wonderland' + 1234: 'The wizard of Oz' + 1235: 'I'm singin' in the rain' + 1236: 'Alice in Wonderland' + 1237: 'The wizard of Oz' + 1238: 'I'm singin' in the rain' + 1239: 'Alice in Wonderland' + 1240: 'The wizard of Oz' + 1241: 'I'm singin' in the rain' + 1242: 'Alice in Wonderland' + 1243: 'The wizard of Oz' + 1244: 'I'm singin' in the rain' + 1245: 'Alice in Wonderland' + 1246: 'The wizard of Oz' + 1247: 'I'm singin' in the rain' + 1248: 'Alice in Wonderland' + 1249: 'The wizard of Oz' + 1250: 'I'm singin' in the rain' + 1251: 'Alice in Wonderland' + 1252: 'The wizard of Oz' + 1253: 'I'm singin' in the rain' + 1254: 'Alice in Wonderland' + 1255: 'The wizard of Oz' + 1256: 'I'm singin' in the rain' + 1257: 'Alice in Wonderland' + 1258: 'The wizard of Oz' + 1259: 'I'm singin' in the rain' + 1260: 'Alice in Wonderland' + 1261: 'The wizard of Oz' + 1262: 'I'm singin' in the rain' + 1263: 'Alice in Wonderland' + 1264: 'The wizard of Oz' + 1265: 'I'm singin' in the rain' + 1266: 'Alice in Wonderland' + 1267: 'The wizard of Oz' + 1268: 'I'm singin' in the rain' + 1269: 'Alice in Wonderland' + 1270: 'The wizard of Oz' + 1271: 'I'm singin' in the rain' + 1272: 'Alice in Wonderland' + 1273: 'The wizard of Oz' + 1274: 'I'm singin' in the rain' + 1275: 'Alice in Wonderland' + 1276: 'The wizard of Oz' + 1277: 'I'm singin' in the rain' + 1278: 'Alice in Wonderland' + 1279: 'The wizard of Oz' + 1280: 'I'm singin' in the rain' + 1281: 'Alice in Wonderland' + 1282: 'The wizard of Oz' + 1283: 'I'm singin' in the rain' + 1284: 'Alice in Wonderland' + 1285: 'The wizard of Oz' + 1286: 'I'm singin' in the rain' + 1287: 'Alice in Wonderland' + 1288: 'The wizard of Oz' + 1289: 'I'm singin' in the rain' + 1290: 'Alice in Wonderland' + 1291: 'The wizard of Oz' + 1292: 'I'm singin' in the rain' + 1293: 'Alice in Wonderland' + 1294: 'The wizard of Oz' + 1295: 'I'm singin' in the rain' + 1296: 'Alice in Wonderland' + 1297: 'The wizard of Oz' + 1298: 'I'm singin' in the rain' + 1299: 'Alice in Wonderland' + 1300: 'The wizard of Oz' + 1301: 'I'm singin' in the rain' + 1302: 'Alice in Wonderland' + 1303: 'The wizard of Oz' + 1304: 'I'm singin' in the rain' + 1305: 'Alice in Wonderland' + 1306: 'The wizard of Oz' + 1307: 'I'm singin' in the rain' + 1308: 'Alice in Wonderland' + 1309: 'The wizard of Oz' + 1310: 'I'm singin' in the rain' + 1311: 'Alice in Wonderland' + 1312: 'The wizard of Oz' + 1313: 'I'm singin' in the rain' + 1314: 'Alice in Wonderland' + 1315: 'The wizard of Oz' + 1316: 'I'm singin' in the rain' + 1317: 'Alice in Wonderland' + 1318: 'The wizard of Oz' + 1319: 'I'm singin' in the rain' + 1320: 'Alice in Wonderland' + 1321: 'The wizard of Oz' + 1322: 'I'm singin' in the rain' + 1323: 'Alice in Wonderland' + 1324: 'The wizard of Oz' + 1325: 'I'm singin' in the rain' + 1326: 'Alice in Wonderland' + 1327: 'The wizard of Oz' + 1328: 'I'm singin' in the rain' + 1329: 'Alice in Wonderland' + 1330: 'The wizard of Oz' + 1331: 'I'm singin' in the rain' + 1332: 'Alice in Wonderland' + 1333: 'The wizard of Oz' + 1334: 'I'm singin' in the rain' + 1335: 'Alice in Wonderland' + 1336: 'The wizard of Oz' + 1337: 'I'm singin' in the rain' + 1338: 'Alice in Wonderland' + 1339: 'The wizard of Oz' + 1340: 'I'm singin' in the rain' + 1341: 'Alice in Wonderland' + 1342: 'The wizard of Oz' + 1343: 'I'm singin' in the rain' + 1344: 'Alice in Wonderland' + 1345: 'The wizard of Oz' + 1346: 'I'm singin' in the rain' + 1347: 'Alice in Wonderland' + 1348: 'The wizard of Oz' + 1349: 'I'm singin' in the rain' + 1350: 'Alice in Wonderland' + 1351: 'The wizard of Oz' + 1352: 'I'm singin' in the rain' + 1353: 'Alice in Wonderland' + 1354: 'The wizard of Oz' + 1355: 'I'm singin' in the rain' + 1356: 'Alice in Wonderland' + 1357: 'The wizard of Oz' + 1358: 'I'm singin' in the rain' + 1359: 'Alice in Wonderland' + 1360: 'The wizard of Oz' + 1361: 'I'm singin' in the rain' + 1362: 'Alice in Wonderland' + 1363: 'The wizard of Oz' + 1364: 'I'm singin' in the rain' + 1365: 'Alice in Wonderland' + 1366: 'The wizard of Oz' + 1367: 'I'm singin' in the rain' + 1368: 'Alice in Wonderland' + 1369: 'The wizard of Oz' + 1370: 'I'm singin' in the rain' + 1371: 'Alice in Wonderland' + 1372: 'The wizard of Oz' + 1373: 'I'm singin' in the rain' + 1374: 'Alice in Wonderland' + 1375: 'The wizard of Oz' + 1376: 'I'm singin' in the rain' + 1377: 'Alice in Wonderland' + 1378: 'The wizard of Oz' + 1379: 'I'm singin' in the rain' + 1380: 'Alice in Wonderland' + 1381: 'The wizard of Oz' + 1382: 'I'm singin' in the rain' + 1383: 'Alice in Wonderland' + 1384: 'The wizard of Oz' + 1385: 'I'm singin' in the rain' + 1386: 'Alice in Wonderland' + 1387: 'The wizard of Oz' + 1388: 'I'm singin' in the rain' + 1389: 'Alice in Wonderland' + 1390: 'The wizard of Oz' + 1391: 'I'm singin' in the rain' + 1392: 'Alice in Wonderland' + 1393: 'The wizard of Oz' + 1394: 'I'm singin' in the rain' + 1395: 'Alice in Wonderland' + 1396: 'The wizard of Oz' + 1397: 'I'm singin' in the rain' + 1398: 'Alice in Wonderland' + 1399: 'The wizard of Oz' + 1400: 'I'm singin' in the rain' + 1401: 'Alice in Wonderland' + 1402: 'The wizard of Oz' + 1403: 'I'm singin' in the rain' + 1404: 'Alice in Wonderland' + 1405: 'The wizard of Oz' + 1406: 'I'm singin' in the rain' + 1407: 'Alice in Wonderland' + 1408: 'The wizard of Oz' + 1409: 'I'm singin' in the rain' + 1410: 'Alice in Wonderland' + 1411: 'The wizard of Oz' + 1412: 'I'm singin' in the rain' + 1413: 'Alice in Wonderland' + 1414: 'The wizard of Oz' + 1415: 'I'm singin' in the rain' + 1416: 'Alice in Wonderland' + 1417: 'The wizard of Oz' + 1418: 'I'm singin' in the rain' + 1419: 'Alice in Wonderland' + 1420: 'The wizard of Oz' + 1421: 'I'm singin' in the rain' + 1422: 'Alice in Wonderland' + 1423: 'The wizard of Oz' + 1424: 'I'm singin' in the rain' + 1425: 'Alice in Wonderland' + 1426: 'The wizard of Oz' + 1427: 'I'm singin' in the rain' + 1428: 'Alice in Wonderland' + 1429: 'The wizard of Oz' + 1430: 'I'm singin' in the rain' + 1431: 'Alice in Wonderland' + 1432: 'The wizard of Oz' + 1433: 'I'm singin' in the rain' + 1434: 'Alice in Wonderland' + 1435: 'The wizard of Oz' + 1436: 'I'm singin' in the rain' + 1437: 'Alice in Wonderland' + 1438: 'The wizard of Oz' + 1439: 'I'm singin' in the rain' + 1440: 'Alice in Wonderland' + 1441: 'The wizard of Oz' + 1442: 'I'm singin' in the rain' + 1443: 'Alice in Wonderland' + 1444: 'The wizard of Oz' + 1445: 'I'm singin' in the rain' + 1446: 'Alice in Wonderland' + 1447: 'The wizard of Oz' + 1448: 'I'm singin' in the rain' + 1449: 'Alice in Wonderland' + 1450: 'The wizard of Oz' + 1451: 'I'm singin' in the rain' + 1452: 'Alice in Wonderland' + 1453: 'The wizard of Oz' + 1454: 'I'm singin' in the rain' + 1455: 'Alice in Wonderland' + 1456: 'The wizard of Oz' + 1457: 'I'm singin' in the rain' + 1458: 'Alice in Wonderland' + 1459: 'The wizard of Oz' + 1460: 'I'm singin' in the rain' + 1461: 'Alice in Wonderland' + 1462: 'The wizard of Oz' + 1463: 'I'm singin' in the rain' + 1464: 'Alice in Wonderland' + 1465: 'The wizard of Oz' + 1466: 'I'm singin' in the rain' + 1467: 'Alice in Wonderland' + 1468: 'The wizard of Oz' + 1469: 'I'm singin' in the rain' + 1470: 'Alice in Wonderland' + 1471: 'The wizard of Oz' + 1472: 'I'm singin' in the rain' + 1473: 'Alice in Wonderland' + 1474: 'The wizard of Oz' + 1475: 'I'm singin' in the rain' + 1476: 'Alice in Wonderland' + 1477: 'The wizard of Oz' + 1478: 'I'm singin' in the rain' + 1479: 'Alice in Wonderland' + 1480: 'The wizard of Oz' + 1481: 'I'm singin' in the rain' + 1482: 'Alice in Wonderland' + 1483: 'The wizard of Oz' + 1484: 'I'm singin' in the rain' + 1485: 'Alice in Wonderland' + 1486: 'The wizard of Oz' + 1487: 'I'm singin' in the rain' + 1488: 'Alice in Wonderland' + 1489: 'The wizard of Oz' + 1490: 'I'm singin' in the rain' + 1491: 'Alice in Wonderland' + 1492: 'The wizard of Oz' + 1493: 'I'm singin' in the rain' + 1494: 'Alice in Wonderland' + 1495: 'The wizard of Oz' + 1496: 'I'm singin' in the rain' + 1497: 'Alice in Wonderland' + 1498: 'The wizard of Oz' + 1499: 'I'm singin' in the rain' + 1500: 'Alice in Wonderland' + 1501: 'The wizard of Oz' + 1502: 'I'm singin' in the rain' + 1503: 'Alice in Wonderland' + 1504: 'The wizard of Oz' + 1505: 'I'm singin' in the rain' + 1506: 'Alice in Wonderland' + 1507: 'The wizard of Oz' + 1508: 'I'm singin' in the rain' + 1509: 'Alice in Wonderland' + 1510: 'The wizard of Oz' + 1511: 'I'm singin' in the rain' + 1512: 'Alice in Wonderland' + 1513: 'The wizard of Oz' + 1514: 'I'm singin' in the rain' + 1515: 'Alice in Wonderland' + 1516: 'The wizard of Oz' + 1517: 'I'm singin' in the rain' + 1518: 'Alice in Wonderland' + 1519: 'The wizard of Oz' + 1520: 'I'm singin' in the rain' + 1521: 'Alice in Wonderland' + 1522: 'The wizard of Oz' + 1523: 'I'm singin' in the rain' + 1524: 'Alice in Wonderland' + 1525: 'The wizard of Oz' + 1526: 'I'm singin' in the rain' + 1527: 'Alice in Wonderland' + 1528: 'The wizard of Oz' + 1529: 'I'm singin' in the rain' + 1530: 'Alice in Wonderland' + 1531: 'The wizard of Oz' + 1532: 'I'm singin' in the rain' + 1533: 'Alice in Wonderland' + 1534: 'The wizard of Oz' + 1535: 'I'm singin' in the rain' + 1536: 'Alice in Wonderland' + 1537: 'The wizard of Oz' + 1538: 'I'm singin' in the rain' + 1539: 'Alice in Wonderland' + 1540: 'The wizard of Oz' + 1541: 'I'm singin' in the rain' + 1542: 'Alice in Wonderland' + 1543: 'The wizard of Oz' + 1544: 'I'm singin' in the rain' + 1545: 'Alice in Wonderland' + 1546: 'The wizard of Oz' + 1547: 'I'm singin' in the rain' + 1548: 'Alice in Wonderland' + 1549: 'The wizard of Oz' + 1550: 'I'm singin' in the rain' + 1551: 'Alice in Wonderland' + 1552: 'The wizard of Oz' + 1553: 'I'm singin' in the rain' + 1554: 'Alice in Wonderland' + 1555: 'The wizard of Oz' + 1556: 'I'm singin' in the rain' + 1557: 'Alice in Wonderland' + 1558: 'The wizard of Oz' + 1559: 'I'm singin' in the rain' + 1560: 'Alice in Wonderland' + 1561: 'The wizard of Oz' + 1562: 'I'm singin' in the rain' + 1563: 'Alice in Wonderland' + 1564: 'The wizard of Oz' + 1565: 'I'm singin' in the rain' + 1566: 'Alice in Wonderland' + 1567: 'The wizard of Oz' + 1568: 'I'm singin' in the rain' + 1569: 'Alice in Wonderland' + 1570: 'The wizard of Oz' + 1571: 'I'm singin' in the rain' + 1572: 'Alice in Wonderland' + 1573: 'The wizard of Oz' + 1574: 'I'm singin' in the rain' + 1575: 'Alice in Wonderland' + 1576: 'The wizard of Oz' + 1577: 'I'm singin' in the rain' + 1578: 'Alice in Wonderland' + 1579: 'The wizard of Oz' + 1580: 'I'm singin' in the rain' + 1581: 'Alice in Wonderland' + 1582: 'The wizard of Oz' + 1583: 'I'm singin' in the rain' + 1584: 'Alice in Wonderland' + 1585: 'The wizard of Oz' + 1586: 'I'm singin' in the rain' + 1587: 'Alice in Wonderland' + 1588: 'The wizard of Oz' + 1589: 'I'm singin' in the rain' + 1590: 'Alice in Wonderland' + 1591: 'The wizard of Oz' + 1592: 'I'm singin' in the rain' + 1593: 'Alice in Wonderland' + 1594: 'The wizard of Oz' + 1595: 'I'm singin' in the rain' + 1596: 'Alice in Wonderland' + 1597: 'The wizard of Oz' + 1598: 'I'm singin' in the rain' + 1599: 'Alice in Wonderland' + 1600: 'The wizard of Oz' + 1601: 'I'm singin' in the rain' + 1602: 'Alice in Wonderland' + 1603: 'The wizard of Oz' + 1604: 'I'm singin' in the rain' + 1605: 'Alice in Wonderland' + 1606: 'The wizard of Oz' + 1607: 'I'm singin' in the rain' + 1608: 'Alice in Wonderland' + 1609: 'The wizard of Oz' + 1610: 'I'm singin' in the rain' + 1611: 'Alice in Wonderland' + 1612: 'The wizard of Oz' + 1613: 'I'm singin' in the rain' + 1614: 'Alice in Wonderland' + 1615: 'The wizard of Oz' + 1616: 'I'm singin' in the rain' + 1617: 'Alice in Wonderland' + 1618: 'The wizard of Oz' + 1619: 'I'm singin' in the rain' + 1620: 'Alice in Wonderland' + 1621: 'The wizard of Oz' + 1622: 'I'm singin' in the rain' + 1623: 'Alice in Wonderland' + 1624: 'The wizard of Oz' + 1625: 'I'm singin' in the rain' + 1626: 'Alice in Wonderland' + 1627: 'The wizard of Oz' + 1628: 'I'm singin' in the rain' + 1629: 'Alice in Wonderland' + 1630: 'The wizard of Oz' + 1631: 'I'm singin' in the rain' + 1632: 'Alice in Wonderland' + 1633: 'The wizard of Oz' + 1634: 'I'm singin' in the rain' + 1635: 'Alice in Wonderland' + 1636: 'The wizard of Oz' + 1637: 'I'm singin' in the rain' + 1638: 'Alice in Wonderland' + 1639: 'The wizard of Oz' + 1640: 'I'm singin' in the rain' + 1641: 'Alice in Wonderland' + 1642: 'The wizard of Oz' + 1643: 'I'm singin' in the rain' + 1644: 'Alice in Wonderland' + 1645: 'The wizard of Oz' + 1646: 'I'm singin' in the rain' + 1647: 'Alice in Wonderland' + 1648: 'The wizard of Oz' + 1649: 'I'm singin' in the rain' + 1650: 'Alice in Wonderland' + 1651: 'The wizard of Oz' + 1652: 'I'm singin' in the rain' + 1653: 'Alice in Wonderland' + 1654: 'The wizard of Oz' + 1655: 'I'm singin' in the rain' + 1656: 'Alice in Wonderland' + 1657: 'The wizard of Oz' + 1658: 'I'm singin' in the rain' + 1659: 'Alice in Wonderland' + 1660: 'The wizard of Oz' + 1661: 'I'm singin' in the rain' + 1662: 'Alice in Wonderland' + 1663: 'The wizard of Oz' + 1664: 'I'm singin' in the rain' + 1665: 'Alice in Wonderland' + 1666: 'The wizard of Oz' + 1667: 'I'm singin' in the rain' + 1668: 'Alice in Wonderland' + 1669: 'The wizard of Oz' + 1670: 'I'm singin' in the rain' + 1671: 'Alice in Wonderland' + 1672: 'The wizard of Oz' + 1673: 'I'm singin' in the rain' + 1674: 'Alice in Wonderland' + 1675: 'The wizard of Oz' + 1676: 'I'm singin' in the rain' + 1677: 'Alice in Wonderland' + 1678: 'The wizard of Oz' + 1679: 'I'm singin' in the rain' + 1680: 'Alice in Wonderland' + 1681: 'The wizard of Oz' + 1682: 'I'm singin' in the rain' + 1683: 'Alice in Wonderland' + 1684: 'The wizard of Oz' + 1685: 'I'm singin' in the rain' + 1686: 'Alice in Wonderland' + 1687: 'The wizard of Oz' + 1688: 'I'm singin' in the rain' + 1689: 'Alice in Wonderland' + 1690: 'The wizard of Oz' + 1691: 'I'm singin' in the rain' + 1692: 'Alice in Wonderland' + 1693: 'The wizard of Oz' + 1694: 'I'm singin' in the rain' + 1695: 'Alice in Wonderland' + 1696: 'The wizard of Oz' + 1697: 'I'm singin' in the rain' + 1698: 'Alice in Wonderland' + 1699: 'The wizard of Oz' + 1700: 'I'm singin' in the rain' + 1701: 'Alice in Wonderland' + 1702: 'The wizard of Oz' + 1703: 'I'm singin' in the rain' + 1704: 'Alice in Wonderland' + 1705: 'The wizard of Oz' + 1706: 'I'm singin' in the rain' + 1707: 'Alice in Wonderland' + 1708: 'The wizard of Oz' + 1709: 'I'm singin' in the rain' + 1710: 'Alice in Wonderland' + 1711: 'The wizard of Oz' + 1712: 'I'm singin' in the rain' + 1713: 'Alice in Wonderland' + 1714: 'The wizard of Oz' + 1715: 'I'm singin' in the rain' + 1716: 'Alice in Wonderland' + 1717: 'The wizard of Oz' + 1718: 'I'm singin' in the rain' + 1719: 'Alice in Wonderland' + 1720: 'The wizard of Oz' + 1721: 'I'm singin' in the rain' + 1722: 'Alice in Wonderland' + 1723: 'The wizard of Oz' + 1724: 'I'm singin' in the rain' + 1725: 'Alice in Wonderland' + 1726: 'The wizard of Oz' + 1727: 'I'm singin' in the rain' + 1728: 'Alice in Wonderland' + 1729: 'The wizard of Oz' + 1730: 'I'm singin' in the rain' + 1731: 'Alice in Wonderland' + 1732: 'The wizard of Oz' + 1733: 'I'm singin' in the rain' + 1734: 'Alice in Wonderland' + 1735: 'The wizard of Oz' + 1736: 'I'm singin' in the rain' + 1737: 'Alice in Wonderland' + 1738: 'The wizard of Oz' + 1739: 'I'm singin' in the rain' + 1740: 'Alice in Wonderland' + 1741: 'The wizard of Oz' + 1742: 'I'm singin' in the rain' + 1743: 'Alice in Wonderland' + 1744: 'The wizard of Oz' + 1745: 'I'm singin' in the rain' + 1746: 'Alice in Wonderland' + 1747: 'The wizard of Oz' + 1748: 'I'm singin' in the rain' + 1749: 'Alice in Wonderland' + 1750: 'The wizard of Oz' + 1751: 'I'm singin' in the rain' + 1752: 'Alice in Wonderland' + 1753: 'The wizard of Oz' + 1754: 'I'm singin' in the rain' + 1755: 'Alice in Wonderland' + 1756: 'The wizard of Oz' + 1757: 'I'm singin' in the rain' + 1758: 'Alice in Wonderland' + 1759: 'The wizard of Oz' + 1760: 'I'm singin' in the rain' + 1761: 'Alice in Wonderland' + 1762: 'The wizard of Oz' + 1763: 'I'm singin' in the rain' + 1764: 'Alice in Wonderland' + 1765: 'The wizard of Oz' + 1766: 'I'm singin' in the rain' + 1767: 'Alice in Wonderland' + 1768: 'The wizard of Oz' + 1769: 'I'm singin' in the rain' + 1770: 'Alice in Wonderland' + 1771: 'The wizard of Oz' + 1772: 'I'm singin' in the rain' + 1773: 'Alice in Wonderland' + 1774: 'The wizard of Oz' + 1775: 'I'm singin' in the rain' + 1776: 'Alice in Wonderland' + 1777: 'The wizard of Oz' + 1778: 'I'm singin' in the rain' + 1779: 'Alice in Wonderland' + 1780: 'The wizard of Oz' + 1781: 'I'm singin' in the rain' + 1782: 'Alice in Wonderland' + 1783: 'The wizard of Oz' + 1784: 'I'm singin' in the rain' + 1785: 'Alice in Wonderland' + 1786: 'The wizard of Oz' + 1787: 'I'm singin' in the rain' + 1788: 'Alice in Wonderland' + 1789: 'The wizard of Oz' + 1790: 'I'm singin' in the rain' + 1791: 'Alice in Wonderland' + 1792: 'The wizard of Oz' + 1793: 'I'm singin' in the rain' + 1794: 'Alice in Wonderland' + 1795: 'The wizard of Oz' + 1796: 'I'm singin' in the rain' + 1797: 'Alice in Wonderland' + 1798: 'The wizard of Oz' + 1799: 'I'm singin' in the rain' + 1800: 'Alice in Wonderland' + 1801: 'The wizard of Oz' + 1802: 'I'm singin' in the rain' + 1803: 'Alice in Wonderland' + 1804: 'The wizard of Oz' + 1805: 'I'm singin' in the rain' + 1806: 'Alice in Wonderland' + 1807: 'The wizard of Oz' + 1808: 'I'm singin' in the rain' + 1809: 'Alice in Wonderland' + 1810: 'The wizard of Oz' + 1811: 'I'm singin' in the rain' + 1812: 'Alice in Wonderland' + 1813: 'The wizard of Oz' + 1814: 'I'm singin' in the rain' + 1815: 'Alice in Wonderland' + 1816: 'The wizard of Oz' + 1817: 'I'm singin' in the rain' + 1818: 'Alice in Wonderland' + 1819: 'The wizard of Oz' + 1820: 'I'm singin' in the rain' + 1821: 'Alice in Wonderland' + 1822: 'The wizard of Oz' + 1823: 'I'm singin' in the rain' + 1824: 'Alice in Wonderland' + 1825: 'The wizard of Oz' + 1826: 'I'm singin' in the rain' + 1827: 'Alice in Wonderland' + 1828: 'The wizard of Oz' + 1829: 'I'm singin' in the rain' + 1830: 'Alice in Wonderland' + 1831: 'The wizard of Oz' + 1832: 'I'm singin' in the rain' + 1833: 'Alice in Wonderland' + 1834: 'The wizard of Oz' + 1835: 'I'm singin' in the rain' + 1836: 'Alice in Wonderland' + 1837: 'The wizard of Oz' + 1838: 'I'm singin' in the rain' + 1839: 'Alice in Wonderland' + 1840: 'The wizard of Oz' + 1841: 'I'm singin' in the rain' + 1842: 'Alice in Wonderland' + 1843: 'The wizard of Oz' + 1844: 'I'm singin' in the rain' + 1845: 'Alice in Wonderland' + 1846: 'The wizard of Oz' + 1847: 'I'm singin' in the rain' + 1848: 'Alice in Wonderland' + 1849: 'The wizard of Oz' + 1850: 'I'm singin' in the rain' + 1851: 'Alice in Wonderland' + 1852: 'The wizard of Oz' + 1853: 'I'm singin' in the rain' + 1854: 'Alice in Wonderland' + 1855: 'The wizard of Oz' + 1856: 'I'm singin' in the rain' + 1857: 'Alice in Wonderland' + 1858: 'The wizard of Oz' + 1859: 'I'm singin' in the rain' + 1860: 'Alice in Wonderland' + 1861: 'The wizard of Oz' + 1862: 'I'm singin' in the rain' + 1863: 'Alice in Wonderland' + 1864: 'The wizard of Oz' + 1865: 'I'm singin' in the rain' + 1866: 'Alice in Wonderland' + 1867: 'The wizard of Oz' + 1868: 'I'm singin' in the rain' + 1869: 'Alice in Wonderland' + 1870: 'The wizard of Oz' + 1871: 'I'm singin' in the rain' + 1872: 'Alice in Wonderland' + 1873: 'The wizard of Oz' + 1874: 'I'm singin' in the rain' + 1875: 'Alice in Wonderland' + 1876: 'The wizard of Oz' + 1877: 'I'm singin' in the rain' + 1878: 'Alice in Wonderland' + 1879: 'The wizard of Oz' + 1880: 'I'm singin' in the rain' + 1881: 'Alice in Wonderland' + 1882: 'The wizard of Oz' + 1883: 'I'm singin' in the rain' + 1884: 'Alice in Wonderland' + 1885: 'The wizard of Oz' + 1886: 'I'm singin' in the rain' + 1887: 'Alice in Wonderland' + 1888: 'The wizard of Oz' + 1889: 'I'm singin' in the rain' + 1890: 'Alice in Wonderland' + 1891: 'The wizard of Oz' + 1892: 'I'm singin' in the rain' + 1893: 'Alice in Wonderland' + 1894: 'The wizard of Oz' + 1895: 'I'm singin' in the rain' + 1896: 'Alice in Wonderland' + 1897: 'The wizard of Oz' + 1898: 'I'm singin' in the rain' + 1899: 'Alice in Wonderland' + 1900: 'The wizard of Oz' + 1901: 'I'm singin' in the rain' + 1902: 'Alice in Wonderland' + 1903: 'The wizard of Oz' + 1904: 'I'm singin' in the rain' + 1905: 'Alice in Wonderland' + 1906: 'The wizard of Oz' + 1907: 'I'm singin' in the rain' + 1908: 'Alice in Wonderland' + 1909: 'The wizard of Oz' + 1910: 'I'm singin' in the rain' + 1911: 'Alice in Wonderland' + 1912: 'The wizard of Oz' + 1913: 'I'm singin' in the rain' + 1914: 'Alice in Wonderland' + 1915: 'The wizard of Oz' + 1916: 'I'm singin' in the rain' + 1917: 'Alice in Wonderland' + 1918: 'The wizard of Oz' + 1919: 'I'm singin' in the rain' + 1920: 'Alice in Wonderland' + 1921: 'The wizard of Oz' + 1922: 'I'm singin' in the rain' + 1923: 'Alice in Wonderland' + 1924: 'The wizard of Oz' + 1925: 'I'm singin' in the rain' + 1926: 'Alice in Wonderland' + 1927: 'The wizard of Oz' + 1928: 'I'm singin' in the rain' + 1929: 'Alice in Wonderland' + 1930: 'The wizard of Oz' + 1931: 'I'm singin' in the rain' + 1932: 'Alice in Wonderland' + 1933: 'The wizard of Oz' + 1934: 'I'm singin' in the rain' + 1935: 'Alice in Wonderland' + 1936: 'The wizard of Oz' + 1937: 'I'm singin' in the rain' + 1938: 'Alice in Wonderland' + 1939: 'The wizard of Oz' + 1940: 'I'm singin' in the rain' + 1941: 'Alice in Wonderland' + 1942: 'The wizard of Oz' + 1943: 'I'm singin' in the rain' + 1944: 'Alice in Wonderland' + 1945: 'The wizard of Oz' + 1946: 'I'm singin' in the rain' + 1947: 'Alice in Wonderland' + 1948: 'The wizard of Oz' + 1949: 'I'm singin' in the rain' + 1950: 'Alice in Wonderland' + 1951: 'The wizard of Oz' + 1952: 'I'm singin' in the rain' + 1953: 'Alice in Wonderland' + 1954: 'The wizard of Oz' + 1955: 'I'm singin' in the rain' + 1956: 'Alice in Wonderland' + 1957: 'The wizard of Oz' + 1958: 'I'm singin' in the rain' + 1959: 'Alice in Wonderland' + 1960: 'The wizard of Oz' + 1961: 'I'm singin' in the rain' + 1962: 'Alice in Wonderland' + 1963: 'The wizard of Oz' + 1964: 'I'm singin' in the rain' + 1965: 'Alice in Wonderland' + 1966: 'The wizard of Oz' + 1967: 'I'm singin' in the rain' + 1968: 'Alice in Wonderland' + 1969: 'The wizard of Oz' + 1970: 'I'm singin' in the rain' + 1971: 'Alice in Wonderland' + 1972: 'The wizard of Oz' + 1973: 'I'm singin' in the rain' + 1974: 'Alice in Wonderland' + 1975: 'The wizard of Oz' + 1976: 'I'm singin' in the rain' + 1977: 'Alice in Wonderland' + 1978: 'The wizard of Oz' + 1979: 'I'm singin' in the rain' + 1980: 'Alice in Wonderland' + 1981: 'The wizard of Oz' + 1982: 'I'm singin' in the rain' + 1983: 'Alice in Wonderland' + 1984: 'The wizard of Oz' + 1985: 'I'm singin' in the rain' + 1986: 'Alice in Wonderland' + 1987: 'The wizard of Oz' + 1988: 'I'm singin' in the rain' + 1989: 'Alice in Wonderland' + 1990: 'The wizard of Oz' + 1991: 'I'm singin' in the rain' + 1992: 'Alice in Wonderland' + 1993: 'The wizard of Oz' + 1994: 'I'm singin' in the rain' + 1995: 'Alice in Wonderland' + 1996: 'The wizard of Oz' + 1997: 'I'm singin' in the rain' + 1998: 'Alice in Wonderland' + 1999: 'The wizard of Oz' + 2000: 'I'm singin' in the rain' + 2001: 'Alice in Wonderland' + 2002: 'The wizard of Oz' + 2003: 'I'm singin' in the rain' + 2004: 'Alice in Wonderland' + 2005: 'The wizard of Oz' + 2006: 'I'm singin' in the rain' + 2007: 'Alice in Wonderland' + 2008: 'The wizard of Oz' + 2009: 'I'm singin' in the rain' + 2010: 'Alice in Wonderland' + 2011: 'The wizard of Oz' + 2012: 'I'm singin' in the rain' + 2013: 'Alice in Wonderland' + 2014: 'The wizard of Oz' + 2015: 'I'm singin' in the rain' + 2016: 'Alice in Wonderland' + 2017: 'The wizard of Oz' + 2018: 'I'm singin' in the rain' + 2019: 'Alice in Wonderland' + 2020: 'The wizard of Oz' + 2021: 'I'm singin' in the rain' + 2022: 'Alice in Wonderland' + 2023: 'The wizard of Oz' + 2024: 'I'm singin' in the rain' + 2025: 'Alice in Wonderland' + 2026: 'The wizard of Oz' + 2027: 'I'm singin' in the rain' + 2028: 'Alice in Wonderland' + 2029: 'The wizard of Oz' + 2030: 'I'm singin' in the rain' + 2031: 'Alice in Wonderland' + 2032: 'The wizard of Oz' + 2033: 'I'm singin' in the rain' + 2034: 'Alice in Wonderland' + 2035: 'The wizard of Oz' + 2036: 'I'm singin' in the rain' + 2037: 'Alice in Wonderland' + 2038: 'The wizard of Oz' + 2039: 'I'm singin' in the rain' + 2040: 'Alice in Wonderland' + 2041: 'The wizard of Oz' + 2042: 'I'm singin' in the rain' + 2043: 'Alice in Wonderland' + 2044: 'The wizard of Oz' + 2045: 'I'm singin' in the rain' + 2046: 'Alice in Wonderland' + 2047: 'The wizard of Oz' + 2048: 'I'm singin' in the rain' + 2049: 'Alice in Wonderland' + 2050: 'The wizard of Oz' + 2051: 'I'm singin' in the rain' + 2052: 'Alice in Wonderland' + 2053: 'The wizard of Oz' + 2054: 'I'm singin' in the rain' + 2055: 'Alice in Wonderland' + 2056: 'The wizard of Oz' + 2057: 'I'm singin' in the rain' + 2058: 'Alice in Wonderland' + 2059: 'The wizard of Oz' + 2060: 'I'm singin' in the rain' + 2061: 'Alice in Wonderland' + 2062: 'The wizard of Oz' + 2063: 'I'm singin' in the rain' + 2064: 'Alice in Wonderland' + 2065: 'The wizard of Oz' + 2066: 'I'm singin' in the rain' + 2067: 'Alice in Wonderland' + 2068: 'The wizard of Oz' + 2069: 'I'm singin' in the rain' + 2070: 'Alice in Wonderland' + 2071: 'The wizard of Oz' + 2072: 'I'm singin' in the rain' + 2073: 'Alice in Wonderland' + 2074: 'The wizard of Oz' + 2075: 'I'm singin' in the rain' + 2076: 'Alice in Wonderland' + 2077: 'The wizard of Oz' + 2078: 'I'm singin' in the rain' + 2079: 'Alice in Wonderland' + 2080: 'The wizard of Oz' + 2081: 'I'm singin' in the rain' + 2082: 'Alice in Wonderland' + 2083: 'The wizard of Oz' + 2084: 'I'm singin' in the rain' + 2085: 'Alice in Wonderland' + 2086: 'The wizard of Oz' + 2087: 'I'm singin' in the rain' + 2088: 'Alice in Wonderland' + 2089: 'The wizard of Oz' + 2090: 'I'm singin' in the rain' + 2091: 'Alice in Wonderland' + 2092: 'The wizard of Oz' + 2093: 'I'm singin' in the rain' + 2094: 'Alice in Wonderland' + 2095: 'The wizard of Oz' + 2096: 'I'm singin' in the rain' + 2097: 'Alice in Wonderland' + 2098: 'The wizard of Oz' + 2099: 'I'm singin' in the rain' + 2100: 'Alice in Wonderland' + 2101: 'The wizard of Oz' + 2102: 'I'm singin' in the rain' + 2103: 'Alice in Wonderland' + 2104: 'The wizard of Oz' + 2105: 'I'm singin' in the rain' + 2106: 'Alice in Wonderland' + 2107: 'The wizard of Oz' + 2108: 'I'm singin' in the rain' + 2109: 'Alice in Wonderland' + 2110: 'The wizard of Oz' + 2111: 'I'm singin' in the rain' + 2112: 'Alice in Wonderland' + 2113: 'The wizard of Oz' + 2114: 'I'm singin' in the rain' + 2115: 'Alice in Wonderland' + 2116: 'The wizard of Oz' + 2117: 'I'm singin' in the rain' + 2118: 'Alice in Wonderland' + 2119: 'The wizard of Oz' + 2120: 'I'm singin' in the rain' + 2121: 'Alice in Wonderland' + 2122: 'The wizard of Oz' + 2123: 'I'm singin' in the rain' + 2124: 'Alice in Wonderland' + 2125: 'The wizard of Oz' + 2126: 'I'm singin' in the rain' + 2127: 'Alice in Wonderland' + 2128: 'The wizard of Oz' + 2129: 'I'm singin' in the rain' + 2130: 'Alice in Wonderland' + 2131: 'The wizard of Oz' + 2132: 'I'm singin' in the rain' + 2133: 'Alice in Wonderland' + 2134: 'The wizard of Oz' + 2135: 'I'm singin' in the rain' + 2136: 'Alice in Wonderland' + 2137: 'The wizard of Oz' + 2138: 'I'm singin' in the rain' + 2139: 'Alice in Wonderland' + 2140: 'The wizard of Oz' + 2141: 'I'm singin' in the rain' + 2142: 'Alice in Wonderland' + 2143: 'The wizard of Oz' + 2144: 'I'm singin' in the rain' + 2145: 'Alice in Wonderland' + 2146: 'The wizard of Oz' + 2147: 'I'm singin' in the rain' + 2148: 'Alice in Wonderland' + 2149: 'The wizard of Oz' + 2150: 'I'm singin' in the rain' + 2151: 'Alice in Wonderland' + 2152: 'The wizard of Oz' + 2153: 'I'm singin' in the rain' + 2154: 'Alice in Wonderland' + 2155: 'The wizard of Oz' + 2156: 'I'm singin' in the rain' + 2157: 'Alice in Wonderland' + 2158: 'The wizard of Oz' + 2159: 'I'm singin' in the rain' + 2160: 'Alice in Wonderland' + 2161: 'The wizard of Oz' + 2162: 'I'm singin' in the rain' + 2163: 'Alice in Wonderland' + 2164: 'The wizard of Oz' + 2165: 'I'm singin' in the rain' + 2166: 'Alice in Wonderland' + 2167: 'The wizard of Oz' + 2168: 'I'm singin' in the rain' + 2169: 'Alice in Wonderland' + 2170: 'The wizard of Oz' + 2171: 'I'm singin' in the rain' + 2172: 'Alice in Wonderland' + 2173: 'The wizard of Oz' + 2174: 'I'm singin' in the rain' + 2175: 'Alice in Wonderland' + 2176: 'The wizard of Oz' + 2177: 'I'm singin' in the rain' + 2178: 'Alice in Wonderland' + 2179: 'The wizard of Oz' + 2180: 'I'm singin' in the rain' + 2181: 'Alice in Wonderland' + 2182: 'The wizard of Oz' + 2183: 'I'm singin' in the rain' + 2184: 'Alice in Wonderland' + 2185: 'The wizard of Oz' + 2186: 'I'm singin' in the rain' + 2187: 'Alice in Wonderland' + 2188: 'The wizard of Oz' + 2189: 'I'm singin' in the rain' + 2190: 'Alice in Wonderland' + 2191: 'The wizard of Oz' + 2192: 'I'm singin' in the rain' + 2193: 'Alice in Wonderland' + 2194: 'The wizard of Oz' + 2195: 'I'm singin' in the rain' + 2196: 'Alice in Wonderland' + 2197: 'The wizard of Oz' + 2198: 'I'm singin' in the rain' + 2199: 'Alice in Wonderland' + 2200: 'The wizard of Oz' + 2201: 'I'm singin' in the rain' + 2202: 'Alice in Wonderland' + 2203: 'The wizard of Oz' + 2204: 'I'm singin' in the rain' + 2205: 'Alice in Wonderland' + 2206: 'The wizard of Oz' + 2207: 'I'm singin' in the rain' + 2208: 'Alice in Wonderland' + 2209: 'The wizard of Oz' + 2210: 'I'm singin' in the rain' + 2211: 'Alice in Wonderland' + 2212: 'The wizard of Oz' + 2213: 'I'm singin' in the rain' + 2214: 'Alice in Wonderland' + 2215: 'The wizard of Oz' + 2216: 'I'm singin' in the rain' + 2217: 'Alice in Wonderland' + 2218: 'The wizard of Oz' + 2219: 'I'm singin' in the rain' + 2220: 'Alice in Wonderland' + 2221: 'The wizard of Oz' + 2222: 'I'm singin' in the rain' + 2223: 'Alice in Wonderland' + 2224: 'The wizard of Oz' + 2225: 'I'm singin' in the rain' + 2226: 'Alice in Wonderland' + 2227: 'The wizard of Oz' + 2228: 'I'm singin' in the rain' + 2229: 'Alice in Wonderland' + 2230: 'The wizard of Oz' + 2231: 'I'm singin' in the rain' + 2232: 'Alice in Wonderland' + 2233: 'The wizard of Oz' + 2234: 'I'm singin' in the rain' + 2235: 'Alice in Wonderland' + 2236: 'The wizard of Oz' + 2237: 'I'm singin' in the rain' + 2238: 'Alice in Wonderland' + 2239: 'The wizard of Oz' + 2240: 'I'm singin' in the rain' + 2241: 'Alice in Wonderland' + 2242: 'The wizard of Oz' + 2243: 'I'm singin' in the rain' + 2244: 'Alice in Wonderland' + 2245: 'The wizard of Oz' + 2246: 'I'm singin' in the rain' + 2247: 'Alice in Wonderland' + 2248: 'The wizard of Oz' + 2249: 'I'm singin' in the rain' + 2250: 'Alice in Wonderland' + 2251: 'The wizard of Oz' + 2252: 'I'm singin' in the rain' + 2253: 'Alice in Wonderland' + 2254: 'The wizard of Oz' + 2255: 'I'm singin' in the rain' + 2256: 'Alice in Wonderland' + 2257: 'The wizard of Oz' + 2258: 'I'm singin' in the rain' + 2259: 'Alice in Wonderland' + 2260: 'The wizard of Oz' + 2261: 'I'm singin' in the rain' + 2262: 'Alice in Wonderland' + 2263: 'The wizard of Oz' + 2264: 'I'm singin' in the rain' + 2265: 'Alice in Wonderland' + 2266: 'The wizard of Oz' + 2267: 'I'm singin' in the rain' + 2268: 'Alice in Wonderland' + 2269: 'The wizard of Oz' + 2270: 'I'm singin' in the rain' + 2271: 'Alice in Wonderland' + 2272: 'The wizard of Oz' + 2273: 'I'm singin' in the rain' + 2274: 'Alice in Wonderland' + 2275: 'The wizard of Oz' + 2276: 'I'm singin' in the rain' + 2277: 'Alice in Wonderland' + 2278: 'The wizard of Oz' + 2279: 'I'm singin' in the rain' + 2280: 'Alice in Wonderland' + 2281: 'The wizard of Oz' + 2282: 'I'm singin' in the rain' + 2283: 'Alice in Wonderland' + 2284: 'The wizard of Oz' + 2285: 'I'm singin' in the rain' + 2286: 'Alice in Wonderland' + 2287: 'The wizard of Oz' + 2288: 'I'm singin' in the rain' + 2289: 'Alice in Wonderland' + 2290: 'The wizard of Oz' + 2291: 'I'm singin' in the rain' + 2292: 'Alice in Wonderland' + 2293: 'The wizard of Oz' + 2294: 'I'm singin' in the rain' + 2295: 'Alice in Wonderland' + 2296: 'The wizard of Oz' + 2297: 'I'm singin' in the rain' + 2298: 'Alice in Wonderland' + 2299: 'The wizard of Oz' + 2300: 'I'm singin' in the rain' + 2301: 'Alice in Wonderland' + 2302: 'The wizard of Oz' + 2303: 'I'm singin' in the rain' + 2304: 'Alice in Wonderland' + 2305: 'The wizard of Oz' + 2306: 'I'm singin' in the rain' + 2307: 'Alice in Wonderland' + 2308: 'The wizard of Oz' + 2309: 'I'm singin' in the rain' + 2310: 'Alice in Wonderland' + 2311: 'The wizard of Oz' + 2312: 'I'm singin' in the rain' + 2313: 'Alice in Wonderland' + 2314: 'The wizard of Oz' + 2315: 'I'm singin' in the rain' + 2316: 'Alice in Wonderland' + 2317: 'The wizard of Oz' + 2318: 'I'm singin' in the rain' + 2319: 'Alice in Wonderland' + 2320: 'The wizard of Oz' + 2321: 'I'm singin' in the rain' + 2322: 'Alice in Wonderland' + 2323: 'The wizard of Oz' + 2324: 'I'm singin' in the rain' + 2325: 'Alice in Wonderland' + 2326: 'The wizard of Oz' + 2327: 'I'm singin' in the rain' + 2328: 'Alice in Wonderland' + 2329: 'The wizard of Oz' + 2330: 'I'm singin' in the rain' + 2331: 'Alice in Wonderland' + 2332: 'The wizard of Oz' + 2333: 'I'm singin' in the rain' + 2334: 'Alice in Wonderland' + 2335: 'The wizard of Oz' + 2336: 'I'm singin' in the rain' + 2337: 'Alice in Wonderland' + 2338: 'The wizard of Oz' + 2339: 'I'm singin' in the rain' + 2340: 'Alice in Wonderland' + 2341: 'The wizard of Oz' + 2342: 'I'm singin' in the rain' + 2343: 'Alice in Wonderland' + 2344: 'The wizard of Oz' + 2345: 'I'm singin' in the rain' + 2346: 'Alice in Wonderland' + 2347: 'The wizard of Oz' + 2348: 'I'm singin' in the rain' + 2349: 'Alice in Wonderland' + 2350: 'The wizard of Oz' + 2351: 'I'm singin' in the rain' + 2352: 'Alice in Wonderland' + 2353: 'The wizard of Oz' + 2354: 'I'm singin' in the rain' + 2355: 'Alice in Wonderland' + 2356: 'The wizard of Oz' + 2357: 'I'm singin' in the rain' + 2358: 'Alice in Wonderland' + 2359: 'The wizard of Oz' + 2360: 'I'm singin' in the rain' + 2361: 'Alice in Wonderland' + 2362: 'The wizard of Oz' + 2363: 'I'm singin' in the rain' + 2364: 'Alice in Wonderland' + 2365: 'The wizard of Oz' + 2366: 'I'm singin' in the rain' + 2367: 'Alice in Wonderland' + 2368: 'The wizard of Oz' + 2369: 'I'm singin' in the rain' + 2370: 'Alice in Wonderland' + 2371: 'The wizard of Oz' + 2372: 'I'm singin' in the rain' + 2373: 'Alice in Wonderland' + 2374: 'The wizard of Oz' + 2375: 'I'm singin' in the rain' + 2376: 'Alice in Wonderland' + 2377: 'The wizard of Oz' + 2378: 'I'm singin' in the rain' + 2379: 'Alice in Wonderland' + 2380: 'The wizard of Oz' + 2381: 'I'm singin' in the rain' + 2382: 'Alice in Wonderland' + 2383: 'The wizard of Oz' + 2384: 'I'm singin' in the rain' + 2385: 'Alice in Wonderland' + 2386: 'The wizard of Oz' + 2387: 'I'm singin' in the rain' + 2388: 'Alice in Wonderland' + 2389: 'The wizard of Oz' + 2390: 'I'm singin' in the rain' + 2391: 'Alice in Wonderland' + 2392: 'The wizard of Oz' + 2393: 'I'm singin' in the rain' + 2394: 'Alice in Wonderland' + 2395: 'The wizard of Oz' + 2396: 'I'm singin' in the rain' + 2397: 'Alice in Wonderland' + 2398: 'The wizard of Oz' + 2399: 'I'm singin' in the rain' + 2400: 'Alice in Wonderland' + 2401: 'The wizard of Oz' + 2402: 'I'm singin' in the rain' + 2403: 'Alice in Wonderland' + 2404: 'The wizard of Oz' + 2405: 'I'm singin' in the rain' + 2406: 'Alice in Wonderland' + 2407: 'The wizard of Oz' + 2408: 'I'm singin' in the rain' + 2409: 'Alice in Wonderland' + 2410: 'The wizard of Oz' + 2411: 'I'm singin' in the rain' + 2412: 'Alice in Wonderland' + 2413: 'The wizard of Oz' + 2414: 'I'm singin' in the rain' + 2415: 'Alice in Wonderland' + 2416: 'The wizard of Oz' + 2417: 'I'm singin' in the rain' + 2418: 'Alice in Wonderland' + 2419: 'The wizard of Oz' + 2420: 'I'm singin' in the rain' + 2421: 'Alice in Wonderland' + 2422: 'The wizard of Oz' + 2423: 'I'm singin' in the rain' + 2424: 'Alice in Wonderland' + 2425: 'The wizard of Oz' + 2426: 'I'm singin' in the rain' + 2427: 'Alice in Wonderland' + 2428: 'The wizard of Oz' + 2429: 'I'm singin' in the rain' + 2430: 'Alice in Wonderland' + 2431: 'The wizard of Oz' + 2432: 'I'm singin' in the rain' + 2433: 'Alice in Wonderland' + 2434: 'The wizard of Oz' + 2435: 'I'm singin' in the rain' + 2436: 'Alice in Wonderland' + 2437: 'The wizard of Oz' + 2438: 'I'm singin' in the rain' + 2439: 'Alice in Wonderland' + 2440: 'The wizard of Oz' + 2441: 'I'm singin' in the rain' + 2442: 'Alice in Wonderland' + 2443: 'The wizard of Oz' + 2444: 'I'm singin' in the rain' + 2445: 'Alice in Wonderland' + 2446: 'The wizard of Oz' + 2447: 'I'm singin' in the rain' + 2448: 'Alice in Wonderland' + 2449: 'The wizard of Oz' + 2450: 'I'm singin' in the rain' + 2451: 'Alice in Wonderland' + 2452: 'The wizard of Oz' + 2453: 'I'm singin' in the rain' + 2454: 'Alice in Wonderland' + 2455: 'The wizard of Oz' + 2456: 'I'm singin' in the rain' + 2457: 'Alice in Wonderland' + 2458: 'The wizard of Oz' + 2459: 'I'm singin' in the rain' + 2460: 'Alice in Wonderland' + 2461: 'The wizard of Oz' + 2462: 'I'm singin' in the rain' + 2463: 'Alice in Wonderland' + 2464: 'The wizard of Oz' + 2465: 'I'm singin' in the rain' + 2466: 'Alice in Wonderland' + 2467: 'The wizard of Oz' + 2468: 'I'm singin' in the rain' + 2469: 'Alice in Wonderland' + 2470: 'The wizard of Oz' + 2471: 'I'm singin' in the rain' + 2472: 'Alice in Wonderland' + 2473: 'The wizard of Oz' + 2474: 'I'm singin' in the rain' + 2475: 'Alice in Wonderland' + 2476: 'The wizard of Oz' + 2477: 'I'm singin' in the rain' + 2478: 'Alice in Wonderland' + 2479: 'The wizard of Oz' + 2480: 'I'm singin' in the rain' + 2481: 'Alice in Wonderland' + 2482: 'The wizard of Oz' + 2483: 'I'm singin' in the rain' + 2484: 'Alice in Wonderland' + 2485: 'The wizard of Oz' + 2486: 'I'm singin' in the rain' + 2487: 'Alice in Wonderland' + 2488: 'The wizard of Oz' + 2489: 'I'm singin' in the rain' + 2490: 'Alice in Wonderland' + 2491: 'The wizard of Oz' + 2492: 'I'm singin' in the rain' + 2493: 'Alice in Wonderland' + 2494: 'The wizard of Oz' + 2495: 'I'm singin' in the rain' + 2496: 'Alice in Wonderland' + 2497: 'The wizard of Oz' + 2498: 'I'm singin' in the rain' + 2499: 'Alice in Wonderland' + 2500: 'The wizard of Oz' + 2501: 'I'm singin' in the rain' + 2502: 'Alice in Wonderland' + 2503: 'The wizard of Oz' + 2504: 'I'm singin' in the rain' + 2505: 'Alice in Wonderland' + 2506: 'The wizard of Oz' + 2507: 'I'm singin' in the rain' + 2508: 'Alice in Wonderland' + 2509: 'The wizard of Oz' + 2510: 'I'm singin' in the rain' + 2511: 'Alice in Wonderland' + 2512: 'The wizard of Oz' + 2513: 'I'm singin' in the rain' + 2514: 'Alice in Wonderland' + 2515: 'The wizard of Oz' + 2516: 'I'm singin' in the rain' + 2517: 'Alice in Wonderland' + 2518: 'The wizard of Oz' + 2519: 'I'm singin' in the rain' + 2520: 'Alice in Wonderland' + 2521: 'The wizard of Oz' + 2522: 'I'm singin' in the rain' + 2523: 'Alice in Wonderland' + 2524: 'The wizard of Oz' + 2525: 'I'm singin' in the rain' + 2526: 'Alice in Wonderland' + 2527: 'The wizard of Oz' + 2528: 'I'm singin' in the rain' + 2529: 'Alice in Wonderland' + 2530: 'The wizard of Oz' + 2531: 'I'm singin' in the rain' + 2532: 'Alice in Wonderland' + 2533: 'The wizard of Oz' + 2534: 'I'm singin' in the rain' + 2535: 'Alice in Wonderland' + 2536: 'The wizard of Oz' + 2537: 'I'm singin' in the rain' + 2538: 'Alice in Wonderland' + 2539: 'The wizard of Oz' + 2540: 'I'm singin' in the rain' + 2541: 'Alice in Wonderland' + 2542: 'The wizard of Oz' + 2543: 'I'm singin' in the rain' + 2544: 'Alice in Wonderland' + 2545: 'The wizard of Oz' + 2546: 'I'm singin' in the rain' + 2547: 'Alice in Wonderland' + 2548: 'The wizard of Oz' + 2549: 'I'm singin' in the rain' + 2550: 'Alice in Wonderland' + 2551: 'The wizard of Oz' + 2552: 'I'm singin' in the rain' + 2553: 'Alice in Wonderland' + 2554: 'The wizard of Oz' + 2555: 'I'm singin' in the rain' + 2556: 'Alice in Wonderland' + 2557: 'The wizard of Oz' + 2558: 'I'm singin' in the rain' + 2559: 'Alice in Wonderland' + 2560: 'The wizard of Oz' + 2561: 'I'm singin' in the rain' + 2562: 'Alice in Wonderland' + 2563: 'The wizard of Oz' + 2564: 'I'm singin' in the rain' + 2565: 'Alice in Wonderland' + 2566: 'The wizard of Oz' + 2567: 'I'm singin' in the rain' + 2568: 'Alice in Wonderland' + 2569: 'The wizard of Oz' + 2570: 'I'm singin' in the rain' + 2571: 'Alice in Wonderland' + 2572: 'The wizard of Oz' + 2573: 'I'm singin' in the rain' + 2574: 'Alice in Wonderland' + 2575: 'The wizard of Oz' + 2576: 'I'm singin' in the rain' + 2577: 'Alice in Wonderland' + 2578: 'The wizard of Oz' + 2579: 'I'm singin' in the rain' + 2580: 'Alice in Wonderland' + 2581: 'The wizard of Oz' + 2582: 'I'm singin' in the rain' + 2583: 'Alice in Wonderland' + 2584: 'The wizard of Oz' + 2585: 'I'm singin' in the rain' + 2586: 'Alice in Wonderland' + 2587: 'The wizard of Oz' + 2588: 'I'm singin' in the rain' + 2589: 'Alice in Wonderland' + 2590: 'The wizard of Oz' + 2591: 'I'm singin' in the rain' + 2592: 'Alice in Wonderland' + 2593: 'The wizard of Oz' + 2594: 'I'm singin' in the rain' + 2595: 'Alice in Wonderland' + 2596: 'The wizard of Oz' + 2597: 'I'm singin' in the rain' + 2598: 'Alice in Wonderland' + 2599: 'The wizard of Oz' + 2600: 'I'm singin' in the rain' + 2601: 'Alice in Wonderland' + 2602: 'The wizard of Oz' + 2603: 'I'm singin' in the rain' + 2604: 'Alice in Wonderland' + 2605: 'The wizard of Oz' + 2606: 'I'm singin' in the rain' + 2607: 'Alice in Wonderland' + 2608: 'The wizard of Oz' + 2609: 'I'm singin' in the rain' + 2610: 'Alice in Wonderland' + 2611: 'The wizard of Oz' + 2612: 'I'm singin' in the rain' + 2613: 'Alice in Wonderland' + 2614: 'The wizard of Oz' + 2615: 'I'm singin' in the rain' + 2616: 'Alice in Wonderland' + 2617: 'The wizard of Oz' + 2618: 'I'm singin' in the rain' + 2619: 'Alice in Wonderland' + 2620: 'The wizard of Oz' + 2621: 'I'm singin' in the rain' + 2622: 'Alice in Wonderland' + 2623: 'The wizard of Oz' + 2624: 'I'm singin' in the rain' + 2625: 'Alice in Wonderland' + 2626: 'The wizard of Oz' + 2627: 'I'm singin' in the rain' + 2628: 'Alice in Wonderland' + 2629: 'The wizard of Oz' + 2630: 'I'm singin' in the rain' + 2631: 'Alice in Wonderland' + 2632: 'The wizard of Oz' + 2633: 'I'm singin' in the rain' + 2634: 'Alice in Wonderland' + 2635: 'The wizard of Oz' + 2636: 'I'm singin' in the rain' + 2637: 'Alice in Wonderland' + 2638: 'The wizard of Oz' + 2639: 'I'm singin' in the rain' + 2640: 'Alice in Wonderland' + 2641: 'The wizard of Oz' + 2642: 'I'm singin' in the rain' + 2643: 'Alice in Wonderland' + 2644: 'The wizard of Oz' + 2645: 'I'm singin' in the rain' + 2646: 'Alice in Wonderland' + 2647: 'The wizard of Oz' + 2648: 'I'm singin' in the rain' + 2649: 'Alice in Wonderland' + 2650: 'The wizard of Oz' + 2651: 'I'm singin' in the rain' + 2652: 'Alice in Wonderland' + 2653: 'The wizard of Oz' + 2654: 'I'm singin' in the rain' + 2655: 'Alice in Wonderland' + 2656: 'The wizard of Oz' + 2657: 'I'm singin' in the rain' + 2658: 'Alice in Wonderland' + 2659: 'The wizard of Oz' + 2660: 'I'm singin' in the rain' + 2661: 'Alice in Wonderland' + 2662: 'The wizard of Oz' + 2663: 'I'm singin' in the rain' + 2664: 'Alice in Wonderland' + 2665: 'The wizard of Oz' + 2666: 'I'm singin' in the rain' + 2667: 'Alice in Wonderland' + 2668: 'The wizard of Oz' + 2669: 'I'm singin' in the rain' + 2670: 'Alice in Wonderland' + 2671: 'The wizard of Oz' + 2672: 'I'm singin' in the rain' + 2673: 'Alice in Wonderland' + 2674: 'The wizard of Oz' + 2675: 'I'm singin' in the rain' + 2676: 'Alice in Wonderland' + 2677: 'The wizard of Oz' + 2678: 'I'm singin' in the rain' + 2679: 'Alice in Wonderland' + 2680: 'The wizard of Oz' + 2681: 'I'm singin' in the rain' + 2682: 'Alice in Wonderland' + 2683: 'The wizard of Oz' + 2684: 'I'm singin' in the rain' + 2685: 'Alice in Wonderland' + 2686: 'The wizard of Oz' + 2687: 'I'm singin' in the rain' + 2688: 'Alice in Wonderland' + 2689: 'The wizard of Oz' + 2690: 'I'm singin' in the rain' + 2691: 'Alice in Wonderland' + 2692: 'The wizard of Oz' + 2693: 'I'm singin' in the rain' + 2694: 'Alice in Wonderland' + 2695: 'The wizard of Oz' + 2696: 'I'm singin' in the rain' + 2697: 'Alice in Wonderland' + 2698: 'The wizard of Oz' + 2699: 'I'm singin' in the rain' + 2700: 'Alice in Wonderland' + 2701: 'The wizard of Oz' + 2702: 'I'm singin' in the rain' + 2703: 'Alice in Wonderland' + 2704: 'The wizard of Oz' + 2705: 'I'm singin' in the rain' + 2706: 'Alice in Wonderland' + 2707: 'The wizard of Oz' + 2708: 'I'm singin' in the rain' + 2709: 'Alice in Wonderland' + 2710: 'The wizard of Oz' + 2711: 'I'm singin' in the rain' + 2712: 'Alice in Wonderland' + 2713: 'The wizard of Oz' + 2714: 'I'm singin' in the rain' + 2715: 'Alice in Wonderland' + 2716: 'The wizard of Oz' + 2717: 'I'm singin' in the rain' + 2718: 'Alice in Wonderland' + 2719: 'The wizard of Oz' + 2720: 'I'm singin' in the rain' + 2721: 'Alice in Wonderland' + 2722: 'The wizard of Oz' + 2723: 'I'm singin' in the rain' + 2724: 'Alice in Wonderland' + 2725: 'The wizard of Oz' + 2726: 'I'm singin' in the rain' + 2727: 'Alice in Wonderland' + 2728: 'The wizard of Oz' + 2729: 'I'm singin' in the rain' + 2730: 'Alice in Wonderland' + 2731: 'The wizard of Oz' + 2732: 'I'm singin' in the rain' + 2733: 'Alice in Wonderland' + 2734: 'The wizard of Oz' + 2735: 'I'm singin' in the rain' + 2736: 'Alice in Wonderland' + 2737: 'The wizard of Oz' + 2738: 'I'm singin' in the rain' + 2739: 'Alice in Wonderland' + 2740: 'The wizard of Oz' + 2741: 'I'm singin' in the rain' + 2742: 'Alice in Wonderland' + 2743: 'The wizard of Oz' + 2744: 'I'm singin' in the rain' + 2745: 'Alice in Wonderland' + 2746: 'The wizard of Oz' + 2747: 'I'm singin' in the rain' + 2748: 'Alice in Wonderland' + 2749: 'The wizard of Oz' + 2750: 'I'm singin' in the rain' + 2751: 'Alice in Wonderland' + 2752: 'The wizard of Oz' + 2753: 'I'm singin' in the rain' + 2754: 'Alice in Wonderland' + 2755: 'The wizard of Oz' + 2756: 'I'm singin' in the rain' + 2757: 'Alice in Wonderland' + 2758: 'The wizard of Oz' + 2759: 'I'm singin' in the rain' + 2760: 'Alice in Wonderland' + 2761: 'The wizard of Oz' + 2762: 'I'm singin' in the rain' + 2763: 'Alice in Wonderland' + 2764: 'The wizard of Oz' + 2765: 'I'm singin' in the rain' + 2766: 'Alice in Wonderland' + 2767: 'The wizard of Oz' + 2768: 'I'm singin' in the rain' + 2769: 'Alice in Wonderland' + 2770: 'The wizard of Oz' + 2771: 'I'm singin' in the rain' + 2772: 'Alice in Wonderland' + 2773: 'The wizard of Oz' + 2774: 'I'm singin' in the rain' + 2775: 'Alice in Wonderland' + 2776: 'The wizard of Oz' + 2777: 'I'm singin' in the rain' + 2778: 'Alice in Wonderland' + 2779: 'The wizard of Oz' + 2780: 'I'm singin' in the rain' + 2781: 'Alice in Wonderland' + 2782: 'The wizard of Oz' + 2783: 'I'm singin' in the rain' + 2784: 'Alice in Wonderland' + 2785: 'The wizard of Oz' + 2786: 'I'm singin' in the rain' + 2787: 'Alice in Wonderland' + 2788: 'The wizard of Oz' + 2789: 'I'm singin' in the rain' + 2790: 'Alice in Wonderland' + 2791: 'The wizard of Oz' + 2792: 'I'm singin' in the rain' + 2793: 'Alice in Wonderland' + 2794: 'The wizard of Oz' + 2795: 'I'm singin' in the rain' + 2796: 'Alice in Wonderland' + 2797: 'The wizard of Oz' + 2798: 'I'm singin' in the rain' + 2799: 'Alice in Wonderland' + 2800: 'The wizard of Oz' + 2801: 'I'm singin' in the rain' + 2802: 'Alice in Wonderland' + 2803: 'The wizard of Oz' + 2804: 'I'm singin' in the rain' + 2805: 'Alice in Wonderland' + 2806: 'The wizard of Oz' + 2807: 'I'm singin' in the rain' + 2808: 'Alice in Wonderland' + 2809: 'The wizard of Oz' + 2810: 'I'm singin' in the rain' + 2811: 'Alice in Wonderland' + 2812: 'The wizard of Oz' + 2813: 'I'm singin' in the rain' + 2814: 'Alice in Wonderland' + 2815: 'The wizard of Oz' + 2816: 'I'm singin' in the rain' + 2817: 'Alice in Wonderland' + 2818: 'The wizard of Oz' + 2819: 'I'm singin' in the rain' + 2820: 'Alice in Wonderland' + 2821: 'The wizard of Oz' + 2822: 'I'm singin' in the rain' + 2823: 'Alice in Wonderland' + 2824: 'The wizard of Oz' + 2825: 'I'm singin' in the rain' + 2826: 'Alice in Wonderland' + 2827: 'The wizard of Oz' + 2828: 'I'm singin' in the rain' + 2829: 'Alice in Wonderland' + 2830: 'The wizard of Oz' + 2831: 'I'm singin' in the rain' + 2832: 'Alice in Wonderland' + 2833: 'The wizard of Oz' + 2834: 'I'm singin' in the rain' + 2835: 'Alice in Wonderland' + 2836: 'The wizard of Oz' + 2837: 'I'm singin' in the rain' + 2838: 'Alice in Wonderland' + 2839: 'The wizard of Oz' + 2840: 'I'm singin' in the rain' + 2841: 'Alice in Wonderland' + 2842: 'The wizard of Oz' + 2843: 'I'm singin' in the rain' + 2844: 'Alice in Wonderland' + 2845: 'The wizard of Oz' + 2846: 'I'm singin' in the rain' + 2847: 'Alice in Wonderland' + 2848: 'The wizard of Oz' + 2849: 'I'm singin' in the rain' + 2850: 'Alice in Wonderland' + 2851: 'The wizard of Oz' + 2852: 'I'm singin' in the rain' + 2853: 'Alice in Wonderland' + 2854: 'The wizard of Oz' + 2855: 'I'm singin' in the rain' + 2856: 'Alice in Wonderland' + 2857: 'The wizard of Oz' + 2858: 'I'm singin' in the rain' + 2859: 'Alice in Wonderland' + 2860: 'The wizard of Oz' + 2861: 'I'm singin' in the rain' + 2862: 'Alice in Wonderland' + 2863: 'The wizard of Oz' + 2864: 'I'm singin' in the rain' + 2865: 'Alice in Wonderland' + 2866: 'The wizard of Oz' + 2867: 'I'm singin' in the rain' + 2868: 'Alice in Wonderland' + 2869: 'The wizard of Oz' + 2870: 'I'm singin' in the rain' + 2871: 'Alice in Wonderland' + 2872: 'The wizard of Oz' + 2873: 'I'm singin' in the rain' + 2874: 'Alice in Wonderland' + 2875: 'The wizard of Oz' + 2876: 'I'm singin' in the rain' + 2877: 'Alice in Wonderland' + 2878: 'The wizard of Oz' + 2879: 'I'm singin' in the rain' + 2880: 'Alice in Wonderland' + 2881: 'The wizard of Oz' + 2882: 'I'm singin' in the rain' + 2883: 'Alice in Wonderland' + 2884: 'The wizard of Oz' + 2885: 'I'm singin' in the rain' + 2886: 'Alice in Wonderland' + 2887: 'The wizard of Oz' + 2888: 'I'm singin' in the rain' + 2889: 'Alice in Wonderland' + 2890: 'The wizard of Oz' + 2891: 'I'm singin' in the rain' + 2892: 'Alice in Wonderland' + 2893: 'The wizard of Oz' + 2894: 'I'm singin' in the rain' + 2895: 'Alice in Wonderland' + 2896: 'The wizard of Oz' + 2897: 'I'm singin' in the rain' + 2898: 'Alice in Wonderland' + 2899: 'The wizard of Oz' + 2900: 'I'm singin' in the rain' + 2901: 'Alice in Wonderland' + 2902: 'The wizard of Oz' + 2903: 'I'm singin' in the rain' + 2904: 'Alice in Wonderland' + 2905: 'The wizard of Oz' + 2906: 'I'm singin' in the rain' + 2907: 'Alice in Wonderland' + 2908: 'The wizard of Oz' + 2909: 'I'm singin' in the rain' + 2910: 'Alice in Wonderland' + 2911: 'The wizard of Oz' + 2912: 'I'm singin' in the rain' + 2913: 'Alice in Wonderland' + 2914: 'The wizard of Oz' + 2915: 'I'm singin' in the rain' + 2916: 'Alice in Wonderland' + 2917: 'The wizard of Oz' + 2918: 'I'm singin' in the rain' + 2919: 'Alice in Wonderland' + 2920: 'The wizard of Oz' + 2921: 'I'm singin' in the rain' + 2922: 'Alice in Wonderland' + 2923: 'The wizard of Oz' + 2924: 'I'm singin' in the rain' + 2925: 'Alice in Wonderland' + 2926: 'The wizard of Oz' + 2927: 'I'm singin' in the rain' + 2928: 'Alice in Wonderland' + 2929: 'The wizard of Oz' + 2930: 'I'm singin' in the rain' + 2931: 'Alice in Wonderland' + 2932: 'The wizard of Oz' + 2933: 'I'm singin' in the rain' + 2934: 'Alice in Wonderland' + 2935: 'The wizard of Oz' + 2936: 'I'm singin' in the rain' + 2937: 'Alice in Wonderland' + 2938: 'The wizard of Oz' + 2939: 'I'm singin' in the rain' + 2940: 'Alice in Wonderland' + 2941: 'The wizard of Oz' + 2942: 'I'm singin' in the rain' + 2943: 'Alice in Wonderland' + 2944: 'The wizard of Oz' + 2945: 'I'm singin' in the rain' + 2946: 'Alice in Wonderland' + 2947: 'The wizard of Oz' + 2948: 'I'm singin' in the rain' + 2949: 'Alice in Wonderland' + 2950: 'The wizard of Oz' + 2951: 'I'm singin' in the rain' + 2952: 'Alice in Wonderland' + 2953: 'The wizard of Oz' + 2954: 'I'm singin' in the rain' + 2955: 'Alice in Wonderland' + 2956: 'The wizard of Oz' + 2957: 'I'm singin' in the rain' + 2958: 'Alice in Wonderland' + 2959: 'The wizard of Oz' + 2960: 'I'm singin' in the rain' + 2961: 'Alice in Wonderland' + 2962: 'The wizard of Oz' + 2963: 'I'm singin' in the rain' + 2964: 'Alice in Wonderland' + 2965: 'The wizard of Oz' + 2966: 'I'm singin' in the rain' + 2967: 'Alice in Wonderland' + 2968: 'The wizard of Oz' + 2969: 'I'm singin' in the rain' + 2970: 'Alice in Wonderland' + 2971: 'The wizard of Oz' + 2972: 'I'm singin' in the rain' + 2973: 'Alice in Wonderland' + 2974: 'The wizard of Oz' + 2975: 'I'm singin' in the rain' + 2976: 'Alice in Wonderland' + 2977: 'The wizard of Oz' + 2978: 'I'm singin' in the rain' + 2979: 'Alice in Wonderland' + 2980: 'The wizard of Oz' + 2981: 'I'm singin' in the rain' + 2982: 'Alice in Wonderland' + 2983: 'The wizard of Oz' + 2984: 'I'm singin' in the rain' + 2985: 'Alice in Wonderland' + 2986: 'The wizard of Oz' + 2987: 'I'm singin' in the rain' + 2988: 'Alice in Wonderland' + 2989: 'The wizard of Oz' + 2990: 'I'm singin' in the rain' + 2991: 'Alice in Wonderland' + 2992: 'The wizard of Oz' + 2993: 'I'm singin' in the rain' + 2994: 'Alice in Wonderland' + 2995: 'The wizard of Oz' + 2996: 'I'm singin' in the rain' + 2997: 'Alice in Wonderland' + 2998: 'The wizard of Oz' + 2999: 'I'm singin' in the rain' + 3000: 'Alice in Wonderland' + 3001: 'The wizard of Oz' + 3002: 'I'm singin' in the rain' + 3003: 'Alice in Wonderland' + 3004: 'The wizard of Oz' + 3005: 'I'm singin' in the rain' + 3006: 'Alice in Wonderland' + 3007: 'The wizard of Oz' + 3008: 'I'm singin' in the rain' + 3009: 'Alice in Wonderland' + 3010: 'The wizard of Oz' + 3011: 'I'm singin' in the rain' + 3012: 'Alice in Wonderland' + 3013: 'The wizard of Oz' + 3014: 'I'm singin' in the rain' + 3015: 'Alice in Wonderland' + 3016: 'The wizard of Oz' + 3017: 'I'm singin' in the rain' + 3018: 'Alice in Wonderland' + 3019: 'The wizard of Oz' + 3020: 'I'm singin' in the rain' + 3021: 'Alice in Wonderland' + 3022: 'The wizard of Oz' + 3023: 'I'm singin' in the rain' + 3024: 'Alice in Wonderland' + 3025: 'The wizard of Oz' + 3026: 'I'm singin' in the rain' + 3027: 'Alice in Wonderland' + 3028: 'The wizard of Oz' + 3029: 'I'm singin' in the rain' + 3030: 'Alice in Wonderland' + 3031: 'The wizard of Oz' + 3032: 'I'm singin' in the rain' + 3033: 'Alice in Wonderland' + 3034: 'The wizard of Oz' + 3035: 'I'm singin' in the rain' + 3036: 'Alice in Wonderland' + 3037: 'The wizard of Oz' + 3038: 'I'm singin' in the rain' + 3039: 'Alice in Wonderland' + 3040: 'The wizard of Oz' + 3041: 'I'm singin' in the rain' + 3042: 'Alice in Wonderland' + 3043: 'The wizard of Oz' + 3044: 'I'm singin' in the rain' + 3045: 'Alice in Wonderland' + 3046: 'The wizard of Oz' + 3047: 'I'm singin' in the rain' + 3048: 'Alice in Wonderland' + 3049: 'The wizard of Oz' + 3050: 'I'm singin' in the rain' + 3051: 'Alice in Wonderland' + 3052: 'The wizard of Oz' + 3053: 'I'm singin' in the rain' + 3054: 'Alice in Wonderland' + 3055: 'The wizard of Oz' + 3056: 'I'm singin' in the rain' + 3057: 'Alice in Wonderland' + 3058: 'The wizard of Oz' + 3059: 'I'm singin' in the rain' + 3060: 'Alice in Wonderland' + 3061: 'The wizard of Oz' + 3062: 'I'm singin' in the rain' + 3063: 'Alice in Wonderland' + 3064: 'The wizard of Oz' + 3065: 'I'm singin' in the rain' + 3066: 'Alice in Wonderland' + 3067: 'The wizard of Oz' + 3068: 'I'm singin' in the rain' + 3069: 'Alice in Wonderland' + 3070: 'The wizard of Oz' + 3071: 'I'm singin' in the rain' + 3072: 'Alice in Wonderland' + 3073: 'The wizard of Oz' + 3074: 'I'm singin' in the rain' + 3075: 'Alice in Wonderland' + 3076: 'The wizard of Oz' + 3077: 'I'm singin' in the rain' + 3078: 'Alice in Wonderland' + 3079: 'The wizard of Oz' + 3080: 'I'm singin' in the rain' + 3081: 'Alice in Wonderland' + 3082: 'The wizard of Oz' + 3083: 'I'm singin' in the rain' + 3084: 'Alice in Wonderland' + 3085: 'The wizard of Oz' + 3086: 'I'm singin' in the rain' + 3087: 'Alice in Wonderland' + 3088: 'The wizard of Oz' + 3089: 'I'm singin' in the rain' + 3090: 'Alice in Wonderland' + 3091: 'The wizard of Oz' + 3092: 'I'm singin' in the rain' + 3093: 'Alice in Wonderland' + 3094: 'The wizard of Oz' + 3095: 'I'm singin' in the rain' + 3096: 'Alice in Wonderland' + 3097: 'The wizard of Oz' + 3098: 'I'm singin' in the rain' + 3099: 'Alice in Wonderland' + 3100: 'The wizard of Oz' + 3101: 'I'm singin' in the rain' + 3102: 'Alice in Wonderland' + 3103: 'The wizard of Oz' + 3104: 'I'm singin' in the rain' + 3105: 'Alice in Wonderland' + 3106: 'The wizard of Oz' + 3107: 'I'm singin' in the rain' + 3108: 'Alice in Wonderland' + 3109: 'The wizard of Oz' + 3110: 'I'm singin' in the rain' + 3111: 'Alice in Wonderland' + 3112: 'The wizard of Oz' + 3113: 'I'm singin' in the rain' + 3114: 'Alice in Wonderland' + 3115: 'The wizard of Oz' + 3116: 'I'm singin' in the rain' + 3117: 'Alice in Wonderland' + 3118: 'The wizard of Oz' + 3119: 'I'm singin' in the rain' + 3120: 'Alice in Wonderland' + 3121: 'The wizard of Oz' + 3122: 'I'm singin' in the rain' + 3123: 'Alice in Wonderland' + 3124: 'The wizard of Oz' + 3125: 'I'm singin' in the rain' + 3126: 'Alice in Wonderland' + 3127: 'The wizard of Oz' + 3128: 'I'm singin' in the rain' + 3129: 'Alice in Wonderland' + 3130: 'The wizard of Oz' + 3131: 'I'm singin' in the rain' + 3132: 'Alice in Wonderland' + 3133: 'The wizard of Oz' + 3134: 'I'm singin' in the rain' + 3135: 'Alice in Wonderland' + 3136: 'The wizard of Oz' + 3137: 'I'm singin' in the rain' + 3138: 'Alice in Wonderland' + 3139: 'The wizard of Oz' + 3140: 'I'm singin' in the rain' + 3141: 'Alice in Wonderland' + 3142: 'The wizard of Oz' + 3143: 'I'm singin' in the rain' + 3144: 'Alice in Wonderland' + 3145: 'The wizard of Oz' + 3146: 'I'm singin' in the rain' + 3147: 'Alice in Wonderland' + 3148: 'The wizard of Oz' + 3149: 'I'm singin' in the rain' + 3150: 'Alice in Wonderland' + 3151: 'The wizard of Oz' + 3152: 'I'm singin' in the rain' + 3153: 'Alice in Wonderland' + 3154: 'The wizard of Oz' + 3155: 'I'm singin' in the rain' + 3156: 'Alice in Wonderland' + 3157: 'The wizard of Oz' + 3158: 'I'm singin' in the rain' + 3159: 'Alice in Wonderland' + 3160: 'The wizard of Oz' + 3161: 'I'm singin' in the rain' + 3162: 'Alice in Wonderland' + 3163: 'The wizard of Oz' + 3164: 'I'm singin' in the rain' + 3165: 'Alice in Wonderland' + 3166: 'The wizard of Oz' + 3167: 'I'm singin' in the rain' + 3168: 'Alice in Wonderland' + 3169: 'The wizard of Oz' + 3170: 'I'm singin' in the rain' + 3171: 'Alice in Wonderland' + 3172: 'The wizard of Oz' + 3173: 'I'm singin' in the rain' + 3174: 'Alice in Wonderland' + 3175: 'The wizard of Oz' + 3176: 'I'm singin' in the rain' + 3177: 'Alice in Wonderland' + 3178: 'The wizard of Oz' + 3179: 'I'm singin' in the rain' + 3180: 'Alice in Wonderland' + 3181: 'The wizard of Oz' + 3182: 'I'm singin' in the rain' + 3183: 'Alice in Wonderland' + 3184: 'The wizard of Oz' + 3185: 'I'm singin' in the rain' + 3186: 'Alice in Wonderland' + 3187: 'The wizard of Oz' + 3188: 'I'm singin' in the rain' + 3189: 'Alice in Wonderland' + 3190: 'The wizard of Oz' + 3191: 'I'm singin' in the rain' + 3192: 'Alice in Wonderland' + 3193: 'The wizard of Oz' + 3194: 'I'm singin' in the rain' + 3195: 'Alice in Wonderland' + 3196: 'The wizard of Oz' + 3197: 'I'm singin' in the rain' + 3198: 'Alice in Wonderland' + 3199: 'The wizard of Oz' + 3200: 'I'm singin' in the rain' + 3201: 'Alice in Wonderland' + 3202: 'The wizard of Oz' + 3203: 'I'm singin' in the rain' + 3204: 'Alice in Wonderland' + 3205: 'The wizard of Oz' + 3206: 'I'm singin' in the rain' + 3207: 'Alice in Wonderland' + 3208: 'The wizard of Oz' + 3209: 'I'm singin' in the rain' + 3210: 'Alice in Wonderland' + 3211: 'The wizard of Oz' + 3212: 'I'm singin' in the rain' + 3213: 'Alice in Wonderland' + 3214: 'The wizard of Oz' + 3215: 'I'm singin' in the rain' + 3216: 'Alice in Wonderland' + 3217: 'The wizard of Oz' + 3218: 'I'm singin' in the rain' + 3219: 'Alice in Wonderland' + 3220: 'The wizard of Oz' + 3221: 'I'm singin' in the rain' + 3222: 'Alice in Wonderland' + 3223: 'The wizard of Oz' + 3224: 'I'm singin' in the rain' + 3225: 'Alice in Wonderland' + 3226: 'The wizard of Oz' + 3227: 'I'm singin' in the rain' + 3228: 'Alice in Wonderland' + 3229: 'The wizard of Oz' + 3230: 'I'm singin' in the rain' + 3231: 'Alice in Wonderland' + 3232: 'The wizard of Oz' + 3233: 'I'm singin' in the rain' + 3234: 'Alice in Wonderland' + 3235: 'The wizard of Oz' + 3236: 'I'm singin' in the rain' + 3237: 'Alice in Wonderland' + 3238: 'The wizard of Oz' + 3239: 'I'm singin' in the rain' + 3240: 'Alice in Wonderland' + 3241: 'The wizard of Oz' + 3242: 'I'm singin' in the rain' + 3243: 'Alice in Wonderland' + 3244: 'The wizard of Oz' + 3245: 'I'm singin' in the rain' + 3246: 'Alice in Wonderland' + 3247: 'The wizard of Oz' + 3248: 'I'm singin' in the rain' + 3249: 'Alice in Wonderland' + 3250: 'The wizard of Oz' + 3251: 'I'm singin' in the rain' + 3252: 'Alice in Wonderland' + 3253: 'The wizard of Oz' + 3254: 'I'm singin' in the rain' + 3255: 'Alice in Wonderland' + 3256: 'The wizard of Oz' + 3257: 'I'm singin' in the rain' + 3258: 'Alice in Wonderland' + 3259: 'The wizard of Oz' + 3260: 'I'm singin' in the rain' + 3261: 'Alice in Wonderland' + 3262: 'The wizard of Oz' + 3263: 'I'm singin' in the rain' + 3264: 'Alice in Wonderland' + 3265: 'The wizard of Oz' + 3266: 'I'm singin' in the rain' + 3267: 'Alice in Wonderland' + 3268: 'The wizard of Oz' + 3269: 'I'm singin' in the rain' + 3270: 'Alice in Wonderland' + 3271: 'The wizard of Oz' + 3272: 'I'm singin' in the rain' + 3273: 'Alice in Wonderland' + 3274: 'The wizard of Oz' + 3275: 'I'm singin' in the rain' + 3276: 'Alice in Wonderland' + 3277: 'The wizard of Oz' + 3278: 'I'm singin' in the rain' + 3279: 'Alice in Wonderland' + 3280: 'The wizard of Oz' + 3281: 'I'm singin' in the rain' + 3282: 'Alice in Wonderland' + 3283: 'The wizard of Oz' + 3284: 'I'm singin' in the rain' + 3285: 'Alice in Wonderland' + 3286: 'The wizard of Oz' + 3287: 'I'm singin' in the rain' + 3288: 'Alice in Wonderland' + 3289: 'The wizard of Oz' + 3290: 'I'm singin' in the rain' + 3291: 'Alice in Wonderland' + 3292: 'The wizard of Oz' + 3293: 'I'm singin' in the rain' + 3294: 'Alice in Wonderland' + 3295: 'The wizard of Oz' + 3296: 'I'm singin' in the rain' + 3297: 'Alice in Wonderland' + 3298: 'The wizard of Oz' + 3299: 'I'm singin' in the rain' + 3300: 'Alice in Wonderland' + 3301: 'The wizard of Oz' + 3302: 'I'm singin' in the rain' + 3303: 'Alice in Wonderland' + 3304: 'The wizard of Oz' + 3305: 'I'm singin' in the rain' + 3306: 'Alice in Wonderland' + 3307: 'The wizard of Oz' + 3308: 'I'm singin' in the rain' + 3309: 'Alice in Wonderland' + 3310: 'The wizard of Oz' + 3311: 'I'm singin' in the rain' + 3312: 'Alice in Wonderland' + 3313: 'The wizard of Oz' + 3314: 'I'm singin' in the rain' + 3315: 'Alice in Wonderland' + 3316: 'The wizard of Oz' + 3317: 'I'm singin' in the rain' + 3318: 'Alice in Wonderland' + 3319: 'The wizard of Oz' + 3320: 'I'm singin' in the rain' + 3321: 'Alice in Wonderland' + 3322: 'The wizard of Oz' + 3323: 'I'm singin' in the rain' + 3324: 'Alice in Wonderland' + 3325: 'The wizard of Oz' + 3326: 'I'm singin' in the rain' + 3327: 'Alice in Wonderland' + 3328: 'The wizard of Oz' + 3329: 'I'm singin' in the rain' + 3330: 'Alice in Wonderland' + 3331: 'The wizard of Oz' + 3332: 'I'm singin' in the rain' + 3333: 'Alice in Wonderland' + 3334: 'The wizard of Oz' + 3335: 'I'm singin' in the rain' + 3336: 'Alice in Wonderland' + 3337: 'The wizard of Oz' + 3338: 'I'm singin' in the rain' + 3339: 'Alice in Wonderland' + 3340: 'The wizard of Oz' + 3341: 'I'm singin' in the rain' + 3342: 'Alice in Wonderland' + 3343: 'The wizard of Oz' + 3344: 'I'm singin' in the rain' + 3345: 'Alice in Wonderland' + 3346: 'The wizard of Oz' + 3347: 'I'm singin' in the rain' + 3348: 'Alice in Wonderland' + 3349: 'The wizard of Oz' + 3350: 'I'm singin' in the rain' + 3351: 'Alice in Wonderland' + 3352: 'The wizard of Oz' + 3353: 'I'm singin' in the rain' + 3354: 'Alice in Wonderland' + 3355: 'The wizard of Oz' + 3356: 'I'm singin' in the rain' + 3357: 'Alice in Wonderland' + 3358: 'The wizard of Oz' + 3359: 'I'm singin' in the rain' + 3360: 'Alice in Wonderland' + 3361: 'The wizard of Oz' + 3362: 'I'm singin' in the rain' + 3363: 'Alice in Wonderland' + 3364: 'The wizard of Oz' + 3365: 'I'm singin' in the rain' + 3366: 'Alice in Wonderland' + 3367: 'The wizard of Oz' + 3368: 'I'm singin' in the rain' + 3369: 'Alice in Wonderland' + 3370: 'The wizard of Oz' + 3371: 'I'm singin' in the rain' + 3372: 'Alice in Wonderland' + 3373: 'The wizard of Oz' + 3374: 'I'm singin' in the rain' + 3375: 'Alice in Wonderland' + 3376: 'The wizard of Oz' + 3377: 'I'm singin' in the rain' + 3378: 'Alice in Wonderland' + 3379: 'The wizard of Oz' + 3380: 'I'm singin' in the rain' + 3381: 'Alice in Wonderland' + 3382: 'The wizard of Oz' + 3383: 'I'm singin' in the rain' + 3384: 'Alice in Wonderland' + 3385: 'The wizard of Oz' + 3386: 'I'm singin' in the rain' + 3387: 'Alice in Wonderland' + 3388: 'The wizard of Oz' + 3389: 'I'm singin' in the rain' + 3390: 'Alice in Wonderland' + 3391: 'The wizard of Oz' + 3392: 'I'm singin' in the rain' + 3393: 'Alice in Wonderland' + 3394: 'The wizard of Oz' + 3395: 'I'm singin' in the rain' + 3396: 'Alice in Wonderland' + 3397: 'The wizard of Oz' + 3398: 'I'm singin' in the rain' + 3399: 'Alice in Wonderland' + 3400: 'The wizard of Oz' + 3401: 'I'm singin' in the rain' + 3402: 'Alice in Wonderland' + 3403: 'The wizard of Oz' + 3404: 'I'm singin' in the rain' + 3405: 'Alice in Wonderland' + 3406: 'The wizard of Oz' + 3407: 'I'm singin' in the rain' + 3408: 'Alice in Wonderland' + 3409: 'The wizard of Oz' + 3410: 'I'm singin' in the rain' + 3411: 'Alice in Wonderland' + 3412: 'The wizard of Oz' + 3413: 'I'm singin' in the rain' + 3414: 'Alice in Wonderland' + 3415: 'The wizard of Oz' + 3416: 'I'm singin' in the rain' + 3417: 'Alice in Wonderland' + 3418: 'The wizard of Oz' + 3419: 'I'm singin' in the rain' + 3420: 'Alice in Wonderland' + 3421: 'The wizard of Oz' + 3422: 'I'm singin' in the rain' + 3423: 'Alice in Wonderland' + 3424: 'The wizard of Oz' + 3425: 'I'm singin' in the rain' + 3426: 'Alice in Wonderland' + 3427: 'The wizard of Oz' + 3428: 'I'm singin' in the rain' + 3429: 'Alice in Wonderland' + 3430: 'The wizard of Oz' + 3431: 'I'm singin' in the rain' + 3432: 'Alice in Wonderland' + 3433: 'The wizard of Oz' + 3434: 'I'm singin' in the rain' + 3435: 'Alice in Wonderland' + 3436: 'The wizard of Oz' + 3437: 'I'm singin' in the rain' + 3438: 'Alice in Wonderland' + 3439: 'The wizard of Oz' + 3440: 'I'm singin' in the rain' + 3441: 'Alice in Wonderland' + 3442: 'The wizard of Oz' + 3443: 'I'm singin' in the rain' + 3444: 'Alice in Wonderland' + 3445: 'The wizard of Oz' + 3446: 'I'm singin' in the rain' + 3447: 'Alice in Wonderland' + 3448: 'The wizard of Oz' + 3449: 'I'm singin' in the rain' + 3450: 'Alice in Wonderland' + 3451: 'The wizard of Oz' + 3452: 'I'm singin' in the rain' + 3453: 'Alice in Wonderland' + 3454: 'The wizard of Oz' + 3455: 'I'm singin' in the rain' + 3456: 'Alice in Wonderland' + 3457: 'The wizard of Oz' + 3458: 'I'm singin' in the rain' + 3459: 'Alice in Wonderland' + 3460: 'The wizard of Oz' + 3461: 'I'm singin' in the rain' + 3462: 'Alice in Wonderland' + 3463: 'The wizard of Oz' + 3464: 'I'm singin' in the rain' + 3465: 'Alice in Wonderland' + 3466: 'The wizard of Oz' + 3467: 'I'm singin' in the rain' + 3468: 'Alice in Wonderland' + 3469: 'The wizard of Oz' + 3470: 'I'm singin' in the rain' + 3471: 'Alice in Wonderland' + 3472: 'The wizard of Oz' + 3473: 'I'm singin' in the rain' + 3474: 'Alice in Wonderland' + 3475: 'The wizard of Oz' + 3476: 'I'm singin' in the rain' + 3477: 'Alice in Wonderland' + 3478: 'The wizard of Oz' + 3479: 'I'm singin' in the rain' + 3480: 'Alice in Wonderland' + 3481: 'The wizard of Oz' + 3482: 'I'm singin' in the rain' + 3483: 'Alice in Wonderland' + 3484: 'The wizard of Oz' + 3485: 'I'm singin' in the rain' + 3486: 'Alice in Wonderland' + 3487: 'The wizard of Oz' + 3488: 'I'm singin' in the rain' + 3489: 'Alice in Wonderland' + 3490: 'The wizard of Oz' + 3491: 'I'm singin' in the rain' + 3492: 'Alice in Wonderland' + 3493: 'The wizard of Oz' + 3494: 'I'm singin' in the rain' + 3495: 'Alice in Wonderland' + 3496: 'The wizard of Oz' + 3497: 'I'm singin' in the rain' + 3498: 'Alice in Wonderland' + 3499: 'The wizard of Oz' diff --git a/akregator/src/mk4storage/metakit/tests/ok/l03.txt b/akregator/src/mk4storage/metakit/tests/ok/l03.txt new file mode 100644 index 000000000..82e31887f --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l03.txt @@ -0,0 +1,2 @@ +>>> Force sections in storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/l03a.txt b/akregator/src/mk4storage/metakit/tests/ok/l03a.txt new file mode 100644 index 000000000..daff458fa --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l03a.txt @@ -0,0 +1,1503 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 500 rows = p1:V + 0: subview 'p1' + VIEW 1 rows = p2:I + 0: 9000 + 1: subview 'p1' + VIEW 1 rows = p2:I + 0: 9001 + 2: subview 'p1' + VIEW 1 rows = p2:I + 0: 9002 + 3: subview 'p1' + VIEW 1 rows = p2:I + 0: 9003 + 4: subview 'p1' + VIEW 1 rows = p2:I + 0: 9004 + 5: subview 'p1' + VIEW 1 rows = p2:I + 0: 9005 + 6: subview 'p1' + VIEW 1 rows = p2:I + 0: 9006 + 7: subview 'p1' + VIEW 1 rows = p2:I + 0: 9007 + 8: subview 'p1' + VIEW 1 rows = p2:I + 0: 9008 + 9: subview 'p1' + VIEW 1 rows = p2:I + 0: 9009 + 10: subview 'p1' + VIEW 1 rows = p2:I + 0: 9010 + 11: subview 'p1' + VIEW 1 rows = p2:I + 0: 9011 + 12: subview 'p1' + VIEW 1 rows = p2:I + 0: 9012 + 13: subview 'p1' + VIEW 1 rows = p2:I + 0: 9013 + 14: subview 'p1' + VIEW 1 rows = p2:I + 0: 9014 + 15: subview 'p1' + VIEW 1 rows = p2:I + 0: 9015 + 16: subview 'p1' + VIEW 1 rows = p2:I + 0: 9016 + 17: subview 'p1' + VIEW 1 rows = p2:I + 0: 9017 + 18: subview 'p1' + VIEW 1 rows = p2:I + 0: 9018 + 19: subview 'p1' + VIEW 1 rows = p2:I + 0: 9019 + 20: subview 'p1' + VIEW 1 rows = p2:I + 0: 9020 + 21: subview 'p1' + VIEW 1 rows = p2:I + 0: 9021 + 22: subview 'p1' + VIEW 1 rows = p2:I + 0: 9022 + 23: subview 'p1' + VIEW 1 rows = p2:I + 0: 9023 + 24: subview 'p1' + VIEW 1 rows = p2:I + 0: 9024 + 25: subview 'p1' + VIEW 1 rows = p2:I + 0: 9025 + 26: subview 'p1' + VIEW 1 rows = p2:I + 0: 9026 + 27: subview 'p1' + VIEW 1 rows = p2:I + 0: 9027 + 28: subview 'p1' + VIEW 1 rows = p2:I + 0: 9028 + 29: subview 'p1' + VIEW 1 rows = p2:I + 0: 9029 + 30: subview 'p1' + VIEW 1 rows = p2:I + 0: 9030 + 31: subview 'p1' + VIEW 1 rows = p2:I + 0: 9031 + 32: subview 'p1' + VIEW 1 rows = p2:I + 0: 9032 + 33: subview 'p1' + VIEW 1 rows = p2:I + 0: 9033 + 34: subview 'p1' + VIEW 1 rows = p2:I + 0: 9034 + 35: subview 'p1' + VIEW 1 rows = p2:I + 0: 9035 + 36: subview 'p1' + VIEW 1 rows = p2:I + 0: 9036 + 37: subview 'p1' + VIEW 1 rows = p2:I + 0: 9037 + 38: subview 'p1' + VIEW 1 rows = p2:I + 0: 9038 + 39: subview 'p1' + VIEW 1 rows = p2:I + 0: 9039 + 40: subview 'p1' + VIEW 1 rows = p2:I + 0: 9040 + 41: subview 'p1' + VIEW 1 rows = p2:I + 0: 9041 + 42: subview 'p1' + VIEW 1 rows = p2:I + 0: 9042 + 43: subview 'p1' + VIEW 1 rows = p2:I + 0: 9043 + 44: subview 'p1' + VIEW 1 rows = p2:I + 0: 9044 + 45: subview 'p1' + VIEW 1 rows = p2:I + 0: 9045 + 46: subview 'p1' + VIEW 1 rows = p2:I + 0: 9046 + 47: subview 'p1' + VIEW 1 rows = p2:I + 0: 9047 + 48: subview 'p1' + VIEW 1 rows = p2:I + 0: 9048 + 49: subview 'p1' + VIEW 1 rows = p2:I + 0: 9049 + 50: subview 'p1' + VIEW 1 rows = p2:I + 0: 9050 + 51: subview 'p1' + VIEW 1 rows = p2:I + 0: 9051 + 52: subview 'p1' + VIEW 1 rows = p2:I + 0: 9052 + 53: subview 'p1' + VIEW 1 rows = p2:I + 0: 9053 + 54: subview 'p1' + VIEW 1 rows = p2:I + 0: 9054 + 55: subview 'p1' + VIEW 1 rows = p2:I + 0: 9055 + 56: subview 'p1' + VIEW 1 rows = p2:I + 0: 9056 + 57: subview 'p1' + VIEW 1 rows = p2:I + 0: 9057 + 58: subview 'p1' + VIEW 1 rows = p2:I + 0: 9058 + 59: subview 'p1' + VIEW 1 rows = p2:I + 0: 9059 + 60: subview 'p1' + VIEW 1 rows = p2:I + 0: 9060 + 61: subview 'p1' + VIEW 1 rows = p2:I + 0: 9061 + 62: subview 'p1' + VIEW 1 rows = p2:I + 0: 9062 + 63: subview 'p1' + VIEW 1 rows = p2:I + 0: 9063 + 64: subview 'p1' + VIEW 1 rows = p2:I + 0: 9064 + 65: subview 'p1' + VIEW 1 rows = p2:I + 0: 9065 + 66: subview 'p1' + VIEW 1 rows = p2:I + 0: 9066 + 67: subview 'p1' + VIEW 1 rows = p2:I + 0: 9067 + 68: subview 'p1' + VIEW 1 rows = p2:I + 0: 9068 + 69: subview 'p1' + VIEW 1 rows = p2:I + 0: 9069 + 70: subview 'p1' + VIEW 1 rows = p2:I + 0: 9070 + 71: subview 'p1' + VIEW 1 rows = p2:I + 0: 9071 + 72: subview 'p1' + VIEW 1 rows = p2:I + 0: 9072 + 73: subview 'p1' + VIEW 1 rows = p2:I + 0: 9073 + 74: subview 'p1' + VIEW 1 rows = p2:I + 0: 9074 + 75: subview 'p1' + VIEW 1 rows = p2:I + 0: 9075 + 76: subview 'p1' + VIEW 1 rows = p2:I + 0: 9076 + 77: subview 'p1' + VIEW 1 rows = p2:I + 0: 9077 + 78: subview 'p1' + VIEW 1 rows = p2:I + 0: 9078 + 79: subview 'p1' + VIEW 1 rows = p2:I + 0: 9079 + 80: subview 'p1' + VIEW 1 rows = p2:I + 0: 9080 + 81: subview 'p1' + VIEW 1 rows = p2:I + 0: 9081 + 82: subview 'p1' + VIEW 1 rows = p2:I + 0: 9082 + 83: subview 'p1' + VIEW 1 rows = p2:I + 0: 9083 + 84: subview 'p1' + VIEW 1 rows = p2:I + 0: 9084 + 85: subview 'p1' + VIEW 1 rows = p2:I + 0: 9085 + 86: subview 'p1' + VIEW 1 rows = p2:I + 0: 9086 + 87: subview 'p1' + VIEW 1 rows = p2:I + 0: 9087 + 88: subview 'p1' + VIEW 1 rows = p2:I + 0: 9088 + 89: subview 'p1' + VIEW 1 rows = p2:I + 0: 9089 + 90: subview 'p1' + VIEW 1 rows = p2:I + 0: 9090 + 91: subview 'p1' + VIEW 1 rows = p2:I + 0: 9091 + 92: subview 'p1' + VIEW 1 rows = p2:I + 0: 9092 + 93: subview 'p1' + VIEW 1 rows = p2:I + 0: 9093 + 94: subview 'p1' + VIEW 1 rows = p2:I + 0: 9094 + 95: subview 'p1' + VIEW 1 rows = p2:I + 0: 9095 + 96: subview 'p1' + VIEW 1 rows = p2:I + 0: 9096 + 97: subview 'p1' + VIEW 1 rows = p2:I + 0: 9097 + 98: subview 'p1' + VIEW 1 rows = p2:I + 0: 9098 + 99: subview 'p1' + VIEW 1 rows = p2:I + 0: 9099 + 100: subview 'p1' + VIEW 1 rows = p2:I + 0: 9100 + 101: subview 'p1' + VIEW 1 rows = p2:I + 0: 9101 + 102: subview 'p1' + VIEW 1 rows = p2:I + 0: 9102 + 103: subview 'p1' + VIEW 1 rows = p2:I + 0: 9103 + 104: subview 'p1' + VIEW 1 rows = p2:I + 0: 9104 + 105: subview 'p1' + VIEW 1 rows = p2:I + 0: 9105 + 106: subview 'p1' + VIEW 1 rows = p2:I + 0: 9106 + 107: subview 'p1' + VIEW 1 rows = p2:I + 0: 9107 + 108: subview 'p1' + VIEW 1 rows = p2:I + 0: 9108 + 109: subview 'p1' + VIEW 1 rows = p2:I + 0: 9109 + 110: subview 'p1' + VIEW 1 rows = p2:I + 0: 9110 + 111: subview 'p1' + VIEW 1 rows = p2:I + 0: 9111 + 112: subview 'p1' + VIEW 1 rows = p2:I + 0: 9112 + 113: subview 'p1' + VIEW 1 rows = p2:I + 0: 9113 + 114: subview 'p1' + VIEW 1 rows = p2:I + 0: 9114 + 115: subview 'p1' + VIEW 1 rows = p2:I + 0: 9115 + 116: subview 'p1' + VIEW 1 rows = p2:I + 0: 9116 + 117: subview 'p1' + VIEW 1 rows = p2:I + 0: 9117 + 118: subview 'p1' + VIEW 1 rows = p2:I + 0: 9118 + 119: subview 'p1' + VIEW 1 rows = p2:I + 0: 9119 + 120: subview 'p1' + VIEW 1 rows = p2:I + 0: 9120 + 121: subview 'p1' + VIEW 1 rows = p2:I + 0: 9121 + 122: subview 'p1' + VIEW 1 rows = p2:I + 0: 9122 + 123: subview 'p1' + VIEW 1 rows = p2:I + 0: 9123 + 124: subview 'p1' + VIEW 1 rows = p2:I + 0: 9124 + 125: subview 'p1' + VIEW 1 rows = p2:I + 0: 9125 + 126: subview 'p1' + VIEW 1 rows = p2:I + 0: 9126 + 127: subview 'p1' + VIEW 1 rows = p2:I + 0: 9127 + 128: subview 'p1' + VIEW 1 rows = p2:I + 0: 9128 + 129: subview 'p1' + VIEW 1 rows = p2:I + 0: 9129 + 130: subview 'p1' + VIEW 1 rows = p2:I + 0: 9130 + 131: subview 'p1' + VIEW 1 rows = p2:I + 0: 9131 + 132: subview 'p1' + VIEW 1 rows = p2:I + 0: 9132 + 133: subview 'p1' + VIEW 1 rows = p2:I + 0: 9133 + 134: subview 'p1' + VIEW 1 rows = p2:I + 0: 9134 + 135: subview 'p1' + VIEW 1 rows = p2:I + 0: 9135 + 136: subview 'p1' + VIEW 1 rows = p2:I + 0: 9136 + 137: subview 'p1' + VIEW 1 rows = p2:I + 0: 9137 + 138: subview 'p1' + VIEW 1 rows = p2:I + 0: 9138 + 139: subview 'p1' + VIEW 1 rows = p2:I + 0: 9139 + 140: subview 'p1' + VIEW 1 rows = p2:I + 0: 9140 + 141: subview 'p1' + VIEW 1 rows = p2:I + 0: 9141 + 142: subview 'p1' + VIEW 1 rows = p2:I + 0: 9142 + 143: subview 'p1' + VIEW 1 rows = p2:I + 0: 9143 + 144: subview 'p1' + VIEW 1 rows = p2:I + 0: 9144 + 145: subview 'p1' + VIEW 1 rows = p2:I + 0: 9145 + 146: subview 'p1' + VIEW 1 rows = p2:I + 0: 9146 + 147: subview 'p1' + VIEW 1 rows = p2:I + 0: 9147 + 148: subview 'p1' + VIEW 1 rows = p2:I + 0: 9148 + 149: subview 'p1' + VIEW 1 rows = p2:I + 0: 9149 + 150: subview 'p1' + VIEW 1 rows = p2:I + 0: 9150 + 151: subview 'p1' + VIEW 1 rows = p2:I + 0: 9151 + 152: subview 'p1' + VIEW 1 rows = p2:I + 0: 9152 + 153: subview 'p1' + VIEW 1 rows = p2:I + 0: 9153 + 154: subview 'p1' + VIEW 1 rows = p2:I + 0: 9154 + 155: subview 'p1' + VIEW 1 rows = p2:I + 0: 9155 + 156: subview 'p1' + VIEW 1 rows = p2:I + 0: 9156 + 157: subview 'p1' + VIEW 1 rows = p2:I + 0: 9157 + 158: subview 'p1' + VIEW 1 rows = p2:I + 0: 9158 + 159: subview 'p1' + VIEW 1 rows = p2:I + 0: 9159 + 160: subview 'p1' + VIEW 1 rows = p2:I + 0: 9160 + 161: subview 'p1' + VIEW 1 rows = p2:I + 0: 9161 + 162: subview 'p1' + VIEW 1 rows = p2:I + 0: 9162 + 163: subview 'p1' + VIEW 1 rows = p2:I + 0: 9163 + 164: subview 'p1' + VIEW 1 rows = p2:I + 0: 9164 + 165: subview 'p1' + VIEW 1 rows = p2:I + 0: 9165 + 166: subview 'p1' + VIEW 1 rows = p2:I + 0: 9166 + 167: subview 'p1' + VIEW 1 rows = p2:I + 0: 9167 + 168: subview 'p1' + VIEW 1 rows = p2:I + 0: 9168 + 169: subview 'p1' + VIEW 1 rows = p2:I + 0: 9169 + 170: subview 'p1' + VIEW 1 rows = p2:I + 0: 9170 + 171: subview 'p1' + VIEW 1 rows = p2:I + 0: 9171 + 172: subview 'p1' + VIEW 1 rows = p2:I + 0: 9172 + 173: subview 'p1' + VIEW 1 rows = p2:I + 0: 9173 + 174: subview 'p1' + VIEW 1 rows = p2:I + 0: 9174 + 175: subview 'p1' + VIEW 1 rows = p2:I + 0: 9175 + 176: subview 'p1' + VIEW 1 rows = p2:I + 0: 9176 + 177: subview 'p1' + VIEW 1 rows = p2:I + 0: 9177 + 178: subview 'p1' + VIEW 1 rows = p2:I + 0: 9178 + 179: subview 'p1' + VIEW 1 rows = p2:I + 0: 9179 + 180: subview 'p1' + VIEW 1 rows = p2:I + 0: 9180 + 181: subview 'p1' + VIEW 1 rows = p2:I + 0: 9181 + 182: subview 'p1' + VIEW 1 rows = p2:I + 0: 9182 + 183: subview 'p1' + VIEW 1 rows = p2:I + 0: 9183 + 184: subview 'p1' + VIEW 1 rows = p2:I + 0: 9184 + 185: subview 'p1' + VIEW 1 rows = p2:I + 0: 9185 + 186: subview 'p1' + VIEW 1 rows = p2:I + 0: 9186 + 187: subview 'p1' + VIEW 1 rows = p2:I + 0: 9187 + 188: subview 'p1' + VIEW 1 rows = p2:I + 0: 9188 + 189: subview 'p1' + VIEW 1 rows = p2:I + 0: 9189 + 190: subview 'p1' + VIEW 1 rows = p2:I + 0: 9190 + 191: subview 'p1' + VIEW 1 rows = p2:I + 0: 9191 + 192: subview 'p1' + VIEW 1 rows = p2:I + 0: 9192 + 193: subview 'p1' + VIEW 1 rows = p2:I + 0: 9193 + 194: subview 'p1' + VIEW 1 rows = p2:I + 0: 9194 + 195: subview 'p1' + VIEW 1 rows = p2:I + 0: 9195 + 196: subview 'p1' + VIEW 1 rows = p2:I + 0: 9196 + 197: subview 'p1' + VIEW 1 rows = p2:I + 0: 9197 + 198: subview 'p1' + VIEW 1 rows = p2:I + 0: 9198 + 199: subview 'p1' + VIEW 1 rows = p2:I + 0: 9199 + 200: subview 'p1' + VIEW 1 rows = p2:I + 0: 9200 + 201: subview 'p1' + VIEW 1 rows = p2:I + 0: 9201 + 202: subview 'p1' + VIEW 1 rows = p2:I + 0: 9202 + 203: subview 'p1' + VIEW 1 rows = p2:I + 0: 9203 + 204: subview 'p1' + VIEW 1 rows = p2:I + 0: 9204 + 205: subview 'p1' + VIEW 1 rows = p2:I + 0: 9205 + 206: subview 'p1' + VIEW 1 rows = p2:I + 0: 9206 + 207: subview 'p1' + VIEW 1 rows = p2:I + 0: 9207 + 208: subview 'p1' + VIEW 1 rows = p2:I + 0: 9208 + 209: subview 'p1' + VIEW 1 rows = p2:I + 0: 9209 + 210: subview 'p1' + VIEW 1 rows = p2:I + 0: 9210 + 211: subview 'p1' + VIEW 1 rows = p2:I + 0: 9211 + 212: subview 'p1' + VIEW 1 rows = p2:I + 0: 9212 + 213: subview 'p1' + VIEW 1 rows = p2:I + 0: 9213 + 214: subview 'p1' + VIEW 1 rows = p2:I + 0: 9214 + 215: subview 'p1' + VIEW 1 rows = p2:I + 0: 9215 + 216: subview 'p1' + VIEW 1 rows = p2:I + 0: 9216 + 217: subview 'p1' + VIEW 1 rows = p2:I + 0: 9217 + 218: subview 'p1' + VIEW 1 rows = p2:I + 0: 9218 + 219: subview 'p1' + VIEW 1 rows = p2:I + 0: 9219 + 220: subview 'p1' + VIEW 1 rows = p2:I + 0: 9220 + 221: subview 'p1' + VIEW 1 rows = p2:I + 0: 9221 + 222: subview 'p1' + VIEW 1 rows = p2:I + 0: 9222 + 223: subview 'p1' + VIEW 1 rows = p2:I + 0: 9223 + 224: subview 'p1' + VIEW 1 rows = p2:I + 0: 9224 + 225: subview 'p1' + VIEW 1 rows = p2:I + 0: 9225 + 226: subview 'p1' + VIEW 1 rows = p2:I + 0: 9226 + 227: subview 'p1' + VIEW 1 rows = p2:I + 0: 9227 + 228: subview 'p1' + VIEW 1 rows = p2:I + 0: 9228 + 229: subview 'p1' + VIEW 1 rows = p2:I + 0: 9229 + 230: subview 'p1' + VIEW 1 rows = p2:I + 0: 9230 + 231: subview 'p1' + VIEW 1 rows = p2:I + 0: 9231 + 232: subview 'p1' + VIEW 1 rows = p2:I + 0: 9232 + 233: subview 'p1' + VIEW 1 rows = p2:I + 0: 9233 + 234: subview 'p1' + VIEW 1 rows = p2:I + 0: 9234 + 235: subview 'p1' + VIEW 1 rows = p2:I + 0: 9235 + 236: subview 'p1' + VIEW 1 rows = p2:I + 0: 9236 + 237: subview 'p1' + VIEW 1 rows = p2:I + 0: 9237 + 238: subview 'p1' + VIEW 1 rows = p2:I + 0: 9238 + 239: subview 'p1' + VIEW 1 rows = p2:I + 0: 9239 + 240: subview 'p1' + VIEW 1 rows = p2:I + 0: 9240 + 241: subview 'p1' + VIEW 1 rows = p2:I + 0: 9241 + 242: subview 'p1' + VIEW 1 rows = p2:I + 0: 9242 + 243: subview 'p1' + VIEW 1 rows = p2:I + 0: 9243 + 244: subview 'p1' + VIEW 1 rows = p2:I + 0: 9244 + 245: subview 'p1' + VIEW 1 rows = p2:I + 0: 9245 + 246: subview 'p1' + VIEW 1 rows = p2:I + 0: 9246 + 247: subview 'p1' + VIEW 1 rows = p2:I + 0: 9247 + 248: subview 'p1' + VIEW 1 rows = p2:I + 0: 9248 + 249: subview 'p1' + VIEW 1 rows = p2:I + 0: 9249 + 250: subview 'p1' + VIEW 1 rows = p2:I + 0: 9250 + 251: subview 'p1' + VIEW 1 rows = p2:I + 0: 9251 + 252: subview 'p1' + VIEW 1 rows = p2:I + 0: 9252 + 253: subview 'p1' + VIEW 1 rows = p2:I + 0: 9253 + 254: subview 'p1' + VIEW 1 rows = p2:I + 0: 9254 + 255: subview 'p1' + VIEW 1 rows = p2:I + 0: 9255 + 256: subview 'p1' + VIEW 1 rows = p2:I + 0: 9256 + 257: subview 'p1' + VIEW 1 rows = p2:I + 0: 9257 + 258: subview 'p1' + VIEW 1 rows = p2:I + 0: 9258 + 259: subview 'p1' + VIEW 1 rows = p2:I + 0: 9259 + 260: subview 'p1' + VIEW 1 rows = p2:I + 0: 9260 + 261: subview 'p1' + VIEW 1 rows = p2:I + 0: 9261 + 262: subview 'p1' + VIEW 1 rows = p2:I + 0: 9262 + 263: subview 'p1' + VIEW 1 rows = p2:I + 0: 9263 + 264: subview 'p1' + VIEW 1 rows = p2:I + 0: 9264 + 265: subview 'p1' + VIEW 1 rows = p2:I + 0: 9265 + 266: subview 'p1' + VIEW 1 rows = p2:I + 0: 9266 + 267: subview 'p1' + VIEW 1 rows = p2:I + 0: 9267 + 268: subview 'p1' + VIEW 1 rows = p2:I + 0: 9268 + 269: subview 'p1' + VIEW 1 rows = p2:I + 0: 9269 + 270: subview 'p1' + VIEW 1 rows = p2:I + 0: 9270 + 271: subview 'p1' + VIEW 1 rows = p2:I + 0: 9271 + 272: subview 'p1' + VIEW 1 rows = p2:I + 0: 9272 + 273: subview 'p1' + VIEW 1 rows = p2:I + 0: 9273 + 274: subview 'p1' + VIEW 1 rows = p2:I + 0: 9274 + 275: subview 'p1' + VIEW 1 rows = p2:I + 0: 9275 + 276: subview 'p1' + VIEW 1 rows = p2:I + 0: 9276 + 277: subview 'p1' + VIEW 1 rows = p2:I + 0: 9277 + 278: subview 'p1' + VIEW 1 rows = p2:I + 0: 9278 + 279: subview 'p1' + VIEW 1 rows = p2:I + 0: 9279 + 280: subview 'p1' + VIEW 1 rows = p2:I + 0: 9280 + 281: subview 'p1' + VIEW 1 rows = p2:I + 0: 9281 + 282: subview 'p1' + VIEW 1 rows = p2:I + 0: 9282 + 283: subview 'p1' + VIEW 1 rows = p2:I + 0: 9283 + 284: subview 'p1' + VIEW 1 rows = p2:I + 0: 9284 + 285: subview 'p1' + VIEW 1 rows = p2:I + 0: 9285 + 286: subview 'p1' + VIEW 1 rows = p2:I + 0: 9286 + 287: subview 'p1' + VIEW 1 rows = p2:I + 0: 9287 + 288: subview 'p1' + VIEW 1 rows = p2:I + 0: 9288 + 289: subview 'p1' + VIEW 1 rows = p2:I + 0: 9289 + 290: subview 'p1' + VIEW 1 rows = p2:I + 0: 9290 + 291: subview 'p1' + VIEW 1 rows = p2:I + 0: 9291 + 292: subview 'p1' + VIEW 1 rows = p2:I + 0: 9292 + 293: subview 'p1' + VIEW 1 rows = p2:I + 0: 9293 + 294: subview 'p1' + VIEW 1 rows = p2:I + 0: 9294 + 295: subview 'p1' + VIEW 1 rows = p2:I + 0: 9295 + 296: subview 'p1' + VIEW 1 rows = p2:I + 0: 9296 + 297: subview 'p1' + VIEW 1 rows = p2:I + 0: 9297 + 298: subview 'p1' + VIEW 1 rows = p2:I + 0: 9298 + 299: subview 'p1' + VIEW 1 rows = p2:I + 0: 9299 + 300: subview 'p1' + VIEW 1 rows = p2:I + 0: 9300 + 301: subview 'p1' + VIEW 1 rows = p2:I + 0: 9301 + 302: subview 'p1' + VIEW 1 rows = p2:I + 0: 9302 + 303: subview 'p1' + VIEW 1 rows = p2:I + 0: 9303 + 304: subview 'p1' + VIEW 1 rows = p2:I + 0: 9304 + 305: subview 'p1' + VIEW 1 rows = p2:I + 0: 9305 + 306: subview 'p1' + VIEW 1 rows = p2:I + 0: 9306 + 307: subview 'p1' + VIEW 1 rows = p2:I + 0: 9307 + 308: subview 'p1' + VIEW 1 rows = p2:I + 0: 9308 + 309: subview 'p1' + VIEW 1 rows = p2:I + 0: 9309 + 310: subview 'p1' + VIEW 1 rows = p2:I + 0: 9310 + 311: subview 'p1' + VIEW 1 rows = p2:I + 0: 9311 + 312: subview 'p1' + VIEW 1 rows = p2:I + 0: 9312 + 313: subview 'p1' + VIEW 1 rows = p2:I + 0: 9313 + 314: subview 'p1' + VIEW 1 rows = p2:I + 0: 9314 + 315: subview 'p1' + VIEW 1 rows = p2:I + 0: 9315 + 316: subview 'p1' + VIEW 1 rows = p2:I + 0: 9316 + 317: subview 'p1' + VIEW 1 rows = p2:I + 0: 9317 + 318: subview 'p1' + VIEW 1 rows = p2:I + 0: 9318 + 319: subview 'p1' + VIEW 1 rows = p2:I + 0: 9319 + 320: subview 'p1' + VIEW 1 rows = p2:I + 0: 9320 + 321: subview 'p1' + VIEW 1 rows = p2:I + 0: 9321 + 322: subview 'p1' + VIEW 1 rows = p2:I + 0: 9322 + 323: subview 'p1' + VIEW 1 rows = p2:I + 0: 9323 + 324: subview 'p1' + VIEW 1 rows = p2:I + 0: 9324 + 325: subview 'p1' + VIEW 1 rows = p2:I + 0: 9325 + 326: subview 'p1' + VIEW 1 rows = p2:I + 0: 9326 + 327: subview 'p1' + VIEW 1 rows = p2:I + 0: 9327 + 328: subview 'p1' + VIEW 1 rows = p2:I + 0: 9328 + 329: subview 'p1' + VIEW 1 rows = p2:I + 0: 9329 + 330: subview 'p1' + VIEW 1 rows = p2:I + 0: 9330 + 331: subview 'p1' + VIEW 1 rows = p2:I + 0: 9331 + 332: subview 'p1' + VIEW 1 rows = p2:I + 0: 9332 + 333: subview 'p1' + VIEW 1 rows = p2:I + 0: 9333 + 334: subview 'p1' + VIEW 1 rows = p2:I + 0: 9334 + 335: subview 'p1' + VIEW 1 rows = p2:I + 0: 9335 + 336: subview 'p1' + VIEW 1 rows = p2:I + 0: 9336 + 337: subview 'p1' + VIEW 1 rows = p2:I + 0: 9337 + 338: subview 'p1' + VIEW 1 rows = p2:I + 0: 9338 + 339: subview 'p1' + VIEW 1 rows = p2:I + 0: 9339 + 340: subview 'p1' + VIEW 1 rows = p2:I + 0: 9340 + 341: subview 'p1' + VIEW 1 rows = p2:I + 0: 9341 + 342: subview 'p1' + VIEW 1 rows = p2:I + 0: 9342 + 343: subview 'p1' + VIEW 1 rows = p2:I + 0: 9343 + 344: subview 'p1' + VIEW 1 rows = p2:I + 0: 9344 + 345: subview 'p1' + VIEW 1 rows = p2:I + 0: 9345 + 346: subview 'p1' + VIEW 1 rows = p2:I + 0: 9346 + 347: subview 'p1' + VIEW 1 rows = p2:I + 0: 9347 + 348: subview 'p1' + VIEW 1 rows = p2:I + 0: 9348 + 349: subview 'p1' + VIEW 1 rows = p2:I + 0: 9349 + 350: subview 'p1' + VIEW 1 rows = p2:I + 0: 9350 + 351: subview 'p1' + VIEW 1 rows = p2:I + 0: 9351 + 352: subview 'p1' + VIEW 1 rows = p2:I + 0: 9352 + 353: subview 'p1' + VIEW 1 rows = p2:I + 0: 9353 + 354: subview 'p1' + VIEW 1 rows = p2:I + 0: 9354 + 355: subview 'p1' + VIEW 1 rows = p2:I + 0: 9355 + 356: subview 'p1' + VIEW 1 rows = p2:I + 0: 9356 + 357: subview 'p1' + VIEW 1 rows = p2:I + 0: 9357 + 358: subview 'p1' + VIEW 1 rows = p2:I + 0: 9358 + 359: subview 'p1' + VIEW 1 rows = p2:I + 0: 9359 + 360: subview 'p1' + VIEW 1 rows = p2:I + 0: 9360 + 361: subview 'p1' + VIEW 1 rows = p2:I + 0: 9361 + 362: subview 'p1' + VIEW 1 rows = p2:I + 0: 9362 + 363: subview 'p1' + VIEW 1 rows = p2:I + 0: 9363 + 364: subview 'p1' + VIEW 1 rows = p2:I + 0: 9364 + 365: subview 'p1' + VIEW 1 rows = p2:I + 0: 9365 + 366: subview 'p1' + VIEW 1 rows = p2:I + 0: 9366 + 367: subview 'p1' + VIEW 1 rows = p2:I + 0: 9367 + 368: subview 'p1' + VIEW 1 rows = p2:I + 0: 9368 + 369: subview 'p1' + VIEW 1 rows = p2:I + 0: 9369 + 370: subview 'p1' + VIEW 1 rows = p2:I + 0: 9370 + 371: subview 'p1' + VIEW 1 rows = p2:I + 0: 9371 + 372: subview 'p1' + VIEW 1 rows = p2:I + 0: 9372 + 373: subview 'p1' + VIEW 1 rows = p2:I + 0: 9373 + 374: subview 'p1' + VIEW 1 rows = p2:I + 0: 9374 + 375: subview 'p1' + VIEW 1 rows = p2:I + 0: 9375 + 376: subview 'p1' + VIEW 1 rows = p2:I + 0: 9376 + 377: subview 'p1' + VIEW 1 rows = p2:I + 0: 9377 + 378: subview 'p1' + VIEW 1 rows = p2:I + 0: 9378 + 379: subview 'p1' + VIEW 1 rows = p2:I + 0: 9379 + 380: subview 'p1' + VIEW 1 rows = p2:I + 0: 9380 + 381: subview 'p1' + VIEW 1 rows = p2:I + 0: 9381 + 382: subview 'p1' + VIEW 1 rows = p2:I + 0: 9382 + 383: subview 'p1' + VIEW 1 rows = p2:I + 0: 9383 + 384: subview 'p1' + VIEW 1 rows = p2:I + 0: 9384 + 385: subview 'p1' + VIEW 1 rows = p2:I + 0: 9385 + 386: subview 'p1' + VIEW 1 rows = p2:I + 0: 9386 + 387: subview 'p1' + VIEW 1 rows = p2:I + 0: 9387 + 388: subview 'p1' + VIEW 1 rows = p2:I + 0: 9388 + 389: subview 'p1' + VIEW 1 rows = p2:I + 0: 9389 + 390: subview 'p1' + VIEW 1 rows = p2:I + 0: 9390 + 391: subview 'p1' + VIEW 1 rows = p2:I + 0: 9391 + 392: subview 'p1' + VIEW 1 rows = p2:I + 0: 9392 + 393: subview 'p1' + VIEW 1 rows = p2:I + 0: 9393 + 394: subview 'p1' + VIEW 1 rows = p2:I + 0: 9394 + 395: subview 'p1' + VIEW 1 rows = p2:I + 0: 9395 + 396: subview 'p1' + VIEW 1 rows = p2:I + 0: 9396 + 397: subview 'p1' + VIEW 1 rows = p2:I + 0: 9397 + 398: subview 'p1' + VIEW 1 rows = p2:I + 0: 9398 + 399: subview 'p1' + VIEW 1 rows = p2:I + 0: 9399 + 400: subview 'p1' + VIEW 1 rows = p2:I + 0: 9400 + 401: subview 'p1' + VIEW 1 rows = p2:I + 0: 9401 + 402: subview 'p1' + VIEW 1 rows = p2:I + 0: 9402 + 403: subview 'p1' + VIEW 1 rows = p2:I + 0: 9403 + 404: subview 'p1' + VIEW 1 rows = p2:I + 0: 9404 + 405: subview 'p1' + VIEW 1 rows = p2:I + 0: 9405 + 406: subview 'p1' + VIEW 1 rows = p2:I + 0: 9406 + 407: subview 'p1' + VIEW 1 rows = p2:I + 0: 9407 + 408: subview 'p1' + VIEW 1 rows = p2:I + 0: 9408 + 409: subview 'p1' + VIEW 1 rows = p2:I + 0: 9409 + 410: subview 'p1' + VIEW 1 rows = p2:I + 0: 9410 + 411: subview 'p1' + VIEW 1 rows = p2:I + 0: 9411 + 412: subview 'p1' + VIEW 1 rows = p2:I + 0: 9412 + 413: subview 'p1' + VIEW 1 rows = p2:I + 0: 9413 + 414: subview 'p1' + VIEW 1 rows = p2:I + 0: 9414 + 415: subview 'p1' + VIEW 1 rows = p2:I + 0: 9415 + 416: subview 'p1' + VIEW 1 rows = p2:I + 0: 9416 + 417: subview 'p1' + VIEW 1 rows = p2:I + 0: 9417 + 418: subview 'p1' + VIEW 1 rows = p2:I + 0: 9418 + 419: subview 'p1' + VIEW 1 rows = p2:I + 0: 9419 + 420: subview 'p1' + VIEW 1 rows = p2:I + 0: 9420 + 421: subview 'p1' + VIEW 1 rows = p2:I + 0: 9421 + 422: subview 'p1' + VIEW 1 rows = p2:I + 0: 9422 + 423: subview 'p1' + VIEW 1 rows = p2:I + 0: 9423 + 424: subview 'p1' + VIEW 1 rows = p2:I + 0: 9424 + 425: subview 'p1' + VIEW 1 rows = p2:I + 0: 9425 + 426: subview 'p1' + VIEW 1 rows = p2:I + 0: 9426 + 427: subview 'p1' + VIEW 1 rows = p2:I + 0: 9427 + 428: subview 'p1' + VIEW 1 rows = p2:I + 0: 9428 + 429: subview 'p1' + VIEW 1 rows = p2:I + 0: 9429 + 430: subview 'p1' + VIEW 1 rows = p2:I + 0: 9430 + 431: subview 'p1' + VIEW 1 rows = p2:I + 0: 9431 + 432: subview 'p1' + VIEW 1 rows = p2:I + 0: 9432 + 433: subview 'p1' + VIEW 1 rows = p2:I + 0: 9433 + 434: subview 'p1' + VIEW 1 rows = p2:I + 0: 9434 + 435: subview 'p1' + VIEW 1 rows = p2:I + 0: 9435 + 436: subview 'p1' + VIEW 1 rows = p2:I + 0: 9436 + 437: subview 'p1' + VIEW 1 rows = p2:I + 0: 9437 + 438: subview 'p1' + VIEW 1 rows = p2:I + 0: 9438 + 439: subview 'p1' + VIEW 1 rows = p2:I + 0: 9439 + 440: subview 'p1' + VIEW 1 rows = p2:I + 0: 9440 + 441: subview 'p1' + VIEW 1 rows = p2:I + 0: 9441 + 442: subview 'p1' + VIEW 1 rows = p2:I + 0: 9442 + 443: subview 'p1' + VIEW 1 rows = p2:I + 0: 9443 + 444: subview 'p1' + VIEW 1 rows = p2:I + 0: 9444 + 445: subview 'p1' + VIEW 1 rows = p2:I + 0: 9445 + 446: subview 'p1' + VIEW 1 rows = p2:I + 0: 9446 + 447: subview 'p1' + VIEW 1 rows = p2:I + 0: 9447 + 448: subview 'p1' + VIEW 1 rows = p2:I + 0: 9448 + 449: subview 'p1' + VIEW 1 rows = p2:I + 0: 9449 + 450: subview 'p1' + VIEW 1 rows = p2:I + 0: 9450 + 451: subview 'p1' + VIEW 1 rows = p2:I + 0: 9451 + 452: subview 'p1' + VIEW 1 rows = p2:I + 0: 9452 + 453: subview 'p1' + VIEW 1 rows = p2:I + 0: 9453 + 454: subview 'p1' + VIEW 1 rows = p2:I + 0: 9454 + 455: subview 'p1' + VIEW 1 rows = p2:I + 0: 9455 + 456: subview 'p1' + VIEW 1 rows = p2:I + 0: 9456 + 457: subview 'p1' + VIEW 1 rows = p2:I + 0: 9457 + 458: subview 'p1' + VIEW 1 rows = p2:I + 0: 9458 + 459: subview 'p1' + VIEW 1 rows = p2:I + 0: 9459 + 460: subview 'p1' + VIEW 1 rows = p2:I + 0: 9460 + 461: subview 'p1' + VIEW 1 rows = p2:I + 0: 9461 + 462: subview 'p1' + VIEW 1 rows = p2:I + 0: 9462 + 463: subview 'p1' + VIEW 1 rows = p2:I + 0: 9463 + 464: subview 'p1' + VIEW 1 rows = p2:I + 0: 9464 + 465: subview 'p1' + VIEW 1 rows = p2:I + 0: 9465 + 466: subview 'p1' + VIEW 1 rows = p2:I + 0: 9466 + 467: subview 'p1' + VIEW 1 rows = p2:I + 0: 9467 + 468: subview 'p1' + VIEW 1 rows = p2:I + 0: 9468 + 469: subview 'p1' + VIEW 1 rows = p2:I + 0: 9469 + 470: subview 'p1' + VIEW 1 rows = p2:I + 0: 9470 + 471: subview 'p1' + VIEW 1 rows = p2:I + 0: 9471 + 472: subview 'p1' + VIEW 1 rows = p2:I + 0: 9472 + 473: subview 'p1' + VIEW 1 rows = p2:I + 0: 9473 + 474: subview 'p1' + VIEW 1 rows = p2:I + 0: 9474 + 475: subview 'p1' + VIEW 1 rows = p2:I + 0: 9475 + 476: subview 'p1' + VIEW 1 rows = p2:I + 0: 9476 + 477: subview 'p1' + VIEW 1 rows = p2:I + 0: 9477 + 478: subview 'p1' + VIEW 1 rows = p2:I + 0: 9478 + 479: subview 'p1' + VIEW 1 rows = p2:I + 0: 9479 + 480: subview 'p1' + VIEW 1 rows = p2:I + 0: 9480 + 481: subview 'p1' + VIEW 1 rows = p2:I + 0: 9481 + 482: subview 'p1' + VIEW 1 rows = p2:I + 0: 9482 + 483: subview 'p1' + VIEW 1 rows = p2:I + 0: 9483 + 484: subview 'p1' + VIEW 1 rows = p2:I + 0: 9484 + 485: subview 'p1' + VIEW 1 rows = p2:I + 0: 9485 + 486: subview 'p1' + VIEW 1 rows = p2:I + 0: 9486 + 487: subview 'p1' + VIEW 1 rows = p2:I + 0: 9487 + 488: subview 'p1' + VIEW 1 rows = p2:I + 0: 9488 + 489: subview 'p1' + VIEW 1 rows = p2:I + 0: 9489 + 490: subview 'p1' + VIEW 1 rows = p2:I + 0: 9490 + 491: subview 'p1' + VIEW 1 rows = p2:I + 0: 9491 + 492: subview 'p1' + VIEW 1 rows = p2:I + 0: 9492 + 493: subview 'p1' + VIEW 1 rows = p2:I + 0: 9493 + 494: subview 'p1' + VIEW 1 rows = p2:I + 0: 9494 + 495: subview 'p1' + VIEW 1 rows = p2:I + 0: 9495 + 496: subview 'p1' + VIEW 1 rows = p2:I + 0: 9496 + 497: subview 'p1' + VIEW 1 rows = p2:I + 0: 9497 + 498: subview 'p1' + VIEW 1 rows = p2:I + 0: 9498 + 499: subview 'p1' + VIEW 1 rows = p2:I + 0: 9499 diff --git a/akregator/src/mk4storage/metakit/tests/ok/l03b.txt b/akregator/src/mk4storage/metakit/tests/ok/l03b.txt new file mode 100644 index 000000000..daff458fa --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l03b.txt @@ -0,0 +1,1503 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 500 rows = p1:V + 0: subview 'p1' + VIEW 1 rows = p2:I + 0: 9000 + 1: subview 'p1' + VIEW 1 rows = p2:I + 0: 9001 + 2: subview 'p1' + VIEW 1 rows = p2:I + 0: 9002 + 3: subview 'p1' + VIEW 1 rows = p2:I + 0: 9003 + 4: subview 'p1' + VIEW 1 rows = p2:I + 0: 9004 + 5: subview 'p1' + VIEW 1 rows = p2:I + 0: 9005 + 6: subview 'p1' + VIEW 1 rows = p2:I + 0: 9006 + 7: subview 'p1' + VIEW 1 rows = p2:I + 0: 9007 + 8: subview 'p1' + VIEW 1 rows = p2:I + 0: 9008 + 9: subview 'p1' + VIEW 1 rows = p2:I + 0: 9009 + 10: subview 'p1' + VIEW 1 rows = p2:I + 0: 9010 + 11: subview 'p1' + VIEW 1 rows = p2:I + 0: 9011 + 12: subview 'p1' + VIEW 1 rows = p2:I + 0: 9012 + 13: subview 'p1' + VIEW 1 rows = p2:I + 0: 9013 + 14: subview 'p1' + VIEW 1 rows = p2:I + 0: 9014 + 15: subview 'p1' + VIEW 1 rows = p2:I + 0: 9015 + 16: subview 'p1' + VIEW 1 rows = p2:I + 0: 9016 + 17: subview 'p1' + VIEW 1 rows = p2:I + 0: 9017 + 18: subview 'p1' + VIEW 1 rows = p2:I + 0: 9018 + 19: subview 'p1' + VIEW 1 rows = p2:I + 0: 9019 + 20: subview 'p1' + VIEW 1 rows = p2:I + 0: 9020 + 21: subview 'p1' + VIEW 1 rows = p2:I + 0: 9021 + 22: subview 'p1' + VIEW 1 rows = p2:I + 0: 9022 + 23: subview 'p1' + VIEW 1 rows = p2:I + 0: 9023 + 24: subview 'p1' + VIEW 1 rows = p2:I + 0: 9024 + 25: subview 'p1' + VIEW 1 rows = p2:I + 0: 9025 + 26: subview 'p1' + VIEW 1 rows = p2:I + 0: 9026 + 27: subview 'p1' + VIEW 1 rows = p2:I + 0: 9027 + 28: subview 'p1' + VIEW 1 rows = p2:I + 0: 9028 + 29: subview 'p1' + VIEW 1 rows = p2:I + 0: 9029 + 30: subview 'p1' + VIEW 1 rows = p2:I + 0: 9030 + 31: subview 'p1' + VIEW 1 rows = p2:I + 0: 9031 + 32: subview 'p1' + VIEW 1 rows = p2:I + 0: 9032 + 33: subview 'p1' + VIEW 1 rows = p2:I + 0: 9033 + 34: subview 'p1' + VIEW 1 rows = p2:I + 0: 9034 + 35: subview 'p1' + VIEW 1 rows = p2:I + 0: 9035 + 36: subview 'p1' + VIEW 1 rows = p2:I + 0: 9036 + 37: subview 'p1' + VIEW 1 rows = p2:I + 0: 9037 + 38: subview 'p1' + VIEW 1 rows = p2:I + 0: 9038 + 39: subview 'p1' + VIEW 1 rows = p2:I + 0: 9039 + 40: subview 'p1' + VIEW 1 rows = p2:I + 0: 9040 + 41: subview 'p1' + VIEW 1 rows = p2:I + 0: 9041 + 42: subview 'p1' + VIEW 1 rows = p2:I + 0: 9042 + 43: subview 'p1' + VIEW 1 rows = p2:I + 0: 9043 + 44: subview 'p1' + VIEW 1 rows = p2:I + 0: 9044 + 45: subview 'p1' + VIEW 1 rows = p2:I + 0: 9045 + 46: subview 'p1' + VIEW 1 rows = p2:I + 0: 9046 + 47: subview 'p1' + VIEW 1 rows = p2:I + 0: 9047 + 48: subview 'p1' + VIEW 1 rows = p2:I + 0: 9048 + 49: subview 'p1' + VIEW 1 rows = p2:I + 0: 9049 + 50: subview 'p1' + VIEW 1 rows = p2:I + 0: 9050 + 51: subview 'p1' + VIEW 1 rows = p2:I + 0: 9051 + 52: subview 'p1' + VIEW 1 rows = p2:I + 0: 9052 + 53: subview 'p1' + VIEW 1 rows = p2:I + 0: 9053 + 54: subview 'p1' + VIEW 1 rows = p2:I + 0: 9054 + 55: subview 'p1' + VIEW 1 rows = p2:I + 0: 9055 + 56: subview 'p1' + VIEW 1 rows = p2:I + 0: 9056 + 57: subview 'p1' + VIEW 1 rows = p2:I + 0: 9057 + 58: subview 'p1' + VIEW 1 rows = p2:I + 0: 9058 + 59: subview 'p1' + VIEW 1 rows = p2:I + 0: 9059 + 60: subview 'p1' + VIEW 1 rows = p2:I + 0: 9060 + 61: subview 'p1' + VIEW 1 rows = p2:I + 0: 9061 + 62: subview 'p1' + VIEW 1 rows = p2:I + 0: 9062 + 63: subview 'p1' + VIEW 1 rows = p2:I + 0: 9063 + 64: subview 'p1' + VIEW 1 rows = p2:I + 0: 9064 + 65: subview 'p1' + VIEW 1 rows = p2:I + 0: 9065 + 66: subview 'p1' + VIEW 1 rows = p2:I + 0: 9066 + 67: subview 'p1' + VIEW 1 rows = p2:I + 0: 9067 + 68: subview 'p1' + VIEW 1 rows = p2:I + 0: 9068 + 69: subview 'p1' + VIEW 1 rows = p2:I + 0: 9069 + 70: subview 'p1' + VIEW 1 rows = p2:I + 0: 9070 + 71: subview 'p1' + VIEW 1 rows = p2:I + 0: 9071 + 72: subview 'p1' + VIEW 1 rows = p2:I + 0: 9072 + 73: subview 'p1' + VIEW 1 rows = p2:I + 0: 9073 + 74: subview 'p1' + VIEW 1 rows = p2:I + 0: 9074 + 75: subview 'p1' + VIEW 1 rows = p2:I + 0: 9075 + 76: subview 'p1' + VIEW 1 rows = p2:I + 0: 9076 + 77: subview 'p1' + VIEW 1 rows = p2:I + 0: 9077 + 78: subview 'p1' + VIEW 1 rows = p2:I + 0: 9078 + 79: subview 'p1' + VIEW 1 rows = p2:I + 0: 9079 + 80: subview 'p1' + VIEW 1 rows = p2:I + 0: 9080 + 81: subview 'p1' + VIEW 1 rows = p2:I + 0: 9081 + 82: subview 'p1' + VIEW 1 rows = p2:I + 0: 9082 + 83: subview 'p1' + VIEW 1 rows = p2:I + 0: 9083 + 84: subview 'p1' + VIEW 1 rows = p2:I + 0: 9084 + 85: subview 'p1' + VIEW 1 rows = p2:I + 0: 9085 + 86: subview 'p1' + VIEW 1 rows = p2:I + 0: 9086 + 87: subview 'p1' + VIEW 1 rows = p2:I + 0: 9087 + 88: subview 'p1' + VIEW 1 rows = p2:I + 0: 9088 + 89: subview 'p1' + VIEW 1 rows = p2:I + 0: 9089 + 90: subview 'p1' + VIEW 1 rows = p2:I + 0: 9090 + 91: subview 'p1' + VIEW 1 rows = p2:I + 0: 9091 + 92: subview 'p1' + VIEW 1 rows = p2:I + 0: 9092 + 93: subview 'p1' + VIEW 1 rows = p2:I + 0: 9093 + 94: subview 'p1' + VIEW 1 rows = p2:I + 0: 9094 + 95: subview 'p1' + VIEW 1 rows = p2:I + 0: 9095 + 96: subview 'p1' + VIEW 1 rows = p2:I + 0: 9096 + 97: subview 'p1' + VIEW 1 rows = p2:I + 0: 9097 + 98: subview 'p1' + VIEW 1 rows = p2:I + 0: 9098 + 99: subview 'p1' + VIEW 1 rows = p2:I + 0: 9099 + 100: subview 'p1' + VIEW 1 rows = p2:I + 0: 9100 + 101: subview 'p1' + VIEW 1 rows = p2:I + 0: 9101 + 102: subview 'p1' + VIEW 1 rows = p2:I + 0: 9102 + 103: subview 'p1' + VIEW 1 rows = p2:I + 0: 9103 + 104: subview 'p1' + VIEW 1 rows = p2:I + 0: 9104 + 105: subview 'p1' + VIEW 1 rows = p2:I + 0: 9105 + 106: subview 'p1' + VIEW 1 rows = p2:I + 0: 9106 + 107: subview 'p1' + VIEW 1 rows = p2:I + 0: 9107 + 108: subview 'p1' + VIEW 1 rows = p2:I + 0: 9108 + 109: subview 'p1' + VIEW 1 rows = p2:I + 0: 9109 + 110: subview 'p1' + VIEW 1 rows = p2:I + 0: 9110 + 111: subview 'p1' + VIEW 1 rows = p2:I + 0: 9111 + 112: subview 'p1' + VIEW 1 rows = p2:I + 0: 9112 + 113: subview 'p1' + VIEW 1 rows = p2:I + 0: 9113 + 114: subview 'p1' + VIEW 1 rows = p2:I + 0: 9114 + 115: subview 'p1' + VIEW 1 rows = p2:I + 0: 9115 + 116: subview 'p1' + VIEW 1 rows = p2:I + 0: 9116 + 117: subview 'p1' + VIEW 1 rows = p2:I + 0: 9117 + 118: subview 'p1' + VIEW 1 rows = p2:I + 0: 9118 + 119: subview 'p1' + VIEW 1 rows = p2:I + 0: 9119 + 120: subview 'p1' + VIEW 1 rows = p2:I + 0: 9120 + 121: subview 'p1' + VIEW 1 rows = p2:I + 0: 9121 + 122: subview 'p1' + VIEW 1 rows = p2:I + 0: 9122 + 123: subview 'p1' + VIEW 1 rows = p2:I + 0: 9123 + 124: subview 'p1' + VIEW 1 rows = p2:I + 0: 9124 + 125: subview 'p1' + VIEW 1 rows = p2:I + 0: 9125 + 126: subview 'p1' + VIEW 1 rows = p2:I + 0: 9126 + 127: subview 'p1' + VIEW 1 rows = p2:I + 0: 9127 + 128: subview 'p1' + VIEW 1 rows = p2:I + 0: 9128 + 129: subview 'p1' + VIEW 1 rows = p2:I + 0: 9129 + 130: subview 'p1' + VIEW 1 rows = p2:I + 0: 9130 + 131: subview 'p1' + VIEW 1 rows = p2:I + 0: 9131 + 132: subview 'p1' + VIEW 1 rows = p2:I + 0: 9132 + 133: subview 'p1' + VIEW 1 rows = p2:I + 0: 9133 + 134: subview 'p1' + VIEW 1 rows = p2:I + 0: 9134 + 135: subview 'p1' + VIEW 1 rows = p2:I + 0: 9135 + 136: subview 'p1' + VIEW 1 rows = p2:I + 0: 9136 + 137: subview 'p1' + VIEW 1 rows = p2:I + 0: 9137 + 138: subview 'p1' + VIEW 1 rows = p2:I + 0: 9138 + 139: subview 'p1' + VIEW 1 rows = p2:I + 0: 9139 + 140: subview 'p1' + VIEW 1 rows = p2:I + 0: 9140 + 141: subview 'p1' + VIEW 1 rows = p2:I + 0: 9141 + 142: subview 'p1' + VIEW 1 rows = p2:I + 0: 9142 + 143: subview 'p1' + VIEW 1 rows = p2:I + 0: 9143 + 144: subview 'p1' + VIEW 1 rows = p2:I + 0: 9144 + 145: subview 'p1' + VIEW 1 rows = p2:I + 0: 9145 + 146: subview 'p1' + VIEW 1 rows = p2:I + 0: 9146 + 147: subview 'p1' + VIEW 1 rows = p2:I + 0: 9147 + 148: subview 'p1' + VIEW 1 rows = p2:I + 0: 9148 + 149: subview 'p1' + VIEW 1 rows = p2:I + 0: 9149 + 150: subview 'p1' + VIEW 1 rows = p2:I + 0: 9150 + 151: subview 'p1' + VIEW 1 rows = p2:I + 0: 9151 + 152: subview 'p1' + VIEW 1 rows = p2:I + 0: 9152 + 153: subview 'p1' + VIEW 1 rows = p2:I + 0: 9153 + 154: subview 'p1' + VIEW 1 rows = p2:I + 0: 9154 + 155: subview 'p1' + VIEW 1 rows = p2:I + 0: 9155 + 156: subview 'p1' + VIEW 1 rows = p2:I + 0: 9156 + 157: subview 'p1' + VIEW 1 rows = p2:I + 0: 9157 + 158: subview 'p1' + VIEW 1 rows = p2:I + 0: 9158 + 159: subview 'p1' + VIEW 1 rows = p2:I + 0: 9159 + 160: subview 'p1' + VIEW 1 rows = p2:I + 0: 9160 + 161: subview 'p1' + VIEW 1 rows = p2:I + 0: 9161 + 162: subview 'p1' + VIEW 1 rows = p2:I + 0: 9162 + 163: subview 'p1' + VIEW 1 rows = p2:I + 0: 9163 + 164: subview 'p1' + VIEW 1 rows = p2:I + 0: 9164 + 165: subview 'p1' + VIEW 1 rows = p2:I + 0: 9165 + 166: subview 'p1' + VIEW 1 rows = p2:I + 0: 9166 + 167: subview 'p1' + VIEW 1 rows = p2:I + 0: 9167 + 168: subview 'p1' + VIEW 1 rows = p2:I + 0: 9168 + 169: subview 'p1' + VIEW 1 rows = p2:I + 0: 9169 + 170: subview 'p1' + VIEW 1 rows = p2:I + 0: 9170 + 171: subview 'p1' + VIEW 1 rows = p2:I + 0: 9171 + 172: subview 'p1' + VIEW 1 rows = p2:I + 0: 9172 + 173: subview 'p1' + VIEW 1 rows = p2:I + 0: 9173 + 174: subview 'p1' + VIEW 1 rows = p2:I + 0: 9174 + 175: subview 'p1' + VIEW 1 rows = p2:I + 0: 9175 + 176: subview 'p1' + VIEW 1 rows = p2:I + 0: 9176 + 177: subview 'p1' + VIEW 1 rows = p2:I + 0: 9177 + 178: subview 'p1' + VIEW 1 rows = p2:I + 0: 9178 + 179: subview 'p1' + VIEW 1 rows = p2:I + 0: 9179 + 180: subview 'p1' + VIEW 1 rows = p2:I + 0: 9180 + 181: subview 'p1' + VIEW 1 rows = p2:I + 0: 9181 + 182: subview 'p1' + VIEW 1 rows = p2:I + 0: 9182 + 183: subview 'p1' + VIEW 1 rows = p2:I + 0: 9183 + 184: subview 'p1' + VIEW 1 rows = p2:I + 0: 9184 + 185: subview 'p1' + VIEW 1 rows = p2:I + 0: 9185 + 186: subview 'p1' + VIEW 1 rows = p2:I + 0: 9186 + 187: subview 'p1' + VIEW 1 rows = p2:I + 0: 9187 + 188: subview 'p1' + VIEW 1 rows = p2:I + 0: 9188 + 189: subview 'p1' + VIEW 1 rows = p2:I + 0: 9189 + 190: subview 'p1' + VIEW 1 rows = p2:I + 0: 9190 + 191: subview 'p1' + VIEW 1 rows = p2:I + 0: 9191 + 192: subview 'p1' + VIEW 1 rows = p2:I + 0: 9192 + 193: subview 'p1' + VIEW 1 rows = p2:I + 0: 9193 + 194: subview 'p1' + VIEW 1 rows = p2:I + 0: 9194 + 195: subview 'p1' + VIEW 1 rows = p2:I + 0: 9195 + 196: subview 'p1' + VIEW 1 rows = p2:I + 0: 9196 + 197: subview 'p1' + VIEW 1 rows = p2:I + 0: 9197 + 198: subview 'p1' + VIEW 1 rows = p2:I + 0: 9198 + 199: subview 'p1' + VIEW 1 rows = p2:I + 0: 9199 + 200: subview 'p1' + VIEW 1 rows = p2:I + 0: 9200 + 201: subview 'p1' + VIEW 1 rows = p2:I + 0: 9201 + 202: subview 'p1' + VIEW 1 rows = p2:I + 0: 9202 + 203: subview 'p1' + VIEW 1 rows = p2:I + 0: 9203 + 204: subview 'p1' + VIEW 1 rows = p2:I + 0: 9204 + 205: subview 'p1' + VIEW 1 rows = p2:I + 0: 9205 + 206: subview 'p1' + VIEW 1 rows = p2:I + 0: 9206 + 207: subview 'p1' + VIEW 1 rows = p2:I + 0: 9207 + 208: subview 'p1' + VIEW 1 rows = p2:I + 0: 9208 + 209: subview 'p1' + VIEW 1 rows = p2:I + 0: 9209 + 210: subview 'p1' + VIEW 1 rows = p2:I + 0: 9210 + 211: subview 'p1' + VIEW 1 rows = p2:I + 0: 9211 + 212: subview 'p1' + VIEW 1 rows = p2:I + 0: 9212 + 213: subview 'p1' + VIEW 1 rows = p2:I + 0: 9213 + 214: subview 'p1' + VIEW 1 rows = p2:I + 0: 9214 + 215: subview 'p1' + VIEW 1 rows = p2:I + 0: 9215 + 216: subview 'p1' + VIEW 1 rows = p2:I + 0: 9216 + 217: subview 'p1' + VIEW 1 rows = p2:I + 0: 9217 + 218: subview 'p1' + VIEW 1 rows = p2:I + 0: 9218 + 219: subview 'p1' + VIEW 1 rows = p2:I + 0: 9219 + 220: subview 'p1' + VIEW 1 rows = p2:I + 0: 9220 + 221: subview 'p1' + VIEW 1 rows = p2:I + 0: 9221 + 222: subview 'p1' + VIEW 1 rows = p2:I + 0: 9222 + 223: subview 'p1' + VIEW 1 rows = p2:I + 0: 9223 + 224: subview 'p1' + VIEW 1 rows = p2:I + 0: 9224 + 225: subview 'p1' + VIEW 1 rows = p2:I + 0: 9225 + 226: subview 'p1' + VIEW 1 rows = p2:I + 0: 9226 + 227: subview 'p1' + VIEW 1 rows = p2:I + 0: 9227 + 228: subview 'p1' + VIEW 1 rows = p2:I + 0: 9228 + 229: subview 'p1' + VIEW 1 rows = p2:I + 0: 9229 + 230: subview 'p1' + VIEW 1 rows = p2:I + 0: 9230 + 231: subview 'p1' + VIEW 1 rows = p2:I + 0: 9231 + 232: subview 'p1' + VIEW 1 rows = p2:I + 0: 9232 + 233: subview 'p1' + VIEW 1 rows = p2:I + 0: 9233 + 234: subview 'p1' + VIEW 1 rows = p2:I + 0: 9234 + 235: subview 'p1' + VIEW 1 rows = p2:I + 0: 9235 + 236: subview 'p1' + VIEW 1 rows = p2:I + 0: 9236 + 237: subview 'p1' + VIEW 1 rows = p2:I + 0: 9237 + 238: subview 'p1' + VIEW 1 rows = p2:I + 0: 9238 + 239: subview 'p1' + VIEW 1 rows = p2:I + 0: 9239 + 240: subview 'p1' + VIEW 1 rows = p2:I + 0: 9240 + 241: subview 'p1' + VIEW 1 rows = p2:I + 0: 9241 + 242: subview 'p1' + VIEW 1 rows = p2:I + 0: 9242 + 243: subview 'p1' + VIEW 1 rows = p2:I + 0: 9243 + 244: subview 'p1' + VIEW 1 rows = p2:I + 0: 9244 + 245: subview 'p1' + VIEW 1 rows = p2:I + 0: 9245 + 246: subview 'p1' + VIEW 1 rows = p2:I + 0: 9246 + 247: subview 'p1' + VIEW 1 rows = p2:I + 0: 9247 + 248: subview 'p1' + VIEW 1 rows = p2:I + 0: 9248 + 249: subview 'p1' + VIEW 1 rows = p2:I + 0: 9249 + 250: subview 'p1' + VIEW 1 rows = p2:I + 0: 9250 + 251: subview 'p1' + VIEW 1 rows = p2:I + 0: 9251 + 252: subview 'p1' + VIEW 1 rows = p2:I + 0: 9252 + 253: subview 'p1' + VIEW 1 rows = p2:I + 0: 9253 + 254: subview 'p1' + VIEW 1 rows = p2:I + 0: 9254 + 255: subview 'p1' + VIEW 1 rows = p2:I + 0: 9255 + 256: subview 'p1' + VIEW 1 rows = p2:I + 0: 9256 + 257: subview 'p1' + VIEW 1 rows = p2:I + 0: 9257 + 258: subview 'p1' + VIEW 1 rows = p2:I + 0: 9258 + 259: subview 'p1' + VIEW 1 rows = p2:I + 0: 9259 + 260: subview 'p1' + VIEW 1 rows = p2:I + 0: 9260 + 261: subview 'p1' + VIEW 1 rows = p2:I + 0: 9261 + 262: subview 'p1' + VIEW 1 rows = p2:I + 0: 9262 + 263: subview 'p1' + VIEW 1 rows = p2:I + 0: 9263 + 264: subview 'p1' + VIEW 1 rows = p2:I + 0: 9264 + 265: subview 'p1' + VIEW 1 rows = p2:I + 0: 9265 + 266: subview 'p1' + VIEW 1 rows = p2:I + 0: 9266 + 267: subview 'p1' + VIEW 1 rows = p2:I + 0: 9267 + 268: subview 'p1' + VIEW 1 rows = p2:I + 0: 9268 + 269: subview 'p1' + VIEW 1 rows = p2:I + 0: 9269 + 270: subview 'p1' + VIEW 1 rows = p2:I + 0: 9270 + 271: subview 'p1' + VIEW 1 rows = p2:I + 0: 9271 + 272: subview 'p1' + VIEW 1 rows = p2:I + 0: 9272 + 273: subview 'p1' + VIEW 1 rows = p2:I + 0: 9273 + 274: subview 'p1' + VIEW 1 rows = p2:I + 0: 9274 + 275: subview 'p1' + VIEW 1 rows = p2:I + 0: 9275 + 276: subview 'p1' + VIEW 1 rows = p2:I + 0: 9276 + 277: subview 'p1' + VIEW 1 rows = p2:I + 0: 9277 + 278: subview 'p1' + VIEW 1 rows = p2:I + 0: 9278 + 279: subview 'p1' + VIEW 1 rows = p2:I + 0: 9279 + 280: subview 'p1' + VIEW 1 rows = p2:I + 0: 9280 + 281: subview 'p1' + VIEW 1 rows = p2:I + 0: 9281 + 282: subview 'p1' + VIEW 1 rows = p2:I + 0: 9282 + 283: subview 'p1' + VIEW 1 rows = p2:I + 0: 9283 + 284: subview 'p1' + VIEW 1 rows = p2:I + 0: 9284 + 285: subview 'p1' + VIEW 1 rows = p2:I + 0: 9285 + 286: subview 'p1' + VIEW 1 rows = p2:I + 0: 9286 + 287: subview 'p1' + VIEW 1 rows = p2:I + 0: 9287 + 288: subview 'p1' + VIEW 1 rows = p2:I + 0: 9288 + 289: subview 'p1' + VIEW 1 rows = p2:I + 0: 9289 + 290: subview 'p1' + VIEW 1 rows = p2:I + 0: 9290 + 291: subview 'p1' + VIEW 1 rows = p2:I + 0: 9291 + 292: subview 'p1' + VIEW 1 rows = p2:I + 0: 9292 + 293: subview 'p1' + VIEW 1 rows = p2:I + 0: 9293 + 294: subview 'p1' + VIEW 1 rows = p2:I + 0: 9294 + 295: subview 'p1' + VIEW 1 rows = p2:I + 0: 9295 + 296: subview 'p1' + VIEW 1 rows = p2:I + 0: 9296 + 297: subview 'p1' + VIEW 1 rows = p2:I + 0: 9297 + 298: subview 'p1' + VIEW 1 rows = p2:I + 0: 9298 + 299: subview 'p1' + VIEW 1 rows = p2:I + 0: 9299 + 300: subview 'p1' + VIEW 1 rows = p2:I + 0: 9300 + 301: subview 'p1' + VIEW 1 rows = p2:I + 0: 9301 + 302: subview 'p1' + VIEW 1 rows = p2:I + 0: 9302 + 303: subview 'p1' + VIEW 1 rows = p2:I + 0: 9303 + 304: subview 'p1' + VIEW 1 rows = p2:I + 0: 9304 + 305: subview 'p1' + VIEW 1 rows = p2:I + 0: 9305 + 306: subview 'p1' + VIEW 1 rows = p2:I + 0: 9306 + 307: subview 'p1' + VIEW 1 rows = p2:I + 0: 9307 + 308: subview 'p1' + VIEW 1 rows = p2:I + 0: 9308 + 309: subview 'p1' + VIEW 1 rows = p2:I + 0: 9309 + 310: subview 'p1' + VIEW 1 rows = p2:I + 0: 9310 + 311: subview 'p1' + VIEW 1 rows = p2:I + 0: 9311 + 312: subview 'p1' + VIEW 1 rows = p2:I + 0: 9312 + 313: subview 'p1' + VIEW 1 rows = p2:I + 0: 9313 + 314: subview 'p1' + VIEW 1 rows = p2:I + 0: 9314 + 315: subview 'p1' + VIEW 1 rows = p2:I + 0: 9315 + 316: subview 'p1' + VIEW 1 rows = p2:I + 0: 9316 + 317: subview 'p1' + VIEW 1 rows = p2:I + 0: 9317 + 318: subview 'p1' + VIEW 1 rows = p2:I + 0: 9318 + 319: subview 'p1' + VIEW 1 rows = p2:I + 0: 9319 + 320: subview 'p1' + VIEW 1 rows = p2:I + 0: 9320 + 321: subview 'p1' + VIEW 1 rows = p2:I + 0: 9321 + 322: subview 'p1' + VIEW 1 rows = p2:I + 0: 9322 + 323: subview 'p1' + VIEW 1 rows = p2:I + 0: 9323 + 324: subview 'p1' + VIEW 1 rows = p2:I + 0: 9324 + 325: subview 'p1' + VIEW 1 rows = p2:I + 0: 9325 + 326: subview 'p1' + VIEW 1 rows = p2:I + 0: 9326 + 327: subview 'p1' + VIEW 1 rows = p2:I + 0: 9327 + 328: subview 'p1' + VIEW 1 rows = p2:I + 0: 9328 + 329: subview 'p1' + VIEW 1 rows = p2:I + 0: 9329 + 330: subview 'p1' + VIEW 1 rows = p2:I + 0: 9330 + 331: subview 'p1' + VIEW 1 rows = p2:I + 0: 9331 + 332: subview 'p1' + VIEW 1 rows = p2:I + 0: 9332 + 333: subview 'p1' + VIEW 1 rows = p2:I + 0: 9333 + 334: subview 'p1' + VIEW 1 rows = p2:I + 0: 9334 + 335: subview 'p1' + VIEW 1 rows = p2:I + 0: 9335 + 336: subview 'p1' + VIEW 1 rows = p2:I + 0: 9336 + 337: subview 'p1' + VIEW 1 rows = p2:I + 0: 9337 + 338: subview 'p1' + VIEW 1 rows = p2:I + 0: 9338 + 339: subview 'p1' + VIEW 1 rows = p2:I + 0: 9339 + 340: subview 'p1' + VIEW 1 rows = p2:I + 0: 9340 + 341: subview 'p1' + VIEW 1 rows = p2:I + 0: 9341 + 342: subview 'p1' + VIEW 1 rows = p2:I + 0: 9342 + 343: subview 'p1' + VIEW 1 rows = p2:I + 0: 9343 + 344: subview 'p1' + VIEW 1 rows = p2:I + 0: 9344 + 345: subview 'p1' + VIEW 1 rows = p2:I + 0: 9345 + 346: subview 'p1' + VIEW 1 rows = p2:I + 0: 9346 + 347: subview 'p1' + VIEW 1 rows = p2:I + 0: 9347 + 348: subview 'p1' + VIEW 1 rows = p2:I + 0: 9348 + 349: subview 'p1' + VIEW 1 rows = p2:I + 0: 9349 + 350: subview 'p1' + VIEW 1 rows = p2:I + 0: 9350 + 351: subview 'p1' + VIEW 1 rows = p2:I + 0: 9351 + 352: subview 'p1' + VIEW 1 rows = p2:I + 0: 9352 + 353: subview 'p1' + VIEW 1 rows = p2:I + 0: 9353 + 354: subview 'p1' + VIEW 1 rows = p2:I + 0: 9354 + 355: subview 'p1' + VIEW 1 rows = p2:I + 0: 9355 + 356: subview 'p1' + VIEW 1 rows = p2:I + 0: 9356 + 357: subview 'p1' + VIEW 1 rows = p2:I + 0: 9357 + 358: subview 'p1' + VIEW 1 rows = p2:I + 0: 9358 + 359: subview 'p1' + VIEW 1 rows = p2:I + 0: 9359 + 360: subview 'p1' + VIEW 1 rows = p2:I + 0: 9360 + 361: subview 'p1' + VIEW 1 rows = p2:I + 0: 9361 + 362: subview 'p1' + VIEW 1 rows = p2:I + 0: 9362 + 363: subview 'p1' + VIEW 1 rows = p2:I + 0: 9363 + 364: subview 'p1' + VIEW 1 rows = p2:I + 0: 9364 + 365: subview 'p1' + VIEW 1 rows = p2:I + 0: 9365 + 366: subview 'p1' + VIEW 1 rows = p2:I + 0: 9366 + 367: subview 'p1' + VIEW 1 rows = p2:I + 0: 9367 + 368: subview 'p1' + VIEW 1 rows = p2:I + 0: 9368 + 369: subview 'p1' + VIEW 1 rows = p2:I + 0: 9369 + 370: subview 'p1' + VIEW 1 rows = p2:I + 0: 9370 + 371: subview 'p1' + VIEW 1 rows = p2:I + 0: 9371 + 372: subview 'p1' + VIEW 1 rows = p2:I + 0: 9372 + 373: subview 'p1' + VIEW 1 rows = p2:I + 0: 9373 + 374: subview 'p1' + VIEW 1 rows = p2:I + 0: 9374 + 375: subview 'p1' + VIEW 1 rows = p2:I + 0: 9375 + 376: subview 'p1' + VIEW 1 rows = p2:I + 0: 9376 + 377: subview 'p1' + VIEW 1 rows = p2:I + 0: 9377 + 378: subview 'p1' + VIEW 1 rows = p2:I + 0: 9378 + 379: subview 'p1' + VIEW 1 rows = p2:I + 0: 9379 + 380: subview 'p1' + VIEW 1 rows = p2:I + 0: 9380 + 381: subview 'p1' + VIEW 1 rows = p2:I + 0: 9381 + 382: subview 'p1' + VIEW 1 rows = p2:I + 0: 9382 + 383: subview 'p1' + VIEW 1 rows = p2:I + 0: 9383 + 384: subview 'p1' + VIEW 1 rows = p2:I + 0: 9384 + 385: subview 'p1' + VIEW 1 rows = p2:I + 0: 9385 + 386: subview 'p1' + VIEW 1 rows = p2:I + 0: 9386 + 387: subview 'p1' + VIEW 1 rows = p2:I + 0: 9387 + 388: subview 'p1' + VIEW 1 rows = p2:I + 0: 9388 + 389: subview 'p1' + VIEW 1 rows = p2:I + 0: 9389 + 390: subview 'p1' + VIEW 1 rows = p2:I + 0: 9390 + 391: subview 'p1' + VIEW 1 rows = p2:I + 0: 9391 + 392: subview 'p1' + VIEW 1 rows = p2:I + 0: 9392 + 393: subview 'p1' + VIEW 1 rows = p2:I + 0: 9393 + 394: subview 'p1' + VIEW 1 rows = p2:I + 0: 9394 + 395: subview 'p1' + VIEW 1 rows = p2:I + 0: 9395 + 396: subview 'p1' + VIEW 1 rows = p2:I + 0: 9396 + 397: subview 'p1' + VIEW 1 rows = p2:I + 0: 9397 + 398: subview 'p1' + VIEW 1 rows = p2:I + 0: 9398 + 399: subview 'p1' + VIEW 1 rows = p2:I + 0: 9399 + 400: subview 'p1' + VIEW 1 rows = p2:I + 0: 9400 + 401: subview 'p1' + VIEW 1 rows = p2:I + 0: 9401 + 402: subview 'p1' + VIEW 1 rows = p2:I + 0: 9402 + 403: subview 'p1' + VIEW 1 rows = p2:I + 0: 9403 + 404: subview 'p1' + VIEW 1 rows = p2:I + 0: 9404 + 405: subview 'p1' + VIEW 1 rows = p2:I + 0: 9405 + 406: subview 'p1' + VIEW 1 rows = p2:I + 0: 9406 + 407: subview 'p1' + VIEW 1 rows = p2:I + 0: 9407 + 408: subview 'p1' + VIEW 1 rows = p2:I + 0: 9408 + 409: subview 'p1' + VIEW 1 rows = p2:I + 0: 9409 + 410: subview 'p1' + VIEW 1 rows = p2:I + 0: 9410 + 411: subview 'p1' + VIEW 1 rows = p2:I + 0: 9411 + 412: subview 'p1' + VIEW 1 rows = p2:I + 0: 9412 + 413: subview 'p1' + VIEW 1 rows = p2:I + 0: 9413 + 414: subview 'p1' + VIEW 1 rows = p2:I + 0: 9414 + 415: subview 'p1' + VIEW 1 rows = p2:I + 0: 9415 + 416: subview 'p1' + VIEW 1 rows = p2:I + 0: 9416 + 417: subview 'p1' + VIEW 1 rows = p2:I + 0: 9417 + 418: subview 'p1' + VIEW 1 rows = p2:I + 0: 9418 + 419: subview 'p1' + VIEW 1 rows = p2:I + 0: 9419 + 420: subview 'p1' + VIEW 1 rows = p2:I + 0: 9420 + 421: subview 'p1' + VIEW 1 rows = p2:I + 0: 9421 + 422: subview 'p1' + VIEW 1 rows = p2:I + 0: 9422 + 423: subview 'p1' + VIEW 1 rows = p2:I + 0: 9423 + 424: subview 'p1' + VIEW 1 rows = p2:I + 0: 9424 + 425: subview 'p1' + VIEW 1 rows = p2:I + 0: 9425 + 426: subview 'p1' + VIEW 1 rows = p2:I + 0: 9426 + 427: subview 'p1' + VIEW 1 rows = p2:I + 0: 9427 + 428: subview 'p1' + VIEW 1 rows = p2:I + 0: 9428 + 429: subview 'p1' + VIEW 1 rows = p2:I + 0: 9429 + 430: subview 'p1' + VIEW 1 rows = p2:I + 0: 9430 + 431: subview 'p1' + VIEW 1 rows = p2:I + 0: 9431 + 432: subview 'p1' + VIEW 1 rows = p2:I + 0: 9432 + 433: subview 'p1' + VIEW 1 rows = p2:I + 0: 9433 + 434: subview 'p1' + VIEW 1 rows = p2:I + 0: 9434 + 435: subview 'p1' + VIEW 1 rows = p2:I + 0: 9435 + 436: subview 'p1' + VIEW 1 rows = p2:I + 0: 9436 + 437: subview 'p1' + VIEW 1 rows = p2:I + 0: 9437 + 438: subview 'p1' + VIEW 1 rows = p2:I + 0: 9438 + 439: subview 'p1' + VIEW 1 rows = p2:I + 0: 9439 + 440: subview 'p1' + VIEW 1 rows = p2:I + 0: 9440 + 441: subview 'p1' + VIEW 1 rows = p2:I + 0: 9441 + 442: subview 'p1' + VIEW 1 rows = p2:I + 0: 9442 + 443: subview 'p1' + VIEW 1 rows = p2:I + 0: 9443 + 444: subview 'p1' + VIEW 1 rows = p2:I + 0: 9444 + 445: subview 'p1' + VIEW 1 rows = p2:I + 0: 9445 + 446: subview 'p1' + VIEW 1 rows = p2:I + 0: 9446 + 447: subview 'p1' + VIEW 1 rows = p2:I + 0: 9447 + 448: subview 'p1' + VIEW 1 rows = p2:I + 0: 9448 + 449: subview 'p1' + VIEW 1 rows = p2:I + 0: 9449 + 450: subview 'p1' + VIEW 1 rows = p2:I + 0: 9450 + 451: subview 'p1' + VIEW 1 rows = p2:I + 0: 9451 + 452: subview 'p1' + VIEW 1 rows = p2:I + 0: 9452 + 453: subview 'p1' + VIEW 1 rows = p2:I + 0: 9453 + 454: subview 'p1' + VIEW 1 rows = p2:I + 0: 9454 + 455: subview 'p1' + VIEW 1 rows = p2:I + 0: 9455 + 456: subview 'p1' + VIEW 1 rows = p2:I + 0: 9456 + 457: subview 'p1' + VIEW 1 rows = p2:I + 0: 9457 + 458: subview 'p1' + VIEW 1 rows = p2:I + 0: 9458 + 459: subview 'p1' + VIEW 1 rows = p2:I + 0: 9459 + 460: subview 'p1' + VIEW 1 rows = p2:I + 0: 9460 + 461: subview 'p1' + VIEW 1 rows = p2:I + 0: 9461 + 462: subview 'p1' + VIEW 1 rows = p2:I + 0: 9462 + 463: subview 'p1' + VIEW 1 rows = p2:I + 0: 9463 + 464: subview 'p1' + VIEW 1 rows = p2:I + 0: 9464 + 465: subview 'p1' + VIEW 1 rows = p2:I + 0: 9465 + 466: subview 'p1' + VIEW 1 rows = p2:I + 0: 9466 + 467: subview 'p1' + VIEW 1 rows = p2:I + 0: 9467 + 468: subview 'p1' + VIEW 1 rows = p2:I + 0: 9468 + 469: subview 'p1' + VIEW 1 rows = p2:I + 0: 9469 + 470: subview 'p1' + VIEW 1 rows = p2:I + 0: 9470 + 471: subview 'p1' + VIEW 1 rows = p2:I + 0: 9471 + 472: subview 'p1' + VIEW 1 rows = p2:I + 0: 9472 + 473: subview 'p1' + VIEW 1 rows = p2:I + 0: 9473 + 474: subview 'p1' + VIEW 1 rows = p2:I + 0: 9474 + 475: subview 'p1' + VIEW 1 rows = p2:I + 0: 9475 + 476: subview 'p1' + VIEW 1 rows = p2:I + 0: 9476 + 477: subview 'p1' + VIEW 1 rows = p2:I + 0: 9477 + 478: subview 'p1' + VIEW 1 rows = p2:I + 0: 9478 + 479: subview 'p1' + VIEW 1 rows = p2:I + 0: 9479 + 480: subview 'p1' + VIEW 1 rows = p2:I + 0: 9480 + 481: subview 'p1' + VIEW 1 rows = p2:I + 0: 9481 + 482: subview 'p1' + VIEW 1 rows = p2:I + 0: 9482 + 483: subview 'p1' + VIEW 1 rows = p2:I + 0: 9483 + 484: subview 'p1' + VIEW 1 rows = p2:I + 0: 9484 + 485: subview 'p1' + VIEW 1 rows = p2:I + 0: 9485 + 486: subview 'p1' + VIEW 1 rows = p2:I + 0: 9486 + 487: subview 'p1' + VIEW 1 rows = p2:I + 0: 9487 + 488: subview 'p1' + VIEW 1 rows = p2:I + 0: 9488 + 489: subview 'p1' + VIEW 1 rows = p2:I + 0: 9489 + 490: subview 'p1' + VIEW 1 rows = p2:I + 0: 9490 + 491: subview 'p1' + VIEW 1 rows = p2:I + 0: 9491 + 492: subview 'p1' + VIEW 1 rows = p2:I + 0: 9492 + 493: subview 'p1' + VIEW 1 rows = p2:I + 0: 9493 + 494: subview 'p1' + VIEW 1 rows = p2:I + 0: 9494 + 495: subview 'p1' + VIEW 1 rows = p2:I + 0: 9495 + 496: subview 'p1' + VIEW 1 rows = p2:I + 0: 9496 + 497: subview 'p1' + VIEW 1 rows = p2:I + 0: 9497 + 498: subview 'p1' + VIEW 1 rows = p2:I + 0: 9498 + 499: subview 'p1' + VIEW 1 rows = p2:I + 0: 9499 diff --git a/akregator/src/mk4storage/metakit/tests/ok/l04.txt b/akregator/src/mk4storage/metakit/tests/ok/l04.txt new file mode 100644 index 000000000..23920007e --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l04.txt @@ -0,0 +1,2 @@ +>>> Modify sections in storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/l04a.txt b/akregator/src/mk4storage/metakit/tests/ok/l04a.txt new file mode 100644 index 000000000..2eed1fc28 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l04a.txt @@ -0,0 +1,1503 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 500 rows = p1:V + 0: subview 'p1' + VIEW 1 rows = p2:I + 0: 1 + 1: subview 'p1' + VIEW 1 rows = p2:I + 0: 9001 + 2: subview 'p1' + VIEW 1 rows = p2:I + 0: 9002 + 3: subview 'p1' + VIEW 1 rows = p2:I + 0: 9003 + 4: subview 'p1' + VIEW 1 rows = p2:I + 0: 9004 + 5: subview 'p1' + VIEW 1 rows = p2:I + 0: 9005 + 6: subview 'p1' + VIEW 1 rows = p2:I + 0: 9006 + 7: subview 'p1' + VIEW 1 rows = p2:I + 0: 9007 + 8: subview 'p1' + VIEW 1 rows = p2:I + 0: 9008 + 9: subview 'p1' + VIEW 1 rows = p2:I + 0: 9009 + 10: subview 'p1' + VIEW 1 rows = p2:I + 0: 9010 + 11: subview 'p1' + VIEW 1 rows = p2:I + 0: 9011 + 12: subview 'p1' + VIEW 1 rows = p2:I + 0: 9012 + 13: subview 'p1' + VIEW 1 rows = p2:I + 0: 9013 + 14: subview 'p1' + VIEW 1 rows = p2:I + 0: 9014 + 15: subview 'p1' + VIEW 1 rows = p2:I + 0: 9015 + 16: subview 'p1' + VIEW 1 rows = p2:I + 0: 9016 + 17: subview 'p1' + VIEW 1 rows = p2:I + 0: 9017 + 18: subview 'p1' + VIEW 1 rows = p2:I + 0: 9018 + 19: subview 'p1' + VIEW 1 rows = p2:I + 0: 9019 + 20: subview 'p1' + VIEW 1 rows = p2:I + 0: 9020 + 21: subview 'p1' + VIEW 1 rows = p2:I + 0: 9021 + 22: subview 'p1' + VIEW 1 rows = p2:I + 0: 9022 + 23: subview 'p1' + VIEW 1 rows = p2:I + 0: 9023 + 24: subview 'p1' + VIEW 1 rows = p2:I + 0: 9024 + 25: subview 'p1' + VIEW 1 rows = p2:I + 0: 9025 + 26: subview 'p1' + VIEW 1 rows = p2:I + 0: 9026 + 27: subview 'p1' + VIEW 1 rows = p2:I + 0: 9027 + 28: subview 'p1' + VIEW 1 rows = p2:I + 0: 9028 + 29: subview 'p1' + VIEW 1 rows = p2:I + 0: 9029 + 30: subview 'p1' + VIEW 1 rows = p2:I + 0: 9030 + 31: subview 'p1' + VIEW 1 rows = p2:I + 0: 9031 + 32: subview 'p1' + VIEW 1 rows = p2:I + 0: 9032 + 33: subview 'p1' + VIEW 1 rows = p2:I + 0: 9033 + 34: subview 'p1' + VIEW 1 rows = p2:I + 0: 9034 + 35: subview 'p1' + VIEW 1 rows = p2:I + 0: 9035 + 36: subview 'p1' + VIEW 1 rows = p2:I + 0: 9036 + 37: subview 'p1' + VIEW 1 rows = p2:I + 0: 9037 + 38: subview 'p1' + VIEW 1 rows = p2:I + 0: 9038 + 39: subview 'p1' + VIEW 1 rows = p2:I + 0: 9039 + 40: subview 'p1' + VIEW 1 rows = p2:I + 0: 9040 + 41: subview 'p1' + VIEW 1 rows = p2:I + 0: 9041 + 42: subview 'p1' + VIEW 1 rows = p2:I + 0: 9042 + 43: subview 'p1' + VIEW 1 rows = p2:I + 0: 9043 + 44: subview 'p1' + VIEW 1 rows = p2:I + 0: 9044 + 45: subview 'p1' + VIEW 1 rows = p2:I + 0: 9045 + 46: subview 'p1' + VIEW 1 rows = p2:I + 0: 9046 + 47: subview 'p1' + VIEW 1 rows = p2:I + 0: 9047 + 48: subview 'p1' + VIEW 1 rows = p2:I + 0: 9048 + 49: subview 'p1' + VIEW 1 rows = p2:I + 0: 9049 + 50: subview 'p1' + VIEW 1 rows = p2:I + 0: 9050 + 51: subview 'p1' + VIEW 1 rows = p2:I + 0: 9051 + 52: subview 'p1' + VIEW 1 rows = p2:I + 0: 9052 + 53: subview 'p1' + VIEW 1 rows = p2:I + 0: 9053 + 54: subview 'p1' + VIEW 1 rows = p2:I + 0: 9054 + 55: subview 'p1' + VIEW 1 rows = p2:I + 0: 9055 + 56: subview 'p1' + VIEW 1 rows = p2:I + 0: 9056 + 57: subview 'p1' + VIEW 1 rows = p2:I + 0: 9057 + 58: subview 'p1' + VIEW 1 rows = p2:I + 0: 9058 + 59: subview 'p1' + VIEW 1 rows = p2:I + 0: 9059 + 60: subview 'p1' + VIEW 1 rows = p2:I + 0: 9060 + 61: subview 'p1' + VIEW 1 rows = p2:I + 0: 9061 + 62: subview 'p1' + VIEW 1 rows = p2:I + 0: 9062 + 63: subview 'p1' + VIEW 1 rows = p2:I + 0: 9063 + 64: subview 'p1' + VIEW 1 rows = p2:I + 0: 9064 + 65: subview 'p1' + VIEW 1 rows = p2:I + 0: 9065 + 66: subview 'p1' + VIEW 1 rows = p2:I + 0: 9066 + 67: subview 'p1' + VIEW 1 rows = p2:I + 0: 9067 + 68: subview 'p1' + VIEW 1 rows = p2:I + 0: 9068 + 69: subview 'p1' + VIEW 1 rows = p2:I + 0: 9069 + 70: subview 'p1' + VIEW 1 rows = p2:I + 0: 9070 + 71: subview 'p1' + VIEW 1 rows = p2:I + 0: 9071 + 72: subview 'p1' + VIEW 1 rows = p2:I + 0: 9072 + 73: subview 'p1' + VIEW 1 rows = p2:I + 0: 9073 + 74: subview 'p1' + VIEW 1 rows = p2:I + 0: 9074 + 75: subview 'p1' + VIEW 1 rows = p2:I + 0: 9075 + 76: subview 'p1' + VIEW 1 rows = p2:I + 0: 9076 + 77: subview 'p1' + VIEW 1 rows = p2:I + 0: 9077 + 78: subview 'p1' + VIEW 1 rows = p2:I + 0: 9078 + 79: subview 'p1' + VIEW 1 rows = p2:I + 0: 9079 + 80: subview 'p1' + VIEW 1 rows = p2:I + 0: 9080 + 81: subview 'p1' + VIEW 1 rows = p2:I + 0: 9081 + 82: subview 'p1' + VIEW 1 rows = p2:I + 0: 9082 + 83: subview 'p1' + VIEW 1 rows = p2:I + 0: 9083 + 84: subview 'p1' + VIEW 1 rows = p2:I + 0: 9084 + 85: subview 'p1' + VIEW 1 rows = p2:I + 0: 9085 + 86: subview 'p1' + VIEW 1 rows = p2:I + 0: 9086 + 87: subview 'p1' + VIEW 1 rows = p2:I + 0: 9087 + 88: subview 'p1' + VIEW 1 rows = p2:I + 0: 9088 + 89: subview 'p1' + VIEW 1 rows = p2:I + 0: 9089 + 90: subview 'p1' + VIEW 1 rows = p2:I + 0: 9090 + 91: subview 'p1' + VIEW 1 rows = p2:I + 0: 9091 + 92: subview 'p1' + VIEW 1 rows = p2:I + 0: 9092 + 93: subview 'p1' + VIEW 1 rows = p2:I + 0: 9093 + 94: subview 'p1' + VIEW 1 rows = p2:I + 0: 9094 + 95: subview 'p1' + VIEW 1 rows = p2:I + 0: 9095 + 96: subview 'p1' + VIEW 1 rows = p2:I + 0: 9096 + 97: subview 'p1' + VIEW 1 rows = p2:I + 0: 9097 + 98: subview 'p1' + VIEW 1 rows = p2:I + 0: 9098 + 99: subview 'p1' + VIEW 1 rows = p2:I + 0: 9099 + 100: subview 'p1' + VIEW 1 rows = p2:I + 0: 9100 + 101: subview 'p1' + VIEW 1 rows = p2:I + 0: 9101 + 102: subview 'p1' + VIEW 1 rows = p2:I + 0: 9102 + 103: subview 'p1' + VIEW 1 rows = p2:I + 0: 9103 + 104: subview 'p1' + VIEW 1 rows = p2:I + 0: 9104 + 105: subview 'p1' + VIEW 1 rows = p2:I + 0: 9105 + 106: subview 'p1' + VIEW 1 rows = p2:I + 0: 9106 + 107: subview 'p1' + VIEW 1 rows = p2:I + 0: 9107 + 108: subview 'p1' + VIEW 1 rows = p2:I + 0: 9108 + 109: subview 'p1' + VIEW 1 rows = p2:I + 0: 9109 + 110: subview 'p1' + VIEW 1 rows = p2:I + 0: 9110 + 111: subview 'p1' + VIEW 1 rows = p2:I + 0: 9111 + 112: subview 'p1' + VIEW 1 rows = p2:I + 0: 9112 + 113: subview 'p1' + VIEW 1 rows = p2:I + 0: 9113 + 114: subview 'p1' + VIEW 1 rows = p2:I + 0: 9114 + 115: subview 'p1' + VIEW 1 rows = p2:I + 0: 9115 + 116: subview 'p1' + VIEW 1 rows = p2:I + 0: 9116 + 117: subview 'p1' + VIEW 1 rows = p2:I + 0: 9117 + 118: subview 'p1' + VIEW 1 rows = p2:I + 0: 9118 + 119: subview 'p1' + VIEW 1 rows = p2:I + 0: 9119 + 120: subview 'p1' + VIEW 1 rows = p2:I + 0: 9120 + 121: subview 'p1' + VIEW 1 rows = p2:I + 0: 9121 + 122: subview 'p1' + VIEW 1 rows = p2:I + 0: 9122 + 123: subview 'p1' + VIEW 1 rows = p2:I + 0: 9123 + 124: subview 'p1' + VIEW 1 rows = p2:I + 0: 9124 + 125: subview 'p1' + VIEW 1 rows = p2:I + 0: 9125 + 126: subview 'p1' + VIEW 1 rows = p2:I + 0: 9126 + 127: subview 'p1' + VIEW 1 rows = p2:I + 0: 9127 + 128: subview 'p1' + VIEW 1 rows = p2:I + 0: 9128 + 129: subview 'p1' + VIEW 1 rows = p2:I + 0: 9129 + 130: subview 'p1' + VIEW 1 rows = p2:I + 0: 9130 + 131: subview 'p1' + VIEW 1 rows = p2:I + 0: 9131 + 132: subview 'p1' + VIEW 1 rows = p2:I + 0: 9132 + 133: subview 'p1' + VIEW 1 rows = p2:I + 0: 9133 + 134: subview 'p1' + VIEW 1 rows = p2:I + 0: 9134 + 135: subview 'p1' + VIEW 1 rows = p2:I + 0: 9135 + 136: subview 'p1' + VIEW 1 rows = p2:I + 0: 9136 + 137: subview 'p1' + VIEW 1 rows = p2:I + 0: 9137 + 138: subview 'p1' + VIEW 1 rows = p2:I + 0: 9138 + 139: subview 'p1' + VIEW 1 rows = p2:I + 0: 9139 + 140: subview 'p1' + VIEW 1 rows = p2:I + 0: 9140 + 141: subview 'p1' + VIEW 1 rows = p2:I + 0: 9141 + 142: subview 'p1' + VIEW 1 rows = p2:I + 0: 9142 + 143: subview 'p1' + VIEW 1 rows = p2:I + 0: 9143 + 144: subview 'p1' + VIEW 1 rows = p2:I + 0: 9144 + 145: subview 'p1' + VIEW 1 rows = p2:I + 0: 9145 + 146: subview 'p1' + VIEW 1 rows = p2:I + 0: 9146 + 147: subview 'p1' + VIEW 1 rows = p2:I + 0: 9147 + 148: subview 'p1' + VIEW 1 rows = p2:I + 0: 9148 + 149: subview 'p1' + VIEW 1 rows = p2:I + 0: 9149 + 150: subview 'p1' + VIEW 1 rows = p2:I + 0: 9150 + 151: subview 'p1' + VIEW 1 rows = p2:I + 0: 9151 + 152: subview 'p1' + VIEW 1 rows = p2:I + 0: 9152 + 153: subview 'p1' + VIEW 1 rows = p2:I + 0: 9153 + 154: subview 'p1' + VIEW 1 rows = p2:I + 0: 9154 + 155: subview 'p1' + VIEW 1 rows = p2:I + 0: 9155 + 156: subview 'p1' + VIEW 1 rows = p2:I + 0: 9156 + 157: subview 'p1' + VIEW 1 rows = p2:I + 0: 9157 + 158: subview 'p1' + VIEW 1 rows = p2:I + 0: 9158 + 159: subview 'p1' + VIEW 1 rows = p2:I + 0: 9159 + 160: subview 'p1' + VIEW 1 rows = p2:I + 0: 9160 + 161: subview 'p1' + VIEW 1 rows = p2:I + 0: 9161 + 162: subview 'p1' + VIEW 1 rows = p2:I + 0: 9162 + 163: subview 'p1' + VIEW 1 rows = p2:I + 0: 9163 + 164: subview 'p1' + VIEW 1 rows = p2:I + 0: 9164 + 165: subview 'p1' + VIEW 1 rows = p2:I + 0: 9165 + 166: subview 'p1' + VIEW 1 rows = p2:I + 0: 9166 + 167: subview 'p1' + VIEW 1 rows = p2:I + 0: 9167 + 168: subview 'p1' + VIEW 1 rows = p2:I + 0: 9168 + 169: subview 'p1' + VIEW 1 rows = p2:I + 0: 9169 + 170: subview 'p1' + VIEW 1 rows = p2:I + 0: 9170 + 171: subview 'p1' + VIEW 1 rows = p2:I + 0: 9171 + 172: subview 'p1' + VIEW 1 rows = p2:I + 0: 9172 + 173: subview 'p1' + VIEW 1 rows = p2:I + 0: 9173 + 174: subview 'p1' + VIEW 1 rows = p2:I + 0: 9174 + 175: subview 'p1' + VIEW 1 rows = p2:I + 0: 9175 + 176: subview 'p1' + VIEW 1 rows = p2:I + 0: 9176 + 177: subview 'p1' + VIEW 1 rows = p2:I + 0: 9177 + 178: subview 'p1' + VIEW 1 rows = p2:I + 0: 9178 + 179: subview 'p1' + VIEW 1 rows = p2:I + 0: 9179 + 180: subview 'p1' + VIEW 1 rows = p2:I + 0: 9180 + 181: subview 'p1' + VIEW 1 rows = p2:I + 0: 9181 + 182: subview 'p1' + VIEW 1 rows = p2:I + 0: 9182 + 183: subview 'p1' + VIEW 1 rows = p2:I + 0: 9183 + 184: subview 'p1' + VIEW 1 rows = p2:I + 0: 9184 + 185: subview 'p1' + VIEW 1 rows = p2:I + 0: 9185 + 186: subview 'p1' + VIEW 1 rows = p2:I + 0: 9186 + 187: subview 'p1' + VIEW 1 rows = p2:I + 0: 9187 + 188: subview 'p1' + VIEW 1 rows = p2:I + 0: 9188 + 189: subview 'p1' + VIEW 1 rows = p2:I + 0: 9189 + 190: subview 'p1' + VIEW 1 rows = p2:I + 0: 9190 + 191: subview 'p1' + VIEW 1 rows = p2:I + 0: 9191 + 192: subview 'p1' + VIEW 1 rows = p2:I + 0: 9192 + 193: subview 'p1' + VIEW 1 rows = p2:I + 0: 9193 + 194: subview 'p1' + VIEW 1 rows = p2:I + 0: 9194 + 195: subview 'p1' + VIEW 1 rows = p2:I + 0: 9195 + 196: subview 'p1' + VIEW 1 rows = p2:I + 0: 9196 + 197: subview 'p1' + VIEW 1 rows = p2:I + 0: 9197 + 198: subview 'p1' + VIEW 1 rows = p2:I + 0: 9198 + 199: subview 'p1' + VIEW 1 rows = p2:I + 0: 9199 + 200: subview 'p1' + VIEW 1 rows = p2:I + 0: 9200 + 201: subview 'p1' + VIEW 1 rows = p2:I + 0: 9201 + 202: subview 'p1' + VIEW 1 rows = p2:I + 0: 9202 + 203: subview 'p1' + VIEW 1 rows = p2:I + 0: 9203 + 204: subview 'p1' + VIEW 1 rows = p2:I + 0: 9204 + 205: subview 'p1' + VIEW 1 rows = p2:I + 0: 9205 + 206: subview 'p1' + VIEW 1 rows = p2:I + 0: 9206 + 207: subview 'p1' + VIEW 1 rows = p2:I + 0: 9207 + 208: subview 'p1' + VIEW 1 rows = p2:I + 0: 9208 + 209: subview 'p1' + VIEW 1 rows = p2:I + 0: 9209 + 210: subview 'p1' + VIEW 1 rows = p2:I + 0: 9210 + 211: subview 'p1' + VIEW 1 rows = p2:I + 0: 9211 + 212: subview 'p1' + VIEW 1 rows = p2:I + 0: 9212 + 213: subview 'p1' + VIEW 1 rows = p2:I + 0: 9213 + 214: subview 'p1' + VIEW 1 rows = p2:I + 0: 9214 + 215: subview 'p1' + VIEW 1 rows = p2:I + 0: 9215 + 216: subview 'p1' + VIEW 1 rows = p2:I + 0: 9216 + 217: subview 'p1' + VIEW 1 rows = p2:I + 0: 9217 + 218: subview 'p1' + VIEW 1 rows = p2:I + 0: 9218 + 219: subview 'p1' + VIEW 1 rows = p2:I + 0: 9219 + 220: subview 'p1' + VIEW 1 rows = p2:I + 0: 9220 + 221: subview 'p1' + VIEW 1 rows = p2:I + 0: 9221 + 222: subview 'p1' + VIEW 1 rows = p2:I + 0: 9222 + 223: subview 'p1' + VIEW 1 rows = p2:I + 0: 9223 + 224: subview 'p1' + VIEW 1 rows = p2:I + 0: 9224 + 225: subview 'p1' + VIEW 1 rows = p2:I + 0: 9225 + 226: subview 'p1' + VIEW 1 rows = p2:I + 0: 9226 + 227: subview 'p1' + VIEW 1 rows = p2:I + 0: 9227 + 228: subview 'p1' + VIEW 1 rows = p2:I + 0: 9228 + 229: subview 'p1' + VIEW 1 rows = p2:I + 0: 9229 + 230: subview 'p1' + VIEW 1 rows = p2:I + 0: 9230 + 231: subview 'p1' + VIEW 1 rows = p2:I + 0: 9231 + 232: subview 'p1' + VIEW 1 rows = p2:I + 0: 9232 + 233: subview 'p1' + VIEW 1 rows = p2:I + 0: 9233 + 234: subview 'p1' + VIEW 1 rows = p2:I + 0: 9234 + 235: subview 'p1' + VIEW 1 rows = p2:I + 0: 9235 + 236: subview 'p1' + VIEW 1 rows = p2:I + 0: 9236 + 237: subview 'p1' + VIEW 1 rows = p2:I + 0: 9237 + 238: subview 'p1' + VIEW 1 rows = p2:I + 0: 9238 + 239: subview 'p1' + VIEW 1 rows = p2:I + 0: 9239 + 240: subview 'p1' + VIEW 1 rows = p2:I + 0: 9240 + 241: subview 'p1' + VIEW 1 rows = p2:I + 0: 9241 + 242: subview 'p1' + VIEW 1 rows = p2:I + 0: 9242 + 243: subview 'p1' + VIEW 1 rows = p2:I + 0: 9243 + 244: subview 'p1' + VIEW 1 rows = p2:I + 0: 9244 + 245: subview 'p1' + VIEW 1 rows = p2:I + 0: 9245 + 246: subview 'p1' + VIEW 1 rows = p2:I + 0: 9246 + 247: subview 'p1' + VIEW 1 rows = p2:I + 0: 9247 + 248: subview 'p1' + VIEW 1 rows = p2:I + 0: 9248 + 249: subview 'p1' + VIEW 1 rows = p2:I + 0: 9249 + 250: subview 'p1' + VIEW 1 rows = p2:I + 0: 9250 + 251: subview 'p1' + VIEW 1 rows = p2:I + 0: 9251 + 252: subview 'p1' + VIEW 1 rows = p2:I + 0: 9252 + 253: subview 'p1' + VIEW 1 rows = p2:I + 0: 9253 + 254: subview 'p1' + VIEW 1 rows = p2:I + 0: 9254 + 255: subview 'p1' + VIEW 1 rows = p2:I + 0: 9255 + 256: subview 'p1' + VIEW 1 rows = p2:I + 0: 9256 + 257: subview 'p1' + VIEW 1 rows = p2:I + 0: 9257 + 258: subview 'p1' + VIEW 1 rows = p2:I + 0: 9258 + 259: subview 'p1' + VIEW 1 rows = p2:I + 0: 9259 + 260: subview 'p1' + VIEW 1 rows = p2:I + 0: 9260 + 261: subview 'p1' + VIEW 1 rows = p2:I + 0: 9261 + 262: subview 'p1' + VIEW 1 rows = p2:I + 0: 9262 + 263: subview 'p1' + VIEW 1 rows = p2:I + 0: 9263 + 264: subview 'p1' + VIEW 1 rows = p2:I + 0: 9264 + 265: subview 'p1' + VIEW 1 rows = p2:I + 0: 9265 + 266: subview 'p1' + VIEW 1 rows = p2:I + 0: 9266 + 267: subview 'p1' + VIEW 1 rows = p2:I + 0: 9267 + 268: subview 'p1' + VIEW 1 rows = p2:I + 0: 9268 + 269: subview 'p1' + VIEW 1 rows = p2:I + 0: 9269 + 270: subview 'p1' + VIEW 1 rows = p2:I + 0: 9270 + 271: subview 'p1' + VIEW 1 rows = p2:I + 0: 9271 + 272: subview 'p1' + VIEW 1 rows = p2:I + 0: 9272 + 273: subview 'p1' + VIEW 1 rows = p2:I + 0: 9273 + 274: subview 'p1' + VIEW 1 rows = p2:I + 0: 9274 + 275: subview 'p1' + VIEW 1 rows = p2:I + 0: 9275 + 276: subview 'p1' + VIEW 1 rows = p2:I + 0: 9276 + 277: subview 'p1' + VIEW 1 rows = p2:I + 0: 9277 + 278: subview 'p1' + VIEW 1 rows = p2:I + 0: 9278 + 279: subview 'p1' + VIEW 1 rows = p2:I + 0: 9279 + 280: subview 'p1' + VIEW 1 rows = p2:I + 0: 9280 + 281: subview 'p1' + VIEW 1 rows = p2:I + 0: 9281 + 282: subview 'p1' + VIEW 1 rows = p2:I + 0: 9282 + 283: subview 'p1' + VIEW 1 rows = p2:I + 0: 9283 + 284: subview 'p1' + VIEW 1 rows = p2:I + 0: 9284 + 285: subview 'p1' + VIEW 1 rows = p2:I + 0: 9285 + 286: subview 'p1' + VIEW 1 rows = p2:I + 0: 9286 + 287: subview 'p1' + VIEW 1 rows = p2:I + 0: 9287 + 288: subview 'p1' + VIEW 1 rows = p2:I + 0: 9288 + 289: subview 'p1' + VIEW 1 rows = p2:I + 0: 9289 + 290: subview 'p1' + VIEW 1 rows = p2:I + 0: 9290 + 291: subview 'p1' + VIEW 1 rows = p2:I + 0: 9291 + 292: subview 'p1' + VIEW 1 rows = p2:I + 0: 9292 + 293: subview 'p1' + VIEW 1 rows = p2:I + 0: 9293 + 294: subview 'p1' + VIEW 1 rows = p2:I + 0: 9294 + 295: subview 'p1' + VIEW 1 rows = p2:I + 0: 9295 + 296: subview 'p1' + VIEW 1 rows = p2:I + 0: 9296 + 297: subview 'p1' + VIEW 1 rows = p2:I + 0: 9297 + 298: subview 'p1' + VIEW 1 rows = p2:I + 0: 9298 + 299: subview 'p1' + VIEW 1 rows = p2:I + 0: 9299 + 300: subview 'p1' + VIEW 1 rows = p2:I + 0: 9300 + 301: subview 'p1' + VIEW 1 rows = p2:I + 0: 9301 + 302: subview 'p1' + VIEW 1 rows = p2:I + 0: 9302 + 303: subview 'p1' + VIEW 1 rows = p2:I + 0: 9303 + 304: subview 'p1' + VIEW 1 rows = p2:I + 0: 9304 + 305: subview 'p1' + VIEW 1 rows = p2:I + 0: 9305 + 306: subview 'p1' + VIEW 1 rows = p2:I + 0: 9306 + 307: subview 'p1' + VIEW 1 rows = p2:I + 0: 9307 + 308: subview 'p1' + VIEW 1 rows = p2:I + 0: 9308 + 309: subview 'p1' + VIEW 1 rows = p2:I + 0: 9309 + 310: subview 'p1' + VIEW 1 rows = p2:I + 0: 9310 + 311: subview 'p1' + VIEW 1 rows = p2:I + 0: 9311 + 312: subview 'p1' + VIEW 1 rows = p2:I + 0: 9312 + 313: subview 'p1' + VIEW 1 rows = p2:I + 0: 9313 + 314: subview 'p1' + VIEW 1 rows = p2:I + 0: 9314 + 315: subview 'p1' + VIEW 1 rows = p2:I + 0: 9315 + 316: subview 'p1' + VIEW 1 rows = p2:I + 0: 9316 + 317: subview 'p1' + VIEW 1 rows = p2:I + 0: 9317 + 318: subview 'p1' + VIEW 1 rows = p2:I + 0: 9318 + 319: subview 'p1' + VIEW 1 rows = p2:I + 0: 9319 + 320: subview 'p1' + VIEW 1 rows = p2:I + 0: 9320 + 321: subview 'p1' + VIEW 1 rows = p2:I + 0: 9321 + 322: subview 'p1' + VIEW 1 rows = p2:I + 0: 9322 + 323: subview 'p1' + VIEW 1 rows = p2:I + 0: 9323 + 324: subview 'p1' + VIEW 1 rows = p2:I + 0: 9324 + 325: subview 'p1' + VIEW 1 rows = p2:I + 0: 9325 + 326: subview 'p1' + VIEW 1 rows = p2:I + 0: 9326 + 327: subview 'p1' + VIEW 1 rows = p2:I + 0: 9327 + 328: subview 'p1' + VIEW 1 rows = p2:I + 0: 9328 + 329: subview 'p1' + VIEW 1 rows = p2:I + 0: 9329 + 330: subview 'p1' + VIEW 1 rows = p2:I + 0: 9330 + 331: subview 'p1' + VIEW 1 rows = p2:I + 0: 9331 + 332: subview 'p1' + VIEW 1 rows = p2:I + 0: 9332 + 333: subview 'p1' + VIEW 1 rows = p2:I + 0: 9333 + 334: subview 'p1' + VIEW 1 rows = p2:I + 0: 9334 + 335: subview 'p1' + VIEW 1 rows = p2:I + 0: 9335 + 336: subview 'p1' + VIEW 1 rows = p2:I + 0: 9336 + 337: subview 'p1' + VIEW 1 rows = p2:I + 0: 9337 + 338: subview 'p1' + VIEW 1 rows = p2:I + 0: 9338 + 339: subview 'p1' + VIEW 1 rows = p2:I + 0: 9339 + 340: subview 'p1' + VIEW 1 rows = p2:I + 0: 9340 + 341: subview 'p1' + VIEW 1 rows = p2:I + 0: 9341 + 342: subview 'p1' + VIEW 1 rows = p2:I + 0: 9342 + 343: subview 'p1' + VIEW 1 rows = p2:I + 0: 9343 + 344: subview 'p1' + VIEW 1 rows = p2:I + 0: 9344 + 345: subview 'p1' + VIEW 1 rows = p2:I + 0: 9345 + 346: subview 'p1' + VIEW 1 rows = p2:I + 0: 9346 + 347: subview 'p1' + VIEW 1 rows = p2:I + 0: 9347 + 348: subview 'p1' + VIEW 1 rows = p2:I + 0: 9348 + 349: subview 'p1' + VIEW 1 rows = p2:I + 0: 9349 + 350: subview 'p1' + VIEW 1 rows = p2:I + 0: 9350 + 351: subview 'p1' + VIEW 1 rows = p2:I + 0: 9351 + 352: subview 'p1' + VIEW 1 rows = p2:I + 0: 9352 + 353: subview 'p1' + VIEW 1 rows = p2:I + 0: 9353 + 354: subview 'p1' + VIEW 1 rows = p2:I + 0: 9354 + 355: subview 'p1' + VIEW 1 rows = p2:I + 0: 9355 + 356: subview 'p1' + VIEW 1 rows = p2:I + 0: 9356 + 357: subview 'p1' + VIEW 1 rows = p2:I + 0: 9357 + 358: subview 'p1' + VIEW 1 rows = p2:I + 0: 9358 + 359: subview 'p1' + VIEW 1 rows = p2:I + 0: 9359 + 360: subview 'p1' + VIEW 1 rows = p2:I + 0: 9360 + 361: subview 'p1' + VIEW 1 rows = p2:I + 0: 9361 + 362: subview 'p1' + VIEW 1 rows = p2:I + 0: 9362 + 363: subview 'p1' + VIEW 1 rows = p2:I + 0: 9363 + 364: subview 'p1' + VIEW 1 rows = p2:I + 0: 9364 + 365: subview 'p1' + VIEW 1 rows = p2:I + 0: 9365 + 366: subview 'p1' + VIEW 1 rows = p2:I + 0: 9366 + 367: subview 'p1' + VIEW 1 rows = p2:I + 0: 9367 + 368: subview 'p1' + VIEW 1 rows = p2:I + 0: 9368 + 369: subview 'p1' + VIEW 1 rows = p2:I + 0: 9369 + 370: subview 'p1' + VIEW 1 rows = p2:I + 0: 9370 + 371: subview 'p1' + VIEW 1 rows = p2:I + 0: 9371 + 372: subview 'p1' + VIEW 1 rows = p2:I + 0: 9372 + 373: subview 'p1' + VIEW 1 rows = p2:I + 0: 9373 + 374: subview 'p1' + VIEW 1 rows = p2:I + 0: 9374 + 375: subview 'p1' + VIEW 1 rows = p2:I + 0: 9375 + 376: subview 'p1' + VIEW 1 rows = p2:I + 0: 9376 + 377: subview 'p1' + VIEW 1 rows = p2:I + 0: 9377 + 378: subview 'p1' + VIEW 1 rows = p2:I + 0: 9378 + 379: subview 'p1' + VIEW 1 rows = p2:I + 0: 9379 + 380: subview 'p1' + VIEW 1 rows = p2:I + 0: 9380 + 381: subview 'p1' + VIEW 1 rows = p2:I + 0: 9381 + 382: subview 'p1' + VIEW 1 rows = p2:I + 0: 9382 + 383: subview 'p1' + VIEW 1 rows = p2:I + 0: 9383 + 384: subview 'p1' + VIEW 1 rows = p2:I + 0: 9384 + 385: subview 'p1' + VIEW 1 rows = p2:I + 0: 9385 + 386: subview 'p1' + VIEW 1 rows = p2:I + 0: 9386 + 387: subview 'p1' + VIEW 1 rows = p2:I + 0: 9387 + 388: subview 'p1' + VIEW 1 rows = p2:I + 0: 9388 + 389: subview 'p1' + VIEW 1 rows = p2:I + 0: 9389 + 390: subview 'p1' + VIEW 1 rows = p2:I + 0: 9390 + 391: subview 'p1' + VIEW 1 rows = p2:I + 0: 9391 + 392: subview 'p1' + VIEW 1 rows = p2:I + 0: 9392 + 393: subview 'p1' + VIEW 1 rows = p2:I + 0: 9393 + 394: subview 'p1' + VIEW 1 rows = p2:I + 0: 9394 + 395: subview 'p1' + VIEW 1 rows = p2:I + 0: 9395 + 396: subview 'p1' + VIEW 1 rows = p2:I + 0: 9396 + 397: subview 'p1' + VIEW 1 rows = p2:I + 0: 9397 + 398: subview 'p1' + VIEW 1 rows = p2:I + 0: 9398 + 399: subview 'p1' + VIEW 1 rows = p2:I + 0: 9399 + 400: subview 'p1' + VIEW 1 rows = p2:I + 0: 9400 + 401: subview 'p1' + VIEW 1 rows = p2:I + 0: 9401 + 402: subview 'p1' + VIEW 1 rows = p2:I + 0: 9402 + 403: subview 'p1' + VIEW 1 rows = p2:I + 0: 9403 + 404: subview 'p1' + VIEW 1 rows = p2:I + 0: 9404 + 405: subview 'p1' + VIEW 1 rows = p2:I + 0: 9405 + 406: subview 'p1' + VIEW 1 rows = p2:I + 0: 9406 + 407: subview 'p1' + VIEW 1 rows = p2:I + 0: 9407 + 408: subview 'p1' + VIEW 1 rows = p2:I + 0: 9408 + 409: subview 'p1' + VIEW 1 rows = p2:I + 0: 9409 + 410: subview 'p1' + VIEW 1 rows = p2:I + 0: 9410 + 411: subview 'p1' + VIEW 1 rows = p2:I + 0: 9411 + 412: subview 'p1' + VIEW 1 rows = p2:I + 0: 9412 + 413: subview 'p1' + VIEW 1 rows = p2:I + 0: 9413 + 414: subview 'p1' + VIEW 1 rows = p2:I + 0: 9414 + 415: subview 'p1' + VIEW 1 rows = p2:I + 0: 9415 + 416: subview 'p1' + VIEW 1 rows = p2:I + 0: 9416 + 417: subview 'p1' + VIEW 1 rows = p2:I + 0: 9417 + 418: subview 'p1' + VIEW 1 rows = p2:I + 0: 9418 + 419: subview 'p1' + VIEW 1 rows = p2:I + 0: 9419 + 420: subview 'p1' + VIEW 1 rows = p2:I + 0: 9420 + 421: subview 'p1' + VIEW 1 rows = p2:I + 0: 9421 + 422: subview 'p1' + VIEW 1 rows = p2:I + 0: 9422 + 423: subview 'p1' + VIEW 1 rows = p2:I + 0: 9423 + 424: subview 'p1' + VIEW 1 rows = p2:I + 0: 9424 + 425: subview 'p1' + VIEW 1 rows = p2:I + 0: 9425 + 426: subview 'p1' + VIEW 1 rows = p2:I + 0: 9426 + 427: subview 'p1' + VIEW 1 rows = p2:I + 0: 9427 + 428: subview 'p1' + VIEW 1 rows = p2:I + 0: 9428 + 429: subview 'p1' + VIEW 1 rows = p2:I + 0: 9429 + 430: subview 'p1' + VIEW 1 rows = p2:I + 0: 9430 + 431: subview 'p1' + VIEW 1 rows = p2:I + 0: 9431 + 432: subview 'p1' + VIEW 1 rows = p2:I + 0: 9432 + 433: subview 'p1' + VIEW 1 rows = p2:I + 0: 9433 + 434: subview 'p1' + VIEW 1 rows = p2:I + 0: 9434 + 435: subview 'p1' + VIEW 1 rows = p2:I + 0: 9435 + 436: subview 'p1' + VIEW 1 rows = p2:I + 0: 9436 + 437: subview 'p1' + VIEW 1 rows = p2:I + 0: 9437 + 438: subview 'p1' + VIEW 1 rows = p2:I + 0: 9438 + 439: subview 'p1' + VIEW 1 rows = p2:I + 0: 9439 + 440: subview 'p1' + VIEW 1 rows = p2:I + 0: 9440 + 441: subview 'p1' + VIEW 1 rows = p2:I + 0: 9441 + 442: subview 'p1' + VIEW 1 rows = p2:I + 0: 9442 + 443: subview 'p1' + VIEW 1 rows = p2:I + 0: 9443 + 444: subview 'p1' + VIEW 1 rows = p2:I + 0: 9444 + 445: subview 'p1' + VIEW 1 rows = p2:I + 0: 9445 + 446: subview 'p1' + VIEW 1 rows = p2:I + 0: 9446 + 447: subview 'p1' + VIEW 1 rows = p2:I + 0: 9447 + 448: subview 'p1' + VIEW 1 rows = p2:I + 0: 9448 + 449: subview 'p1' + VIEW 1 rows = p2:I + 0: 9449 + 450: subview 'p1' + VIEW 1 rows = p2:I + 0: 9450 + 451: subview 'p1' + VIEW 1 rows = p2:I + 0: 9451 + 452: subview 'p1' + VIEW 1 rows = p2:I + 0: 9452 + 453: subview 'p1' + VIEW 1 rows = p2:I + 0: 9453 + 454: subview 'p1' + VIEW 1 rows = p2:I + 0: 9454 + 455: subview 'p1' + VIEW 1 rows = p2:I + 0: 9455 + 456: subview 'p1' + VIEW 1 rows = p2:I + 0: 9456 + 457: subview 'p1' + VIEW 1 rows = p2:I + 0: 9457 + 458: subview 'p1' + VIEW 1 rows = p2:I + 0: 9458 + 459: subview 'p1' + VIEW 1 rows = p2:I + 0: 9459 + 460: subview 'p1' + VIEW 1 rows = p2:I + 0: 9460 + 461: subview 'p1' + VIEW 1 rows = p2:I + 0: 9461 + 462: subview 'p1' + VIEW 1 rows = p2:I + 0: 9462 + 463: subview 'p1' + VIEW 1 rows = p2:I + 0: 9463 + 464: subview 'p1' + VIEW 1 rows = p2:I + 0: 9464 + 465: subview 'p1' + VIEW 1 rows = p2:I + 0: 9465 + 466: subview 'p1' + VIEW 1 rows = p2:I + 0: 9466 + 467: subview 'p1' + VIEW 1 rows = p2:I + 0: 9467 + 468: subview 'p1' + VIEW 1 rows = p2:I + 0: 9468 + 469: subview 'p1' + VIEW 1 rows = p2:I + 0: 9469 + 470: subview 'p1' + VIEW 1 rows = p2:I + 0: 9470 + 471: subview 'p1' + VIEW 1 rows = p2:I + 0: 9471 + 472: subview 'p1' + VIEW 1 rows = p2:I + 0: 9472 + 473: subview 'p1' + VIEW 1 rows = p2:I + 0: 9473 + 474: subview 'p1' + VIEW 1 rows = p2:I + 0: 9474 + 475: subview 'p1' + VIEW 1 rows = p2:I + 0: 9475 + 476: subview 'p1' + VIEW 1 rows = p2:I + 0: 9476 + 477: subview 'p1' + VIEW 1 rows = p2:I + 0: 9477 + 478: subview 'p1' + VIEW 1 rows = p2:I + 0: 9478 + 479: subview 'p1' + VIEW 1 rows = p2:I + 0: 9479 + 480: subview 'p1' + VIEW 1 rows = p2:I + 0: 9480 + 481: subview 'p1' + VIEW 1 rows = p2:I + 0: 9481 + 482: subview 'p1' + VIEW 1 rows = p2:I + 0: 9482 + 483: subview 'p1' + VIEW 1 rows = p2:I + 0: 9483 + 484: subview 'p1' + VIEW 1 rows = p2:I + 0: 9484 + 485: subview 'p1' + VIEW 1 rows = p2:I + 0: 9485 + 486: subview 'p1' + VIEW 1 rows = p2:I + 0: 9486 + 487: subview 'p1' + VIEW 1 rows = p2:I + 0: 9487 + 488: subview 'p1' + VIEW 1 rows = p2:I + 0: 9488 + 489: subview 'p1' + VIEW 1 rows = p2:I + 0: 9489 + 490: subview 'p1' + VIEW 1 rows = p2:I + 0: 9490 + 491: subview 'p1' + VIEW 1 rows = p2:I + 0: 9491 + 492: subview 'p1' + VIEW 1 rows = p2:I + 0: 9492 + 493: subview 'p1' + VIEW 1 rows = p2:I + 0: 9493 + 494: subview 'p1' + VIEW 1 rows = p2:I + 0: 9494 + 495: subview 'p1' + VIEW 1 rows = p2:I + 0: 9495 + 496: subview 'p1' + VIEW 1 rows = p2:I + 0: 9496 + 497: subview 'p1' + VIEW 1 rows = p2:I + 0: 9497 + 498: subview 'p1' + VIEW 1 rows = p2:I + 0: 9498 + 499: subview 'p1' + VIEW 1 rows = p2:I + 0: 9499 diff --git a/akregator/src/mk4storage/metakit/tests/ok/l05.txt b/akregator/src/mk4storage/metakit/tests/ok/l05.txt new file mode 100644 index 000000000..64d8af7ad --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l05.txt @@ -0,0 +1,2 @@ +>>> Delete from 32 Kb of strings +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/l05a.txt b/akregator/src/mk4storage/metakit/tests/ok/l05a.txt new file mode 100644 index 000000000..d01cb2dc6 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l05a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I p2:S p3:S + 0: 1747 'The wizard of Oz' 'The wizard of Oz' diff --git a/akregator/src/mk4storage/metakit/tests/ok/l06.txt b/akregator/src/mk4storage/metakit/tests/ok/l06.txt new file mode 100644 index 000000000..d5e421d0d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l06.txt @@ -0,0 +1,2 @@ +>>> Bit field manipulations +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/l06a.txt b/akregator/src/mk4storage/metakit/tests/ok/l06a.txt new file mode 100644 index 000000000..05757a48e --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l06a.txt @@ -0,0 +1,1371 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1368 rows = p1:I + 0: 0 + 1: 1 + 2: 2 + 3: 3 + 4: 4 + 5: 5 + 6: 6 + 7: 7 + 8: 8 + 9: 9 + 10: 10 + 11: 11 + 12: 12 + 13: 13 + 14: 14 + 15: 15 + 16: 16 + 17: 17 + 18: 17 + 19: 17 + 20: 17 + 21: 17 + 22: 17 + 23: 17 + 24: 17 + 25: 17 + 26: 17 + 27: 17 + 28: 17 + 29: 17 + 30: 17 + 31: 17 + 32: 17 + 33: 17 + 34: 17 + 35: 16 + 36: 16 + 37: 16 + 38: 16 + 39: 16 + 40: 16 + 41: 16 + 42: 16 + 43: 16 + 44: 16 + 45: 16 + 46: 16 + 47: 16 + 48: 16 + 49: 16 + 50: 16 + 51: 15 + 52: 15 + 53: 15 + 54: 15 + 55: 15 + 56: 15 + 57: 15 + 58: 15 + 59: 15 + 60: 15 + 61: 15 + 62: 15 + 63: 15 + 64: 15 + 65: 15 + 66: 14 + 67: 14 + 68: 14 + 69: 14 + 70: 14 + 71: 14 + 72: 14 + 73: 14 + 74: 14 + 75: 14 + 76: 14 + 77: 14 + 78: 14 + 79: 14 + 80: 13 + 81: 13 + 82: 13 + 83: 13 + 84: 13 + 85: 13 + 86: 13 + 87: 13 + 88: 13 + 89: 13 + 90: 13 + 91: 13 + 92: 13 + 93: 12 + 94: 12 + 95: 12 + 96: 12 + 97: 12 + 98: 12 + 99: 12 + 100: 12 + 101: 12 + 102: 12 + 103: 12 + 104: 12 + 105: 11 + 106: 11 + 107: 11 + 108: 11 + 109: 11 + 110: 11 + 111: 11 + 112: 11 + 113: 11 + 114: 11 + 115: 11 + 116: 10 + 117: 10 + 118: 10 + 119: 10 + 120: 10 + 121: 10 + 122: 10 + 123: 10 + 124: 10 + 125: 10 + 126: 9 + 127: 9 + 128: 9 + 129: 9 + 130: 9 + 131: 9 + 132: 9 + 133: 9 + 134: 9 + 135: 8 + 136: 8 + 137: 8 + 138: 8 + 139: 8 + 140: 8 + 141: 8 + 142: 8 + 143: 7 + 144: 7 + 145: 7 + 146: 7 + 147: 7 + 148: 7 + 149: 7 + 150: 6 + 151: 6 + 152: 6 + 153: 6 + 154: 6 + 155: 6 + 156: 5 + 157: 5 + 158: 5 + 159: 5 + 160: 5 + 161: 4 + 162: 4 + 163: 4 + 164: 4 + 165: 3 + 166: 3 + 167: 3 + 168: 2 + 169: 2 + 170: 1 + 171: 0 + 172: 1 + 173: 2 + 174: 3 + 175: 4 + 176: 5 + 177: 6 + 178: 7 + 179: 8 + 180: 9 + 181: 10 + 182: 11 + 183: 12 + 184: 13 + 185: 14 + 186: 15 + 187: 16 + 188: 17 + 189: 17 + 190: 17 + 191: 17 + 192: 17 + 193: 17 + 194: 17 + 195: 17 + 196: 17 + 197: 17 + 198: 17 + 199: 17 + 200: 17 + 201: 17 + 202: 17 + 203: 17 + 204: 17 + 205: 17 + 206: 16 + 207: 16 + 208: 16 + 209: 16 + 210: 16 + 211: 16 + 212: 16 + 213: 16 + 214: 16 + 215: 16 + 216: 16 + 217: 16 + 218: 16 + 219: 16 + 220: 16 + 221: 16 + 222: 15 + 223: 15 + 224: 15 + 225: 15 + 226: 15 + 227: 15 + 228: 15 + 229: 15 + 230: 15 + 231: 15 + 232: 15 + 233: 15 + 234: 15 + 235: 15 + 236: 15 + 237: 14 + 238: 14 + 239: 14 + 240: 14 + 241: 14 + 242: 14 + 243: 14 + 244: 14 + 245: 14 + 246: 14 + 247: 14 + 248: 14 + 249: 14 + 250: 14 + 251: 13 + 252: 13 + 253: 13 + 254: 13 + 255: 13 + 256: 13 + 257: 13 + 258: 13 + 259: 13 + 260: 13 + 261: 13 + 262: 13 + 263: 13 + 264: 12 + 265: 12 + 266: 12 + 267: 12 + 268: 12 + 269: 12 + 270: 12 + 271: 12 + 272: 12 + 273: 12 + 274: 12 + 275: 12 + 276: 11 + 277: 11 + 278: 11 + 279: 11 + 280: 11 + 281: 11 + 282: 11 + 283: 11 + 284: 11 + 285: 11 + 286: 11 + 287: 10 + 288: 10 + 289: 10 + 290: 10 + 291: 10 + 292: 10 + 293: 10 + 294: 10 + 295: 10 + 296: 10 + 297: 9 + 298: 9 + 299: 9 + 300: 9 + 301: 9 + 302: 9 + 303: 9 + 304: 9 + 305: 9 + 306: 8 + 307: 8 + 308: 8 + 309: 8 + 310: 8 + 311: 8 + 312: 8 + 313: 8 + 314: 7 + 315: 7 + 316: 7 + 317: 7 + 318: 7 + 319: 7 + 320: 7 + 321: 6 + 322: 6 + 323: 6 + 324: 6 + 325: 6 + 326: 6 + 327: 5 + 328: 5 + 329: 5 + 330: 5 + 331: 5 + 332: 4 + 333: 4 + 334: 4 + 335: 4 + 336: 3 + 337: 3 + 338: 3 + 339: 2 + 340: 2 + 341: 1 + 342: 0 + 343: 1 + 344: 2 + 345: 3 + 346: 4 + 347: 5 + 348: 6 + 349: 7 + 350: 8 + 351: 9 + 352: 10 + 353: 11 + 354: 12 + 355: 13 + 356: 14 + 357: 15 + 358: 16 + 359: 17 + 360: 17 + 361: 17 + 362: 17 + 363: 17 + 364: 17 + 365: 17 + 366: 17 + 367: 17 + 368: 17 + 369: 17 + 370: 17 + 371: 17 + 372: 17 + 373: 17 + 374: 17 + 375: 17 + 376: 17 + 377: 16 + 378: 16 + 379: 16 + 380: 16 + 381: 16 + 382: 16 + 383: 16 + 384: 16 + 385: 16 + 386: 16 + 387: 16 + 388: 16 + 389: 16 + 390: 16 + 391: 16 + 392: 16 + 393: 15 + 394: 15 + 395: 15 + 396: 15 + 397: 15 + 398: 15 + 399: 15 + 400: 15 + 401: 15 + 402: 15 + 403: 15 + 404: 15 + 405: 15 + 406: 15 + 407: 15 + 408: 14 + 409: 14 + 410: 14 + 411: 14 + 412: 14 + 413: 14 + 414: 14 + 415: 14 + 416: 14 + 417: 14 + 418: 14 + 419: 14 + 420: 14 + 421: 14 + 422: 13 + 423: 13 + 424: 13 + 425: 13 + 426: 13 + 427: 13 + 428: 13 + 429: 13 + 430: 13 + 431: 13 + 432: 13 + 433: 13 + 434: 13 + 435: 12 + 436: 12 + 437: 12 + 438: 12 + 439: 12 + 440: 12 + 441: 12 + 442: 12 + 443: 12 + 444: 12 + 445: 12 + 446: 12 + 447: 11 + 448: 11 + 449: 11 + 450: 11 + 451: 11 + 452: 11 + 453: 11 + 454: 11 + 455: 11 + 456: 11 + 457: 11 + 458: 10 + 459: 10 + 460: 10 + 461: 10 + 462: 10 + 463: 10 + 464: 10 + 465: 10 + 466: 10 + 467: 10 + 468: 9 + 469: 9 + 470: 9 + 471: 9 + 472: 9 + 473: 9 + 474: 9 + 475: 9 + 476: 9 + 477: 8 + 478: 8 + 479: 8 + 480: 8 + 481: 8 + 482: 8 + 483: 8 + 484: 8 + 485: 7 + 486: 7 + 487: 7 + 488: 7 + 489: 7 + 490: 7 + 491: 7 + 492: 6 + 493: 6 + 494: 6 + 495: 6 + 496: 6 + 497: 6 + 498: 5 + 499: 5 + 500: 5 + 501: 5 + 502: 5 + 503: 4 + 504: 4 + 505: 4 + 506: 4 + 507: 3 + 508: 3 + 509: 3 + 510: 2 + 511: 2 + 512: 1 + 513: 0 + 514: 1 + 515: 2 + 516: 3 + 517: 4 + 518: 5 + 519: 6 + 520: 7 + 521: 8 + 522: 9 + 523: 10 + 524: 11 + 525: 12 + 526: 13 + 527: 14 + 528: 15 + 529: 16 + 530: 17 + 531: 17 + 532: 17 + 533: 17 + 534: 17 + 535: 17 + 536: 17 + 537: 17 + 538: 17 + 539: 17 + 540: 17 + 541: 17 + 542: 17 + 543: 17 + 544: 17 + 545: 17 + 546: 17 + 547: 17 + 548: 16 + 549: 16 + 550: 16 + 551: 16 + 552: 16 + 553: 16 + 554: 16 + 555: 16 + 556: 16 + 557: 16 + 558: 16 + 559: 16 + 560: 16 + 561: 16 + 562: 16 + 563: 16 + 564: 15 + 565: 15 + 566: 15 + 567: 15 + 568: 15 + 569: 15 + 570: 15 + 571: 15 + 572: 15 + 573: 15 + 574: 15 + 575: 15 + 576: 15 + 577: 15 + 578: 15 + 579: 14 + 580: 14 + 581: 14 + 582: 14 + 583: 14 + 584: 14 + 585: 14 + 586: 14 + 587: 14 + 588: 14 + 589: 14 + 590: 14 + 591: 14 + 592: 14 + 593: 13 + 594: 13 + 595: 13 + 596: 13 + 597: 13 + 598: 13 + 599: 13 + 600: 13 + 601: 13 + 602: 13 + 603: 13 + 604: 13 + 605: 13 + 606: 12 + 607: 12 + 608: 12 + 609: 12 + 610: 12 + 611: 12 + 612: 12 + 613: 12 + 614: 12 + 615: 12 + 616: 12 + 617: 12 + 618: 11 + 619: 11 + 620: 11 + 621: 11 + 622: 11 + 623: 11 + 624: 11 + 625: 11 + 626: 11 + 627: 11 + 628: 11 + 629: 10 + 630: 10 + 631: 10 + 632: 10 + 633: 10 + 634: 10 + 635: 10 + 636: 10 + 637: 10 + 638: 10 + 639: 9 + 640: 9 + 641: 9 + 642: 9 + 643: 9 + 644: 9 + 645: 9 + 646: 9 + 647: 9 + 648: 8 + 649: 8 + 650: 8 + 651: 8 + 652: 8 + 653: 8 + 654: 8 + 655: 8 + 656: 7 + 657: 7 + 658: 7 + 659: 7 + 660: 7 + 661: 7 + 662: 7 + 663: 6 + 664: 6 + 665: 6 + 666: 6 + 667: 6 + 668: 6 + 669: 5 + 670: 5 + 671: 5 + 672: 5 + 673: 5 + 674: 4 + 675: 4 + 676: 4 + 677: 4 + 678: 3 + 679: 3 + 680: 3 + 681: 2 + 682: 2 + 683: 1 + 684: 0 + 685: 1 + 686: 2 + 687: 3 + 688: 4 + 689: 5 + 690: 6 + 691: 7 + 692: 8 + 693: 9 + 694: 10 + 695: 11 + 696: 12 + 697: 13 + 698: 14 + 699: 15 + 700: 0 + 701: 1 + 702: 1 + 703: 1 + 704: 1 + 705: 1 + 706: 1 + 707: 1 + 708: 1 + 709: 1 + 710: 1 + 711: 1 + 712: 1 + 713: 1 + 714: 1 + 715: 1 + 716: 1 + 717: 1 + 718: 1 + 719: 0 + 720: 0 + 721: 0 + 722: 0 + 723: 0 + 724: 0 + 725: 0 + 726: 0 + 727: 0 + 728: 0 + 729: 0 + 730: 0 + 731: 0 + 732: 0 + 733: 0 + 734: 0 + 735: 15 + 736: 15 + 737: 15 + 738: 15 + 739: 15 + 740: 15 + 741: 15 + 742: 15 + 743: 15 + 744: 15 + 745: 15 + 746: 15 + 747: 15 + 748: 15 + 749: 15 + 750: 14 + 751: 14 + 752: 14 + 753: 14 + 754: 14 + 755: 14 + 756: 14 + 757: 14 + 758: 14 + 759: 14 + 760: 14 + 761: 14 + 762: 14 + 763: 14 + 764: 13 + 765: 13 + 766: 13 + 767: 13 + 768: 13 + 769: 13 + 770: 13 + 771: 13 + 772: 13 + 773: 13 + 774: 13 + 775: 13 + 776: 13 + 777: 12 + 778: 12 + 779: 12 + 780: 12 + 781: 12 + 782: 12 + 783: 12 + 784: 12 + 785: 12 + 786: 12 + 787: 12 + 788: 12 + 789: 11 + 790: 11 + 791: 11 + 792: 11 + 793: 11 + 794: 11 + 795: 11 + 796: 11 + 797: 11 + 798: 11 + 799: 11 + 800: 10 + 801: 10 + 802: 10 + 803: 10 + 804: 10 + 805: 10 + 806: 10 + 807: 10 + 808: 10 + 809: 10 + 810: 9 + 811: 9 + 812: 9 + 813: 9 + 814: 9 + 815: 9 + 816: 9 + 817: 9 + 818: 9 + 819: 8 + 820: 8 + 821: 8 + 822: 8 + 823: 8 + 824: 8 + 825: 8 + 826: 8 + 827: 7 + 828: 7 + 829: 7 + 830: 7 + 831: 7 + 832: 7 + 833: 7 + 834: 6 + 835: 6 + 836: 6 + 837: 6 + 838: 6 + 839: 6 + 840: 5 + 841: 5 + 842: 5 + 843: 5 + 844: 5 + 845: 4 + 846: 4 + 847: 4 + 848: 4 + 849: 3 + 850: 3 + 851: 3 + 852: 2 + 853: 2 + 854: 1 + 855: 0 + 856: 1 + 857: 2 + 858: 3 + 859: 4 + 860: 5 + 861: 6 + 862: 7 + 863: 0 + 864: 1 + 865: 2 + 866: 3 + 867: 4 + 868: 5 + 869: 6 + 870: 7 + 871: 0 + 872: 1 + 873: 1 + 874: 1 + 875: 1 + 876: 1 + 877: 1 + 878: 1 + 879: 1 + 880: 1 + 881: 1 + 882: 1 + 883: 1 + 884: 1 + 885: 1 + 886: 1 + 887: 1 + 888: 1 + 889: 1 + 890: 0 + 891: 0 + 892: 0 + 893: 0 + 894: 0 + 895: 0 + 896: 0 + 897: 0 + 898: 0 + 899: 0 + 900: 0 + 901: 0 + 902: 0 + 903: 0 + 904: 0 + 905: 0 + 906: 7 + 907: 7 + 908: 7 + 909: 7 + 910: 7 + 911: 7 + 912: 7 + 913: 7 + 914: 7 + 915: 7 + 916: 7 + 917: 7 + 918: 7 + 919: 7 + 920: 7 + 921: 6 + 922: 6 + 923: 6 + 924: 6 + 925: 6 + 926: 6 + 927: 6 + 928: 6 + 929: 6 + 930: 6 + 931: 6 + 932: 6 + 933: 6 + 934: 6 + 935: 5 + 936: 5 + 937: 5 + 938: 5 + 939: 5 + 940: 5 + 941: 5 + 942: 5 + 943: 5 + 944: 5 + 945: 5 + 946: 5 + 947: 5 + 948: 4 + 949: 4 + 950: 4 + 951: 4 + 952: 4 + 953: 4 + 954: 4 + 955: 4 + 956: 4 + 957: 4 + 958: 4 + 959: 4 + 960: 3 + 961: 3 + 962: 3 + 963: 3 + 964: 3 + 965: 3 + 966: 3 + 967: 3 + 968: 3 + 969: 3 + 970: 3 + 971: 2 + 972: 2 + 973: 2 + 974: 2 + 975: 2 + 976: 2 + 977: 2 + 978: 2 + 979: 2 + 980: 2 + 981: 1 + 982: 1 + 983: 1 + 984: 1 + 985: 1 + 986: 1 + 987: 1 + 988: 1 + 989: 1 + 990: 0 + 991: 0 + 992: 0 + 993: 0 + 994: 0 + 995: 0 + 996: 0 + 997: 0 + 998: 7 + 999: 7 + 1000: 7 + 1001: 7 + 1002: 7 + 1003: 7 + 1004: 7 + 1005: 6 + 1006: 6 + 1007: 6 + 1008: 6 + 1009: 6 + 1010: 6 + 1011: 5 + 1012: 5 + 1013: 5 + 1014: 5 + 1015: 5 + 1016: 4 + 1017: 4 + 1018: 4 + 1019: 4 + 1020: 3 + 1021: 3 + 1022: 3 + 1023: 2 + 1024: 2 + 1025: 1 + 1026: 0 + 1027: 1 + 1028: 2 + 1029: 3 + 1030: 0 + 1031: 1 + 1032: 2 + 1033: 3 + 1034: 0 + 1035: 1 + 1036: 2 + 1037: 3 + 1038: 0 + 1039: 1 + 1040: 2 + 1041: 3 + 1042: 0 + 1043: 1 + 1044: 1 + 1045: 1 + 1046: 1 + 1047: 1 + 1048: 1 + 1049: 1 + 1050: 1 + 1051: 1 + 1052: 1 + 1053: 1 + 1054: 1 + 1055: 1 + 1056: 1 + 1057: 1 + 1058: 1 + 1059: 1 + 1060: 1 + 1061: 0 + 1062: 0 + 1063: 0 + 1064: 0 + 1065: 0 + 1066: 0 + 1067: 0 + 1068: 0 + 1069: 0 + 1070: 0 + 1071: 0 + 1072: 0 + 1073: 0 + 1074: 0 + 1075: 0 + 1076: 0 + 1077: 3 + 1078: 3 + 1079: 3 + 1080: 3 + 1081: 3 + 1082: 3 + 1083: 3 + 1084: 3 + 1085: 3 + 1086: 3 + 1087: 3 + 1088: 3 + 1089: 3 + 1090: 3 + 1091: 3 + 1092: 2 + 1093: 2 + 1094: 2 + 1095: 2 + 1096: 2 + 1097: 2 + 1098: 2 + 1099: 2 + 1100: 2 + 1101: 2 + 1102: 2 + 1103: 2 + 1104: 2 + 1105: 2 + 1106: 1 + 1107: 1 + 1108: 1 + 1109: 1 + 1110: 1 + 1111: 1 + 1112: 1 + 1113: 1 + 1114: 1 + 1115: 1 + 1116: 1 + 1117: 1 + 1118: 1 + 1119: 0 + 1120: 0 + 1121: 0 + 1122: 0 + 1123: 0 + 1124: 0 + 1125: 0 + 1126: 0 + 1127: 0 + 1128: 0 + 1129: 0 + 1130: 0 + 1131: 3 + 1132: 3 + 1133: 3 + 1134: 3 + 1135: 3 + 1136: 3 + 1137: 3 + 1138: 3 + 1139: 3 + 1140: 3 + 1141: 3 + 1142: 2 + 1143: 2 + 1144: 2 + 1145: 2 + 1146: 2 + 1147: 2 + 1148: 2 + 1149: 2 + 1150: 2 + 1151: 2 + 1152: 1 + 1153: 1 + 1154: 1 + 1155: 1 + 1156: 1 + 1157: 1 + 1158: 1 + 1159: 1 + 1160: 1 + 1161: 0 + 1162: 0 + 1163: 0 + 1164: 0 + 1165: 0 + 1166: 0 + 1167: 0 + 1168: 0 + 1169: 3 + 1170: 3 + 1171: 3 + 1172: 3 + 1173: 3 + 1174: 3 + 1175: 3 + 1176: 2 + 1177: 2 + 1178: 2 + 1179: 2 + 1180: 2 + 1181: 2 + 1182: 1 + 1183: 1 + 1184: 1 + 1185: 1 + 1186: 1 + 1187: 0 + 1188: 0 + 1189: 0 + 1190: 0 + 1191: 3 + 1192: 3 + 1193: 3 + 1194: 2 + 1195: 2 + 1196: 1 + 1197: 0 + 1198: 1 + 1199: 0 + 1200: 1 + 1201: 0 + 1202: 1 + 1203: 0 + 1204: 1 + 1205: 0 + 1206: 1 + 1207: 0 + 1208: 1 + 1209: 0 + 1210: 1 + 1211: 0 + 1212: 1 + 1213: 0 + 1214: 1 + 1215: 1 + 1216: 1 + 1217: 1 + 1218: 1 + 1219: 1 + 1220: 1 + 1221: 1 + 1222: 1 + 1223: 1 + 1224: 1 + 1225: 1 + 1226: 1 + 1227: 1 + 1228: 1 + 1229: 1 + 1230: 1 + 1231: 1 + 1232: 0 + 1233: 0 + 1234: 0 + 1235: 0 + 1236: 0 + 1237: 0 + 1238: 0 + 1239: 0 + 1240: 0 + 1241: 0 + 1242: 0 + 1243: 0 + 1244: 0 + 1245: 0 + 1246: 0 + 1247: 0 + 1248: 1 + 1249: 1 + 1250: 1 + 1251: 1 + 1252: 1 + 1253: 1 + 1254: 1 + 1255: 1 + 1256: 1 + 1257: 1 + 1258: 1 + 1259: 1 + 1260: 1 + 1261: 1 + 1262: 1 + 1263: 0 + 1264: 0 + 1265: 0 + 1266: 0 + 1267: 0 + 1268: 0 + 1269: 0 + 1270: 0 + 1271: 0 + 1272: 0 + 1273: 0 + 1274: 0 + 1275: 0 + 1276: 0 + 1277: 1 + 1278: 1 + 1279: 1 + 1280: 1 + 1281: 1 + 1282: 1 + 1283: 1 + 1284: 1 + 1285: 1 + 1286: 1 + 1287: 1 + 1288: 1 + 1289: 1 + 1290: 0 + 1291: 0 + 1292: 0 + 1293: 0 + 1294: 0 + 1295: 0 + 1296: 0 + 1297: 0 + 1298: 0 + 1299: 0 + 1300: 0 + 1301: 0 + 1302: 1 + 1303: 1 + 1304: 1 + 1305: 1 + 1306: 1 + 1307: 1 + 1308: 1 + 1309: 1 + 1310: 1 + 1311: 1 + 1312: 1 + 1313: 0 + 1314: 0 + 1315: 0 + 1316: 0 + 1317: 0 + 1318: 0 + 1319: 0 + 1320: 0 + 1321: 0 + 1322: 0 + 1323: 1 + 1324: 1 + 1325: 1 + 1326: 1 + 1327: 1 + 1328: 1 + 1329: 1 + 1330: 1 + 1331: 1 + 1332: 0 + 1333: 0 + 1334: 0 + 1335: 0 + 1336: 0 + 1337: 0 + 1338: 0 + 1339: 0 + 1340: 1 + 1341: 1 + 1342: 1 + 1343: 1 + 1344: 1 + 1345: 1 + 1346: 1 + 1347: 0 + 1348: 0 + 1349: 0 + 1350: 0 + 1351: 0 + 1352: 0 + 1353: 1 + 1354: 1 + 1355: 1 + 1356: 1 + 1357: 1 + 1358: 0 + 1359: 0 + 1360: 0 + 1361: 0 + 1362: 1 + 1363: 1 + 1364: 1 + 1365: 0 + 1366: 0 + 1367: 1 diff --git a/akregator/src/mk4storage/metakit/tests/ok/l07.txt b/akregator/src/mk4storage/metakit/tests/ok/l07.txt new file mode 100644 index 000000000..fa6b10cfc --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l07.txt @@ -0,0 +1,2 @@ +>>> Huge description +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/l07a.txt b/akregator/src/mk4storage/metakit/tests/ok/l07a.txt new file mode 100644 index 000000000..11c013725 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/l07a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = a123456789a123456789a123456789p1:I a123456789a123456789a123456789p2:I a123456789a123456789a123456789p3:I a123456789a123456789a123456789p4:I a123456789a123456789a123456789p5:I a123456789a123456789a123456789p6:I a123456789a123456789a123456789p7:I a123456789a123456789a123456789p8:I a123456789a123456789a123456789p9:I a123456789a123456789a123456789p10:I a123456789a123456789a123456789p11:I a123456789a123456789a123456789p12:I a123456789a123456789a123456789p13:I a123456789a123456789a123456789p14:I a123456789a123456789a123456789p15:I a123456789a123456789a123456789p16:I a123456789a123456789a123456789p17:I a123456789a123456789a123456789p18:I a123456789a123456789a123456789p19:I a123456789a123456789a123456789p20:I a123456789a123456789a123456789p21:I a123456789a123456789a123456789p22:I a123456789a123456789a123456789p23:I a123456789a123456789a123456789p24:I a123456789a123456789a123456789p25:I a123456789a123456789a123456789p26:I a123456789a123456789a123456789p27:I a123456789a123456789a123456789p28:I a123456789a123456789a123456789p29:I a123456789a123456789a123456789p30:I a123456789a123456789a123456789p31:I a123456789a123456789a123456789p32:I a123456789a123456789a123456789p33:I a123456789a123456789a123456789p34:I a123456789a123456789a123456789p35:I a123456789a123456789a123456789p36:I a123456789a123456789a123456789p37:I a123456789a123456789a123456789p38:I a123456789a123456789a123456789p39:I a123456789a123456789a123456789p40:I a123456789a123456789a123456789p41:I a123456789a123456789a123456789p42:I a123456789a123456789a123456789p43:I a123456789a123456789a123456789p44:I a123456789a123456789a123456789p45:I a123456789a123456789a123456789p46:I a123456789a123456789a123456789p47:I a123456789a123456789a123456789p48:I a123456789a123456789a123456789p49:I a123456789a123456789a123456789p50:I a123456789a123456789a123456789p51:I a123456789a123456789a123456789p52:I a123456789a123456789a123456789p53:I a123456789a123456789a123456789p54:I a123456789a123456789a123456789p55:I a123456789a123456789a123456789p56:I a123456789a123456789a123456789p57:I a123456789a123456789a123456789p58:I a123456789a123456789a123456789p59:I a123456789a123456789a123456789p60:I a123456789a123456789a123456789p61:I a123456789a123456789a123456789p62:I a123456789a123456789a123456789p63:I a123456789a123456789a123456789p64:I a123456789a123456789a123456789p65:I a123456789a123456789a123456789p66:I a123456789a123456789a123456789p67:I a123456789a123456789a123456789p68:I a123456789a123456789a123456789p69:I a123456789a123456789a123456789p70:I a123456789a123456789a123456789p71:I a123456789a123456789a123456789p72:I a123456789a123456789a123456789p73:I a123456789a123456789a123456789p74:I a123456789a123456789a123456789p75:I a123456789a123456789a123456789p76:I a123456789a123456789a123456789p77:I a123456789a123456789a123456789p78:I a123456789a123456789a123456789p79:I a123456789a123456789a123456789p80:I a123456789a123456789a123456789p81:I a123456789a123456789a123456789p82:I a123456789a123456789a123456789p83:I a123456789a123456789a123456789p84:I a123456789a123456789a123456789p85:I a123456789a123456789a123456789p86:I a123456789a123456789a123456789p87:I a123456789a123456789a123456789p88:I a123456789a123456789a123456789p89:I a123456789a123456789a123456789p90:I a123456789a123456789a123456789p91:I a123456789a123456789a123456789p92:I a123456789a123456789a123456789p93:I a123456789a123456789a123456789p94:I a123456789a123456789a123456789p95:I a123456789a123456789a123456789p96:I a123456789a123456789a123456789p97:I a123456789a123456789a123456789p98:I a123456789a123456789a123456789p99:I a123456789a123456789a123456789p100:I a123456789a123456789a123456789p101:I a123456789a123456789a123456789p102:I a123456789a123456789a123456789p103:I a123456789a123456789a123456789p104:I a123456789a123456789a123456789p105:I a123456789a123456789a123456789p106:I a123456789a123456789a123456789p107:I a123456789a123456789a123456789p108:I a123456789a123456789a123456789p109:I a123456789a123456789a123456789p110:I a123456789a123456789a123456789p111:I a123456789a123456789a123456789p112:I a123456789a123456789a123456789p113:I a123456789a123456789a123456789p114:I a123456789a123456789a123456789p115:I a123456789a123456789a123456789p116:I a123456789a123456789a123456789p117:I a123456789a123456789a123456789p118:I a123456789a123456789a123456789p119:I a123456789a123456789a123456789p120:I a123456789a123456789a123456789p121:I a123456789a123456789a123456789p122:I a123456789a123456789a123456789p123:I a123456789a123456789a123456789p124:I a123456789a123456789a123456789p125:I a123456789a123456789a123456789p126:I a123456789a123456789a123456789p127:I a123456789a123456789a123456789p128:I a123456789a123456789a123456789p129:I a123456789a123456789a123456789p130:I a123456789a123456789a123456789p131:I a123456789a123456789a123456789p132:I a123456789a123456789a123456789p133:I a123456789a123456789a123456789p134:I a123456789a123456789a123456789p135:I a123456789a123456789a123456789p136:I a123456789a123456789a123456789p137:I a123456789a123456789a123456789p138:I a123456789a123456789a123456789p139:I a123456789a123456789a123456789p140:I a123456789a123456789a123456789p141:I a123456789a123456789a123456789p142:I a123456789a123456789a123456789p143:I a123456789a123456789a123456789p144:I a123456789a123456789a123456789p145:I a123456789a123456789a123456789p146:I a123456789a123456789a123456789p147:I a123456789a123456789a123456789p148:I a123456789a123456789a123456789p149:I + 0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/akregator/src/mk4storage/metakit/tests/ok/m01.txt b/akregator/src/mk4storage/metakit/tests/ok/m01.txt new file mode 100644 index 000000000..0c3bb84be --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m01.txt @@ -0,0 +1,2 @@ +>>> Hash mapping +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/m02.txt b/akregator/src/mk4storage/metakit/tests/ok/m02.txt new file mode 100644 index 000000000..703e30b2d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m02.txt @@ -0,0 +1,2 @@ +>>> Blocked view bug +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/m02a.txt b/akregator/src/mk4storage/metakit/tests/ok/m02a.txt new file mode 100644 index 000000000..c51c6f2a6 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m02a.txt @@ -0,0 +1,22 @@ + VIEW 1 rows = v1:V + 0: subview 'v1' + VIEW 2 rows = _B:V + 0: subview '_B' + VIEW 15 rows = p1:B + 0: (3490b) + 1: (3491b) + 2: (3492b) + 3: (3493b) + 4: (3494b) + 5: (3495b) + 6: (3496b) + 7: (3497b) + 8: (3498b) + 9: (3499b) + 10: (3500b) + 11: (3501b) + 12: (3502b) + 13: (3503b) + 14: (3504b) + 1: subview '_B' + VIEW 0 rows = p1:B diff --git a/akregator/src/mk4storage/metakit/tests/ok/m03.txt b/akregator/src/mk4storage/metakit/tests/ok/m03.txt new file mode 100644 index 000000000..f80527b3c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m03.txt @@ -0,0 +1,2 @@ +>>> Hash adds +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/m03a.txt b/akregator/src/mk4storage/metakit/tests/ok/m03a.txt new file mode 100644 index 000000000..dc96ffa34 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m03a.txt @@ -0,0 +1,55 @@ + VIEW 1 rows = d1:V m1:V d2:V m2:V d3:V m3:V d4:V m4:V + 0: subview 'd1' + VIEW 4 rows = p1:S + 0: 'one' + 1: 'two' + 2: 'three' + 3: 'four' + 0: subview 'm1' + VIEW 9 rows = _H:I _R:I + 0: 0 -1 + 1: -1413211378 1 + 2: -153687682 2 + 3: 0 -1 + 4: 0 -1 + 5: 0 -1 + 6: -1614533319 3 + 7: -245760992 0 + 8: 11 0 + 0: subview 'd2' + VIEW 3 rows = p1:S + 0: 'two' + 1: 'three' + 2: 'four' + 0: subview 'm2' + VIEW 9 rows = _H:I _R:I + 0: 0 -1 + 1: -1413211378 0 + 2: -153687682 1 + 3: 0 -1 + 4: 0 -1 + 5: 0 -1 + 6: -1614533319 2 + 7: 0 -1 + 8: 11 0 + 0: subview 'd3' + VIEW 2 rows = p1:S + 0: 'three' + 1: 'four' + 0: subview 'm3' + VIEW 5 rows = _H:I _R:I + 0: 0 -1 + 1: -153687682 0 + 2: -1614533319 1 + 3: 0 -1 + 4: 7 0 + 0: subview 'd4' + VIEW 1 rows = p1:S + 0: 'four' + 0: subview 'm4' + VIEW 5 rows = _H:I _R:I + 0: 0 -1 + 1: 0 -1 + 2: -1614533319 0 + 3: 0 -1 + 4: 7 0 diff --git a/akregator/src/mk4storage/metakit/tests/ok/m04.txt b/akregator/src/mk4storage/metakit/tests/ok/m04.txt new file mode 100644 index 000000000..036ca9951 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m04.txt @@ -0,0 +1,2 @@ +>>> Locate bug +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/m04a.txt b/akregator/src/mk4storage/metakit/tests/ok/m04a.txt new file mode 100644 index 000000000..fb38c3971 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m04a.txt @@ -0,0 +1,9 @@ + VIEW 1 rows = v1:V + 0: subview 'v1' + VIEW 6 rows = p1:I p2:S + 0: 1 'one' + 1: 2 'two' + 2: 3 'three' + 3: 4 'four' + 4: 5 'five' + 5: 6 'six' diff --git a/akregator/src/mk4storage/metakit/tests/ok/m05.txt b/akregator/src/mk4storage/metakit/tests/ok/m05.txt new file mode 100644 index 000000000..7b4e60c7d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m05.txt @@ -0,0 +1,2 @@ +>>> Blocked view with subviews +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/m05a.txt b/akregator/src/mk4storage/metakit/tests/ok/m05a.txt new file mode 100644 index 000000000..a9f34115d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m05a.txt @@ -0,0 +1,4012 @@ + VIEW 1 rows = v1:V + 0: subview 'v1' + VIEW 3 rows = _B:V + 0: subview '_B' + VIEW 500 rows = p1:S sv:V + 0: 'id-0' + 0: subview 'sv' + VIEW 1 rows = p2:I + 0: 0 + 1: 'id-1' + 1: subview 'sv' + VIEW 1 rows = p2:I + 0: 1 + 2: 'id-2' + 2: subview 'sv' + VIEW 1 rows = p2:I + 0: 2 + 3: 'id-3' + 3: subview 'sv' + VIEW 1 rows = p2:I + 0: 3 + 4: 'id-4' + 4: subview 'sv' + VIEW 1 rows = p2:I + 0: 4 + 5: 'id-5' + 5: subview 'sv' + VIEW 1 rows = p2:I + 0: 5 + 6: 'id-6' + 6: subview 'sv' + VIEW 1 rows = p2:I + 0: 6 + 7: 'id-7' + 7: subview 'sv' + VIEW 1 rows = p2:I + 0: 7 + 8: 'id-8' + 8: subview 'sv' + VIEW 1 rows = p2:I + 0: 8 + 9: 'id-9' + 9: subview 'sv' + VIEW 1 rows = p2:I + 0: 9 + 10: 'id-10' + 10: subview 'sv' + VIEW 1 rows = p2:I + 0: 10 + 11: 'id-11' + 11: subview 'sv' + VIEW 1 rows = p2:I + 0: 11 + 12: 'id-12' + 12: subview 'sv' + VIEW 1 rows = p2:I + 0: 12 + 13: 'id-13' + 13: subview 'sv' + VIEW 1 rows = p2:I + 0: 13 + 14: 'id-14' + 14: subview 'sv' + VIEW 1 rows = p2:I + 0: 14 + 15: 'id-15' + 15: subview 'sv' + VIEW 1 rows = p2:I + 0: 15 + 16: 'id-16' + 16: subview 'sv' + VIEW 1 rows = p2:I + 0: 16 + 17: 'id-17' + 17: subview 'sv' + VIEW 1 rows = p2:I + 0: 17 + 18: 'id-18' + 18: subview 'sv' + VIEW 1 rows = p2:I + 0: 18 + 19: 'id-19' + 19: subview 'sv' + VIEW 1 rows = p2:I + 0: 19 + 20: 'id-20' + 20: subview 'sv' + VIEW 1 rows = p2:I + 0: 20 + 21: 'id-21' + 21: subview 'sv' + VIEW 1 rows = p2:I + 0: 21 + 22: 'id-22' + 22: subview 'sv' + VIEW 1 rows = p2:I + 0: 22 + 23: 'id-23' + 23: subview 'sv' + VIEW 1 rows = p2:I + 0: 23 + 24: 'id-24' + 24: subview 'sv' + VIEW 1 rows = p2:I + 0: 24 + 25: 'id-25' + 25: subview 'sv' + VIEW 1 rows = p2:I + 0: 25 + 26: 'id-26' + 26: subview 'sv' + VIEW 1 rows = p2:I + 0: 26 + 27: 'id-27' + 27: subview 'sv' + VIEW 1 rows = p2:I + 0: 27 + 28: 'id-28' + 28: subview 'sv' + VIEW 1 rows = p2:I + 0: 28 + 29: 'id-29' + 29: subview 'sv' + VIEW 1 rows = p2:I + 0: 29 + 30: 'id-30' + 30: subview 'sv' + VIEW 1 rows = p2:I + 0: 30 + 31: 'id-31' + 31: subview 'sv' + VIEW 1 rows = p2:I + 0: 31 + 32: 'id-32' + 32: subview 'sv' + VIEW 1 rows = p2:I + 0: 32 + 33: 'id-33' + 33: subview 'sv' + VIEW 1 rows = p2:I + 0: 33 + 34: 'id-34' + 34: subview 'sv' + VIEW 1 rows = p2:I + 0: 34 + 35: 'id-35' + 35: subview 'sv' + VIEW 1 rows = p2:I + 0: 35 + 36: 'id-36' + 36: subview 'sv' + VIEW 1 rows = p2:I + 0: 36 + 37: 'id-37' + 37: subview 'sv' + VIEW 1 rows = p2:I + 0: 37 + 38: 'id-38' + 38: subview 'sv' + VIEW 1 rows = p2:I + 0: 38 + 39: 'id-39' + 39: subview 'sv' + VIEW 1 rows = p2:I + 0: 39 + 40: 'id-40' + 40: subview 'sv' + VIEW 1 rows = p2:I + 0: 40 + 41: 'id-41' + 41: subview 'sv' + VIEW 1 rows = p2:I + 0: 41 + 42: 'id-42' + 42: subview 'sv' + VIEW 1 rows = p2:I + 0: 42 + 43: 'id-43' + 43: subview 'sv' + VIEW 1 rows = p2:I + 0: 43 + 44: 'id-44' + 44: subview 'sv' + VIEW 1 rows = p2:I + 0: 44 + 45: 'id-45' + 45: subview 'sv' + VIEW 1 rows = p2:I + 0: 45 + 46: 'id-46' + 46: subview 'sv' + VIEW 1 rows = p2:I + 0: 46 + 47: 'id-47' + 47: subview 'sv' + VIEW 1 rows = p2:I + 0: 47 + 48: 'id-48' + 48: subview 'sv' + VIEW 1 rows = p2:I + 0: 48 + 49: 'id-49' + 49: subview 'sv' + VIEW 1 rows = p2:I + 0: 49 + 50: 'id-50' + 50: subview 'sv' + VIEW 1 rows = p2:I + 0: 50 + 51: 'id-51' + 51: subview 'sv' + VIEW 1 rows = p2:I + 0: 51 + 52: 'id-52' + 52: subview 'sv' + VIEW 1 rows = p2:I + 0: 52 + 53: 'id-53' + 53: subview 'sv' + VIEW 1 rows = p2:I + 0: 53 + 54: 'id-54' + 54: subview 'sv' + VIEW 1 rows = p2:I + 0: 54 + 55: 'id-55' + 55: subview 'sv' + VIEW 1 rows = p2:I + 0: 55 + 56: 'id-56' + 56: subview 'sv' + VIEW 1 rows = p2:I + 0: 56 + 57: 'id-57' + 57: subview 'sv' + VIEW 1 rows = p2:I + 0: 57 + 58: 'id-58' + 58: subview 'sv' + VIEW 1 rows = p2:I + 0: 58 + 59: 'id-59' + 59: subview 'sv' + VIEW 1 rows = p2:I + 0: 59 + 60: 'id-60' + 60: subview 'sv' + VIEW 1 rows = p2:I + 0: 60 + 61: 'id-61' + 61: subview 'sv' + VIEW 1 rows = p2:I + 0: 61 + 62: 'id-62' + 62: subview 'sv' + VIEW 1 rows = p2:I + 0: 62 + 63: 'id-63' + 63: subview 'sv' + VIEW 1 rows = p2:I + 0: 63 + 64: 'id-64' + 64: subview 'sv' + VIEW 1 rows = p2:I + 0: 64 + 65: 'id-65' + 65: subview 'sv' + VIEW 1 rows = p2:I + 0: 65 + 66: 'id-66' + 66: subview 'sv' + VIEW 1 rows = p2:I + 0: 66 + 67: 'id-67' + 67: subview 'sv' + VIEW 1 rows = p2:I + 0: 67 + 68: 'id-68' + 68: subview 'sv' + VIEW 1 rows = p2:I + 0: 68 + 69: 'id-69' + 69: subview 'sv' + VIEW 1 rows = p2:I + 0: 69 + 70: 'id-70' + 70: subview 'sv' + VIEW 1 rows = p2:I + 0: 70 + 71: 'id-71' + 71: subview 'sv' + VIEW 1 rows = p2:I + 0: 71 + 72: 'id-72' + 72: subview 'sv' + VIEW 1 rows = p2:I + 0: 72 + 73: 'id-73' + 73: subview 'sv' + VIEW 1 rows = p2:I + 0: 73 + 74: 'id-74' + 74: subview 'sv' + VIEW 1 rows = p2:I + 0: 74 + 75: 'id-75' + 75: subview 'sv' + VIEW 1 rows = p2:I + 0: 75 + 76: 'id-76' + 76: subview 'sv' + VIEW 1 rows = p2:I + 0: 76 + 77: 'id-77' + 77: subview 'sv' + VIEW 1 rows = p2:I + 0: 77 + 78: 'id-78' + 78: subview 'sv' + VIEW 1 rows = p2:I + 0: 78 + 79: 'id-79' + 79: subview 'sv' + VIEW 1 rows = p2:I + 0: 79 + 80: 'id-80' + 80: subview 'sv' + VIEW 1 rows = p2:I + 0: 80 + 81: 'id-81' + 81: subview 'sv' + VIEW 1 rows = p2:I + 0: 81 + 82: 'id-82' + 82: subview 'sv' + VIEW 1 rows = p2:I + 0: 82 + 83: 'id-83' + 83: subview 'sv' + VIEW 1 rows = p2:I + 0: 83 + 84: 'id-84' + 84: subview 'sv' + VIEW 1 rows = p2:I + 0: 84 + 85: 'id-85' + 85: subview 'sv' + VIEW 1 rows = p2:I + 0: 85 + 86: 'id-86' + 86: subview 'sv' + VIEW 1 rows = p2:I + 0: 86 + 87: 'id-87' + 87: subview 'sv' + VIEW 1 rows = p2:I + 0: 87 + 88: 'id-88' + 88: subview 'sv' + VIEW 1 rows = p2:I + 0: 88 + 89: 'id-89' + 89: subview 'sv' + VIEW 1 rows = p2:I + 0: 89 + 90: 'id-90' + 90: subview 'sv' + VIEW 1 rows = p2:I + 0: 90 + 91: 'id-91' + 91: subview 'sv' + VIEW 1 rows = p2:I + 0: 91 + 92: 'id-92' + 92: subview 'sv' + VIEW 1 rows = p2:I + 0: 92 + 93: 'id-93' + 93: subview 'sv' + VIEW 1 rows = p2:I + 0: 93 + 94: 'id-94' + 94: subview 'sv' + VIEW 1 rows = p2:I + 0: 94 + 95: 'id-95' + 95: subview 'sv' + VIEW 1 rows = p2:I + 0: 95 + 96: 'id-96' + 96: subview 'sv' + VIEW 1 rows = p2:I + 0: 96 + 97: 'id-97' + 97: subview 'sv' + VIEW 1 rows = p2:I + 0: 97 + 98: 'id-98' + 98: subview 'sv' + VIEW 1 rows = p2:I + 0: 98 + 99: 'id-99' + 99: subview 'sv' + VIEW 1 rows = p2:I + 0: 99 + 100: 'id-100' + 100: subview 'sv' + VIEW 1 rows = p2:I + 0: 100 + 101: 'id-101' + 101: subview 'sv' + VIEW 1 rows = p2:I + 0: 101 + 102: 'id-102' + 102: subview 'sv' + VIEW 1 rows = p2:I + 0: 102 + 103: 'id-103' + 103: subview 'sv' + VIEW 1 rows = p2:I + 0: 103 + 104: 'id-104' + 104: subview 'sv' + VIEW 1 rows = p2:I + 0: 104 + 105: 'id-105' + 105: subview 'sv' + VIEW 1 rows = p2:I + 0: 105 + 106: 'id-106' + 106: subview 'sv' + VIEW 1 rows = p2:I + 0: 106 + 107: 'id-107' + 107: subview 'sv' + VIEW 1 rows = p2:I + 0: 107 + 108: 'id-108' + 108: subview 'sv' + VIEW 1 rows = p2:I + 0: 108 + 109: 'id-109' + 109: subview 'sv' + VIEW 1 rows = p2:I + 0: 109 + 110: 'id-110' + 110: subview 'sv' + VIEW 1 rows = p2:I + 0: 110 + 111: 'id-111' + 111: subview 'sv' + VIEW 1 rows = p2:I + 0: 111 + 112: 'id-112' + 112: subview 'sv' + VIEW 1 rows = p2:I + 0: 112 + 113: 'id-113' + 113: subview 'sv' + VIEW 1 rows = p2:I + 0: 113 + 114: 'id-114' + 114: subview 'sv' + VIEW 1 rows = p2:I + 0: 114 + 115: 'id-115' + 115: subview 'sv' + VIEW 1 rows = p2:I + 0: 115 + 116: 'id-116' + 116: subview 'sv' + VIEW 1 rows = p2:I + 0: 116 + 117: 'id-117' + 117: subview 'sv' + VIEW 1 rows = p2:I + 0: 117 + 118: 'id-118' + 118: subview 'sv' + VIEW 1 rows = p2:I + 0: 118 + 119: 'id-119' + 119: subview 'sv' + VIEW 1 rows = p2:I + 0: 119 + 120: 'id-120' + 120: subview 'sv' + VIEW 1 rows = p2:I + 0: 120 + 121: 'id-121' + 121: subview 'sv' + VIEW 1 rows = p2:I + 0: 121 + 122: 'id-122' + 122: subview 'sv' + VIEW 1 rows = p2:I + 0: 122 + 123: 'id-123' + 123: subview 'sv' + VIEW 1 rows = p2:I + 0: 123 + 124: 'id-124' + 124: subview 'sv' + VIEW 1 rows = p2:I + 0: 124 + 125: 'id-125' + 125: subview 'sv' + VIEW 1 rows = p2:I + 0: 125 + 126: 'id-126' + 126: subview 'sv' + VIEW 1 rows = p2:I + 0: 126 + 127: 'id-127' + 127: subview 'sv' + VIEW 1 rows = p2:I + 0: 127 + 128: 'id-128' + 128: subview 'sv' + VIEW 1 rows = p2:I + 0: 128 + 129: 'id-129' + 129: subview 'sv' + VIEW 1 rows = p2:I + 0: 129 + 130: 'id-130' + 130: subview 'sv' + VIEW 1 rows = p2:I + 0: 130 + 131: 'id-131' + 131: subview 'sv' + VIEW 1 rows = p2:I + 0: 131 + 132: 'id-132' + 132: subview 'sv' + VIEW 1 rows = p2:I + 0: 132 + 133: 'id-133' + 133: subview 'sv' + VIEW 1 rows = p2:I + 0: 133 + 134: 'id-134' + 134: subview 'sv' + VIEW 1 rows = p2:I + 0: 134 + 135: 'id-135' + 135: subview 'sv' + VIEW 1 rows = p2:I + 0: 135 + 136: 'id-136' + 136: subview 'sv' + VIEW 1 rows = p2:I + 0: 136 + 137: 'id-137' + 137: subview 'sv' + VIEW 1 rows = p2:I + 0: 137 + 138: 'id-138' + 138: subview 'sv' + VIEW 1 rows = p2:I + 0: 138 + 139: 'id-139' + 139: subview 'sv' + VIEW 1 rows = p2:I + 0: 139 + 140: 'id-140' + 140: subview 'sv' + VIEW 1 rows = p2:I + 0: 140 + 141: 'id-141' + 141: subview 'sv' + VIEW 1 rows = p2:I + 0: 141 + 142: 'id-142' + 142: subview 'sv' + VIEW 1 rows = p2:I + 0: 142 + 143: 'id-143' + 143: subview 'sv' + VIEW 1 rows = p2:I + 0: 143 + 144: 'id-144' + 144: subview 'sv' + VIEW 1 rows = p2:I + 0: 144 + 145: 'id-145' + 145: subview 'sv' + VIEW 1 rows = p2:I + 0: 145 + 146: 'id-146' + 146: subview 'sv' + VIEW 1 rows = p2:I + 0: 146 + 147: 'id-147' + 147: subview 'sv' + VIEW 1 rows = p2:I + 0: 147 + 148: 'id-148' + 148: subview 'sv' + VIEW 1 rows = p2:I + 0: 148 + 149: 'id-149' + 149: subview 'sv' + VIEW 1 rows = p2:I + 0: 149 + 150: 'id-150' + 150: subview 'sv' + VIEW 1 rows = p2:I + 0: 150 + 151: 'id-151' + 151: subview 'sv' + VIEW 1 rows = p2:I + 0: 151 + 152: 'id-152' + 152: subview 'sv' + VIEW 1 rows = p2:I + 0: 152 + 153: 'id-153' + 153: subview 'sv' + VIEW 1 rows = p2:I + 0: 153 + 154: 'id-154' + 154: subview 'sv' + VIEW 1 rows = p2:I + 0: 154 + 155: 'id-155' + 155: subview 'sv' + VIEW 1 rows = p2:I + 0: 155 + 156: 'id-156' + 156: subview 'sv' + VIEW 1 rows = p2:I + 0: 156 + 157: 'id-157' + 157: subview 'sv' + VIEW 1 rows = p2:I + 0: 157 + 158: 'id-158' + 158: subview 'sv' + VIEW 1 rows = p2:I + 0: 158 + 159: 'id-159' + 159: subview 'sv' + VIEW 1 rows = p2:I + 0: 159 + 160: 'id-160' + 160: subview 'sv' + VIEW 1 rows = p2:I + 0: 160 + 161: 'id-161' + 161: subview 'sv' + VIEW 1 rows = p2:I + 0: 161 + 162: 'id-162' + 162: subview 'sv' + VIEW 1 rows = p2:I + 0: 162 + 163: 'id-163' + 163: subview 'sv' + VIEW 1 rows = p2:I + 0: 163 + 164: 'id-164' + 164: subview 'sv' + VIEW 1 rows = p2:I + 0: 164 + 165: 'id-165' + 165: subview 'sv' + VIEW 1 rows = p2:I + 0: 165 + 166: 'id-166' + 166: subview 'sv' + VIEW 1 rows = p2:I + 0: 166 + 167: 'id-167' + 167: subview 'sv' + VIEW 1 rows = p2:I + 0: 167 + 168: 'id-168' + 168: subview 'sv' + VIEW 1 rows = p2:I + 0: 168 + 169: 'id-169' + 169: subview 'sv' + VIEW 1 rows = p2:I + 0: 169 + 170: 'id-170' + 170: subview 'sv' + VIEW 1 rows = p2:I + 0: 170 + 171: 'id-171' + 171: subview 'sv' + VIEW 1 rows = p2:I + 0: 171 + 172: 'id-172' + 172: subview 'sv' + VIEW 1 rows = p2:I + 0: 172 + 173: 'id-173' + 173: subview 'sv' + VIEW 1 rows = p2:I + 0: 173 + 174: 'id-174' + 174: subview 'sv' + VIEW 1 rows = p2:I + 0: 174 + 175: 'id-175' + 175: subview 'sv' + VIEW 1 rows = p2:I + 0: 175 + 176: 'id-176' + 176: subview 'sv' + VIEW 1 rows = p2:I + 0: 176 + 177: 'id-177' + 177: subview 'sv' + VIEW 1 rows = p2:I + 0: 177 + 178: 'id-178' + 178: subview 'sv' + VIEW 1 rows = p2:I + 0: 178 + 179: 'id-179' + 179: subview 'sv' + VIEW 1 rows = p2:I + 0: 179 + 180: 'id-180' + 180: subview 'sv' + VIEW 1 rows = p2:I + 0: 180 + 181: 'id-181' + 181: subview 'sv' + VIEW 1 rows = p2:I + 0: 181 + 182: 'id-182' + 182: subview 'sv' + VIEW 1 rows = p2:I + 0: 182 + 183: 'id-183' + 183: subview 'sv' + VIEW 1 rows = p2:I + 0: 183 + 184: 'id-184' + 184: subview 'sv' + VIEW 1 rows = p2:I + 0: 184 + 185: 'id-185' + 185: subview 'sv' + VIEW 1 rows = p2:I + 0: 185 + 186: 'id-186' + 186: subview 'sv' + VIEW 1 rows = p2:I + 0: 186 + 187: 'id-187' + 187: subview 'sv' + VIEW 1 rows = p2:I + 0: 187 + 188: 'id-188' + 188: subview 'sv' + VIEW 1 rows = p2:I + 0: 188 + 189: 'id-189' + 189: subview 'sv' + VIEW 1 rows = p2:I + 0: 189 + 190: 'id-190' + 190: subview 'sv' + VIEW 1 rows = p2:I + 0: 190 + 191: 'id-191' + 191: subview 'sv' + VIEW 1 rows = p2:I + 0: 191 + 192: 'id-192' + 192: subview 'sv' + VIEW 1 rows = p2:I + 0: 192 + 193: 'id-193' + 193: subview 'sv' + VIEW 1 rows = p2:I + 0: 193 + 194: 'id-194' + 194: subview 'sv' + VIEW 1 rows = p2:I + 0: 194 + 195: 'id-195' + 195: subview 'sv' + VIEW 1 rows = p2:I + 0: 195 + 196: 'id-196' + 196: subview 'sv' + VIEW 1 rows = p2:I + 0: 196 + 197: 'id-197' + 197: subview 'sv' + VIEW 1 rows = p2:I + 0: 197 + 198: 'id-198' + 198: subview 'sv' + VIEW 1 rows = p2:I + 0: 198 + 199: 'id-199' + 199: subview 'sv' + VIEW 1 rows = p2:I + 0: 199 + 200: 'id-200' + 200: subview 'sv' + VIEW 1 rows = p2:I + 0: 200 + 201: 'id-201' + 201: subview 'sv' + VIEW 1 rows = p2:I + 0: 201 + 202: 'id-202' + 202: subview 'sv' + VIEW 1 rows = p2:I + 0: 202 + 203: 'id-203' + 203: subview 'sv' + VIEW 1 rows = p2:I + 0: 203 + 204: 'id-204' + 204: subview 'sv' + VIEW 1 rows = p2:I + 0: 204 + 205: 'id-205' + 205: subview 'sv' + VIEW 1 rows = p2:I + 0: 205 + 206: 'id-206' + 206: subview 'sv' + VIEW 1 rows = p2:I + 0: 206 + 207: 'id-207' + 207: subview 'sv' + VIEW 1 rows = p2:I + 0: 207 + 208: 'id-208' + 208: subview 'sv' + VIEW 1 rows = p2:I + 0: 208 + 209: 'id-209' + 209: subview 'sv' + VIEW 1 rows = p2:I + 0: 209 + 210: 'id-210' + 210: subview 'sv' + VIEW 1 rows = p2:I + 0: 210 + 211: 'id-211' + 211: subview 'sv' + VIEW 1 rows = p2:I + 0: 211 + 212: 'id-212' + 212: subview 'sv' + VIEW 1 rows = p2:I + 0: 212 + 213: 'id-213' + 213: subview 'sv' + VIEW 1 rows = p2:I + 0: 213 + 214: 'id-214' + 214: subview 'sv' + VIEW 1 rows = p2:I + 0: 214 + 215: 'id-215' + 215: subview 'sv' + VIEW 1 rows = p2:I + 0: 215 + 216: 'id-216' + 216: subview 'sv' + VIEW 1 rows = p2:I + 0: 216 + 217: 'id-217' + 217: subview 'sv' + VIEW 1 rows = p2:I + 0: 217 + 218: 'id-218' + 218: subview 'sv' + VIEW 1 rows = p2:I + 0: 218 + 219: 'id-219' + 219: subview 'sv' + VIEW 1 rows = p2:I + 0: 219 + 220: 'id-220' + 220: subview 'sv' + VIEW 1 rows = p2:I + 0: 220 + 221: 'id-221' + 221: subview 'sv' + VIEW 1 rows = p2:I + 0: 221 + 222: 'id-222' + 222: subview 'sv' + VIEW 1 rows = p2:I + 0: 222 + 223: 'id-223' + 223: subview 'sv' + VIEW 1 rows = p2:I + 0: 223 + 224: 'id-224' + 224: subview 'sv' + VIEW 1 rows = p2:I + 0: 224 + 225: 'id-225' + 225: subview 'sv' + VIEW 1 rows = p2:I + 0: 225 + 226: 'id-226' + 226: subview 'sv' + VIEW 1 rows = p2:I + 0: 226 + 227: 'id-227' + 227: subview 'sv' + VIEW 1 rows = p2:I + 0: 227 + 228: 'id-228' + 228: subview 'sv' + VIEW 1 rows = p2:I + 0: 228 + 229: 'id-229' + 229: subview 'sv' + VIEW 1 rows = p2:I + 0: 229 + 230: 'id-230' + 230: subview 'sv' + VIEW 1 rows = p2:I + 0: 230 + 231: 'id-231' + 231: subview 'sv' + VIEW 1 rows = p2:I + 0: 231 + 232: 'id-232' + 232: subview 'sv' + VIEW 1 rows = p2:I + 0: 232 + 233: 'id-233' + 233: subview 'sv' + VIEW 1 rows = p2:I + 0: 233 + 234: 'id-234' + 234: subview 'sv' + VIEW 1 rows = p2:I + 0: 234 + 235: 'id-235' + 235: subview 'sv' + VIEW 1 rows = p2:I + 0: 235 + 236: 'id-236' + 236: subview 'sv' + VIEW 1 rows = p2:I + 0: 236 + 237: 'id-237' + 237: subview 'sv' + VIEW 1 rows = p2:I + 0: 237 + 238: 'id-238' + 238: subview 'sv' + VIEW 1 rows = p2:I + 0: 238 + 239: 'id-239' + 239: subview 'sv' + VIEW 1 rows = p2:I + 0: 239 + 240: 'id-240' + 240: subview 'sv' + VIEW 1 rows = p2:I + 0: 240 + 241: 'id-241' + 241: subview 'sv' + VIEW 1 rows = p2:I + 0: 241 + 242: 'id-242' + 242: subview 'sv' + VIEW 1 rows = p2:I + 0: 242 + 243: 'id-243' + 243: subview 'sv' + VIEW 1 rows = p2:I + 0: 243 + 244: 'id-244' + 244: subview 'sv' + VIEW 1 rows = p2:I + 0: 244 + 245: 'id-245' + 245: subview 'sv' + VIEW 1 rows = p2:I + 0: 245 + 246: 'id-246' + 246: subview 'sv' + VIEW 1 rows = p2:I + 0: 246 + 247: 'id-247' + 247: subview 'sv' + VIEW 1 rows = p2:I + 0: 247 + 248: 'id-248' + 248: subview 'sv' + VIEW 1 rows = p2:I + 0: 248 + 249: 'id-249' + 249: subview 'sv' + VIEW 1 rows = p2:I + 0: 249 + 250: 'id-250' + 250: subview 'sv' + VIEW 1 rows = p2:I + 0: 250 + 251: 'id-251' + 251: subview 'sv' + VIEW 1 rows = p2:I + 0: 251 + 252: 'id-252' + 252: subview 'sv' + VIEW 1 rows = p2:I + 0: 252 + 253: 'id-253' + 253: subview 'sv' + VIEW 1 rows = p2:I + 0: 253 + 254: 'id-254' + 254: subview 'sv' + VIEW 1 rows = p2:I + 0: 254 + 255: 'id-255' + 255: subview 'sv' + VIEW 1 rows = p2:I + 0: 255 + 256: 'id-256' + 256: subview 'sv' + VIEW 1 rows = p2:I + 0: 256 + 257: 'id-257' + 257: subview 'sv' + VIEW 1 rows = p2:I + 0: 257 + 258: 'id-258' + 258: subview 'sv' + VIEW 1 rows = p2:I + 0: 258 + 259: 'id-259' + 259: subview 'sv' + VIEW 1 rows = p2:I + 0: 259 + 260: 'id-260' + 260: subview 'sv' + VIEW 1 rows = p2:I + 0: 260 + 261: 'id-261' + 261: subview 'sv' + VIEW 1 rows = p2:I + 0: 261 + 262: 'id-262' + 262: subview 'sv' + VIEW 1 rows = p2:I + 0: 262 + 263: 'id-263' + 263: subview 'sv' + VIEW 1 rows = p2:I + 0: 263 + 264: 'id-264' + 264: subview 'sv' + VIEW 1 rows = p2:I + 0: 264 + 265: 'id-265' + 265: subview 'sv' + VIEW 1 rows = p2:I + 0: 265 + 266: 'id-266' + 266: subview 'sv' + VIEW 1 rows = p2:I + 0: 266 + 267: 'id-267' + 267: subview 'sv' + VIEW 1 rows = p2:I + 0: 267 + 268: 'id-268' + 268: subview 'sv' + VIEW 1 rows = p2:I + 0: 268 + 269: 'id-269' + 269: subview 'sv' + VIEW 1 rows = p2:I + 0: 269 + 270: 'id-270' + 270: subview 'sv' + VIEW 1 rows = p2:I + 0: 270 + 271: 'id-271' + 271: subview 'sv' + VIEW 1 rows = p2:I + 0: 271 + 272: 'id-272' + 272: subview 'sv' + VIEW 1 rows = p2:I + 0: 272 + 273: 'id-273' + 273: subview 'sv' + VIEW 1 rows = p2:I + 0: 273 + 274: 'id-274' + 274: subview 'sv' + VIEW 1 rows = p2:I + 0: 274 + 275: 'id-275' + 275: subview 'sv' + VIEW 1 rows = p2:I + 0: 275 + 276: 'id-276' + 276: subview 'sv' + VIEW 1 rows = p2:I + 0: 276 + 277: 'id-277' + 277: subview 'sv' + VIEW 1 rows = p2:I + 0: 277 + 278: 'id-278' + 278: subview 'sv' + VIEW 1 rows = p2:I + 0: 278 + 279: 'id-279' + 279: subview 'sv' + VIEW 1 rows = p2:I + 0: 279 + 280: 'id-280' + 280: subview 'sv' + VIEW 1 rows = p2:I + 0: 280 + 281: 'id-281' + 281: subview 'sv' + VIEW 1 rows = p2:I + 0: 281 + 282: 'id-282' + 282: subview 'sv' + VIEW 1 rows = p2:I + 0: 282 + 283: 'id-283' + 283: subview 'sv' + VIEW 1 rows = p2:I + 0: 283 + 284: 'id-284' + 284: subview 'sv' + VIEW 1 rows = p2:I + 0: 284 + 285: 'id-285' + 285: subview 'sv' + VIEW 1 rows = p2:I + 0: 285 + 286: 'id-286' + 286: subview 'sv' + VIEW 1 rows = p2:I + 0: 286 + 287: 'id-287' + 287: subview 'sv' + VIEW 1 rows = p2:I + 0: 287 + 288: 'id-288' + 288: subview 'sv' + VIEW 1 rows = p2:I + 0: 288 + 289: 'id-289' + 289: subview 'sv' + VIEW 1 rows = p2:I + 0: 289 + 290: 'id-290' + 290: subview 'sv' + VIEW 1 rows = p2:I + 0: 290 + 291: 'id-291' + 291: subview 'sv' + VIEW 1 rows = p2:I + 0: 291 + 292: 'id-292' + 292: subview 'sv' + VIEW 1 rows = p2:I + 0: 292 + 293: 'id-293' + 293: subview 'sv' + VIEW 1 rows = p2:I + 0: 293 + 294: 'id-294' + 294: subview 'sv' + VIEW 1 rows = p2:I + 0: 294 + 295: 'id-295' + 295: subview 'sv' + VIEW 1 rows = p2:I + 0: 295 + 296: 'id-296' + 296: subview 'sv' + VIEW 1 rows = p2:I + 0: 296 + 297: 'id-297' + 297: subview 'sv' + VIEW 1 rows = p2:I + 0: 297 + 298: 'id-298' + 298: subview 'sv' + VIEW 1 rows = p2:I + 0: 298 + 299: 'id-299' + 299: subview 'sv' + VIEW 1 rows = p2:I + 0: 299 + 300: 'id-300' + 300: subview 'sv' + VIEW 1 rows = p2:I + 0: 300 + 301: 'id-301' + 301: subview 'sv' + VIEW 1 rows = p2:I + 0: 301 + 302: 'id-302' + 302: subview 'sv' + VIEW 1 rows = p2:I + 0: 302 + 303: 'id-303' + 303: subview 'sv' + VIEW 1 rows = p2:I + 0: 303 + 304: 'id-304' + 304: subview 'sv' + VIEW 1 rows = p2:I + 0: 304 + 305: 'id-305' + 305: subview 'sv' + VIEW 1 rows = p2:I + 0: 305 + 306: 'id-306' + 306: subview 'sv' + VIEW 1 rows = p2:I + 0: 306 + 307: 'id-307' + 307: subview 'sv' + VIEW 1 rows = p2:I + 0: 307 + 308: 'id-308' + 308: subview 'sv' + VIEW 1 rows = p2:I + 0: 308 + 309: 'id-309' + 309: subview 'sv' + VIEW 1 rows = p2:I + 0: 309 + 310: 'id-310' + 310: subview 'sv' + VIEW 1 rows = p2:I + 0: 310 + 311: 'id-311' + 311: subview 'sv' + VIEW 1 rows = p2:I + 0: 311 + 312: 'id-312' + 312: subview 'sv' + VIEW 1 rows = p2:I + 0: 312 + 313: 'id-313' + 313: subview 'sv' + VIEW 1 rows = p2:I + 0: 313 + 314: 'id-314' + 314: subview 'sv' + VIEW 1 rows = p2:I + 0: 314 + 315: 'id-315' + 315: subview 'sv' + VIEW 1 rows = p2:I + 0: 315 + 316: 'id-316' + 316: subview 'sv' + VIEW 1 rows = p2:I + 0: 316 + 317: 'id-317' + 317: subview 'sv' + VIEW 1 rows = p2:I + 0: 317 + 318: 'id-318' + 318: subview 'sv' + VIEW 1 rows = p2:I + 0: 318 + 319: 'id-319' + 319: subview 'sv' + VIEW 1 rows = p2:I + 0: 319 + 320: 'id-320' + 320: subview 'sv' + VIEW 1 rows = p2:I + 0: 320 + 321: 'id-321' + 321: subview 'sv' + VIEW 1 rows = p2:I + 0: 321 + 322: 'id-322' + 322: subview 'sv' + VIEW 1 rows = p2:I + 0: 322 + 323: 'id-323' + 323: subview 'sv' + VIEW 1 rows = p2:I + 0: 323 + 324: 'id-324' + 324: subview 'sv' + VIEW 1 rows = p2:I + 0: 324 + 325: 'id-325' + 325: subview 'sv' + VIEW 1 rows = p2:I + 0: 325 + 326: 'id-326' + 326: subview 'sv' + VIEW 1 rows = p2:I + 0: 326 + 327: 'id-327' + 327: subview 'sv' + VIEW 1 rows = p2:I + 0: 327 + 328: 'id-328' + 328: subview 'sv' + VIEW 1 rows = p2:I + 0: 328 + 329: 'id-329' + 329: subview 'sv' + VIEW 1 rows = p2:I + 0: 329 + 330: 'id-330' + 330: subview 'sv' + VIEW 1 rows = p2:I + 0: 330 + 331: 'id-331' + 331: subview 'sv' + VIEW 1 rows = p2:I + 0: 331 + 332: 'id-332' + 332: subview 'sv' + VIEW 1 rows = p2:I + 0: 332 + 333: 'id-333' + 333: subview 'sv' + VIEW 1 rows = p2:I + 0: 333 + 334: 'id-334' + 334: subview 'sv' + VIEW 1 rows = p2:I + 0: 334 + 335: 'id-335' + 335: subview 'sv' + VIEW 1 rows = p2:I + 0: 335 + 336: 'id-336' + 336: subview 'sv' + VIEW 1 rows = p2:I + 0: 336 + 337: 'id-337' + 337: subview 'sv' + VIEW 1 rows = p2:I + 0: 337 + 338: 'id-338' + 338: subview 'sv' + VIEW 1 rows = p2:I + 0: 338 + 339: 'id-339' + 339: subview 'sv' + VIEW 1 rows = p2:I + 0: 339 + 340: 'id-340' + 340: subview 'sv' + VIEW 1 rows = p2:I + 0: 340 + 341: 'id-341' + 341: subview 'sv' + VIEW 1 rows = p2:I + 0: 341 + 342: 'id-342' + 342: subview 'sv' + VIEW 1 rows = p2:I + 0: 342 + 343: 'id-343' + 343: subview 'sv' + VIEW 1 rows = p2:I + 0: 343 + 344: 'id-344' + 344: subview 'sv' + VIEW 1 rows = p2:I + 0: 344 + 345: 'id-345' + 345: subview 'sv' + VIEW 1 rows = p2:I + 0: 345 + 346: 'id-346' + 346: subview 'sv' + VIEW 1 rows = p2:I + 0: 346 + 347: 'id-347' + 347: subview 'sv' + VIEW 1 rows = p2:I + 0: 347 + 348: 'id-348' + 348: subview 'sv' + VIEW 1 rows = p2:I + 0: 348 + 349: 'id-349' + 349: subview 'sv' + VIEW 1 rows = p2:I + 0: 349 + 350: 'id-350' + 350: subview 'sv' + VIEW 1 rows = p2:I + 0: 350 + 351: 'id-351' + 351: subview 'sv' + VIEW 1 rows = p2:I + 0: 351 + 352: 'id-352' + 352: subview 'sv' + VIEW 1 rows = p2:I + 0: 352 + 353: 'id-353' + 353: subview 'sv' + VIEW 1 rows = p2:I + 0: 353 + 354: 'id-354' + 354: subview 'sv' + VIEW 1 rows = p2:I + 0: 354 + 355: 'id-355' + 355: subview 'sv' + VIEW 1 rows = p2:I + 0: 355 + 356: 'id-356' + 356: subview 'sv' + VIEW 1 rows = p2:I + 0: 356 + 357: 'id-357' + 357: subview 'sv' + VIEW 1 rows = p2:I + 0: 357 + 358: 'id-358' + 358: subview 'sv' + VIEW 1 rows = p2:I + 0: 358 + 359: 'id-359' + 359: subview 'sv' + VIEW 1 rows = p2:I + 0: 359 + 360: 'id-360' + 360: subview 'sv' + VIEW 1 rows = p2:I + 0: 360 + 361: 'id-361' + 361: subview 'sv' + VIEW 1 rows = p2:I + 0: 361 + 362: 'id-362' + 362: subview 'sv' + VIEW 1 rows = p2:I + 0: 362 + 363: 'id-363' + 363: subview 'sv' + VIEW 1 rows = p2:I + 0: 363 + 364: 'id-364' + 364: subview 'sv' + VIEW 1 rows = p2:I + 0: 364 + 365: 'id-365' + 365: subview 'sv' + VIEW 1 rows = p2:I + 0: 365 + 366: 'id-366' + 366: subview 'sv' + VIEW 1 rows = p2:I + 0: 366 + 367: 'id-367' + 367: subview 'sv' + VIEW 1 rows = p2:I + 0: 367 + 368: 'id-368' + 368: subview 'sv' + VIEW 1 rows = p2:I + 0: 368 + 369: 'id-369' + 369: subview 'sv' + VIEW 1 rows = p2:I + 0: 369 + 370: 'id-370' + 370: subview 'sv' + VIEW 1 rows = p2:I + 0: 370 + 371: 'id-371' + 371: subview 'sv' + VIEW 1 rows = p2:I + 0: 371 + 372: 'id-372' + 372: subview 'sv' + VIEW 1 rows = p2:I + 0: 372 + 373: 'id-373' + 373: subview 'sv' + VIEW 1 rows = p2:I + 0: 373 + 374: 'id-374' + 374: subview 'sv' + VIEW 1 rows = p2:I + 0: 374 + 375: 'id-375' + 375: subview 'sv' + VIEW 1 rows = p2:I + 0: 375 + 376: 'id-376' + 376: subview 'sv' + VIEW 1 rows = p2:I + 0: 376 + 377: 'id-377' + 377: subview 'sv' + VIEW 1 rows = p2:I + 0: 377 + 378: 'id-378' + 378: subview 'sv' + VIEW 1 rows = p2:I + 0: 378 + 379: 'id-379' + 379: subview 'sv' + VIEW 1 rows = p2:I + 0: 379 + 380: 'id-380' + 380: subview 'sv' + VIEW 1 rows = p2:I + 0: 380 + 381: 'id-381' + 381: subview 'sv' + VIEW 1 rows = p2:I + 0: 381 + 382: 'id-382' + 382: subview 'sv' + VIEW 1 rows = p2:I + 0: 382 + 383: 'id-383' + 383: subview 'sv' + VIEW 1 rows = p2:I + 0: 383 + 384: 'id-384' + 384: subview 'sv' + VIEW 1 rows = p2:I + 0: 384 + 385: 'id-385' + 385: subview 'sv' + VIEW 1 rows = p2:I + 0: 385 + 386: 'id-386' + 386: subview 'sv' + VIEW 1 rows = p2:I + 0: 386 + 387: 'id-387' + 387: subview 'sv' + VIEW 1 rows = p2:I + 0: 387 + 388: 'id-388' + 388: subview 'sv' + VIEW 1 rows = p2:I + 0: 388 + 389: 'id-389' + 389: subview 'sv' + VIEW 1 rows = p2:I + 0: 389 + 390: 'id-390' + 390: subview 'sv' + VIEW 1 rows = p2:I + 0: 390 + 391: 'id-391' + 391: subview 'sv' + VIEW 1 rows = p2:I + 0: 391 + 392: 'id-392' + 392: subview 'sv' + VIEW 1 rows = p2:I + 0: 392 + 393: 'id-393' + 393: subview 'sv' + VIEW 1 rows = p2:I + 0: 393 + 394: 'id-394' + 394: subview 'sv' + VIEW 1 rows = p2:I + 0: 394 + 395: 'id-395' + 395: subview 'sv' + VIEW 1 rows = p2:I + 0: 395 + 396: 'id-396' + 396: subview 'sv' + VIEW 1 rows = p2:I + 0: 396 + 397: 'id-397' + 397: subview 'sv' + VIEW 1 rows = p2:I + 0: 397 + 398: 'id-398' + 398: subview 'sv' + VIEW 1 rows = p2:I + 0: 398 + 399: 'id-399' + 399: subview 'sv' + VIEW 1 rows = p2:I + 0: 399 + 400: 'id-400' + 400: subview 'sv' + VIEW 1 rows = p2:I + 0: 400 + 401: 'id-401' + 401: subview 'sv' + VIEW 1 rows = p2:I + 0: 401 + 402: 'id-402' + 402: subview 'sv' + VIEW 1 rows = p2:I + 0: 402 + 403: 'id-403' + 403: subview 'sv' + VIEW 1 rows = p2:I + 0: 403 + 404: 'id-404' + 404: subview 'sv' + VIEW 1 rows = p2:I + 0: 404 + 405: 'id-405' + 405: subview 'sv' + VIEW 1 rows = p2:I + 0: 405 + 406: 'id-406' + 406: subview 'sv' + VIEW 1 rows = p2:I + 0: 406 + 407: 'id-407' + 407: subview 'sv' + VIEW 1 rows = p2:I + 0: 407 + 408: 'id-408' + 408: subview 'sv' + VIEW 1 rows = p2:I + 0: 408 + 409: 'id-409' + 409: subview 'sv' + VIEW 1 rows = p2:I + 0: 409 + 410: 'id-410' + 410: subview 'sv' + VIEW 1 rows = p2:I + 0: 410 + 411: 'id-411' + 411: subview 'sv' + VIEW 1 rows = p2:I + 0: 411 + 412: 'id-412' + 412: subview 'sv' + VIEW 1 rows = p2:I + 0: 412 + 413: 'id-413' + 413: subview 'sv' + VIEW 1 rows = p2:I + 0: 413 + 414: 'id-414' + 414: subview 'sv' + VIEW 1 rows = p2:I + 0: 414 + 415: 'id-415' + 415: subview 'sv' + VIEW 1 rows = p2:I + 0: 415 + 416: 'id-416' + 416: subview 'sv' + VIEW 1 rows = p2:I + 0: 416 + 417: 'id-417' + 417: subview 'sv' + VIEW 1 rows = p2:I + 0: 417 + 418: 'id-418' + 418: subview 'sv' + VIEW 1 rows = p2:I + 0: 418 + 419: 'id-419' + 419: subview 'sv' + VIEW 1 rows = p2:I + 0: 419 + 420: 'id-420' + 420: subview 'sv' + VIEW 1 rows = p2:I + 0: 420 + 421: 'id-421' + 421: subview 'sv' + VIEW 1 rows = p2:I + 0: 421 + 422: 'id-422' + 422: subview 'sv' + VIEW 1 rows = p2:I + 0: 422 + 423: 'id-423' + 423: subview 'sv' + VIEW 1 rows = p2:I + 0: 423 + 424: 'id-424' + 424: subview 'sv' + VIEW 1 rows = p2:I + 0: 424 + 425: 'id-425' + 425: subview 'sv' + VIEW 1 rows = p2:I + 0: 425 + 426: 'id-426' + 426: subview 'sv' + VIEW 1 rows = p2:I + 0: 426 + 427: 'id-427' + 427: subview 'sv' + VIEW 1 rows = p2:I + 0: 427 + 428: 'id-428' + 428: subview 'sv' + VIEW 1 rows = p2:I + 0: 428 + 429: 'id-429' + 429: subview 'sv' + VIEW 1 rows = p2:I + 0: 429 + 430: 'id-430' + 430: subview 'sv' + VIEW 1 rows = p2:I + 0: 430 + 431: 'id-431' + 431: subview 'sv' + VIEW 1 rows = p2:I + 0: 431 + 432: 'id-432' + 432: subview 'sv' + VIEW 1 rows = p2:I + 0: 432 + 433: 'id-433' + 433: subview 'sv' + VIEW 1 rows = p2:I + 0: 433 + 434: 'id-434' + 434: subview 'sv' + VIEW 1 rows = p2:I + 0: 434 + 435: 'id-435' + 435: subview 'sv' + VIEW 1 rows = p2:I + 0: 435 + 436: 'id-436' + 436: subview 'sv' + VIEW 1 rows = p2:I + 0: 436 + 437: 'id-437' + 437: subview 'sv' + VIEW 1 rows = p2:I + 0: 437 + 438: 'id-438' + 438: subview 'sv' + VIEW 1 rows = p2:I + 0: 438 + 439: 'id-439' + 439: subview 'sv' + VIEW 1 rows = p2:I + 0: 439 + 440: 'id-440' + 440: subview 'sv' + VIEW 1 rows = p2:I + 0: 440 + 441: 'id-441' + 441: subview 'sv' + VIEW 1 rows = p2:I + 0: 441 + 442: 'id-442' + 442: subview 'sv' + VIEW 1 rows = p2:I + 0: 442 + 443: 'id-443' + 443: subview 'sv' + VIEW 1 rows = p2:I + 0: 443 + 444: 'id-444' + 444: subview 'sv' + VIEW 1 rows = p2:I + 0: 444 + 445: 'id-445' + 445: subview 'sv' + VIEW 1 rows = p2:I + 0: 445 + 446: 'id-446' + 446: subview 'sv' + VIEW 1 rows = p2:I + 0: 446 + 447: 'id-447' + 447: subview 'sv' + VIEW 1 rows = p2:I + 0: 447 + 448: 'id-448' + 448: subview 'sv' + VIEW 1 rows = p2:I + 0: 448 + 449: 'id-449' + 449: subview 'sv' + VIEW 1 rows = p2:I + 0: 449 + 450: 'id-450' + 450: subview 'sv' + VIEW 1 rows = p2:I + 0: 450 + 451: 'id-451' + 451: subview 'sv' + VIEW 1 rows = p2:I + 0: 451 + 452: 'id-452' + 452: subview 'sv' + VIEW 1 rows = p2:I + 0: 452 + 453: 'id-453' + 453: subview 'sv' + VIEW 1 rows = p2:I + 0: 453 + 454: 'id-454' + 454: subview 'sv' + VIEW 1 rows = p2:I + 0: 454 + 455: 'id-455' + 455: subview 'sv' + VIEW 1 rows = p2:I + 0: 455 + 456: 'id-456' + 456: subview 'sv' + VIEW 1 rows = p2:I + 0: 456 + 457: 'id-457' + 457: subview 'sv' + VIEW 1 rows = p2:I + 0: 457 + 458: 'id-458' + 458: subview 'sv' + VIEW 1 rows = p2:I + 0: 458 + 459: 'id-459' + 459: subview 'sv' + VIEW 1 rows = p2:I + 0: 459 + 460: 'id-460' + 460: subview 'sv' + VIEW 1 rows = p2:I + 0: 460 + 461: 'id-461' + 461: subview 'sv' + VIEW 1 rows = p2:I + 0: 461 + 462: 'id-462' + 462: subview 'sv' + VIEW 1 rows = p2:I + 0: 462 + 463: 'id-463' + 463: subview 'sv' + VIEW 1 rows = p2:I + 0: 463 + 464: 'id-464' + 464: subview 'sv' + VIEW 1 rows = p2:I + 0: 464 + 465: 'id-465' + 465: subview 'sv' + VIEW 1 rows = p2:I + 0: 465 + 466: 'id-466' + 466: subview 'sv' + VIEW 1 rows = p2:I + 0: 466 + 467: 'id-467' + 467: subview 'sv' + VIEW 1 rows = p2:I + 0: 467 + 468: 'id-468' + 468: subview 'sv' + VIEW 1 rows = p2:I + 0: 468 + 469: 'id-469' + 469: subview 'sv' + VIEW 1 rows = p2:I + 0: 469 + 470: 'id-470' + 470: subview 'sv' + VIEW 1 rows = p2:I + 0: 470 + 471: 'id-471' + 471: subview 'sv' + VIEW 1 rows = p2:I + 0: 471 + 472: 'id-472' + 472: subview 'sv' + VIEW 1 rows = p2:I + 0: 472 + 473: 'id-473' + 473: subview 'sv' + VIEW 1 rows = p2:I + 0: 473 + 474: 'id-474' + 474: subview 'sv' + VIEW 1 rows = p2:I + 0: 474 + 475: 'id-475' + 475: subview 'sv' + VIEW 1 rows = p2:I + 0: 475 + 476: 'id-476' + 476: subview 'sv' + VIEW 1 rows = p2:I + 0: 476 + 477: 'id-477' + 477: subview 'sv' + VIEW 1 rows = p2:I + 0: 477 + 478: 'id-478' + 478: subview 'sv' + VIEW 1 rows = p2:I + 0: 478 + 479: 'id-479' + 479: subview 'sv' + VIEW 1 rows = p2:I + 0: 479 + 480: 'id-480' + 480: subview 'sv' + VIEW 1 rows = p2:I + 0: 480 + 481: 'id-481' + 481: subview 'sv' + VIEW 1 rows = p2:I + 0: 481 + 482: 'id-482' + 482: subview 'sv' + VIEW 1 rows = p2:I + 0: 482 + 483: 'id-483' + 483: subview 'sv' + VIEW 1 rows = p2:I + 0: 483 + 484: 'id-484' + 484: subview 'sv' + VIEW 1 rows = p2:I + 0: 484 + 485: 'id-485' + 485: subview 'sv' + VIEW 1 rows = p2:I + 0: 485 + 486: 'id-486' + 486: subview 'sv' + VIEW 1 rows = p2:I + 0: 486 + 487: 'id-487' + 487: subview 'sv' + VIEW 1 rows = p2:I + 0: 487 + 488: 'id-488' + 488: subview 'sv' + VIEW 1 rows = p2:I + 0: 488 + 489: 'id-489' + 489: subview 'sv' + VIEW 1 rows = p2:I + 0: 489 + 490: 'id-490' + 490: subview 'sv' + VIEW 1 rows = p2:I + 0: 490 + 491: 'id-491' + 491: subview 'sv' + VIEW 1 rows = p2:I + 0: 491 + 492: 'id-492' + 492: subview 'sv' + VIEW 1 rows = p2:I + 0: 492 + 493: 'id-493' + 493: subview 'sv' + VIEW 1 rows = p2:I + 0: 493 + 494: 'id-494' + 494: subview 'sv' + VIEW 1 rows = p2:I + 0: 494 + 495: 'id-495' + 495: subview 'sv' + VIEW 1 rows = p2:I + 0: 495 + 496: 'id-496' + 496: subview 'sv' + VIEW 1 rows = p2:I + 0: 496 + 497: 'id-497' + 497: subview 'sv' + VIEW 1 rows = p2:I + 0: 497 + 498: 'id-498' + 498: subview 'sv' + VIEW 1 rows = p2:I + 0: 498 + 499: 'id-499' + 499: subview 'sv' + VIEW 1 rows = p2:I + 0: 499 + 1: subview '_B' + VIEW 500 rows = p1:S sv:V + 0: 'id-500' + 0: subview 'sv' + VIEW 1 rows = p2:I + 0: 500 + 1: 'id-501' + 1: subview 'sv' + VIEW 1 rows = p2:I + 0: 501 + 2: 'id-502' + 2: subview 'sv' + VIEW 1 rows = p2:I + 0: 502 + 3: 'id-503' + 3: subview 'sv' + VIEW 1 rows = p2:I + 0: 503 + 4: 'id-504' + 4: subview 'sv' + VIEW 1 rows = p2:I + 0: 504 + 5: 'id-505' + 5: subview 'sv' + VIEW 1 rows = p2:I + 0: 505 + 6: 'id-506' + 6: subview 'sv' + VIEW 1 rows = p2:I + 0: 506 + 7: 'id-507' + 7: subview 'sv' + VIEW 1 rows = p2:I + 0: 507 + 8: 'id-508' + 8: subview 'sv' + VIEW 1 rows = p2:I + 0: 508 + 9: 'id-509' + 9: subview 'sv' + VIEW 1 rows = p2:I + 0: 509 + 10: 'id-510' + 10: subview 'sv' + VIEW 1 rows = p2:I + 0: 510 + 11: 'id-511' + 11: subview 'sv' + VIEW 1 rows = p2:I + 0: 511 + 12: 'id-512' + 12: subview 'sv' + VIEW 1 rows = p2:I + 0: 512 + 13: 'id-513' + 13: subview 'sv' + VIEW 1 rows = p2:I + 0: 513 + 14: 'id-514' + 14: subview 'sv' + VIEW 1 rows = p2:I + 0: 514 + 15: 'id-515' + 15: subview 'sv' + VIEW 1 rows = p2:I + 0: 515 + 16: 'id-516' + 16: subview 'sv' + VIEW 1 rows = p2:I + 0: 516 + 17: 'id-517' + 17: subview 'sv' + VIEW 1 rows = p2:I + 0: 517 + 18: 'id-518' + 18: subview 'sv' + VIEW 1 rows = p2:I + 0: 518 + 19: 'id-519' + 19: subview 'sv' + VIEW 1 rows = p2:I + 0: 519 + 20: 'id-520' + 20: subview 'sv' + VIEW 1 rows = p2:I + 0: 520 + 21: 'id-521' + 21: subview 'sv' + VIEW 1 rows = p2:I + 0: 521 + 22: 'id-522' + 22: subview 'sv' + VIEW 1 rows = p2:I + 0: 522 + 23: 'id-523' + 23: subview 'sv' + VIEW 1 rows = p2:I + 0: 523 + 24: 'id-524' + 24: subview 'sv' + VIEW 1 rows = p2:I + 0: 524 + 25: 'id-525' + 25: subview 'sv' + VIEW 1 rows = p2:I + 0: 525 + 26: 'id-526' + 26: subview 'sv' + VIEW 1 rows = p2:I + 0: 526 + 27: 'id-527' + 27: subview 'sv' + VIEW 1 rows = p2:I + 0: 527 + 28: 'id-528' + 28: subview 'sv' + VIEW 1 rows = p2:I + 0: 528 + 29: 'id-529' + 29: subview 'sv' + VIEW 1 rows = p2:I + 0: 529 + 30: 'id-530' + 30: subview 'sv' + VIEW 1 rows = p2:I + 0: 530 + 31: 'id-531' + 31: subview 'sv' + VIEW 1 rows = p2:I + 0: 531 + 32: 'id-532' + 32: subview 'sv' + VIEW 1 rows = p2:I + 0: 532 + 33: 'id-533' + 33: subview 'sv' + VIEW 1 rows = p2:I + 0: 533 + 34: 'id-534' + 34: subview 'sv' + VIEW 1 rows = p2:I + 0: 534 + 35: 'id-535' + 35: subview 'sv' + VIEW 1 rows = p2:I + 0: 535 + 36: 'id-536' + 36: subview 'sv' + VIEW 1 rows = p2:I + 0: 536 + 37: 'id-537' + 37: subview 'sv' + VIEW 1 rows = p2:I + 0: 537 + 38: 'id-538' + 38: subview 'sv' + VIEW 1 rows = p2:I + 0: 538 + 39: 'id-539' + 39: subview 'sv' + VIEW 1 rows = p2:I + 0: 539 + 40: 'id-540' + 40: subview 'sv' + VIEW 1 rows = p2:I + 0: 540 + 41: 'id-541' + 41: subview 'sv' + VIEW 1 rows = p2:I + 0: 541 + 42: 'id-542' + 42: subview 'sv' + VIEW 1 rows = p2:I + 0: 542 + 43: 'id-543' + 43: subview 'sv' + VIEW 1 rows = p2:I + 0: 543 + 44: 'id-544' + 44: subview 'sv' + VIEW 1 rows = p2:I + 0: 544 + 45: 'id-545' + 45: subview 'sv' + VIEW 1 rows = p2:I + 0: 545 + 46: 'id-546' + 46: subview 'sv' + VIEW 1 rows = p2:I + 0: 546 + 47: 'id-547' + 47: subview 'sv' + VIEW 1 rows = p2:I + 0: 547 + 48: 'id-548' + 48: subview 'sv' + VIEW 1 rows = p2:I + 0: 548 + 49: 'id-549' + 49: subview 'sv' + VIEW 1 rows = p2:I + 0: 549 + 50: 'id-550' + 50: subview 'sv' + VIEW 1 rows = p2:I + 0: 550 + 51: 'id-551' + 51: subview 'sv' + VIEW 1 rows = p2:I + 0: 551 + 52: 'id-552' + 52: subview 'sv' + VIEW 1 rows = p2:I + 0: 552 + 53: 'id-553' + 53: subview 'sv' + VIEW 1 rows = p2:I + 0: 553 + 54: 'id-554' + 54: subview 'sv' + VIEW 1 rows = p2:I + 0: 554 + 55: 'id-555' + 55: subview 'sv' + VIEW 1 rows = p2:I + 0: 555 + 56: 'id-556' + 56: subview 'sv' + VIEW 1 rows = p2:I + 0: 556 + 57: 'id-557' + 57: subview 'sv' + VIEW 1 rows = p2:I + 0: 557 + 58: 'id-558' + 58: subview 'sv' + VIEW 1 rows = p2:I + 0: 558 + 59: 'id-559' + 59: subview 'sv' + VIEW 1 rows = p2:I + 0: 559 + 60: 'id-560' + 60: subview 'sv' + VIEW 1 rows = p2:I + 0: 560 + 61: 'id-561' + 61: subview 'sv' + VIEW 1 rows = p2:I + 0: 561 + 62: 'id-562' + 62: subview 'sv' + VIEW 1 rows = p2:I + 0: 562 + 63: 'id-563' + 63: subview 'sv' + VIEW 1 rows = p2:I + 0: 563 + 64: 'id-564' + 64: subview 'sv' + VIEW 1 rows = p2:I + 0: 564 + 65: 'id-565' + 65: subview 'sv' + VIEW 1 rows = p2:I + 0: 565 + 66: 'id-566' + 66: subview 'sv' + VIEW 1 rows = p2:I + 0: 566 + 67: 'id-567' + 67: subview 'sv' + VIEW 1 rows = p2:I + 0: 567 + 68: 'id-568' + 68: subview 'sv' + VIEW 1 rows = p2:I + 0: 568 + 69: 'id-569' + 69: subview 'sv' + VIEW 1 rows = p2:I + 0: 569 + 70: 'id-570' + 70: subview 'sv' + VIEW 1 rows = p2:I + 0: 570 + 71: 'id-571' + 71: subview 'sv' + VIEW 1 rows = p2:I + 0: 571 + 72: 'id-572' + 72: subview 'sv' + VIEW 1 rows = p2:I + 0: 572 + 73: 'id-573' + 73: subview 'sv' + VIEW 1 rows = p2:I + 0: 573 + 74: 'id-574' + 74: subview 'sv' + VIEW 1 rows = p2:I + 0: 574 + 75: 'id-575' + 75: subview 'sv' + VIEW 1 rows = p2:I + 0: 575 + 76: 'id-576' + 76: subview 'sv' + VIEW 1 rows = p2:I + 0: 576 + 77: 'id-577' + 77: subview 'sv' + VIEW 1 rows = p2:I + 0: 577 + 78: 'id-578' + 78: subview 'sv' + VIEW 1 rows = p2:I + 0: 578 + 79: 'id-579' + 79: subview 'sv' + VIEW 1 rows = p2:I + 0: 579 + 80: 'id-580' + 80: subview 'sv' + VIEW 1 rows = p2:I + 0: 580 + 81: 'id-581' + 81: subview 'sv' + VIEW 1 rows = p2:I + 0: 581 + 82: 'id-582' + 82: subview 'sv' + VIEW 1 rows = p2:I + 0: 582 + 83: 'id-583' + 83: subview 'sv' + VIEW 1 rows = p2:I + 0: 583 + 84: 'id-584' + 84: subview 'sv' + VIEW 1 rows = p2:I + 0: 584 + 85: 'id-585' + 85: subview 'sv' + VIEW 1 rows = p2:I + 0: 585 + 86: 'id-586' + 86: subview 'sv' + VIEW 1 rows = p2:I + 0: 586 + 87: 'id-587' + 87: subview 'sv' + VIEW 1 rows = p2:I + 0: 587 + 88: 'id-588' + 88: subview 'sv' + VIEW 1 rows = p2:I + 0: 588 + 89: 'id-589' + 89: subview 'sv' + VIEW 1 rows = p2:I + 0: 589 + 90: 'id-590' + 90: subview 'sv' + VIEW 1 rows = p2:I + 0: 590 + 91: 'id-591' + 91: subview 'sv' + VIEW 1 rows = p2:I + 0: 591 + 92: 'id-592' + 92: subview 'sv' + VIEW 1 rows = p2:I + 0: 592 + 93: 'id-593' + 93: subview 'sv' + VIEW 1 rows = p2:I + 0: 593 + 94: 'id-594' + 94: subview 'sv' + VIEW 1 rows = p2:I + 0: 594 + 95: 'id-595' + 95: subview 'sv' + VIEW 1 rows = p2:I + 0: 595 + 96: 'id-596' + 96: subview 'sv' + VIEW 1 rows = p2:I + 0: 596 + 97: 'id-597' + 97: subview 'sv' + VIEW 1 rows = p2:I + 0: 597 + 98: 'id-598' + 98: subview 'sv' + VIEW 1 rows = p2:I + 0: 598 + 99: 'id-599' + 99: subview 'sv' + VIEW 1 rows = p2:I + 0: 599 + 100: 'id-600' + 100: subview 'sv' + VIEW 1 rows = p2:I + 0: 600 + 101: 'id-601' + 101: subview 'sv' + VIEW 1 rows = p2:I + 0: 601 + 102: 'id-602' + 102: subview 'sv' + VIEW 1 rows = p2:I + 0: 602 + 103: 'id-603' + 103: subview 'sv' + VIEW 1 rows = p2:I + 0: 603 + 104: 'id-604' + 104: subview 'sv' + VIEW 1 rows = p2:I + 0: 604 + 105: 'id-605' + 105: subview 'sv' + VIEW 1 rows = p2:I + 0: 605 + 106: 'id-606' + 106: subview 'sv' + VIEW 1 rows = p2:I + 0: 606 + 107: 'id-607' + 107: subview 'sv' + VIEW 1 rows = p2:I + 0: 607 + 108: 'id-608' + 108: subview 'sv' + VIEW 1 rows = p2:I + 0: 608 + 109: 'id-609' + 109: subview 'sv' + VIEW 1 rows = p2:I + 0: 609 + 110: 'id-610' + 110: subview 'sv' + VIEW 1 rows = p2:I + 0: 610 + 111: 'id-611' + 111: subview 'sv' + VIEW 1 rows = p2:I + 0: 611 + 112: 'id-612' + 112: subview 'sv' + VIEW 1 rows = p2:I + 0: 612 + 113: 'id-613' + 113: subview 'sv' + VIEW 1 rows = p2:I + 0: 613 + 114: 'id-614' + 114: subview 'sv' + VIEW 1 rows = p2:I + 0: 614 + 115: 'id-615' + 115: subview 'sv' + VIEW 1 rows = p2:I + 0: 615 + 116: 'id-616' + 116: subview 'sv' + VIEW 1 rows = p2:I + 0: 616 + 117: 'id-617' + 117: subview 'sv' + VIEW 1 rows = p2:I + 0: 617 + 118: 'id-618' + 118: subview 'sv' + VIEW 1 rows = p2:I + 0: 618 + 119: 'id-619' + 119: subview 'sv' + VIEW 1 rows = p2:I + 0: 619 + 120: 'id-620' + 120: subview 'sv' + VIEW 1 rows = p2:I + 0: 620 + 121: 'id-621' + 121: subview 'sv' + VIEW 1 rows = p2:I + 0: 621 + 122: 'id-622' + 122: subview 'sv' + VIEW 1 rows = p2:I + 0: 622 + 123: 'id-623' + 123: subview 'sv' + VIEW 1 rows = p2:I + 0: 623 + 124: 'id-624' + 124: subview 'sv' + VIEW 1 rows = p2:I + 0: 624 + 125: 'id-625' + 125: subview 'sv' + VIEW 1 rows = p2:I + 0: 625 + 126: 'id-626' + 126: subview 'sv' + VIEW 1 rows = p2:I + 0: 626 + 127: 'id-627' + 127: subview 'sv' + VIEW 1 rows = p2:I + 0: 627 + 128: 'id-628' + 128: subview 'sv' + VIEW 1 rows = p2:I + 0: 628 + 129: 'id-629' + 129: subview 'sv' + VIEW 1 rows = p2:I + 0: 629 + 130: 'id-630' + 130: subview 'sv' + VIEW 1 rows = p2:I + 0: 630 + 131: 'id-631' + 131: subview 'sv' + VIEW 1 rows = p2:I + 0: 631 + 132: 'id-632' + 132: subview 'sv' + VIEW 1 rows = p2:I + 0: 632 + 133: 'id-633' + 133: subview 'sv' + VIEW 1 rows = p2:I + 0: 633 + 134: 'id-634' + 134: subview 'sv' + VIEW 1 rows = p2:I + 0: 634 + 135: 'id-635' + 135: subview 'sv' + VIEW 1 rows = p2:I + 0: 635 + 136: 'id-636' + 136: subview 'sv' + VIEW 1 rows = p2:I + 0: 636 + 137: 'id-637' + 137: subview 'sv' + VIEW 1 rows = p2:I + 0: 637 + 138: 'id-638' + 138: subview 'sv' + VIEW 1 rows = p2:I + 0: 638 + 139: 'id-639' + 139: subview 'sv' + VIEW 1 rows = p2:I + 0: 639 + 140: 'id-640' + 140: subview 'sv' + VIEW 1 rows = p2:I + 0: 640 + 141: 'id-641' + 141: subview 'sv' + VIEW 1 rows = p2:I + 0: 641 + 142: 'id-642' + 142: subview 'sv' + VIEW 1 rows = p2:I + 0: 642 + 143: 'id-643' + 143: subview 'sv' + VIEW 1 rows = p2:I + 0: 643 + 144: 'id-644' + 144: subview 'sv' + VIEW 1 rows = p2:I + 0: 644 + 145: 'id-645' + 145: subview 'sv' + VIEW 1 rows = p2:I + 0: 645 + 146: 'id-646' + 146: subview 'sv' + VIEW 1 rows = p2:I + 0: 646 + 147: 'id-647' + 147: subview 'sv' + VIEW 1 rows = p2:I + 0: 647 + 148: 'id-648' + 148: subview 'sv' + VIEW 1 rows = p2:I + 0: 648 + 149: 'id-649' + 149: subview 'sv' + VIEW 1 rows = p2:I + 0: 649 + 150: 'id-650' + 150: subview 'sv' + VIEW 1 rows = p2:I + 0: 650 + 151: 'id-651' + 151: subview 'sv' + VIEW 1 rows = p2:I + 0: 651 + 152: 'id-652' + 152: subview 'sv' + VIEW 1 rows = p2:I + 0: 652 + 153: 'id-653' + 153: subview 'sv' + VIEW 1 rows = p2:I + 0: 653 + 154: 'id-654' + 154: subview 'sv' + VIEW 1 rows = p2:I + 0: 654 + 155: 'id-655' + 155: subview 'sv' + VIEW 1 rows = p2:I + 0: 655 + 156: 'id-656' + 156: subview 'sv' + VIEW 1 rows = p2:I + 0: 656 + 157: 'id-657' + 157: subview 'sv' + VIEW 1 rows = p2:I + 0: 657 + 158: 'id-658' + 158: subview 'sv' + VIEW 1 rows = p2:I + 0: 658 + 159: 'id-659' + 159: subview 'sv' + VIEW 1 rows = p2:I + 0: 659 + 160: 'id-660' + 160: subview 'sv' + VIEW 1 rows = p2:I + 0: 660 + 161: 'id-661' + 161: subview 'sv' + VIEW 1 rows = p2:I + 0: 661 + 162: 'id-662' + 162: subview 'sv' + VIEW 1 rows = p2:I + 0: 662 + 163: 'id-663' + 163: subview 'sv' + VIEW 1 rows = p2:I + 0: 663 + 164: 'id-664' + 164: subview 'sv' + VIEW 1 rows = p2:I + 0: 664 + 165: 'id-665' + 165: subview 'sv' + VIEW 1 rows = p2:I + 0: 665 + 166: 'id-666' + 166: subview 'sv' + VIEW 1 rows = p2:I + 0: 666 + 167: 'id-667' + 167: subview 'sv' + VIEW 1 rows = p2:I + 0: 667 + 168: 'id-668' + 168: subview 'sv' + VIEW 1 rows = p2:I + 0: 668 + 169: 'id-669' + 169: subview 'sv' + VIEW 1 rows = p2:I + 0: 669 + 170: 'id-670' + 170: subview 'sv' + VIEW 1 rows = p2:I + 0: 670 + 171: 'id-671' + 171: subview 'sv' + VIEW 1 rows = p2:I + 0: 671 + 172: 'id-672' + 172: subview 'sv' + VIEW 1 rows = p2:I + 0: 672 + 173: 'id-673' + 173: subview 'sv' + VIEW 1 rows = p2:I + 0: 673 + 174: 'id-674' + 174: subview 'sv' + VIEW 1 rows = p2:I + 0: 674 + 175: 'id-675' + 175: subview 'sv' + VIEW 1 rows = p2:I + 0: 675 + 176: 'id-676' + 176: subview 'sv' + VIEW 1 rows = p2:I + 0: 676 + 177: 'id-677' + 177: subview 'sv' + VIEW 1 rows = p2:I + 0: 677 + 178: 'id-678' + 178: subview 'sv' + VIEW 1 rows = p2:I + 0: 678 + 179: 'id-679' + 179: subview 'sv' + VIEW 1 rows = p2:I + 0: 679 + 180: 'id-680' + 180: subview 'sv' + VIEW 1 rows = p2:I + 0: 680 + 181: 'id-681' + 181: subview 'sv' + VIEW 1 rows = p2:I + 0: 681 + 182: 'id-682' + 182: subview 'sv' + VIEW 1 rows = p2:I + 0: 682 + 183: 'id-683' + 183: subview 'sv' + VIEW 1 rows = p2:I + 0: 683 + 184: 'id-684' + 184: subview 'sv' + VIEW 1 rows = p2:I + 0: 684 + 185: 'id-685' + 185: subview 'sv' + VIEW 1 rows = p2:I + 0: 685 + 186: 'id-686' + 186: subview 'sv' + VIEW 1 rows = p2:I + 0: 686 + 187: 'id-687' + 187: subview 'sv' + VIEW 1 rows = p2:I + 0: 687 + 188: 'id-688' + 188: subview 'sv' + VIEW 1 rows = p2:I + 0: 688 + 189: 'id-689' + 189: subview 'sv' + VIEW 1 rows = p2:I + 0: 689 + 190: 'id-690' + 190: subview 'sv' + VIEW 1 rows = p2:I + 0: 690 + 191: 'id-691' + 191: subview 'sv' + VIEW 1 rows = p2:I + 0: 691 + 192: 'id-692' + 192: subview 'sv' + VIEW 1 rows = p2:I + 0: 692 + 193: 'id-693' + 193: subview 'sv' + VIEW 1 rows = p2:I + 0: 693 + 194: 'id-694' + 194: subview 'sv' + VIEW 1 rows = p2:I + 0: 694 + 195: 'id-695' + 195: subview 'sv' + VIEW 1 rows = p2:I + 0: 695 + 196: 'id-696' + 196: subview 'sv' + VIEW 1 rows = p2:I + 0: 696 + 197: 'id-697' + 197: subview 'sv' + VIEW 1 rows = p2:I + 0: 697 + 198: 'id-698' + 198: subview 'sv' + VIEW 1 rows = p2:I + 0: 698 + 199: 'id-699' + 199: subview 'sv' + VIEW 1 rows = p2:I + 0: 699 + 200: 'id-700' + 200: subview 'sv' + VIEW 1 rows = p2:I + 0: 700 + 201: 'id-701' + 201: subview 'sv' + VIEW 1 rows = p2:I + 0: 701 + 202: 'id-702' + 202: subview 'sv' + VIEW 1 rows = p2:I + 0: 702 + 203: 'id-703' + 203: subview 'sv' + VIEW 1 rows = p2:I + 0: 703 + 204: 'id-704' + 204: subview 'sv' + VIEW 1 rows = p2:I + 0: 704 + 205: 'id-705' + 205: subview 'sv' + VIEW 1 rows = p2:I + 0: 705 + 206: 'id-706' + 206: subview 'sv' + VIEW 1 rows = p2:I + 0: 706 + 207: 'id-707' + 207: subview 'sv' + VIEW 1 rows = p2:I + 0: 707 + 208: 'id-708' + 208: subview 'sv' + VIEW 1 rows = p2:I + 0: 708 + 209: 'id-709' + 209: subview 'sv' + VIEW 1 rows = p2:I + 0: 709 + 210: 'id-710' + 210: subview 'sv' + VIEW 1 rows = p2:I + 0: 710 + 211: 'id-711' + 211: subview 'sv' + VIEW 1 rows = p2:I + 0: 711 + 212: 'id-712' + 212: subview 'sv' + VIEW 1 rows = p2:I + 0: 712 + 213: 'id-713' + 213: subview 'sv' + VIEW 1 rows = p2:I + 0: 713 + 214: 'id-714' + 214: subview 'sv' + VIEW 1 rows = p2:I + 0: 714 + 215: 'id-715' + 215: subview 'sv' + VIEW 1 rows = p2:I + 0: 715 + 216: 'id-716' + 216: subview 'sv' + VIEW 1 rows = p2:I + 0: 716 + 217: 'id-717' + 217: subview 'sv' + VIEW 1 rows = p2:I + 0: 717 + 218: 'id-718' + 218: subview 'sv' + VIEW 1 rows = p2:I + 0: 718 + 219: 'id-719' + 219: subview 'sv' + VIEW 1 rows = p2:I + 0: 719 + 220: 'id-720' + 220: subview 'sv' + VIEW 1 rows = p2:I + 0: 720 + 221: 'id-721' + 221: subview 'sv' + VIEW 1 rows = p2:I + 0: 721 + 222: 'id-722' + 222: subview 'sv' + VIEW 1 rows = p2:I + 0: 722 + 223: 'id-723' + 223: subview 'sv' + VIEW 1 rows = p2:I + 0: 723 + 224: 'id-724' + 224: subview 'sv' + VIEW 1 rows = p2:I + 0: 724 + 225: 'id-725' + 225: subview 'sv' + VIEW 1 rows = p2:I + 0: 725 + 226: 'id-726' + 226: subview 'sv' + VIEW 1 rows = p2:I + 0: 726 + 227: 'id-727' + 227: subview 'sv' + VIEW 1 rows = p2:I + 0: 727 + 228: 'id-728' + 228: subview 'sv' + VIEW 1 rows = p2:I + 0: 728 + 229: 'id-729' + 229: subview 'sv' + VIEW 1 rows = p2:I + 0: 729 + 230: 'id-730' + 230: subview 'sv' + VIEW 1 rows = p2:I + 0: 730 + 231: 'id-731' + 231: subview 'sv' + VIEW 1 rows = p2:I + 0: 731 + 232: 'id-732' + 232: subview 'sv' + VIEW 1 rows = p2:I + 0: 732 + 233: 'id-733' + 233: subview 'sv' + VIEW 1 rows = p2:I + 0: 733 + 234: 'id-734' + 234: subview 'sv' + VIEW 1 rows = p2:I + 0: 734 + 235: 'id-735' + 235: subview 'sv' + VIEW 1 rows = p2:I + 0: 735 + 236: 'id-736' + 236: subview 'sv' + VIEW 1 rows = p2:I + 0: 736 + 237: 'id-737' + 237: subview 'sv' + VIEW 1 rows = p2:I + 0: 737 + 238: 'id-738' + 238: subview 'sv' + VIEW 1 rows = p2:I + 0: 738 + 239: 'id-739' + 239: subview 'sv' + VIEW 1 rows = p2:I + 0: 739 + 240: 'id-740' + 240: subview 'sv' + VIEW 1 rows = p2:I + 0: 740 + 241: 'id-741' + 241: subview 'sv' + VIEW 1 rows = p2:I + 0: 741 + 242: 'id-742' + 242: subview 'sv' + VIEW 1 rows = p2:I + 0: 742 + 243: 'id-743' + 243: subview 'sv' + VIEW 1 rows = p2:I + 0: 743 + 244: 'id-744' + 244: subview 'sv' + VIEW 1 rows = p2:I + 0: 744 + 245: 'id-745' + 245: subview 'sv' + VIEW 1 rows = p2:I + 0: 745 + 246: 'id-746' + 246: subview 'sv' + VIEW 1 rows = p2:I + 0: 746 + 247: 'id-747' + 247: subview 'sv' + VIEW 1 rows = p2:I + 0: 747 + 248: 'id-748' + 248: subview 'sv' + VIEW 1 rows = p2:I + 0: 748 + 249: 'id-749' + 249: subview 'sv' + VIEW 1 rows = p2:I + 0: 749 + 250: 'id-750' + 250: subview 'sv' + VIEW 1 rows = p2:I + 0: 750 + 251: 'id-751' + 251: subview 'sv' + VIEW 1 rows = p2:I + 0: 751 + 252: 'id-752' + 252: subview 'sv' + VIEW 1 rows = p2:I + 0: 752 + 253: 'id-753' + 253: subview 'sv' + VIEW 1 rows = p2:I + 0: 753 + 254: 'id-754' + 254: subview 'sv' + VIEW 1 rows = p2:I + 0: 754 + 255: 'id-755' + 255: subview 'sv' + VIEW 1 rows = p2:I + 0: 755 + 256: 'id-756' + 256: subview 'sv' + VIEW 1 rows = p2:I + 0: 756 + 257: 'id-757' + 257: subview 'sv' + VIEW 1 rows = p2:I + 0: 757 + 258: 'id-758' + 258: subview 'sv' + VIEW 1 rows = p2:I + 0: 758 + 259: 'id-759' + 259: subview 'sv' + VIEW 1 rows = p2:I + 0: 759 + 260: 'id-760' + 260: subview 'sv' + VIEW 1 rows = p2:I + 0: 760 + 261: 'id-761' + 261: subview 'sv' + VIEW 1 rows = p2:I + 0: 761 + 262: 'id-762' + 262: subview 'sv' + VIEW 1 rows = p2:I + 0: 762 + 263: 'id-763' + 263: subview 'sv' + VIEW 1 rows = p2:I + 0: 763 + 264: 'id-764' + 264: subview 'sv' + VIEW 1 rows = p2:I + 0: 764 + 265: 'id-765' + 265: subview 'sv' + VIEW 1 rows = p2:I + 0: 765 + 266: 'id-766' + 266: subview 'sv' + VIEW 1 rows = p2:I + 0: 766 + 267: 'id-767' + 267: subview 'sv' + VIEW 1 rows = p2:I + 0: 767 + 268: 'id-768' + 268: subview 'sv' + VIEW 1 rows = p2:I + 0: 768 + 269: 'id-769' + 269: subview 'sv' + VIEW 1 rows = p2:I + 0: 769 + 270: 'id-770' + 270: subview 'sv' + VIEW 1 rows = p2:I + 0: 770 + 271: 'id-771' + 271: subview 'sv' + VIEW 1 rows = p2:I + 0: 771 + 272: 'id-772' + 272: subview 'sv' + VIEW 1 rows = p2:I + 0: 772 + 273: 'id-773' + 273: subview 'sv' + VIEW 1 rows = p2:I + 0: 773 + 274: 'id-774' + 274: subview 'sv' + VIEW 1 rows = p2:I + 0: 774 + 275: 'id-775' + 275: subview 'sv' + VIEW 1 rows = p2:I + 0: 775 + 276: 'id-776' + 276: subview 'sv' + VIEW 1 rows = p2:I + 0: 776 + 277: 'id-777' + 277: subview 'sv' + VIEW 1 rows = p2:I + 0: 777 + 278: 'id-778' + 278: subview 'sv' + VIEW 1 rows = p2:I + 0: 778 + 279: 'id-779' + 279: subview 'sv' + VIEW 1 rows = p2:I + 0: 779 + 280: 'id-780' + 280: subview 'sv' + VIEW 1 rows = p2:I + 0: 780 + 281: 'id-781' + 281: subview 'sv' + VIEW 1 rows = p2:I + 0: 781 + 282: 'id-782' + 282: subview 'sv' + VIEW 1 rows = p2:I + 0: 782 + 283: 'id-783' + 283: subview 'sv' + VIEW 1 rows = p2:I + 0: 783 + 284: 'id-784' + 284: subview 'sv' + VIEW 1 rows = p2:I + 0: 784 + 285: 'id-785' + 285: subview 'sv' + VIEW 1 rows = p2:I + 0: 785 + 286: 'id-786' + 286: subview 'sv' + VIEW 1 rows = p2:I + 0: 786 + 287: 'id-787' + 287: subview 'sv' + VIEW 1 rows = p2:I + 0: 787 + 288: 'id-788' + 288: subview 'sv' + VIEW 1 rows = p2:I + 0: 788 + 289: 'id-789' + 289: subview 'sv' + VIEW 1 rows = p2:I + 0: 789 + 290: 'id-790' + 290: subview 'sv' + VIEW 1 rows = p2:I + 0: 790 + 291: 'id-791' + 291: subview 'sv' + VIEW 1 rows = p2:I + 0: 791 + 292: 'id-792' + 292: subview 'sv' + VIEW 1 rows = p2:I + 0: 792 + 293: 'id-793' + 293: subview 'sv' + VIEW 1 rows = p2:I + 0: 793 + 294: 'id-794' + 294: subview 'sv' + VIEW 1 rows = p2:I + 0: 794 + 295: 'id-795' + 295: subview 'sv' + VIEW 1 rows = p2:I + 0: 795 + 296: 'id-796' + 296: subview 'sv' + VIEW 1 rows = p2:I + 0: 796 + 297: 'id-797' + 297: subview 'sv' + VIEW 1 rows = p2:I + 0: 797 + 298: 'id-798' + 298: subview 'sv' + VIEW 1 rows = p2:I + 0: 798 + 299: 'id-799' + 299: subview 'sv' + VIEW 1 rows = p2:I + 0: 799 + 300: 'id-800' + 300: subview 'sv' + VIEW 1 rows = p2:I + 0: 800 + 301: 'id-801' + 301: subview 'sv' + VIEW 1 rows = p2:I + 0: 801 + 302: 'id-802' + 302: subview 'sv' + VIEW 1 rows = p2:I + 0: 802 + 303: 'id-803' + 303: subview 'sv' + VIEW 1 rows = p2:I + 0: 803 + 304: 'id-804' + 304: subview 'sv' + VIEW 1 rows = p2:I + 0: 804 + 305: 'id-805' + 305: subview 'sv' + VIEW 1 rows = p2:I + 0: 805 + 306: 'id-806' + 306: subview 'sv' + VIEW 1 rows = p2:I + 0: 806 + 307: 'id-807' + 307: subview 'sv' + VIEW 1 rows = p2:I + 0: 807 + 308: 'id-808' + 308: subview 'sv' + VIEW 1 rows = p2:I + 0: 808 + 309: 'id-809' + 309: subview 'sv' + VIEW 1 rows = p2:I + 0: 809 + 310: 'id-810' + 310: subview 'sv' + VIEW 1 rows = p2:I + 0: 810 + 311: 'id-811' + 311: subview 'sv' + VIEW 1 rows = p2:I + 0: 811 + 312: 'id-812' + 312: subview 'sv' + VIEW 1 rows = p2:I + 0: 812 + 313: 'id-813' + 313: subview 'sv' + VIEW 1 rows = p2:I + 0: 813 + 314: 'id-814' + 314: subview 'sv' + VIEW 1 rows = p2:I + 0: 814 + 315: 'id-815' + 315: subview 'sv' + VIEW 1 rows = p2:I + 0: 815 + 316: 'id-816' + 316: subview 'sv' + VIEW 1 rows = p2:I + 0: 816 + 317: 'id-817' + 317: subview 'sv' + VIEW 1 rows = p2:I + 0: 817 + 318: 'id-818' + 318: subview 'sv' + VIEW 1 rows = p2:I + 0: 818 + 319: 'id-819' + 319: subview 'sv' + VIEW 1 rows = p2:I + 0: 819 + 320: 'id-820' + 320: subview 'sv' + VIEW 1 rows = p2:I + 0: 820 + 321: 'id-821' + 321: subview 'sv' + VIEW 1 rows = p2:I + 0: 821 + 322: 'id-822' + 322: subview 'sv' + VIEW 1 rows = p2:I + 0: 822 + 323: 'id-823' + 323: subview 'sv' + VIEW 1 rows = p2:I + 0: 823 + 324: 'id-824' + 324: subview 'sv' + VIEW 1 rows = p2:I + 0: 824 + 325: 'id-825' + 325: subview 'sv' + VIEW 1 rows = p2:I + 0: 825 + 326: 'id-826' + 326: subview 'sv' + VIEW 1 rows = p2:I + 0: 826 + 327: 'id-827' + 327: subview 'sv' + VIEW 1 rows = p2:I + 0: 827 + 328: 'id-828' + 328: subview 'sv' + VIEW 1 rows = p2:I + 0: 828 + 329: 'id-829' + 329: subview 'sv' + VIEW 1 rows = p2:I + 0: 829 + 330: 'id-830' + 330: subview 'sv' + VIEW 1 rows = p2:I + 0: 830 + 331: 'id-831' + 331: subview 'sv' + VIEW 1 rows = p2:I + 0: 831 + 332: 'id-832' + 332: subview 'sv' + VIEW 1 rows = p2:I + 0: 832 + 333: 'id-833' + 333: subview 'sv' + VIEW 1 rows = p2:I + 0: 833 + 334: 'id-834' + 334: subview 'sv' + VIEW 1 rows = p2:I + 0: 834 + 335: 'id-835' + 335: subview 'sv' + VIEW 1 rows = p2:I + 0: 835 + 336: 'id-836' + 336: subview 'sv' + VIEW 1 rows = p2:I + 0: 836 + 337: 'id-837' + 337: subview 'sv' + VIEW 1 rows = p2:I + 0: 837 + 338: 'id-838' + 338: subview 'sv' + VIEW 1 rows = p2:I + 0: 838 + 339: 'id-839' + 339: subview 'sv' + VIEW 1 rows = p2:I + 0: 839 + 340: 'id-840' + 340: subview 'sv' + VIEW 1 rows = p2:I + 0: 840 + 341: 'id-841' + 341: subview 'sv' + VIEW 1 rows = p2:I + 0: 841 + 342: 'id-842' + 342: subview 'sv' + VIEW 1 rows = p2:I + 0: 842 + 343: 'id-843' + 343: subview 'sv' + VIEW 1 rows = p2:I + 0: 843 + 344: 'id-844' + 344: subview 'sv' + VIEW 1 rows = p2:I + 0: 844 + 345: 'id-845' + 345: subview 'sv' + VIEW 1 rows = p2:I + 0: 845 + 346: 'id-846' + 346: subview 'sv' + VIEW 1 rows = p2:I + 0: 846 + 347: 'id-847' + 347: subview 'sv' + VIEW 1 rows = p2:I + 0: 847 + 348: 'id-848' + 348: subview 'sv' + VIEW 1 rows = p2:I + 0: 848 + 349: 'id-849' + 349: subview 'sv' + VIEW 1 rows = p2:I + 0: 849 + 350: 'id-850' + 350: subview 'sv' + VIEW 1 rows = p2:I + 0: 850 + 351: 'id-851' + 351: subview 'sv' + VIEW 1 rows = p2:I + 0: 851 + 352: 'id-852' + 352: subview 'sv' + VIEW 1 rows = p2:I + 0: 852 + 353: 'id-853' + 353: subview 'sv' + VIEW 1 rows = p2:I + 0: 853 + 354: 'id-854' + 354: subview 'sv' + VIEW 1 rows = p2:I + 0: 854 + 355: 'id-855' + 355: subview 'sv' + VIEW 1 rows = p2:I + 0: 855 + 356: 'id-856' + 356: subview 'sv' + VIEW 1 rows = p2:I + 0: 856 + 357: 'id-857' + 357: subview 'sv' + VIEW 1 rows = p2:I + 0: 857 + 358: 'id-858' + 358: subview 'sv' + VIEW 1 rows = p2:I + 0: 858 + 359: 'id-859' + 359: subview 'sv' + VIEW 1 rows = p2:I + 0: 859 + 360: 'id-860' + 360: subview 'sv' + VIEW 1 rows = p2:I + 0: 860 + 361: 'id-861' + 361: subview 'sv' + VIEW 1 rows = p2:I + 0: 861 + 362: 'id-862' + 362: subview 'sv' + VIEW 1 rows = p2:I + 0: 862 + 363: 'id-863' + 363: subview 'sv' + VIEW 1 rows = p2:I + 0: 863 + 364: 'id-864' + 364: subview 'sv' + VIEW 1 rows = p2:I + 0: 864 + 365: 'id-865' + 365: subview 'sv' + VIEW 1 rows = p2:I + 0: 865 + 366: 'id-866' + 366: subview 'sv' + VIEW 1 rows = p2:I + 0: 866 + 367: 'id-867' + 367: subview 'sv' + VIEW 1 rows = p2:I + 0: 867 + 368: 'id-868' + 368: subview 'sv' + VIEW 1 rows = p2:I + 0: 868 + 369: 'id-869' + 369: subview 'sv' + VIEW 1 rows = p2:I + 0: 869 + 370: 'id-870' + 370: subview 'sv' + VIEW 1 rows = p2:I + 0: 870 + 371: 'id-871' + 371: subview 'sv' + VIEW 1 rows = p2:I + 0: 871 + 372: 'id-872' + 372: subview 'sv' + VIEW 1 rows = p2:I + 0: 872 + 373: 'id-873' + 373: subview 'sv' + VIEW 1 rows = p2:I + 0: 873 + 374: 'id-874' + 374: subview 'sv' + VIEW 1 rows = p2:I + 0: 874 + 375: 'id-875' + 375: subview 'sv' + VIEW 1 rows = p2:I + 0: 875 + 376: 'id-876' + 376: subview 'sv' + VIEW 1 rows = p2:I + 0: 876 + 377: 'id-877' + 377: subview 'sv' + VIEW 1 rows = p2:I + 0: 877 + 378: 'id-878' + 378: subview 'sv' + VIEW 1 rows = p2:I + 0: 878 + 379: 'id-879' + 379: subview 'sv' + VIEW 1 rows = p2:I + 0: 879 + 380: 'id-880' + 380: subview 'sv' + VIEW 1 rows = p2:I + 0: 880 + 381: 'id-881' + 381: subview 'sv' + VIEW 1 rows = p2:I + 0: 881 + 382: 'id-882' + 382: subview 'sv' + VIEW 1 rows = p2:I + 0: 882 + 383: 'id-883' + 383: subview 'sv' + VIEW 1 rows = p2:I + 0: 883 + 384: 'id-884' + 384: subview 'sv' + VIEW 1 rows = p2:I + 0: 884 + 385: 'id-885' + 385: subview 'sv' + VIEW 1 rows = p2:I + 0: 885 + 386: 'id-886' + 386: subview 'sv' + VIEW 1 rows = p2:I + 0: 886 + 387: 'id-887' + 387: subview 'sv' + VIEW 1 rows = p2:I + 0: 887 + 388: 'id-888' + 388: subview 'sv' + VIEW 1 rows = p2:I + 0: 888 + 389: 'id-889' + 389: subview 'sv' + VIEW 1 rows = p2:I + 0: 889 + 390: 'id-890' + 390: subview 'sv' + VIEW 1 rows = p2:I + 0: 890 + 391: 'id-891' + 391: subview 'sv' + VIEW 1 rows = p2:I + 0: 891 + 392: 'id-892' + 392: subview 'sv' + VIEW 1 rows = p2:I + 0: 892 + 393: 'id-893' + 393: subview 'sv' + VIEW 1 rows = p2:I + 0: 893 + 394: 'id-894' + 394: subview 'sv' + VIEW 1 rows = p2:I + 0: 894 + 395: 'id-895' + 395: subview 'sv' + VIEW 1 rows = p2:I + 0: 895 + 396: 'id-896' + 396: subview 'sv' + VIEW 1 rows = p2:I + 0: 896 + 397: 'id-897' + 397: subview 'sv' + VIEW 1 rows = p2:I + 0: 897 + 398: 'id-898' + 398: subview 'sv' + VIEW 1 rows = p2:I + 0: 898 + 399: 'id-899' + 399: subview 'sv' + VIEW 1 rows = p2:I + 0: 899 + 400: 'id-900' + 400: subview 'sv' + VIEW 1 rows = p2:I + 0: 900 + 401: 'id-901' + 401: subview 'sv' + VIEW 1 rows = p2:I + 0: 901 + 402: 'id-902' + 402: subview 'sv' + VIEW 1 rows = p2:I + 0: 902 + 403: 'id-903' + 403: subview 'sv' + VIEW 1 rows = p2:I + 0: 903 + 404: 'id-904' + 404: subview 'sv' + VIEW 1 rows = p2:I + 0: 904 + 405: 'id-905' + 405: subview 'sv' + VIEW 1 rows = p2:I + 0: 905 + 406: 'id-906' + 406: subview 'sv' + VIEW 1 rows = p2:I + 0: 906 + 407: 'id-907' + 407: subview 'sv' + VIEW 1 rows = p2:I + 0: 907 + 408: 'id-908' + 408: subview 'sv' + VIEW 1 rows = p2:I + 0: 908 + 409: 'id-909' + 409: subview 'sv' + VIEW 1 rows = p2:I + 0: 909 + 410: 'id-910' + 410: subview 'sv' + VIEW 1 rows = p2:I + 0: 910 + 411: 'id-911' + 411: subview 'sv' + VIEW 1 rows = p2:I + 0: 911 + 412: 'id-912' + 412: subview 'sv' + VIEW 1 rows = p2:I + 0: 912 + 413: 'id-913' + 413: subview 'sv' + VIEW 1 rows = p2:I + 0: 913 + 414: 'id-914' + 414: subview 'sv' + VIEW 1 rows = p2:I + 0: 914 + 415: 'id-915' + 415: subview 'sv' + VIEW 1 rows = p2:I + 0: 915 + 416: 'id-916' + 416: subview 'sv' + VIEW 1 rows = p2:I + 0: 916 + 417: 'id-917' + 417: subview 'sv' + VIEW 1 rows = p2:I + 0: 917 + 418: 'id-918' + 418: subview 'sv' + VIEW 1 rows = p2:I + 0: 918 + 419: 'id-919' + 419: subview 'sv' + VIEW 1 rows = p2:I + 0: 919 + 420: 'id-920' + 420: subview 'sv' + VIEW 1 rows = p2:I + 0: 920 + 421: 'id-921' + 421: subview 'sv' + VIEW 1 rows = p2:I + 0: 921 + 422: 'id-922' + 422: subview 'sv' + VIEW 1 rows = p2:I + 0: 922 + 423: 'id-923' + 423: subview 'sv' + VIEW 1 rows = p2:I + 0: 923 + 424: 'id-924' + 424: subview 'sv' + VIEW 1 rows = p2:I + 0: 924 + 425: 'id-925' + 425: subview 'sv' + VIEW 1 rows = p2:I + 0: 925 + 426: 'id-926' + 426: subview 'sv' + VIEW 1 rows = p2:I + 0: 926 + 427: 'id-927' + 427: subview 'sv' + VIEW 1 rows = p2:I + 0: 927 + 428: 'id-928' + 428: subview 'sv' + VIEW 1 rows = p2:I + 0: 928 + 429: 'id-929' + 429: subview 'sv' + VIEW 1 rows = p2:I + 0: 929 + 430: 'id-930' + 430: subview 'sv' + VIEW 1 rows = p2:I + 0: 930 + 431: 'id-931' + 431: subview 'sv' + VIEW 1 rows = p2:I + 0: 931 + 432: 'id-932' + 432: subview 'sv' + VIEW 1 rows = p2:I + 0: 932 + 433: 'id-933' + 433: subview 'sv' + VIEW 1 rows = p2:I + 0: 933 + 434: 'id-934' + 434: subview 'sv' + VIEW 1 rows = p2:I + 0: 934 + 435: 'id-935' + 435: subview 'sv' + VIEW 1 rows = p2:I + 0: 935 + 436: 'id-936' + 436: subview 'sv' + VIEW 1 rows = p2:I + 0: 936 + 437: 'id-937' + 437: subview 'sv' + VIEW 1 rows = p2:I + 0: 937 + 438: 'id-938' + 438: subview 'sv' + VIEW 1 rows = p2:I + 0: 938 + 439: 'id-939' + 439: subview 'sv' + VIEW 1 rows = p2:I + 0: 939 + 440: 'id-940' + 440: subview 'sv' + VIEW 1 rows = p2:I + 0: 940 + 441: 'id-941' + 441: subview 'sv' + VIEW 1 rows = p2:I + 0: 941 + 442: 'id-942' + 442: subview 'sv' + VIEW 1 rows = p2:I + 0: 942 + 443: 'id-943' + 443: subview 'sv' + VIEW 1 rows = p2:I + 0: 943 + 444: 'id-944' + 444: subview 'sv' + VIEW 1 rows = p2:I + 0: 944 + 445: 'id-945' + 445: subview 'sv' + VIEW 1 rows = p2:I + 0: 945 + 446: 'id-946' + 446: subview 'sv' + VIEW 1 rows = p2:I + 0: 946 + 447: 'id-947' + 447: subview 'sv' + VIEW 1 rows = p2:I + 0: 947 + 448: 'id-948' + 448: subview 'sv' + VIEW 1 rows = p2:I + 0: 948 + 449: 'id-949' + 449: subview 'sv' + VIEW 1 rows = p2:I + 0: 949 + 450: 'id-950' + 450: subview 'sv' + VIEW 1 rows = p2:I + 0: 950 + 451: 'id-951' + 451: subview 'sv' + VIEW 1 rows = p2:I + 0: 951 + 452: 'id-952' + 452: subview 'sv' + VIEW 1 rows = p2:I + 0: 952 + 453: 'id-953' + 453: subview 'sv' + VIEW 1 rows = p2:I + 0: 953 + 454: 'id-954' + 454: subview 'sv' + VIEW 1 rows = p2:I + 0: 954 + 455: 'id-955' + 455: subview 'sv' + VIEW 1 rows = p2:I + 0: 955 + 456: 'id-956' + 456: subview 'sv' + VIEW 1 rows = p2:I + 0: 956 + 457: 'id-957' + 457: subview 'sv' + VIEW 1 rows = p2:I + 0: 957 + 458: 'id-958' + 458: subview 'sv' + VIEW 1 rows = p2:I + 0: 958 + 459: 'id-959' + 459: subview 'sv' + VIEW 1 rows = p2:I + 0: 959 + 460: 'id-960' + 460: subview 'sv' + VIEW 1 rows = p2:I + 0: 960 + 461: 'id-961' + 461: subview 'sv' + VIEW 1 rows = p2:I + 0: 961 + 462: 'id-962' + 462: subview 'sv' + VIEW 1 rows = p2:I + 0: 962 + 463: 'id-963' + 463: subview 'sv' + VIEW 1 rows = p2:I + 0: 963 + 464: 'id-964' + 464: subview 'sv' + VIEW 1 rows = p2:I + 0: 964 + 465: 'id-965' + 465: subview 'sv' + VIEW 1 rows = p2:I + 0: 965 + 466: 'id-966' + 466: subview 'sv' + VIEW 1 rows = p2:I + 0: 966 + 467: 'id-967' + 467: subview 'sv' + VIEW 1 rows = p2:I + 0: 967 + 468: 'id-968' + 468: subview 'sv' + VIEW 1 rows = p2:I + 0: 968 + 469: 'id-969' + 469: subview 'sv' + VIEW 1 rows = p2:I + 0: 969 + 470: 'id-970' + 470: subview 'sv' + VIEW 1 rows = p2:I + 0: 970 + 471: 'id-971' + 471: subview 'sv' + VIEW 1 rows = p2:I + 0: 971 + 472: 'id-972' + 472: subview 'sv' + VIEW 1 rows = p2:I + 0: 972 + 473: 'id-973' + 473: subview 'sv' + VIEW 1 rows = p2:I + 0: 973 + 474: 'id-974' + 474: subview 'sv' + VIEW 1 rows = p2:I + 0: 974 + 475: 'id-975' + 475: subview 'sv' + VIEW 1 rows = p2:I + 0: 975 + 476: 'id-976' + 476: subview 'sv' + VIEW 1 rows = p2:I + 0: 976 + 477: 'id-977' + 477: subview 'sv' + VIEW 1 rows = p2:I + 0: 977 + 478: 'id-978' + 478: subview 'sv' + VIEW 1 rows = p2:I + 0: 978 + 479: 'id-979' + 479: subview 'sv' + VIEW 1 rows = p2:I + 0: 979 + 480: 'id-980' + 480: subview 'sv' + VIEW 1 rows = p2:I + 0: 980 + 481: 'id-981' + 481: subview 'sv' + VIEW 1 rows = p2:I + 0: 981 + 482: 'id-982' + 482: subview 'sv' + VIEW 1 rows = p2:I + 0: 982 + 483: 'id-983' + 483: subview 'sv' + VIEW 1 rows = p2:I + 0: 983 + 484: 'id-984' + 484: subview 'sv' + VIEW 1 rows = p2:I + 0: 984 + 485: 'id-985' + 485: subview 'sv' + VIEW 1 rows = p2:I + 0: 985 + 486: 'id-986' + 486: subview 'sv' + VIEW 1 rows = p2:I + 0: 986 + 487: 'id-987' + 487: subview 'sv' + VIEW 1 rows = p2:I + 0: 987 + 488: 'id-988' + 488: subview 'sv' + VIEW 1 rows = p2:I + 0: 988 + 489: 'id-989' + 489: subview 'sv' + VIEW 1 rows = p2:I + 0: 989 + 490: 'id-990' + 490: subview 'sv' + VIEW 1 rows = p2:I + 0: 990 + 491: 'id-991' + 491: subview 'sv' + VIEW 1 rows = p2:I + 0: 991 + 492: 'id-992' + 492: subview 'sv' + VIEW 1 rows = p2:I + 0: 992 + 493: 'id-993' + 493: subview 'sv' + VIEW 1 rows = p2:I + 0: 993 + 494: 'id-994' + 494: subview 'sv' + VIEW 1 rows = p2:I + 0: 994 + 495: 'id-995' + 495: subview 'sv' + VIEW 1 rows = p2:I + 0: 995 + 496: 'id-996' + 496: subview 'sv' + VIEW 1 rows = p2:I + 0: 996 + 497: 'id-997' + 497: subview 'sv' + VIEW 1 rows = p2:I + 0: 997 + 498: 'id-998' + 498: subview 'sv' + VIEW 1 rows = p2:I + 0: 998 + 499: 'id-999' + 499: subview 'sv' + VIEW 1 rows = p2:I + 0: 999 + 2: subview '_B' + VIEW 1 rows = p1:S sv:V + 0: 'insert-0' + 0: subview 'sv' + VIEW 0 rows = p2:I diff --git a/akregator/src/mk4storage/metakit/tests/ok/m06.txt b/akregator/src/mk4storage/metakit/tests/ok/m06.txt new file mode 100644 index 000000000..ef0cb4640 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m06.txt @@ -0,0 +1,2 @@ +>>> Blocked view multi-row deletion +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/m06a.txt b/akregator/src/mk4storage/metakit/tests/ok/m06a.txt new file mode 100644 index 000000000..d72b2e671 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m06a.txt @@ -0,0 +1,2963 @@ + VIEW 1 rows = v1:V v2:V + 0: subview 'v1' + VIEW 901 rows = p1:I + 0: 1 + 1: 100 + 2: 101 + 3: 102 + 4: 103 + 5: 104 + 6: 105 + 7: 106 + 8: 107 + 9: 108 + 10: 109 + 11: 110 + 12: 111 + 13: 112 + 14: 113 + 15: 114 + 16: 115 + 17: 116 + 18: 117 + 19: 118 + 20: 119 + 21: 120 + 22: 121 + 23: 122 + 24: 123 + 25: 124 + 26: 125 + 27: 126 + 28: 127 + 29: 128 + 30: 129 + 31: 130 + 32: 131 + 33: 132 + 34: 133 + 35: 134 + 36: 135 + 37: 136 + 38: 137 + 39: 138 + 40: 139 + 41: 140 + 42: 141 + 43: 142 + 44: 143 + 45: 144 + 46: 145 + 47: 146 + 48: 147 + 49: 148 + 50: 149 + 51: 150 + 52: 151 + 53: 152 + 54: 153 + 55: 154 + 56: 155 + 57: 156 + 58: 157 + 59: 158 + 60: 159 + 61: 160 + 62: 161 + 63: 162 + 64: 163 + 65: 164 + 66: 165 + 67: 166 + 68: 167 + 69: 168 + 70: 169 + 71: 170 + 72: 171 + 73: 172 + 74: 173 + 75: 174 + 76: 175 + 77: 176 + 78: 177 + 79: 178 + 80: 179 + 81: 180 + 82: 181 + 83: 182 + 84: 183 + 85: 184 + 86: 185 + 87: 186 + 88: 187 + 89: 188 + 90: 189 + 91: 190 + 92: 191 + 93: 192 + 94: 193 + 95: 194 + 96: 195 + 97: 196 + 98: 197 + 99: 198 + 100: 199 + 101: 200 + 102: 201 + 103: 202 + 104: 203 + 105: 204 + 106: 205 + 107: 206 + 108: 207 + 109: 208 + 110: 209 + 111: 210 + 112: 211 + 113: 212 + 114: 213 + 115: 214 + 116: 215 + 117: 216 + 118: 217 + 119: 218 + 120: 219 + 121: 220 + 122: 221 + 123: 222 + 124: 223 + 125: 224 + 126: 225 + 127: 226 + 128: 227 + 129: 228 + 130: 229 + 131: 230 + 132: 231 + 133: 232 + 134: 233 + 135: 234 + 136: 235 + 137: 236 + 138: 237 + 139: 238 + 140: 239 + 141: 240 + 142: 241 + 143: 242 + 144: 243 + 145: 244 + 146: 245 + 147: 246 + 148: 247 + 149: 248 + 150: 249 + 151: 250 + 152: 251 + 153: 252 + 154: 253 + 155: 254 + 156: 255 + 157: 256 + 158: 257 + 159: 258 + 160: 259 + 161: 260 + 162: 261 + 163: 262 + 164: 263 + 165: 264 + 166: 265 + 167: 266 + 168: 267 + 169: 268 + 170: 269 + 171: 270 + 172: 271 + 173: 272 + 174: 273 + 175: 274 + 176: 275 + 177: 276 + 178: 277 + 179: 278 + 180: 279 + 181: 280 + 182: 281 + 183: 282 + 184: 283 + 185: 284 + 186: 285 + 187: 286 + 188: 287 + 189: 288 + 190: 289 + 191: 290 + 192: 291 + 193: 292 + 194: 293 + 195: 294 + 196: 295 + 197: 296 + 198: 297 + 199: 298 + 200: 299 + 201: 300 + 202: 301 + 203: 302 + 204: 303 + 205: 304 + 206: 305 + 207: 306 + 208: 307 + 209: 308 + 210: 309 + 211: 310 + 212: 311 + 213: 312 + 214: 313 + 215: 314 + 216: 315 + 217: 316 + 218: 317 + 219: 318 + 220: 319 + 221: 320 + 222: 321 + 223: 322 + 224: 323 + 225: 324 + 226: 325 + 227: 326 + 228: 327 + 229: 328 + 230: 329 + 231: 330 + 232: 331 + 233: 332 + 234: 333 + 235: 334 + 236: 335 + 237: 336 + 238: 337 + 239: 338 + 240: 339 + 241: 340 + 242: 341 + 243: 342 + 244: 343 + 245: 344 + 246: 345 + 247: 346 + 248: 347 + 249: 348 + 250: 349 + 251: 350 + 252: 351 + 253: 352 + 254: 353 + 255: 354 + 256: 355 + 257: 356 + 258: 357 + 259: 358 + 260: 359 + 261: 360 + 262: 361 + 263: 362 + 264: 363 + 265: 364 + 266: 365 + 267: 366 + 268: 367 + 269: 368 + 270: 369 + 271: 370 + 272: 371 + 273: 372 + 274: 373 + 275: 374 + 276: 375 + 277: 376 + 278: 377 + 279: 378 + 280: 379 + 281: 380 + 282: 381 + 283: 382 + 284: 383 + 285: 384 + 286: 385 + 287: 386 + 288: 387 + 289: 388 + 290: 389 + 291: 390 + 292: 391 + 293: 392 + 294: 393 + 295: 394 + 296: 395 + 297: 396 + 298: 397 + 299: 398 + 300: 399 + 301: 400 + 302: 401 + 303: 402 + 304: 403 + 305: 404 + 306: 405 + 307: 406 + 308: 407 + 309: 408 + 310: 409 + 311: 410 + 312: 411 + 313: 412 + 314: 413 + 315: 414 + 316: 415 + 317: 416 + 318: 417 + 319: 418 + 320: 419 + 321: 420 + 322: 421 + 323: 422 + 324: 423 + 325: 424 + 326: 425 + 327: 426 + 328: 427 + 329: 428 + 330: 429 + 331: 430 + 332: 431 + 333: 432 + 334: 433 + 335: 434 + 336: 435 + 337: 436 + 338: 437 + 339: 438 + 340: 439 + 341: 440 + 342: 441 + 343: 442 + 344: 443 + 345: 444 + 346: 445 + 347: 446 + 348: 447 + 349: 448 + 350: 449 + 351: 450 + 352: 451 + 353: 452 + 354: 453 + 355: 454 + 356: 455 + 357: 456 + 358: 457 + 359: 458 + 360: 459 + 361: 460 + 362: 461 + 363: 462 + 364: 463 + 365: 464 + 366: 465 + 367: 466 + 368: 467 + 369: 468 + 370: 469 + 371: 470 + 372: 471 + 373: 472 + 374: 473 + 375: 474 + 376: 475 + 377: 476 + 378: 477 + 379: 478 + 380: 479 + 381: 480 + 382: 481 + 383: 482 + 384: 483 + 385: 484 + 386: 485 + 387: 486 + 388: 487 + 389: 488 + 390: 489 + 391: 490 + 392: 491 + 393: 492 + 394: 493 + 395: 494 + 396: 495 + 397: 496 + 398: 497 + 399: 498 + 400: 499 + 401: 500 + 402: 501 + 403: 502 + 404: 503 + 405: 504 + 406: 505 + 407: 506 + 408: 507 + 409: 508 + 410: 509 + 411: 510 + 412: 511 + 413: 512 + 414: 513 + 415: 514 + 416: 515 + 417: 516 + 418: 517 + 419: 518 + 420: 519 + 421: 520 + 422: 521 + 423: 522 + 424: 523 + 425: 524 + 426: 525 + 427: 526 + 428: 527 + 429: 528 + 430: 529 + 431: 530 + 432: 531 + 433: 532 + 434: 533 + 435: 534 + 436: 535 + 437: 536 + 438: 537 + 439: 538 + 440: 539 + 441: 540 + 442: 541 + 443: 542 + 444: 543 + 445: 544 + 446: 545 + 447: 546 + 448: 547 + 449: 548 + 450: 549 + 451: 550 + 452: 551 + 453: 552 + 454: 553 + 455: 554 + 456: 555 + 457: 556 + 458: 557 + 459: 558 + 460: 559 + 461: 560 + 462: 561 + 463: 562 + 464: 563 + 465: 564 + 466: 565 + 467: 566 + 468: 567 + 469: 568 + 470: 569 + 471: 570 + 472: 571 + 473: 572 + 474: 573 + 475: 574 + 476: 575 + 477: 576 + 478: 577 + 479: 578 + 480: 579 + 481: 580 + 482: 581 + 483: 582 + 484: 583 + 485: 584 + 486: 585 + 487: 586 + 488: 587 + 489: 588 + 490: 589 + 491: 590 + 492: 591 + 493: 592 + 494: 593 + 495: 594 + 496: 595 + 497: 596 + 498: 597 + 499: 598 + 500: 599 + 501: 600 + 502: 601 + 503: 602 + 504: 603 + 505: 604 + 506: 605 + 507: 606 + 508: 607 + 509: 608 + 510: 609 + 511: 610 + 512: 611 + 513: 612 + 514: 613 + 515: 614 + 516: 615 + 517: 616 + 518: 617 + 519: 618 + 520: 619 + 521: 620 + 522: 621 + 523: 622 + 524: 623 + 525: 624 + 526: 625 + 527: 626 + 528: 627 + 529: 628 + 530: 629 + 531: 630 + 532: 631 + 533: 632 + 534: 633 + 535: 634 + 536: 635 + 537: 636 + 538: 637 + 539: 638 + 540: 639 + 541: 640 + 542: 641 + 543: 642 + 544: 643 + 545: 644 + 546: 645 + 547: 646 + 548: 647 + 549: 648 + 550: 649 + 551: 650 + 552: 651 + 553: 652 + 554: 653 + 555: 654 + 556: 655 + 557: 656 + 558: 657 + 559: 658 + 560: 659 + 561: 660 + 562: 661 + 563: 662 + 564: 663 + 565: 664 + 566: 665 + 567: 666 + 568: 667 + 569: 668 + 570: 669 + 571: 670 + 572: 671 + 573: 672 + 574: 673 + 575: 674 + 576: 675 + 577: 676 + 578: 677 + 579: 678 + 580: 679 + 581: 680 + 582: 681 + 583: 682 + 584: 683 + 585: 684 + 586: 685 + 587: 686 + 588: 687 + 589: 688 + 590: 689 + 591: 690 + 592: 691 + 593: 692 + 594: 693 + 595: 694 + 596: 695 + 597: 696 + 598: 697 + 599: 698 + 600: 699 + 601: 700 + 602: 701 + 603: 702 + 604: 703 + 605: 704 + 606: 705 + 607: 706 + 608: 707 + 609: 708 + 610: 709 + 611: 710 + 612: 711 + 613: 712 + 614: 713 + 615: 714 + 616: 715 + 617: 716 + 618: 717 + 619: 718 + 620: 719 + 621: 720 + 622: 721 + 623: 722 + 624: 723 + 625: 724 + 626: 725 + 627: 726 + 628: 727 + 629: 728 + 630: 729 + 631: 730 + 632: 731 + 633: 732 + 634: 733 + 635: 734 + 636: 735 + 637: 736 + 638: 737 + 639: 738 + 640: 739 + 641: 740 + 642: 741 + 643: 742 + 644: 743 + 645: 744 + 646: 745 + 647: 746 + 648: 747 + 649: 748 + 650: 749 + 651: 750 + 652: 751 + 653: 752 + 654: 753 + 655: 754 + 656: 755 + 657: 756 + 658: 757 + 659: 758 + 660: 759 + 661: 760 + 662: 761 + 663: 762 + 664: 763 + 665: 764 + 666: 765 + 667: 766 + 668: 767 + 669: 768 + 670: 769 + 671: 770 + 672: 771 + 673: 772 + 674: 773 + 675: 774 + 676: 775 + 677: 776 + 678: 777 + 679: 778 + 680: 779 + 681: 780 + 682: 781 + 683: 782 + 684: 783 + 685: 784 + 686: 785 + 687: 786 + 688: 787 + 689: 788 + 690: 789 + 691: 790 + 692: 791 + 693: 792 + 694: 793 + 695: 794 + 696: 795 + 697: 796 + 698: 797 + 699: 798 + 700: 799 + 701: 800 + 702: 801 + 703: 802 + 704: 803 + 705: 804 + 706: 805 + 707: 806 + 708: 807 + 709: 808 + 710: 809 + 711: 810 + 712: 811 + 713: 812 + 714: 813 + 715: 814 + 716: 815 + 717: 816 + 718: 817 + 719: 818 + 720: 819 + 721: 820 + 722: 821 + 723: 822 + 724: 823 + 725: 824 + 726: 825 + 727: 826 + 728: 827 + 729: 828 + 730: 829 + 731: 830 + 732: 831 + 733: 832 + 734: 833 + 735: 834 + 736: 835 + 737: 836 + 738: 837 + 739: 838 + 740: 839 + 741: 840 + 742: 841 + 743: 842 + 744: 843 + 745: 844 + 746: 845 + 747: 846 + 748: 847 + 749: 848 + 750: 849 + 751: 850 + 752: 851 + 753: 852 + 754: 853 + 755: 854 + 756: 855 + 757: 856 + 758: 857 + 759: 858 + 760: 859 + 761: 860 + 762: 861 + 763: 862 + 764: 863 + 765: 864 + 766: 865 + 767: 866 + 768: 867 + 769: 868 + 770: 869 + 771: 870 + 772: 871 + 773: 872 + 774: 873 + 775: 874 + 776: 875 + 777: 876 + 778: 877 + 779: 878 + 780: 879 + 781: 880 + 782: 881 + 783: 882 + 784: 883 + 785: 884 + 786: 885 + 787: 886 + 788: 887 + 789: 888 + 790: 889 + 791: 890 + 792: 891 + 793: 892 + 794: 893 + 795: 894 + 796: 895 + 797: 896 + 798: 897 + 799: 898 + 800: 899 + 801: 900 + 802: 901 + 803: 902 + 804: 903 + 805: 904 + 806: 905 + 807: 906 + 808: 907 + 809: 908 + 810: 909 + 811: 910 + 812: 911 + 813: 912 + 814: 913 + 815: 914 + 816: 915 + 817: 916 + 818: 917 + 819: 918 + 820: 919 + 821: 920 + 822: 921 + 823: 922 + 824: 923 + 825: 924 + 826: 925 + 827: 926 + 828: 927 + 829: 928 + 830: 929 + 831: 930 + 832: 931 + 833: 932 + 834: 933 + 835: 934 + 836: 935 + 837: 936 + 838: 937 + 839: 938 + 840: 939 + 841: 940 + 842: 941 + 843: 942 + 844: 943 + 845: 944 + 846: 945 + 847: 946 + 848: 947 + 849: 948 + 850: 949 + 851: 950 + 852: 951 + 853: 952 + 854: 953 + 855: 954 + 856: 955 + 857: 956 + 858: 957 + 859: 958 + 860: 959 + 861: 960 + 862: 961 + 863: 962 + 864: 963 + 865: 964 + 866: 965 + 867: 966 + 868: 967 + 869: 968 + 870: 969 + 871: 970 + 872: 971 + 873: 972 + 874: 973 + 875: 974 + 876: 975 + 877: 976 + 878: 977 + 879: 978 + 880: 979 + 881: 980 + 882: 981 + 883: 982 + 884: 983 + 885: 984 + 886: 985 + 887: 986 + 888: 987 + 889: 988 + 890: 989 + 891: 990 + 892: 991 + 893: 992 + 894: 993 + 895: 994 + 896: 995 + 897: 996 + 898: 997 + 899: 998 + 900: 999 + 0: subview 'v2' + VIEW 4 rows = _B:V + 0: subview '_B' + VIEW 999 rows = _H:I _R:I + 0: 0 -1 + 1: 0 -1 + 2: -1461104643 430 + 3: 0 -1 + 4: 0 -1 + 5: 1034993658 780 + 6: 777013241 36 + 7: 608274424 551 + 8: 0 -1 + 9: -1460496394 19 + 10: 0 -1 + 11: 0 -1 + 12: 1035601907 881 + 13: 0 -1 + 14: 0 -1 + 15: 1799477232 164 + 16: 0 -1 + 17: 0 -1 + 18: 0 -1 + 19: -696012820 427 + 20: -683483157 154 + 21: 0 -1 + 22: 1800085481 265 + 23: 0 -1 + 24: -268685337 757 + 25: 0 -1 + 26: -695404571 16 + 27: -953384988 296 + 28: 0 -1 + 29: 338980834 282 + 30: 81000417 562 + 31: 1373974496 137 + 32: -1987770401 30 + 33: -694796322 629 + 34: 0 -1 + 35: 0 -1 + 36: 339589083 383 + 37: 0 -1 + 38: 0 -1 + 39: 1103464408 690 + 40: 0 -1 + 41: -965306410 158 + 42: 0 -1 + 43: 69687252 13 + 44: 1530791891 508 + 45: 0 -1 + 46: 1104072657 791 + 47: 0 -1 + 48: -964698161 259 + 49: 0 -1 + 50: 0 -1 + 51: -1649397812 822 + 52: 0 -1 + 53: 0 -1 + 54: 846700489 148 + 55: 677961672 663 + 56: -1222070329 640 + 57: -1390809146 131 + 58: 0 -1 + 59: 0 -1 + 60: 0 -1 + 61: -1999083582 505 + 62: 0 -1 + 63: 1869164480 276 + 64: 0 -1 + 65: -199606338 768 + 66: 0 -1 + 67: -626325572 539 + 68: 834779067 10 + 69: 576798650 290 + 70: 1869772729 377 + 71: 0 -1 + 72: -198998089 869 + 73: 0 -1 + 74: 0 -1 + 75: -883697740 408 + 76: 0 -1 + 77: 0 -1 + 78: 1599262641 418 + 79: 0 -1 + 80: -1918083153 142 + 81: 0 -1 + 82: 565485485 253 + 83: 0 -1 + 84: 0 -1 + 85: 1599870890 7 + 86: 0 -1 + 87: 0 -1 + 88: 0 -1 + 89: -895619162 270 + 90: -1153599579 550 + 91: 139374500 125 + 92: 0 -1 + 93: 1342498722 900 + 94: 0 -1 + 95: -1930612832 415 + 96: -895010913 371 + 97: 0 -1 + 98: 0 -1 + 99: -131135588 678 + 100: -1503285349 745 + 101: 0 -1 + 102: -1930004583 4 + 103: 0 -1 + 104: 296191895 496 + 105: 0 -1 + 106: -130527339 779 + 107: 0 -1 + 108: 0 -1 + 109: -1929396334 617 + 110: 0 -1 + 111: 0 -1 + 112: 0 -1 + 113: 0 -1 + 114: -387899507 136 + 115: -556638324 651 + 116: 0 -1 + 117: 646485898 402 + 118: 0 -1 + 119: -1164912760 1 + 120: 0 -1 + 121: 1061283718 493 + 122: 0 -1 + 123: 634564484 264 + 124: 2095669123 247 + 125: 0 -1 + 126: 1668949889 530 + 127: 1410969472 810 + 128: 0 -1 + 129: 0 -1 + 130: 635172733 365 + 131: 0 -1 + 132: 1838296955 628 + 133: 1669558138 119 + 134: 0 -1 + 135: 0 -1 + 136: 0 -1 + 137: 0 -1 + 138: -1083912331 662 + 139: 0 -1 + 140: 0 -1 + 141: -1434206350 756 + 142: 0 -1 + 143: -1860925584 527 + 144: 0 -1 + 145: 0 -1 + 146: 0 -1 + 147: -61448340 790 + 148: -1433598101 857 + 149: 0 -1 + 150: 0 -1 + 151: -2118297752 396 + 152: 0 -1 + 153: 107898726 888 + 154: -60840091 891 + 155: 0 -1 + 156: 1142284131 130 + 157: 0 -1 + 158: -669114527 241 + 159: 0 -1 + 160: 1557081951 733 + 161: 0 -1 + 162: 0 -1 + 163: 0 -1 + 164: 0 -1 + 165: -2130219174 258 + 166: 0 -1 + 167: -1095225512 113 + 168: 0 -1 + 169: 1130970966 605 + 170: 0 -1 + 171: 0 -1 + 172: -2129610925 359 + 173: 0 -1 + 174: 0 -1 + 175: 0 -1 + 176: 0 -1 + 177: -588114098 390 + 178: 0 -1 + 179: 0 -1 + 180: -938408117 484 + 181: 0 -1 + 182: -1365127351 767 + 183: 0 -1 + 184: 861069127 235 + 185: 0 -1 + 186: 434349893 518 + 187: 176369476 798 + 188: 0 -1 + 189: 0 -1 + 190: -1622499519 124 + 191: -1791238336 639 + 192: 603696959 616 + 193: 434958142 107 + 194: 0 -1 + 195: 0 -1 + 196: 0 -1 + 197: -173316294 481 + 198: 0 -1 + 199: -600035528 252 + 200: 0 -1 + 201: 1626160950 744 + 202: 0 -1 + 203: 1199441716 515 + 204: 0 -1 + 205: 0 -1 + 206: -599427279 353 + 207: 0 -1 + 208: 1626769199 845 + 209: 0 -1 + 210: 0 -1 + 211: 942069548 384 + 212: 0 -1 + 213: 0 -1 + 214: 1976454953 650 + 215: 0 -1 + 216: -92315865 118 + 217: 0 -1 + 218: -1903714523 229 + 219: 0 -1 + 220: 0 -1 + 221: 0 -1 + 222: 0 -1 + 223: -1296048352 778 + 224: 0 -1 + 225: 930148126 246 + 226: 0 -1 + 227: 1965141788 101 + 228: 0 -1 + 229: -1126701286 876 + 230: -1295440103 879 + 231: 0 -1 + 232: 930756375 347 + 233: 0 -1 + 234: 0 -1 + 235: 0 -1 + 236: 322481939 721 + 237: 0 -1 + 238: 0 -1 + 239: 0 -1 + 240: 2121959183 472 + 241: 0 -1 + 242: 1695239949 755 + 243: 0 -1 + 244: 0 -1 + 245: -103629046 593 + 246: 0 -1 + 247: 0 -1 + 248: 0 -1 + 249: 0 -1 + 250: 1437867781 112 + 251: 1269128964 627 + 252: 0 -1 + 253: -1822714110 378 + 254: 0 -1 + 255: 0 -1 + 256: 0 -1 + 257: -1407916290 469 + 258: 0 -1 + 259: -1834635524 240 + 260: -373530885 223 + 261: 0 -1 + 262: -800250119 506 + 263: 0 -1 + 264: 0 -1 + 265: 0 -1 + 266: -1834027275 341 + 267: 0 -1 + 268: -630903053 604 + 269: -799641870 95 + 270: 0 -1 + 271: 0 -1 + 272: 0 -1 + 273: 0 -1 + 274: 741854957 638 + 275: 0 -1 + 276: 0 -1 + 277: 391560938 732 + 278: 0 -1 + 279: -35158296 503 + 280: 0 -1 + 281: 0 -1 + 282: 0 -1 + 283: 1764318948 766 + 284: 392169187 833 + 285: 0 -1 + 286: 0 -1 + 287: -292530464 372 + 288: 0 -1 + 289: 1933666014 864 + 290: 1764927197 867 + 291: 0 -1 + 292: -1326915877 106 + 293: 0 -1 + 294: 1156652761 217 + 295: 0 -1 + 296: -912118057 709 + 297: 0 -1 + 298: 0 -1 + 299: 0 -1 + 300: 0 -1 + 301: -304451886 234 + 302: 0 -1 + 303: 730541776 89 + 304: 0 -1 + 305: -1338229042 581 + 306: 0 -1 + 307: 0 -1 + 308: -303843637 335 + 309: 0 -1 + 310: 0 -1 + 311: -988543288 898 + 312: 0 -1 + 313: 1237653190 366 + 314: 0 -1 + 315: 0 -1 + 316: 887359171 460 + 317: 0 -1 + 318: 460639937 743 + 319: 0 -1 + 320: -1608130881 211 + 321: 0 -1 + 322: -2034850115 494 + 323: 0 -1 + 324: 0 -1 + 325: 0 -1 + 326: 203267769 100 + 327: 34528952 615 + 328: -1865503049 592 + 329: -2034241866 83 + 330: 0 -1 + 331: 0 -1 + 332: 0 -1 + 333: 1652450994 457 + 334: 0 -1 + 335: 1225731760 228 + 336: 0 -1 + 337: -843039058 720 + 338: 0 -1 + 339: -1269758292 491 + 340: 0 -1 + 341: 0 -1 + 342: 1226340009 329 + 343: 0 -1 + 344: -842430809 821 + 345: 0 -1 + 346: 0 -1 + 347: -1527130460 360 + 348: 0 -1 + 349: 0 -1 + 350: -492745055 626 + 351: 0 -1 + 352: 1733451423 94 + 353: 0 -1 + 354: -77947235 205 + 355: 0 -1 + 356: 0 -1 + 357: 0 -1 + 358: 0 -1 + 359: 529718936 754 + 360: 0 -1 + 361: -1539051882 222 + 362: 0 -1 + 363: -504058220 77 + 364: 0 -1 + 365: 699066002 852 + 366: 530327185 855 + 367: 0 -1 + 368: -1538443633 323 + 369: 0 -1 + 370: 0 -1 + 371: 2071824012 886 + 372: -2146718069 697 + 373: 0 -1 + 374: 0 -1 + 375: 0 -1 + 376: -347240825 448 + 377: 0 -1 + 378: -773960059 731 + 379: 0 -1 + 380: 0 -1 + 381: 1722138242 569 + 382: 0 -1 + 383: 0 -1 + 384: 0 -1 + 385: 0 -1 + 386: -1031332227 88 + 387: -1200071044 603 + 388: 0 -1 + 389: 3053178 354 + 390: 0 -1 + 391: 0 -1 + 392: 0 -1 + 393: 417850998 445 + 394: 0 -1 + 395: -8868236 216 + 396: 1452236403 199 + 397: 0 -1 + 398: 1025517169 482 + 399: 0 -1 + 400: 0 -1 + 401: 0 -1 + 402: -8259987 317 + 403: 0 -1 + 404: 1194864235 580 + 405: 1026125418 71 + 406: 0 -1 + 407: 0 -1 + 408: 0 -1 + 409: 0 -1 + 410: -1727345051 614 + 411: 0 -1 + 412: 0 -1 + 413: -2077639070 708 + 414: 0 -1 + 415: 1790608992 479 + 416: 0 -1 + 417: 0 -1 + 418: 0 -1 + 419: -704881060 742 + 420: -2077030821 809 + 421: 0 -1 + 422: 0 -1 + 423: 1533236824 348 + 424: 0 -1 + 425: -535533994 840 + 426: -704272811 843 + 427: 0 -1 + 428: 498851411 82 + 429: 0 -1 + 430: -1312547247 193 + 431: 0 -1 + 432: 913649231 685 + 433: 0 -1 + 434: 0 -1 + 435: 0 -1 + 436: 0 -1 + 437: 1521315402 210 + 438: 0 -1 + 439: -1738658232 65 + 440: 0 -1 + 441: 487538246 557 + 442: 0 -1 + 443: 0 -1 + 444: 1521923651 311 + 445: 0 -1 + 446: 0 -1 + 447: 837224000 874 + 448: 0 -1 + 449: -1231546818 342 + 450: 0 -1 + 451: 0 -1 + 452: -1581840837 436 + 453: 0 -1 + 454: -2008560071 719 + 455: 0 -1 + 456: 217636407 187 + 457: 0 -1 + 458: -209082827 470 + 459: 0 -1 + 460: 0 -1 + 461: 0 -1 + 462: 2029035057 76 + 463: 1860296240 591 + 464: -39735761 568 + 465: -208474578 59 + 466: 0 -1 + 467: 0 -1 + 468: 0 -1 + 469: -816749014 433 + 470: 0 -1 + 471: -1243468248 204 + 472: 0 -1 + 473: 982728230 696 + 474: 0 -1 + 475: 556008996 467 + 476: 0 -1 + 477: 0 -1 + 478: -1242859999 305 + 479: 0 -1 + 480: 983336479 797 + 481: 0 -1 + 482: 0 -1 + 483: 298636828 336 + 484: 0 -1 + 485: 0 -1 + 486: 1333022233 602 + 487: 0 -1 + 488: -735748585 70 + 489: 0 -1 + 490: 1747820053 181 + 491: 0 -1 + 492: 0 -1 + 493: 0 -1 + 494: 0 -1 + 495: -1939481072 730 + 496: 0 -1 + 497: 286715406 198 + 498: 0 -1 + 499: 1321709068 53 + 500: 0 -1 + 501: -1770134006 828 + 502: -1938872823 831 + 503: 0 -1 + 504: 287323655 299 + 505: 0 -1 + 506: 0 -1 + 507: -397375996 862 + 508: -320950781 673 + 509: 0 -1 + 510: 0 -1 + 511: 0 -1 + 512: 1478526463 424 + 513: 0 -1 + 514: 1051807229 707 + 515: 0 -1 + 516: 0 -1 + 517: -747061766 545 + 518: 0 -1 + 519: 0 -1 + 520: 0 -1 + 521: 0 -1 + 522: 794435061 64 + 523: 625696244 579 + 524: 0 -1 + 525: 1828820466 330 + 526: 0 -1 + 527: 0 -1 + 528: 0 -1 + 529: -2051349010 421 + 530: 0 -1 + 531: 1816899052 192 + 532: -1016963605 175 + 533: 0 -1 + 534: -1443682839 458 + 535: 0 -1 + 536: 0 -1 + 537: 0 -1 + 538: 1817507301 293 + 539: 0 -1 + 540: -1274335773 556 + 541: -1443074590 47 + 542: 0 -1 + 543: 0 -1 + 544: 0 -1 + 545: 0 -1 + 546: 98422237 590 + 547: 0 -1 + 548: 0 -1 + 549: -251871782 684 + 550: 0 -1 + 551: -678591016 455 + 552: 357010903 411 + 553: 0 -1 + 554: 0 -1 + 555: 1120886228 718 + 556: -251263533 785 + 557: 0 -1 + 558: 0 -1 + 559: -935963184 324 + 560: 1548213711 536 + 561: 1290233294 816 + 562: 1121494477 819 + 563: 0 -1 + 564: -1970348597 58 + 565: -677374518 657 + 566: 513220041 169 + 567: 0 -1 + 568: 0 -1 + 569: 0 -1 + 570: 0 -1 + 571: 0 -1 + 572: 0 -1 + 573: -947884606 186 + 574: 0 -1 + 575: 87109056 41 + 576: 0 -1 + 577: -1981661762 533 + 578: 0 -1 + 579: 0 -1 + 580: -947276357 287 + 581: 0 -1 + 582: 0 -1 + 583: -1631976008 850 + 584: 0 -1 + 585: 594220470 318 + 586: 1887194549 405 + 587: 0 -1 + 588: -1204648525 668 + 589: 0 -1 + 590: -182792783 695 + 591: 0 -1 + 592: 2043403695 163 + 593: 0 -1 + 594: 1616684461 446 + 595: 0 -1 + 596: 0 -1 + 597: -182184534 796 + 598: -440164951 52 + 599: -608903768 567 + 600: 1786031527 544 + 601: 1617292710 35 + 602: 0 -1 + 603: 0 -1 + 604: -181576285 897 + 605: 0 -1 + 606: 0 -1 + 607: 582299040 180 + 608: 0 -1 + 609: -1486471778 672 + 610: 0 -1 + 611: -1913191012 443 + 612: 0 -1 + 613: 0 -1 + 614: 582907289 281 + 615: 0 -1 + 616: -1485863529 773 + 617: 0 -1 + 618: 0 -1 + 619: 2124404116 312 + 620: 0 -1 + 621: 0 -1 + 622: -1136177775 578 + 623: 156796304 153 + 624: 1090018703 46 + 625: -1911974514 645 + 626: -721379955 157 + 627: 0 -1 + 628: -877589109 399 + 629: 0 -1 + 630: 0 -1 + 631: -113713784 706 + 632: 0 -1 + 633: 2112482694 174 + 634: 0 -1 + 635: -1147490940 29 + 636: 313613699 524 + 637: 55633282 804 + 638: -113105535 807 + 639: 0 -1 + 640: 2113090943 275 + 641: 0 -1 + 642: 0 -1 + 643: 1428391292 838 + 644: 0 -1 + 645: 0 -1 + 646: 0 -1 + 647: 0 -1 + 648: 1855718775 656 + 649: 1686979958 147 + 650: -1417392779 683 + 651: 0 -1 + 652: 0 -1 + 653: 1078705522 521 + 654: 0 -1 + 655: 0 -1 + 656: 0 -1 + 657: -1416784530 784 + 658: -1674764947 40 + 659: -1843503764 555 + 660: -382399125 26 + 661: -640379542 306 + 662: 652594537 393 + 663: 0 -1 + 664: -1416176281 885 + 665: 0 -1 + 666: 0 -1 + 667: -652300956 168 + 668: 0 -1 + 669: 0 -1 + 670: 382084449 434 + 671: 0 -1 + 672: -1686686369 0 + 673: 0 -1 + 674: -651692707 269 + 675: 0 -1 + 676: 0 -1 + 677: 382692698 23 + 678: 0 -1 + 679: 0 -1 + 680: 0 -1 + 681: 0 -1 + 682: 1924189525 566 + 683: -1077803692 141 + 684: 0 -1 + 685: 0 -1 + 686: 0 -1 + 687: 1147176272 431 + 688: -2112189105 387 + 689: 0 -1 + 690: 0 -1 + 691: -1348313780 694 + 692: 1574503755 761 + 693: 0 -1 + 694: 1147784521 20 + 695: 889804104 300 + 696: -920986297 512 + 697: 0 -1 + 698: -1347705531 795 + 699: 0 -1 + 700: -144581309 34 + 701: 1148392770 633 + 702: 0 -1 + 703: 0 -1 + 704: 0 -1 + 705: 0 -1 + 706: -1605077699 152 + 707: -1773816516 667 + 708: 0 -1 + 709: 877882682 162 + 710: 0 -1 + 711: 1912876344 17 + 712: 0 -1 + 713: -155894474 509 + 714: 0 -1 + 715: -582613708 280 + 716: 878490931 263 + 717: 0 -1 + 718: 0 -1 + 719: 193791280 826 + 720: -1616999121 14 + 721: -1874979538 294 + 722: -582005459 381 + 723: 0 -1 + 724: 621118763 644 + 725: 452379946 135 + 726: 1642974505 671 + 727: 0 -1 + 728: 0 -1 + 729: 0 -1 + 730: -852515547 422 + 731: 0 -1 + 732: 0 -1 + 733: 1643582754 772 + 734: 0 -1 + 735: 1216863520 543 + 736: 0 -1 + 737: -851907298 11 + 738: 0 -1 + 739: 0 -1 + 740: 1644191003 873 + 741: 0 -1 + 742: 0 -1 + 743: 959491352 412 + 744: 0 -1 + 745: 0 -1 + 746: 0 -1 + 747: -87423724 419 + 748: -74894061 146 + 749: 0 -1 + 750: -1886292719 257 + 751: 0 -1 + 752: 339903759 749 + 753: 0 -1 + 754: -86815475 8 + 755: -344795892 288 + 756: 0 -1 + 757: 947569930 274 + 758: 689589513 554 + 759: 1982563592 129 + 760: 0 -1 + 761: -86207226 621 + 762: 0 -1 + 763: 0 -1 + 764: 948178179 375 + 765: 0 -1 + 766: 0 -1 + 767: 1712053504 682 + 768: 0 -1 + 769: -1805292290 406 + 770: 0 -1 + 771: 678276348 5 + 772: 2139380987 500 + 773: 0 -1 + 774: 1712661753 783 + 775: 0 -1 + 776: -356109065 251 + 777: 0 -1 + 778: -782828299 534 + 779: -1040808716 814 + 780: 0 -1 + 781: 0 -1 + 782: 1455289585 140 + 783: 1286550768 655 + 784: -613481233 632 + 785: -782220050 123 + 786: 0 -1 + 787: 0 -1 + 788: 0 -1 + 789: -1390494486 497 + 790: 0 -1 + 791: -1817213720 268 + 792: 0 -1 + 793: 408982758 760 + 794: 0 -1 + 795: -17736476 531 + 796: 1443368163 2 + 797: 0 -1 + 798: -1816605471 369 + 799: 0 -1 + 800: 409591007 861 + 801: 0 -1 + 802: 0 -1 + 803: -275108644 400 + 804: 0 -1 + 805: 0 -1 + 806: 759276761 666 + 807: 0 -1 + 808: -1309494057 134 + 809: 0 -1 + 810: 1174074581 245 + 811: 0 -1 + 812: 0 -1 + 813: 0 -1 + 814: 0 -1 + 815: 1781740752 794 + 816: 0 -1 + 817: -287030066 262 + 818: -545010483 542 + 819: 747963596 117 + 820: 0 -1 + 821: 1951087818 892 + 822: 1782349001 895 + 823: 0 -1 + 824: -286421817 363 + 825: 0 -1 + 826: 0 -1 + 827: 477453508 670 + 828: -894696253 737 + 829: 0 -1 + 830: 0 -1 + 831: 0 -1 + 832: 904780991 488 + 833: 0 -1 + 834: 478061757 771 + 835: 0 -1 + 836: 0 -1 + 837: -1320807238 609 + 838: 0 -1 + 839: 0 -1 + 840: 0 -1 + 841: 0 -1 + 842: 220689589 128 + 843: 51950772 643 + 844: 0 -1 + 845: 1255074994 394 + 846: 0 -1 + 847: 0 -1 + 848: 0 -1 + 849: 1669872814 485 + 850: 0 -1 + 851: 1243153580 256 + 852: -1590709077 239 + 853: 0 -1 + 854: -2017428311 522 + 855: 2019558568 802 + 856: 0 -1 + 857: 0 -1 + 858: 1243761829 357 + 859: 0 -1 + 860: -1848081245 620 + 861: -2016820062 111 + 862: 0 -1 + 863: 0 -1 + 864: 0 -1 + 865: 0 -1 + 866: -475323235 654 + 867: 0 -1 + 868: 0 -1 + 869: -825617254 748 + 870: 0 -1 + 871: -1252336488 519 + 872: 0 -1 + 873: 0 -1 + 874: 0 -1 + 875: 547140756 782 + 876: -825009005 849 + 877: 0 -1 + 878: 0 -1 + 879: -1509708656 388 + 880: 0 -1 + 881: 716487822 880 + 882: 547749005 883 + 883: 0 -1 + 884: 1750873227 122 + 885: 0 -1 + 886: -60525431 233 + 887: 0 -1 + 888: -2129296249 725 + 889: 0 -1 + 890: 0 -1 + 891: 0 -1 + 892: 0 -1 + 893: -1521630078 250 + 894: 0 -1 + 895: -486636416 105 + 896: 0 -1 + 897: 1739560062 597 + 898: 0 -1 + 899: 0 -1 + 900: -1521021829 351 + 901: 0 -1 + 902: 0 -1 + 903: 0 -1 + 904: 0 -1 + 905: 20474998 382 + 906: 0 -1 + 907: 0 -1 + 908: -329819021 476 + 909: 0 -1 + 910: -756538255 759 + 911: 0 -1 + 912: 1469658223 227 + 913: 0 -1 + 914: 1042938989 510 + 915: 0 -1 + 916: 0 -1 + 917: 0 -1 + 918: -1013910423 116 + 919: -1182649240 631 + 920: 1212286055 608 + 921: 1043547238 99 + 922: 0 -1 + 923: 0 -1 + 924: 0 -1 + 925: 435272802 473 + 926: 0 -1 + 927: 8553568 244 + 928: 0 -1 + 929: -2060217250 736 + 930: 0 -1 + 931: 1808030812 507 + 932: 0 -1 + 933: 0 -1 + 934: 9161817 345 + 935: 0 -1 + 936: -2059609001 837 + 937: 0 -1 + 938: 0 -1 + 939: 1550658644 376 + 940: 0 -1 + 941: 0 -1 + 942: -1709923247 642 + 943: 0 -1 + 944: 516273231 110 + 945: 0 -1 + 946: -1295125427 221 + 947: 0 -1 + 948: 0 -1 + 949: 0 -1 + 950: 0 -1 + 951: -687459256 770 + 952: 0 -1 + 953: 1538737222 238 + 954: 0 -1 + 955: -1721236412 93 + 956: 0 -1 + 957: -518112190 868 + 958: -686851007 871 + 959: 0 -1 + 960: 1539345471 339 + 961: 0 -1 + 962: 0 -1 + 963: 0 -1 + 964: 931071035 713 + 965: 0 -1 + 966: 0 -1 + 967: 0 -1 + 968: -1564419017 464 + 969: 0 -1 + 970: -1991138251 747 + 971: 0 -1 + 972: 0 -1 + 973: 504960050 585 + 974: 0 -1 + 975: 0 -1 + 976: 0 -1 + 977: 0 -1 + 978: 2046456877 104 + 979: 1877718060 619 + 980: 0 -1 + 981: -1214125014 370 + 982: 0 -1 + 983: 0 -1 + 984: 0 -1 + 985: -799327194 461 + 986: 0 -1 + 987: -1226046428 232 + 988: 235058211 215 + 989: 0 -1 + 990: -191661023 498 + 991: 0 -1 + 992: 0 -1 + 993: 0 -1 + 994: -1225438179 333 + 995: 0 -1 + 996: -22313957 596 + 997: -191052774 87 + 998: 0 -1 + 1: subview '_B' + VIEW 47 rows = _H:I _R:I + 0: 0 -1 + 1: 0 -1 + 2: 1350444053 630 + 3: 0 -1 + 4: 0 -1 + 5: 1000150034 724 + 6: 0 -1 + 7: 573430800 495 + 8: 0 -1 + 9: 0 -1 + 10: 0 -1 + 11: -1922059252 758 + 12: 1000758283 825 + 13: 0 -1 + 14: 0 -1 + 15: 316058632 364 + 16: 0 -1 + 17: -1752712186 856 + 18: -1921451003 859 + 19: 0 -1 + 20: -718326781 98 + 21: 0 -1 + 22: 1765241857 209 + 23: 0 -1 + 24: -303528961 701 + 25: 0 -1 + 26: 0 -1 + 27: 0 -1 + 28: 0 -1 + 29: 304137210 226 + 30: 0 -1 + 31: 1339130872 81 + 32: 0 -1 + 33: -729639946 573 + 34: 0 -1 + 35: 0 -1 + 36: 304745459 327 + 37: 0 -1 + 38: 0 -1 + 39: -379954192 890 + 40: 0 -1 + 41: 1846242286 358 + 42: 0 -1 + 43: 0 -1 + 44: 1495948267 452 + 45: 0 -1 + 46: 1069229033 735 + 2: subview '_B' + VIEW 1001 rows = _H:I _R:I + 0: -999541785 203 + 1: 0 -1 + 2: -1426261019 486 + 3: 0 -1 + 4: 0 -1 + 5: 0 -1 + 6: 811856865 92 + 7: 643118048 607 + 8: -1256913953 584 + 9: -1425652770 75 + 10: 0 -1 + 11: 0 -1 + 12: 0 -1 + 13: -2033927206 449 + 14: 0 -1 + 15: 1834320856 220 + 16: 0 -1 + 17: -234449962 712 + 18: 0 -1 + 19: -661169196 483 + 20: 0 -1 + 21: 0 -1 + 22: 1834929105 321 + 23: 0 -1 + 24: -233841713 813 + 25: 0 -1 + 26: 0 -1 + 27: -918541364 352 + 28: 0 -1 + 29: 0 -1 + 30: 115844041 618 + 31: 0 -1 + 32: -1952926777 86 + 33: 0 -1 + 34: 530641861 197 + 35: 0 -1 + 36: 0 -1 + 37: 0 -1 + 38: 0 -1 + 39: 1138308032 746 + 40: 0 -1 + 41: -930462786 214 + 42: 0 -1 + 43: 104530876 69 + 44: 0 -1 + 45: 1307655098 844 + 46: 1138916281 847 + 47: 0 -1 + 48: -929854537 315 + 49: 0 -1 + 50: 0 -1 + 51: -1614554188 878 + 52: -1538128973 689 + 53: 0 -1 + 54: 0 -1 + 55: 0 -1 + 56: 261348271 440 + 57: 0 -1 + 58: -165370963 723 + 59: 0 -1 + 60: 0 -1 + 61: -1964239958 561 + 62: 0 -1 + 63: 0 -1 + 64: 0 -1 + 65: 0 -1 + 66: -422743131 80 + 67: -591481948 595 + 68: 0 -1 + 69: 611642274 346 + 70: 0 -1 + 71: 0 -1 + 72: 0 -1 + 73: 1026440094 437 + 74: 0 -1 + 75: 599720860 208 + 76: 2060825499 191 + 77: 0 -1 + 78: 1634106265 474 + 79: 0 -1 + 80: 0 -1 + 81: 0 -1 + 82: 600329109 309 + 83: 0 -1 + 84: 1803453331 572 + 85: 1634714514 63 + 86: 0 -1 + 87: 0 -1 + 88: 0 -1 + 89: 0 -1 + 90: -1118755955 606 + 91: 0 -1 + 92: 0 -1 + 93: -1469049974 700 + 94: 0 -1 + 95: -1895769208 471 + 96: 0 -1 + 97: 0 -1 + 98: 0 -1 + 99: -96291964 734 + 100: -1468441725 801 + 101: 0 -1 + 102: 0 -1 + 103: 2141825920 340 + 104: 0 -1 + 105: 73055102 832 + 106: -95683715 835 + 107: 0 -1 + 108: 1107440507 74 + 109: 0 -1 + 110: -703958151 185 + 111: 0 -1 + 112: 1522238327 677 + 113: 0 -1 + 114: 0 -1 + 115: 0 -1 + 116: 0 -1 + 117: 2129904498 202 + 118: 0 -1 + 119: -1130069136 57 + 120: 0 -1 + 121: 1096127342 549 + 122: 0 -1 + 123: 0 -1 + 124: 2130512747 303 + 125: 0 -1 + 126: 0 -1 + 127: 1445813096 866 + 128: 0 -1 + 129: -622957722 334 + 130: 0 -1 + 131: 0 -1 + 132: -973251741 428 + 133: 0 -1 + 134: -1399970975 711 + 135: 0 -1 + 136: 826225503 179 + 137: 0 -1 + 138: 399506269 462 + 139: 0 -1 + 140: 0 -1 + 141: 0 -1 + 142: -1657343143 68 + 143: -1826081960 583 + 144: 568853335 560 + 145: 400114518 51 + 146: 0 -1 + 147: 0 -1 + 148: 0 -1 + 149: -208159918 425 + 150: 0 -1 + 151: -634879152 196 + 152: 0 -1 + 153: 1591317326 688 + 154: 0 -1 + 155: 1164598092 459 + 156: 0 -1 + 157: 0 -1 + 158: -634270903 297 + 159: 0 -1 + 160: 1591925575 789 + 161: 0 -1 + 162: 0 -1 + 163: 907225924 328 + 164: 0 -1 + 165: 0 -1 + 166: 1941611329 594 + 167: 0 -1 + 168: -127159489 62 + 169: 1165814590 661 + 170: -1938558147 173 + 171: 0 -1 + 172: 0 -1 + 173: 0 -1 + 174: 0 -1 + 175: -1330891976 722 + 176: 0 -1 + 177: 895304502 190 + 178: 0 -1 + 179: 1930298164 45 + 180: -903564493 540 + 181: -1161544910 820 + 182: -1330283727 823 + 183: 0 -1 + 184: 895912751 291 + 185: 0 -1 + 186: 0 -1 + 187: 211213100 854 + 188: 0 -1 + 189: 0 -1 + 190: 0 -1 + 191: 0 -1 + 192: 2087115559 416 + 193: 0 -1 + 194: 1660396325 699 + 195: 0 -1 + 196: 0 -1 + 197: -138472670 537 + 198: 0 -1 + 199: 0 -1 + 200: 0 -1 + 201: 0 -1 + 202: 1403024157 56 + 203: 1234285340 571 + 204: 0 -1 + 205: -1857557734 322 + 206: -564583655 409 + 207: 0 -1 + 208: 0 -1 + 209: -1442759914 413 + 210: 0 -1 + 211: -1869479148 184 + 212: -408374509 167 + 213: 0 -1 + 214: -835093743 450 + 215: 0 -1 + 216: 0 -1 + 217: 0 -1 + 218: -1868870899 285 + 219: 0 -1 + 220: -665746677 548 + 221: -834485494 39 + 222: 0 -1 + 223: 0 -1 + 224: 0 -1 + 225: 0 -1 + 226: 707011333 582 + 227: 0 -1 + 228: 0 -1 + 229: 356717314 676 + 230: 0 -1 + 231: -70001920 447 + 232: 965599999 403 + 233: 0 -1 + 234: 0 -1 + 235: 1729475324 710 + 236: 357325563 777 + 237: 0 -1 + 238: 0 -1 + 239: -327374088 316 + 240: -2138164489 528 + 241: 1898822390 808 + 242: 1730083573 811 + 243: 0 -1 + 244: -1361759501 50 + 245: -68785422 649 + 246: 1121809137 161 + 247: 0 -1 + 248: 0 -1 + 249: 0 -1 + 250: 0 -1 + 251: 0 -1 + 252: 0 -1 + 253: -339295510 178 + 254: 0 -1 + 255: 695698152 33 + 256: 0 -1 + 257: -1373072666 525 + 258: 0 -1 + 259: 0 -1 + 260: -338687261 279 + 261: 0 -1 + 262: 0 -1 + 263: -1023386912 842 + 264: 0 -1 + 265: 1202809566 310 + 266: -1799183651 397 + 267: 0 -1 + 268: -596059429 660 + 269: -764798246 151 + 270: 425796313 687 + 271: 0 -1 + 272: 0 -1 + 273: 0 -1 + 274: -2069693739 438 + 275: 0 -1 + 276: 0 -1 + 277: 426404562 788 + 278: 168424145 44 + 279: -314672 559 + 280: 0 -1 + 281: -2069085490 27 + 282: 0 -1 + 283: 0 -1 + 284: 427012811 889 + 285: 0 -1 + 286: 0 -1 + 287: 1190888136 172 + 288: 0 -1 + 289: 0 -1 + 290: 0 -1 + 291: -1304601916 435 + 292: 0 -1 + 293: 0 -1 + 294: 1191496385 273 + 295: 0 -1 + 296: -877274433 765 + 297: 0 -1 + 298: -1303993667 24 + 299: -1561974084 304 + 300: 0 -1 + 301: 0 -1 + 302: -527588679 570 + 303: 765385400 145 + 304: 1698607799 38 + 305: -1303385418 637 + 306: 0 -1 + 307: 0 -1 + 308: -269000013 391 + 309: 0 -1 + 310: 0 -1 + 311: 494875312 698 + 312: 0 -1 + 313: -1573895506 166 + 314: 0 -1 + 315: -538901844 21 + 316: 922202795 516 + 317: 0 -1 + 318: 495483561 799 + 319: 0 -1 + 320: -1573287257 267 + 321: 0 -1 + 322: 0 -1 + 323: 2036980388 830 + 324: 0 -1 + 325: 0 -1 + 326: 238111393 156 + 327: 0 -1 + 328: -1830659425 648 + 329: -1999398242 139 + 330: -808803683 675 + 331: 0 -1 + 332: 0 -1 + 333: 1687294618 513 + 334: 0 -1 + 335: 1260575384 284 + 336: 0 -1 + 337: -808195434 776 + 338: -1066175851 32 + 339: -1234914668 547 + 340: 226189971 18 + 341: -31790446 298 + 342: 1261183633 385 + 343: 0 -1 + 344: -807587185 877 + 345: 0 -1 + 346: 0 -1 + 347: -43711860 160 + 348: 0 -1 + 349: 0 -1 + 350: 990673545 426 + 351: 0 -1 + 352: 1768295047 150 + 353: 0 -1 + 354: -43103611 261 + 355: 0 -1 + 356: 0 -1 + 357: 991281794 15 + 358: 0 -1 + 359: 0 -1 + 360: 0 -1 + 361: -1504208258 278 + 362: -1762188675 558 + 363: -469214596 133 + 364: 0 -1 + 365: 0 -1 + 366: 0 -1 + 367: 1755765368 423 + 368: -1503600009 379 + 369: 0 -1 + 370: 0 -1 + 371: -739724684 686 + 372: -2111874445 753 + 373: 0 -1 + 374: 1756373617 12 + 375: 1498393200 292 + 376: -312397201 504 + 377: 0 -1 + 378: -739116435 787 + 379: 0 -1 + 380: 0 -1 + 381: 1756981866 625 + 382: 0 -1 + 383: 0 -1 + 384: 0 -1 + 385: 0 -1 + 386: -996488603 144 + 387: -1165227420 659 + 388: 0 -1 + 389: 37896802 410 + 390: 0 -1 + 391: -1773501856 9 + 392: 0 -1 + 393: 452694622 501 + 394: 0 -1 + 395: 25975388 272 + 396: 1487080027 255 + 397: 0 -1 + 398: 1060360793 538 + 399: 802380376 818 + 400: -1008410025 6 + 401: -1266390442 286 + 402: 26583637 373 + 403: 0 -1 + 404: 1229707859 636 + 405: 1060969042 127 + 406: 0 -1 + 407: 0 -1 + 408: 0 -1 + 409: 0 -1 + 410: -243926451 414 + 411: 0 -1 + 412: 0 -1 + 413: -2042795446 764 + 414: 0 -1 + 415: 1825452616 535 + 416: 0 -1 + 417: -243318202 3 + 418: 0 -1 + 419: 0 -1 + 420: -2042187197 865 + 421: 0 -1 + 422: 0 -1 + 423: 1568080448 404 + 424: 0 -1 + 425: -500690370 896 + 426: -669429187 899 + 427: 0 -1 + 428: 533695035 138 + 429: 0 -1 + 430: -1277703623 249 + 431: 0 -1 + 432: 948492855 741 + 433: 0 -1 + 434: 0 -1 + 435: 0 -1 + 436: 0 -1 + 437: 1556159026 266 + 438: 1298178609 546 + 439: -1703814608 121 + 440: 0 -1 + 441: 522381870 613 + 442: 0 -1 + 443: 0 -1 + 444: 1556767275 367 + 445: 0 -1 + 446: 0 -1 + 447: -1974324696 674 + 448: 0 -1 + 449: -1196703194 398 + 450: 0 -1 + 451: 0 -1 + 452: -1546997213 492 + 453: 0 -1 + 454: -1973716447 775 + 455: 0 -1 + 456: 252480031 243 + 457: 0 -1 + 458: -174239203 526 + 459: -432219620 806 + 460: 0 -1 + 461: 0 -1 + 462: 2063878681 132 + 463: 1895139864 647 + 464: -4892137 624 + 465: -173630954 115 + 466: 0 -1 + 467: 0 -1 + 468: 0 -1 + 469: -781905390 489 + 470: 0 -1 + 471: -1208624624 260 + 472: 0 -1 + 473: 1017571854 752 + 474: 0 -1 + 475: 590852620 523 + 476: 0 -1 + 477: 0 -1 + 478: -1208016375 361 + 479: 0 -1 + 480: 1018180103 853 + 481: 0 -1 + 482: 0 -1 + 483: 333480452 392 + 484: 0 -1 + 485: 0 -1 + 486: 1367865857 658 + 487: 0 -1 + 488: -700904961 126 + 489: 0 -1 + 490: 1782663677 237 + 491: 0 -1 + 492: 0 -1 + 493: 0 -1 + 494: 0 -1 + 495: -1904637448 786 + 496: 0 -1 + 497: 321559030 254 + 498: 0 -1 + 499: 1356552692 109 + 500: 0 -1 + 501: -1735290382 884 + 502: -1904029199 887 + 503: 0 -1 + 504: 322167279 355 + 505: 0 -1 + 506: 0 -1 + 507: 0 -1 + 508: -286107157 729 + 509: 0 -1 + 510: 0 -1 + 511: 0 -1 + 512: 1513370087 480 + 513: 0 -1 + 514: 1086650853 763 + 515: 0 -1 + 516: 0 -1 + 517: -712218142 601 + 518: 0 -1 + 519: 0 -1 + 520: 0 -1 + 521: 0 -1 + 522: 829278685 120 + 523: 660539868 635 + 524: 0 -1 + 525: 1863664090 386 + 526: 0 -1 + 527: 0 -1 + 528: 0 -1 + 529: -2016505386 477 + 530: 0 -1 + 531: 1851742676 248 + 532: -982119981 231 + 533: 0 -1 + 534: -1408839215 514 + 535: 0 -1 + 536: 0 -1 + 537: 0 -1 + 538: 1852350925 349 + 539: 0 -1 + 540: -1239492149 612 + 541: -1408230966 103 + 542: 0 -1 + 543: 0 -1 + 544: 0 -1 + 545: 0 -1 + 546: 133265861 646 + 547: 0 -1 + 548: 0 -1 + 549: -217028158 740 + 550: 0 -1 + 551: -643747392 511 + 552: 0 -1 + 553: 0 -1 + 554: 0 -1 + 555: 1155729852 774 + 556: -216419909 841 + 557: 0 -1 + 558: 0 -1 + 559: -901119560 380 + 560: 0 -1 + 561: 1325076918 872 + 562: 1156338101 875 + 563: 0 -1 + 564: -1935504973 114 + 565: 0 -1 + 566: 548063665 225 + 567: 0 -1 + 568: -1520707153 717 + 569: 0 -1 + 570: 0 -1 + 571: 0 -1 + 572: 0 -1 + 573: -913040982 242 + 574: 0 -1 + 575: 121952680 97 + 576: 0 -1 + 577: -1946818138 589 + 578: 0 -1 + 579: 0 -1 + 580: -912432733 343 + 581: 0 -1 + 582: 0 -1 + 583: 0 -1 + 584: 0 -1 + 585: 629064094 374 + 586: 0 -1 + 587: 0 -1 + 588: 278770075 468 + 589: 0 -1 + 590: -147949159 751 + 591: 0 -1 + 592: 2078247319 219 + 593: 0 -1 + 594: 1651528085 502 + 595: 0 -1 + 596: 0 -1 + 597: 0 -1 + 598: -405321327 108 + 599: -574060144 623 + 600: 1820875151 600 + 601: 1652136334 91 + 602: 0 -1 + 603: 0 -1 + 604: 0 -1 + 605: 1043861898 465 + 606: 0 -1 + 607: 617142664 236 + 608: 0 -1 + 609: -1451628154 728 + 610: 0 -1 + 611: -1878347388 499 + 612: 0 -1 + 613: 0 -1 + 614: 617750913 337 + 615: 0 -1 + 616: -1451019905 829 + 617: 0 -1 + 618: 0 -1 + 619: -2135719556 368 + 620: 0 -1 + 621: 0 -1 + 622: -1101334151 634 + 623: 0 -1 + 624: 1124862327 102 + 625: 0 -1 + 626: -686536331 213 + 627: 0 -1 + 628: 0 -1 + 629: 0 -1 + 630: 0 -1 + 631: -78870160 762 + 632: 0 -1 + 633: 2147326318 230 + 634: 0 -1 + 635: -1112647316 85 + 636: 0 -1 + 637: 90476906 860 + 638: -78261911 863 + 639: 0 -1 + 640: -2147032729 331 + 641: 0 -1 + 642: 0 -1 + 643: 1463234916 894 + 644: 1539660131 705 + 645: 0 -1 + 646: 0 -1 + 647: 0 -1 + 648: -955829921 456 + 649: 0 -1 + 650: -1382549155 739 + 651: 0 -1 + 652: 0 -1 + 653: 1113549146 577 + 654: 0 -1 + 655: 0 -1 + 656: 0 -1 + 657: 0 -1 + 658: -1639921323 96 + 659: -1808660140 611 + 660: 0 -1 + 661: -605535918 362 + 662: 0 -1 + 663: 0 -1 + 664: 0 -1 + 665: -190738098 453 + 666: 0 -1 + 667: -617457332 224 + 668: 843647307 207 + 669: 0 -1 + 670: 416928073 490 + 671: 0 -1 + 672: 0 -1 + 673: 0 -1 + 674: -616849083 325 + 675: 0 -1 + 676: 586275139 588 + 677: 417536322 79 + 678: 0 -1 + 679: 0 -1 + 680: 0 -1 + 681: 0 -1 + 682: 1959033149 622 + 683: 0 -1 + 684: 0 -1 + 685: 1608739130 716 + 686: 0 -1 + 687: 1182019896 487 + 688: 0 -1 + 689: 0 -1 + 690: 0 -1 + 691: -1313470156 750 + 692: 1609347379 817 + 693: 0 -1 + 694: 0 -1 + 695: 924647728 356 + 696: 0 -1 + 697: -1144123090 848 + 698: -1312861907 851 + 699: 0 -1 + 700: -109737685 90 + 701: 0 -1 + 702: -1921136343 201 + 703: 0 -1 + 704: 305060135 693 + 705: 0 -1 + 706: 0 -1 + 707: 0 -1 + 708: 0 -1 + 709: 912726306 218 + 710: 0 -1 + 711: 1947719968 73 + 712: 0 -1 + 713: -121050850 565 + 714: 0 -1 + 715: 0 -1 + 716: 913334555 319 + 717: 0 -1 + 718: 0 -1 + 719: 228634904 882 + 720: 0 -1 + 721: -1840135914 350 + 722: 0 -1 + 723: 0 -1 + 724: 2104537363 444 + 725: 0 -1 + 726: 1677818129 727 + 727: 0 -1 + 728: -390952689 195 + 729: 0 -1 + 730: -817671923 478 + 731: 0 -1 + 732: 0 -1 + 733: 0 -1 + 734: 1420445961 84 + 735: 1251707144 599 + 736: -648324857 576 + 737: -817063674 67 + 738: 0 -1 + 739: 0 -1 + 740: 0 -1 + 741: -1425338110 441 + 742: 0 -1 + 743: -1852057344 212 + 744: 0 -1 + 745: 374139134 704 + 746: 0 -1 + 747: -52580100 475 + 748: 0 -1 + 749: 0 -1 + 750: -1851449095 313 + 751: 0 -1 + 752: 374747383 805 + 753: 0 -1 + 754: 0 -1 + 755: -309952268 344 + 756: 0 -1 + 757: 0 -1 + 758: 724433137 610 + 759: 0 -1 + 760: -1344337681 78 + 761: 0 -1 + 762: 1139230957 189 + 763: 0 -1 + 764: 0 -1 + 765: 0 -1 + 766: 0 -1 + 767: 1746897128 738 + 768: 0 -1 + 769: -321873690 206 + 770: 0 -1 + 771: 713119972 61 + 772: 0 -1 + 773: 1916244194 836 + 774: 1747505377 839 + 775: 0 -1 + 776: -321265441 307 + 777: 0 -1 + 778: 0 -1 + 779: -1005965092 870 + 780: -929539877 681 + 781: 0 -1 + 782: 0 -1 + 783: 0 -1 + 784: 869937367 432 + 785: 0 -1 + 786: 443218133 715 + 787: 0 -1 + 788: 0 -1 + 789: -1355650862 553 + 790: 0 -1 + 791: 0 -1 + 792: 0 -1 + 793: 0 -1 + 794: 185845965 72 + 795: 17107148 587 + 796: 0 -1 + 797: 1220231370 338 + 798: 0 -1 + 799: 0 -1 + 800: 0 -1 + 801: 1635029190 429 + 802: 0 -1 + 803: 1208309956 200 + 804: -1625552701 183 + 805: 0 -1 + 806: -2052271935 466 + 807: 0 -1 + 808: 0 -1 + 809: 0 -1 + 810: 1208918205 301 + 811: 0 -1 + 812: -1882924869 564 + 813: -2051663686 55 + 814: 0 -1 + 815: 0 -1 + 816: 0 -1 + 817: 0 -1 + 818: -510166859 598 + 819: 0 -1 + 820: 0 -1 + 821: -860460878 692 + 822: 0 -1 + 823: -1287180112 463 + 824: 0 -1 + 825: 0 -1 + 826: 0 -1 + 827: 512297132 726 + 828: -859852629 793 + 829: 0 -1 + 830: 0 -1 + 831: -1544552280 332 + 832: 0 -1 + 833: 681644198 824 + 834: 512905381 827 + 835: 0 -1 + 836: 1716029603 66 + 837: -1285963614 665 + 838: -95369055 177 + 839: 0 -1 + 840: 2130827423 669 + 841: 0 -1 + 842: 0 -1 + 843: 0 -1 + 844: 0 -1 + 845: -1556473702 194 + 846: 0 -1 + 847: -521480040 49 + 848: 0 -1 + 849: 1704716438 541 + 850: 0 -1 + 851: 0 -1 + 852: -1555865453 295 + 853: 0 -1 + 854: 0 -1 + 855: 2054402192 858 + 856: 0 -1 + 857: -14368626 326 + 858: 0 -1 + 859: 0 -1 + 860: -364662645 420 + 861: 0 -1 + 862: -791381879 703 + 863: 0 -1 + 864: 1434814599 171 + 865: 0 -1 + 866: 1008095365 454 + 867: 0 -1 + 868: 0 -1 + 869: 0 -1 + 870: -1048754047 60 + 871: -1217492864 575 + 872: 1177442431 552 + 873: 1008703614 43 + 874: 0 -1 + 875: 0 -1 + 876: 0 -1 + 877: 400429178 417 + 878: 0 -1 + 879: -26290056 188 + 880: 0 -1 + 881: -2095060874 680 + 882: 0 -1 + 883: 1773187188 451 + 884: 0 -1 + 885: 0 -1 + 886: -25681807 289 + 887: 0 -1 + 888: -2094452625 781 + 889: 0 -1 + 890: 0 -1 + 891: 1515815020 320 + 892: 0 -1 + 893: 0 -1 + 894: -1744766871 586 + 895: 0 -1 + 896: 481429607 54 + 897: 1774403686 653 + 898: -1329969051 165 + 899: 0 -1 + 900: -1486178205 407 + 901: 0 -1 + 902: 0 -1 + 903: -722302880 714 + 904: 0 -1 + 905: 1503893598 182 + 906: 0 -1 + 907: -1756080036 37 + 908: -294975397 532 + 909: -552955814 812 + 910: -721694631 815 + 911: 0 -1 + 912: 1504501847 283 + 913: 0 -1 + 914: 0 -1 + 915: 819802196 846 + 916: 0 -1 + 917: 0 -1 + 918: 0 -1 + 919: 0 -1 + 920: 1247129679 664 + 921: 1078390862 155 + 922: -2025981875 691 + 923: 0 -1 + 924: 0 -1 + 925: 470116426 529 + 926: 0 -1 + 927: 0 -1 + 928: 0 -1 + 929: -2025373626 792 + 930: 2011613253 48 + 931: 1842874436 563 + 932: 0 -1 + 933: -1248968638 314 + 934: 44005441 401 + 935: 0 -1 + 936: -2024765377 893 + 937: 0 -1 + 938: 0 -1 + 939: -1260890052 176 + 940: 200214587 159 + 941: 0 -1 + 942: -226504647 442 + 943: 0 -1 + 944: 0 -1 + 945: 0 -1 + 946: -1260281803 277 + 947: 0 -1 + 948: 0 -1 + 949: -225896398 31 + 950: 0 -1 + 951: 0 -1 + 952: 0 -1 + 953: 0 -1 + 954: 1315600429 574 + 955: -1686392788 149 + 956: 0 -1 + 957: 0 -1 + 958: 0 -1 + 959: 538587176 439 + 960: 1574189095 395 + 961: 0 -1 + 962: 0 -1 + 963: -1956902876 702 + 964: 965914659 769 + 965: 0 -1 + 966: 539195425 28 + 967: 281215008 308 + 968: -1529575393 520 + 969: -1787555810 800 + 970: -1956294627 803 + 971: 0 -1 + 972: -753170405 42 + 973: 539803674 641 + 974: 0 -1 + 975: 0 -1 + 976: 0 -1 + 977: 0 -1 + 978: 0 -1 + 979: 0 -1 + 980: 0 -1 + 981: 269293586 170 + 982: 0 -1 + 983: 1304287248 25 + 984: 0 -1 + 985: -764483570 517 + 986: 0 -1 + 987: 0 -1 + 988: 269901835 271 + 989: 0 -1 + 990: 0 -1 + 991: -414797816 834 + 992: 2069379079 22 + 993: 1811398662 302 + 994: -1190594555 389 + 995: 0 -1 + 996: 12529667 652 + 997: -156209150 143 + 998: 1034385409 679 + 999: 0 -1 + 1000: 2053 0 + 3: subview '_B' + VIEW 2 rows = _H:I _R:I + 0: 0 -1 + 1: 0 -1 diff --git a/akregator/src/mk4storage/metakit/tests/ok/m07.txt b/akregator/src/mk4storage/metakit/tests/ok/m07.txt new file mode 100644 index 000000000..aac9208cf --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/m07.txt @@ -0,0 +1,63 @@ +>>> All blocked view multi-deletion cases +blockdel pos 0 len 1 +blockdel pos 0 len 2 +blockdel pos 0 len 3 +blockdel pos 0 len 998 +blockdel pos 0 len 999 +blockdel pos 0 len 1000 +blockdel pos 0 len 1001 +blockdel pos 0 len 1998 +blockdel pos 0 len 1999 +blockdel pos 0 len 2000 +blockdel pos 0 len 2001 +blockdel pos 1 len 1 +blockdel pos 1 len 2 +blockdel pos 1 len 3 +blockdel pos 1 len 998 +blockdel pos 1 len 999 +blockdel pos 1 len 1000 +blockdel pos 1 len 1001 +blockdel pos 1 len 1998 +blockdel pos 1 len 1999 +blockdel pos 1 len 2000 +blockdel pos 1 len 2001 +blockdel pos 998 len 1 +blockdel pos 998 len 2 +blockdel pos 998 len 3 +blockdel pos 998 len 998 +blockdel pos 998 len 999 +blockdel pos 998 len 1000 +blockdel pos 998 len 1001 +blockdel pos 999 len 1 +blockdel pos 999 len 2 +blockdel pos 999 len 3 +blockdel pos 999 len 998 +blockdel pos 999 len 999 +blockdel pos 999 len 1000 +blockdel pos 999 len 1001 +blockdel pos 1000 len 1 +blockdel pos 1000 len 2 +blockdel pos 1000 len 3 +blockdel pos 1000 len 998 +blockdel pos 1000 len 999 +blockdel pos 1000 len 1000 +blockdel pos 1000 len 1001 +blockdel pos 1001 len 1 +blockdel pos 1001 len 2 +blockdel pos 1001 len 3 +blockdel pos 1001 len 998 +blockdel pos 1001 len 999 +blockdel pos 1001 len 1000 +blockdel pos 1001 len 1001 +blockdel pos 2998 len 1 +blockdel pos 2997 len 2 +blockdel pos 2996 len 3 +blockdel pos 2001 len 998 +blockdel pos 2000 len 999 +blockdel pos 1999 len 1000 +blockdel pos 1998 len 1001 +blockdel pos 1001 len 1998 +blockdel pos 1000 len 1999 +blockdel pos 999 len 2000 +blockdel pos 998 len 2001 +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n01.txt b/akregator/src/mk4storage/metakit/tests/ok/n01.txt new file mode 100644 index 000000000..28b19811f --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n01.txt @@ -0,0 +1,2 @@ +>>> Add to selection +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n02.txt b/akregator/src/mk4storage/metakit/tests/ok/n02.txt new file mode 100644 index 000000000..a912aae04 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n02.txt @@ -0,0 +1,2 @@ +>>> Remove from selection +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n03.txt b/akregator/src/mk4storage/metakit/tests/ok/n03.txt new file mode 100644 index 000000000..5934324cc --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n03.txt @@ -0,0 +1,2 @@ +>>> Modify into selection +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n04.txt b/akregator/src/mk4storage/metakit/tests/ok/n04.txt new file mode 100644 index 000000000..efb96017d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n04.txt @@ -0,0 +1,2 @@ +>>> Modify out of selection +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n05.txt b/akregator/src/mk4storage/metakit/tests/ok/n05.txt new file mode 100644 index 000000000..bc40bea33 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n05.txt @@ -0,0 +1,2 @@ +>>> Add to sorted +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n06.txt b/akregator/src/mk4storage/metakit/tests/ok/n06.txt new file mode 100644 index 000000000..f8c499a53 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n06.txt @@ -0,0 +1,2 @@ +>>> Remove from sorted +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n07.txt b/akregator/src/mk4storage/metakit/tests/ok/n07.txt new file mode 100644 index 000000000..87ff41217 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n07.txt @@ -0,0 +1,2 @@ +>>> New property through sort +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n08.txt b/akregator/src/mk4storage/metakit/tests/ok/n08.txt new file mode 100644 index 000000000..0939301c6 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n08.txt @@ -0,0 +1,2 @@ +>>> Nested project and select +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n09.txt b/akregator/src/mk4storage/metakit/tests/ok/n09.txt new file mode 100644 index 000000000..0732f4208 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n09.txt @@ -0,0 +1,2 @@ +>>> Multiple dependencies +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n10.txt b/akregator/src/mk4storage/metakit/tests/ok/n10.txt new file mode 100644 index 000000000..8c1f92ded --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n10.txt @@ -0,0 +1,2 @@ +>>> Modify sorted duplicates +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n11.txt b/akregator/src/mk4storage/metakit/tests/ok/n11.txt new file mode 100644 index 000000000..07d6ebddf --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n11.txt @@ -0,0 +1,2 @@ +>>> Resize compound derived view +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n12.txt b/akregator/src/mk4storage/metakit/tests/ok/n12.txt new file mode 100644 index 000000000..fa194393d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n12.txt @@ -0,0 +1,2 @@ +>>> Alter multiply derived view +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n13.txt b/akregator/src/mk4storage/metakit/tests/ok/n13.txt new file mode 100644 index 000000000..e9ba290d1 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n13.txt @@ -0,0 +1,2 @@ +>>> Project without +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n14.txt b/akregator/src/mk4storage/metakit/tests/ok/n14.txt new file mode 100644 index 000000000..2522115e5 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n14.txt @@ -0,0 +1,2 @@ +>>> Insert in non-mapped position +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/n14a.txt b/akregator/src/mk4storage/metakit/tests/ok/n14a.txt new file mode 100644 index 000000000..ef5e47ac5 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/n14a.txt @@ -0,0 +1,15 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 12 rows = p1:I + 0: 0 + 1: 1 + 2: 2 + 3: 6 + 4: 0 + 5: 1 + 6: 2 + 7: 1 + 8: 0 + 9: 1 + 10: 2 + 11: 0 diff --git a/akregator/src/mk4storage/metakit/tests/ok/r00.txt b/akregator/src/mk4storage/metakit/tests/ok/r00.txt new file mode 100644 index 000000000..c2dd0e8c5 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/r00.txt @@ -0,0 +1,2 @@ +>>> Simple insert +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/r00a.txt b/akregator/src/mk4storage/metakit/tests/ok/r00a.txt new file mode 100644 index 000000000..16475db59 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/r00a.txt @@ -0,0 +1,253 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 250 rows = p1:I + 0: 1 + 1: 2 + 2: 3 + 3: 4 + 4: 5 + 5: 6 + 6: 7 + 7: 8 + 8: 9 + 9: 10 + 10: 11 + 11: 12 + 12: 13 + 13: 14 + 14: 15 + 15: 16 + 16: 17 + 17: 18 + 18: 19 + 19: 20 + 20: 21 + 21: 22 + 22: 23 + 23: 24 + 24: 25 + 25: 26 + 26: 27 + 27: 28 + 28: 29 + 29: 30 + 30: 31 + 31: 32 + 32: 33 + 33: 34 + 34: 35 + 35: 36 + 36: 37 + 37: 38 + 38: 39 + 39: 40 + 40: 41 + 41: 42 + 42: 43 + 43: 44 + 44: 45 + 45: 46 + 46: 47 + 47: 48 + 48: 49 + 49: 50 + 50: 51 + 51: 52 + 52: 53 + 53: 54 + 54: 55 + 55: 56 + 56: 57 + 57: 58 + 58: 59 + 59: 60 + 60: 61 + 61: 62 + 62: 63 + 63: 64 + 64: 65 + 65: 66 + 66: 67 + 67: 68 + 68: 69 + 69: 70 + 70: 71 + 71: 72 + 72: 73 + 73: 74 + 74: 75 + 75: 76 + 76: 77 + 77: 78 + 78: 79 + 79: 80 + 80: 81 + 81: 82 + 82: 83 + 83: 84 + 84: 85 + 85: 86 + 86: 87 + 87: 88 + 88: 89 + 89: 90 + 90: 91 + 91: 92 + 92: 93 + 93: 94 + 94: 95 + 95: 96 + 96: 97 + 97: 98 + 98: 99 + 99: 100 + 100: 101 + 101: 102 + 102: 103 + 103: 104 + 104: 105 + 105: 106 + 106: 107 + 107: 108 + 108: 109 + 109: 110 + 110: 111 + 111: 112 + 112: 113 + 113: 114 + 114: 115 + 115: 116 + 116: 117 + 117: 118 + 118: 119 + 119: 120 + 120: 121 + 121: 122 + 122: 123 + 123: 1 + 124: 2 + 125: 3 + 126: 4 + 127: 5 + 128: 6 + 129: 7 + 130: 8 + 131: 9 + 132: 10 + 133: 11 + 134: 12 + 135: 13 + 136: 14 + 137: 15 + 138: 16 + 139: 17 + 140: 18 + 141: 19 + 142: 20 + 143: 21 + 144: 22 + 145: 23 + 146: 24 + 147: 25 + 148: 26 + 149: 27 + 150: 28 + 151: 29 + 152: 30 + 153: 31 + 154: 32 + 155: 33 + 156: 34 + 157: 35 + 158: 36 + 159: 37 + 160: 38 + 161: 39 + 162: 40 + 163: 41 + 164: 42 + 165: 43 + 166: 44 + 167: 45 + 168: 46 + 169: 47 + 170: 48 + 171: 49 + 172: 50 + 173: 51 + 174: 52 + 175: 53 + 176: 54 + 177: 55 + 178: 56 + 179: 57 + 180: 58 + 181: 59 + 182: 60 + 183: 61 + 184: 62 + 185: 63 + 186: 64 + 187: 65 + 188: 66 + 189: 67 + 190: 68 + 191: 69 + 192: 70 + 193: 71 + 194: 72 + 195: 73 + 196: 74 + 197: 75 + 198: 76 + 199: 77 + 200: 78 + 201: 79 + 202: 80 + 203: 81 + 204: 82 + 205: 83 + 206: 84 + 207: 85 + 208: 86 + 209: 87 + 210: 88 + 211: 89 + 212: 90 + 213: 91 + 214: 92 + 215: 93 + 216: 94 + 217: 95 + 218: 96 + 219: 97 + 220: 98 + 221: 99 + 222: 100 + 223: 101 + 224: 102 + 225: 103 + 226: 104 + 227: 105 + 228: 106 + 229: 107 + 230: 108 + 231: 109 + 232: 110 + 233: 111 + 234: 112 + 235: 113 + 236: 114 + 237: 115 + 238: 116 + 239: 117 + 240: 118 + 241: 119 + 242: 120 + 243: 121 + 244: 122 + 245: 123 + 246: 1 + 247: 2 + 248: 3 + 249: 4 diff --git a/akregator/src/mk4storage/metakit/tests/ok/r01.txt b/akregator/src/mk4storage/metakit/tests/ok/r01.txt new file mode 100644 index 000000000..092f02ca2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/r01.txt @@ -0,0 +1,2 @@ +>>> Simple removes +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/r01a.txt b/akregator/src/mk4storage/metakit/tests/ok/r01a.txt new file mode 100644 index 000000000..65a6851c8 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/r01a.txt @@ -0,0 +1,18 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 15 rows = p1:I + 0: 9 + 1: 10 + 2: 11 + 3: 12 + 4: 13 + 5: 14 + 6: 15 + 7: 16 + 8: 17 + 9: 18 + 10: 19 + 11: 20 + 12: 21 + 13: 22 + 14: 23 diff --git a/akregator/src/mk4storage/metakit/tests/ok/r02.txt b/akregator/src/mk4storage/metakit/tests/ok/r02.txt new file mode 100644 index 000000000..9f5b549a9 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/r02.txt @@ -0,0 +1,2 @@ +>>> Large inserts and removes +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/r02a.txt b/akregator/src/mk4storage/metakit/tests/ok/r02a.txt new file mode 100644 index 000000000..c86766a9c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/r02a.txt @@ -0,0 +1,8 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 5 rows = p1:I + 0: 37 + 1: 38 + 2: 39 + 3: 95 + 4: 96 diff --git a/akregator/src/mk4storage/metakit/tests/ok/r03.txt b/akregator/src/mk4storage/metakit/tests/ok/r03.txt new file mode 100644 index 000000000..34f35e910 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/r03.txt @@ -0,0 +1,2 @@ +>>> Binary property insertions +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/r03a.txt b/akregator/src/mk4storage/metakit/tests/ok/r03a.txt new file mode 100644 index 000000000..a99e46af8 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/r03a.txt @@ -0,0 +1,8 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 5 rows = p1:B + 0: (1024b) + 1: (256b) + 2: (1024b) + 3: (1024b) + 4: (341b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/r04.txt b/akregator/src/mk4storage/metakit/tests/ok/r04.txt new file mode 100644 index 000000000..60404b776 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/r04.txt @@ -0,0 +1,2 @@ +>>> Scripted string property tests +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/r04a.txt b/akregator/src/mk4storage/metakit/tests/ok/r04a.txt new file mode 100644 index 000000000..88ad82637 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/r04a.txt @@ -0,0 +1,3 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 0 rows = p1:S diff --git a/akregator/src/mk4storage/metakit/tests/ok/reversed.txt b/akregator/src/mk4storage/metakit/tests/ok/reversed.txt new file mode 100644 index 000000000..59ba292d4 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/reversed.txt @@ -0,0 +1,14 @@ + VIEW 1 rows = address:V info:V + 0: subview 'address' + VIEW 8 rows = name:S country:S age:I + 0: 'John Williams' 'UK' 0 + 1: 'Paco Pena' 'Spain' 44 + 2: 'Julian Bream' '' 50 + 3: 'Julian Bream' '' 0 + 4: 'Julien Coco' 'Netherlands' 0 + 5: 'John Williams' 'UK' 0 + 6: 'Paco Pena' 'Spain' 0 + 7: 'Julien Coco' 'Netherlands' 0 + 0: subview 'info' + VIEW 1 rows = version:I + 0: 100 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s00.txt b/akregator/src/mk4storage/metakit/tests/ok/s00.txt new file mode 100644 index 000000000..24df9e24a --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s00.txt @@ -0,0 +1,2 @@ +>>> Simple storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s00a.txt b/akregator/src/mk4storage/metakit/tests/ok/s00a.txt new file mode 100644 index 000000000..0872f9361 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s00a.txt @@ -0,0 +1,3 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 0 rows = p1:I diff --git a/akregator/src/mk4storage/metakit/tests/ok/s01.txt b/akregator/src/mk4storage/metakit/tests/ok/s01.txt new file mode 100644 index 000000000..b5d474170 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s01.txt @@ -0,0 +1,2 @@ +>>> Integer storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s01a.txt b/akregator/src/mk4storage/metakit/tests/ok/s01a.txt new file mode 100644 index 000000000..b393bf251 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s01a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:I + 0: 123 + 1: 789 + 2: 456 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s02.txt b/akregator/src/mk4storage/metakit/tests/ok/s02.txt new file mode 100644 index 000000000..7ec87fa82 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s02.txt @@ -0,0 +1,2 @@ +>>> Float storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s02a.txt b/akregator/src/mk4storage/metakit/tests/ok/s02a.txt new file mode 100644 index 000000000..6a561a53e --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s02a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:F + 0: 12.3 + 1: 78.9 + 2: 45.6 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s03.txt b/akregator/src/mk4storage/metakit/tests/ok/s03.txt new file mode 100644 index 000000000..307fb2a61 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s03.txt @@ -0,0 +1,2 @@ +>>> String storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s03a.txt b/akregator/src/mk4storage/metakit/tests/ok/s03a.txt new file mode 100644 index 000000000..83b694645 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s03a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:S + 0: 'one' + 1: 'three' + 2: 'two' diff --git a/akregator/src/mk4storage/metakit/tests/ok/s04.txt b/akregator/src/mk4storage/metakit/tests/ok/s04.txt new file mode 100644 index 000000000..2a6472b6f --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s04.txt @@ -0,0 +1,2 @@ +>>> View storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s04a.txt b/akregator/src/mk4storage/metakit/tests/ok/s04a.txt new file mode 100644 index 000000000..2b6800f31 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s04a.txt @@ -0,0 +1,18 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:S p2:V + 0: 'one' + 0: subview 'p2' + VIEW 1 rows = p3:I + 0: 1 + 1: 'three' + 1: subview 'p2' + VIEW 3 rows = p3:I + 0: 111 + 1: 222 + 2: 333 + 2: 'two' + 2: subview 'p2' + VIEW 2 rows = p3:I + 0: 11 + 1: 22 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s05.txt b/akregator/src/mk4storage/metakit/tests/ok/s05.txt new file mode 100644 index 000000000..625769c15 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s05.txt @@ -0,0 +1,2 @@ +>>> Store and reload +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s05a.txt b/akregator/src/mk4storage/metakit/tests/ok/s05a.txt new file mode 100644 index 000000000..fb8a3f195 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s05a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 123 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s06.txt b/akregator/src/mk4storage/metakit/tests/ok/s06.txt new file mode 100644 index 000000000..6246a4721 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s06.txt @@ -0,0 +1,2 @@ +>>> Commit twice +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s06a.txt b/akregator/src/mk4storage/metakit/tests/ok/s06a.txt new file mode 100644 index 000000000..5688100e0 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s06a.txt @@ -0,0 +1,5 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 2 rows = p1:I + 0: 123 + 1: 234 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s07.txt b/akregator/src/mk4storage/metakit/tests/ok/s07.txt new file mode 100644 index 000000000..93559f6fe --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s07.txt @@ -0,0 +1,2 @@ +>>> Commit modified +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s07a.txt b/akregator/src/mk4storage/metakit/tests/ok/s07a.txt new file mode 100644 index 000000000..80140cf7f --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s07a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 234 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s08.txt b/akregator/src/mk4storage/metakit/tests/ok/s08.txt new file mode 100644 index 000000000..cd8f89711 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s08.txt @@ -0,0 +1,2 @@ +>>> View after storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s08a.txt b/akregator/src/mk4storage/metakit/tests/ok/s08a.txt new file mode 100644 index 000000000..fb8a3f195 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s08a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 123 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s09.txt b/akregator/src/mk4storage/metakit/tests/ok/s09.txt new file mode 100644 index 000000000..9599ae19e --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s09.txt @@ -0,0 +1,2 @@ +>>> Copy storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s09a.txt b/akregator/src/mk4storage/metakit/tests/ok/s09a.txt new file mode 100644 index 000000000..fb8a3f195 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s09a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 123 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s09b.txt b/akregator/src/mk4storage/metakit/tests/ok/s09b.txt new file mode 100644 index 000000000..fb8a3f195 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s09b.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 123 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s10.txt b/akregator/src/mk4storage/metakit/tests/ok/s10.txt new file mode 100644 index 000000000..03f9bed9b --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s10.txt @@ -0,0 +1,2 @@ +>>> Stream storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s10a.txt b/akregator/src/mk4storage/metakit/tests/ok/s10a.txt new file mode 100644 index 000000000..2b6800f31 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s10a.txt @@ -0,0 +1,18 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:S p2:V + 0: 'one' + 0: subview 'p2' + VIEW 1 rows = p3:I + 0: 1 + 1: 'three' + 1: subview 'p2' + VIEW 3 rows = p3:I + 0: 111 + 1: 222 + 2: 333 + 2: 'two' + 2: subview 'p2' + VIEW 2 rows = p3:I + 0: 11 + 1: 22 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s10b.txt b/akregator/src/mk4storage/metakit/tests/ok/s10b.txt new file mode 100644 index 000000000..2b6800f31 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s10b.txt @@ -0,0 +1,18 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:S p2:V + 0: 'one' + 0: subview 'p2' + VIEW 1 rows = p3:I + 0: 1 + 1: 'three' + 1: subview 'p2' + VIEW 3 rows = p3:I + 0: 111 + 1: 222 + 2: 333 + 2: 'two' + 2: subview 'p2' + VIEW 2 rows = p3:I + 0: 11 + 1: 22 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s10c.txt b/akregator/src/mk4storage/metakit/tests/ok/s10c.txt new file mode 100644 index 000000000..bb34ca011 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s10c.txt @@ -0,0 +1,21 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 4 rows = p1:S p2:V + 0: 'one' + 0: subview 'p2' + VIEW 1 rows = p3:I + 0: 1 + 1: 'three' + 1: subview 'p2' + VIEW 3 rows = p3:I + 0: 111 + 1: 222 + 2: 333 + 2: 'two' + 2: subview 'p2' + VIEW 2 rows = p3:I + 0: 11 + 1: 22 + 3: 'four' + 3: subview 'p2' + VIEW 0 rows = p3:I diff --git a/akregator/src/mk4storage/metakit/tests/ok/s11.txt b/akregator/src/mk4storage/metakit/tests/ok/s11.txt new file mode 100644 index 000000000..fd82a1603 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s11.txt @@ -0,0 +1,2 @@ +>>> Commit and rollback +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s11a.txt b/akregator/src/mk4storage/metakit/tests/ok/s11a.txt new file mode 100644 index 000000000..fb8a3f195 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s11a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 123 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s12.txt b/akregator/src/mk4storage/metakit/tests/ok/s12.txt new file mode 100644 index 000000000..be9ca7a36 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s12.txt @@ -0,0 +1,2 @@ +>>> Remove subview +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s12a.txt b/akregator/src/mk4storage/metakit/tests/ok/s12a.txt new file mode 100644 index 000000000..9b6e95f0a --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s12a.txt @@ -0,0 +1,3 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 0 rows = p1:I p2:V diff --git a/akregator/src/mk4storage/metakit/tests/ok/s13.txt b/akregator/src/mk4storage/metakit/tests/ok/s13.txt new file mode 100644 index 000000000..112c33c70 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s13.txt @@ -0,0 +1,2 @@ +>>> Remove middle subview +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s13a.txt b/akregator/src/mk4storage/metakit/tests/ok/s13a.txt new file mode 100644 index 000000000..2189bb0a0 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s13a.txt @@ -0,0 +1,13 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 2 rows = p1:I p2:V + 0: 123 + 0: subview 'p2' + VIEW 1 rows = p3:I + 0: 234 + 1: 125 + 1: subview 'p2' + VIEW 3 rows = p3:I + 0: 456 + 1: 457 + 2: 458 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s14.txt b/akregator/src/mk4storage/metakit/tests/ok/s14.txt new file mode 100644 index 000000000..0e9348b04 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s14.txt @@ -0,0 +1,2 @@ +>>> Replace attached subview +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s14a.txt b/akregator/src/mk4storage/metakit/tests/ok/s14a.txt new file mode 100644 index 000000000..e87913c05 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s14a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I p2:V + 0: 0 + 0: subview 'p2' + VIEW 0 rows = p3:I diff --git a/akregator/src/mk4storage/metakit/tests/ok/s15.txt b/akregator/src/mk4storage/metakit/tests/ok/s15.txt new file mode 100644 index 000000000..305baef1a --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s15.txt @@ -0,0 +1,2 @@ +>>> Add after removed subviews +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s15a.txt b/akregator/src/mk4storage/metakit/tests/ok/s15a.txt new file mode 100644 index 000000000..9e07c88fe --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s15a.txt @@ -0,0 +1,7 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I p2:V + 0: 111 + 0: subview 'p2' + VIEW 1 rows = p3:I + 0: 234 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s16.txt b/akregator/src/mk4storage/metakit/tests/ok/s16.txt new file mode 100644 index 000000000..27c69ca10 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s16.txt @@ -0,0 +1,2 @@ +>>> Add after removed ints +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s16a.txt b/akregator/src/mk4storage/metakit/tests/ok/s16a.txt new file mode 100644 index 000000000..fd274ae0d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s16a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I p2:V + 0: 4 + 0: subview 'p2' + VIEW 0 rows = p3:I diff --git a/akregator/src/mk4storage/metakit/tests/ok/s17.txt b/akregator/src/mk4storage/metakit/tests/ok/s17.txt new file mode 100644 index 000000000..979139531 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s17.txt @@ -0,0 +1,2 @@ +>>> Add after removed strings +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s17a.txt b/akregator/src/mk4storage/metakit/tests/ok/s17a.txt new file mode 100644 index 000000000..40720e9d5 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s17a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:S p2:V + 0: 'four' + 0: subview 'p2' + VIEW 0 rows = p3:I diff --git a/akregator/src/mk4storage/metakit/tests/ok/s18.txt b/akregator/src/mk4storage/metakit/tests/ok/s18.txt new file mode 100644 index 000000000..5ff2d8e73 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s18.txt @@ -0,0 +1,2 @@ +>>> Empty storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s18a.txt b/akregator/src/mk4storage/metakit/tests/ok/s18a.txt new file mode 100644 index 000000000..a28a7cae2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s18a.txt @@ -0,0 +1 @@ + VIEW 1 rows = diff --git a/akregator/src/mk4storage/metakit/tests/ok/s19.txt b/akregator/src/mk4storage/metakit/tests/ok/s19.txt new file mode 100644 index 000000000..0a9ff3a17 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s19.txt @@ -0,0 +1,2 @@ +>>> Empty view outlives storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s19a.txt b/akregator/src/mk4storage/metakit/tests/ok/s19a.txt new file mode 100644 index 000000000..a28a7cae2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s19a.txt @@ -0,0 +1 @@ + VIEW 1 rows = diff --git a/akregator/src/mk4storage/metakit/tests/ok/s20.txt b/akregator/src/mk4storage/metakit/tests/ok/s20.txt new file mode 100644 index 000000000..5124a59e3 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s20.txt @@ -0,0 +1,2 @@ +>>> View outlives storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s20a.txt b/akregator/src/mk4storage/metakit/tests/ok/s20a.txt new file mode 100644 index 000000000..a28a7cae2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s20a.txt @@ -0,0 +1 @@ + VIEW 1 rows = diff --git a/akregator/src/mk4storage/metakit/tests/ok/s21.txt b/akregator/src/mk4storage/metakit/tests/ok/s21.txt new file mode 100644 index 000000000..4b944bf03 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s21.txt @@ -0,0 +1,2 @@ +>>> Test demo scenario +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s21a.txt b/akregator/src/mk4storage/metakit/tests/ok/s21a.txt new file mode 100644 index 000000000..2da5bab8b --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s21a.txt @@ -0,0 +1,10 @@ + VIEW 1 rows = a:V b:V + 0: subview 'a' + VIEW 4 rows = p1:S p2:S p3:I + 0: 'One' 'Un' 0 + 1: 'Two' 'Deux' 123 + 2: 'Four' '' 0 + 3: 'Three' 'Trois' 0 + 0: subview 'b' + VIEW 1 rows = p4:I + 0: 234 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s22.txt b/akregator/src/mk4storage/metakit/tests/ok/s22.txt new file mode 100644 index 000000000..8d5c2bc75 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s22.txt @@ -0,0 +1,2 @@ +>>> Double storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s22a.txt b/akregator/src/mk4storage/metakit/tests/ok/s22a.txt new file mode 100644 index 000000000..03d83cf6f --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s22a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:D + 0: 1234.5678 + 1: 3456.789 + 2: 2345.6789 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s23.txt b/akregator/src/mk4storage/metakit/tests/ok/s23.txt new file mode 100644 index 000000000..7f921bcf3 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s23.txt @@ -0,0 +1,2 @@ +>>> Find absent record +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s23a.txt b/akregator/src/mk4storage/metakit/tests/ok/s23a.txt new file mode 100644 index 000000000..a28a7cae2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s23a.txt @@ -0,0 +1 @@ + VIEW 1 rows = diff --git a/akregator/src/mk4storage/metakit/tests/ok/s24.txt b/akregator/src/mk4storage/metakit/tests/ok/s24.txt new file mode 100644 index 000000000..91ef101b2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s24.txt @@ -0,0 +1,2 @@ +>>> Bitwise storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s24a.txt b/akregator/src/mk4storage/metakit/tests/ok/s24a.txt new file mode 100644 index 000000000..37fc08320 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s24a.txt @@ -0,0 +1,45 @@ + VIEW 1 rows = a1:V a2:V a3:V a4:V + 0: subview 'a1' + VIEW 9 rows = p1:I + 0: 1 + 1: 0 + 2: 1 + 3: 0 + 4: 1 + 5: 0 + 6: 1 + 7: 0 + 8: 1 + 0: subview 'a2' + VIEW 9 rows = p1:I + 0: 3 + 1: 0 + 2: 1 + 3: 2 + 4: 3 + 5: 0 + 6: 1 + 7: 2 + 8: 3 + 0: subview 'a3' + VIEW 9 rows = p1:I + 0: 7 + 1: 8 + 2: 9 + 3: 10 + 4: 11 + 5: 12 + 6: 13 + 7: 14 + 8: 15 + 0: subview 'a4' + VIEW 9 rows = p1:I + 0: 119 + 1: 120 + 2: 121 + 3: 122 + 4: 123 + 5: 124 + 6: 125 + 7: 126 + 8: 127 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s25.txt b/akregator/src/mk4storage/metakit/tests/ok/s25.txt new file mode 100644 index 000000000..a9f5a3686 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s25.txt @@ -0,0 +1,2 @@ +>>> Bytes storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s25a.txt b/akregator/src/mk4storage/metakit/tests/ok/s25a.txt new file mode 100644 index 000000000..ced17ab22 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s25a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:B + 0: (2b) + 1: (4b) + 2: (5b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/s26.txt b/akregator/src/mk4storage/metakit/tests/ok/s26.txt new file mode 100644 index 000000000..4496d6ee5 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s26.txt @@ -0,0 +1,2 @@ +>>> Bitwise autosizing +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s26a.txt b/akregator/src/mk4storage/metakit/tests/ok/s26a.txt new file mode 100644 index 000000000..2b644a8e8 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s26a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I p2:I p3:I p4:I + 0: 100000 100000 100000 100000 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s27.txt b/akregator/src/mk4storage/metakit/tests/ok/s27.txt new file mode 100644 index 000000000..d35484ead --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s27.txt @@ -0,0 +1,2 @@ +>>> Bytes restructuring +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s27a.txt b/akregator/src/mk4storage/metakit/tests/ok/s27a.txt new file mode 100644 index 000000000..c39554b1e --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s27a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:B + 0: (4b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/s28.txt b/akregator/src/mk4storage/metakit/tests/ok/s28.txt new file mode 100644 index 000000000..4e7e1a167 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s28.txt @@ -0,0 +1,2 @@ +>>> Doubles added later +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s28a.txt b/akregator/src/mk4storage/metakit/tests/ok/s28a.txt new file mode 100644 index 000000000..a731a6a3c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s28a.txt @@ -0,0 +1,7 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:F p2:D p3:V + 0: 123 123 + 0: subview 'p3' + VIEW 1 rows = p1:F p2:D + 0: 234 234 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s29.txt b/akregator/src/mk4storage/metakit/tests/ok/s29.txt new file mode 100644 index 000000000..982807abc --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s29.txt @@ -0,0 +1,2 @@ +>>> Delete bytes property +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s29a.txt b/akregator/src/mk4storage/metakit/tests/ok/s29a.txt new file mode 100644 index 000000000..36e2113bd --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s29a.txt @@ -0,0 +1,3 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 0 rows = p1:B diff --git a/akregator/src/mk4storage/metakit/tests/ok/s30.txt b/akregator/src/mk4storage/metakit/tests/ok/s30.txt new file mode 100644 index 000000000..e86ceee7c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s30.txt @@ -0,0 +1,2 @@ +>>> Memo storage +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s30a.txt b/akregator/src/mk4storage/metakit/tests/ok/s30a.txt new file mode 100644 index 000000000..ced17ab22 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s30a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:B + 0: (2b) + 1: (4b) + 2: (5b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/s31.txt b/akregator/src/mk4storage/metakit/tests/ok/s31.txt new file mode 100644 index 000000000..661077237 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s31.txt @@ -0,0 +1,2 @@ +>>> Check sort buffer use +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s31a.txt b/akregator/src/mk4storage/metakit/tests/ok/s31a.txt new file mode 100644 index 000000000..bb0beb13e --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s31a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:I + 0: 3 + 1: 1 + 2: 2 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s32.txt b/akregator/src/mk4storage/metakit/tests/ok/s32.txt new file mode 100644 index 000000000..342f61ddb --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s32.txt @@ -0,0 +1,2 @@ +>>> Set memo empty or same size +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s32a.txt b/akregator/src/mk4storage/metakit/tests/ok/s32a.txt new file mode 100644 index 000000000..c39554b1e --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s32a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:B + 0: (4b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/s33.txt b/akregator/src/mk4storage/metakit/tests/ok/s33.txt new file mode 100644 index 000000000..749e7d37c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s33.txt @@ -0,0 +1,2 @@ +>>> Serialize memo fields +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s33a.txt b/akregator/src/mk4storage/metakit/tests/ok/s33a.txt new file mode 100644 index 000000000..ced17ab22 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s33a.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:B + 0: (2b) + 1: (4b) + 2: (5b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/s33b.txt b/akregator/src/mk4storage/metakit/tests/ok/s33b.txt new file mode 100644 index 000000000..ced17ab22 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s33b.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:B + 0: (2b) + 1: (4b) + 2: (5b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/s33c.txt b/akregator/src/mk4storage/metakit/tests/ok/s33c.txt new file mode 100644 index 000000000..ced17ab22 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s33c.txt @@ -0,0 +1,6 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 3 rows = p1:B + 0: (2b) + 1: (4b) + 2: (5b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/s34.txt b/akregator/src/mk4storage/metakit/tests/ok/s34.txt new file mode 100644 index 000000000..63a225ef2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s34.txt @@ -0,0 +1,2 @@ +>>> Smart and failed commits +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s34a.txt b/akregator/src/mk4storage/metakit/tests/ok/s34a.txt new file mode 100644 index 000000000..81e1847d2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s34a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 111 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s35.txt b/akregator/src/mk4storage/metakit/tests/ok/s35.txt new file mode 100644 index 000000000..18835d389 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s35.txt @@ -0,0 +1,2 @@ +>>> Datafile with preamble +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s35a.txt b/akregator/src/mk4storage/metakit/tests/ok/s35a.txt new file mode 100644 index 000000000..81e1847d2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s35a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 111 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s36.txt b/akregator/src/mk4storage/metakit/tests/ok/s36.txt new file mode 100644 index 000000000..63d15af21 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s36.txt @@ -0,0 +1,2 @@ +>>> Commit after load +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s36a.txt b/akregator/src/mk4storage/metakit/tests/ok/s36a.txt new file mode 100644 index 000000000..81e1847d2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s36a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 111 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s36b.txt b/akregator/src/mk4storage/metakit/tests/ok/s36b.txt new file mode 100644 index 000000000..81e1847d2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s36b.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 111 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s37.txt b/akregator/src/mk4storage/metakit/tests/ok/s37.txt new file mode 100644 index 000000000..8bf5f8b73 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s37.txt @@ -0,0 +1,2 @@ +>>> Change short partial fields +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s37a.txt b/akregator/src/mk4storage/metakit/tests/ok/s37a.txt new file mode 100644 index 000000000..21eab9c9d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s37a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = v1:V + 0: subview 'v1' + VIEW 1 rows = key:I p1:B + 0: 0 (6b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/s38.txt b/akregator/src/mk4storage/metakit/tests/ok/s38.txt new file mode 100644 index 000000000..acd0b2019 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s38.txt @@ -0,0 +1,2 @@ +>>> Lots of empty subviews +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s38a.txt b/akregator/src/mk4storage/metakit/tests/ok/s38a.txt new file mode 100644 index 000000000..8ee8c3471 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s38a.txt @@ -0,0 +1,7 @@ + VIEW 1 rows = v:V + 0: subview 'v' + VIEW 2 rows = v1:V + 0: subview 'v1' + VIEW 0 rows = p1:S + 1: subview 'v1' + VIEW 0 rows = p1:S diff --git a/akregator/src/mk4storage/metakit/tests/ok/s39.txt b/akregator/src/mk4storage/metakit/tests/ok/s39.txt new file mode 100644 index 000000000..a907f11e6 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s39.txt @@ -0,0 +1,2 @@ +>>> Do not detach empty top-level views +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s39a.txt b/akregator/src/mk4storage/metakit/tests/ok/s39a.txt new file mode 100644 index 000000000..7799a2713 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s39a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = v1:V + 0: subview 'v1' + VIEW 1 rows = p1:I + 0: 123 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s40.txt b/akregator/src/mk4storage/metakit/tests/ok/s40.txt new file mode 100644 index 000000000..b799ff95c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s40.txt @@ -0,0 +1,2 @@ +>>> LoadFrom after commit +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s40a.txt b/akregator/src/mk4storage/metakit/tests/ok/s40a.txt new file mode 100644 index 000000000..d256fc735 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s40a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I + 0: 456 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s41.txt b/akregator/src/mk4storage/metakit/tests/ok/s41.txt new file mode 100644 index 000000000..419edbad8 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s41.txt @@ -0,0 +1,2 @@ +>>> Partial modify blocked +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s41a.txt b/akregator/src/mk4storage/metakit/tests/ok/s41a.txt new file mode 100644 index 000000000..51bc32d70 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s41a.txt @@ -0,0 +1,8 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 2 rows = _B:V + 0: subview '_B' + VIEW 1 rows = p1:B + 0: (8b) + 1: subview '_B' + VIEW 0 rows = p1:B diff --git a/akregator/src/mk4storage/metakit/tests/ok/s42.txt b/akregator/src/mk4storage/metakit/tests/ok/s42.txt new file mode 100644 index 000000000..61c83f2aa --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s42.txt @@ -0,0 +1,2 @@ +>>> Get descriptions +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s43.txt b/akregator/src/mk4storage/metakit/tests/ok/s43.txt new file mode 100644 index 000000000..af2b07123 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s43.txt @@ -0,0 +1,2 @@ +>>> View reuse after sub-byte ints +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s43a.txt b/akregator/src/mk4storage/metakit/tests/ok/s43a.txt new file mode 100644 index 000000000..df0a22ecb --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s43a.txt @@ -0,0 +1,5 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 2 rows = p1:I + 0: 0 + 1: 12345 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s44.txt b/akregator/src/mk4storage/metakit/tests/ok/s44.txt new file mode 100644 index 000000000..640461805 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s44.txt @@ -0,0 +1,2 @@ +>>> Bad memo free space +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s44a.txt b/akregator/src/mk4storage/metakit/tests/ok/s44a.txt new file mode 100644 index 000000000..02b49b500 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s44a.txt @@ -0,0 +1,4 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I p2:B + 0: 0 (12345b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/s45.txt b/akregator/src/mk4storage/metakit/tests/ok/s45.txt new file mode 100644 index 000000000..066826a49 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s45.txt @@ -0,0 +1,2 @@ +>>> Bad subview memo free space +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s45a.txt b/akregator/src/mk4storage/metakit/tests/ok/s45a.txt new file mode 100644 index 000000000..920e28eed --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s45a.txt @@ -0,0 +1,7 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 1 rows = p1:I p2:V + 0: 0 + 0: subview 'p2' + VIEW 1 rows = p3:B + 0: (12345b) diff --git a/akregator/src/mk4storage/metakit/tests/ok/s46.txt b/akregator/src/mk4storage/metakit/tests/ok/s46.txt new file mode 100644 index 000000000..b799ff95c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s46.txt @@ -0,0 +1,2 @@ +>>> LoadFrom after commit +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s46a.txt b/akregator/src/mk4storage/metakit/tests/ok/s46a.txt new file mode 100644 index 000000000..9fa04509b --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s46a.txt @@ -0,0 +1,15 @@ + VIEW 1 rows = a:V + 0: subview 'a' + VIEW 12 rows = p1:I + 0: 11 + 1: 22 + 2: 33 + 3: 44 + 4: 0 + 5: 55 + 6: 66 + 7: 77 + 8: 0 + 9: 88 + 10: 99 + 11: 1000 diff --git a/akregator/src/mk4storage/metakit/tests/ok/s47.txt b/akregator/src/mk4storage/metakit/tests/ok/s47.txt new file mode 100644 index 000000000..55827d04d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s47.txt @@ -0,0 +1,2 @@ +>>> Defining bad property type +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s48.txt b/akregator/src/mk4storage/metakit/tests/ok/s48.txt new file mode 100644 index 000000000..28fdd3a5c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s48.txt @@ -0,0 +1,2 @@ +>>> Resize subview to zero and back +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s48a.txt b/akregator/src/mk4storage/metakit/tests/ok/s48a.txt new file mode 100644 index 000000000..5e398789c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s48a.txt @@ -0,0 +1,5 @@ + VIEW 1 rows = v1:V + 0: subview 'v1' + VIEW 1 rows = v2:V + 0: subview 'v2' + VIEW 0 rows = p1:I diff --git a/akregator/src/mk4storage/metakit/tests/ok/s48b.txt b/akregator/src/mk4storage/metakit/tests/ok/s48b.txt new file mode 100644 index 000000000..5e398789c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s48b.txt @@ -0,0 +1,5 @@ + VIEW 1 rows = v1:V + 0: subview 'v1' + VIEW 1 rows = v2:V + 0: subview 'v2' + VIEW 0 rows = p1:I diff --git a/akregator/src/mk4storage/metakit/tests/ok/s49.txt b/akregator/src/mk4storage/metakit/tests/ok/s49.txt new file mode 100644 index 000000000..b32d7ff00 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s49.txt @@ -0,0 +1,2 @@ +>>> Specify conflicting properties +<<< done. diff --git a/akregator/src/mk4storage/metakit/tests/ok/s49a.txt b/akregator/src/mk4storage/metakit/tests/ok/s49a.txt new file mode 100644 index 000000000..992724c50 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/ok/s49a.txt @@ -0,0 +1,7 @@ + VIEW 1 rows = v1:V v2:V v3:V + 0: subview 'v1' + VIEW 0 rows = p1:I + 0: subview 'v2' + VIEW 0 rows = p1:I + 0: subview 'v3' + VIEW 0 rows = v3:V diff --git a/akregator/src/mk4storage/metakit/tests/regress.cpp b/akregator/src/mk4storage/metakit/tests/regress.cpp new file mode 100644 index 000000000..0725f57f5 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/regress.cpp @@ -0,0 +1,269 @@ +// regress.cpp -- Regression test program, main code +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +#include "regress.h" + +#if defined (macintosh) + #include <SIOUX.h> + #ifdef DEBUG_NEW + #include <DebugNew.cp> + //#include "ZoneRanger.c" + #if DEBUG_NEW >= 2 && !defined (q4_MAC_LEAK_CHECK) + #define q4_MAC_LEAK_CHECK 1 + #endif + #endif +#endif + +#if __profile__ + #define q4_MWCW_PROFILER 1 + #include <profiler.h> +#endif + +#if q4_NOTHROW + const char* msg; +#endif + +#if _WIN32_WCE + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +int remove(const char* fn_) +{ + c4_Bytes buffer; + int n = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, fn_, -1, NULL, 0); + wchar_t* w = (wchar_t*) buffer.SetBufferClear((n+1) * sizeof (wchar_t)); + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, fn_, -1, w, n); + return DeleteFile((wchar_t*) buffer.Contents()) ? 0 : -1; +} + +#endif + +int +#if _WIN32_WCE +_cdecl +#endif +main() +{ +// afxMemDF |= allocMemDF | checkAlwaysMemDF; + + // The M4STRING package sometimes keeps a spare empty string around. + // By allocating it here, we avoid a few bogus memory leak reports. + c4_String empty; + + #if q4_MAC_LEAK_CHECK + DebugNewForgetLeaks(); + #endif + +#if q4_MWCW_PROFILER + if (!ProfilerInit(collectDetailed, bestTimeBase, 20, 5)) + { +#endif + TestBasics1(); + TestBasics2(); + TestNotify(); + TestCustom1(); + TestCustom2(); + TestResize(); + TestStores1(); + TestStores2(); + TestStores3(); + TestStores4(); + TestStores5(); + TestDiffer(); + TestExtend(); + TestFormat(); + TestMapped(); + TestLimits(); + +#if q4_MWCW_PROFILER + ProfilerDump("\pRegress.prof"); + ProfilerTerm(); + } +#endif + + #if defined (_DEBUG) + fputs("\nPress return... ", stderr); + getchar(); + #endif + #if q4_MAC_LEAK_CHECK + if (DebugNewReportLeaks()) + fputs(" *** Memory leaks found ***\n", stderr); + #endif + #if defined (macintosh) + SIOUXSettings.asktosaveonclose = false; + #endif + + fputs("Done.\n", stderr); + return 0; +} + +// Recursively display the entire view contents. The results shown do not +// depend on file layout (free space, file positions, flat vs. on-demand). + +static void ViewDisplay(const c4_View& v_, FILE* fp, int l_ =0) +{ + c4_String types; + bool hasData = false, hasSubs = false; + + // display header info and collect all data types + fprintf(fp, "%*s VIEW %5d rows =", l_, "", v_.GetSize()); + for (int n = 0; n < v_.NumProperties(); ++n) + { + c4_Property prop = v_.NthProperty(n); + char t = prop.Type(); + + fprintf(fp, " %s:%c", (const char*) prop.Name(), t); + + types += t; + + if (t == 'V') + hasSubs = true; + else + hasData = true; + } + fprintf(fp, "\n"); + + for (int j = 0; j < v_.GetSize(); ++j) + { + if (hasData) // data properties are all shown on the same line + { + fprintf(fp, "%*s %4d:", l_, "", j); + c4_RowRef r = v_[j]; + + for (int k = 0; k < types.GetLength(); ++k) + { + c4_Property p = v_.NthProperty(k); + + switch (types[k]) + { + case 'I': + fprintf(fp, " %ld", (long) ((c4_IntProp&) p) (r)); + break; + +#if !q4_TINY + case 'F': + fprintf(fp, " %g", (double) ((c4_FloatProp&) p) (r)); + break; + + case 'D': + fprintf(fp, " %.12g", (double) ((c4_DoubleProp&) p) (r)); + break; +#endif + + case 'S': + fprintf(fp, " '%s'", (const char*) ((c4_StringProp&) p) (r)); + break; + + case 'M': // backward compatibility + case 'B': + fprintf(fp, " (%db)", (p (r)).GetSize()); + break; + + default: + if (types[k] != 'V') + fprintf(fp, " (%c?)", types[k]); + } + } + + fprintf(fp, "\n"); + } + + if (hasSubs) // subviews are then shown, each as a separate block + { + for (int k = 0; k < types.GetLength(); ++k) + { + if (types[k] == 'V') + { + c4_Property prop = v_.NthProperty(k); + + fprintf(fp, "%*s %4d: subview '%s'\n", l_, "", j, + (const char*) prop.Name()); + + c4_ViewProp& vp = (c4_ViewProp&) prop; + + ViewDisplay(vp (v_[j]), fp, l_ + 2); + } + } + } + } +} + +void DumpFile(const char* in_, const char* out_) +{ + FILE* fp = fopen(out_, TEXTOUT); + A(fp); + + c4_Storage store (in_, 0); + + ViewDisplay(store, fp); + + fclose(fp); +} + +void Fail(const char* msg) +{ + #if q4_NOTHROW + fprintf(stderr, "\t%s\n", msg); + printf("*** %s ***\n", msg); + #else + throw msg; + #endif +} + +void FailExpr(const char* expr) +{ + char buffer [100]; + sprintf(buffer, "Failed: A(%s)", expr); + Fail(buffer); +} + +int StartTest(int mask_, const char* name_, const char* desc_) +{ + if (mask_) + { + #if q4_MFC && defined(_DEBUG) + TRACE("%s - %-40s *** DISABLED ***\n", name_, desc_); + #endif + #if !q4_MWCW_PROFILER + fprintf(stderr, "%s - %-40s *** DISABLED ***\n", name_, desc_); + #endif + return false; + } + + #if q4_MFC && defined(_DEBUG) + TRACE("%s - %s\n", name_, desc_); + #endif + #if !q4_MWCW_PROFILER + fprintf(stderr, "%s - %s\n", name_, desc_); + #endif + + char buffer [50]; + sprintf(buffer, "%s%s.txt", TESTDIR, name_); + #if _WIN32_WCE + fclose(stdout); + fopen(buffer, TEXTOUT); + #else + freopen(buffer, TEXTOUT, stdout); + #endif + printf(">>> %s\n", desc_); + + return true; +} + +void CatchMsg(const char* msg) +{ + #if !q4_MWCW_PROFILER + fprintf(stderr, "\t%s\n", msg); + #endif + printf("*** %s ***\n", msg); +} + +void CatchOther() +{ + #if !q4_MWCW_PROFILER + fputs("\tException!\n", stderr); + #endif + printf("*** Exception ***\n"); +} diff --git a/akregator/src/mk4storage/metakit/tests/regress.h b/akregator/src/mk4storage/metakit/tests/regress.h new file mode 100644 index 000000000..f317bcb26 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/regress.h @@ -0,0 +1,145 @@ +// regress.h -- Regression test program, header file +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +#include "mk4.h" +#include "mk4io.h" +#include "mk4str.h" + +#define TraceAll false + + // default for dos and unix is to assume they don't support exceptions +#if defined (_DOS) || defined (unix) || defined (__unix__) || \ + defined (__GNUC__) || defined (_WIN32_WCE) +#if !defined (q4_NOTHROW) +#define q4_NOTHROW 1 +#endif +#endif + +#ifdef _WIN32_WCE +int remove(const char*); +#endif + +#if _MSC_VER == 800 +#pragma warning (disable: 4703) // function too large for global optimizations + +// also no exceptions in MSVC 1.52 when used with a QuickWin target +#if defined (_QWINVER) && !defined (q4_NOTHROW) +#define q4_NOTHROW 1 +#endif +#endif + +#if q4_NOTHROW +#define try +#define catch(x) if (0) + +extern const char* msg; +#endif + +#if defined (macintosh) +#define TESTDIR ":tests:" +#define TEXTOUT "wt" +#define LINESEP "\r" +#elif defined (__VMS) +#define TESTDIR "[.tests]" +#define TEXTOUT "w" +#define LINESEP "\r\n" // is this correct? +#elif defined (unix) || defined (__unix__) || defined (__GNUC__) +#define TESTDIR "tests/" +#define TEXTOUT "w" +#define LINESEP "\n" +#else +#define TESTDIR "tests\\" +#define TEXTOUT "wt" +#define LINESEP "\r\n" +#endif + +#include <stdio.h> + +#if q4_MFC && defined(_DEBUG) +#define B(n_,d_,c_) \ + if (StartTest(c_, #n_, #d_)) \ + { \ + CMemoryState oldState, newState, diffState; \ + oldState.Checkpoint(); \ + afxTraceEnabled = TraceAll; \ + try \ + { \ + { +#define E \ + } \ + puts("<<< done."); \ + } \ + catch (const char* msg) { CatchMsg(msg); } \ + catch (...) { CatchOther(); } \ + afxTraceEnabled = true; \ + fflush(stdout); \ + newState.Checkpoint(); \ + if (diffState.Difference(oldState, newState)) \ + { \ + fputs("\tMemory leaked!\n", stderr); \ + puts("*** Memory leaked ***"); \ + TRACE(" *** Memory leaked, "); \ + diffState.DumpAllObjectsSince(); \ + } \ + fflush(stdout); \ + } +#else +#define B(n_,d_,c_) \ + if (StartTest(c_, #n_, #d_)) \ + { \ + try \ + { \ + { +#define E \ + } \ + puts("<<< done."); \ + } \ + catch (const char* msg) { CatchMsg(msg); } \ + catch (...) { CatchOther(); } \ + fflush(stdout); \ + } +#endif + +#define A(e_) if (e_) ; else FailExpr(#e_) + +#define W(f_) remove(#f_) +#define R(f_) A(remove(#f_) == 0) +#define D(f_) DumpFile(#f_, TESTDIR #f_ ".txt") + +typedef c4_BytesProp c4_MemoProp; + +extern void DumpFile(const char* in_, const char* out_); +extern void Fail(const char* msg); +extern void FailExpr(const char* expr); +extern int StartTest(int, const char*, const char*); +extern void CatchMsg(const char* msg); +extern void CatchOther(); + +extern void TestBasics1(); +extern void TestBasics2(); +extern void TestCustom1(); +extern void TestCustom2(); +extern void TestDiffer(); +extern void TestExtend(); +extern void TestFormat(); +extern void TestLimits(); +extern void TestMapped(); +extern void TestNotify(); +extern void TestResize(); +extern void TestStores1(); +extern void TestStores2(); +extern void TestStores3(); +extern void TestStores4(); +extern void TestStores5(); + + // The Borland C++ RTL does not want file handle objects to cross + // DLL boundaries, so we use special fopen/fclose hooks in the DLL. + +#if defined (__BORLANDC__) // this assumes Metakit is in a DLL! +extern FILE* f4_FileOpenInDLL(const char*, const char*); +extern int f4_FileCloseInDLL(FILE*); + +#define fopen f4_FileOpenInDLL +#define fclose f4_FileCloseInDLL +#endif diff --git a/akregator/src/mk4storage/metakit/tests/tbasic1.cpp b/akregator/src/mk4storage/metakit/tests/tbasic1.cpp new file mode 100644 index 000000000..0c5742a25 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tbasic1.cpp @@ -0,0 +1,268 @@ +// tbasic1.cpp -- Regression test program, basic tests part 1 +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestBasics1() +{ + B(b00, Should fail, 0) + { + A(false); + } E; + + B(b01, Should succeed, 0) + { + A(sizeof (t4_byte) == 1); + A(sizeof (short) == 2); + A(sizeof (t4_i32) == 4); + A(sizeof (float) == 4); + A(sizeof (double) == 8); + } E; + + B(b02, Int property, 0) + { + c4_Row r1; + c4_IntProp p1 ("p1"); + p1 (r1) = 1234567890L; + long x1 = p1 (r1); + A(x1 == 1234567890L); + } E; + +#if !q4_TINY + B(b03, Float property, 0) + { + c4_Row r1; + c4_FloatProp p1 ("p1"); + p1 (r1) = 123.456; + double x1 = p1 (r1); + A((float) x1 == (float) 123.456); + } E; +#endif + + B(b04, String property, 0) + { + c4_Row r1; + c4_StringProp p1 ("p1"); + p1 (r1) = "abc"; + const char* x1 = p1 (r1); + A((c4_String) x1 == "abc"); + } E; + + B(b05, View property, 0) + { + c4_View v1; + c4_Row r1; + c4_ViewProp p1 ("p1"); + p1 (r1) = v1; + c4_View x1 = p1 (r1); + // compare cursors to make sure this is the same sequence + A(x1[0] == v1[0]); + } E; + + B(b06, View construction, 0) + { + c4_IntProp p1 ("p1"), p2 ("p2"), p3 ("p3"); + c4_View v1 = (p1, p3, p2); + A(v1.FindProperty(p1.GetId()) == 0); + A(v1.FindProperty(p2.GetId()) == 2); + A(v1.FindProperty(p3.GetId()) == 1); + } E; + + B(b07, Row manipulation, 0) + { + c4_StringProp p1 ("p1"), p2 ("p2"); + c4_IntProp p3 ("p3"); + c4_Row r1; + p1 (r1) = "look at this" ; + const char* x1 = p1 (r1); + A(x1 == (c4_String) "look at this"); + r1 = p1 [ "what's in a" ] + p2 [ "name..." ]; + c4_String t = (const char*) p2 (r1); + p1 (r1) = t + (const char*) (p1 (r1)); + p2 (r1) = p1 (r1); + c4_String x2 = (const char*) p1 (r1); // 2000-03-16, store as c4_String + A(x2 == "name...what's in a"); + // the above change avoids an evaluation order issue in assert below + A(x2 == p2 (r1)); + p3 (r1) = 12345; + p3 (r1) = p3 (r1) + 123; + int x3 = p3 (r1); + A(x3 == 12345 + 123); + } E; + + B(b08, Row expressions, 0) + { + c4_StringProp p1 ("p1"), p2 ("p2"); + c4_IntProp p3 ("p3"); + c4_Row r1; + c4_View v1 = (p1, p2, p3); + v1.SetSize(5); + r1 = v1[1]; + v1[2] = v1[1]; + v1[3] = r1; + v1[4] = v1[4]; + r1 = r1; + } E; + + B(b09, View manipulation, 0) + { + c4_StringProp p1 ("p1"), p2 ("p2"); + c4_Row r1 = p1 ["One"] + p2 ["Two"]; + c4_Row r2; + c4_View v1; + v1.Add(r1); + v1.Add(r2); + v1.Add(r1); + A(v1.GetSize() == 3); + A(v1[0] == r1); + A(v1[1] == r2); + A(v1[2] == r1); + v1.RemoveAt(1, 1); + A(v1.GetSize() == 2); + A(v1[0] == r1); + A(v1[0] == v1[1]); + } E; + + B(b10, View sorting, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + v1.Add(p1 [111]); + v1.Add(p1 [222]); + v1.Add(p1 [333]); + v1.Add(p1 [345]); + v1.Add(p1 [234]); + v1.Add(p1 [123]); + c4_View v2 = v1.Sort(); + A(v2.GetSize() == 6); + A(p1 (v2[0]) == 111); + A(p1 (v2[1]) == 123); + A(p1 (v2[2]) == 222); + A(p1 (v2[3]) == 234); + A(p1 (v2[4]) == 333); + A(p1 (v2[5]) == 345); + } E; + + B(b11, View selection, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + v1.Add(p1 [111]); + v1.Add(p1 [222]); + v1.Add(p1 [333]); + v1.Add(p1 [345]); + v1.Add(p1 [234]); + v1.Add(p1 [123]); + c4_View v2 = v1.SelectRange(p1 [200], p1 [333]); + A(v2.GetSize() == 3); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 234); + } E; + + B(b12, Add after remove, 0) + { + c4_StringProp p1 ("p1"); + c4_View v1; + v1.Add(p1 ["abc"]); + A(v1.GetSize() == 1); + v1.RemoveAt(0); + A(v1.GetSize() == 0); + v1.Add(p1 ["def"]); + A(v1.GetSize() == 1); + } E; + + B(b13, Clear view entry, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + + v1.Add(p1 [123]); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + + v1[0] = c4_Row (); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 0); + } E; + + B(b14, Empty view outlives temp storage, 0) + { + c4_View v1; + c4_Storage s1; + v1 = s1.GetAs("a[p1:I,p2:S]"); + } E; + + B(b15, View outlives temp storage, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + + { + c4_Storage s1; + v1 = s1.GetAs("a[p1:I,p2:S]"); + v1.Add(p1 [123]); + } + + // 19990916 - semantics changed, view now 1 row, but 0 props + A(v1.GetSize() == 1); + A(v1.NumProperties() == 0); + //A(p1 (v1[0]) == 123); + } E; + + B(b16, View outlives cleared temp storage, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + + { + c4_Storage s1; + v1 = s1.GetAs("a[p1:I,p2:S]"); + v1.Add(p1 [123]); + v1.RemoveAll(); + } + + A(v1.GetSize() == 0); + v1.Add(p1 [123]); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + } E; + +#if !q4_TINY + B(b17, Double property, 0) + { + c4_Row r1; + c4_DoubleProp p1 ("p1"); + p1 (r1) = 1234.5678; + double x1 = p1 (r1); + A(x1 == (double) 1234.5678); + } E; +#endif + + B(b18, SetAtGrow usage, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + + v1.SetAtGrow(3, p1 [333]); + v1.SetAtGrow(1, p1 [111]); + v1.SetAtGrow(5, p1 [555]); + + A(v1.GetSize() == 6); + A(p1 (v1[1]) == 111); + A(p1 (v1[3]) == 333); + A(p1 (v1[5]) == 555); + } E; + + B(b19, Bytes property, 0) + { + c4_Row r1; + c4_BytesProp p1 ("p1"); + c4_Bytes x1 ("hi!", 3); + + p1 (r1) = x1; + c4_Bytes x2 = p1 (r1); + A(x1 == x2); + } E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tbasic2.cpp b/akregator/src/mk4storage/metakit/tests/tbasic2.cpp new file mode 100644 index 000000000..53c6b307f --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tbasic2.cpp @@ -0,0 +1,218 @@ +// tbasic2.cpp -- Regression test program, basic tests part 2 +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +#include "regress.h" + + static int ViewSize(c4_View v) + { + return v.GetSize(); + } + +void TestBasics2() +{ + B(b20, Search sorted view, 0) + { + c4_IntProp p1 ("p1"); + c4_StringProp p2 ("p2"); + c4_View v1; + v1.Add(p1 [111] + p2 ["one"]); + v1.Add(p1 [222] + p2 ["two"]); + v1.Add(p1 [333] + p2 ["three"]); + v1.Add(p1 [345] + p2 ["four"]); + v1.Add(p1 [234] + p2 ["five"]); + v1.Add(p1 [123] + p2 ["six"]); + c4_View v2 = v1.Sort(); + A(v2.GetSize() == 6); + A(p1 (v2[0]) == 111); + A(p1 (v2[1]) == 123); + A(p1 (v2[2]) == 222); + A(p1 (v2[3]) == 234); + A(p1 (v2[4]) == 333); + A(p1 (v2[5]) == 345); + A(v2.Search(p1 [123]) == 1); + A(v2.Search(p1 [100]) == 0); + A(v2.Search(p1 [200]) == 2); + A(v2.Search(p1 [400]) == 6); + c4_View v3 = v1.SortOn(p2); + A(v3.GetSize() == 6); + A(p1 (v3[0]) == 234); + A(p1 (v3[1]) == 345); + A(p1 (v3[2]) == 111); + A(p1 (v3[3]) == 123); + A(p1 (v3[4]) == 333); + A(p1 (v3[5]) == 222); + A(v3.Search(p2 ["six"]) == 3); + A(v3.Search(p2 ["aha"]) == 0); + A(v3.Search(p2 ["gee"]) == 2); + A(v3.Search(p2 ["wow"]) == 6); + c4_View v4 = v1.SortOnReverse(p2, p2); + A(v4.GetSize() == 6); + A(p1 (v4[0]) == 222); + A(p1 (v4[1]) == 333); + A(p1 (v4[2]) == 123); + A(p1 (v4[3]) == 111); + A(p1 (v4[4]) == 345); + A(p1 (v4[5]) == 234); + A(v4.Search(p2 ["six"]) == 2); + A(v4.Search(p2 ["aha"]) == 6); + A(v4.Search(p2 ["gee"]) == 4); + A(v4.Search(p2 ["wow"]) == 0); + } E; + + B(b21, Memo property, 0) + { + c4_Row r1; + c4_MemoProp p1 ("p1"); + c4_Bytes x1 ("hi!", 3); + + p1 (r1) = x1; + c4_Bytes x2 = p1 (r1); + A(x1 == x2); + } E; + + B(b22, Stored view references, 0) + { + c4_ViewProp p1 ("p1"); + c4_View v1; + + { + v1.Add(p1 [c4_View ()]); + } + + // this works + int n = ViewSize(p1.Get(v1[0])); + A(n == 0); + + // this fails in 1.8b2 using MSVC 1.52 (tq_wvus) + // + // The compiler destructs temp c4_View once too often, or + // what's more likely, fails to call the constructor once. + // + // So for MSVC 1.52: use prop.Get(rowref) for subviews, + // or immediately assign the result to a c4_View object, + // do not pass a "prop (rowref)" expression as argument. + +#if _MSC_VER != 800 + int m = ViewSize(p1 (v1[0])); + A(m == 0); +#endif + } E; + + B(b23, Sort comparison fix, 0) // 1.9e: compare buffering problem + { + c4_DoubleProp p1 ("p1"); + c4_View v1; + for (int i = 0; i < 100; ++i) + v1.Add(p1 [99-i]); + c4_View v2 = v1.Sort(); + A(v2.GetSize() == 100); + for (int j = 0; j < 100; ++j) + { + A(p1 (v1[j]) == (double) 99-j); + A(p1 (v2[j]) == (double) j); + } + } E; + + B(b24, Custom view comparisons, 0) // 1.9f: more compare cache problems + { + c4_IntProp p1 ("p1"); + c4_FloatProp p2 ("p2"); + c4_DoubleProp p3 ("p3"); + c4_IntProp p4 ("p4"); + c4_View v1; + v1.Add(p1 [2] + p2 [2] + p3 [2]); + v1.Add(p1 [1] + p2 [1] + p3 [1]); + v1.Add(p1 [3] + p2 [3] + p3 [3]); + A(v1.GetSize() == 3); + A((int) p1 (v1[0]) > (int) p1 (v1[1])); + A((float) p2 (v1[0]) > (float) p2 (v1[1])); + A((double) p3 (v1[0]) > (double) p3 (v1[1])); + A((int) p1 (v1[0]) < (int) p1 (v1[2])); + A((float) p2 (v1[0]) < (float) p2 (v1[2])); + A((double) p3 (v1[0]) < (double) p3 (v1[2])); + c4_View v2 = v1.Unique(); + A(v2.GetSize() == 3); + A((int) p1 (v2[0]) != (int) p1 (v2[1])); + A((float) p2 (v2[0]) != (float) p2 (v2[1])); + A((double) p3 (v2[0]) != (double) p3 (v2[1])); + A((int) p1 (v2[0]) != (int) p1 (v2[2])); + A((float) p2 (v2[0]) != (float) p2 (v2[2])); + A((double) p3 (v2[0]) != (double) p3 (v2[2])); + v1.Add(p1 [2] + p2 [2] + p3 [2]); + v1.Add(p1 [1] + p2 [1] + p3 [1]); + v1.Add(p1 [3] + p2 [3] + p3 [3]); + c4_View v3 = v1.Unique(); + A(v3.GetSize() == 3); + A((int) p1 (v3[0]) != (int) p1 (v3[1])); + A((float) p2 (v3[0]) != (float) p2 (v3[1])); + A((double) p3 (v3[0]) != (double) p3 (v3[1])); + A((int) p1 (v3[0]) != (int) p1 (v3[2])); + A((float) p2 (v3[0]) != (float) p2 (v3[2])); + A((double) p3 (v3[0]) != (double) p3 (v3[2])); + c4_View v4 = v1.Counts(p1, p4); + A(v4.GetSize() == 3); + c4_View v5 = v1.Counts(p2, p4); + A(v5.GetSize() == 3); // this failed in 1.9f + c4_View v6 = v1.Counts(p3, p4); + A(v6.GetSize() == 3); // this failed in 1.9f + } E; + + B(b25, Copy row from derived, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + v1.Add(p1 [111]); + v1.Add(p1 [222]); + v1.Add(p1 [333]); + c4_View v2 = v1.Select(p1 [222]); + A(v2.GetSize() == 1); + A(p1 (v2[0]) == 222); + c4_Row r = v2[0]; + A(p1 (r) == 222); // 1.9g: failed because SetAt did not remap + } E; + + B(b26, Partial memo field access, 0) + { + c4_BytesProp p1 ("p1"); + c4_View v1; + v1.Add(p1 [c4_Bytes ("12345", 5)]); + A(v1.GetSize() == 1); + c4_Bytes buf = p1 (v1[0]); + A(buf.Size() == 5); + A(buf == c4_Bytes ("12345", 5)); + buf = p1(v1[0]).Access(1,3); + A(buf == c4_Bytes ("234", 3)); + p1 (v1[0]).Modify(c4_Bytes ("ab", 2), 2, 0); + buf = p1 (v1[0]); + A(buf == c4_Bytes ("12ab5", 5)); + p1 (v1[0]).Modify(c4_Bytes ("ABC", 3), 1, 2); + buf = p1 (v1[0]); + A(buf == c4_Bytes ("1ABCab5", 7)); + p1 (v1[0]).Modify(c4_Bytes ("xyz", 3), 2, -2); + buf = p1 (v1[0]); + A(buf == c4_Bytes ("1Axyz", 5)); + p1 (v1[0]).Modify(c4_Bytes ("3456", 4), 4, 0); + buf = p1 (v1[0]); + A(buf == c4_Bytes ("1Axy3456", 8)); + } E; + + B(b27, Copy value to another row, 0) + { + c4_StringProp p1 ("p1"); + c4_View v1; + v1.SetSize(2); + p1 (v1[1]) = "abc"; + // next assert fails in MacOS X 10.2.1 "Jaguar" with 2.4.7 + // seems bug in gcc 3.1, -O i.s.o. -O2 works [jcw 21oct02] + A((const char*) (p1 (v1[0])) == (c4_String) ""); + A((const char*) (p1 (v1[1])) == (c4_String) "abc"); + + // fails in 2.4.0, reported by Jerry McRae, August 2001 + p1 (v1[0]) = (const char*) p1 (v1[1]); + // MacOS 10.2.3 gcc 3.1 dec 2002 is still weird, inserting the + // following code (which should be a noop) *fixes* the assert! + //const char* q = p1 (v1[1]); + A((c4_String)(const char*) (p1 (v1[0])) == (c4_String) "abc"); + } E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tcusto1.cpp b/akregator/src/mk4storage/metakit/tests/tcusto1.cpp new file mode 100644 index 000000000..38161d5fc --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tcusto1.cpp @@ -0,0 +1,277 @@ +// tcusto1.cpp -- Regression test program, custom view tests +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestCustom1() +{ + B(c01, Slice forward, 0) + { + c4_IntProp p1 ("p1"); + + c4_View v1; + v1.Add(p1 [123]); + v1.Add(p1 [234]); + v1.Add(p1 [345]); + v1.Add(p1 [456]); + v1.Add(p1 [567]); + + c4_View v2 = v1.Slice(1, -1, 2); + A(v2.GetSize() == 2); + A(p1 (v2[0]) == 234); + A(p1 (v2[1]) == 456); + + v1.Add(p1 [678]); + A(v1.GetSize() == 6); + A(v2.GetSize() == 3); + A(p1 (v2[2]) == 678); + } E; + + B(c02, Slice backward, 0) + { + c4_IntProp p1 ("p1"); + + c4_View v1; + v1.Add(p1 [123]); + v1.Add(p1 [234]); + v1.Add(p1 [345]); + v1.Add(p1 [456]); + v1.Add(p1 [567]); + + c4_View v2 = v1.Slice(1, -1, -2); + A(v2.GetSize() == 2); + A(p1 (v2[0]) == 456); + A(p1 (v2[1]) == 234); + + v1.Add(p1 [678]); + A(v1.GetSize() == 6); + A(v2.GetSize() == 3); + A(p1 (v2[0]) == 678); + A(p1 (v2[1]) == 456); + A(p1 (v2[2]) == 234); + } E; + + B(c03, Slice reverse, 0) + { + c4_IntProp p1 ("p1"); + + c4_View v1; + v1.Add(p1 [123]); + v1.Add(p1 [234]); + v1.Add(p1 [345]); + v1.Add(p1 [456]); + v1.Add(p1 [567]); + + c4_View v2 = v1.Slice(1, 5, -1); + A(v2.GetSize() == 4); + A(p1 (v2[0]) == 567); + A(p1 (v2[1]) == 456); + A(p1 (v2[2]) == 345); + A(p1 (v2[3]) == 234); + + v1.Add(p1 [678]); + A(v1.GetSize() == 6); + A(v2.GetSize() == 4); + } E; + + B(c04, Cartesian product, 0) + { + c4_IntProp p1 ("p1"); + c4_IntProp p2 ("p2"); + + c4_View v1; + v1.Add(p1 [123]); + v1.Add(p1 [234]); + v1.Add(p1 [345]); + + c4_View v2; + v2.Add(p2 [111]); + v2.Add(p2 [222]); + + c4_View v3 = v1.Product(v2); + A(v3.GetSize() == 6); + A(p1 (v3[0]) == 123); + A(p2 (v3[0]) == 111); + A(p1 (v3[1]) == 123); + A(p2 (v3[1]) == 222); + A(p1 (v3[2]) == 234); + A(p2 (v3[2]) == 111); + A(p1 (v3[3]) == 234); + A(p2 (v3[3]) == 222); + A(p1 (v3[4]) == 345); + A(p2 (v3[4]) == 111); + A(p1 (v3[5]) == 345); + A(p2 (v3[5]) == 222); + + v1.Add(p1 [456]); + A(v3.GetSize() == 8); + v2.Add(p2 [333]); + A(v3.GetSize() == 12); + } E; + + B(c05, Remapping, 0) + { + c4_IntProp p1 ("p1"); + + c4_View v1; + v1.Add(p1 [123]); + v1.Add(p1 [234]); + v1.Add(p1 [345]); + + c4_View v2; + v2.Add(p1 [2]); + v2.Add(p1 [0]); + v2.Add(p1 [1]); + v2.Add(p1 [0]); + + c4_View v3 = v1.RemapWith(v2); + A(v3.GetSize() == 4); + A(p1 (v3[0]) == 345); + A(p1 (v3[1]) == 123); + A(p1 (v3[2]) == 234); + A(p1 (v3[3]) == 123); + } E; + + B(c06, Pairwise combination, 0) + { + c4_IntProp p1 ("p1"); + c4_IntProp p2 ("p2"); + + c4_View v1; + v1.Add(p1 [123]); + v1.Add(p1 [234]); + v1.Add(p1 [345]); + + c4_View v2; + v2.Add(p2 [111]); + v2.Add(p2 [222]); + v2.Add(p2 [333]); + + c4_View v3 = v1.Pair(v2); + A(v3.GetSize() == 3); + A(p1 (v3[0]) == 123); + A(p2 (v3[0]) == 111); + A(p1 (v3[1]) == 234); + A(p2 (v3[1]) == 222); + A(p1 (v3[2]) == 345); + A(p2 (v3[2]) == 333); + } E; + + B(c07, Concatenate views, 0) + { + c4_IntProp p1 ("p1"); + + c4_View v1; + v1.Add(p1 [123]); + v1.Add(p1 [234]); + v1.Add(p1 [345]); + + c4_View v2; + v2.Add(p1 [111]); + v2.Add(p1 [222]); + + c4_View v3 = v1.Concat(v2); + A(v3.GetSize() == 5); + A(p1 (v3[0]) == 123); + A(p1 (v3[1]) == 234); + A(p1 (v3[2]) == 345); + A(p1 (v3[3]) == 111); + A(p1 (v3[4]) == 222); + } E; + + B(c08, Rename property, 0) + { + c4_IntProp p1 ("p1"); + c4_IntProp p2 ("p2"); + + c4_View v1; + v1.Add(p1 [123]); + v1.Add(p1 [234]); + v1.Add(p1 [345]); + + c4_View v2 = v1.Rename(p1, p2); + A(v2.GetSize() == 3); + A(p2 (v2[0]) == 123); + A(p2 (v2[1]) == 234); + A(p2 (v2[2]) == 345); + A(p1 (v2[0]) == 0); + A(p1 (v2[1]) == 0); + A(p1 (v2[2]) == 0); + } E; + + B(c09, GroupBy operation, 0) + { + c4_StringProp p1 ("p1"); + c4_IntProp p2 ("p2"); + c4_ViewProp p3 ("p3"); + + c4_View v1, v2, v3; + + v1.Add(p1 [""]); + v1.Add(p1 ["1"] + p2 [1]); + v1.Add(p1 ["12"] + p2 [1]); + v1.Add(p1 ["12"] + p2 [2]); + v1.Add(p1 ["123"] + p2 [1]); + v1.Add(p1 ["123"] + p2 [2]); + v1.Add(p1 ["123"] + p2 [3]); + + v2 = v1.GroupBy(p1, p3); + A(v2.GetSize() == 4); + A(p1 (v2[0]) == (c4_String) ""); + A(p1 (v2[1]) == (c4_String) "1"); + A(p1 (v2[2]) == (c4_String) "12"); + A(p1 (v2[3]) == (c4_String) "123"); + + v3 = p3 (v2[0]); + A(v3.GetSize() == 1); + A(p2 (v3[0]) == 0); + v3 = p3 (v2[1]); + A(v3.GetSize() == 1); + A(p2 (v3[0]) == 1); + v3 = p3 (v2[2]); + A(v3.GetSize() == 2); + A(p2 (v3[0]) == 1); + A(p2 (v3[1]) == 2); + v3 = p3 (v2[3]); + A(v3.GetSize() == 3); + A(p2 (v3[0]) == 1); + A(p2 (v3[1]) == 2); + A(p2 (v3[2]) == 3); + + } E; + + B(c10, Counts operation, 0) + { + c4_StringProp p1 ("p1"); + c4_IntProp p2 ("p2"), p3 ("p3"); + + c4_View v1, v2, v3; + + v1.Add(p1 [""]); + v1.Add(p1 ["1"] + p2 [1]); + v1.Add(p1 ["12"] + p2 [1]); + v1.Add(p1 ["12"] + p2 [2]); + v1.Add(p1 ["123"] + p2 [1]); + v1.Add(p1 ["123"] + p2 [2]); + v1.Add(p1 ["123"] + p2 [3]); + + v2 = v1.Counts(p1, p3); + A(v2.GetSize() == 4); + A(p1 (v2[0]) == (c4_String) ""); + A(p1 (v2[1]) == (c4_String) "1"); + A(p1 (v2[2]) == (c4_String) "12"); + A(p1 (v2[3]) == (c4_String) "123"); + + A(p2 (v2[0]) == 0); + A(p2 (v2[1]) == 0); + A(p2 (v2[2]) == 0); + A(p2 (v2[3]) == 0); + + A(p3 (v2[0]) == 1); + A(p3 (v2[1]) == 1); + A(p3 (v2[2]) == 2); + A(p3 (v2[3]) == 3); + + } E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tcusto2.cpp b/akregator/src/mk4storage/metakit/tests/tcusto2.cpp new file mode 100644 index 000000000..53949592d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tcusto2.cpp @@ -0,0 +1,441 @@ +// tcusto2.cpp -- Regression test program, custom view tests +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestCustom2() +{ + B(c11, Unique operation, 0); + { + c4_IntProp p1 ("p1"), p2 ("p2"); + + c4_View v1, v2, v3; + + v1.Add(p1 [1] + p2 [11]); + v1.Add(p1 [1] + p2 [22]); + v1.Add(p1 [2] + p2 [33]); + v1.Add(p1 [2] + p2 [33]); + v1.Add(p1 [3] + p2 [44]); + v1.Add(p1 [4] + p2 [55]); + v1.Add(p1 [4] + p2 [55]); + v1.Add(p1 [4] + p2 [55]); + + v2 = v1.Unique(); + A(v2.GetSize() == 5); + A(p1 (v2[0]) == 1); + A(p1 (v2[1]) == 1); + A(p1 (v2[2]) == 2); + A(p1 (v2[3]) == 3); + A(p1 (v2[4]) == 4); + + A(p2 (v2[0]) == 11); + A(p2 (v2[1]) == 22); + A(p2 (v2[2]) == 33); + A(p2 (v2[3]) == 44); + A(p2 (v2[4]) == 55); + + } E; + + B(c12, Union operation, 0) + { + c4_IntProp p1 ("p1"); + + c4_View v1, v2, v3; + + v1.Add(p1 [1]); + v1.Add(p1 [2]); + v1.Add(p1 [3]); + + v2.Add(p1 [2]); + v2.Add(p1 [3]); + v2.Add(p1 [4]); + v2.Add(p1 [5]); + + v3 = v1.Union(v2); + A(v3.GetSize() == 5); + A(p1 (v3[0]) == 1); + A(p1 (v3[1]) == 2); + A(p1 (v3[2]) == 3); + A(p1 (v3[3]) == 4); + A(p1 (v3[4]) == 5); + } E; + + B(c13, Intersect operation, 0) + { + c4_IntProp p1 ("p1"); + + c4_View v1, v2, v3; + + v1.Add(p1 [1]); + v1.Add(p1 [2]); + v1.Add(p1 [3]); + + v2.Add(p1 [2]); + v2.Add(p1 [3]); + v2.Add(p1 [4]); + v2.Add(p1 [5]); + + v3 = v1.Intersect(v2); + A(v3.GetSize() == 2); + A(p1 (v3[0]) == 2); + A(p1 (v3[1]) == 3); + } E; + + B(c14, Different operation, 0) + { + c4_IntProp p1 ("p1"); + + c4_View v1, v2, v3; + + v1.Add(p1 [1]); + v1.Add(p1 [2]); + v1.Add(p1 [3]); + + v2.Add(p1 [2]); + v2.Add(p1 [3]); + v2.Add(p1 [4]); + v2.Add(p1 [5]); + + v3 = v1.Different(v2); + A(v3.GetSize() == 3); + A(p1 (v3[0]) == 1); + A(p1 (v3[1]) == 4); + A(p1 (v3[2]) == 5); + } E; + + B(c15, Minus operation, 0) + { + c4_IntProp p1 ("p1"); + + c4_View v1, v2, v3; + + v1.Add(p1 [1]); + v1.Add(p1 [2]); + v1.Add(p1 [3]); + + v2.Add(p1 [2]); + v2.Add(p1 [3]); + v2.Add(p1 [4]); + v2.Add(p1 [5]); + + v3 = v1.Minus(v2); + A(v3.GetSize() == 1); + A(p1 (v3[0]) == 1); + } E; + + B(c16, View comparisons, 0) + { + c4_IntProp p1 ("p1"); + + c4_View v1; + v1.Add(p1 [1]); + v1.Add(p1 [2]); + v1.Add(p1 [3]); + v1.Add(p1 [4]); + v1.Add(p1 [5]); + + A(v1 == v1); + A(v1 == v1.Slice(0)); + A(v1.Slice(0,2) < v1.Slice(0,3)); + A(v1.Slice(0,3) == v1.Slice(0,3)); + A(v1.Slice(0,4) > v1.Slice(0,3)); + A(v1.Slice(0,3) < v1.Slice(1,3)); + A(v1.Slice(0,3) < v1.Slice(1,4)); + A(v1.Slice(1,3) > v1.Slice(0,3)); + A(v1.Slice(1,4) > v1.Slice(0,3)); + } E; + + B(c17, Join operation, 0) + { + c4_StringProp p1 ("p1"), p2 ("p2"); + c4_IntProp p3 ("p3"); + + c4_View v1, v2, v3; + + v1.Add(p1 [""]); + v1.Add(p1 ["1"] + p2 ["a"]); + v1.Add(p1 ["12"] + p2 ["ab"]); + v1.Add(p1 ["123"] + p2 ["abc"]); + + v2.Add(p1 ["1"] + p3 [1]); + v2.Add(p1 ["12"] + p3 [1]); + v2.Add(p1 ["12"] + p3 [2]); + v2.Add(p1 ["123"] + p3 [1]); + v2.Add(p1 ["123"] + p3 [2]); + v2.Add(p1 ["123"] + p3 [3]); + + v3 = v1.Join(p1, v2); // inner join + A(v3.GetSize() == 6); + + A(p1 (v3[0]) == (c4_String) "1"); + A(p1 (v3[1]) == (c4_String) "12"); + A(p1 (v3[2]) == (c4_String) "12"); + A(p1 (v3[3]) == (c4_String) "123"); + A(p1 (v3[4]) == (c4_String) "123"); + A(p1 (v3[5]) == (c4_String) "123"); + + A(p2 (v3[0]) == (c4_String) "a"); + A(p2 (v3[1]) == (c4_String) "ab"); + A(p2 (v3[2]) == (c4_String) "ab"); + A(p2 (v3[3]) == (c4_String) "abc"); + A(p2 (v3[4]) == (c4_String) "abc"); + A(p2 (v3[5]) == (c4_String) "abc"); + + A(p3 (v3[0]) == 1); + A(p3 (v3[1]) == 1); + A(p3 (v3[2]) == 2); + A(p3 (v3[3]) == 1); + A(p3 (v3[4]) == 2); + A(p3 (v3[5]) == 3); + + v3 = v1.Join(p1, v2, true); // outer join + A(v3.GetSize() == 7); + + A(p1 (v3[0]) == (c4_String) ""); + A(p1 (v3[1]) == (c4_String) "1"); + A(p1 (v3[2]) == (c4_String) "12"); + A(p1 (v3[3]) == (c4_String) "12"); + A(p1 (v3[4]) == (c4_String) "123"); + A(p1 (v3[5]) == (c4_String) "123"); + A(p1 (v3[6]) == (c4_String) "123"); + + A(p2 (v3[0]) == (c4_String) ""); + A(p2 (v3[1]) == (c4_String) "a"); + A(p2 (v3[2]) == (c4_String) "ab"); + A(p2 (v3[3]) == (c4_String) "ab"); + A(p2 (v3[4]) == (c4_String) "abc"); + A(p2 (v3[5]) == (c4_String) "abc"); + A(p2 (v3[6]) == (c4_String) "abc"); + + A(p3 (v3[0]) == 0); + A(p3 (v3[1]) == 1); + A(p3 (v3[2]) == 1); + A(p3 (v3[3]) == 2); + A(p3 (v3[4]) == 1); + A(p3 (v3[5]) == 2); + A(p3 (v3[6]) == 3); + } E; + + B(c18, Groupby sort fix, 0) // fails in 1.8.4 (from P. Ritter, 14-10-1998) + { + c4_StringProp p1 ("Country"); + c4_StringProp p2 ("City"); + c4_ViewProp p3 ("SubList"); + + c4_View v1, v2, v3; + + v1.Add(p1 ["US"] + p2 ["Philadelphia"]); + v1.Add(p1 ["France"] + p2 ["Bordeaux"]); + v1.Add(p1 ["US"] + p2 ["Miami"]); + v1.Add(p1 ["France"] + p2 ["Paris"]); + v1.Add(p1 ["US"] + p2 ["Boston"]); + v1.Add(p1 ["France"] + p2 ["Nice"]); + v1.Add(p1 ["US"] + p2 ["NY"]); + v1.Add(p1 ["US"] + p2 ["Miami"]); + + v2 = v1.GroupBy(p1, p3); + A(v2.GetSize() == 2); + A(p1 (v2[0]) == (c4_String) "France"); + A(p1 (v2[1]) == (c4_String) "US"); + + v3 = p3 (v2[0]); + A(v3.GetSize() == 3); + A(p2 (v3[0]) == (c4_String) "Bordeaux"); + A(p2 (v3[1]) == (c4_String) "Nice"); + A(p2 (v3[2]) == (c4_String) "Paris"); + v3 = p3 (v2[1]); + A(v3.GetSize() == 5); + A(p2 (v3[0]) == (c4_String) "Boston"); + A(p2 (v3[1]) == (c4_String) "Miami"); + A(p2 (v3[2]) == (c4_String) "Miami"); + A(p2 (v3[3]) == (c4_String) "NY"); + A(p2 (v3[4]) == (c4_String) "Philadelphia"); + } E; + + B(c19, JoinProp operation, 0) // moved, used to also be called c15 + { + c4_StringProp p1 ("p1"); + c4_ViewProp p2 ("p2"); + c4_IntProp p3 ("p3"); + + c4_View v1, v2a, v2b, v2c, v3; + + v2a.Add(p3 [1]); + v2a.Add(p3 [2]); + v2a.Add(p3 [3]); + v1.Add(p1 ["123"] + p2 [v2a]); + + v2b.Add(p3 [1]); + v2b.Add(p3 [2]); + v1.Add(p1 ["12"] + p2 [v2b]); + + v2c.Add(p3 [1]); + v1.Add(p1 ["1"] + p2 [v2c]); + + v1.Add(p1 [""]); + + v3 = v1.JoinProp(p2); // inner join + A(v3.GetSize() == 6); + + A(p1 (v3[0]) == (c4_String) "123"); + A(p1 (v3[1]) == (c4_String) "123"); + A(p1 (v3[2]) == (c4_String) "123"); + A(p1 (v3[3]) == (c4_String) "12"); + A(p1 (v3[4]) == (c4_String) "12"); + A(p1 (v3[5]) == (c4_String) "1"); + + A(p3 (v3[0]) == 1); + A(p3 (v3[1]) == 2); + A(p3 (v3[2]) == 3); + A(p3 (v3[3]) == 1); + A(p3 (v3[4]) == 2); + A(p3 (v3[5]) == 1); + + v3 = v1.JoinProp(p2, true); // outer join + A(v3.GetSize() == 7); + + A(p1 (v3[0]) == (c4_String) "123"); + A(p1 (v3[1]) == (c4_String) "123"); + A(p1 (v3[2]) == (c4_String) "123"); + A(p1 (v3[3]) == (c4_String) "12"); + A(p1 (v3[4]) == (c4_String) "12"); + A(p1 (v3[5]) == (c4_String) "1"); + A(p1 (v3[6]) == (c4_String) ""); + + A(p3 (v3[0]) == 1); + A(p3 (v3[1]) == 2); + A(p3 (v3[2]) == 3); + A(p3 (v3[3]) == 1); + A(p3 (v3[4]) == 2); + A(p3 (v3[5]) == 1); + A(p3 (v3[6]) == 0); + } E; + + B(c20, Wide cartesian product, 0) + { + // added 2nd prop's to do a better test - 1999-12-23 + c4_IntProp p1 ("p1"); + c4_IntProp p2 ("p2"); + c4_IntProp p3 ("p3"); + c4_IntProp p4 ("p4"); + + c4_View v1; + v1.Add(p1 [123] + p2 [321]); + v1.Add(p1 [234] + p2 [432]); + v1.Add(p1 [345] + p2 [543]); + + c4_View v2; + v2.Add(p3 [111] + p4 [11]); + v2.Add(p3 [222] + p4 [22]); + + c4_View v3 = v1.Product(v2); + A(v3.GetSize() == 6); + A(p1 (v3[0]) == 123); + A(p2 (v3[0]) == 321); + A(p3 (v3[0]) == 111); + A(p4 (v3[0]) == 11); + A(p1 (v3[1]) == 123); + A(p2 (v3[1]) == 321); + A(p3 (v3[1]) == 222); + A(p4 (v3[1]) == 22); + A(p1 (v3[2]) == 234); + A(p2 (v3[2]) == 432); + A(p3 (v3[2]) == 111); + A(p4 (v3[2]) == 11); + A(p1 (v3[3]) == 234); + A(p2 (v3[3]) == 432); + A(p3 (v3[3]) == 222); + A(p4 (v3[3]) == 22); + A(p1 (v3[4]) == 345); + A(p2 (v3[4]) == 543); + A(p3 (v3[4]) == 111); + A(p4 (v3[4]) == 11); + A(p1 (v3[5]) == 345); + A(p2 (v3[5]) == 543); + A(p3 (v3[5]) == 222); + A(p4 (v3[5]) == 22); + + v1.Add(p1 [456]); + A(v3.GetSize() == 8); + v2.Add(p2 [333]); + A(v3.GetSize() == 12); + } E; + + B(c21, Join on compound key, 0) + { + c4_IntProp p1 ("p1"), p2 ("p2"), p3 ("p3"), p4 ("p4"); + + c4_View v1, v2, v3; + + v1.Add(p1 [1] + p2 [11] + p3 [111]); + v1.Add(p1 [2] + p2 [22] + p3 [222]); + v1.Add(p1 [3] + p2 [22] + p3 [111]); + + v2.Add(p2 [11] + p3 [111] + p4 [1111]); + v2.Add(p2 [22] + p3 [222] + p4 [2222]); + v2.Add(p2 [22] + p3 [222] + p4 [3333]); + v2.Add(p2 [22] + p3 [333] + p4 [4444]); + + // this works here, but it fails in Python, i.e. Mk4py 2.4.0 + v3 = v1.Join((p2, p3), v2); + + A(v3.GetSize() == 3); + + A(p1 (v3[0]) == 1); + A(p1 (v3[1]) == 2); + A(p1 (v3[2]) == 2); + + A(p2 (v3[0]) == 11); + A(p2 (v3[1]) == 22); + A(p2 (v3[2]) == 22); + + A(p3 (v3[0]) == 111); + A(p3 (v3[1]) == 222); + A(p3 (v3[2]) == 222); + + A(p4 (v3[0]) == 1111); + A(p4 (v3[1]) == 2222); + A(p4 (v3[2]) == 3333); + } E; + + B(c22, Groupby with selection, 0) + { + c4_Storage s1; + c4_View v1 = s1.GetAs("v1[p1:I,p2:I,p3:I]"); + c4_IntProp p1 ("p1"), p2 ("p2"), p3 ("p3"); + c4_ViewProp p4 ("p4"); + + v1.Add(p1[0] + p2[1] + p3[10]); + v1.Add(p1[1] + p2[1] + p3[20]); + v1.Add(p1[2] + p2[2] + p3[30]); + v1.Add(p1[3] + p2[3] + p3[40]); + v1.Add(p1[4] + p2[3] + p3[50]); + + s1.Commit(); + A(v1.GetSize() == 5); + + c4_View v2 = v1.GroupBy(p2,p4); + A(v2.GetSize() == 3); + + c4_View v3 = p4 (v2[0]); + A(v3.GetSize() == 2); + A(p3 (v3[0]) == 10); + A(p3 (v3[1]) == 20); + + c4_View v4 = p4 (v2[1]); + A(v4.GetSize() == 1); + A(p3 (v4[0]) == 30); + + c4_View v5 = p4 (v2[2]); + A(v5.GetSize() == 2); + A(p3 (v5[0]) == 40); + A(p3 (v5[1]) == 50); + + c4_View v6 = v4.Sort(); + A(v6.GetSize() == 1); + A(p1 (v6[0]) == 2); + A(p3 (v6[0]) == 30); + + } E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tdiffer.cpp b/akregator/src/mk4storage/metakit/tests/tdiffer.cpp new file mode 100644 index 000000000..16dc97c34 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tdiffer.cpp @@ -0,0 +1,52 @@ +// tdiffer.cpp -- Regression test program, differential commit tests +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestDiffer() +{ + B(d01, Commit aside, 0) W(d01a); W(d01b); + { + c4_IntProp p1 ("p1"); + { + c4_Storage s1 ("d01a", 1); + A(s1.Strategy().FileSize() == 0); + c4_View v1 = s1.GetAs("a[p1:I]"); + v1.Add(p1 [123]); + s1.Commit(); + } + { + c4_Storage s1 ("d01a", 0); + c4_Storage s2 ("d01b", 1); + s1.SetAside(s2); + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + v1.Add(p1 [456]); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 123); + A(p1 (v1[1]) == 456); + s1.Commit(); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 123); + A(p1 (v1[1]) == 456); + s2.Commit(); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 123); + A(p1 (v1[1]) == 456); + } + { + c4_Storage s1 ("d01a", 0); + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + c4_Storage s2 ("d01b", 0); + s1.SetAside(s2); + c4_View v2 = s1.View("a"); + A(v2.GetSize() == 2); + A(p1 (v2[0]) == 123); + A(p1 (v2[1]) == 456); + } + } D(d01a); D(d01b); R(d01a); R(d01b); E; +} diff --git a/akregator/src/mk4storage/metakit/tests/textend.cpp b/akregator/src/mk4storage/metakit/tests/textend.cpp new file mode 100644 index 000000000..98331f1af --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/textend.cpp @@ -0,0 +1,195 @@ +// textend.cpp -- Regression test program, commit extend tests +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +#include "regress.h" + +const int kSize1 = 41; +const int kSize2 = 85; + +void TestExtend() +{ + B(e01, Extend new file, 0) W(e01a); + { + c4_IntProp p1 ("p1"); + c4_Storage s1 ("e01a", 2); + A(s1.Strategy().FileSize() == 0); + c4_View v1 = s1.GetAs("a[p1:I]"); + v1.Add(p1 [123]); + s1.Commit(); + A(s1.Strategy().FileSize() == kSize1); + v1.Add(p1 [456]); + s1.Commit(); + A(s1.Strategy().FileSize() == kSize2); + } D(e01a); R(e01a); E; + + B(e02, Extend committing twice, 0) W(e02a); + { + c4_IntProp p1 ("p1"); + c4_Storage s1 ("e02a", 2); + A(s1.Strategy().FileSize() == 0); + c4_View v1 = s1.GetAs("a[p1:I]"); + v1.Add(p1 [123]); + s1.Commit(); + A(s1.Strategy().FileSize() == kSize1); + s1.Commit(); + A(s1.Strategy().FileSize() == kSize1); + v1.Add(p1 [456]); + s1.Commit(); + A(s1.Strategy().FileSize() == kSize2); + } D(e02a); R(e02a); E; + + B(e03, Read during extend, 0) W(e03a); + { + c4_IntProp p1 ("p1"); + c4_Storage s1 ("e03a", 2); + A(s1.Strategy().FileSize() == 0); + c4_View v1 = s1.GetAs("a[p1:I]"); + v1.Add(p1 [123]); + s1.Commit(); + A(s1.Strategy().FileSize() == kSize1); + + { + c4_Storage s2 ("e03a", 0); + c4_View v2 = s2.View("a"); + A(v2.GetSize() == 1); + A(p1 (v2[0]) == 123); + } + + v1.Add(p1 [456]); + s1.Commit(); + A(s1.Strategy().FileSize() == kSize2); + + { + c4_Storage s3 ("e03a", 0); + c4_View v3 = s3.View("a"); + A(v3.GetSize() == 2); + A(p1 (v3[0]) == 123); + A(p1 (v3[1]) == 456); + } + } D(e03a); R(e03a); E; + + B(e04, Extend during read, 0) W(e04a); + { + c4_IntProp p1 ("p1"); + + { + c4_Storage s1 ("e04a", 2); + A(s1.Strategy().FileSize() == 0); + c4_View v1 = s1.GetAs("a[p1:I]"); + v1.Add(p1 [123]); + s1.Commit(); + A(s1.Strategy().FileSize() == kSize1); + } + + c4_Storage s2 ("e04a", 0); + c4_View v2 = s2.View("a"); + A(v2.GetSize() == 1); + A(p1 (v2[0]) == 123); + + c4_Storage s3 ("e04a", 0); // open, don't load + + { + c4_Storage s4 ("e04a", 2); + A(s4.Strategy().FileSize() == kSize1); + c4_View v4 = s4.View("a"); + v4.Add(p1 [123]); + s4.Commit(); + A(s4.Strategy().FileSize() > kSize1); // == kSize2); + } + + c4_View v2a = s2.View("a"); + A(v2a.GetSize() == 1); + A(p1 (v2a[0]) == 123); + + c4_View v3 = s3.View("a"); + A(v3.GetSize() == 1); + A(p1 (v3[0]) == 123); + + } D(e04a); R(e04a); E; + + B(e05, Test memory mapping, 0) W(e05a); + { + // this is not a test of MK, but of the underlying system code + + { + c4_FileStrategy fs; + bool f1 = fs.DataOpen("e05a", 1); + A(!f1); + fs.DataWrite(0, "hi!", 3); + A(fs._failure == 0); + A(fs.FileSize() == 3); + fs.DataCommit(0); + A(fs.FileSize() == 3); + fs.ResetFileMapping(); + if (fs._mapStart != 0) + { + A(fs._dataSize == 3); + c4_String s ((char*) fs._mapStart, 3); + A(s == "hi!"); + } + fs.DataWrite(3, "hello", 5); + A(fs._failure == 0); + A(fs.FileSize() == 8); + fs.DataCommit(0); + A(fs.FileSize() == 8); + if (fs._mapStart != 0) + { + A(fs._dataSize == 3); + c4_String s ((char*) fs._mapStart, 8); + A(s == "hi!hello"); + } + fs.DataWrite(100, "wow!", 4); + A(fs._failure == 0); + A(fs.FileSize() == 104); + fs.DataCommit(0); + A(fs.FileSize() == 104); + fs.ResetFileMapping(); + if (fs._mapStart != 0) + { + A(fs._dataSize == 104); + c4_String s ((char*) fs._mapStart + 100, 4); + A(s == "wow!"); + } + } + + // clear the file, so dump doesn't choke on it + FILE* fp = fopen("e05a", "w"); + A(fp != 0); + fclose(fp); + + } D(e05a); R(e05a); E; + + B(e06, Rollback during extend, 0) W(e06a); + { + c4_IntProp p1 ("p1"); + c4_Storage s1 ("e06a", 2); + A(s1.Strategy().FileSize() == 0); + c4_View v1 = s1.GetAs("a[p1:I]"); + v1.Add(p1 [123]); + s1.Commit(); + A(s1.Strategy().FileSize() == kSize1); + + c4_Storage s2 ("e06a", 0); + c4_View v2 = s2.View("a"); + A(v2.GetSize() == 1); + A(p1 (v2[0]) == 123); + + v1.Add(p1 [456]); + s1.Commit(); + A(s1.Strategy().FileSize() == kSize2); +#if 0 +/* fails on NT + Samba, though it works fine with mmap'ing disabled */ + s2.Rollback(); + + c4_View v2a = s2.View("a"); + A(v2a.GetSize() == 2); + A(p1 (v2a[0]) == 123); + A(p1 (v2a[1]) == 456); +#else + c4_View v2a = s2.View("a"); + A(v2a.GetSize() == 1); + A(p1 (v2a[0]) == 123); +#endif + } D(e06a); R(e06a); E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tformat.cpp b/akregator/src/mk4storage/metakit/tests/tformat.cpp new file mode 100644 index 000000000..ba6e92549 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tformat.cpp @@ -0,0 +1,306 @@ +// tformat.cpp -- Regression test program, (re-)format tests +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestFormat() +{ + B(f01, Add view to format, 0) W(f01a); + { + c4_IntProp p1 ("p1"); + c4_IntProp p2 ("p2"); + { + c4_Storage s1 ("f01a", 1); + s1.SetStructure("a[p1:I]"); + + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + s1.Commit(); + + c4_View v2 = s1.GetAs("b[p2:I]"); + + v2.Add(p2 [345]); + v2.Add(p2 [567]); + + s1.Commit(); + } + { + c4_Storage s1 ("f01a", 0); + + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + + c4_View v2 = s1.View("b"); + A(v2.GetSize() == 2); + A(p2 (v2[0]) == 345); + A(p2 (v2[1]) == 567); + } + } D(f01a); R(f01a); E; + + B(f02, Remove view from format, 0) W(f02a); + { + c4_IntProp p1 ("p1"); + c4_IntProp p2 ("p2"); + { + c4_Storage s1 ("f02a", 1); + s1.SetStructure("a[p1:I],b[p2:I]"); + + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + + c4_View v2 = s1.View("b"); + v2.Add(p2 [345]); + v2.Add(p2 [567]); + + s1.Commit(); + } + { + c4_Storage s1 ("f02a", 1); + s1.SetStructure("b[p2:I]"); + + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); // 19990916 new semantics, still as temp view + A(p1 (v1[0]) == 123); + + c4_View v2 = s1.View("b"); + A(v2.GetSize() == 2); + A(p2 (v2[0]) == 345); + A(p2 (v2[1]) == 567); + + s1.Commit(); + } + { + c4_Storage s1 ("f02a", 0); + + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 0); + + c4_View v2 = s1.View("b"); + A(v2.GetSize() == 2); + A(p2 (v2[0]) == 345); + A(p2 (v2[1]) == 567); + } + } D(f02a); R(f02a); E; + + B(f03, Rollback format change, 0) W(f03a); + { + c4_IntProp p1 ("p1"); + { + c4_Storage s1 ("f03a", 1); + s1.SetStructure("a[p1:I]"); + + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + + s1.Commit(); + + v1 = s1.GetAs("a"); + A(v1.GetSize() == 0); + + s1.Rollback(); + + v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + } + } D(f03a); R(f03a); E; + + B(f04, Rearrange format, 0) W(f04a); + { + c4_IntProp p1 ("p1"); + c4_IntProp p2 ("p2"); + { + c4_Storage s1 ("f04a", 1); + s1.SetStructure("a[p1:I],b[p2:I]"); + + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + + c4_View v2 = s1.View("b"); + v2.Add(p2 [345]); + v2.Add(p2 [567]); + + s1.Commit(); + } + { + c4_Storage s1 ("f04a", 1); + s1.SetStructure("b[p2:I],a[p1:I]"); + + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + + c4_View v2 = s1.View("b"); + A(v2.GetSize() == 2); + A(p2 (v2[0]) == 345); + A(p2 (v2[1]) == 567); + + s1.Commit(); + } + } D(f04a); R(f04a); E; + + B(f05, Nested reformat, 0) W(f05a); + { + c4_IntProp p1 ("p1"); + c4_IntProp p2 ("p2"); + { + c4_Storage s1 ("f05a", 1); + s1.SetStructure("a[p1:I],b[p2:I]"); + + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + + c4_View v2 = s1.View("b"); + v2.Add(p2 [345]); + v2.Add(p2 [567]); + + s1.Commit(); + } + { + c4_Storage s1 ("f05a", 1); + s1.SetStructure("a[p1:I],b[p1:I,p2:I]"); + + c4_View v2 = s1.View("b"); + p1 (v2[0]) = 543; + p1 (v2[1]) = 765; + + s1.Commit(); + } + { + c4_Storage s1 ("f05a", 0); + + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + + c4_View v2 = s1.View("b"); + A(v2.GetSize() == 2); + A(p1 (v2[0]) == 543); + A(p1 (v2[1]) == 765); + A(p2 (v2[0]) == 345); + A(p2 (v2[1]) == 567); + } + } D(f05a); R(f05a); E; + + B(f06, Flip foreign data, 0) + { + D(reversed); // not created here, only dump existing file + } E; + + B(f07, Automatic structure info (obsolete), 0) W(f07a); + { +/* Structure() and Store() are no longer supported + c4_StringProp p1 ("p1"), p2 ("p2"); + c4_Row r1 = p1 ["One"] + p2 ["Two"]; + c4_Row r2; + c4_View v1; + v1.Add(r1); + v1.Add(r2); + v1.Add(r1); + + c4_View v2 = v1.Structure(); + A(v2.GetSize() == 1); + + c4_ViewProp pView ("view"); + c4_View v3 = pView (v2[0]); + A(v3.GetSize() == 2); +*/ + #define FORMAT07 "dict[parent:I,index:I,view[name:S,type:S,child:I]]" + c4_Storage s1 ("f07a", 1); + s1.SetStructure(FORMAT07); + + //s1.View("dict") = v1.Structure(); + + s1.Commit(); + + } D(f07a); R(f07a); E; + + B(f08, Automatic storage format, 0) W(f08a); + { + c4_StringProp p1 ("p1"), p2 ("p2"); + c4_Row r1 = p1 ["One"] + p2 ["Two"]; + c4_Row r2; + c4_View v1; + v1.Add(r1); + v1.Add(r2); + v1.Add(r1); + + c4_Storage s1 ("f08a", 1); + + // changed 2000-03-15: Store is gone + //s1.Store("dict", v1); + c4_View v2 = s1.GetAs("dict[p1:S,p2:S]"); + v2.InsertAt(0, v1); + + s1.Commit(); + + } D(f08a); R(f08a); E; + + B(f09, Partial restructuring, 0) W(f09a); + { + c4_IntProp p1 ("p1"), p2 ("p2"), p3 ("p3"); + c4_Storage s1 ("f09a", 1); + + c4_View v1 = s1.GetAs("a[p1:I]"); + v1.SetSize(10); + + for (int i = 0; i < v1.GetSize(); ++i) + p1 (v1[i]) = 1000 + i; + + c4_View v2 = s1.GetAs("a[p1:I,p2:I]"); + + for (int j = 0; j < v2.GetSize(); j += 2) + p2 (v2[j]) = 2000 + j; + + c4_View v3 = s1.GetAs("a[p1:I,p2:I,p3:I]"); + + for (int k = 0; k < v3.GetSize(); k += 3) + p3 (v3[k]) = 3000 + k; + + s1.Commit(); + + } D(f09a); R(f09a); E; + + B(f10, Committed restructuring, 0) W(f10a); + { + c4_IntProp p1 ("p1"), p2 ("p2"), p3 ("p3"); + c4_Storage s1 ("f10a", 1); + + c4_View v1 = s1.GetAs("a[p1:I]"); + v1.SetSize(10); + + for (int i = 0; i < v1.GetSize(); ++i) + p1 (v1[i]) = 1000 + i; + + s1.Commit(); + + c4_View v2 = s1.GetAs("a[p1:I,p2:I]"); + + for (int j = 0; j < v2.GetSize(); j += 2) + p2 (v2[j]) = 2000 + j; + + s1.Commit(); + + c4_View v3 = s1.GetAs("a[p1:I,p2:I,p3:I]"); + + for (int k = 0; k < v3.GetSize(); k += 3) + p3 (v3[k]) = 3000 + k; + + s1.Commit(); + + } D(f10a); R(f10a); E; + + // 19990824: don't crash on GetAs with an inexistent view + B(f11, Delete missing view, 0) W(f11a); + { + c4_Storage s1 ("f11a", 1); + + c4_View v1 = s1.GetAs("a"); + v1.SetSize(10); + + s1.Commit(); + + } D(f11a); R(f11a); E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tlimits.cpp b/akregator/src/mk4storage/metakit/tests/tlimits.cpp new file mode 100644 index 000000000..a66ccbf05 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tlimits.cpp @@ -0,0 +1,282 @@ +// tlimits.cpp -- Regression test program, limit tests +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestLimits() +{ + B(l00, Lots of properties, 0) W(l00a); + { + c4_String desc; + + for (int i = 1; i < 150; ++i) + { + char buf [20]; + sprintf(buf, ",p%d:I", i); + + desc += buf; + } + + desc = "a[" + desc.Mid(1) + "]"; + + c4_Storage s1 ("l00a", 1); + s1.SetStructure(desc); + c4_View v1 = s1.View("a"); + c4_IntProp p123 ("p123"); + v1.Add(p123 [123]); + s1.Commit(); + + } D(l00a); R(l00a); E; + + B(l01, Over 32 Kb of integers, 0) W(l01a); + { + c4_Storage s1 ("l01a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + c4_IntProp p1 ("p1"); + v1.SetSize(9000); + + for (int i = 0; i < v1.GetSize(); ++i) + { + p1 (v1[i]) = 1000000L + i; + + A(p1 (v1[i]) - 1000000L == i); + } + + for (int j = 0; j < v1.GetSize(); ++j) + { + A(p1 (v1[j]) - 1000000L == j); + } + + s1.Commit(); + + for (int k = 0; k < v1.GetSize(); ++k) + { + A(p1 (v1[k]) - 1000000L == k); + } + + } D(l01a); R(l01a); E; + + B(l02, Over 64 Kb of strings, 0) W(l02a); + { + static char* texts[3] = { "Alice in Wonderland", + "The wizard of Oz", + "I'm singin' in the rain" }; + + c4_Storage s1 ("l02a", 1); + s1.SetStructure("a[p1:S]"); + c4_View v1 = s1.View("a"); + c4_StringProp p1 ("p1"); + c4_Row r1; + + for (int i = 0; i < 3500; ++i) + { + p1 (r1) = texts [i % 3]; + v1.Add(r1); + + A(p1 (v1[i]) == (c4_String) texts [i % 3]); + } + + for (int j = 0; j < v1.GetSize(); ++j) + { + A(p1 (v1[j]) == (c4_String) texts [j % 3]); + } + + s1.Commit(); + + for (int k = 0; k < v1.GetSize(); ++k) + { + A(p1 (v1[k]) == (c4_String) texts [k % 3]); + } + + } D(l02a); R(l02a); E; + + B(l03, Force sections in storage, 0) W(l03a); W(l03b); + { + c4_ViewProp p1 ("p1"); + c4_IntProp p2 ("p2"); + + { + c4_Storage s1 ("l03a", 1); + s1.SetStructure("a[p1[p2:I]]"); + c4_View v1 = s1.View("a"); + + c4_View v2; + v2.SetSize(1); + + for (int i = 0; i < 500; ++i) + { + p2 (v2[0]) = 9000 + i; + v1.Add(p1 [v2]); + } + + s1.Commit(); + } + { + c4_Storage s1 ("l03a", 0); + c4_View v1 = s1.View("a"); + + for (int i = 0; i < 500; ++i) + { + c4_View v2 = p1 (v1[i]); + A(p2 (v2[0]) == 9000 + i); + } + + c4_FileStream fs1 (fopen("l03b", "wb"), true); + s1.SaveTo(fs1); + } + { + c4_Storage s1; + + c4_FileStream fs1 (fopen("l03b", "rb"), true); + s1.LoadFrom(fs1); + + c4_View v1 = s1.View("a"); + + for (int i = 0; i < 500; ++i) + { + c4_View v2 = p1 (v1[i]); + A(p2 (v2[0]) == 9000 + i); + } + } + } D(l03a); D(l03b); R(l03a); R(l03b); E; + + B(l04, Modify sections in storage, 0) W(l04a); + { + c4_ViewProp p1 ("p1"); + c4_IntProp p2 ("p2"); + + { + c4_Storage s1 ("l04a", 1); + s1.SetStructure("a[p1[p2:I]]"); + c4_View v1 = s1.View("a"); + + c4_View v2; + v2.SetSize(1); + + for (int i = 0; i < 500; ++i) + { + p2 (v2[0]) = 9000 + i; + v1.Add(p1 [v2]); + } + + s1.Commit(); + } + { + c4_Storage s1 ("l04a", 1); + c4_View v1 = s1.View("a"); + c4_View v2 = p1 (v1[0]); + + p2 (v2[0]) = 1; + // this corrupted file in 1.5: free space was bad after load + s1.Commit(); + } + { + c4_Storage s1 ("l04a", 0); + } + } D(l04a); R(l04a); E; + + B(l05, Delete from 32 Kb of strings, 0) W(l05a); + { + static char* texts[3] = { "Alice in Wonderland", + "The wizard of Oz", + "I'm singin' in the rain" }; + + c4_Storage s1 ("l05a", 1); + s1.SetStructure("a[p1:I,p2:S,p3:S]"); + c4_View v1 = s1.View("a"); + c4_IntProp p1 ("p1"); + c4_StringProp p2 ("p2"), p3 ("p3"); + c4_Row r1; + + for (int i = 0; i < 1750; ++i) + { + p1 (r1) = i; + p2 (r1) = texts [i % 3]; + p3 (r1) = texts [i % 3]; + v1.Add(r1); + + A(p2 (v1[i]) == (c4_String) texts [i % 3]); + } + + for (int j = 0; j < v1.GetSize(); ++j) + { + A(p1 (v1[j]) == j); + A(p2 (v1[j]) == (c4_String) texts [j % 3]); + A(p3 (v1[j]) == (c4_String) texts [j % 3]); + } + + s1.Commit(); + + while (v1.GetSize() > 1) // randomly remove entries + v1.RemoveAt((unsigned short) (211 * v1.GetSize()) % v1.GetSize()); + + s1.Commit(); + + } D(l05a); R(l05a); E; + + B(l06, Bit field manipulations, 0) W(l06a); + { + c4_IntProp p1 ("p1"); + c4_View v2; + + { + c4_Storage s1 ("l06a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + c4_Row r1; + + for (int i = 2; i <= 256; i <<= 1) + { + for (int j = 0; j < 18; ++j) + { + p1 (r1) = j & (i - 1); + + v1.InsertAt(j, r1, j + 1); + v2.InsertAt(j, r1, j + 1); + } + + s1.Commit(); + } + } + { + c4_Storage s1 ("l06a", 0); + c4_View v1 = s1.View("a"); + + int n = v2.GetSize(); + A(n == v1.GetSize()); + + for (int i = 0; i < n; ++i) + { + long v = p1 (v2[i]); + A(p1 (v1[i]) == v); + } + } + + } D(l06a); R(l06a); E; + + B(l07, Huge description, 0) W(l07a); + { + c4_String desc; + + for (int i = 1; i < 150; ++i) + { + char buf [50]; + // 1999-07-25: longer size to force over 4 Kb of description + sprintf(buf, ",a123456789a123456789a123456789p%d:I", i); + + desc += buf; + } + + desc = "a[" + desc.Mid(1) + "]"; + + c4_Storage s1 ("l07a", 1); + s1.SetStructure(desc); + c4_View v1 = s1.View("a"); + c4_IntProp p123 ("p123"); + v1.Add(p123 [123]); + s1.Commit(); + + } D(l07a); R(l07a); E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tmapped.cpp b/akregator/src/mk4storage/metakit/tests/tmapped.cpp new file mode 100644 index 000000000..20f8de5f2 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tmapped.cpp @@ -0,0 +1,244 @@ +// tmapped.cpp -- Regression test program, mapped view tests +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestBlockDel(int pos_, int len_) +{ + printf("blockdel pos %d len %d\n", pos_, len_); + + c4_ViewProp p1 ("_B"); + c4_IntProp p2 ("p2"); + + c4_Storage s1; + c4_View v1 = s1.GetAs("v1[_B[p2:I]]"); + + int n = 0; + static int sizes[] = {999, 999, 999, 2, 0}; + + for (int i = 0; sizes[i]; ++i) { + c4_View v; + v.SetSize(sizes[i]); + for (int j = 0; j < sizes[i]; ++j) + p2 (v[j]) = ++n; + v1.Add(p1 [v]); + } + + c4_View v2 = v1.Blocked(); + A(v2.GetSize() == 2999); + + v2.RemoveAt(pos_, len_); + A(v2.GetSize() == 2999 - len_); +} + +void TestMapped() +{ + B(m01, Hash mapping, 0); + { + c4_StringProp p1 ("p1"); + + c4_Storage s1; + c4_View v1 = s1.GetAs("v1[p1:S]"); + c4_View v2 = s1.GetAs("v2[_H:I,_R:I]"); + c4_View v3 = v1.Hash(v2, 1); + + v3.Add(p1 ["b93655249726e5ef4c68e45033c2e0850570e1e07"]); + v3.Add(p1 ["2ab03fba463d214f854a71ab5c951cea096887adf"]); + v3.Add(p1 ["2e196eecb91b02c16c23360d8e1b205f0b3e3fa3d"]); + A(v3.GetSize() == 3); + + // infinite loop in 2.4.0, reported by Nathan Rogers, July 2001 + // happens when looking for missing key after a hash collision + int f = v3.Find(p1 ["7c0734c9187133f34588517fb5b39294076f22ba3"]); + A(f == -1); + } E; + + // example from Steve Baxter, Nov 2001, after block perf bugfix + // assertion failure on row 1001, due to commit data mismatch + B(m02, Blocked view bug, 0) W(m02a); + { + c4_BytesProp p1 ("p1"); + c4_Bytes h; + + c4_Storage s1 ("m02a", true); + c4_View v1 = s1.GetAs("v1[_B[p1:B]]"); + c4_View v2 = v1.Blocked(); + + for (int i = 0; i < 1005; ++i) { + h.SetBuffer(2500 + i); + v2.Add(p1 [h]); + + if (i >= 999) // will crash a few rounds later, at row 1001 + s1.Commit(); + } + + // reduce size to shorten the dump output + v2.RemoveAt(0, 990); + s1.Commit(); + + } D(m02a); R(m02a); E; + + B(m03, Hash adds, 0) W(m03a); + { + c4_StringProp p1 ("p1"); + + c4_Storage s1 ("m03a", true); + + c4_View d1 = s1.GetAs("d1[p1:S]"); + c4_View m1 = s1.GetAs("m1[_H:I,_R:I]"); + c4_View h1 = d1.Hash(m1); + + h1.Add(p1 ["one"]); + s1.Commit(); + + c4_View d2 = s1.GetAs("d2[p1:S]"); + c4_View m2 = s1.GetAs("m2[_H:I,_R:I]"); + c4_View h2 = d2.Hash(m2); + + h1.Add(p1 ["two"]); + h2.Add(p1 ["two"]); + s1.Commit(); + + c4_View d3 = s1.GetAs("d3[p1:S]"); + c4_View m3 = s1.GetAs("m3[_H:I,_R:I]"); + c4_View h3 = d3.Hash(m3); + + h1.Add(p1 ["three"]); + h2.Add(p1 ["three"]); + h3.Add(p1 ["three"]); + s1.Commit(); + + c4_View d4 = s1.GetAs("d4[p1:S]"); + c4_View m4 = s1.GetAs("m4[_H:I,_R:I]"); + c4_View h4 = d4.Hash(m4); + + h1.Add(p1 ["four"]); + h2.Add(p1 ["four"]); + h3.Add(p1 ["four"]); + h4.Add(p1 ["four"]); + s1.Commit(); + + } D(m03a); R(m03a); E; + + B(m04, Locate bug, 0) W(m04a); + { + c4_IntProp p1 ("p1"); + c4_StringProp p2 ("p2"); + + c4_Storage s1 ("m04a", true); + s1.AutoCommit(); + + c4_View v1 = s1.GetAs("v1[p1:I,p2:S]"); + + v1.Add(p1 [1] + p2 ["one"]); + v1.Add(p1 [2] + p2 ["two"]); + v1.Add(p1 [3] + p2 ["three"]); + s1.Commit(); + + c4_View v2 = v1.Ordered(); + A(v2.GetSize() == 3); + v2.Add(p1 [6] + p2 ["six"]); + v2.Add(p1 [5] + p2 ["five"]); + v2.Add(p1 [4] + p2 ["four"]); + A(v2.GetSize() == 6); + A(v1.GetSize() == 6); + + A(p1 (v1[0]) == 1); + A(p1 (v1[1]) == 2); + A(p1 (v1[2]) == 3); + A(p1 (v1[3]) == 4); + A(p1 (v1[4]) == 5); + A(p1 (v1[5]) == 6); + + A(v2.Find(p1 [4]) == 3); + A(v2.Search(p1 [4]) == 3); + + int i1 = -1; + A(v1.Locate(p1 [4], &i1) == 1); + A(i1 == 3); + + int i2 = -1; + A(v2.Locate(p1 [4], &i2) == 1); + A(i2 == 3); + + } D(m04a); R(m04a); E; + + // subviews are not relocated properly with blocked views in 2.4.7 + B(m05, Blocked view with subviews, 0) W(m05a); + { + char buf[10]; + c4_StringProp p1 ("p1"); + c4_IntProp p2 ("p2"); + c4_ViewProp pSv ("sv"); + + c4_Storage s1 ("m05a", true); + c4_View v1 = s1.GetAs("v1[_B[p1:S,sv[p2:I]]]"); + c4_View v2 = v1.Blocked(); + + for (int i = 0; i < 1000; ++i) { + sprintf(buf, "id-%d", i); + v2.Add(p1 [buf]); + + c4_View v3 = pSv (v2[i]); + v3.Add(p2 [i]); + } + + for (int j = 0; j < 1; ++j) { + sprintf(buf, "insert-%d", j); + v2.InsertAt(500, p1 [buf]); + } + + s1.Commit(); + + } D(m05a); R(m05a); E; + + // 2003/02/14 - assert fails for 2.4.8 in c4_Column::RemoveData + B(m06, Blocked view multi-row deletion, 0) W(m06a); + { + c4_IntProp p1 ("p1"); + + c4_Storage s1 ("m06a", true); + c4_View v1 = s1.GetAs("v1[p1:I]"); + c4_View v2 = s1.GetAs("v2[_B[_H:I,_R:I]]"); + c4_View v3 = v2.Blocked(); + c4_View v4 = v1.Hash(v3, 1); + + v4.Add(p1 [1]); + v4.Add(p1 [2]); + v4.RemoveAt(1); + + for (int i = 100; i < 1000; ++i) { + v4.Add(p1 [i]); + } + + s1.Commit(); + + } D(m06a); R(m06a); E; + + // 2003/03/07 - still not correct on blocked veiw deletions + B(m07, All blocked view multi-deletion cases, 0); + { + int i, j; + for (i = 0; i < 2; ++i) { + for (j = 1; j < 4; ++j) + TestBlockDel(i, j); + for (j = 998; j < 1002; ++j) + TestBlockDel(i, j); + for (j = 1998; j < 2002; ++j) + TestBlockDel(i, j); + } + for (i = 998; i < 1002; ++i) { + for (j = 1; j < 4; ++j) + TestBlockDel(i, j); + for (j = 998; j < 1002; ++j) + TestBlockDel(i, j); + } + for (i = 1; i < 4; ++i) + TestBlockDel(2999 - i, i); + for (i = 998; i < 1002; ++i) + TestBlockDel(2999 - i, i); + for (i = 1998; i < 2002; ++i) + TestBlockDel(2999 - i, i); + } E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tnotify.cpp b/akregator/src/mk4storage/metakit/tests/tnotify.cpp new file mode 100644 index 000000000..e630db951 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tnotify.cpp @@ -0,0 +1,444 @@ +// tnotify.cpp -- Regression test program, notification tests +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestNotify() +{ + B(n01, Add to selection, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + v1.Add(p1 [111]); + v1.Add(p1 [222]); + v1.Add(p1 [333]); + v1.Add(p1 [345]); + v1.Add(p1 [234]); + v1.Add(p1 [123]); + A(v1.GetSize() == 6); + c4_View v2 = v1.SelectRange(p1 [200], p1 [333]); + A(v2.GetSize() == 3); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 234); + v1.Add(p1 [300]); + A(v1.GetSize() == 7); + A(v2.GetSize() == 4); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 234); + A(p1 (v2[3]) == 300); + v1.Add(p1 [199]); + A(v1.GetSize() == 8); + A(v2.GetSize() == 4); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 234); + A(p1 (v2[3]) == 300); + } E; + + B(n02, Remove from selection, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + v1.Add(p1 [111]); + v1.Add(p1 [222]); + v1.Add(p1 [333]); + v1.Add(p1 [345]); + v1.Add(p1 [234]); + v1.Add(p1 [123]); + A(v1.GetSize() == 6); + c4_View v2 = v1.SelectRange(p1 [200], p1 [333]); + A(v2.GetSize() == 3); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 234); + v1.RemoveAt(2); + A(v1.GetSize() == 5); + A(v2.GetSize() == 2); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 234); + v1.RemoveAt(2); + A(v1.GetSize() == 4); + A(v2.GetSize() == 2); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 234); + } E; + + B(n03, Modify into selection, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + v1.Add(p1 [111]); + v1.Add(p1 [222]); + v1.Add(p1 [333]); + v1.Add(p1 [345]); + v1.Add(p1 [234]); + v1.Add(p1 [123]); + A(v1.GetSize() == 6); + c4_View v2 = v1.SelectRange(p1 [200], p1 [333]); + A(v2.GetSize() == 3); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 234); + p1 (v1[5]) = 300; + A(v2.GetSize() == 4); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 234); + A(p1 (v2[3]) == 300); + } E; + + B(n04, Modify out of selection, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + v1.Add(p1 [111]); + v1.Add(p1 [222]); + v1.Add(p1 [333]); + v1.Add(p1 [345]); + v1.Add(p1 [234]); + v1.Add(p1 [123]); + A(v1.GetSize() == 6); + c4_View v2 = v1.SelectRange(p1 [200], p1 [333]); + A(v2.GetSize() == 3); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 234); + p1 (v1[2]) = 100; + A(v2.GetSize() == 2); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 234); + } E; + + B(n05, Add to sorted, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + v1.Add(p1 [111]); + v1.Add(p1 [222]); + v1.Add(p1 [333]); + v1.Add(p1 [345]); + v1.Add(p1 [234]); + v1.Add(p1 [123]); + A(v1.GetSize() == 6); + c4_View v2 = v1.Sort(); + A(v2.GetSize() == 6); + A(p1 (v2[0]) == 111); + A(p1 (v2[1]) == 123); + A(p1 (v2[2]) == 222); + A(p1 (v2[3]) == 234); + A(p1 (v2[4]) == 333); + A(p1 (v2[5]) == 345); + v1.Add(p1 [300]); + A(v2.GetSize() == 7); + A(p1 (v2[0]) == 111); + A(p1 (v2[1]) == 123); + A(p1 (v2[2]) == 222); + A(p1 (v2[3]) == 234); + A(p1 (v2[4]) == 300); + A(p1 (v2[5]) == 333); + A(p1 (v2[6]) == 345); + } E; + + B(n06, Remove from sorted, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + v1.Add(p1 [111]); + v1.Add(p1 [222]); + v1.Add(p1 [333]); + v1.Add(p1 [345]); + v1.Add(p1 [234]); + v1.Add(p1 [123]); + A(v1.GetSize() == 6); + c4_View v2 = v1.Sort(); + A(v2.GetSize() == 6); + A(p1 (v2[0]) == 111); + A(p1 (v2[1]) == 123); + A(p1 (v2[2]) == 222); + A(p1 (v2[3]) == 234); + A(p1 (v2[4]) == 333); + A(p1 (v2[5]) == 345); + v1.RemoveAt(2); + A(v2.GetSize() == 5); + A(p1 (v2[0]) == 111); + A(p1 (v2[1]) == 123); + A(p1 (v2[2]) == 222); + A(p1 (v2[3]) == 234); + A(p1 (v2[4]) == 345); + } E; + + B(n07, New property through sort, 0) + { + c4_IntProp p1 ("p1"), p2 ("p2"); + c4_View v1; + v1.Add(p1 [11]); + v1.Add(p1 [1]); + v1.Add(p1 [111]); + A(v1.FindProperty(p2.GetId()) < 0); + + c4_View v2 = v1.SortOn(p1); + A(v2.FindProperty(p2.GetId()) < 0); + + A(v2.GetSize() == 3); + A(p1 (v2[0]) == 1); + A(p1 (v2[1]) == 11); + A(p1 (v2[2]) == 111); + + p2 (v1[0]) = 22; + A(v1.FindProperty(p2.GetId()) == 1); + A(v2.FindProperty(p2.GetId()) == 1); + + A(p2 (v2[1]) == 22); + } E; + + B(n08, Nested project and select, 0) + { + c4_IntProp p1 ("p1"), p2 ("p2"); + c4_View v1; + v1.Add(p1 [10] + p2 [1]); + v1.Add(p1 [11]); + v1.Add(p1 [12] + p2 [1]); + v1.Add(p1 [13]); + v1.Add(p1 [14] + p2 [1]); + v1.Add(p1 [15]); + v1.Add(p1 [16] + p2 [1]); + A(v1.GetSize() == 7); + + c4_View v2 = v1.Select(p2 [1]); + A(v2.GetSize() == 4); + A(p1 (v2[0]) == 10); + A(p1 (v2[1]) == 12); + A(p1 (v2[2]) == 14); + A(p1 (v2[3]) == 16); + + c4_View v3 = v2.Project(p1); + A(v3.GetSize() == 4); + A(p1 (v3[0]) == 10); + A(p1 (v3[1]) == 12); + A(p1 (v3[2]) == 14); + A(p1 (v3[3]) == 16); + + A(p2 (v3[0]) == 0); + A(p2 (v3[1]) == 0); + A(p2 (v3[2]) == 0); + A(p2 (v3[3]) == 0); + +/* not yet implemented: setting result of selection + p1 (v3[1]) = 123; + A(p1 (v3[1]) == 123); + A(p1 (v2[1]) == 123); + A(p1 (v1[2]) == 123); +*/ + } E; + + B(n09, Multiple dependencies, 0) + { + c4_IntProp p1 ("p1"), p2 ("p2"); + c4_View v1; + v1.Add(p1 [111] + p2[1111]); + v1.Add(p1 [222]); + v1.Add(p1 [333]); + v1.Add(p1 [345]); + v1.Add(p1 [234]); + v1.Add(p1 [123]); + A(v1.GetSize() == 6); + + c4_View v2 = v1.SelectRange(p1 [200], p1 [333]); + A(v2.GetSize() == 3); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 234); + + c4_View v3 = v1.SelectRange(p1 [340], p1 [350]); + A(v3.GetSize() == 1); + A(p1 (v3[0]) == 345); + + c4_View v4 = v2.SortOn(p1); + A(v4.GetSize() == 3); + A(p1 (v4[0]) == 222); + A(p1 (v4[1]) == 234); + A(p1 (v4[2]) == 333); + + c4_View v5 = v3.SortOn(p1); + A(v5.GetSize() == 1); + A(p1 (v5[0]) == 345); + + p1 (v1[2]) = 346; + + A(v2.GetSize() == 2); + A(p1 (v2[0]) == 222); + A(p1 (v2[1]) == 234); + + A(v3.GetSize() == 2); + A(p1 (v3[0]) == 346); + A(p1 (v3[1]) == 345); + + A(v4.GetSize() == 2); + A(p1 (v4[0]) == 222); + A(p1 (v4[1]) == 234); + + A(v5.GetSize() == 2); + A(p1 (v5[0]) == 345); + A(p1 (v5[1]) == 346); + } E; + + B(n10, Modify sorted duplicates, 0) + { + c4_IntProp p1 ("p1"); + c4_View v1; + v1.SetSize(3); + p1 (v1[0]) = 0; + c4_View v2 = v1.Sort(); + p1 (v1[0]) = 1; + p1 (v1[1]) = 1; // crashed in 1.5, fix in: c4_SortSeq::PosInMap + } E; + + B(n11, Resize compound derived view, 0) + { + c4_IntProp p1 ("p1"), p2 ("p2"); + c4_View v1 = (p1, p2); + c4_View v2 = v1.SelectRange(p2 [200], p2 [333]); + c4_View v3 = v2.SortOn(p1); + A(v2.GetSize() == 0); + A(v3.GetSize() == 0); + v1.SetSize(1); // crashed in 1.5, fix in: c4_FilterSeq::Match + A(v1.GetSize() == 1); + A(v2.GetSize() == 0); + A(v3.GetSize() == 0); + v1[0] = p2 [300]; + A(v1.GetSize() == 1); + A(v2.GetSize() == 1); + A(v3.GetSize() == 1); + A(p2 (v2[0]) == 300); + v1.Add(p1 [199]); + A(v1.GetSize() == 2); + A(v2.GetSize() == 1); + A(p2 (v2[0]) == 300); + } E; + + B(n12, Alter multiply derived view, 0) + { + c4_IntProp p1 ("p1"); + c4_StringProp p2 ("p2"), p3 ("p3"); + c4_View v1 = (p1, p2); + c4_View v2 = v1.Select(p1 [1]); + c4_View v3 = v2.SortOn(p2); + c4_View v4 = v1.Select(p1 [2]); + c4_View v5 = v4.SortOn(p2); + + v1.Add(p1[1] + p2 ["een"] + p3 ["1"]); + v1.Add(p1[1] + p2 ["elf"] + p3 ["11"]); + v1.Add(p1[2] + p2 ["twee"] + p3 ["2"]); + v1.Add(p1[2] + p2 ["twaalf"] + p3 ["12"]); + v1.Add(p1[2] + p2 ["twintig"] + p3 ["20"]); + v1.Add(p1[2] + p2 ["tachtig"] + p3 ["80"]); + + A(v1.GetSize() == 6); + A(v2.GetSize() == 2); + A(v3.GetSize() == 2); + A(v4.GetSize() == 4); + A(v5.GetSize() == 4); + + A(p3 (v1[2]) == (c4_String) "2"); + A(p3 (v4[0]) == (c4_String) "2"); + + A(p3 (v3[0]) == (c4_String) "1"); + A(p3 (v3[1]) == (c4_String) "11"); + + A(p3 (v5[0]) == (c4_String) "80"); + A(p3 (v5[1]) == (c4_String) "12"); + A(p3 (v5[2]) == (c4_String) "2"); + A(p3 (v5[3]) == (c4_String) "20"); + + v1[3] = p1[2] + p2 ["twaalf"] + p3 ["12+"]; + + A(p3 (v3[0]) == (c4_String) "1"); + A(p3 (v3[1]) == (c4_String) "11"); + + A(p3 (v1[3]) == (c4_String) "12+"); + A(p3 (v4[1]) == (c4_String) "12+"); + + A(p3 (v5[0]) == (c4_String) "80"); + A(p3 (v5[1]) == (c4_String) "12+"); + A(p3 (v5[2]) == (c4_String) "2"); + A(p3 (v5[3]) == (c4_String) "20"); + } E; + + B(n13, Project without, 0) // failed in 1.8.4 + { + c4_IntProp p1 ("p1"), p2 ("p2"); + c4_View v1; + + v1.Add(p1 [1] + p2 [2]); + int n1 = v1.NumProperties(); + A(n1 == 2); + + c4_View v2 = v1.ProjectWithout(p2); + int n2 = v2.NumProperties(); + A(n2 == 1); + } E; + +/* + B(n14, Add to reverse sorted, 0) + { + c4_IntProp p1 ("p1"), p2 ("p2"); + c4_View v1; + v1.Add(p1 [333] + p2 [1]); + v1.Add(p1 [345] + p2 [1]); + v1.Add(p1 [234] + p2 [1]); + v1.Add(p1 [123] + p2 [0]); + A(v1.GetSize() == 4); + c4_View v1a = v1.Select(p2 [1]); + A(v1a.GetSize() == 3); + c4_View v1b = v1a.SelectRange(p1 [100], p1 [999]); + A(v1b.GetSize() == 3); + c4_View v2 = v1b.SortOnReverse(p1, p1); + A(v2.GetSize() == 3); + A(p1 (v2[0]) == 345); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 234); + v1.Add(p1 [300] + p2 [1]); + A(v2.GetSize() == 4); + A(p1 (v2[0]) == 345); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 300); + A(p1 (v2[3]) == 234); + v1.Add(p1 [299] + p2 [1]); + A(v2.GetSize() == 5); + A(p1 (v2[0]) == 345); + A(p1 (v2[1]) == 333); + A(p1 (v2[2]) == 300); + A(p1 (v2[3]) == 299); + A(p1 (v2[4]) == 234); + } E; +*/ + // this failed in 2.4.8, reported by S. Selznick, 2002-11-22 + B(n14, Insert in non-mapped position, 0) W(n14a); + { + c4_IntProp p1 ("p1"); + c4_Storage s1 ("n14a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + + static int intlist[] = { 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, -1 }; + for (int c = 0; -1 != intlist[c]; c++) + v1.Add(p1 [intlist[c]]); + + A(v1.GetSize() == 10); + c4_View v2 = v1.Select(p1 [1]); + A(v2.GetSize() == 3); + + v1.InsertAt(3, p1 [6]); + A(v1.GetSize() == 11); + A(v2.GetSize() == 3); + + v1.InsertAt(7, p1 [1]); + A(v1.GetSize() == 12); + A(v2.GetSize() == 4); + + s1.Commit(); + } D(n14a); R(n14a); E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tresize.cpp b/akregator/src/mk4storage/metakit/tests/tresize.cpp new file mode 100644 index 000000000..e6011090d --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tresize.cpp @@ -0,0 +1,289 @@ +// trseize.cpp -- Regression test program, resizing tests +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +#include "regress.h" + +#include <stdlib.h> // strtol +#include <string.h> // memory functions + +class CResizer : public c4_Storage +{ +public: + CResizer (const char* file); + ~CResizer (); + + void Verify(); + + int Ins(int, int); + int Del(int, int); + +private: + enum { kMaxData = 15000 }; + char* _refData; + int _refSize; + + c4_View _attached; + c4_View _unattached; + c4_IntProp _prop; + + char _seed; + + CResizer (const CResizer&); // not implemented + void operator= (const CResizer&); // not implemented +}; + +CResizer::CResizer (const char* file) + : c4_Storage (file, 1), _refSize (0), _prop ("p1"), _seed (0) +{ + SetStructure("a[p1:I]"); + + _refData = new char [kMaxData]; + + _attached = View("a"); + + Verify(); +} + +CResizer::~CResizer () +{ + Verify(); + + Commit(); + + Verify(); + + delete [] _refData; +} + +void CResizer::Verify() +{ + int i; + + A(_refSize == _unattached.GetSize()); + A(_refSize == _attached.GetSize()); + + for (i = 0; i < _refSize; ++i) + { + A(_refData[i] == _prop (_unattached[i])); + A(_refData[i] == _prop (_attached[i])); + } +} + +int CResizer::Ins(int pos_, int cnt_) +{ + A(pos_ <= _refSize); + A(_refSize + cnt_ < kMaxData); + + memmove(_refData + pos_ + cnt_, _refData + pos_, _refSize - pos_); + _refSize += cnt_; + + c4_Row row; + _unattached.InsertAt(pos_, row, cnt_); + _attached.InsertAt(pos_, row, cnt_); + + for (int i = pos_; i < pos_ + cnt_; ++i) + { + _refData[i] = ++_seed; + _prop (_unattached[i]) = _seed; + _prop (_attached[i]) = _seed; + + if (_seed >= 123) + _seed = 0; + } + + Verify(); + + return _refSize; +} + +int CResizer::Del(int pos_, int cnt_) +{ + A(pos_ + cnt_ <= _refSize); + + _refSize -= cnt_; + memmove(_refData + pos_, _refData + pos_ + cnt_, _refSize - pos_); + + _unattached.RemoveAt(pos_, cnt_); + _attached.RemoveAt(pos_, cnt_); + + Verify(); + + return _refSize; +} + +void TestResize() +{ + B(r00, Simple insert, 0) W(r00a); + { + CResizer r1 ("r00a"); + + int n = r1.Ins(0, 250); + A(n == 250); + + } D(r00a); R(r00a); E; + + B(r01, Simple removes, 0) W(r01a); + { + CResizer r1 ("r01a"); + int n; + + n = r1.Ins( 0, 500); A(n == 500); + + n = r1.Del( 0, 50); A(n == 450); + n = r1.Del( 350, 100); A(n == 350); + n = r1.Del( 25, 150); A(n == 200); + n = r1.Del( 0, 200); A(n == 0); + + n = r1.Ins( 0, 15); A(n == 15); + + } D(r01a); R(r01a); E; + + B(r02, Large inserts and removes, 0) W(r02a); + { + int big = sizeof (int) == sizeof (short) ? 1000 : 4000; + + CResizer r1 ("r02a"); + int n; + + n = r1.Ins( 0, 2000); A(n == 2000); + n = r1.Ins( 0, 3000); A(n == 5000); + n = r1.Ins(5000, 1000 + big); A(n == 6000 + big); + n = r1.Ins( 100, 10); A(n == 6010 + big); + n = r1.Ins(4000, 100); A(n == 6110 + big); + n = r1.Ins( 0, 1001); A(n == 7111 + big); + + n = r1.Del(7111, big); A(n == 7111); + n = r1.Del( 0, 4111); A(n == 3000); + n = r1.Del( 10, 10); A(n == 2990); + n = r1.Del( 10, 10); A(n == 2980); + n = r1.Del( 5, 10); A(n == 2970); + n = r1.Del( 0, 990); A(n == 1980); + n = r1.Del( 3, 1975); A(n == 5); + + } D(r02a); R(r02a); E; + + B(r03, Binary property insertions, 0) W(r03a); + { + c4_BytesProp p1 ("p1"); + c4_Storage s1 ("r03a", 1); + s1.SetStructure("a[p1:B]"); + c4_View v1 = s1.View("a"); + + char buf [1024]; + + memset(buf, 0x11, sizeof buf); + v1.Add(p1 [c4_Bytes (buf, sizeof buf)]); + + memset(buf, 0x22, sizeof buf); + v1.Add(p1 [c4_Bytes (buf, sizeof buf / 2)]); + + s1.Commit(); + + memset(buf, 0x33, sizeof buf); + p1 (v1[1]) = c4_Bytes (buf, sizeof buf); // fix c4_Column::CopyData + + memset(buf, 0x44, sizeof buf); + v1.Add(p1 [c4_Bytes (buf, sizeof buf / 3)]); + + s1.Commit(); + + memset(buf, 0x55, sizeof buf); + v1.InsertAt(1, p1 [c4_Bytes (buf, sizeof buf)]); + + memset(buf, 0x66, sizeof buf); + v1.InsertAt(1, p1 [c4_Bytes (buf, sizeof buf / 4)]); + + s1.Commit(); + + } D(r03a); R(r03a); E; + + B(r04, Scripted string property tests, 0) W(r04a); + { + c4_StringProp p1 ("p1"); + c4_Storage s1 ("r04a", 1); + s1.SetStructure("a[p1:S]"); + + // This code implements a tiny language to specify tests in: + // + // "<X>,<Y>A" add X partial buffers of size Y + // "<X>a" add X full buffers at end + // "<X>,<Y>C" change entry X to a partial buffer of size Y + // "<X>c" change entry at position X to a full buffer + // "<X>,<Y>I" insert partial buffer of size Y at position X + // "<X>i" insert a full buffer at position X + // "<X>,<Y>R" remove Y entries at position X + // "<X>r" remove one entry at position X + // + // ">" commit changes + // "<" rollback changes + // + // " " ignore spaces + // "<X>," for additional args + // "<X>=" verify number of rows is X + + const char* scripts [] = + { + // A B C D E F G H I J + "5a 5a 5a 1r 5r 10r 6r 2r > 10=", + "5a 5a 5a 1,200C 5,200C 10,200C 6,200C 2,200C > 15=", + "5a 5a 5a 1,300C 5,300C 10,300C 6,300C 2,300C > 15=", + + // A B C D E F G H I J + "50a 50a 50a 10r 50r 100r 60r 20r > 145=", + "50a 50a 50a 10,200C 50,200C 100,200C 60,200C 20,200C > 150=", + "50a 50a 50a 10,300C 50,300C 100,300C 60,300C 20,300C > 150=", + + // A B C D E F G H I J + "50,0A 50,0A 50,0A 10c 50c 100c 60c 20c > 150=", // asserts in 1.7b1 + + // A B C D E + "3,3A 1,10C 1,1C > 3=", // asserts in 1.7 - June 6 build + + "", + 0 + }; + + for (int i = 0; scripts[i]; ++i) + { + c4_View v1 = s1.View("a"); + v1.RemoveAll(); + s1.Commit(); + A(v1.GetSize() == 0); // start with a clean slate each time + + const char* p = scripts[i]; + + char fill = '@'; + int save = 0; + c4_Row row; + + while (*p) + { + // default is a string of 255 chars (with additional null byte) + p1 (row) = c4_String (++fill, 255); + + int arg = (int) strtol(p, (char**) &p, 10); // loses const + + switch (*p++) + { + case 'A': p1 (row) = c4_String (fill, arg); arg = save; + case 'a': while (--arg >= 0) v1.Add(row); break; + case 'C': p1 (row) = c4_String (fill, arg); arg = save; + case 'c': v1.SetAt(arg, row); break; + case 'I': p1 (row) = c4_String (fill, arg); arg = save; + case 'i': v1.InsertAt(arg, row); break; + case 'R': v1.RemoveAt(save, arg); break; + case 'r': v1.RemoveAt(arg); break; + case '>': s1.Commit(); break; + case '<': s1.Rollback(); v1 = s1.View("a"); break; + case ' ': break; + case ',': save = arg; break; + case '=': A(v1.GetSize() == arg); break; + } + } + } + + s1.Commit(); + + } D(r04a); R(r04a); E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tstore1.cpp b/akregator/src/mk4storage/metakit/tests/tstore1.cpp new file mode 100644 index 000000000..c071648a6 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tstore1.cpp @@ -0,0 +1,180 @@ +// tstore1.cpp -- Regression test program, storage tests, part 1 +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestStores1() +{ + B(s00, Simple storage, 0) W(s00a); + { + c4_Storage s1 ("s00a", 1); + s1.SetStructure("a[p1:I]"); + s1.Commit(); + } D(s00a); R(s00a); E; + + B(s01, Integer storage, 0) W(s01a); + { + c4_IntProp p1 ("p1"); + c4_Storage s1 ("s01a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + v1.Add(p1 [456]); + v1.InsertAt(1, p1 [789]); + A(v1.GetSize() == 3); + s1.Commit(); + A(v1.GetSize() == 3); + } D(s01a); R(s01a); E; + +#if !q4_TINY + B(s02, Float storage, 0) W(s02a); + { + c4_FloatProp p1 ("p1"); + c4_Storage s1 ("s02a", 1); + s1.SetStructure("a[p1:F]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [12.3]); + v1.Add(p1 [45.6]); + v1.InsertAt(1, p1 [78.9]); + s1.Commit(); + } D(s02a); R(s02a); E; +#endif + + B(s03, String storage, 0) W(s03a); + { + c4_StringProp p1 ("p1"); + c4_Storage s1 ("s03a", 1); + s1.SetStructure("a[p1:S]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 ["one"]); + v1.Add(p1 ["two"]); + v1.InsertAt(1, p1 ["three"]); + s1.Commit(); + } D(s03a); R(s03a); E; + + B(s04, View storage, 0) W(s04a); + { + c4_StringProp p1 ("p1"); + c4_ViewProp p2 ("p2"); + c4_IntProp p3 ("p3"); + c4_Storage s1 ("s04a", 1); + s1.SetStructure("a[p1:S,p2[p3:I]]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 ["one"]); + v1.Add(p1 ["two"]); + c4_View v2 = p2 (v1[0]); + v2.Add(p3 [1]); + v2 = p2 (v1[1]); + v2.Add(p3 [11]); + v2.Add(p3 [22]); + v1.InsertAt(1, p1 ["three"]); + v2 = p2 (v1[1]); + v2.Add(p3 [111]); + v2.Add(p3 [222]); + v2.Add(p3 [333]); + s1.Commit(); + } D(s04a); R(s04a); E; + + B(s05, Store and reload, 0) W(s05a); + { + c4_IntProp p1 ("p1"); + { + c4_Storage s1 ("s05a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + s1.Commit(); + } + { + c4_Storage s1 ("s05a", 0); + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + } + } D(s05a); R(s05a); E; + + B(s06, Commit twice, 0) W(s06a); + { + c4_IntProp p1 ("p1"); + { + c4_Storage s1 ("s06a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + s1.Commit(); + v1.Add(p1 [234]); + s1.Commit(); + } + { + c4_Storage s1 ("s06a", 0); + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 123); + A(p1 (v1[1]) == 234); + } + } D(s06a); R(s06a); E; + + B(s07, Commit modified, 0) W(s07a); + { + c4_IntProp p1 ("p1"); + { + c4_Storage s1 ("s07a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + s1.Commit(); + p1 (v1[0]) = 234; + s1.Commit(); + } + { + c4_Storage s1 ("s07a", 0); + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 234); + } + } D(s07a); R(s07a); E; + + B(s08, View after storage, 0) W(s08a); + { + c4_IntProp p1 ("p1"); + { + c4_Storage s1 ("s08a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + s1.Commit(); + } + c4_View v1; + { + c4_Storage s1 ("s08a", 0); + v1 = s1.View("a"); + } + // 19990916 - semantics changed, view now 1 row, but 0 props + A(v1.GetSize() == 1); + A(v1.NumProperties() == 0); + v1.InsertAt(0, p1 [234]); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 234); + A(p1 (v1[1]) == 0); // the original value is gone + } D(s08a); R(s08a); E; + + B(s09, Copy storage, 0) W(s09a); W(s09b); + { + c4_IntProp p1 ("p1"); + { + c4_Storage s1 ("s09a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + s1.Commit(); + } + { + c4_Storage s1 ("s09a", 0); + c4_Storage s2 ("s09b", 1); + s2.SetStructure("a[p1:I]"); + s2.View("a") = s1.View("a"); + s2.Commit(); + } + } D(s09a); D(s09b); R(s09a); R(s09b); E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tstore2.cpp b/akregator/src/mk4storage/metakit/tests/tstore2.cpp new file mode 100644 index 000000000..371ffcca5 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tstore2.cpp @@ -0,0 +1,326 @@ +// tstore2.cpp -- Regression test program, storage tests, part 2 +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestStores2() +{ + B(s10, Stream storage, 0) W(s10a); W(s10b); W(s10c); + { + // s10a is original + // s10b is a copy, random access + // s10c is a serialized copy + c4_StringProp p1 ("p1"); + c4_ViewProp p2 ("p2"); + c4_IntProp p3 ("p3"); + { + c4_Storage s1 ("s10a", 1); + s1.SetStructure("a[p1:S,p2[p3:I]]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 ["one"]); + v1.Add(p1 ["two"]); + c4_View v2 = p2 (v1[0]); + v2.Add(p3 [1]); + v2 = p2 (v1[1]); + v2.Add(p3 [11]); + v2.Add(p3 [22]); + v1.InsertAt(1, p1 ["three"]); + v2 = p2 (v1[1]); + v2.Add(p3 [111]); + v2.Add(p3 [222]); + v2.Add(p3 [333]); + s1.Commit(); + } + { + c4_Storage s1 ("s10a", 0); + c4_Storage s2 ("s10b", 1); + s2.SetStructure("a[p1:S,p2[p3:I]]"); + s2.View("a") = s1.View("a"); + s2.Commit(); + } + { + c4_Storage s3 ("s10b", 0); + + c4_FileStream fs1 (fopen("s10c", "wb"), true); + s3.SaveTo(fs1); + } + { + c4_Storage s1 ("s10c", 0); // new after 2.01: serialized is no longer special + + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 3); + c4_View v2 = p2 (v1[0]); + A(v2.GetSize() == 1); + c4_View v3 = p2 (v1[1]); + A(v3.GetSize() == 3); + c4_View v4 = p2 (v1[2]); + A(v4.GetSize() == 2); + } + { + c4_Storage s1; + + c4_FileStream fs1 (fopen("s10c", "rb"), true); + s1.LoadFrom(fs1); + + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 3); + c4_View v2 = p2 (v1[0]); + A(v2.GetSize() == 1); + c4_View v3 = p2 (v1[1]); + A(v3.GetSize() == 3); + c4_View v4 = p2 (v1[2]); + A(v4.GetSize() == 2); + } + { + c4_Storage s1 ("s10c", 1); + + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 3); + c4_View v2 = p2 (v1[0]); + A(v2.GetSize() == 1); + c4_View v3 = p2 (v1[1]); + A(v3.GetSize() == 3); + c4_View v4 = p2 (v1[2]); + A(v4.GetSize() == 2); + v1.Add(p1 ["four"]); + s1.Commit(); + } + { + c4_Storage s1 ("s10c", 0); + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 4); + c4_View v2 = p2 (v1[0]); + A(v2.GetSize() == 1); + c4_View v3 = p2 (v1[1]); + A(v3.GetSize() == 3); + c4_View v4 = p2 (v1[2]); + A(v4.GetSize() == 2); + c4_View v5 = p2 (v1[3]); + A(v5.GetSize() == 0); + } + } D(s10a); D(s10b); D(s10c); R(s10a); R(s10b); R(s10c); E; + + B(s11, Commit and rollback, 0) W(s11a); + { + c4_IntProp p1 ("p1"); + { + c4_Storage s1 ("s11a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + s1.Commit(); + } + { + c4_Storage s1 ("s11a", 0); + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + v1.InsertAt(0, p1 [234]); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 234); + A(p1 (v1[1]) == 123); + s1.Rollback(); + // 19990916 - semantics changed, still 2 rows, but 0 props + A(v1.GetSize() == 2); + A(v1.NumProperties() == 0); + v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + } + } D(s11a); R(s11a); E; + + B(s12, Remove subview, 0) W(s12a); + { + c4_IntProp p1 ("p1"), p3 ("p3"); + c4_ViewProp p2 ("p2"); + { + c4_Storage s1 ("s12a", 1); + s1.SetStructure("a[p1:I,p2[p3:I]]"); + c4_View v1 = s1.View("a"); + c4_View v2; + v2.Add(p3 [234]); + v1.Add(p1 [123] + p2 [v2]); + s1.Commit(); + } + { + c4_Storage s1 ("s12a", 1); + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 123); + c4_View v2 = p2 (v1[0]); + A(v2.GetSize() == 1); + A(p3 (v2[0]) == 234); + v1.RemoveAt(0); + A(v1.GetSize() == 0); + s1.Commit(); + A(v1.GetSize() == 0); + } + } D(s12a); R(s12a); E; + + B(s13, Remove middle subview, 0) W(s13a); + { + c4_IntProp p1 ("p1"), p3 ("p3"); + c4_ViewProp p2 ("p2"); + { + c4_Storage s1 ("s13a", 1); + s1.SetStructure("a[p1:I,p2[p3:I]]"); + c4_View v1 = s1.View("a"); + + c4_View v2a; + v2a.Add(p3 [234]); + v1.Add(p1 [123] + p2 [v2a]); + + c4_View v2b; + v2b.Add(p3 [345]); + v2b.Add(p3 [346]); + v1.Add(p1 [124] + p2 [v2b]); + + c4_View v2c; + v2c.Add(p3 [456]); + v2c.Add(p3 [457]); + v2c.Add(p3 [458]); + v1.Add(p1 [125] + p2 [v2c]); + + s1.Commit(); + } + { + c4_Storage s1 ("s13a", 1); + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 3); + A(p1 (v1[0]) == 123); + A(p1 (v1[1]) == 124); + A(p1 (v1[2]) == 125); + c4_View v2a = p2 (v1[0]); + A(v2a.GetSize() == 1); + A(p3 (v2a[0]) == 234); + c4_View v2b = p2 (v1[1]); + A(v2b.GetSize() == 2); + A(p3 (v2b[0]) == 345); + c4_View v2c = p2 (v1[2]); + A(v2c.GetSize() == 3); + A(p3 (v2c[0]) == 456); + v1.RemoveAt(1); + A(v1.GetSize() == 2); + v2a = p2 (v1[0]); + A(v2a.GetSize() == 1); + A(p3 (v2a[0]) == 234); + v2b = p2 (v1[1]); + A(v2b.GetSize() == 3); + A(p3 (v2b[0]) == 456); + s1.Commit(); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 123); + A(p1 (v1[1]) == 125); + } + } D(s13a); R(s13a); E; + + B(s14, Replace attached subview, 0) W(s14a); + { + c4_IntProp p1 ("p1"); + c4_ViewProp p2 ("p2"); + { + c4_Storage s1 ("s14a", 1); + s1.SetStructure("a[p1:I,p2[p3:I]]"); + c4_View v1 = s1.View("a"); + + v1.Add(p1 [123] + p2 [c4_View ()]); + A(v1.GetSize() == 1); + + v1[0] = p2 [c4_View ()]; + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 0); + + s1.Commit(); + } + } D(s14a); R(s14a); E; + + B(s15, Add after removed subviews, 0) W(s15a); + { + c4_IntProp p1 ("p1"), p3 ("p3"); + c4_ViewProp p2 ("p2"); + { + c4_Storage s1 ("s15a", 1); + s1.SetStructure("a[p1:I,p2[p3:I]]"); + c4_View v1 = s1.View("a"); + + c4_View v2; + v2.Add(p3 [234]); + + v1.Add(p1 [123] + p2 [v2]); + v1.Add(p1 [456] + p2 [v2]); + v1.Add(p1 [789] + p2 [v2]); + A(v1.GetSize() == 3); + + v1[0] = v1[2]; + v1.RemoveAt(2); + + v1[0] = v1[1]; + v1.RemoveAt(1); + + v1.RemoveAt(0); + + v1.Add(p1 [111] + p2 [v2]); + + s1.Commit(); + } + } D(s15a); R(s15a); E; + + B(s16, Add after removed ints, 0) W(s16a); + { + c4_IntProp p1 ("p1"); + + c4_Storage s1 ("s16a", 1); + s1.SetStructure("a[p1:I,p2[p3:I]]"); + c4_View v1 = s1.View("a"); + + v1.Add(p1 [1]); + v1.Add(p1 [2]); + v1.Add(p1 [3]); + + v1.RemoveAt(2); + v1.RemoveAt(1); + v1.RemoveAt(0); + + v1.Add(p1 [4]); + + s1.Commit(); + + } D(s16a); R(s16a); E; + + B(s17, Add after removed strings, 0) W(s17a); + { + c4_StringProp p1 ("p1"); + + c4_Storage s1 ("s17a", 1); + s1.SetStructure("a[p1:S,p2[p3:I]]"); + c4_View v1 = s1.View("a"); + + v1.Add(p1 ["one"]); + v1.Add(p1 ["two"]); + v1.Add(p1 ["three"]); + + v1.RemoveAt(2); + v1.RemoveAt(1); + v1.RemoveAt(0); + + v1.Add(p1 ["four"]); + + s1.Commit(); + + } D(s17a); R(s17a); E; + + B(s18, Empty storage, 0) W(s18a); + { + c4_Storage s1 ("s18a", 1); + + } D(s18a); R(s18a); E; + + B(s19, Empty view outlives storage, 0) W(s19a); + { + c4_View v1; + c4_Storage s1 ("s19a", 1); + v1 = s1.GetAs("a[p1:I,p2:S]"); + + } D(s19a); R(s19a); E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tstore3.cpp b/akregator/src/mk4storage/metakit/tests/tstore3.cpp new file mode 100644 index 000000000..21418151c --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tstore3.cpp @@ -0,0 +1,366 @@ +// tstore3.cpp -- Regression test program, storage tests, part 3 +// $Id$ +// This is part of Metakit, the homepage is http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestStores3() +{ + B(s20, View outlives storage, 0) W(s20a); + { + c4_IntProp p1 ("p1"); + c4_View v1; + + { + c4_Storage s1 ("s20a", 1); + v1 = s1.GetAs("a[p1:I,p2:S]"); + v1.Add(p1 [123]); + } + + // 19990916 - semantics changed, rows kept but no properties + //A(p1 (v1[0]) == 123); + A(v1.GetSize() == 1); + A(v1.NumProperties() == 0); + + } D(s20a); R(s20a); E; + + B(s21, Test demo scenario, 0) W(s21a); + { + c4_StringProp p1 ("p1"), p2 ("p2"); + { + c4_Storage storage ("s21a", 1); + storage.SetStructure("a[p1:S,p2:S]"); + c4_View v1; + c4_Row r1; + + p1 (r1) = "One"; + p2 (r1) = "Un"; + v1.Add(r1); + A(v1.GetSize() == 1); + + p1 (r1) = "Two"; + p2 (r1) = "Deux"; + v1.Add(r1); + A(v1.GetSize() == 2); + + // changed 2000-03-15: Store is gone + //v1 = storage.Store("a", v1); + v1 = storage.View("a") = v1; + + A(v1.GetSize() == 2); + A(p1 (v1[1]) == (c4_String) "Two"); + A(p2 (v1[1]) == (c4_String) "Deux"); + A(p1 (v1[0]) == (c4_String) "One"); + A(p2 (v1[0]) == (c4_String) "Un"); + + storage.Commit(); + A(v1.GetSize() == 2); + A(p1 (v1[1]) == (c4_String) "Two"); + A(p2 (v1[1]) == (c4_String) "Deux"); + A(p1 (v1[0]) == (c4_String) "One"); + A(p2 (v1[0]) == (c4_String) "Un"); + + c4_String s1 (p1 (v1[1])); + c4_String s2 (p2 (v1[1])); + A(s1 == "Two"); + A(s2 == "Deux"); + + storage.Commit(); + + v1.Add(p1 ["Three"] + p2 ["Trois"]); + + storage.Commit(); + A(v1.GetSize() == 3); + A(p2 (v1[2]) == (c4_String) "Trois"); + + v1 = storage.GetAs("a[p1:S,p2:S,p3:I]"); + A(v1.GetSize() == 3); + A(p2 (v1[2]) == (c4_String) "Trois"); + + c4_IntProp p3 ("p3"); + p3 (v1[1]) = 123; + + storage.Commit(); + A(v1.GetSize() == 3); + A(p2 (v1[2]) == (c4_String) "Trois"); + + c4_View v2 = storage.GetAs("b[p4:I]"); + + c4_IntProp p4 ("p4"); + v2.Add(p4 [234]); + + storage.Commit(); + A(v1.GetSize() == 3); + A(p2 (v1[2]) == (c4_String) "Trois"); + + c4_IntProp p4a ("p4"); + v1.InsertAt(2, p1 ["Four"] + p4a [345]); + + storage.Commit(); + A(v1.GetSize() == 4); + A(p1 (v1[0]) == (c4_String) "One"); + A(p1 (v1[1]) == (c4_String) "Two"); + A(p1 (v1[2]) == (c4_String) "Four"); + A(p1 (v1[3]) == (c4_String) "Three"); + A(p2 (v1[3]) == (c4_String) "Trois"); + A(v2.GetSize() == 1); + A(p4 (v2[0]) == 234); + } + { + c4_Storage storage ("s21a", 0); + c4_View v1 = storage.View("a"); + A(v1.GetSize() == 4); + A(p1 (v1[0]) == (c4_String) "One"); + A(p1 (v1[1]) == (c4_String) "Two"); + A(p1 (v1[2]) == (c4_String) "Four"); + A(p1 (v1[3]) == (c4_String) "Three"); + c4_View v2 = storage.View("b"); + c4_IntProp p4 ("p4"); + A(v2.GetSize() == 1); + A(p4 (v2[0]) == 234); + } + } D(s21a); R(s21a); E; + +#if !q4_TINY + B(s22, Double storage, 0) W(s22a); + { + c4_DoubleProp p1 ("p1"); + c4_Storage s1 ("s22a", 1); + s1.SetStructure("a[p1:D]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [1234.5678]); + v1.Add(p1 [2345.6789]); + v1.InsertAt(1, p1 [3456.7890]); + s1.Commit(); + } D(s22a); R(s22a); E; +#endif + + B(s23, Find absent record, 0) W(s23a); + { + c4_Storage s1 ("s23a", 1); + s1.SetStructure("v[h:S,p:I,a:I,b:I,c:I,d:I,e:I,f:I,g:I,x:I]"); + c4_View view = s1.View("v"); + + c4_StringProp H("h"); + c4_IntProp P("p"); + + c4_Row row; + H(row) = "someString"; + P(row) = 99; + + int x = view.Find(row); + A(x == -1); + + } D(s23a); R(s23a); E; + + B(s24, Bitwise storage, 0) W(s24a); + { + c4_IntProp p1 ("p1"); + + int m = 9; + + // insert values in front, but check fractional sizes at each step + for (int n = 0; n < m; ++n) + { + { + c4_Storage s1 ("s24a", 1); + s1.SetStructure("a1[p1:I],a2[p1:I],a3[p1:I],a4[p1:I]"); + s1.AutoCommit(); // new feature in 1.6 + + c4_View v1 = s1.View("a1"); + c4_View v2 = s1.View("a2"); + c4_View v3 = s1.View("a3"); + c4_View v4 = s1.View("a4"); + + c4_Row row; + int k = ~ n; + + p1 (row) = k & 0x01; + v1.InsertAt(0, row); + + p1 (row) = k & 0x03; + v2.InsertAt(0, row); + + p1 (row) = k & 0x0F; + v3.InsertAt(0, row); + + p1 (row) = k & 0x7F; + v4.InsertAt(0, row); + } + // the following checks that all tiny size combinations work + { + c4_Storage s1 ("s24a", 0); + + c4_View v1 = s1.View("a1"); + c4_View v2 = s1.View("a2"); + c4_View v3 = s1.View("a3"); + c4_View v4 = s1.View("a4"); + + A(v1.GetSize() == n + 1); + A(v2.GetSize() == n + 1); + A(v3.GetSize() == n + 1); + A(v4.GetSize() == n + 1); + } + } + + c4_Storage s1 ("s24a", 0); + + c4_View v1 = s1.View("a1"); + c4_View v2 = s1.View("a2"); + c4_View v3 = s1.View("a3"); + c4_View v4 = s1.View("a4"); + + A(v1.GetSize() == m); + A(v2.GetSize() == m); + A(v3.GetSize() == m); + A(v4.GetSize() == m); + + // now check that the inserted values are correct + for (int i = 0; i < m; ++i) + { + int j = m - i - 1; + int k = ~ i; + + A(p1 (v1[j]) == (k & 0x01)); + A(p1 (v2[j]) == (k & 0x03)); + A(p1 (v3[j]) == (k & 0x0F)); + A(p1 (v4[j]) == (k & 0x7F)); + } + + } D(s24a); R(s24a); E; + + B(s25, Bytes storage, 0) W(s25a); + { + c4_Bytes hi ("hi", 2); + c4_Bytes gday ("gday", 4); + c4_Bytes hello ("hello", 5); + + c4_BytesProp p1 ("p1"); + c4_Storage s1 ("s25a", 1); + s1.SetStructure("a[p1:B]"); + c4_View v1 = s1.View("a"); + + v1.Add(p1 [hi]); + A(p1 (v1[0]) == hi); + v1.Add(p1 [hello]); + A(p1 (v1[0]) == hi); + A(p1 (v1[1]) == hello); + v1.InsertAt(1, p1 [gday]); + A(p1 (v1[0]) == hi); + A(p1 (v1[1]) == gday); + A(p1 (v1[2]) == hello); + s1.Commit(); + A(p1 (v1[0]) == hi); + A(p1 (v1[1]) == gday); + A(p1 (v1[2]) == hello); + + } D(s25a); R(s25a); E; + + B(s26, Bitwise autosizing, 0) W(s26a); + { + c4_IntProp p1 ("p1"), p2 ("p2"), p3 ("p3"), p4 ("p4"); + c4_Storage s1 ("s26a", 1); + s1.SetStructure("a[p1:I,p2:I,p3:I,p4:I]"); + c4_View v1 = s1.View("a"); + + v1.Add(p1 [1] + p2 [3] + p3 [15] + p4 [127]); + A(p1 (v1[0]) == 1); + A(p2 (v1[0]) == 3); + A(p3 (v1[0]) == 15); + A(p4 (v1[0]) == 127); + + p1 (v1[0]) = 100000L; + p2 (v1[0]) = 100000L; + p3 (v1[0]) = 100000L; + p4 (v1[0]) = 100000L; + + // these failed in 1.61 + A(p1 (v1[0]) == 100000L); + A(p2 (v1[0]) == 100000L); + A(p3 (v1[0]) == 100000L); + A(p4 (v1[0]) == 100000L); + + s1.Commit(); + + } D(s26a); R(s26a); E; + + B(s27, Bytes restructuring, 0) W(s27a); + { + c4_Bytes test ("test", 4); + + c4_BytesProp p1 ("p1"); + c4_Storage s1 ("s27a", 1); + + c4_Row row; + p1 (row) = test; + + c4_View v1; + v1.Add(row); + + // changed 2000-03-15: Store is gone + //s1.Store("a", v1); // asserts in 1.61 + c4_View v2 = s1.GetAs("a[p1:B]"); + v2.InsertAt(0, v1); + + s1.Commit(); + + } D(s27a); R(s27a); E; + +#if !q4_TINY + B(s28, Doubles added later, 0) W(s28a); + { + c4_FloatProp p1 ("p1"); + c4_DoubleProp p2 ("p2"); + c4_ViewProp p3 ("p3"); + + c4_Storage s1 ("s28a", 1); + s1.SetStructure("a[p1:F,p2:D,p3[p1:F,p2:D]]"); + c4_View v1 = s1.View("a"); + + c4_Row r1; + + p1 (r1) = 123; + p2 (r1) = 123; + + c4_View v2; + v2.Add (p1 [234] + p2 [234]); + p3 (r1) = v2; + + v1.Add(r1); + double x1 = p1 (v1[0]); + A(x1 == p2 (v1[0])); + + v2 = p3 (v1[0]); + double x2 = p1 (v2[0]); + A(x2 == p2 (v2[0])); // fails in 1.6 + + s1.Commit(); + + } D(s28a); R(s28a); E; +#endif + + B(s29, Delete bytes property, 0) W(s29a); + { + { + c4_BytesProp p1 ("p1"); + + c4_Storage s1 ("s29a", 1); + s1.SetStructure("a[p1:B]"); + c4_View v1 = s1.View("a"); + + int data = 99; + v1.Add(p1 [c4_Bytes (&data, sizeof data)]); + + s1.Commit(); + } + { + c4_Storage s1 ("s29a", 1); + c4_View v1 = s1.View("a"); + + v1.RemoveAt(0); // asserts in 1.7 + + s1.Commit(); + } + + } D(s29a); R(s29a); E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tstore4.cpp b/akregator/src/mk4storage/metakit/tests/tstore4.cpp new file mode 100644 index 000000000..930c074c3 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tstore4.cpp @@ -0,0 +1,323 @@ +// tstore4.cpp -- Regression test program, storage tests, part 4 +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestStores4() +{ + B(s30, Memo storage, 0) W(s30a); + { + c4_Bytes hi ("hi", 2); + c4_Bytes gday ("gday", 4); + c4_Bytes hello ("hello", 5); + + c4_MemoProp p1 ("p1"); + c4_Storage s1 ("s30a", 1); + s1.SetStructure("a[p1:B]"); + c4_View v1 = s1.View("a"); + + v1.Add(p1 [hi]); + A(p1 (v1[0]) == hi); + v1.Add(p1 [hello]); + A(p1 (v1[0]) == hi); + A(p1 (v1[1]) == hello); + v1.InsertAt(1, p1 [gday]); + A(p1 (v1[0]) == hi); + A(p1 (v1[1]) == gday); + A(p1 (v1[2]) == hello); + s1.Commit(); + A(p1 (v1[0]) == hi); + A(p1 (v1[1]) == gday); + A(p1 (v1[2]) == hello); + + } D(s30a); R(s30a); E; + + // this failed in the unbuffered 1.8.5a interim release in Mk4tcl 1.0.5 + B(s31, Check sort buffer use, 0) W(s31a); + { + c4_IntProp p1 ("p1"); + c4_Storage s1 ("s31a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [3]); + v1.Add(p1 [1]); + v1.Add(p1 [2]); + s1.Commit(); + + c4_View v2 = v1.SortOn(p1); + A(v2.GetSize() == 3); + A(p1 (v2[0]) == 1); + A(p1 (v2[1]) == 2); + A(p1 (v2[2]) == 3); + + } D(s31a); R(s31a); E; + + // this failed in 1.8.6, fixed 19990828 + B(s32, Set memo empty or same size, 0) W(s32a); + { + c4_Bytes empty; + c4_Bytes full ("full", 4); + c4_Bytes more ("more", 4); + + c4_MemoProp p1 ("p1"); + c4_Storage s1 ("s32a", 1); + s1.SetStructure("a[p1:B]"); + c4_View v1 = s1.View("a"); + + v1.Add(p1 [full]); + A(p1 (v1[0]) == full); + s1.Commit(); + A(p1 (v1[0]) == full); + + p1 (v1[0]) = empty; + A(p1 (v1[0]) == empty); + s1.Commit(); + A(p1 (v1[0]) == empty); + + p1 (v1[0]) = more; + A(p1 (v1[0]) == more); + s1.Commit(); + A(p1 (v1[0]) == more); + + p1 (v1[0]) = full; + A(p1 (v1[0]) == full); + s1.Commit(); + A(p1 (v1[0]) == full); + + } D(s32a); R(s32a); E; + + // this failed in 1.8.6, fixed 19990828 + B(s33, Serialize memo fields, 0) W(s33a); W(s33b); W(s33c); + { + c4_Bytes hi ("hi", 2); + c4_Bytes gday ("gday", 4); + c4_Bytes hello ("hello", 5); + + c4_MemoProp p1 ("p1"); + + c4_Storage s1 ("s33a", 1); + s1.SetStructure("a[p1:B]"); + c4_View v1 = s1.View("a"); + + v1.Add(p1 [hi]); + v1.Add(p1 [gday]); + v1.Add(p1 [hello]); + A(p1 (v1[0]) == hi); + A(p1 (v1[1]) == gday); + A(p1 (v1[2]) == hello); + s1.Commit(); + A(p1 (v1[0]) == hi); + A(p1 (v1[1]) == gday); + A(p1 (v1[2]) == hello); + + { + c4_FileStream fs1 (fopen("s33b", "wb"), true); + s1.SaveTo(fs1); + } + + c4_Storage s2 ("s33c", 1); + + c4_FileStream fs2 (fopen("s33b", "rb"), true); + s2.LoadFrom(fs2); + + c4_View v2 = s2.View("a"); + A(p1 (v2[0]) == hi); + A(p1 (v2[1]) == gday); + A(p1 (v2[2]) == hello); + s2.Commit(); + A(p1 (v2[0]) == hi); + A(p1 (v2[1]) == gday); + A(p1 (v2[2]) == hello); + s2.Commit(); + A(p1 (v2[0]) == hi); + A(p1 (v2[1]) == gday); + A(p1 (v2[2]) == hello); + + } D(s33a); D(s33b); D(s33c); R(s33a); R(s33b); R(s33c); E; + + // check smarter commit and commit failure on r/o + B(s34, Smart and failed commits, 0) W(s34a); + { + c4_IntProp p1 ("p1"); + { + c4_Storage s1 ("s34a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [111]); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 111); + bool f1 = s1.Commit(); + A(f1); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 111); + bool f2 = s1.Commit(); + A(f2); // succeeds, but should not write anything + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 111); + } + { + c4_Storage s1 ("s34a", 0); + c4_View v1 = s1.View("a"); + v1.Add(p1 [222]); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 111); + A(p1 (v1[1]) == 222); + bool f1 = s1.Commit(); + A(!f1); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 111); + A(p1 (v1[1]) == 222); + } + } D(s34a); R(s34a); E; + + B(s35, Datafile with preamble, 0) W(s35a); + { + { + c4_FileStream fs1 (fopen("s35a", "wb"), true); + fs1.Write("abc", 3); + } + c4_IntProp p1 ("p1"); + { + c4_Storage s1 ("s35a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [111]); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 111); + bool f1 = s1.Commit(); + A(f1); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 111); + bool f2 = s1.Commit(); + A(f2); // succeeds, but should not write anything + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 111); + } + { + c4_FileStream fs1 (fopen("s35a", "rb"), true); + char buffer [10]; + int n1 = fs1.Read(buffer, 3); + A(n1 == 3); + A(c4_String (buffer, 3) == "abc"); + } + { + c4_Storage s1 ("s35a", 0); + c4_View v1 = s1.View("a"); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 111); + v1.Add(p1 [222]); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 111); + A(p1 (v1[1]) == 222); + bool f1 = s1.Commit(); + A(!f1); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 111); + A(p1 (v1[1]) == 222); + } + } D(s35a); R(s35a); E; + + B(s36, Commit after load, 0) W(s36a); W(s36b); + { + c4_IntProp p1 ("p1"); + + c4_Storage s1 ("s36a", 1); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + v1.Add(p1 [111]); + A(v1.GetSize() == 1); + A(p1 (v1[0]) == 111); + + { + c4_FileStream fs1 (fopen("s36b", "wb"), true); + s1.SaveTo(fs1); + } + + p1 (v1[0]) = 222; + v1.Add(p1 [333]); + bool f1 = s1.Commit(); + A(f1); + A(v1.GetSize() == 2); + A(p1 (v1[0]) == 222); + A(p1 (v1[1]) == 333); + + c4_FileStream fs2 (fopen("s36b", "rb"), true); + s1.LoadFrom(fs2); + //A(v1.GetSize() == 0); // should be detached, but it's still 2 + + c4_View v2 = s1.View("a"); + A(v2.GetSize() == 1); + A(p1 (v2[0]) == 111); + + // this fails in 2.4.0, reported by James Lupo, August 2001 + bool f2 = s1.Commit(); + A(f2); + } D(s36a); D(s36b); R(s36a); R(s36b); E; + + // fails in 2.4.1, reported Oct 31. 2001 by Steve Baxter + B(s37, Change short partial fields, 0) W(s37a); + { + c4_BytesProp p1 ("p1"); + c4_Storage s1( "s37a", true ); + c4_View v1 = s1.GetAs("v1[key:I,p1:B]"); + + v1.Add(p1 [c4_Bytes ("12345", 6)]); + A(v1.GetSize() == 1); + s1.Commit(); + + c4_Bytes buf = p1 (v1[0]); + A(buf.Size() == 6); + A(buf == c4_Bytes ("12345", 6)); + buf = p1(v1[0]).Access(1,3); + A(buf == c4_Bytes ("234", 3)); + p1 (v1[0]).Modify(c4_Bytes ("ab", 2), 2, 0); + s1.Commit(); + + buf = p1 (v1[0]); + A(buf == c4_Bytes ("12ab5", 6)); + } D(s37a); R(s37a); E; + + // Gross memory use (but no leaks), January 2002, Murat Berk + B(s38, Lots of empty subviews, 0) W(s38a); + { + c4_BytesProp p1 ("p1"); + { + c4_Storage s1( "s38a", true ); + c4_View v = s1.GetAs("v[v1[p1:S]]"); + + v.SetSize(100000); + s1.Commit(); + } + { + c4_Storage s2( "s38a", true ); + c4_View v2 = s2.View("v"); + // this should not materialize all the empty subviews + v2.SetSize(v2.GetSize() + 1); + // nor should this + s2.Commit(); + } + { + c4_Storage s3( "s38a", true ); + c4_View v3 = s3.View("v"); + v3.RemoveAt(1, v3.GetSize() - 2); + A(v3.GetSize() == 2); + s3.Commit(); + } + } D(s38a); R(s38a); E; + + // Fix bug introduced on 7-2-2002, as reported by M. Berk + B(s39, Do not detach empty top-level views, 0) W(s39a); + { + c4_IntProp p1 ("p1"); + c4_Storage s1( "s39a", true ); + c4_View v1 = s1.GetAs("v1[p1:I]"); + s1.Commit(); + A(v1.GetSize() == 0); + v1.Add(p1 [123]); + A(v1.GetSize() == 1); + s1.Commit(); + c4_View v2 = s1.View("v1"); + A(v2.GetSize() == 1); // fails with 0 due to recent bug + } D(s39a); R(s39a); E; +} diff --git a/akregator/src/mk4storage/metakit/tests/tstore5.cpp b/akregator/src/mk4storage/metakit/tests/tstore5.cpp new file mode 100644 index 000000000..1e50b91f3 --- /dev/null +++ b/akregator/src/mk4storage/metakit/tests/tstore5.cpp @@ -0,0 +1,286 @@ +// tstore5.cpp -- Regression test program, storage tests, part 5 +// $Id$ +// This is part of Metakit, see http://www.equi4.com/metakit/ + +#include "regress.h" + +void TestStores5() +{ + B(s40, LoadFrom after commit, 0) W(s40a); + { + c4_IntProp p1 ("p1"); + + { // create datafile by streaming out + c4_Storage s1; + s1.SetStructure("a[p1:I]"); + + c4_View v1 = s1.View("a"); + v1.Add(p1 [123]); + A(p1 (v1[0]) == 123); + A(v1.GetSize() == 1); + + c4_FileStream fs1 (fopen("s40a", "wb"), true); + s1.SaveTo(fs1); + } + { // it should load just fine + c4_Storage s2; + c4_FileStream fs1 (fopen("s40a", "rb"), true); + bool ok = s2.LoadFrom(fs1); + A(ok); + + c4_View v1 = s2.View("a"); + A(p1 (v1[0]) == 123); + A(v1.GetSize() == 1); + } + { // open the datafile and commit a change + c4_Storage s3 ("s40a", true); + + c4_View v1 = s3.View("a"); + A(p1 (v1[0]) == 123); + A(v1.GetSize() == 1); + p1 (v1[0]) = 456; + s3.Commit(); + A(p1 (v1[0]) == 456); + A(v1.GetSize() == 1); + } + { // it should load fine and show the last changes + c4_Storage s4; + c4_FileStream fs1 (fopen("s40a", "rb"), true); + bool ok = s4.LoadFrom(fs1); + A(ok); + + c4_View v1 = s4.View("a"); + A(p1 (v1[0]) == 456); + A(v1.GetSize() == 1); + } + { // it should open just fine in the normal way as well + c4_Storage s5 ("s40a", false); + c4_View v1 = s5.View("a"); + A(p1 (v1[0]) == 456); + A(v1.GetSize() == 1); + } + } D(s40a); R(s40a); E; + + // 2002-03-13: failure on Win32, Modify calls base class GetNthMemoCol + B(s41, Partial modify blocked, 0) W(s41a); + { + c4_BytesProp p1 ("p1"); + c4_Storage s1 ("s41a", true); + c4_View v1 = s1.GetAs("a[_B[p1:B]]"); + + // custom viewers did not support partial access in 2.4.3 + c4_View v2 = v1.Blocked(); + s1.Commit(); + + v2.SetSize(1); + + c4_BytesRef m = p1 (v2[0]); + m.Modify(c4_Bytes ("abcdefgh", 8), 0); + + s1.Commit(); + + } D(s41a); R(s41a); E; + + B(s42, Get descriptions, 0) + { + c4_Storage s1; + s1.SetStructure("a[p1:I],b[p2:S]"); + + c4_String x1 = s1.Description(); + A(x1 == "a[p1:I],b[p2:S]"); + + c4_String x2 = s1.Description("b"); + A(x2 == "p2:S"); + + const char* cp = s1.Description("c"); + A(cp == 0); + } E; + + // 2002-04-24: VPI subview ints clobbered + B(s43, View reuse after sub-byte ints, 0) W(s43a); + { + c4_IntProp p1 ("p1"); + c4_Storage s1 ("s43a", true); + c4_View v1 = s1.GetAs("a[p1:I]"); + + v1.Add(p1 [0]); + v1.Add(p1 [1]); + s1.Commit(); + + v1.SetSize(1); // 1 is an even trickier bug than 0 + s1.Commit(); + + // adding the following two lines works around the 2.4.4 bug + //s1.Rollback(); + //v1 = s1.GetAs("a[p1:I]"); + + v1.Add(p1 [12345]); + s1.Commit(); + + //int n = p1 (v1[1]); + A(p1 (v1[1]) == 12345); + + } D(s43a); R(s43a); E; + + B(s44, Bad memo free space, 0) W(s44a); + { + c4_IntProp p1 ("p1"); + c4_BytesProp p2 ("p2"); + c4_Storage s1 ("s44a", true); + c4_View v1 = s1.GetAs("a[p1:I,p2:B]"); + + c4_Bytes data; + t4_byte* p = data.SetBuffer(12345); + for (int i = 0; i < data.Size(); ++i) + p[i] = (t4_byte) i; + + v1.Add(p2 [data]); + s1.Commit(); + + p1 (v1[0]) = 1; + s1.Commit(); + + p1 (v1[0]) = 0; + s1.Commit(); + + c4_Bytes temp = p2 (v1[0]); + A(temp == data); // this failed in 2.4.5 + + } D(s44a); R(s44a); E; + + B(s45, Bad subview memo free space, 0) W(s45a); + { + c4_IntProp p1 ("p1"); + c4_ViewProp p2 ("p2"); + c4_BytesProp p3 ("p3"); + c4_Storage s1 ("s45a", true); + c4_View v1 = s1.GetAs("a[p1:I,p2[p3:B]]"); + + c4_Bytes data; + t4_byte* p = data.SetBuffer(12345); + for (int i = 0; i < data.Size(); ++i) + p[i] = (t4_byte) i; + + v1.SetSize(1); + c4_View v2 = p2 (v1[0]); + v2.Add(p3 [data]); + s1.Commit(); + + p1 (v1[0]) = 1; + s1.Commit(); + + p1 (v1[0]) = 0; + s1.Commit(); + + c4_View v3 = p2 (v1[0]); + c4_Bytes temp = p3 (v3[0]); + A(temp == data); // this failed in 2.4.5 + + } D(s45a); R(s45a); E; + + B(s46, LoadFrom after commit, 0) W(s46a); + { + c4_IntProp p1 ("p1"); + + { + c4_Storage s1 ("s46a", true); + s1.SetStructure("a[p1:I]"); + c4_View v1 = s1.View("a"); + + v1.Add(p1 [11]); + v1.Add(p1 [22]); + v1.Add(p1 [33]); + v1.Add(p1 [44]); + v1.Add(p1 [55]); + v1.Add(p1 [66]); + v1.Add(p1 [77]); + v1.Add(p1 [88]); + v1.Add(p1 [99]); + + s1.Commit(); + } + { + c4_Storage s2 ("s46a", true); + c4_View v2 = s2.View("a"); + + v2.Add(p1 [1000]); // force 1->2 byte ints + v2.InsertAt(7, c4_Row ()); + v2.InsertAt(4, c4_Row ()); + + //for (int i = 6; i <= 9; ++i) printf("%d\n", (int) p1 (v2[i])); + + A(p1 (v2[6]) == 66); + A(p1 (v2[8]) == 0); + A(p1 (v2[9]) == 88); + A(p1 (v2[7]) == 77); // this failed in 2.4.6 + + s2.Commit(); + } + } D(s46a); R(s46a); E; + + // 2004-01-16 bad property type crashes MK 2.4.9.2 and before + // this hits an assertion in debug mode, so then it has to be disabled + B(s47, Defining bad property type, 0) + { + c4_IntProp p1 ("p2"); + + c4_Storage s1; +#if defined(NDEBUG) + c4_View v1 = s1.GetAs("v1[p1:A]"); +#else + // assertions are enabled, turn this into a dummy test instead + c4_View v1 = s1.GetAs("v1[p1:I]"); +#endif + v1.Add(p1 [123]); + + A(v1.GetSize() == 1); + A(p1 (v1 [0]) == 123); + } E; + + // 2004-01-18 file damaging bug, when resizing a comitted subview + // to empty, committing, and then resizing back to containing data. + // Fortunately this usage pattern never happened in blocked views! + B(s48, Resize subview to zero and back, 0) W(s48a); W(s48b); + { + { + c4_Storage s1 ("s48a", true); + c4_View v1 = s1.GetAs("v1[v2[p1:I]]"); + v1.SetSize(1); + s1.Commit(); + } + { + c4_Storage s1 ("s48a", true); + c4_View v1 = s1.View("v1"); + v1.SetSize(0); + s1.Commit(); + // the problem is that the in-memory copy has forgotten that it + // has nothing left on disk, and a comparison is done later on to + // avoid saving unmodified data - the bad decision is that data has + // not changed, but actually it has and must be reallocated! + // (fixes are in c4_FormatV::Insert and c4_FormatV::Remove) + v1.SetSize(1); + s1.Commit(); + // at this point, the 2.4.9.2 file is corrupt! + c4_FileStream fs1 (fopen("s48b", "wb"), true); + s1.SaveTo(fs1); + } + { + // using this damaged datafile will then crash + c4_Storage s1 ("s48a", false); + c4_View v1 = s1.View("v1"); + v1.SetSize(2); + } + } D(s48a); D(s48b); R(s48a); R(s48b); E; + + // 2004-01-20 better handling of bad input: ignore repeated props + B(s49, Specify conflicting properties, 0) W(s49a); + { + c4_Storage s1 ("s49a", true); + c4_View v1 = s1.GetAs("v1[p1:I,p1:S]"); + c4_View v2 = s1.GetAs("v2[p1:I,P1:S]"); + c4_View v3 = s1.GetAs("v3[v3[^]]"); + c4_String x1 = s1.Description(); + A(x1 == "v1[p1:I],v2[p1:I],v3[v3[^]]"); + s1.Commit(); + } D(s49a); E; +} diff --git a/akregator/src/mk4storage/mk4config.kcfg b/akregator/src/mk4storage/mk4config.kcfg new file mode 100644 index 000000000..2067756cf --- /dev/null +++ b/akregator/src/mk4storage/mk4config.kcfg @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="akregatorrc" /> + <group name="StorageMK4" > + <entry key="Commit Interval" type="Int" > + <label>Commit Interval</label> + <whatsthis>Commit interval in seconds for writing back changes</whatsthis> + <default>3</default> + </entry> + <entry key="Archive Path" type="String" > + <whatsthis>Path to archive</whatsthis> + <default></default> + </entry> + </group> +</kcfg> diff --git a/akregator/src/mk4storage/mk4config.kcfgc b/akregator/src/mk4storage/mk4config.kcfgc new file mode 100644 index 000000000..d7285cd60 --- /dev/null +++ b/akregator/src/mk4storage/mk4config.kcfgc @@ -0,0 +1,6 @@ +File=mk4config.kcfg +ClassName=MK4Config +Singleton=true +NameSpace=Akregator +Mutators=true +MemberVariables=private diff --git a/akregator/src/mk4storage/mk4confwidget.cpp b/akregator/src/mk4storage/mk4confwidget.cpp new file mode 100644 index 000000000..fb95e1602 --- /dev/null +++ b/akregator/src/mk4storage/mk4confwidget.cpp @@ -0,0 +1,92 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "mk4config.h" +#include "mk4confwidget.h" +#include "storagemk4impl.h" + +#include <qcheckbox.h> +#include <qlabel.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kurlrequester.h> + +namespace Akregator { +namespace Backend { + +MK4ConfWidget::MK4ConfWidget() : MK4ConfWidgetBase() +{ + if (MK4Config::archivePath() == StorageMK4Impl::defaultArchivePath() || MK4Config::archivePath().isEmpty()) + { + filereq->setURL(StorageMK4Impl::defaultArchivePath()); + MK4Config::setArchivePath(StorageMK4Impl::defaultArchivePath()); + cbUseDefault->setChecked(true); + filereq->setEnabled(false); + label->setEnabled(false); + } + else + { + cbUseDefault->setChecked(false); + filereq->setEnabled(true); + label->setEnabled(true); + } + filereq->setURL(MK4Config::archivePath()); + connect(cbUseDefault, SIGNAL(toggled(bool)), this, SLOT(slotChkBoxUseDefault(bool))); + +} + +void MK4ConfWidget::accept() +{ + + QString path = cbUseDefault->isChecked() ? StorageMK4Impl::defaultArchivePath() : filereq->url(); + if (path != MK4Config::archivePath()) + { + // TODO: if the user changed the archive location, inform him that + // the archive is not migrated automatically, but that he has to + // close Akregator and copy the files over/use some fancy CLI tool not + // yet written + } + MK4Config::setArchivePath(path); + MK4Config::writeConfig(); + MK4ConfWidgetBase::accept(); +} + +void MK4ConfWidget::slotChkBoxUseDefault(bool checked) +{ + if (checked) + { + filereq->setURL(StorageMK4Impl::defaultArchivePath()); + filereq->setEnabled(false); + } + else + { + filereq->setEnabled(true); + } +} + + +} +} + +#include "mk4confwidget.moc" diff --git a/akregator/src/mk4storage/mk4confwidget.h b/akregator/src/mk4storage/mk4confwidget.h new file mode 100644 index 000000000..e697c21bc --- /dev/null +++ b/akregator/src/mk4storage/mk4confwidget.h @@ -0,0 +1,48 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_BACKEND_MK4CONFWIDGET_H +#define AKREGATOR_BACKEND_MK4CONFWIDGET_H + +#include "mk4confwidgetbase.h" + +namespace Akregator { +namespace Backend { + +class MK4ConfWidget : public MK4ConfWidgetBase +{ + Q_OBJECT + public: + + MK4ConfWidget(); + + public slots: + virtual void accept(); + void slotChkBoxUseDefault(bool checked); +}; + +} +} + +#endif // AKREGATOR_BACKEND_MK4CONFWIDGET_H diff --git a/akregator/src/mk4storage/mk4confwidgetbase.ui b/akregator/src/mk4storage/mk4confwidgetbase.ui new file mode 100644 index 000000000..cc91559af --- /dev/null +++ b/akregator/src/mk4storage/mk4confwidgetbase.ui @@ -0,0 +1,178 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Akregator::Backend::MK4ConfWidgetBase</class> +<widget class="QDialog"> + <property name="name"> + <cstring>MK4ConfWidgetBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>377</width> + <height>115</height> + </rect> + </property> + <property name="caption"> + <string>Metakit Settings</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <property name="modal"> + <bool>true</bool> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>cbUseDefault</cstring> + </property> + <property name="text"> + <string>Use default location</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>label</cstring> + </property> + <property name="text"> + <string>Archive location:</string> + </property> + </widget> + <widget class="KURLRequester"> + <property name="name"> + <cstring>filereq</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QLayoutWidget" row="1" column="0"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>Horizontal Spacing2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>140</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonOk</cstring> + </property> + <property name="text"> + <string>&OK</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonApply</cstring> + </property> + <property name="text"> + <string>&Apply</string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>buttonCancel</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="autoDefault"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>buttonOk</sender> + <signal>clicked()</signal> + <receiver>MK4ConfWidgetBase</receiver> + <slot>accept()</slot> + </connection> + <connection> + <sender>buttonCancel</sender> + <signal>clicked()</signal> + <receiver>MK4ConfWidgetBase</receiver> + <slot>reject()</slot> + </connection> + <connection> + <sender>cbUseDefault</sender> + <signal>toggled(bool)</signal> + <receiver>label</receiver> + <slot>setDisabled(bool)</slot> + </connection> + <connection> + <sender>cbUseDefault</sender> + <signal><No Signal></signal> + <receiver>cbUseDefault</receiver> + <slot><No)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kurlrequester.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/akregator/src/mk4storage/mk4plugin.cpp b/akregator/src/mk4storage/mk4plugin.cpp new file mode 100644 index 000000000..a475a226a --- /dev/null +++ b/akregator/src/mk4storage/mk4plugin.cpp @@ -0,0 +1,50 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "mk4plugin.h" + +#include <klocale.h> + +#include "storagefactorymk4impl.h" +#include "storagefactoryregistry.h" + +AKREGATOR_EXPORT_PLUGIN( Akregator::Backend::MK4Plugin ) + +namespace Akregator { +namespace Backend { + +bool MK4Plugin::init() +{ + m_factory = new StorageFactoryMK4Impl(); + return StorageFactoryRegistry::self()->registerFactory(m_factory, "metakit"); +} + +MK4Plugin::~MK4Plugin() +{ + StorageFactoryRegistry::self()->unregisterFactory("metakit"); + delete m_factory; +} + +} +} diff --git a/akregator/src/mk4storage/mk4plugin.h b/akregator/src/mk4storage/mk4plugin.h new file mode 100644 index 000000000..0c46e4fb5 --- /dev/null +++ b/akregator/src/mk4storage/mk4plugin.h @@ -0,0 +1,49 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_BACKEND_MK4PLUGIN_H +#define AKREGATOR_BACKEND_MK4PLUGIN_H + +#include "../plugin.h" + +class QString; + +namespace Akregator { +namespace Backend { + +class StorageFactory; + +class MK4Plugin : public Akregator::Plugin +{ + public: + virtual ~MK4Plugin(); + virtual bool init(); + + private: + StorageFactory* m_factory; +}; + +} +} +#endif // AKREGATOR_BACKEND_MK4PLUGIN_H diff --git a/akregator/src/mk4storage/storagefactorymk4impl.cpp b/akregator/src/mk4storage/storagefactorymk4impl.cpp new file mode 100644 index 000000000..9303bb965 --- /dev/null +++ b/akregator/src/mk4storage/storagefactorymk4impl.cpp @@ -0,0 +1,71 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#include "storagefactorymk4impl.h" +#include "storagemk4impl.h" +//#include "mk4confwidget.h" +//#include "mk4config.h" + +#include <klocale.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qwidget.h> + +namespace Akregator { +namespace Backend { + +Storage* StorageFactoryMK4Impl::createStorage(const QStringList& params) const +{ + Storage* storage = new StorageMK4Impl; + storage->initialize(params); + return storage; +} + +QString StorageFactoryMK4Impl::key() const +{ + return "metakit"; +} + +QString StorageFactoryMK4Impl::name() const +{ + return i18n("Metakit"); +} + + +void StorageFactoryMK4Impl::configure() +{ /* + MK4ConfWidgetBase* confWidget = new MK4ConfWidget(); + // fill with Settings + + + if (confWidget->exec() == QDialog::Accepted) + { + // store and apply settings + } + + delete confWidget; + */ +} + +} +} diff --git a/akregator/src/mk4storage/storagefactorymk4impl.h b/akregator/src/mk4storage/storagefactorymk4impl.h new file mode 100644 index 000000000..b5e51db9d --- /dev/null +++ b/akregator/src/mk4storage/storagefactorymk4impl.h @@ -0,0 +1,53 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef STORAGEFACTORYMK4IMPL_H +#define STORAGEFACTORYMK4IMPL_H + +#include "storagefactory.h" + +class QString; +class QStringList; + +namespace Akregator { +namespace Backend { + +class Storage; + +class StorageFactoryMK4Impl : public StorageFactory +{ + public: + virtual QString key() const; + virtual QString name() const; + virtual void configure(); + virtual Storage* createStorage(const QStringList& params) const; + virtual bool isConfigurable() const { return false; } + virtual bool allowsMultipleWriteAccess() const { return false; } + +}; + +} +} + +#endif diff --git a/akregator/src/mk4storage/storagemk4impl.cpp b/akregator/src/mk4storage/storagemk4impl.cpp new file mode 100644 index 000000000..8a3af0e02 --- /dev/null +++ b/akregator/src/mk4storage/storagemk4impl.cpp @@ -0,0 +1,402 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#include "storagemk4impl.h" +#include "feedstoragemk4impl.h" + +#include <mk4.h> + +#include <qmap.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qtimer.h> + +#include <kdebug.h> +#include <kstandarddirs.h> + +namespace Akregator { +namespace Backend { + +class StorageMK4Impl::StorageMK4ImplPrivate +{ + public: + StorageMK4ImplPrivate() : modified(false), + purl("url"), + pFeedList("feedList"), + pTagSet("tagSet"), + punread("unread"), + ptotalCount("totalCount"), + plastFetch("lastFetch") {} + + c4_Storage* storage; + c4_View archiveView; + bool autoCommit; + bool modified; + QMap<QString, FeedStorage*> feeds; + QStringList feedURLs; + c4_StringProp purl, pFeedList, pTagSet; + c4_IntProp punread, ptotalCount, plastFetch; + QString archivePath; + + bool taggingEnabled; + + c4_Storage* feedListStorage; + c4_View feedListView; +}; + +bool StorageMK4Impl::taggingEnabled() const +{ + return d->taggingEnabled; +} + +void StorageMK4Impl::setArchivePath(const QString& archivePath) +{ + if (archivePath.isNull()) // if isNull, reset to default + d->archivePath = defaultArchivePath(); + else + d->archivePath = archivePath; +} + +QString StorageMK4Impl::archivePath() const +{ + return d->archivePath; +} + +StorageMK4Impl::StorageMK4Impl() : d(new StorageMK4ImplPrivate) +{ + setArchivePath(QString::null); // set path to default +} + +QString StorageMK4Impl::defaultArchivePath() +{ + return KGlobal::dirs()->saveLocation("data", "akregator")+"Archive"; +} + +StorageMK4Impl::~StorageMK4Impl() +{ + close(); + delete d; + d = 0; +} +void StorageMK4Impl::initialize(const QStringList& params) +{ + d->taggingEnabled = false; + + QStringList::ConstIterator it = params.begin(); + QStringList::ConstIterator end = params.end(); + + for ( ; it != end; ++it) + { + QStringList tokens = QStringList::split("=", *it); + if (tokens.count() == 2 && *(tokens.at(0)) == "taggingEnabled" + && *(tokens.at(1)) == "true") + { + d->taggingEnabled = true; + } + + } +} + +bool StorageMK4Impl::open(bool autoCommit) +{ + QString filePath = d->archivePath +"/archiveindex.mk4"; + d->storage = new c4_Storage(filePath.local8Bit(), true); + d->archiveView = d->storage->GetAs("archive[url:S,unread:I,totalCount:I,lastFetch:I]"); + c4_View hash = d->storage->GetAs("archiveHash[_H:I,_R:I]"); + d->archiveView = d->archiveView.Hash(hash, 1); // hash on url + d->autoCommit = autoCommit; + + filePath = d->archivePath +"/feedlistbackup.mk4"; + d->feedListStorage = new c4_Storage(filePath.local8Bit(), true); + d->feedListView = d->feedListStorage->GetAs("archive[feedList:S,tagSet:S]"); + return true; +} + +bool StorageMK4Impl::autoCommit() const +{ + return d->autoCommit; +} + +bool StorageMK4Impl::close() +{ + QMap<QString, FeedStorage*>::Iterator it; + QMap<QString, FeedStorage*>::Iterator end(d->feeds.end() ) ; + for (it = d->feeds.begin(); it != end; ++it) + { + it.data()->close(); + delete it.data(); + } + if(d->autoCommit) + d->storage->Commit(); + + delete d->storage; + d->storage = 0; + + d->feedListStorage->Commit(); + delete d->feedListStorage; + d->feedListStorage = 0; + + return true; +} + +bool StorageMK4Impl::commit() +{ + QMap<QString, FeedStorage*>::Iterator it; + QMap<QString, FeedStorage*>::Iterator end(d->feeds.end() ) ; + for ( it = d->feeds.begin(); it != end; ++it ) + it.data()->commit(); + + if(d->storage) + { + d->storage->Commit(); + return true; + } + + return false; +} + +bool StorageMK4Impl::rollback() +{ + QMap<QString, FeedStorage*>::Iterator it; + QMap<QString, FeedStorage*>::Iterator end(d->feeds.end() ) ; + for ( it = d->feeds.begin(); it != end; ++it ) + it.data()->rollback(); + + if(d->storage) + { + d->storage->Rollback(); + return true; + } + return false; +} + +int StorageMK4Impl::unreadFor(const QString &url) +{ + c4_Row findrow; + d->purl(findrow) = url.ascii(); + int findidx = d->archiveView.Find(findrow); + + return findidx != -1 ? d->punread(d->archiveView.GetAt(findidx)) : 0; +} + +void StorageMK4Impl::setUnreadFor(const QString &url, int unread) +{ + c4_Row findrow; + d->purl(findrow) = url.ascii(); + int findidx = d->archiveView.Find(findrow); + if (findidx == -1) + return; + findrow = d->archiveView.GetAt(findidx); + d->punread(findrow) = unread; + d->archiveView.SetAt(findidx, findrow); + markDirty(); +} + +int StorageMK4Impl::totalCountFor(const QString &url) +{ + c4_Row findrow; + d->purl(findrow) = url.ascii(); + int findidx = d->archiveView.Find(findrow); + + return findidx != -1 ? d->ptotalCount(d->archiveView.GetAt(findidx)) : 0; +} + +void StorageMK4Impl::setTotalCountFor(const QString &url, int total) +{ + c4_Row findrow; + d->purl(findrow) = url.ascii(); + int findidx = d->archiveView.Find(findrow); + if (findidx == -1) + return; + findrow = d->archiveView.GetAt(findidx); + d->ptotalCount(findrow) = total; + d->archiveView.SetAt(findidx, findrow); + markDirty(); +} + +int StorageMK4Impl::lastFetchFor(const QString& url) +{ + c4_Row findrow; + d->purl(findrow) = url.ascii(); + int findidx = d->archiveView.Find(findrow); + + return (findidx != -1 ? d->plastFetch(d->archiveView.GetAt(findidx)) : 0); +} + +void StorageMK4Impl::setLastFetchFor(const QString& url, int lastFetch) +{ + c4_Row findrow; + d->purl(findrow) = url.ascii(); + int findidx = d->archiveView.Find(findrow); + if (findidx == -1) + return; + findrow = d->archiveView.GetAt(findidx); + d->plastFetch(findrow) = lastFetch; + d->archiveView.SetAt(findidx, findrow); + markDirty(); +} + +void StorageMK4Impl::markDirty() +{ + if (!d->modified) + { + d->modified = true; + // commit changes after 3 seconds + QTimer::singleShot(3000, this, SLOT(slotCommit())); + } +} + +void StorageMK4Impl::slotCommit() +{ + if (d->modified) + commit(); + d->modified = false; +} + +FeedStorage* StorageMK4Impl::archiveFor(const QString& url) +{ + if (!d->feeds.contains(url)) + { + FeedStorage* fs = new FeedStorageMK4Impl(url, this); + d->feeds[url] = fs; + c4_Row findrow; + d->purl(findrow) = url.ascii(); + int findidx = d->archiveView.Find(findrow); + if (findidx == -1) + { + d->punread(findrow) = 0; + d->ptotalCount(findrow) = 0; + d->plastFetch(findrow) = 0; + d->archiveView.Add(findrow); + markDirty(); + } + fs->convertOldArchive(); + } + return d->feeds[url]; +} + +QStringList StorageMK4Impl::feeds() const +{ + // TODO: cache list + QStringList list; + int size = d->archiveView.GetSize(); + for (int i = 0; i < size; i++) + list += QString(d->purl(d->archiveView.GetAt(i))); + // fill with urls + return list; + +} + +void StorageMK4Impl::add(Storage* source) +{ + QStringList feeds = source->feeds(); + QStringList::ConstIterator end(feeds.end() ) ; + + for (QStringList::ConstIterator it = feeds.begin(); it != end; ++it) + { + FeedStorage* fa = archiveFor(*it); + fa->add(source->archiveFor(*it)); + } +} + + +void StorageMK4Impl::clear() +{ + QStringList feeds; + int size = d->archiveView.GetSize(); + for (int i = 0; i < size; i++) + feeds += QString(d->purl(d->archiveView.GetAt(i))); + QStringList::ConstIterator end(feeds.end() ) ; + + for (QStringList::ConstIterator it = feeds.begin(); it != end; ++it) + { + FeedStorage* fa = archiveFor(*it); + fa->clear(); + fa->commit(); + // FIXME: delete file (should be 0 in size now) + } + d->storage->RemoveAll(); + +} + +void StorageMK4Impl::storeFeedList(const QString& opmlStr) +{ + + if (d->feedListView.GetSize() == 0) + { + c4_Row row; + d->pFeedList(row) = !opmlStr.isEmpty() ? opmlStr.utf8().data() : ""; + d->pTagSet(row) = ""; + d->feedListView.Add(row); + } + else + { + c4_Row row = d->feedListView.GetAt(0); + d->pFeedList(row) = !opmlStr.isEmpty() ? opmlStr.utf8().data() : ""; + d->feedListView.SetAt(0, row); + } + markDirty(); +} + +QString StorageMK4Impl::restoreFeedList() const +{ + if (d->feedListView.GetSize() == 0) + return ""; + + c4_Row row = d->feedListView.GetAt(0); + return QString::fromUtf8(d->pFeedList(row)); +} + +void StorageMK4Impl::storeTagSet(const QString& xmlStr) +{ + + if (d->feedListView.GetSize() == 0) + { + c4_Row row; + d->pTagSet(row) = !xmlStr.isEmpty() ? xmlStr.utf8().data() : ""; + d->pFeedList(row) = ""; + d->feedListView.Add(row); + } + else + { + c4_Row row = d->feedListView.GetAt(0); + d->pTagSet(row) = !xmlStr.isEmpty() ? xmlStr.utf8().data() : ""; + d->feedListView.SetAt(0, row); + } + markDirty(); +} + +QString StorageMK4Impl::restoreTagSet() const +{ + if (d->feedListView.GetSize() == 0) + return ""; + + c4_Row row = d->feedListView.GetAt(0); + return QString::fromUtf8(d->pTagSet(row)); +} + +} // namespace Backend +} // namespace Akregator + +#include "storagemk4impl.moc" diff --git a/akregator/src/mk4storage/storagemk4impl.h b/akregator/src/mk4storage/storagemk4impl.h new file mode 100644 index 000000000..7b3a43783 --- /dev/null +++ b/akregator/src/mk4storage/storagemk4impl.h @@ -0,0 +1,130 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef STORAGEMK4IMPL_H +#define STORAGEMK4IMPL_H + +#include "storage.h" + +namespace Akregator { +namespace Backend { + +/** + * Metakit implementation of Storage interface + */ +class StorageMK4Impl : public Storage +{ + Q_OBJECT + public: + + StorageMK4Impl(); + StorageMK4Impl(const StorageMK4Impl&); + StorageMK4Impl &operator =(const StorageMK4Impl&); + virtual ~StorageMK4Impl(); + + + /** KGlobal::dirs()->saveLocation("data", "akregator")+"/Archive" */ + static QString defaultArchivePath(); + + /** sets the directory where the metakit files will be stored. + + @param archivePath the path to the archive, or QString::null to reset it to the default. + */ + void setArchivePath(const QString& archivePath); + + /** returns the path to the metakit archives */ + QString archivePath() const; + + + + virtual void initialize(const QStringList& params); + /** + * Open storage and prepare it for work. + * @return true on success. + */ + virtual bool open(bool autoCommit = false); + + /** + * Commit changes made in feeds and articles, making them persistent. + * @return true on success. + */ + virtual bool commit(); + + /** + * Rollback changes made in feeds and articles, reverting to last committed values. + * @returns true on success. + */ + virtual bool rollback(); + + /** + * Closes storage, freeing all allocated resources. Called from destructor, so you don't need to call it directly. + * @return true on success. + */ + virtual bool close(); + + /** + * @return Article archive for feed at given url. + */ + virtual FeedStorage* archiveFor(const QString &url); + virtual bool autoCommit() const; + virtual int unreadFor(const QString &url); + virtual void setUnreadFor(const QString &url, int unread); + virtual int totalCountFor(const QString &url); + virtual void setTotalCountFor(const QString &url, int total); + virtual int lastFetchFor(const QString& url); + virtual void setLastFetchFor(const QString& url, int lastFetch); + + virtual QStringList feeds() const; + + virtual void storeFeedList(const QString& opmlStr); + virtual QString restoreFeedList() const; + + virtual void storeTagSet(const QString& xmlStr); + virtual QString restoreTagSet() const; + + /** adds all feed storages from a source to this storage + existing articles are replaced + */ + virtual void add(Storage* source); + + /** deletes all feed storages in this archive */ + virtual void clear(); + + virtual bool taggingEnabled() const; + + void markDirty(); + + protected slots: + virtual void slotCommit(); + + private: + class StorageMK4ImplPrivate; + StorageMK4ImplPrivate *d; +}; + +} +} + +#endif // STORAGEMK4IMPL_H diff --git a/akregator/src/nodelist.cpp b/akregator/src/nodelist.cpp new file mode 100644 index 000000000..c59cb5c11 --- /dev/null +++ b/akregator/src/nodelist.cpp @@ -0,0 +1,243 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "folder.h" +#include "nodelist.h" +#include "treenode.h" +#include "treenodevisitor.h" + +#include <kapplication.h> + +#include <qmap.h> +#include <qstring.h> +#include <qvaluelist.h> + +namespace Akregator { + +class NodeList::NodeListPrivate +{ + public: + QValueList<TreeNode*> flatList; + Folder* rootNode; + QString title; + QMap<int, TreeNode*> idMap; + AddNodeVisitor* addNodeVisitor; + RemoveNodeVisitor* removeNodeVisitor; +}; + + +class NodeList::AddNodeVisitor : public TreeNodeVisitor +{ + public: + AddNodeVisitor(NodeList* list) : m_list(list) {} + + + virtual bool visitTreeNode(TreeNode* node) + { + if (!m_preserveID) + node->setId(m_list->generateID()); + m_list->d->idMap[node->id()] = node; + m_list->d->flatList.append(node); + + connect(node, SIGNAL(signalDestroyed(TreeNode*)), m_list, SLOT(slotNodeDestroyed(TreeNode*) )); + m_list->signalNodeAdded(node); // emit + + return true; + } + virtual bool visitFolder(Folder* node) + { + connect(node, SIGNAL(signalChildAdded(TreeNode*)), m_list, SLOT(slotNodeAdded(TreeNode*) )); + connect(node, SIGNAL(signalChildRemoved(Folder*, TreeNode*)), m_list, SLOT(slotNodeRemoved(Folder*, TreeNode*) )); + + visitTreeNode(node); + + for (TreeNode* i = node->firstChild(); i && i != node; i = i->next() ) + m_list->slotNodeAdded(i); + + return true; + } + + virtual void visit(TreeNode* node, bool preserveID) + { + m_preserveID = preserveID; + TreeNodeVisitor::visit(node); + } + + private: + NodeList* m_list; + bool m_preserveID; +}; + +class NodeList::RemoveNodeVisitor : public TreeNodeVisitor +{ + public: + RemoveNodeVisitor(NodeList* list) : m_list(list) {} + + virtual bool visitTreeNode(TreeNode* node) + { + m_list->d->idMap.remove(node->id()); + m_list->d->flatList.remove(node); + + disconnect(node, SIGNAL(signalDestroyed(TreeNode*)), m_list, SLOT(slotNodeDestroyed(TreeNode*) )); + m_list->signalNodeRemoved(node); // emit signal + + return true; + } + + virtual bool visitFolder(Folder* node) + { + + disconnect(node, SIGNAL(signalChildAdded(TreeNode*)), m_list, SLOT(slotNodeAdded(TreeNode*) )); + disconnect(node, SIGNAL(signalChildRemoved(Folder*, TreeNode*)), m_list, SLOT(slotNodeRemoved(Folder*, TreeNode*) )); + visitTreeNode(node); + + return true; + } + private: + NodeList* m_list; +}; + +NodeList::NodeList(QObject *parent, const char *name) : d(new NodeListPrivate) +{ + d->rootNode = 0; + d->addNodeVisitor = new AddNodeVisitor(this); + d->removeNodeVisitor = new RemoveNodeVisitor(this); + +} + +const QString& NodeList::title() const +{ + return d->title; +} + +TreeNode* NodeList::findByID(int id) const +{ + return d->idMap[id]; +} + +void NodeList::setTitle(const QString& title) +{ + d->title = title; +} + +Folder* NodeList::rootNode() const +{ + return d->rootNode; +} + +const QValueList<TreeNode*>& NodeList::asFlatList() const +{ + return d->flatList; +} + +bool NodeList::isEmpty() const +{ + return d->rootNode->firstChild() == 0; +} + +QValueList<TreeNode*>* NodeList::flatList() const +{ + return &(d->flatList); +} + +void NodeList::clear() +{ + Q_ASSERT(rootNode()); + + QValueList<TreeNode*> children = rootNode()->children(); + + for (QValueList<TreeNode*>::ConstIterator it = children.begin(); it != children.end(); ++it) + delete *it; // emits signal "emitSignalDestroyed" +} + +QMap<int, TreeNode*>* NodeList::idMap() const +{ + return &(d->idMap); +} + +void NodeList::setRootNode(Folder* folder) +{ + delete d->rootNode; + d->rootNode = folder; + + if (d->rootNode) + { + d->rootNode->setOpen(true); + connect(d->rootNode, SIGNAL(signalChildAdded(TreeNode*)), this, SLOT(slotNodeAdded(TreeNode*))); + connect(d->rootNode, SIGNAL(signalChildRemoved(Folder*, TreeNode*)), this, SLOT(slotNodeRemoved(Folder*, TreeNode*))); + } +} + +void NodeList::addNode(TreeNode* node, bool preserveID) +{ + d->addNodeVisitor->visit(node, preserveID); +} + +void NodeList::removeNode(TreeNode* node) +{ + d->removeNodeVisitor->visit(node); +} + +NodeList::~NodeList() +{ + emit signalDestroyed(this); + delete d->addNodeVisitor; + delete d->removeNodeVisitor; + delete d; + d = 0; +} + +int NodeList::generateID() +{ + return KApplication::random(); +} + +void NodeList::slotNodeAdded(TreeNode* node) +{ + Folder* parent = node->parent(); + if ( !node || !d->flatList.contains(parent) || d->flatList.contains(node) ) + return; + + addNode(node, false); +} + +void NodeList::slotNodeDestroyed(TreeNode* node) +{ + if ( !node || !d->flatList.contains(node) ) + return; + + removeNode(node); +} + +void NodeList::slotNodeRemoved(Folder* /*parent*/, TreeNode* node) +{ + if ( !node || !d->flatList.contains(node) ) + return; + + removeNode(node); +} + +} + +#include "nodelist.moc" diff --git a/akregator/src/nodelist.h b/akregator/src/nodelist.h new file mode 100644 index 000000000..20a52fee9 --- /dev/null +++ b/akregator/src/nodelist.h @@ -0,0 +1,126 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_NODELIST_H +#define AKREGATOR_NODELIST_H + +#include <qobject.h> + +class QDomDocument; +class QDomNode; +class QString; + +template <class K,class T> class QMap; +template <class T> class QValueList; + +// hack for KDE 3.x series +#ifdef signals +# undef signals +# define signals public +#endif + +namespace Akregator { + +class Folder; +class TreeNode; + +class NodeList : public QObject +{ + +Q_OBJECT + +public: + NodeList(QObject *parent=0, const char *name=0); + virtual ~NodeList(); + + virtual Folder* rootNode() const; + + virtual bool readFromXML(const QDomDocument& doc) = 0; + + virtual QDomDocument toXML() const = 0; + + virtual bool isEmpty() const; + + TreeNode* findByID(int id) const; + + /** returns the title of the feed list (as used in the OPML document) */ + const QString& title() const; + + /** sets the title of the feed list */ + void setTitle(const QString& name); + + /** returns a flat list containing all nodes in the tree */ + const QValueList<TreeNode*>& asFlatList() const; + +signals: + void signalDestroyed(NodeList*); + /** emitted when a node was added to the list */ + void signalNodeAdded(TreeNode*); + /** emitted when a node was removed from the list */ + void signalNodeRemoved(TreeNode*); + +public slots: + + /** + * Clears the list without touching the root node. + */ + void clear(); + +public: // compat with KDE-3.x assertions, remove for KDE 4 +// protected: + + QValueList<TreeNode*>* flatList() const; + QMap<int, TreeNode*>* idMap() const; + + int generateID(); + void setRootNode(Folder* folder); + virtual void addNode(TreeNode* node, bool preserveID); + virtual void removeNode(TreeNode* node); + +public slots: // compat with KDE-3.x assertions, remove for KDE 4 +// protected slots: + + virtual void slotNodeDestroyed(TreeNode* node); + virtual void slotNodeAdded(TreeNode* node); + virtual void slotNodeRemoved(Folder* parent, TreeNode* node); + +public: // compat with KDE-3.x assertions, remove for KDE 4 +// private: + NodeList(const NodeList&) : QObject() {} + NodeList& operator=(const NodeList&) { return *this; } + + friend class AddNodeVisitor; + class AddNodeVisitor; + + friend class RemoveNodeVisitor; + class RemoveNodeVisitor; + + class NodeListPrivate; + NodeListPrivate* d; + +}; + +} // namespace Akregator + +#endif // AKREGATOR_NODELIST_H diff --git a/akregator/src/notificationmanager.cpp b/akregator/src/notificationmanager.cpp new file mode 100644 index 000000000..ae1b2c886 --- /dev/null +++ b/akregator/src/notificationmanager.cpp @@ -0,0 +1,143 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <klocale.h> +#include <knotifyclient.h> +#include <kstaticdeleter.h> +#include <kurl.h> + +#include <qlabel.h> +#include <qtimer.h> + +#include "feed.h" +#include "notificationmanager.h" + +namespace Akregator { + +NotificationManager::NotificationManager() : QObject() +{ + m_intervalsLapsed = 0; + m_checkInterval = 2000; + m_maxIntervals = 10; + m_running = false; + m_addedInLastInterval = false; + m_maxArticles = 20; + m_widget = NULL; + m_instance = NULL; +} + +NotificationManager::~NotificationManager() +{ + m_self = 0; +} + +void NotificationManager::setWidget(QWidget* widget, KInstance* inst) +{ + m_widget = widget; + m_instance = inst != NULL ? inst : KGlobal::instance(); +} + +void NotificationManager::slotNotifyArticle(const Article& article) +{ + m_articles.append(article); + m_addedInLastInterval = true; + if (m_articles.count() >= m_maxArticles) + doNotify(); + else if (!m_running) + { + m_running = true; + QTimer::singleShot(m_checkInterval, this, SLOT(slotIntervalCheck())); + } +} + +void NotificationManager::slotNotifyFeeds(const QStringList& feeds) +{ + if (feeds.count() == 1) + { + KNotifyClient::Instance inst(m_instance); + KNotifyClient::event(m_widget->winId(), "feed_added", i18n("Feed added:\n %1").arg(feeds[0])); + } + else if (feeds.count() > 1) + { + QString message; + for (QStringList::ConstIterator it = feeds.begin(); it != feeds.end(); ++it) + message += *it + "\n"; + KNotifyClient::Instance inst(m_instance); + KNotifyClient::event(m_widget->winId(), "feed_added", i18n("Feeds added:\n %1").arg(message)); + } +} + +void NotificationManager::doNotify() +{ + QString message = "<html><body>"; + QString feedTitle; + QValueList<Article>::ConstIterator it = m_articles.begin(); + QValueList<Article>::ConstIterator en = m_articles.end(); + for (; it != en; ++it) + { + if (feedTitle != (*it).feed()->title()) + { + feedTitle = (*it).feed()->title(); + message += QString("<p><b>%1:</b></p>").arg(feedTitle); + } + message += (*it).title() + "<br>"; + } + message += "</body></html>"; + KNotifyClient::Instance inst(m_instance); + KNotifyClient::event(m_widget->winId(), "new_articles", message); + + m_articles.clear(); + m_running = false; + m_intervalsLapsed = 0; + m_addedInLastInterval = false; +} + +void NotificationManager::slotIntervalCheck() +{ + if (!m_running) + return; + m_intervalsLapsed++; + if (!m_addedInLastInterval || m_articles.count() >= m_maxArticles || m_intervalsLapsed >= m_maxIntervals) + doNotify(); + else + { + m_addedInLastInterval = false; + QTimer::singleShot(m_checkInterval, this, SLOT(slotIntervalCheck())); + } + +} + +NotificationManager* NotificationManager::m_self; +static KStaticDeleter<NotificationManager> notificationmanagersd; + +NotificationManager* NotificationManager::self() +{ + if (!m_self) + m_self = notificationmanagersd.setObject(m_self, new NotificationManager); + return m_self; +} + +} // namespace Akregator + +#include "notificationmanager.moc" diff --git a/akregator/src/notificationmanager.h b/akregator/src/notificationmanager.h new file mode 100644 index 000000000..5b274cef7 --- /dev/null +++ b/akregator/src/notificationmanager.h @@ -0,0 +1,85 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATORNOTIFICATIONMANAGER_H +#define AKREGATORNOTIFICATIONMANAGER_H + +#include <qobject.h> + +#include "article.h" + +namespace Akregator +{ + + /** this class collects notification requests (new articles etc.) and processes them using KNotify. */ +class NotificationManager : public QObject +{ + Q_OBJECT + public: + /** singleton instance of notification manager */ + static NotificationManager* self(); + + ~NotificationManager(); + + /** the widget used for notification, normally either the mainwindow or the tray icon */ + void setWidget(QWidget* widget, KInstance* inst=0); + + public slots: + + /** notifies an article. Note that articles are not notified separately, but + "collected" and notified all together */ + void slotNotifyArticle(const Article& article); + + /** notifies the addition of feeds (used when added via DCOP or command line) */ + void slotNotifyFeeds(const QStringList& feeds); + + protected: + + void doNotify(); + + protected slots: + + void slotIntervalCheck(); + + private: + NotificationManager(); + NotificationManager(const NotificationManager&) : QObject(){} + + uint m_checkInterval; + uint m_intervalsLapsed; + uint m_maxIntervals; + uint m_maxArticles; + bool m_running; + bool m_addedInLastInterval; + QWidget* m_widget; + KInstance* m_instance; + + QValueList<Article> m_articles; + + static NotificationManager* m_self; +}; + +} // namespace + +#endif diff --git a/akregator/src/pageviewer.cpp b/akregator/src/pageviewer.cpp new file mode 100644 index 000000000..1a10b4953 --- /dev/null +++ b/akregator/src/pageviewer.cpp @@ -0,0 +1,514 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "akregatorconfig.h" +#include "akregator_run.h" +#include "feediconmanager.h" +#include "pageviewer.h" +#include "viewer.h" + +#include <kaction.h> +#include <kapplication.h> +#include <kbookmark.h> +#include <kbookmarkmanager.h> +#include <kconfig.h> +#include <kglobalsettings.h> +#include <khtml_settings.h> +#include <khtmlview.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> +#include <kstdaccel.h> +#include <kparts/browserinterface.h> + +#include <qclipboard.h> +#include <qcstring.h> +#include <qdatastream.h> +#include <qdatetime.h> +#include <qfile.h> +#include <qmetaobject.h> +#include <qscrollview.h> +#include <qstring.h> +#include <qvaluelist.h> +#include <private/qucomextra_p.h> + +#include <cstdlib> +using std::abs; + +namespace Akregator { + + +// taken from KDevelop +class PageViewer::HistoryEntry +{ + public: + + KURL url; + QString title; + QByteArray state; + int id; + + HistoryEntry() {} + HistoryEntry(const KURL& u, const QString& t=QString::null): url(u), title(t) + { + id = abs( QTime::currentTime().msecsTo( QTime() ) ); // nasty, but should provide a reasonably unique number + } + +}; + +class PageViewer::PageViewerPrivate +{ + public: + + QValueList<HistoryEntry> history; + QValueList<HistoryEntry>::Iterator current; + + KToolBarPopupAction* backAction; + KToolBarPopupAction* forwardAction; + KAction* reloadAction; + KAction* stopAction; + + QString caption; +}; + + +PageViewer::PageViewer(QWidget *parent, const char *name) + : Viewer(parent, name), d(new PageViewerPrivate) +{ + // this hack is necessary since the part looks for []HTML Settings] in + // KGlobal::config() by default, which is wrong when running in Kontact + KHTMLSettings* s = const_cast<KHTMLSettings*> (settings()); + s->init(Settings::self()->config()); + + setXMLFile(locate("data", "akregator/pageviewer.rc"), true); + + QPair< KGuiItem, KGuiItem > backForward = KStdGuiItem::backAndForward(); + + d->backAction = new KToolBarPopupAction(backForward.first, + KStdAccel::shortcut(KStdAccel::Back), this, + SLOT(slotBack()), actionCollection(), + "pageviewer_back"); + + connect(d->backAction->popupMenu(), SIGNAL(aboutToShow()), + this, SLOT(slotBackAboutToShow())); + connect(d->backAction->popupMenu(), SIGNAL(activated(int)), + this, SLOT(slotPopupActivated(int))); + + + d->forwardAction = new KToolBarPopupAction(backForward.second, + KStdAccel::shortcut(KStdAccel::Forward),this, + SLOT(slotForward()), actionCollection(), + "pageviewer_forward"); + + connect(d->forwardAction->popupMenu(), SIGNAL(aboutToShow()), + this, SLOT(slotForwardAboutToShow())); + connect(d->forwardAction->popupMenu(), SIGNAL(activated(int)), + this, SLOT(slotPopupActivated(int))); + + d->reloadAction = new KAction(i18n("Reload"), "reload", 0, + this, SLOT(slotReload()), + actionCollection(), "pageviewer_reload"); + d->stopAction = new KAction(KStdGuiItem::guiItem(KStdGuiItem::Stop), 0, + this, SLOT(slotStop()), + actionCollection(), "pageviewer_stop"); + + //connect( this, SIGNAL(popupMenu(const QString &, const QPoint &)), this, SLOT(slotPopupMenu(const QString &, const QPoint &))); + + d->backAction->setEnabled(false); + d->forwardAction->setEnabled(false); + d->stopAction->setEnabled(false); + + connect( this, SIGNAL(setWindowCaption (const QString &)), + this, SLOT(slotSetCaption (const QString &)) ); + + connect(this, SIGNAL(started(KIO::Job *)), this, SLOT(slotStarted(KIO::Job* ))); + connect(this, SIGNAL(completed()), this, SLOT(slotCompleted())); + connect(this, SIGNAL(canceled(const QString &)), this, SLOT(slotCancelled(const QString &))); + + d->current = d->history.end(); + + // uncomment this to load konq plugins (doesn't work properly and clutters the GUI) + //loadPlugins( partObject(), this, instance() ); + +} + +PageViewer::~PageViewer() +{ + delete d; + d = 0; +} + +// Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp) +void PageViewer::slotBack() +{ + if ( d->current != d->history.begin() ) + { + QValueList<HistoryEntry>::Iterator tmp = d->current; + --tmp; + restoreHistoryEntry(tmp); + } +} + +// Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp) +void PageViewer::slotForward() +{ + if ( d->current != d->history.fromLast() && d->current != d->history.end() ) + { + QValueList<HistoryEntry>::Iterator tmp = d->current; + ++tmp; + restoreHistoryEntry(tmp); + } +} + +void PageViewer::slotBackAboutToShow() +{ + KPopupMenu *popup = d->backAction->popupMenu(); + popup->clear(); + + if ( d->current == d->history.begin() ) + return; + + QValueList<HistoryEntry>::Iterator it = d->current; + --it; + + int i = 0; + while( i < 10 ) + { + if ( it == d->history.begin() ) + { + popup->insertItem( (*it).title, (*it).id ); + return; + } + + popup->insertItem( (*it).title, (*it).id ); + ++i; + --it; + } +} + +void PageViewer::slotForwardAboutToShow() +{ + KPopupMenu *popup = d->forwardAction->popupMenu(); + popup->clear(); + + if ( d->current == d->history.fromLast() ) + return; + + QValueList<HistoryEntry>::Iterator it = d->current; + ++it; + + int i = 0; + while( i < 10 ) + { + if ( it == d->history.fromLast() ) + { + popup->insertItem( (*it).title, (*it).id ); + return; + } + + popup->insertItem( (*it).title, (*it).id ); + ++i; + ++it; + } +} + + +void PageViewer::slotReload() +{ + openURL( url() ); +} + +void PageViewer::slotStop() +{ + closeURL(); +} + +bool PageViewer::openURL(const KURL& url) +{ + updateHistoryEntry(); // update old history entry before switching to the new one + emit started(0); + + bool val = KHTMLPart::openURL(url); + + addHistoryEntry(url); // add new URL to history + + d->backAction->setEnabled( d->current != d->history.begin() ); + d->forwardAction->setEnabled( d->current != d->history.fromLast() ); + + QString favicon = FeedIconManager::self()->iconLocation(url); + if (!favicon.isEmpty()) + emit setTabIcon(QPixmap(KGlobal::dirs()->findResource("cache", favicon+".png"))); + else + emit setTabIcon(SmallIcon("html")); + + return val; +} + + +void PageViewer::slotOpenURLRequest(const KURL& url, const KParts::URLArgs& args) +{ + updateHistoryEntry(); + if (args.doPost()) + { + browserExtension()->setURLArgs(args); + openURL(url); + } + +} + +void PageViewer::slotPopupActivated( int id ) +{ + QValueList<HistoryEntry>::Iterator it = d->history.begin(); + while( it != d->history.end() ) + { + if ( (*it).id == id ) + { + restoreHistoryEntry(it); + return; + } + ++it; + } +} + +void PageViewer::updateHistoryEntry() +{ + (*d->current).title = d->caption; + (*d->current).state = QByteArray(); // Start with empty buffer. + QDataStream stream( (*d->current).state, IO_WriteOnly); + browserExtension()->saveState(stream); +} + +void PageViewer::restoreHistoryEntry(const QValueList<HistoryEntry>::Iterator& entry) +{ + updateHistoryEntry(); + + QDataStream stream( (*entry).state, IO_ReadOnly ); + browserExtension()->restoreState( stream ); + d->current = entry; + d->backAction->setEnabled( d->current != d->history.begin() ); + d->forwardAction->setEnabled( d->current != d->history.fromLast() ); + //openURL( entry.url ); // TODO read state + + +} + +// Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp) +void PageViewer::addHistoryEntry(const KURL& url) +{ + QValueList<HistoryEntry>::Iterator it = d->current; + + // if We're not already the last entry, we truncate the list here before adding an entry + if ( it != d->history.end() && it != d->history.fromLast() ) + { + d->history.erase( ++it, d->history.end() ); + } + HistoryEntry newEntry( url, url.url() ); + + // Only save the new entry if it is different from the last + if ( newEntry.url != (*d->current).url ) + { + d->history.append( newEntry ); + d->current = d->history.fromLast(); + } + updateHistoryEntry(); +} + +// Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp) +void PageViewer::slotStarted( KIO::Job * ) +{ + d->stopAction->setEnabled(true); +} + +// Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp) +void PageViewer::slotCompleted( ) +{ + d->stopAction->setEnabled(false); +} + +// Taken from KDevelop (lib/widgets/kdevhtmlpart.cpp) +void PageViewer::slotCancelled( const QString & /*errMsg*/ ) +{ + d->stopAction->setEnabled(false); +} + +void PageViewer::urlSelected(const QString &url, int button, int state, const QString &_target, KParts::URLArgs args) +{ + if (url.startsWith(QString::fromLatin1( "javascript:" ), /*case-sensitive=*/false) ) + { + KHTMLPart::urlSelected(url,button,state,_target,args); + } + else + { + if (button == LeftButton) + { + m_url = completeURL(url); + browserExtension()->setURLArgs(args); + slotOpenLinkInThisTab(); + } + else + { + Viewer::urlSelected(url,button,state,_target,args); + } + } +} + +void PageViewer::slotSetCaption(const QString& cap) +{ + d->caption = cap; + (*d->current).title = cap; +} + +void PageViewer::slotPaletteOrFontChanged() +{ + kdDebug() << "PageViewer::slotPaletteOrFontChanged()" << endl; + // taken from KonqView (kdebase/konqueror/konq_view.cc) + + QObject *obj = KParts::BrowserExtension::childObject(this); + if ( !obj ) // not all views have a browser extension ! + return; + + int id = obj->metaObject()->findSlot("reparseConfiguration()"); + if (id == -1) + return; + QUObject o[1]; + + obj->qt_invoke(id, o); + + // this hack is necessary since the part looks for []HTML Settings] in + // KGlobal::config() by default, which is wrong when running in Kontact + // NOTE: when running in Kontact, immediate updating doesn't work + KHTMLSettings* s = const_cast<KHTMLSettings*> (settings()); + s->init(Settings::self()->config()); +} + +void PageViewer::slotGlobalBookmarkArticle() +{ + KBookmarkManager *mgr = KBookmarkManager::userBookmarksManager(); + KBookmarkGroup grp = mgr->root(); + grp.addBookmark(mgr, d->caption, toplevelURL()); + mgr->emitChanged(grp); + mgr->save(); +} + + +void PageViewer::slotPopupMenu(KXMLGUIClient*, const QPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags kpf, mode_t) +{ + m_url = kurl; + QString url = kurl.url(); // maximal url confusion + + const bool showReload = (kpf & KParts::BrowserExtension::ShowReload) != 0; + const bool showNavigationItems = (kpf & KParts::BrowserExtension::ShowNavigationItems) != 0; + const bool isLink = (kpf & (KParts::BrowserExtension::ShowNavigationItems | KParts::BrowserExtension::ShowTextSelectionItems)) == 0; + const bool isSelection = (kpf & KParts::BrowserExtension::ShowTextSelectionItems) != 0; + + KPopupMenu popup(this->widget()); + + int idNewWindow = -2; + if (isLink) + { + idNewWindow = popup.insertItem(SmallIcon("tab_new"),i18n("Open Link in New &Tab"), this, SLOT(slotOpenLinkInForegroundTab())); + popup.setWhatsThis(idNewWindow, i18n("<b>Open Link in New Tab</b><p>Opens current link in a new tab.")); + popup.insertItem(SmallIcon("window_new"), i18n("Open Link in External &Browser"), this, SLOT(slotOpenLinkInBrowser())); + + popup.insertSeparator(); + action("savelinkas")->plug(&popup); + KAction* copylinkaddress = action("copylinkaddress"); + if (copylinkaddress) + { + copylinkaddress->plug( &popup); + //popup.insertSeparator(); + } + } + else // we are not on a link + { + if (showNavigationItems) + { + d->backAction->plug( &popup ); + d->forwardAction->plug( &popup ); + } + + if (showReload) + d->reloadAction->plug(&popup); + + d->stopAction->plug(&popup); + + popup.insertSeparator(); + + if (isSelection) + { + action("viewer_copy")->plug(&popup); + popup.insertSeparator(); + } + + KAction* incFontAction = this->action("incFontSizes"); + KAction* decFontAction = this->action("decFontSizes"); + if ( incFontAction && decFontAction ) + { + incFontAction->plug( &popup ); + decFontAction->plug( &popup ); + popup.insertSeparator(); + } + + popup.insertItem(SmallIcon("window_new"), i18n("Open Page in External Browser"), this, SLOT(slotOpenLinkInBrowser())); + + action("viewer_print")->plug(&popup); + popup.insertSeparator(); + + KAction *ac = action("setEncoding"); + if (ac) + ac->plug(&popup); + popup.insertItem(SmallIcon("bookmark_add"),i18n("Add to Konqueror Bookmarks"), this, SLOT(slotGlobalBookmarkArticle())); + } + + int r = popup.exec(p); + + if (r == idNewWindow) + { + KURL kurl; + if (!KURL(url).path().startsWith("/")) + { + kdDebug() << "processing relative url: " << url << endl; + if (url.startsWith("#")) + { + kurl = KURL(PageViewer::url()); + kurl.setRef(url.mid(1)); + } + else + kurl = KURL(PageViewer::url().upURL().url(true)+url); + } + else + kurl = KURL(url); +// kurl.addPath(url); + if (kurl.isValid()) { + //slotOpenInNewWindow(kurl); + } +// ( kurl ); + } +} + +} // namespace Akregator + +#include "pageviewer.moc" diff --git a/akregator/src/pageviewer.h b/akregator/src/pageviewer.h new file mode 100644 index 000000000..68001d80c --- /dev/null +++ b/akregator/src/pageviewer.h @@ -0,0 +1,87 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef PAGEVIEWER_H +#define PAGEVIEWER_H + +#include "viewer.h" + + +class KAction; +class KToolBarPopupAction; +class QString; + +namespace Akregator +{ + + // the back/forward navigation was taken from KDevelop. Kudos to the KDevelop team! + class PageViewer : public Viewer + { + Q_OBJECT + public: + PageViewer(QWidget* parent, const char* name); + virtual ~PageViewer(); + virtual bool openURL(const KURL &url); + + protected: + + class HistoryEntry; + void addHistoryEntry(const KURL& url); + void restoreHistoryEntry(const QValueList<HistoryEntry>::Iterator& entry); + void updateHistoryEntry(); + + protected slots: + + void slotSetCaption(const QString& cap); + void slotBack(); + void slotForward(); + void slotReload(); + void slotStop(); + + virtual void slotPaletteOrFontChanged(); + + void slotStarted(KIO::Job *); + void slotCompleted(); + void slotCancelled(const QString &errMsg); + void slotBackAboutToShow(); + void slotForwardAboutToShow(); + void slotPopupActivated( int id ); + virtual void slotPopupMenu(KXMLGUIClient*, const QPoint&, const KURL&, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t); + + void slotGlobalBookmarkArticle(); + + virtual void slotOpenURLRequest(const KURL& url, const KParts::URLArgs& args); + virtual void urlSelected(const QString &url, int button, int state, const QString &_target, KParts::URLArgs args); + + signals: + void setTabIcon(const QPixmap&); + + private: + class PageViewerPrivate; + PageViewerPrivate* d; + }; +} + +#endif // PAGEVIEWER_H diff --git a/akregator/src/pageviewer.rc b/akregator/src/pageviewer.rc new file mode 100644 index 000000000..dd44ee368 --- /dev/null +++ b/akregator/src/pageviewer.rc @@ -0,0 +1,41 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui name="pageviewer" version="17"> +<ToolBar name="mainToolBar"> + <Separator/> + <Action name="pageviewer_back"/> + <Action name="pageviewer_forward"/> + <Action name="pageviewer_reload"/> + <Action name="pageviewer_stop"/> + <Action name="pageviewer_print"/> +</ToolBar> +<MenuBar> +<Menu name="file"> + <Action name="viewer_print"/> + <Separator/> +</Menu> +<Menu noMerge="1" name="edit"> + <Action name="viewer_copy"/> + <Action name="selectAll"/> + <Separator/> + <Action name="find"/> + <Action name="findNext"/> + <Merge/> +</Menu> +<Menu noMerge="1" name="view"> + <Separator/> + <Action name="pageviewer_reload"/> + <Action name="pageviewer_stop"/> + <Action name="incFontSizes"/> + <Action name="decFontSizes"/> + <Action name="setEncoding"/> + <Merge/> +</Menu> +<Menu name="go"> + <text>&Go</text> + <Separator/> + <Action name="pageviewer_back"/> + <Action name="pageviewer_forward"/> +</Menu> +</MenuBar> + +</kpartgui> diff --git a/akregator/src/pics/Makefile.am b/akregator/src/pics/Makefile.am new file mode 100644 index 000000000..344a54364 --- /dev/null +++ b/akregator/src/pics/Makefile.am @@ -0,0 +1,8 @@ +pics_DATA = akregator_flag.png kmmsgflag.png kmmsgnew.png kmmsgunseen.png +picsdir = $(kde_datadir)/akregator/pics + +KDE_ICON = AUTO + +#EXTRA_DIST = $(pics_DATA) +#akregatoricondir = $(kde_datadir)/akregator/icons +#akregator_ICON = rss_label diff --git a/akregator/src/pics/action-rss_tag.svg b/akregator/src/pics/action-rss_tag.svg new file mode 100644 index 000000000..94b116540 --- /dev/null +++ b/akregator/src/pics/action-rss_tag.svg @@ -0,0 +1,1359 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" +"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + sodipodi:docname="action-rss_tag.svg" + sodipodi:docbase="/mnt/ablage01/frank/cvs-mirror/kde_3.5_branch/kdepim/akregator/src/pics" + inkscape:version="0.40" + sodipodi:version="0.32" + id="svg1291" + height="64px" + width="64px" + inkscape:export-filename="/home/frank/akregator_label128.png" + inkscape:export-xdpi="180.00160" + inkscape:export-ydpi="180.00160" + xmlns="http://www.w3.org/2000/svg" + xmlns:cc="http://web.resource.org/cc/" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs + id="defs3"> + <linearGradient + id="linearGradient2661" + inkscape:collect="always"> + <stop + id="stop2663" + offset="0" + style="stop-color:#fba200;stop-opacity:1;" /> + <stop + id="stop2665" + offset="1" + style="stop-color:#fba200;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient2653" + inkscape:collect="always"> + <stop + id="stop2655" + offset="0" + style="stop-color:#fbbc01;stop-opacity:1;" /> + <stop + id="stop2657" + offset="1" + style="stop-color:#fbbc01;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient1126"> + <stop + id="stop1127" + offset="0.0000000" + style="stop-color:#d10000;stop-opacity:1.0000000;" /> + <stop + id="stop1128" + offset="1.0000000" + style="stop-color:#ffffff;stop-opacity:0.00000000;" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient2323"> + <stop + style="stop-color:#c20000;stop-opacity:1;" + offset="0" + id="stop2325" /> + <stop + style="stop-color:#c20000;stop-opacity:0;" + offset="1" + id="stop2327" /> + </linearGradient> + <linearGradient + id="linearGradient15992"> + <stop + id="stop15994" + offset="0.00000000" + style="stop-color:#000008;stop-opacity:1.0000000;" /> + <stop + id="stop15996" + offset="1" + style="stop-color:#54538a;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient9809" + inkscape:collect="always"> + <stop + id="stop9811" + offset="0" + style="stop-color:#fff5f5;stop-opacity:1;" /> + <stop + id="stop9813" + offset="1" + style="stop-color:#fff5f5;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient9017"> + <stop + id="stop9019" + offset="0.0000000" + style="stop-color:#0000ff;stop-opacity:1.0000000;" /> + <stop + id="stop9021" + offset="1.0000000" + style="stop-color:#ffffff;stop-opacity:0.00000000;" /> + </linearGradient> + <linearGradient + spreadMethod="pad" + gradientUnits="userSpaceOnUse" + y2="66.025200" + x2="83.280640" + y1="17.930931" + x1="28.580629" + gradientTransform="scale(0.847174,1.180395)" + id="linearGradient9023" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="51.873116" + x2="75.208023" + y1="32.532787" + x1="47.131489" + gradientTransform="scale(0.847174,1.180395)" + id="linearGradient9035" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="25.961311" + x2="22.343248" + y1="14.214316" + x1="11.890828" + gradientTransform="scale(1.451933,0.688737)" + id="linearGradient9825" + xlink:href="#linearGradient9809" + inkscape:collect="always" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="60.378582" + x2="39.641415" + y1="47.939220" + x1="-4.3552227" + gradientTransform="matrix(1.017027,0.000000,0.000000,1.096079,-1.333333,-1.704201)" + id="linearGradient15998" + xlink:href="#linearGradient15992" + inkscape:collect="always" /> + <linearGradient + y2="25.961311" + x2="22.343248" + y1="14.214316" + x1="11.890828" + gradientTransform="scale(1.451933,0.688737)" + gradientUnits="userSpaceOnUse" + id="linearGradient16010" + xlink:href="#linearGradient9809" + inkscape:collect="always" /> + <linearGradient + y2="25.961311" + x2="22.343248" + y1="14.214316" + x1="11.890828" + gradientTransform="matrix(1.451933,0.000000,0.000000,0.688737,-10.06250,-5.750000)" + gradientUnits="userSpaceOnUse" + id="linearGradient16013" + xlink:href="#linearGradient9809" + inkscape:collect="always" /> + <linearGradient + y2="66.025200" + x2="83.280640" + y1="17.930931" + x1="28.580629" + spreadMethod="pad" + gradientTransform="scale(0.847174,1.180395)" + gradientUnits="userSpaceOnUse" + id="linearGradient16029" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="51.873116" + x2="75.208023" + y1="32.532787" + x1="47.131489" + gradientTransform="scale(0.847174,1.180395)" + gradientUnits="userSpaceOnUse" + id="linearGradient16031" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="25.961311" + x2="22.343248" + y1="14.214316" + x1="11.890828" + gradientTransform="scale(1.451933,0.688737)" + gradientUnits="userSpaceOnUse" + id="linearGradient16033" + xlink:href="#linearGradient9809" + inkscape:collect="always" /> + <linearGradient + y2="60.378582" + x2="39.641415" + y1="47.939220" + x1="-4.3552227" + gradientTransform="matrix(1.017027,5.265212e-17,-9.704534e-17,1.096079,-1.333333,-1.704201)" + gradientUnits="userSpaceOnUse" + id="linearGradient16035" + xlink:href="#linearGradient15992" + inkscape:collect="always" /> + <linearGradient + y2="60.378582" + x2="39.641415" + y1="47.939220" + x1="-4.3552227" + gradientTransform="matrix(1.017027,9.543544e-17,-1.289828e-16,1.096079,23.27125,-34.12652)" + gradientUnits="userSpaceOnUse" + id="linearGradient16038" + xlink:href="#linearGradient15992" + inkscape:collect="always" /> + <linearGradient + y2="66.025200" + x2="83.280640" + y1="17.930931" + x1="28.580629" + spreadMethod="pad" + gradientTransform="matrix(0.648973,0.544552,-0.758743,0.904236,39.68886,-9.021447)" + gradientUnits="userSpaceOnUse" + id="linearGradient16044" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="51.873116" + x2="75.208023" + y1="32.532787" + x1="47.131489" + gradientTransform="matrix(0.648973,0.544552,-0.758743,0.904236,39.68886,-9.021447)" + gradientUnits="userSpaceOnUse" + id="linearGradient16046" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="66.025200" + x2="83.280640" + y1="17.930931" + x1="28.580629" + spreadMethod="pad" + gradientTransform="matrix(0.648973,0.544552,-0.758743,0.904236,39.68886,-9.021447)" + gradientUnits="userSpaceOnUse" + id="linearGradient16059" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="51.873116" + x2="75.208023" + y1="32.532787" + x1="47.131489" + gradientTransform="matrix(0.648973,0.544552,-0.758743,0.904236,39.68886,-9.021447)" + gradientUnits="userSpaceOnUse" + id="linearGradient16061" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="60.378582" + x2="39.641415" + y1="47.939220" + x1="-4.3552227" + gradientTransform="matrix(1.017027,-3.805566e-16,1.277781e-15,1.096079,23.27125,-34.12652)" + gradientUnits="userSpaceOnUse" + id="linearGradient16065" + xlink:href="#linearGradient15992" + inkscape:collect="always" /> + <linearGradient + y2="51.873116" + x2="75.208023" + y1="32.532787" + x1="47.131489" + gradientTransform="matrix(0.648973,0.544552,-0.758743,0.904236,39.68886,-9.021447)" + gradientUnits="userSpaceOnUse" + id="linearGradient16085" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="25.961311" + x2="22.343248" + y1="14.214316" + x1="11.890828" + gradientTransform="scale(1.451933,0.688737)" + gradientUnits="userSpaceOnUse" + id="linearGradient16087" + xlink:href="#linearGradient9809" + inkscape:collect="always" /> + <linearGradient + y2="66.025200" + x2="83.280640" + y1="17.930931" + x1="28.580629" + spreadMethod="pad" + gradientTransform="matrix(0.648973,0.544552,-0.758743,0.904236,39.68886,-9.021447)" + gradientUnits="userSpaceOnUse" + id="linearGradient16879" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="51.873116" + x2="75.208023" + y1="32.532787" + x1="47.131489" + gradientTransform="matrix(0.648973,0.544552,-0.758743,0.904236,39.68886,-9.021447)" + gradientUnits="userSpaceOnUse" + id="linearGradient16881" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="25.961311" + x2="22.343248" + y1="14.214316" + x1="11.890828" + gradientTransform="scale(1.451933,0.688737)" + gradientUnits="userSpaceOnUse" + id="linearGradient16883" + xlink:href="#linearGradient9809" + inkscape:collect="always" /> + <linearGradient + y2="60.378582" + x2="39.641415" + y1="47.939220" + x1="-4.3552227" + gradientTransform="matrix(1.017027,-5.191614e-16,2.040001e-15,1.096079,23.27125,-34.12652)" + gradientUnits="userSpaceOnUse" + id="linearGradient16885" + xlink:href="#linearGradient15992" + inkscape:collect="always" /> + <linearGradient + y2="66.025200" + x2="83.280640" + y1="17.930931" + x1="28.580629" + spreadMethod="pad" + gradientTransform="matrix(0.716108,0.557983,-0.837233,0.926539,44.89806,-11.29329)" + gradientUnits="userSpaceOnUse" + id="linearGradient16894" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="51.873116" + x2="75.208023" + y1="32.532787" + x1="47.131489" + gradientTransform="matrix(0.716108,0.557983,-0.837233,0.926539,44.89806,-11.29329)" + gradientUnits="userSpaceOnUse" + id="linearGradient16896" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="66.025200" + x2="83.280640" + y1="17.930931" + x1="28.580629" + spreadMethod="pad" + gradientTransform="matrix(0.716108,0.557983,-0.837233,0.926539,44.89806,-11.29329)" + gradientUnits="userSpaceOnUse" + id="linearGradient16922" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="51.873116" + x2="75.208023" + y1="32.532787" + x1="47.131489" + gradientTransform="matrix(0.716108,0.557983,-0.837233,0.926539,44.89806,-11.29329)" + gradientUnits="userSpaceOnUse" + id="linearGradient16924" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="25.961311" + x2="22.343248" + y1="14.214316" + x1="11.890828" + gradientTransform="scale(1.451933,0.688737)" + gradientUnits="userSpaceOnUse" + id="linearGradient16926" + xlink:href="#linearGradient9809" + inkscape:collect="always" /> + <linearGradient + y2="66.025200" + x2="83.280640" + y1="17.930931" + x1="28.580629" + spreadMethod="pad" + gradientTransform="matrix(0.716108,0.557983,-0.837233,0.926539,-32.89505,-11.53480)" + gradientUnits="userSpaceOnUse" + id="linearGradient17710" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="51.873116" + x2="75.208023" + y1="32.532787" + x1="47.131489" + gradientTransform="matrix(0.716108,0.557983,-0.837233,0.926539,-32.89505,-11.53480)" + gradientUnits="userSpaceOnUse" + id="linearGradient17712" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="25.961311" + x2="22.343248" + y1="14.214316" + x1="11.890828" + gradientTransform="scale(1.451933,0.688737)" + gradientUnits="userSpaceOnUse" + id="linearGradient17714" + xlink:href="#linearGradient9809" + inkscape:collect="always" /> + <linearGradient + y2="17.047716" + x2="11.577650" + y1="42.168972" + x1="84.025276" + gradientTransform="matrix(0.969468,3.374029e-17,3.539568e-17,1.031493,-58.03532,47.75054)" + gradientUnits="userSpaceOnUse" + id="linearGradient17716" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="17.047716" + x2="11.577650" + y1="42.168972" + x1="84.025276" + gradientTransform="matrix(0.242367,-5.966952e-8,1.019423e-7,0.257873,-25.76823,85.71433)" + gradientUnits="userSpaceOnUse" + id="linearGradient17725" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="25.961311" + x2="22.343248" + y1="14.214316" + x1="11.890828" + gradientTransform="scale(1.451933,0.688737)" + gradientUnits="userSpaceOnUse" + id="linearGradient17727" + xlink:href="#linearGradient9809" + inkscape:collect="always" /> + <linearGradient + y2="66.025200" + x2="83.280640" + y1="17.930931" + x1="28.580629" + spreadMethod="pad" + gradientTransform="matrix(0.179027,0.139496,-0.209308,0.231635,-66.56857,44.93517)" + gradientUnits="userSpaceOnUse" + id="linearGradient17729" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + y2="51.873116" + x2="75.208023" + y1="32.532787" + x1="47.131489" + gradientTransform="matrix(0.179027,0.139496,-0.209308,0.231635,-66.56857,44.93517)" + gradientUnits="userSpaceOnUse" + id="linearGradient17731" + xlink:href="#linearGradient9017" + inkscape:collect="always" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1360" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.899908,-3.078910e-7,-2.133748e-7,0.958551,3.173289,4.587904)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1366" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.724069,0.550685,-0.846541,0.914419,43.01773,-8.637672)" + spreadMethod="pad" + x1="28.580629" + y1="17.930931" + x2="83.280640" + y2="66.025200" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1368" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.724069,0.550685,-0.846541,0.914419,43.01773,-8.637672)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient1372" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.451933,0.688737)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1376" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.758880,0.573629,-0.887241,0.952520,43.41738,-10.55313)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1462" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.701433,0.535266,-0.634318,0.687491,-0.838775,6.784376)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1892" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.701433,0.535266,-0.634318,0.687491,-0.838775,6.784376)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1895" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.899908,-3.078911e-7,-2.133747e-7,0.958551,3.173288,4.587905)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1901" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.724069,0.550685,-0.846541,0.914419,43.01773,-8.637672)" + spreadMethod="pad" + x1="28.580629" + y1="17.930931" + x2="83.280640" + y2="66.025200" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1903" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.724069,0.550685,-0.846541,0.914419,43.01773,-8.637672)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1907" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.758880,0.573629,-0.887241,0.952520,43.41738,-10.55313)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1919" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.758880,0.573629,-0.887241,0.952520,43.41738,-10.55313)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1921" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.724069,0.550685,-0.846541,0.914419,43.01773,-8.637672)" + spreadMethod="pad" + x1="28.580629" + y1="17.930931" + x2="83.280640" + y2="66.025200" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1923" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.724069,0.550685,-0.846541,0.914419,43.01773,-8.637672)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient1925" + gradientUnits="userSpaceOnUse" + gradientTransform="scale(1.451933,0.688737)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1927" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.899908,-3.078911e-7,-2.133747e-7,0.958551,3.173288,4.587905)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1929" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.701433,0.535266,-0.634318,0.687491,-0.838775,6.784376)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1951" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.593401,0.510882,-0.536622,0.656171,6.979416,9.066452)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1954" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.799881,1.915204e-7,2.366056e-8,0.870019,10.46089,1.005455)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1960" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.632955,0.558304,-0.740016,0.927070,45.50530,-9.610880)" + spreadMethod="pad" + x1="28.580629" + y1="17.930931" + x2="83.280640" + y2="66.025200" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1962" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.632955,0.558304,-0.740016,0.927070,45.50530,-9.610880)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1966" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.663385,0.581565,-0.775594,0.965698,45.85466,-11.55283)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1387" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.663385,0.581565,-0.775594,0.965698,45.85466,-11.55283)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1389" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.632955,0.558304,-0.740016,0.927070,45.50530,-9.610880)" + spreadMethod="pad" + x1="28.580629" + y1="17.930931" + x2="83.280640" + y2="66.025200" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1391" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.632955,0.558304,-0.740016,0.927070,45.50530,-9.610880)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient1393" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.403670,-0.127293,0.212178,0.693176,0.394752,1.355056)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1395" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.799881,1.915204e-7,2.366056e-8,0.870019,10.46089,1.005455)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1397" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.593401,0.510882,-0.536622,0.656171,6.979416,9.066452)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1385" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.665876,0.407712,-0.419742,0.734488,7.975098,16.70923)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1388" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.794396,-0.130341,0.141893,0.848609,15.80607,6.172408)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1394" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.712442,0.448078,-0.575215,1.034774,42.74812,-7.971678)" + spreadMethod="pad" + x1="28.580629" + y1="17.930931" + x2="83.280640" + y2="66.025200" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1396" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.712442,0.448078,-0.575215,1.034774,42.74812,-7.971678)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1400" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.751995,0.465115,-0.610722,1.079798,43.11020,-9.999868)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1422" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.666041,0.581565,-0.778699,0.965699,46.00630,-11.55284)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1424" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.629798,0.558303,-0.736324,0.927071,45.31819,-9.610910)" + spreadMethod="pad" + x1="28.580629" + y1="17.930931" + x2="83.280640" + y2="66.025200" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1426" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.629798,0.558303,-0.736324,0.927071,45.31819,-9.610910)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient1428" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.474082,-8.042974e-2,0.135171,0.716721,1.053385,0.966886)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1430" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.797082,-8.465833e-2,0.101338,0.857708,10.56968,-0.145509)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1432" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.590443,0.510882,-0.533945,0.656170,6.984538,9.066474)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient1481" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.474082,-8.042974e-2,0.135171,0.716721,1.053385,0.966886)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1578" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.595144,0.510882,-0.538196,0.656170,6.121598,9.066474)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1581" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.808243,-8.597047e-2,0.102757,0.871002,9.911163,0.698494)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1589" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.649128,0.558303,-0.758924,0.927071,45.54965,-9.610910)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1593" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.686484,0.581565,-0.802600,0.965699,46.25888,-11.55284)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2323" + id="linearGradient2329" + x1="36.338085" + y1="46.014549" + x2="44.730427" + y2="-5.8960142" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient2359" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.686484,0.581565,-0.802600,0.965699,46.25888,-11.55284)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient2363" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.649128,0.558303,-0.758924,0.927071,45.54965,-9.610910)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient2365" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.474082,-8.042974e-2,0.135171,0.716721,1.053385,0.966886)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3913" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.686484,0.581565,-0.802600,0.965699,46.25888,-11.55284)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3915" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.649128,0.558303,-0.758924,0.927071,45.54965,-9.610910)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient3917" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.474082,-8.042974e-2,0.135171,0.716721,1.053385,0.966886)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3919" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.686484,0.581565,-0.802600,0.965699,46.25888,-11.55284)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2323" + id="linearGradient3921" + gradientUnits="userSpaceOnUse" + x1="36.338085" + y1="46.014549" + x2="44.730427" + y2="-5.8960142" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3923" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.649128,0.558303,-0.758924,0.927071,45.54965,-9.610910)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient3925" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.474082,-8.042974e-2,0.135171,0.716721,1.053385,0.966886)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3927" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.808243,-8.597047e-2,0.102757,0.871002,9.911163,0.698494)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3929" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.595144,0.510882,-0.538196,0.656170,6.121598,9.066474)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3951" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.686484,0.581565,-0.802600,0.965699,46.25888,-11.55284)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3953" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.649128,0.558303,-0.758924,0.927071,45.54965,-9.610910)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient3955" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.474082,-8.042974e-2,0.135171,0.716721,1.053385,0.966886)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3957" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.686484,0.581565,-0.802600,0.965699,46.25888,-11.55284)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2323" + id="linearGradient3959" + gradientUnits="userSpaceOnUse" + x1="36.338085" + y1="46.014549" + x2="44.730427" + y2="-5.8960142" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3961" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.649128,0.558303,-0.758924,0.927071,45.54965,-9.610910)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient3963" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.474082,-8.042974e-2,0.135171,0.716721,1.053385,0.966886)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3965" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.808243,-8.597047e-2,0.102757,0.871002,9.911163,0.698494)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3967" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.595144,0.510882,-0.538196,0.656170,6.121598,9.066474)" + x1="84.025276" + y1="42.168972" + x2="11.577650" + y2="17.047716" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3987" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.686484,0.581565,-0.802600,0.965699,46.25888,-11.55284)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient3989" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.649128,0.558303,-0.758924,0.927071,45.54965,-9.610910)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient3991" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.474082,-8.042974e-2,0.135171,0.716721,1.053385,0.966886)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient4170" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.686484,0.581565,-0.802600,0.965699,46.25888,-11.55284)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient4172" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.649128,0.558303,-0.758924,0.927071,45.54965,-9.610910)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient4174" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.474082,-8.042974e-2,0.135171,0.716721,1.053385,0.966886)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9809" + id="linearGradient1434" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1.474082,-8.042974e-2,0.135171,0.716721,1.053385,0.966886)" + x1="11.890828" + y1="14.214316" + x2="22.343248" + y2="25.961311" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1440" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.641007,0.551319,-0.749430,0.915473,0.000000,0.000000)" + x1="82.225357" + y1="1.6718886" + x2="110.65758" + y2="21.257229" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient9017" + id="linearGradient1443" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.686484,0.581565,-0.802600,0.965699,46.25888,-11.55284)" + x1="47.131489" + y1="32.532787" + x2="75.208023" + y2="51.873116" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="-1.3823222" + x2="3.4841976" + y1="55.011410" + x1="31.139175" + gradientTransform="scale(1.158577,0.863128)" + id="linearGradient2659" + xlink:href="#linearGradient2653" + inkscape:collect="always" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="7.1703129" + x2="52.906246" + y1="2.8235404" + x1="64.717384" + gradientTransform="scale(0.854546,1.170213)" + id="linearGradient2667" + xlink:href="#linearGradient2661" + inkscape:collect="always" /> + </defs> + <sodipodi:namedview + inkscape:window-y="0" + inkscape:window-x="58" + inkscape:window-height="749" + inkscape:window-width="966" + inkscape:grid-bbox="true" + inkscape:document-units="px" + showgrid="true" + inkscape:current-layer="svg1291" + inkscape:cy="13.742020" + inkscape:cx="215.93571" + inkscape:zoom="1.9999999" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" /> + <metadata + id="metadata4"> + <rdf:RDF + id="RDF131"> + <cc:Work + rdf:about="" + id="Work133"> + <dc:format + id="format135">image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" + id="type137" /> + <dc:contributor + id="contributor1205"> + <cc:Agent + id="Agent1206"> + <dc:title + id="title1207"></dc:title> + </cc:Agent> + </dc:contributor> + <dc:creator + id="creator1209"> + <cc:Agent + id="Agent1210"> + <dc:title + id="title1211">Lee Olson, Dimitri Rizek</dc:title> + </cc:Agent> + </dc:creator> + <dc:rights + id="rights1213"> + <cc:Agent + id="Agent1214"> + <dc:title + id="title1215">Lee Olson, Dimitri Rizek</dc:title> + </cc:Agent> + </dc:rights> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + inkscape:label="Layer 1" + id="layer1" /> + <g + id="g2341" + transform="matrix(0.975140,0.000000,0.000000,0.976367,1.590990,1.459834)" + style="opacity:0.62325579"> + <path + id="path1100" + d="M 38.531250 9.1875000 C 37.438743 9.2373158 36.511748 9.5619588 35.843750 10.218750 C 35.827167 10.235055 35.828692 10.264630 35.812500 10.281250 C 33.863819 8.9707330 31.179702 8.9115024 29.468750 10.593750 L 2.9687500 36.156250 C 0.98791028 38.103857 1.2326767 41.600286 3.1562500 43.593750 L 20.968750 62.406250 C 22.892323 64.399714 26.550408 64.572611 28.531250 62.625000 L 55.156250 36.937500 C 56.838314 35.283656 56.707611 32.644502 55.468750 30.656250 C 55.513499 30.616766 55.582221 30.604561 55.625000 30.562500 C 57.166552 29.046809 56.918335 26.165661 55.531250 22.937500 C 56.999678 20.471700 56.102165 16.462609 53.062500 13.312500 C 50.019848 10.159296 46.073673 9.1704084 43.593750 10.593750 C 41.742029 9.7167942 39.981626 9.1213662 38.531250 9.1875000 z M 45.750000 13.718750 C 46.079577 14.718341 46.606434 15.632066 47.375000 16.406250 C 48.331406 17.369645 49.572184 17.914039 50.875000 18.218750 C 50.860808 19.143634 50.521568 20.033428 49.750000 20.687500 C 48.064490 22.116336 45.179857 21.923030 43.312500 20.250000 C 41.445146 18.576969 41.283242 16.053836 42.968750 14.625000 C 43.717945 13.989895 44.721873 13.724309 45.750000 13.718750 z M 47.218750 13.937500 C 48.011163 14.157826 48.764118 14.487190 49.406250 15.062500 C 50.024947 15.616814 50.388000 16.268099 50.625000 16.937500 C 49.716968 16.705107 48.878305 16.289373 48.218750 15.625000 C 47.740264 15.143019 47.460769 14.551966 47.218750 13.937500 z " + style="fill:#000000;fill-opacity:1.0000000;fill-rule:nonzero;stroke:url(#linearGradient4170);stroke-width:0.00000000;stroke-miterlimit:0.00000000;stroke-opacity:1.0000000" /> + <path + id="path2345" + d="M 61.952845,2.1236304 C 58.760508,-1.0444567 52.976848,-0.62029176 49.050328,3.0778104 C 45.123804,6.7759122 44.533979,12.354460 47.726317,15.522549 C 50.918656,18.690635 56.681031,18.245348 60.607552,14.547246 C 64.534076,10.849145 65.145181,5.2917177 61.952845,2.1236304 z M 60.279135,3.6999734 C 62.722883,6.1251582 62.081839,10.565129 58.848230,13.610624 C 55.614625,16.656118 51.006922,17.159560 48.563173,14.734376 C 46.119421,12.309193 46.760470,7.8692215 49.994075,4.8237267 C 53.227681,1.7782315 57.835388,1.2747898 60.279135,3.6999734 z " + style="fill:#000000;fill-opacity:1.0000000;fill-rule:nonzero;stroke:#000000;stroke-width:0.00000000;stroke-miterlimit:0.00000000;stroke-opacity:1.0000000" /> + </g> + <path + id="black" + d="M 37.281250,8.0303954 C 36.178396,8.0812612 35.236824,8.4199089 34.562500,9.0905441 C 34.546041,9.1069130 34.547328,9.1362256 34.531250,9.1529056 C 32.565284,7.8260007 29.878613,7.7517755 28.156250,9.4647140 L 1.3750000,35.563077 C -0.62459913,37.551736 -0.34804034,41.104540 1.5937500,43.140020 L 19.562500,62.378598 C 21.504290,64.414079 25.187899,64.585526 27.187500,62.596863 L 54.093750,36.342599 C 55.794252,34.651401 55.630915,31.981623 54.375000,29.950525 C 54.420173,29.910208 54.488066,29.899929 54.531250,29.856981 C 56.089022,28.307733 55.841916,25.362181 54.437500,22.061772 C 55.916949,19.543639 55.035843,15.454890 51.968750,12.239809 C 48.897283,9.0201422 44.909659,8.0113710 42.406250,9.4647140 C 40.541471,8.5731220 38.742202,7.9630135 37.281250,8.0303954 z M 45.718750,12.894606 C 46.883729,12.974500 48.066322,13.431899 49.000000,14.266564 C 50.867354,15.935892 51.029259,18.453441 49.343750,19.879114 C 47.658243,21.304789 44.773606,21.111911 42.906250,19.442581 C 41.038895,17.773254 40.876991,15.255705 42.562500,13.830032 C 43.405255,13.117195 44.553771,12.814713 45.718750,12.894606 z " + style="stroke:url(#linearGradient1443);stroke-width:0.0000000;stroke-miterlimit:0.0000000" /> + <path + id="red" + d="M 37.093750,8.9687500 C 36.067538,9.0248371 35.169956,9.3539053 34.531250,10.000000 C 34.504302,10.027260 34.494662,10.065612 34.468750,10.093750 C 32.636978,8.7733764 30.137866,8.8740790 28.468750,10.562500 L 2.9375000,35.343750 C 1.0435199,37.259637 1.0103297,40.357146 2.8437500,42.312500 L 20.031250,60.687500 C 21.864669,62.642854 24.887272,62.665890 26.781250,60.750000 L 52.312500,35.968750 C 53.909849,34.352925 54.112306,31.907013 53.062500,30.000000 C 53.107562,29.959306 53.175760,29.949737 53.218750,29.906250 C 54.696819,28.411082 54.455031,25.554744 53.125000,22.375000 C 54.523514,19.949966 53.706386,16.023850 50.812500,12.937500 C 47.926950,9.8600426 44.184993,8.9116594 41.812500,10.281250 C 40.089327,9.4446700 38.446070,8.8948397 37.093750,8.9687500 z M 45.718750,12.781250 C 46.883729,12.861321 48.066322,13.319734 49.000000,14.156250 C 50.867354,15.829281 51.029259,18.352414 49.343750,19.781250 C 47.658243,21.210085 44.773606,21.016781 42.906250,19.343750 C 41.038895,17.670718 40.876991,15.147586 42.562500,13.718750 C 43.405255,13.004332 44.553771,12.701179 45.718750,12.781250 z " + style="fill:#fca703;fill-opacity:1.0000000;stroke:url(#linearGradient1440);stroke-width:0.0000000;stroke-miterlimit:0.0000000" /> + <rect + style="fill:#ffffff;stroke:#000000;stroke-width:0.0000000;stroke-miterlimit:0.0000000" + id="paper" + width="25.225115" + height="34.648060" + x="32.284386" + y="-12.584444" + transform="matrix(0.687023,0.726636,-0.721694,0.692212,0.000000,0.000000)" + ry="4.7757635" + rx="3.8291748" /> + <path + id="path1064" + d="M 45.625000,11.625000 C 44.110098,11.526946 42.632452,11.941497 41.531250,12.875000 C 39.328845,14.742005 39.488623,17.990207 41.906250,20.156250 C 44.323876,22.322293 48.078845,22.585756 50.281250,20.718750 C 52.483655,18.851745 52.292625,15.572292 49.875000,13.406250 C 48.666187,12.323229 47.139902,11.723054 45.625000,11.625000 z M 45.718750,12.781250 C 46.883729,12.861321 48.066322,13.319734 49.000000,14.156250 C 50.867354,15.829281 51.029259,18.352414 49.343750,19.781250 C 47.658243,21.210086 44.773606,21.016781 42.906250,19.343750 C 41.038895,17.670719 40.876991,15.147586 42.562500,13.718750 C 43.405255,13.004332 44.553771,12.701179 45.718750,12.781250 z " + style="fill:#000000;fill-opacity:1.0000000;stroke:url(#linearGradient1434);stroke-width:0.0000000;stroke-miterlimit:0.0000000" /> + <path + id="path1108" + d="M 54.906250,0.031250000 C 52.606974,0.20339804 50.213834,1.2150058 48.281250,3.0625000 C 46.402889,4.8581570 45.339118,7.1214448 45.062500,9.3125000 L 46.562500,9.5312500 C 46.905921,7.8856842 47.765844,6.2014350 49.218750,4.8125000 C 52.401831,1.7695693 56.938187,1.2643584 59.343750,3.6875000 C 61.749315,6.1106429 61.120584,10.550819 57.937500,13.593750 C 54.754420,16.636680 50.218065,17.141892 47.812500,14.718750 C 47.203888,14.105693 46.790216,13.378107 46.562500,12.562500 L 45.281250,12.500000 C 45.596078,13.617967 46.122538,14.647604 46.968750,15.500000 C 50.111207,18.665419 55.791081,18.226238 59.656250,14.531250 C 63.521420,10.836263 64.142456,5.2904198 61.000000,2.1250000 C 59.428771,0.54229017 57.205526,-0.14089804 54.906250,0.031250000 z " + style="stroke:#000000;stroke-width:0.0000000;stroke-miterlimit:0.0000000" /> + <path + id="path1125" + d="M 54.031250,17.281250 C 53.085110,17.531560 52.178297,17.628233 51.281250,17.562500 L 50.812500,18.750000 C 51.970797,18.952501 53.143953,18.950257 54.375000,18.718750 L 54.031250,17.281250 z " + style="fill:#000000;fill-opacity:0.49999997;fill-rule:evenodd;stroke:none;stroke-width:1.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000" /> + <rect + style="opacity:0.12616821;fill:url(#linearGradient2667);fill-opacity:1.0;stroke:#000000;stroke-width:0.0000000;stroke-miterlimit:0.0000000" + id="paperred" + width="28.222322" + height="38.647583" + x="30.925850" + y="-10.516919" + rx="4.9494791" + ry="6.2216563" + transform="matrix(0.735183,0.677869,-0.767156,0.641461,0.000000,0.000000)" /> + <path + style="opacity:0.12616821;fill:url(#linearGradient2659);fill-opacity:1.0;stroke:#000000;stroke-width:0.0000000;stroke-miterlimit:0.0000000" + d="M 25.010654,31.735940 L 26.237321,35.931927 C 28.866096,44.924010 44.034897,38.635171 46.999997,37.076518 L 26.313078,54.334913 C 24.263760,56.050050 20.261727,56.601308 18.746508,55.220299 L 4.4567263,42.099445 C 2.9415032,40.718437 3.3714859,37.233761 5.4208045,35.518632 L 19.006261,24.103441 C 21.055576,22.388309 23.169373,25.437572 25.010654,31.735940 z " + id="paperlight" + sodipodi:nodetypes="czccccccz" /> +</svg> diff --git a/akregator/src/pics/akregator_flag.png b/akregator/src/pics/akregator_flag.png Binary files differnew file mode 100644 index 000000000..c3c3da372 --- /dev/null +++ b/akregator/src/pics/akregator_flag.png diff --git a/akregator/src/pics/cr16-action-rss_tag.png b/akregator/src/pics/cr16-action-rss_tag.png Binary files differnew file mode 100644 index 000000000..120a2601e --- /dev/null +++ b/akregator/src/pics/cr16-action-rss_tag.png diff --git a/akregator/src/pics/cr22-action-rss_tag.png b/akregator/src/pics/cr22-action-rss_tag.png Binary files differnew file mode 100644 index 000000000..9e93eb6c2 --- /dev/null +++ b/akregator/src/pics/cr22-action-rss_tag.png diff --git a/akregator/src/pics/cr32-action-rss_tag.png b/akregator/src/pics/cr32-action-rss_tag.png Binary files differnew file mode 100644 index 000000000..73a341fa7 --- /dev/null +++ b/akregator/src/pics/cr32-action-rss_tag.png diff --git a/akregator/src/pics/cr48-action-rss_tag.png b/akregator/src/pics/cr48-action-rss_tag.png Binary files differnew file mode 100644 index 000000000..58a1e87b9 --- /dev/null +++ b/akregator/src/pics/cr48-action-rss_tag.png diff --git a/akregator/src/pics/cr64-action-rss_tag.png b/akregator/src/pics/cr64-action-rss_tag.png Binary files differnew file mode 100644 index 000000000..cbdec466d --- /dev/null +++ b/akregator/src/pics/cr64-action-rss_tag.png diff --git a/akregator/src/pics/kmmsgflag.png b/akregator/src/pics/kmmsgflag.png Binary files differnew file mode 100644 index 000000000..c3c3da372 --- /dev/null +++ b/akregator/src/pics/kmmsgflag.png diff --git a/akregator/src/pics/kmmsgnew.png b/akregator/src/pics/kmmsgnew.png Binary files differnew file mode 100644 index 000000000..245d1569b --- /dev/null +++ b/akregator/src/pics/kmmsgnew.png diff --git a/akregator/src/pics/kmmsgunseen.png b/akregator/src/pics/kmmsgunseen.png Binary files differnew file mode 100644 index 000000000..8e3de2022 --- /dev/null +++ b/akregator/src/pics/kmmsgunseen.png diff --git a/akregator/src/plugin.cpp b/akregator/src/plugin.cpp new file mode 100644 index 000000000..ad3825ba9 --- /dev/null +++ b/akregator/src/plugin.cpp @@ -0,0 +1,41 @@ +// Author: Mark Kretschmann (C) Copyright 2004 +// Copyright: See COPYING file that comes with this distribution + +#include "plugin.h" + + +namespace Akregator { + + +Plugin::Plugin() +{} + + +Plugin::~Plugin() +{} + + +void +Plugin::addPluginProperty( const QString& key, const QString& value ) +{ + m_properties[key.lower()] = value; +} + + +QString +Plugin::pluginProperty( const QString& key ) +{ + if ( m_properties.find( key.lower() ) == m_properties.end() ) + return "false"; + + return m_properties[key.lower()]; +} + + +bool +Plugin::hasPluginProperty( const QString& key ) +{ + return m_properties.find( key.lower() ) != m_properties.end(); +} + +} diff --git a/akregator/src/plugin.h b/akregator/src/plugin.h new file mode 100644 index 000000000..ed10bf200 --- /dev/null +++ b/akregator/src/plugin.h @@ -0,0 +1,51 @@ +// Author: Mark Kretschmann (C) Copyright 2004 +// Copyright: See COPYING file that comes with this distribution + +#ifndef AKREGATOR_PLUGIN_H +#define AKREGATOR_PLUGIN_H + +#include "akregator_export.h" + +#define AKREGATOR_EXPORT_PLUGIN( classname ) \ + extern "C" { \ + Akregator::Plugin* create_plugin() { return new classname; } \ + } + +#include <qmap.h> +#include <qstring.h> + + +namespace Akregator +{ +// class PluginConfig; + + class AKREGATOR_EXPORT Plugin + { + public: + virtual ~Plugin(); + + virtual bool init() = 0; + /** + * TODO @param parent you must parent the widget to parent + * @return the configure widget for your plugin, create it on the heap! + */ + //TODO rename configureWidget( QWidget *parent ) + // virtual PluginConfig* configure() const { return 0; } + + void addPluginProperty( const QString& key, const QString& value ); + QString pluginProperty( const QString& key ); + bool hasPluginProperty( const QString& key ); + + protected: + Plugin(); + + private: + QMap<QString, QString> m_properties; + }; + +} //namespace Akregator + + +#endif /* AKREGATOR_PLUGIN_H */ + + diff --git a/akregator/src/pluginmanager.cpp b/akregator/src/pluginmanager.cpp new file mode 100644 index 000000000..db6c70117 --- /dev/null +++ b/akregator/src/pluginmanager.cpp @@ -0,0 +1,222 @@ +/*************************************************************************** +begin : 2004/03/12 +copyright : (C) Mark Kretschmann +email : markey@web.de +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "plugin.h" +#include "pluginmanager.h" + +#include <vector> + +#include <qfile.h> +#include <qstring.h> + +#include <klibloader.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> + +using std::vector; +using Akregator::Plugin; + +namespace Akregator { + +vector<PluginManager::StoreItem> +PluginManager::m_store; + + +///////////////////////////////////////////////////////////////////////////////////// +// PUBLIC INTERFACE +///////////////////////////////////////////////////////////////////////////////////// + +KTrader::OfferList +PluginManager::query( const QString& constraint ) +{ + // Add versioning constraint + QString + str = "[X-KDE-akregator-framework-version] == "; + str += QString::number( FrameworkVersion ); + str += " and "; + if (!constraint.stripWhiteSpace().isEmpty()) + str += constraint + " and "; + str += "[X-KDE-akregator-rank] > 0"; + + kdDebug() << "Plugin trader constraint: " << str << endl; + + return KTrader::self()->query( "Akregator/Plugin", str ); +} + + +Plugin* +PluginManager::createFromQuery( const QString &constraint ) +{ + KTrader::OfferList offers = query( constraint ); + + if ( offers.isEmpty() ) { + kdWarning() << k_funcinfo << "No matching plugin found.\n"; + return 0; + } + + // Select plugin with highest rank + int rank = 0; + uint current = 0; + for ( uint i = 0; i < offers.count(); i++ ) { + if ( offers[i]->property( "X-KDE-akregator-rank" ).toInt() > rank ) + current = i; + } + + return createFromService( offers[current] ); +} + + +Plugin* +PluginManager::createFromService( const KService::Ptr service ) +{ + kdDebug() << "Trying to load: " << service->library() << endl; + + //get the library loader instance + KLibLoader *loader = KLibLoader::self(); + //try to load the specified library + KLibrary *lib = loader->globalLibrary( QFile::encodeName( service->library() ) ); + + if ( !lib ) { + KMessageBox::error( 0, i18n( "<p>KLibLoader could not load the plugin:<br/><i>%1</i></p>" + "<p>Error message:<br/><i>%2</i></p>" ) + .arg( service->library() ) + .arg( loader->lastErrorMessage() ) ); + return 0; + } + //look up address of init function and cast it to pointer-to-function + Plugin* (*create_plugin)() = ( Plugin* (*)() ) lib->symbol( "create_plugin" ); + + if ( !create_plugin ) { + kdWarning() << k_funcinfo << "create_plugin == NULL\n"; + return 0; + } + //create plugin on the heap + Plugin* plugin = create_plugin(); + + //put plugin into store + StoreItem item; + item.plugin = plugin; + item.library = lib; + item.service = service; + m_store.push_back( item ); + + dump( service ); + return plugin; +} + + +void +PluginManager::unload( Plugin* plugin ) +{ + vector<StoreItem>::iterator iter = lookupPlugin( plugin ); + + if ( iter != m_store.end() ) { + delete (*iter).plugin; + kdDebug() << "Unloading library: "<< (*iter).service->library() << endl; + (*iter).library->unload(); + + m_store.erase( iter ); + } + else + kdWarning() << k_funcinfo << "Could not unload plugin (not found in store).\n"; +} + + +KService::Ptr +PluginManager::getService( const Plugin* plugin ) +{ + if ( !plugin ) { + kdWarning() << k_funcinfo << "pointer == NULL\n"; + return 0; + } + + //search plugin in store + vector<StoreItem>::const_iterator iter = lookupPlugin( plugin ); + + if ( iter == m_store.end() ) + kdWarning() << k_funcinfo << "Plugin not found in store.\n"; + + return (*iter).service; +} + + +void +PluginManager::showAbout( const QString &constraint ) +{ + KTrader::OfferList offers = query( constraint ); + + if ( offers.isEmpty() ) + return; + + KService::Ptr s = offers.front(); + + const QString body = "<tr><td>%1</td><td>%2</td></tr>"; + + QString str = "<html><body><table width=\"100%\" border=\"1\">"; + + str += body.arg( i18n( "Name" ), s->name() ); + str += body.arg( i18n( "Library" ), s->library() ); + str += body.arg( i18n( "Authors" ), s->property( "X-KDE-akregator-authors" ).toStringList().join( "\n" ) ); + str += body.arg( i18n( "Email" ), s->property( "X-KDE-akregator-email" ).toStringList().join( "\n" ) ); + str += body.arg( i18n( "Version" ), s->property( "X-KDE-akregator-version" ).toString() ); + str += body.arg( i18n( "Framework Version" ), s->property( "X-KDE-akregator-framework-version" ).toString() ); + + str += "</table></body></html>"; + + KMessageBox::information( 0, str, i18n( "Plugin Information" ) ); +} + + +void +PluginManager::dump( const KService::Ptr service ) +{ + kdDebug() + << "PluginManager Service Info:" << endl + << "---------------------------" << endl + << "name : " << service->name() << endl + << "library : " << service->library() << endl + << "desktopEntryPath : " << service->desktopEntryPath() << endl + << "X-KDE-akregator-plugintype : " << service->property( "X-KDE-akregator-plugintype" ).toString() << endl + << "X-KDE-akregator-name : " << service->property( "X-KDE-akregator-name" ).toString() << endl + << "X-KDE-akregator-authors : " << service->property( "X-KDE-akregator-authors" ).toStringList() << endl + << "X-KDE-akregator-rank : " << service->property( "X-KDE-akregator-rank" ).toString() << endl + << "X-KDE-akregator-version : " << service->property( "X-KDE-akregator-version" ).toString() << endl + << "X-KDE-akregator-framework-version: " << service->property( "X-KDE-akregator-framework-version" ).toString() + << endl; + +} + + +///////////////////////////////////////////////////////////////////////////////////// +// PRIVATE INTERFACE +///////////////////////////////////////////////////////////////////////////////////// + +vector<PluginManager::StoreItem>::iterator +PluginManager::lookupPlugin( const Plugin* plugin ) +{ + vector<StoreItem>::iterator iter; + + //search plugin pointer in store + vector<StoreItem>::const_iterator end; + for ( iter = m_store.begin(); iter != end; ++iter ) { + if ( (*iter).plugin == plugin ) + break; + } + + return iter; +} + +} // namespace Akregator diff --git a/akregator/src/pluginmanager.h b/akregator/src/pluginmanager.h new file mode 100644 index 000000000..15d0c253e --- /dev/null +++ b/akregator/src/pluginmanager.h @@ -0,0 +1,116 @@ +/*************************************************************************** +begin : 2004/03/12 +copyright : (C) Mark Kretschmann +email : markey@web.de +***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef AKREGATOR_PLUGINMANAGER_H +#define AKREGATOR_PLUGINMANAGER_H + +#include <vector> + +#include <kservice.h> +#include <ktrader.h> + + +class KLibrary; +namespace Akregator { + +class Plugin; +class PluginManager +{ + public: + /** Bump this number whenever the plugin framework gets incompatible with older versions */ + static const int FrameworkVersion = 1; + + /** + * It will return a list of services that match your + * specifications. The only required parameter is the service + * type. This is something like 'text/plain' or 'text/html'. The + * constraint parameter is used to limit the possible choices + * returned based on the constraints you give it. + * + * The @p constraint language is rather full. The most common + * keywords are AND, OR, NOT, IN, and EXIST, all used in an + * almost spoken-word form. An example is: + * \code + * (Type == 'Service') and (('KParts/ReadOnlyPart' in ServiceTypes) or (exist Exec)) + * \endcode + * + * The keys used in the query (Type, ServiceType, Exec) are all + * fields found in the .desktop files. + * + * @param constraint A constraint to limit the choices returned, QString::null to + * get all services of the given @p servicetype + * + * @return A list of services that satisfy the query + * @see http://developer.kde.org/documentation/library/kdeqt/tradersyntax.html + */ + static KTrader::OfferList query( const QString& constraint = QString::null ); + + /** + * Load and instantiate plugin from query + * @param constraint A constraint to limit the choices returned, QString::null to + * get all services of the given @p servicetype + * @return Pointer to Plugin, or NULL if error + * @see http://developer.kde.org/documentation/library/kdeqt/tradersyntax.html + */ + static Akregator::Plugin* createFromQuery( const QString& constraint = QString::null ); + + /** + * Load and instantiate plugin from service + * @param service Pointer to KService + * @return Pointer to Plugin, or NULL if error + */ + static Akregator::Plugin* createFromService( const KService::Ptr service ); + + /** + * Remove library and delete plugin + * @param plugin Pointer to plugin + */ + static void unload( Akregator::Plugin* plugin ); + + /** + * Look up service for loaded plugin from store + * @param plugin Pointer to plugin + * @return KService, or 0 if not found + */ + static KService::Ptr getService( const Akregator::Plugin* plugin ); + + /** + * Dump properties from a service to stdout for debugging + * @param service Pointer to KService + */ + static void dump( const KService::Ptr service ); + + /** + * Show modal info dialog about plugin + * @param constraint A constraint to limit the choices returned + */ + static void showAbout( const QString& constraint ); + + private: + struct StoreItem { + Akregator::Plugin* plugin; + KLibrary* library; + KService::Ptr service; + }; + + static std::vector<StoreItem>::iterator lookupPlugin( const Akregator::Plugin* plugin ); + + //attributes: + static std::vector<StoreItem> m_store; +}; +} + +#endif /* AKREGATOR_PLUGINMANAGER_H */ + diff --git a/akregator/src/progressmanager.cpp b/akregator/src/progressmanager.cpp new file mode 100644 index 000000000..201cf5b6d --- /dev/null +++ b/akregator/src/progressmanager.cpp @@ -0,0 +1,209 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <qmap.h> +#include <qstylesheet.h> + +#include <klocale.h> +#include <kstaticdeleter.h> + +#include <libkdepim/progressmanager.h> + +#include "feedlist.h" +#include "feed.h" +#include "treenode.h" + +#include "progressmanager.h" + +//#include <kdebug.h> + +namespace Akregator { + +class ProgressManager::ProgressManagerPrivate +{ + public: + FeedList* feedList; + QMap<Feed*, ProgressItemHandler*> handlers; + +}; + +static KStaticDeleter<ProgressManager> progressmanagersd; +ProgressManager* ProgressManager::m_self = 0; + +ProgressManager* ProgressManager::self() +{ + if (!m_self) + m_self = progressmanagersd.setObject(m_self, new ProgressManager); + return m_self; +} + +ProgressManager::ProgressManager() : d(new ProgressManagerPrivate) +{ + d->feedList = 0; +} + +ProgressManager::~ProgressManager() +{ + delete d; + d = 0; +} + +void ProgressManager::setFeedList(FeedList* feedList) +{ + if (feedList == d->feedList) + return; + + if (d->feedList != 0) + { + for (QMap<Feed*, ProgressItemHandler*>::ConstIterator it = d->handlers.begin(); it != d->handlers.end(); ++it) + delete *it; + d->handlers.clear(); + + disconnect(d->feedList, SIGNAL(signalNodeAdded(TreeNode*)), this, SLOT(slotNodeAdded(TreeNode*))); + disconnect(d->feedList, SIGNAL(signalNodeRemoved(TreeNode*)), this, SLOT(slotNodeRemoved(TreeNode*))); + } + + d->feedList = feedList; + + if (feedList != 0) + { + QValueList<TreeNode*> list = feedList->asFlatList(); + + for (QValueList<TreeNode*>::ConstIterator it = list.begin(); it != list.end(); ++it) + slotNodeAdded(*it); + connect(feedList, SIGNAL(signalNodeAdded(TreeNode*)), this, SLOT(slotNodeAdded(TreeNode*))); + connect(feedList, SIGNAL(signalNodeRemoved(TreeNode*)), this, SLOT(slotNodeRemoved(TreeNode*))); + } +} + +void ProgressManager::slotNodeAdded(TreeNode* node) +{ + Feed* feed = dynamic_cast<Feed*>(node); + if (feed) + { + if (!d->handlers.contains(feed)) + d->handlers[feed] = new ProgressItemHandler(feed); + connect(feed, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotNodeDestroyed(TreeNode*))); + } +} + +void ProgressManager::slotNodeRemoved(TreeNode* node) +{ + Feed* feed = dynamic_cast<Feed*>(node); + if (feed) + { + disconnect(feed, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotNodeDestroyed(TreeNode*))); + delete d->handlers[feed]; + d->handlers.remove(feed); + } +} + +void ProgressManager::slotNodeDestroyed(TreeNode* node) +{ + Feed* feed = dynamic_cast<Feed*>(node); + if (feed) + { + delete d->handlers[feed]; + d->handlers.remove(feed); + } +} + +class ProgressItemHandler::ProgressItemHandlerPrivate +{ + public: + + Feed* feed; + KPIM::ProgressItem* progressItem; +}; + +ProgressItemHandler::ProgressItemHandler(Feed* feed) : d(new ProgressItemHandlerPrivate) +{ + d->feed = feed; + d->progressItem = 0; + + connect(feed, SIGNAL(fetchStarted(Feed*)), this, SLOT(slotFetchStarted())); + connect(feed, SIGNAL(fetched(Feed*)), this, SLOT(slotFetchCompleted())); + connect(feed, SIGNAL(fetchError(Feed*)), this, SLOT(slotFetchError())); + connect(feed, SIGNAL(fetchAborted(Feed*)), this, SLOT(slotFetchAborted())); +} + +ProgressItemHandler::~ProgressItemHandler() +{ + if (d->progressItem) + { + d->progressItem->setComplete(); + d->progressItem = 0; + } + + delete d; + d = 0; +} + +void ProgressItemHandler::slotFetchStarted() +{ + if (d->progressItem) + { + d->progressItem->setComplete(); + d->progressItem = 0; + } + + d->progressItem = KPIM::ProgressManager::createProgressItem(KPIM::ProgressManager::getUniqueID(), QStyleSheet::escape( d->feed->title() ), QString::null, true); + + connect(d->progressItem, SIGNAL(progressItemCanceled(KPIM::ProgressItem*)), d->feed, SLOT(slotAbortFetch())); +} + + +void ProgressItemHandler::slotFetchCompleted() +{ + if (d->progressItem) + { + d->progressItem->setStatus(i18n("Fetch completed")); + d->progressItem->setComplete(); + d->progressItem = 0; + } +} + +void ProgressItemHandler::slotFetchError() +{ + if (d->progressItem) + { + d->progressItem->setStatus(i18n("Fetch error")); + d->progressItem->setComplete(); + d->progressItem = 0; + } +} + +void ProgressItemHandler::slotFetchAborted() +{ + if (d->progressItem) + { + d->progressItem->setStatus(i18n("Fetch aborted")); + d->progressItem->setComplete(); + d->progressItem = 0; + } +} + +} // namespace Akregator + +#include "progressmanager.moc" diff --git a/akregator/src/progressmanager.h b/akregator/src/progressmanager.h new file mode 100644 index 000000000..c348becc4 --- /dev/null +++ b/akregator/src/progressmanager.h @@ -0,0 +1,87 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_PROGRESSMANAGER_H +#define AKREGATOR_PROGRESSMANAGER_H + +namespace Akregator +{ + +class Feed; +class TreeNode; +class ProgressItemHandler; +/** This class manages the progress items for all feeds */ + +class ProgressManager : public QObject +{ + Q_OBJECT + public: + + static ProgressManager* self(); + + ProgressManager(); + virtual ~ProgressManager(); + + /** sets the feed list to be managed */ + void setFeedList(FeedList* feedList); + + protected slots: + + void slotNodeAdded(TreeNode* node); + void slotNodeRemoved(TreeNode* node); + void slotNodeDestroyed(TreeNode* node); + + private: + + static ProgressManager* m_self; + + class ProgressManagerPrivate; + ProgressManagerPrivate* d; +}; + +/** this class handles the creation and deletion of progress items for one feed. + This is an internal class intended to be used in ProgressManager only */ + +class ProgressItemHandler : public QObject +{ + Q_OBJECT + public: + ProgressItemHandler(Feed* feed); + virtual ~ProgressItemHandler(); + + public slots: + + void slotFetchStarted(); + void slotFetchCompleted(); + void slotFetchAborted(); + void slotFetchError(); + + private: + class ProgressItemHandlerPrivate; + ProgressItemHandlerPrivate* d; +}; + +} // namespace Akregator + +#endif // AKREGATOR_PROGRESSMANAGER_H diff --git a/akregator/src/propertiesdialog.cpp b/akregator/src/propertiesdialog.cpp new file mode 100644 index 000000000..cd63c2045 --- /dev/null +++ b/akregator/src/propertiesdialog.cpp @@ -0,0 +1,324 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "akregatorconfig.h" +#include "feed.h" +#include "propertiesdialog.h" + +#include <kcombobox.h> +#include <klineedit.h> +#include <kpassdlg.h> +#include <klocale.h> +#include <knuminput.h> + +#include <qcheckbox.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> + +namespace Akregator { + +FeedPropertiesWidget::FeedPropertiesWidget(QWidget *parent, const char *name) + : FeedPropertiesWidgetBase(parent, name) +{ +} + +FeedPropertiesWidget::~FeedPropertiesWidget() +{} + + +void FeedPropertiesWidget::slotUpdateComboBoxActivated( int index ) +{ + if ( index == 3 ) // "never" + updateSpinBox->setEnabled(false); + else + updateSpinBox->setEnabled(true); +} + + +void FeedPropertiesWidget::slotUpdateCheckBoxToggled( bool enabled ) +{ + if (enabled && updateComboBox->currentItem() != 3 ) // "never" + updateSpinBox->setEnabled(true); + else + updateSpinBox->setEnabled(false); +} + + +FeedPropertiesDialog::FeedPropertiesDialog(QWidget *parent, const char *name) + : KDialogBase(KDialogBase::Swallow, Qt::WStyle_DialogBorder, parent, name, true, i18n("Feed Properties"), KDialogBase::Ok|KDialogBase::Cancel) +{ + widget=new FeedPropertiesWidget(this); + setMainWidget(widget); + widget->feedNameEdit->setFocus(); + + connect(widget->feedNameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(slotSetCaption(const QString&))); +} + +FeedPropertiesDialog::~FeedPropertiesDialog() +{} + +void FeedPropertiesDialog::slotOk() +{ + m_feed->setNotificationMode(false); + m_feed->setTitle( feedName() ); + m_feed->setXmlUrl( url() ); + m_feed->setCustomFetchIntervalEnabled(autoFetch()); + if (autoFetch()) + m_feed->setFetchInterval(fetchInterval()); + m_feed->setArchiveMode(archiveMode()); + m_feed->setMaxArticleAge(maxArticleAge()); + m_feed->setMaxArticleNumber(maxArticleNumber()); + m_feed->setMarkImmediatelyAsRead(markImmediatelyAsRead()); + m_feed->setUseNotification(useNotification()); + m_feed->setLoadLinkedWebsite(loadLinkedWebsite()); + m_feed->setNotificationMode(true, true); + + KDialogBase::slotOk(); +} + +void FeedPropertiesDialog::slotSetCaption(const QString& c) +{ + if(c.isEmpty()) + setCaption(i18n("Feed Properties")); + else + setCaption(i18n("Properties of %1").arg(c)); + +} + +void FeedPropertiesDialog::setFeed(Feed* feed) +{ + m_feed = feed; + if (!feed) + return; + + setFeedName( feed->title() ); + setUrl( feed->xmlUrl() ); + setAutoFetch(feed->useCustomFetchInterval()); + if (feed->useCustomFetchInterval()) + setFetchInterval(feed->fetchInterval()); + else + setFetchInterval(Settings::autoFetchInterval()); + setArchiveMode(feed->archiveMode()); + setMaxArticleAge(feed->maxArticleAge()); + setMaxArticleNumber(feed->maxArticleNumber()); + setMarkImmediatelyAsRead(feed->markImmediatelyAsRead()); + setUseNotification(feed->useNotification()); + setLoadLinkedWebsite(feed->loadLinkedWebsite()); + slotSetCaption(feedName()); +} + + +const QString FeedPropertiesDialog::feedName() const +{ + return widget->feedNameEdit->text(); +} + +const QString FeedPropertiesDialog::url() const +{ + return widget->urlEdit->text(); +} + +bool FeedPropertiesDialog::autoFetch() const +{ + return widget->upChkbox->isChecked(); +} + +int FeedPropertiesDialog::fetchInterval() const +{ + switch (widget->updateComboBox->currentItem() ) + { + case 0: // minutes + return widget->updateSpinBox->value(); + case 1: // hours + return widget->updateSpinBox->value()*60; + case 2: // days + return widget->updateSpinBox->value()*60*24; + default: + return -1; // never + } +} + +Feed::ArchiveMode FeedPropertiesDialog::archiveMode() const +{ + // i could check the button group's int, but order could change... + if ( widget->rb_globalDefault->isChecked() ) + return Feed::globalDefault; + + if ( widget->rb_keepAllArticles->isChecked() ) + return Feed::keepAllArticles; + + if ( widget->rb_limitArticleAge->isChecked() ) + return Feed::limitArticleAge; + + if ( widget->rb_limitArticleNumber->isChecked() ) + return Feed::limitArticleNumber; + + if ( widget->rb_disableArchiving->isChecked() ) + return Feed::disableArchiving; + + // in a perfect world, this is never reached + + return Feed::globalDefault; +} + + +int FeedPropertiesDialog::maxArticleAge() const +{ + return widget->sb_maxArticleAge->value(); +} + +int FeedPropertiesDialog::maxArticleNumber() const +{ + return widget->sb_maxArticleNumber->value(); +} + +void FeedPropertiesDialog::setArchiveMode(Feed::ArchiveMode mode) + { + switch (mode) + { + case Feed::globalDefault: + widget->rb_globalDefault->setChecked(true); + break; + case Feed::keepAllArticles: + widget->rb_keepAllArticles->setChecked(true); + break; + case Feed::disableArchiving: + widget->rb_disableArchiving->setChecked(true); + break; + case Feed::limitArticleAge: + widget->rb_limitArticleAge->setChecked(true); + break; + case Feed::limitArticleNumber: + widget->rb_limitArticleNumber->setChecked(true); + } +} +void FeedPropertiesDialog::setFeedName(const QString& title) +{ + widget->feedNameEdit->setText(title); +} + +void FeedPropertiesDialog::setUrl(const QString& url) +{ + widget->urlEdit->setText(url); +} + +void FeedPropertiesDialog::setAutoFetch(bool customFetchEnabled) +{ + widget->upChkbox->setChecked(customFetchEnabled); + widget->updateComboBox->setEnabled(customFetchEnabled); + + if (widget->updateSpinBox->value() > -1) + widget->updateSpinBox->setEnabled(customFetchEnabled); + else + widget->updateSpinBox->setEnabled(false); +} + +void FeedPropertiesDialog::setFetchInterval(int interval) +{ + if (interval == -1) // never update + { + widget->updateSpinBox->setValue(0); + widget->updateSpinBox->setDisabled(true); + widget->updateComboBox->setCurrentItem(3); // never + return; + } + + if (interval == 0) + { + widget->updateSpinBox->setValue(0); + widget->updateSpinBox->setEnabled(widget->upChkbox->isChecked()); + widget->updateComboBox->setCurrentItem(0); // minutes + return; + } + + if (interval % (60*24) == 0) + { + widget->updateSpinBox->setValue(interval / (60*24) ); + widget->updateSpinBox->setEnabled(widget->upChkbox->isChecked()); + widget->updateComboBox->setCurrentItem(2); // days + return; + } + + if (interval % 60 == 0) + { + widget->updateSpinBox->setValue(interval / 60 ); + widget->updateSpinBox->setEnabled(widget->upChkbox->isChecked()); + widget->updateComboBox->setCurrentItem(1); // hours + return; + } + + widget->updateSpinBox->setValue(interval); + widget->updateSpinBox->setEnabled(widget->upChkbox->isChecked()); + widget->updateComboBox->setCurrentItem(0); // minutes +} + +void FeedPropertiesDialog::setMaxArticleAge(int age) + { + widget->sb_maxArticleAge->setValue(age); +} + +void FeedPropertiesDialog::setMaxArticleNumber(int number) +{ + widget->sb_maxArticleNumber->setValue(number); +} + +void FeedPropertiesDialog::setMarkImmediatelyAsRead(bool enabled) +{ + widget->checkBox_markRead->setChecked(enabled); +} + +bool FeedPropertiesDialog::markImmediatelyAsRead() const +{ + return widget->checkBox_markRead->isChecked(); +} + +void FeedPropertiesDialog::setUseNotification(bool enabled) +{ + widget->checkBox_useNotification->setChecked(enabled); +} + +bool FeedPropertiesDialog::useNotification() const +{ + return widget->checkBox_useNotification->isChecked(); +} + +bool FeedPropertiesDialog::loadLinkedWebsite() const +{ + return widget->checkBox_loadWebsite->isChecked(); +} + +void FeedPropertiesDialog::setLoadLinkedWebsite(bool enabled) +{ + widget->checkBox_loadWebsite->setChecked(enabled); +} + +void FeedPropertiesDialog::selectFeedName() +{ + widget->feedNameEdit->selectAll(); +} + +} // namespace Akregator + +#include "propertiesdialog.moc" +// vim: ts=4 sw=4 et diff --git a/akregator/src/propertiesdialog.h b/akregator/src/propertiesdialog.h new file mode 100644 index 000000000..234df190a --- /dev/null +++ b/akregator/src/propertiesdialog.h @@ -0,0 +1,91 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef AKREGATORPROPDIALOG_H +#define AKREGATORPROPDIALOG_H + +#include "propertieswidgetbase.h" + +#include <kdialogbase.h> + +namespace Akregator +{ + class FeedPropertiesWidget : public FeedPropertiesWidgetBase + { + Q_OBJECT + public: + FeedPropertiesWidget(QWidget *parent = 0, const char *name = 0); + ~FeedPropertiesWidget(); + virtual void slotUpdateComboBoxActivated(int index); + virtual void slotUpdateCheckBoxToggled(bool enabled); + + }; + + class FeedPropertiesDialog : public KDialogBase + { + Q_OBJECT + public: + FeedPropertiesDialog(QWidget *parent = 0, const char *name = 0); + ~FeedPropertiesDialog(); + + void setFeed(Feed* feed); + + /** selects the text in the feed title lineedit */ + void selectFeedName(); + + protected: + const QString feedName() const; + const QString url() const; + bool autoFetch() const; + int fetchInterval() const; + Feed::ArchiveMode archiveMode() const; + int maxArticleAge() const; + int maxArticleNumber() const; + bool markImmediatelyAsRead() const; + bool useNotification() const; + bool loadLinkedWebsite() const; + + void setFeedName(const QString& title); + void setUrl(const QString& url); + void setAutoFetch(bool); + void setFetchInterval(int); + void setArchiveMode(Feed::ArchiveMode mode); + void setMaxArticleAge(int age); + void setMaxArticleNumber(int number); + void setMarkImmediatelyAsRead(bool enabled); + void setUseNotification(bool enabled); + void setLoadLinkedWebsite(bool enabled); + + protected slots: + void slotOk(); + + private: + FeedPropertiesWidget *widget; + Feed* m_feed; + + private slots: + void slotSetCaption(const QString&); + }; +} + +#endif diff --git a/akregator/src/propertieswidgetbase.ui b/akregator/src/propertieswidgetbase.ui new file mode 100644 index 000000000..eb7d39622 --- /dev/null +++ b/akregator/src/propertieswidgetbase.ui @@ -0,0 +1,467 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Akregator::FeedPropertiesWidgetBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>FeedPropertiesWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>355</width> + <height>189</height> + </rect> + </property> + <property name="caption"> + <string>Feed Properties</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabWidget2</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&General</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>&URL:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>urlEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>&Name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>feedNameEdit</cstring> + </property> + </widget> + <widget class="KLineEdit" row="0" column="1"> + <property name="name"> + <cstring>feedNameEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" stdset="0"> + <string>Display name of RSS column</string> + </property> + </widget> + <widget class="KLineEdit" row="1" column="1"> + <property name="name"> + <cstring>urlEdit</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>upChkbox</cstring> + </property> + <property name="text"> + <string>U&se a custom update interval</string> + </property> + </widget> + <widget class="QLayoutWidget" row="2" column="0"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer8</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Update &every:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>updateSpinBox</cstring> + </property> + </widget> + <widget class="KIntSpinBox"> + <property name="name"> + <cstring>updateSpinBox</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="suffix"> + <string></string> + </property> + <property name="maxValue"> + <number>100000000</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>Minutes</string> + </property> + </item> + <item> + <property name="text"> + <string>Hours</string> + </property> + </item> + <item> + <property name="text"> + <string>Days</string> + </property> + </item> + <item> + <property name="text"> + <string>Never</string> + </property> + </item> + <property name="name"> + <cstring>updateComboBox</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox" row="3" column="0"> + <property name="name"> + <cstring>checkBox_useNotification</cstring> + </property> + <property name="text"> + <string>Notify when new articles arri&ve</string> + </property> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Ar&chive</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup" row="0" column="0"> + <property name="name"> + <cstring>bg_feedArchive</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="title"> + <string></string> + </property> + <property name="exclusive"> + <bool>false</bool> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QRadioButton" row="1" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>rb_keepAllArticles</cstring> + </property> + <property name="text"> + <string>&Keep all articles</string> + </property> + </widget> + <widget class="QRadioButton" row="2" column="0"> + <property name="name"> + <cstring>rb_limitArticleNumber</cstring> + </property> + <property name="text"> + <string>Limit archi&ve to:</string> + </property> + </widget> + <widget class="QRadioButton" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>rb_limitArticleAge</cstring> + </property> + <property name="text"> + <string>&Delete articles older than:</string> + </property> + </widget> + <widget class="KIntSpinBox" row="3" column="2"> + <property name="name"> + <cstring>sb_maxArticleAge</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="suffix"> + <string> days</string> + </property> + <property name="specialValueText"> + <string>1 day</string> + </property> + <property name="maxValue"> + <number>1000000</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>1</number> + </property> + </widget> + <spacer row="3" column="3"> + <property name="name"> + <cstring>spacer5_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>99</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="2" column="3"> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>100</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KIntSpinBox" row="2" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>sb_maxArticleNumber</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="suffix"> + <string> articles</string> + </property> + <property name="specialValueText"> + <string>1 article</string> + </property> + <property name="maxValue"> + <number>1000000</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + </widget> + <widget class="QRadioButton" row="4" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>rb_disableArchiving</cstring> + </property> + <property name="text"> + <string>Di&sable archiving</string> + </property> + </widget> + <widget class="QRadioButton" row="0" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>rb_globalDefault</cstring> + </property> + <property name="text"> + <string>&Use default settings</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>Adva&nced</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>checkBox_loadWebsite</cstring> + </property> + <property name="text"> + <string>Load the &full website when reading articles</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>checkBox_markRead</cstring> + </property> + <property name="text"> + <string>Mar&k articles as read when they arrive</string> + </property> + </widget> + </vbox> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>71</height> + </size> + </property> + </spacer> + </grid> + </widget> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>upChkbox</sender> + <signal>toggled(bool)</signal> + <receiver>updateSpinBox</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>rb_limitArticleNumber</sender> + <signal>toggled(bool)</signal> + <receiver>sb_maxArticleNumber</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>rb_limitArticleAge</sender> + <signal>toggled(bool)</signal> + <receiver>sb_maxArticleAge</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>upChkbox</sender> + <signal>toggled(bool)</signal> + <receiver>updateComboBox</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>updateComboBox</sender> + <signal>activated(int)</signal> + <receiver>FeedPropertiesWidget</receiver> + <slot>slotUpdateComboBoxActivated(int)</slot> + </connection> + <connection> + <sender>upChkbox</sender> + <signal>toggled(bool)</signal> + <receiver>FeedPropertiesWidget</receiver> + <slot>slotUpdateCheckBoxToggled(bool)</slot> + </connection> + <connection> + <sender>upChkbox</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<slots> + <slot access="protected" specifier="pure virtual">slotUpdateComboBoxActivated( int )</slot> + <slot access="protected" specifier="pure virtual">slotUpdateCheckBoxToggled( bool )</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klineedit.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/akregator/src/searchbar.cpp b/akregator/src/searchbar.cpp new file mode 100644 index 000000000..d565c2847 --- /dev/null +++ b/akregator/src/searchbar.cpp @@ -0,0 +1,229 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "akregatorconfig.h" +#include "articlefilter.h" +#include "article.h" +#include "searchbar.h" + +#include <kcombobox.h> +#include <kiconloader.h> +#include <klineedit.h> +#include <klocale.h> +#include <kstandarddirs.h> + +#include <qapplication.h> +#include <qhbox.h> +#include <qlabel.h> +#include <qpixmap.h> +#include <qstring.h> +#include <qtimer.h> +#include <qtoolbutton.h> +#include <qtooltip.h> + +using Akregator::Filters::ArticleMatcher; +using Akregator::Filters::Criterion; + +namespace Akregator +{ + +class SearchBar::SearchBarPrivate +{ +public: + Akregator::Filters::ArticleMatcher textFilter; + Akregator::Filters::ArticleMatcher statusFilter; + QString searchText; + QTimer timer; + KLineEdit* searchLine; + KComboBox* searchCombo; + int delay; +}; + +SearchBar::SearchBar(QWidget* parent, const char* name) : QHBox(parent, name), d(new SearchBar::SearchBarPrivate) +{ + d->delay = 400; + setMargin(2); + setSpacing(5); + setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) ); + QToolButton *clearButton = new QToolButton(this); + clearButton->setIconSet( SmallIconSet( QApplication::reverseLayout() ? "clear_left" : "locationbar_erase" ) ); + + clearButton->setAutoRaise(true); + + QLabel* searchLabel = new QLabel(this); + searchLabel->setText( i18n("S&earch:") ); + + d->searchLine = new KLineEdit(this, "searchline"); + connect(d->searchLine, SIGNAL(textChanged(const QString &)), + this, SLOT(slotSearchStringChanged(const QString &))); + + searchLabel->setBuddy(d->searchLine); + + QLabel* statusLabel = new QLabel(this); + statusLabel->setText( i18n("Status:") ); + + d->searchCombo = new KComboBox(this, "searchcombo"); + QPixmap iconAll = KGlobal::iconLoader()->loadIcon("exec", KIcon::Small); + QPixmap iconNew(locate("data", "akregator/pics/kmmsgnew.png")); + QPixmap iconUnread(locate("data", "akregator/pics/kmmsgunseen.png")); + QPixmap iconKeep(locate("data", "akregator/pics/kmmsgflag.png")); + + d->searchCombo->insertItem(iconAll, i18n("All Articles")); + d->searchCombo->insertItem(iconUnread, i18n("Unread")); + d->searchCombo->insertItem(iconNew, i18n("New")); + d->searchCombo->insertItem(iconKeep, i18n("Important")); + + QToolTip::add( clearButton, i18n( "Clear filter" ) ); + QToolTip::add( d->searchLine, i18n( "Enter space-separated terms to filter article list" ) ); + QToolTip::add( d->searchCombo, i18n( "Choose what kind of articles to show in article list" ) ); + + connect(clearButton, SIGNAL( clicked() ), + this, SLOT(slotClearSearch()) ); + + connect(d->searchCombo, SIGNAL(activated(int)), + this, SLOT(slotSearchComboChanged(int))); + + connect(&(d->timer), SIGNAL(timeout()), this, SLOT(slotActivateSearch())); +} + +SearchBar::~SearchBar() +{ + delete d; + d = 0; +} + +QString SearchBar::text() const +{ + return d->searchText; +} + +int SearchBar::status() const +{ + return d->searchCombo->currentItem(); +} + +void SearchBar::setDelay(int ms) +{ + d->delay = ms; +} + +int SearchBar::delay() const +{ + return d->delay; +} + +void SearchBar::slotClearSearch() +{ + if (status() != 0 || !d->searchLine->text().isEmpty()) + { + d->searchLine->clear(); + d->searchCombo->setCurrentItem(0); + d->timer.stop(); + slotActivateSearch(); + } +} + +void SearchBar::slotSetStatus(int status) +{ + d->searchCombo->setCurrentItem(status); + slotSearchComboChanged(status); +} + +void SearchBar::slotSetText(const QString& text) +{ + d->searchLine->setText(text); + slotSearchStringChanged(text); +} + +void SearchBar::slotSearchComboChanged(int /*index*/) +{ + if (d->timer.isActive()) + d->timer.stop(); + + d->timer.start(200, true); +} + +void SearchBar::slotSearchStringChanged(const QString& search) +{ + d->searchText = search; + if (d->timer.isActive()) + d->timer.stop(); + + d->timer.start(200, true); +} + +void SearchBar::slotActivateSearch() +{ + QValueList<Criterion> textCriteria; + QValueList<Criterion> statusCriteria; + + if (!d->searchText.isEmpty()) + { + Criterion subjCrit( Criterion::Title, Criterion::Contains, d->searchText); + textCriteria << subjCrit; + Criterion crit1( Criterion::Description, Criterion::Contains, d->searchText); + textCriteria << crit1; + Criterion crit2( Criterion::Author, Criterion::Contains, d->searchText); + textCriteria << crit2; + } + + if (d->searchCombo->currentItem()) + { + switch (d->searchCombo->currentItem()) + { + case 1: // Unread + { + Criterion crit1( Criterion::Status, Criterion::Equals, Article::New); + Criterion crit2( Criterion::Status, Criterion::Equals, Article::Unread); + statusCriteria << crit1; + statusCriteria << crit2; + break; + } + case 2: // New + { + Criterion crit( Criterion::Status, Criterion::Equals, Article::New); + statusCriteria << crit; + break; + } + case 3: // Keep flag set + { + Criterion crit( Criterion::KeepFlag, Criterion::Equals, true); + statusCriteria << crit; + break; + } + default: + break; + } + } + + d->textFilter = ArticleMatcher(textCriteria, ArticleMatcher::LogicalOr); + d->statusFilter = ArticleMatcher(statusCriteria, ArticleMatcher::LogicalOr); + Settings::setStatusFilter(d->searchCombo->currentItem()); + Settings::setTextFilter(d->searchText); + emit signalSearch(d->textFilter, d->statusFilter); +} + +} + +#include "searchbar.moc" diff --git a/akregator/src/searchbar.h b/akregator/src/searchbar.h new file mode 100644 index 000000000..f9cfb879c --- /dev/null +++ b/akregator/src/searchbar.h @@ -0,0 +1,78 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_SEARCHBAR_H +#define AKREGATOR_SEARCHBAR_H + +#include <qhbox.h> + +class QString; + +namespace Akregator +{ + +namespace Filters +{ + class ArticleMatcher; +} + +class SearchBar : public QHBox +{ + Q_OBJECT + + public: + + SearchBar(QWidget* parent=0, const char* name=0); + virtual ~SearchBar(); + + QString text() const; + int status() const; + + void setDelay(int ms); + int delay() const; + + signals: + /** emitted when the text and status filters were updated. Params are textfilter, statusfilter */ + void signalSearch(const Akregator::Filters::ArticleMatcher&, const Akregator::Filters::ArticleMatcher&); + + public slots: + void slotClearSearch(); + void slotSetStatus(int status); + void slotSetText(const QString& text); + + private slots: + + void slotSearchStringChanged(const QString& search); + void slotSearchComboChanged(int index); + void slotActivateSearch(); + + private: + + class SearchBarPrivate; + SearchBarPrivate* d; +}; + +} + +#endif //AKREGATOR_SEARCHBAR_H diff --git a/akregator/src/settings_advanced.cpp b/akregator/src/settings_advanced.cpp new file mode 100644 index 000000000..5d1254835 --- /dev/null +++ b/akregator/src/settings_advanced.cpp @@ -0,0 +1,54 @@ +#include "akregatorconfig.h" +#include "settings_advanced.h" +#include "storagefactory.h" +#include "storagefactoryregistry.h" + +#include <qpushbutton.h> +#include <qstringlist.h> +#include <qwidget.h> + +#include <kcombobox.h> + +namespace Akregator { + +SettingsAdvanced::SettingsAdvanced(QWidget* parent, const char* name) : SettingsAdvancedBase(parent, name) +{ + + QStringList backends = Backend::StorageFactoryRegistry::self()->list(); + QString tname; + int i = 0; + QStringList::Iterator end( backends.end() ); + for (QStringList::Iterator it = backends.begin(); it != end; ++it) + { + m_factories[i] = Backend::StorageFactoryRegistry::self()->getFactory(*it); + m_keyPos[m_factories[i]->key()] = i; + cbBackend->insertItem(m_factories[i]->name()); + i++; + } + connect(pbBackendConfigure, SIGNAL(clicked()), this, SLOT(slotConfigureStorage())); + connect(cbBackend, SIGNAL(activated(int)), this, SLOT(slotFactorySelected(int))); +} + +QString SettingsAdvanced::selectedFactory() const +{ + return m_factories[cbBackend->currentItem()]->key(); +} + +void SettingsAdvanced::selectFactory(const QString& key) +{ + cbBackend->setCurrentItem(m_keyPos[key]); + pbBackendConfigure->setEnabled((m_factories[m_keyPos[key]]->isConfigurable())); +} + +void SettingsAdvanced::slotConfigureStorage() +{ + m_factories[cbBackend->currentItem()]->configure(); +} + +void SettingsAdvanced::slotFactorySelected(int pos) +{ + pbBackendConfigure->setEnabled(m_factories[pos]->isConfigurable()); +} + +} //namespace Akregator +#include "settings_advanced.moc" diff --git a/akregator/src/settings_advanced.h b/akregator/src/settings_advanced.h new file mode 100644 index 000000000..c741f3f77 --- /dev/null +++ b/akregator/src/settings_advanced.h @@ -0,0 +1,42 @@ +#ifndef AKREGATOR_SETTINGS_ADVANCED_H +#define AKREGATOR_SETTINGS_ADVANCED_H + +#include "settings_advancedbase.h" + +#include <qmap.h> + +class QString; +class QWidget; + +namespace Akregator { + +namespace Backend +{ + class StorageFactory; +} + +class SettingsAdvanced : public SettingsAdvancedBase +{ + Q_OBJECT + + public: + SettingsAdvanced(QWidget* parent=0, const char* name=0); + + /** returns the key of the currently selected factory */ + QString selectedFactory() const; + + void selectFactory(const QString& key); + + public slots: + + void slotConfigureStorage(); + void slotFactorySelected(int); + + private: + QMap<int,Backend::StorageFactory*> m_factories; + QMap<QString, int> m_keyPos; +}; + +} // namespace Akregator + +#endif //AKREGATOR_SETTINGS_ADVANCED_H diff --git a/akregator/src/settings_advancedbase.ui b/akregator/src/settings_advancedbase.ui new file mode 100644 index 000000000..0fc992959 --- /dev/null +++ b/akregator/src/settings_advancedbase.ui @@ -0,0 +1,157 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Akregator::SettingsAdvancedBase</class> +<author>Frank Osterfeld</author> +<widget class="QWidget"> + <property name="name"> + <cstring>SettingsAdvanced</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>476</width> + <height>486</height> + </rect> + </property> + <property name="caption"> + <string>SettingsAdvanced</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>groupBox3_3_2</cstring> + </property> + <property name="title"> + <string>Archive</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Archive backend:</string> + </property> + </widget> + <widget class="KComboBox"> + <property name="name"> + <cstring>cbBackend</cstring> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>pbBackendConfigure</cstring> + </property> + <property name="text"> + <string>&Configure...</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QGroupBox" row="1" column="0"> + <property name="name"> + <cstring>groupBox3_3</cstring> + </property> + <property name="title"> + <string>Article List</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QSpinBox" row="0" column="1"> + <property name="name"> + <cstring>kcfg_MarkReadDelay</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="suffix"> + <string> sec</string> + </property> + <property name="value"> + <number>0</number> + </property> + </widget> + <spacer row="0" column="2"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>174</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_ResetQuickFilterOnNodeChange</cstring> + </property> + <property name="text"> + <string>Reset search bar when changing feeds</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="tristate"> + <bool>false</bool> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>kcfg_UseMarkReadDelay</cstring> + </property> + <property name="text"> + <string>Mar&k selected article read after</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>260</height> + </size> + </property> + </spacer> + </grid> +</widget> +<connections> + <connection> + <sender>kcfg_UseMarkReadDelay</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_MarkReadDelay</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> +</includehints> +</UI> diff --git a/akregator/src/settings_appearance.ui b/akregator/src/settings_appearance.ui new file mode 100644 index 000000000..48985a878 --- /dev/null +++ b/akregator/src/settings_appearance.ui @@ -0,0 +1,250 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Akregator::SettingsAppearance</class> +<author>Frank Osterfeld</author> +<widget class="QWidget"> + <property name="name"> + <cstring>SettingsAppearance</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>418</width> + <height>377</height> + </rect> + </property> + <property name="caption"> + <string>General</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Font Size</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>lbl_MinimumFontSize</cstring> + </property> + <property name="text"> + <string>Minimum font size:</string> + </property> + </widget> + <widget class="QSlider" row="1" column="0"> + <property name="name"> + <cstring>slider_minimumFontSize</cstring> + </property> + <property name="minValue"> + <number>2</number> + </property> + <property name="maxValue"> + <number>30</number> + </property> + <property name="value"> + <number>8</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Below</enum> + </property> + <property name="tickInterval"> + <number>3</number> + </property> + </widget> + <widget class="KIntSpinBox" row="1" column="1"> + <property name="name"> + <cstring>kcfg_MinimumFontSize</cstring> + </property> + <property name="value"> + <number>8</number> + </property> + </widget> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>lbl_MediumFontSize</cstring> + </property> + <property name="text"> + <string>Medium font size:</string> + </property> + </widget> + <widget class="QSlider" row="3" column="0"> + <property name="name"> + <cstring>slider_mediumFontSize</cstring> + </property> + <property name="minValue"> + <number>2</number> + </property> + <property name="maxValue"> + <number>30</number> + </property> + <property name="value"> + <number>12</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Below</enum> + </property> + <property name="tickInterval"> + <number>3</number> + </property> + </widget> + <widget class="KIntSpinBox" row="3" column="1"> + <property name="name"> + <cstring>kcfg_MediumFontSize</cstring> + </property> + <property name="value"> + <number>12</number> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>FontsGroupBox</cstring> + </property> + <property name="title"> + <string>Fonts</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Standard font:</string> + </property> + </widget> + <widget class="KFontCombo" row="0" column="1"> + <property name="name"> + <cstring>kcfg_StandardFont</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>Fixed font:</string> + </property> + </widget> + <widget class="KFontCombo" row="1" column="1"> + <property name="name"> + <cstring>kcfg_FixedFont</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Serif font:</string> + </property> + </widget> + <widget class="KFontCombo" row="2" column="1"> + <property name="name"> + <cstring>kcfg_SerifFont</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Sans serif font:</string> + </property> + </widget> + <widget class="KFontCombo" row="3" column="1"> + <property name="name"> + <cstring>kcfg_SansSerifFont</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_UnderlineLinks</cstring> + </property> + <property name="text"> + <string>&Underline links</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>41</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>kcfg_MediumFontSize</sender> + <signal>valueChanged(int)</signal> + <receiver>slider_mediumFontSize</receiver> + <slot>setValue(int)</slot> + </connection> + <connection> + <sender>slider_mediumFontSize</sender> + <signal>valueChanged(int)</signal> + <receiver>kcfg_MediumFontSize</receiver> + <slot>setValue(int)</slot> + </connection> + <connection> + <sender>kcfg_MinimumFontSize</sender> + <signal>valueChanged(int)</signal> + <receiver>slider_minimumFontSize</receiver> + <slot>setValue(int)</slot> + </connection> + <connection> + <sender>slider_minimumFontSize</sender> + <signal>valueChanged(int)</signal> + <receiver>kcfg_MinimumFontSize</receiver> + <slot>setValue(int)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>kfontcombo.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kfontcombo.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kfontcombo.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kfontcombo.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/akregator/src/settings_archive.ui b/akregator/src/settings_archive.ui new file mode 100644 index 000000000..4fcfc4e5c --- /dev/null +++ b/akregator/src/settings_archive.ui @@ -0,0 +1,176 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Akregator::SettingsArchive</class> +<author>Frank Osterfeld</author> +<widget class="QWidget"> + <property name="name"> + <cstring>SettingsArchive</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>374</width> + <height>203</height> + </rect> + </property> + <property name="caption"> + <string>Archive</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>kcfg_ArchiveMode</cstring> + </property> + <property name="title"> + <string>Default Archive Settings</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>rb_KeepAllArticles</cstring> + </property> + <property name="text"> + <string>Keep all articles</string> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>rb_LimitArticleNumber</cstring> + </property> + <property name="text"> + <string>Limit feed archive size to:</string> + </property> + </widget> + <widget class="QRadioButton" row="2" column="0"> + <property name="name"> + <cstring>rb_LimitArticleAge</cstring> + </property> + <property name="text"> + <string>Delete articles older than: </string> + </property> + </widget> + <widget class="QRadioButton" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>rb_DisableArchiving</cstring> + </property> + <property name="text"> + <string>Disable archiving</string> + </property> + </widget> + <widget class="KIntSpinBox" row="1" column="1"> + <property name="name"> + <cstring>kcfg_MaxArticleNumber</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="suffix"> + <string> articles</string> + </property> + <property name="specialValueText"> + <string>1 article</string> + </property> + <property name="maxValue"> + <number>1000000</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="lineStep"> + <number>10</number> + </property> + <property name="value"> + <number>1000</number> + </property> + </widget> + <widget class="KIntSpinBox" row="2" column="1"> + <property name="name"> + <cstring>kcfg_MaxArticleAge</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="suffix"> + <string> days</string> + </property> + <property name="specialValueText"> + <string>1 day</string> + </property> + <property name="maxValue"> + <number>1000000</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>30</number> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_DoNotExpireImportantArticles</cstring> + </property> + <property name="text"> + <string>Do not expire important articles</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>30</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>rb_LimitArticleAge</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_MaxArticleAge</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>rb_LimitArticleNumber</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_MaxArticleNumber</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/akregator/src/settings_browser.ui b/akregator/src/settings_browser.ui new file mode 100644 index 000000000..52729a169 --- /dev/null +++ b/akregator/src/settings_browser.ui @@ -0,0 +1,206 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Akregator::SettingsBrowser</class> +<author>Gary Cramblitt</author> +<widget class="QWidget"> + <property name="name"> + <cstring>SettingsBrowser</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>340</width> + <height>299</height> + </rect> + </property> + <property name="caption"> + <string>ExternalBrowser</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QButtonGroup" row="1" column="0"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="title"> + <string>For External Browsing</string> + </property> + <property name="exclusive"> + <bool>false</bool> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_ExternalBrowserUseKdeDefault</cstring> + </property> + <property name="text"> + <string>Use default KDE web browser</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>kcfg_ExternalBrowserUseCustomCommand</cstring> + </property> + <property name="text"> + <string>Use this command:</string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>kcfg_ExternalBrowserCustomCommand</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>firefox %u</string> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox" row="2" column="0"> + <property name="name"> + <cstring>kcfg_CloseButtonOnTabs</cstring> + </property> + <property name="text"> + <string>Show tab close button on hover</string> + </property> + </widget> + <spacer row="3" column="0"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>Open in Tab</string> + </property> + </item> + <item> + <property name="text"> + <string>Open in Background Tab</string> + </property> + </item> + <item> + <property name="text"> + <string>Open in External Browser</string> + </property> + </item> + <property name="name"> + <cstring>kcfg_LMBBehaviour</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Middle mouse click:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Left mouse click:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Open in Tab</string> + </property> + </item> + <item> + <property name="text"> + <string>Open in Background Tab</string> + </property> + </item> + <item> + <property name="text"> + <string>Open in External Browser</string> + </property> + </item> + <property name="name"> + <cstring>kcfg_MMBBehaviour</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </grid> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>kcfg_ExternalBrowserUseCustomCommand</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_ExternalBrowserCustomCommand</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/akregator/src/settings_general.ui b/akregator/src/settings_general.ui new file mode 100644 index 000000000..dc0e559da --- /dev/null +++ b/akregator/src/settings_general.ui @@ -0,0 +1,187 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Akregator::SettingsGeneral</class> +<author>Teemu Rytilahti</author> +<widget class="QWidget"> + <property name="name"> + <cstring>SettingsGeneral</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>272</width> + <height>382</height> + </rect> + </property> + <property name="caption"> + <string>General</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>groupBox3_2</cstring> + </property> + <property name="title"> + <string>Global</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_UseIntervalFetch</cstring> + </property> + <property name="text"> + <string>&Use interval fetching</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_UseNotifications</cstring> + </property> + <property name="text"> + <string>Use &notifications for all feeds</string> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Select this if you want to get notified when there are new articles.</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_ShowTrayIcon</cstring> + </property> + <property name="text"> + <string>Show tra&y icon</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Fetch feeds every:</string> + </property> + </widget> + <widget class="QSpinBox" row="3" column="1"> + <property name="name"> + <cstring>kcfg_AutoFetchInterval</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="suffix"> + <string> minutes</string> + </property> + <property name="specialValueText"> + <string>1 minute</string> + </property> + <property name="maxValue"> + <number>3600</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="lineStep"> + <number>10</number> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="1" column="0"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>Startup</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>kcfg_MarkAllFeedsReadOnStartup</cstring> + </property> + <property name="text"> + <string>Mark &all feeds as read on startup</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>kcfg_FetchOnStartup</cstring> + </property> + <property name="text"> + <string>Fetch all fee&ds on startup</string> + </property> + </widget> + </grid> + </widget> + <spacer row="3" column="0"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QGroupBox" row="2" column="0"> + <property name="name"> + <cstring>groupBox3_3</cstring> + </property> + <property name="title"> + <string>Network</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>kcfg_UseHTMLCache</cstring> + </property> + <property name="text"> + <string>Use the &browser cache (less network traffic)</string> + </property> + </widget> + </grid> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>kcfg_UseIntervalFetch</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_AutoFetchInterval</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_UseIntervalFetch</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/akregator/src/shared.h b/akregator/src/shared.h new file mode 100644 index 000000000..e295c5626 --- /dev/null +++ b/akregator/src/shared.h @@ -0,0 +1,27 @@ +/* + * tools_p.h + * + * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org> + * + * 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. For licensing and distribution details, check the + * accompanying file 'COPYING'. + */ +#ifndef AKREGATOR_SHARED_H +#define AKREGATOR_SHARED_H + +namespace Akregator +{ + struct Shared + { + Shared() : count(1) { } + void ref() { count++; } + bool deref() { return !--count; } + unsigned int count; + }; + +} + +#endif // AKREGATOR_SHARED_H + diff --git a/akregator/src/simplenodeselector.cpp b/akregator/src/simplenodeselector.cpp new file mode 100644 index 000000000..ea6f7974e --- /dev/null +++ b/akregator/src/simplenodeselector.cpp @@ -0,0 +1,200 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "feedlist.h" +#include "folder.h" +#include "simplenodeselector.h" +#include "treenode.h" +#include "treenodevisitor.h" + +#include <klistview.h> +#include <klocale.h> + +#include <qlayout.h> +#include <qmap.h> +#include <qwidget.h> + +namespace Akregator +{ + +class SelectNodeDialog::SelectNodeDialogPrivate +{ + public: + SimpleNodeSelector* widget; +}; + +SelectNodeDialog::SelectNodeDialog(FeedList* feedList, QWidget* parent, char* name) : + KDialogBase(parent, name, true, i18n("Select Feed or Folder"), + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true), d(new SelectNodeDialogPrivate) +{ + d->widget = new SimpleNodeSelector(feedList, this); + + connect(d->widget, SIGNAL(signalNodeSelected(TreeNode*)), this, SLOT(slotNodeSelected(TreeNode*))); + + setMainWidget(d->widget); + enableButtonOK(false); +} + +SelectNodeDialog::~SelectNodeDialog() +{ + delete d; + d = 0; +} + +TreeNode* SelectNodeDialog::selectedNode() const +{ + return d->widget->selectedNode(); +} + +void SelectNodeDialog::slotSelectNode(TreeNode* node) +{ + d->widget->slotSelectNode(node); +} + +void SelectNodeDialog::slotNodeSelected(TreeNode* node) +{ + enableButtonOK(node != 0); +} + + +class SimpleNodeSelector::SimpleNodeSelectorPrivate +{ + public: + KListView* view; + FeedList* list; + NodeVisitor* visitor; + QMap<TreeNode*,QListViewItem*> nodeToItem; + QMap<QListViewItem*, TreeNode*> itemToNode; +}; + +class SimpleNodeSelector::NodeVisitor : public TreeNodeVisitor +{ + public: + + NodeVisitor(SimpleNodeSelector* view) : TreeNodeVisitor(), m_view(view) {} + + void createItems(TreeNode* node) + { + node->accept(this); + } + + virtual bool visitFolder(Folder* node) + { + visitTreeNode(node); + QValueList<TreeNode*> children = node->children(); + m_view->d->nodeToItem[node]->setExpandable(true); + for (QValueList<TreeNode*>::ConstIterator it = children.begin(); it != children.end(); ++it) + createItems(*it); + return true; + } + + virtual bool visitTreeNode(TreeNode* node) + { + QListViewItem* pi = node->parent() ? m_view->d->nodeToItem[node->parent()] : 0; + + KListViewItem* item = 0; + if (pi != 0) + item = new KListViewItem(pi, node->title()); + else + item = new KListViewItem(m_view->d->view, node->title()); + item->setExpandable(false); + m_view->d->nodeToItem.insert(node, item); + m_view->d->itemToNode.insert(item, node); + connect(node, SIGNAL(signalDestroyed(TreeNode*)), m_view, SLOT(slotNodeDestroyed(TreeNode*))); + return true; + } + + private: + + SimpleNodeSelector* m_view; +}; + + +SimpleNodeSelector::SimpleNodeSelector(FeedList* feedList, QWidget* parent, const char* name) : QWidget(parent, name), d(new SimpleNodeSelectorPrivate) +{ + d->list = feedList; + connect(feedList, SIGNAL(signalDestroyed(FeedList*)), this, SLOT(slotFeedListDestroyed(FeedList*))); + + d->view = new KListView(this); + d->view->setRootIsDecorated(true); + d->view->addColumn(i18n("Feeds")); + + connect(d->view, SIGNAL(selectionChanged(QListViewItem*)), this, SLOT(slotItemSelected(QListViewItem*))); + + QGridLayout* layout = new QGridLayout(this, 1, 1); + layout->addWidget(d->view, 0, 0); + + d->visitor = new NodeVisitor(this); + + d->visitor->createItems(d->list->rootNode()); + d->nodeToItem[d->list->rootNode()]->setOpen(true); + d->view->ensureItemVisible(d->nodeToItem[d->list->rootNode()]); +} + +SimpleNodeSelector::~SimpleNodeSelector() +{ + delete d->visitor; + delete d; + d = 0; +} + +TreeNode* SimpleNodeSelector::selectedNode() const +{ + return d->itemToNode[d->view->selectedItem()]; +} + +void SimpleNodeSelector::slotSelectNode(TreeNode* node) +{ + QListViewItem* item = d->nodeToItem[node]; + if (item != 0) + d->view->setSelected(item, true); +} + +void SimpleNodeSelector::slotFeedListDestroyed(FeedList* /*list*/) +{ + d->nodeToItem.clear(); + d->itemToNode.clear(); + d->view->clear(); +} + +void SimpleNodeSelector::slotItemSelected(QListViewItem* item) +{ + emit signalNodeSelected(d->itemToNode[item]); +} + +void SimpleNodeSelector::slotNodeDestroyed(TreeNode* node) +{ + if (d->nodeToItem.contains(node)) + { + QListViewItem* item = d->nodeToItem[node]; + d->nodeToItem.remove(node); + d->itemToNode.remove(item); + delete item; + } +} + +} // namespace Akregator + +#include "simplenodeselector.moc" + diff --git a/akregator/src/simplenodeselector.h b/akregator/src/simplenodeselector.h new file mode 100644 index 000000000..244672abe --- /dev/null +++ b/akregator/src/simplenodeselector.h @@ -0,0 +1,102 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_SIMPLENODESELECTOR_H +#define AKREGATOR_SIMPLENODESELECTOR_H + +#include <qwidget.h> + +#include <kdialogbase.h> + +class QListViewItem; + +namespace Akregator { + +class FeedList; +class TreeNode; + +class SimpleNodeSelector; + +/** \brief A dialog with a simple listview displaying a feed list for selection purposes + Use this dialog if you want the user to select a node from the feed list where FeedListView is inappropriate (e.g. in a filter dialog) */ + +class SelectNodeDialog : public KDialogBase +{ + Q_OBJECT + public: + SelectNodeDialog(FeedList* feedList, QWidget* parent=0, char* name=0); + virtual ~SelectNodeDialog(); + + TreeNode* selectedNode() const; + + public slots: + + virtual void slotSelectNode(TreeNode* node); + + protected slots: + + virtual void slotNodeSelected(TreeNode* node); + + private: + class SelectNodeDialogPrivate; + SelectNodeDialogPrivate* d; +}; + +class SimpleNodeSelector : public QWidget +{ + Q_OBJECT + + public: + SimpleNodeSelector(FeedList* feedList, QWidget* parent=0, const char* name=0); + virtual ~SimpleNodeSelector(); + + TreeNode* selectedNode() const; + + public slots: + void slotSelectNode(TreeNode* node); + + signals: + void signalNodeSelected(TreeNode* node); + + protected slots: + + virtual void slotItemSelected(QListViewItem* item); + + virtual void slotNodeDestroyed(TreeNode* node); + virtual void slotFeedListDestroyed(FeedList* list); + + public: // compat with KDE-3.x assertions, remove for KDE 4 +// private: + + class SimpleNodeSelectorPrivate; + SimpleNodeSelectorPrivate* d; + + friend class NodeVisitor; + class NodeVisitor; +}; + + +} // namespace Akregator + +#endif diff --git a/akregator/src/speechclient.cpp b/akregator/src/speechclient.cpp new file mode 100644 index 000000000..ffa42119c --- /dev/null +++ b/akregator/src/speechclient.cpp @@ -0,0 +1,203 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "article.h" +#include "speechclient.h" +#include "utils.h" + +#include <dcopclient.h> +#include <kapplication.h> +#include <kcharsets.h> +#include <klocale.h> +#include <kdebug.h> +#include <kstaticdeleter.h> +#include <ktrader.h> + +#include <qstring.h> +#include <qvaluelist.h> + +namespace Akregator +{ + +class SpeechClient::SpeechClientPrivate +{ + public: + + bool isTextSpeechInstalled; + QValueList<uint> pendingJobs; +}; + +SpeechClient* SpeechClient::m_self = 0; + +static KStaticDeleter<SpeechClient> speechclsd; + +SpeechClient* SpeechClient::self() +{ + if (!m_self) + m_self = speechclsd.setObject(m_self, new SpeechClient); + return m_self; +} + + +SpeechClient::SpeechClient() : DCOPStub("kttsd", "KSpeech"), DCOPObject("akregatorpart_kspeechsink"), QObject(), d(new SpeechClientPrivate) +{ + d->isTextSpeechInstalled = false; + setupSpeechSystem(); +} + +SpeechClient::~SpeechClient() +{ + delete d; + d = 0; +} + +void SpeechClient::slotSpeak(const QString& text, const QString& language) +{ + if (!isTextToSpeechInstalled() || text.isEmpty()) + return; + uint jobNum = setText(text, language); + startText(jobNum); + d->pendingJobs.append(jobNum); + if (d->pendingJobs.count() == 1) + { + emit signalJobsStarted(); + emit signalActivated(true); + } +} + +void SpeechClient::slotSpeak(const Article& article) +{ + if (!isTextToSpeechInstalled() || article.isNull()) + return; + + QString speakMe; + speakMe += KCharsets::resolveEntities(Utils::stripTags((article).title())) + + ". . . . " + + KCharsets::resolveEntities(Utils::stripTags((article).description())); + slotSpeak(speakMe, "en"); +} + +void SpeechClient::slotSpeak(const QValueList<Article>& articles) +{ + if (!isTextToSpeechInstalled() || articles.isEmpty()) + return; + + QString speakMe; + + for (QValueList<Article>::ConstIterator it = articles.begin(); it != articles.end(); ++it) + { + if (!speakMe.isEmpty()) + speakMe += ". . . . . . " + i18n("Next Article: "); + speakMe += KCharsets::resolveEntities(Utils::stripTags((*it).title())) + + ". . . . " + + KCharsets::resolveEntities(Utils::stripTags((*it).description())); + } + + SpeechClient::self()->slotSpeak(speakMe, "en"); +} + +void SpeechClient::slotAbortJobs() +{ + if (!d->pendingJobs.isEmpty()) + { + for (QValueList<uint>::ConstIterator it = d->pendingJobs.begin(); it != d->pendingJobs.end(); ++it) + { + removeText(*it); + } + + d->pendingJobs.clear(); + emit signalJobsDone(); + emit signalActivated(false); + } +} + +ASYNC SpeechClient::textRemoved(const QCString& /*appId*/, uint jobNum) +{ + kdDebug() << "SpeechClient::textRemoved() called" << endl; + if (d->pendingJobs.contains(jobNum)) + { + d->pendingJobs.remove(jobNum); + if (d->pendingJobs.isEmpty()) + { + emit signalJobsDone(); + emit signalActivated(false); + } + } +} + +bool SpeechClient::isTextToSpeechInstalled() const +{ + return d->isTextSpeechInstalled; +} + +void SpeechClient::setupSpeechSystem() +{ + KTrader::OfferList offers = KTrader::self()->query("DCOP/Text-to-Speech", "Name == 'KTTSD'"); + if (offers.count() == 0) + { + kdDebug() << "KTTSD not installed, disable support" << endl; + d->isTextSpeechInstalled = false; + } + else + { + DCOPClient* client = dcopClient(); + //client->attach(); + if (client->isApplicationRegistered("kttsd")) + { + d->isTextSpeechInstalled = true; + } + else + { + QString error; + if (KApplication::startServiceByDesktopName("kttsd", QStringList(), &error)) + { + kdDebug() << "Starting KTTSD failed with message " << error << endl; + d->isTextSpeechInstalled = false; + } + else + { + d->isTextSpeechInstalled = true; + } + } + } + if (d->isTextSpeechInstalled) + { + + bool c = connectDCOPSignal("kttsd", "KSpeech", + "textRemoved(QCString, uint)", + "textRemoved(QCString, uint)", + false); + if (!c) + kdDebug() << "SpeechClient::setupSpeechSystem(): connecting signals failed" << endl; + c = connectDCOPSignal("kttsd", "KSpeech", + "textFinished(QCString, uint)", + "textRemoved(QCString, uint)", + false); + } +} + + +} // namespace Akregator + +#include "speechclient.moc" diff --git a/akregator/src/speechclient.h b/akregator/src/speechclient.h new file mode 100644 index 000000000..03cf681a1 --- /dev/null +++ b/akregator/src/speechclient.h @@ -0,0 +1,86 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_SPEECHCLIENT_H +#define AKREGATOR_SPEECHCLIENT_H + +#include <kspeechsink.h> +#include "kspeech_stub.h" + +#include <qobject.h> + +class QString; +template <class T> class QValueList; + +namespace Akregator +{ + +class Article; + +class SpeechClient : public QObject, public KSpeech_stub, virtual public KSpeechSink +{ + + Q_OBJECT + + public: + + static SpeechClient* self(); + bool isTextToSpeechInstalled() const; + virtual ~SpeechClient(); + + public slots: + + void slotSpeak(const QString& text, const QString& language); + void slotSpeak(const Article& article); + void slotSpeak(const QValueList<Article>& articles); + void slotAbortJobs(); + + signals: + + /** emitted when the job queue was empty before and the first job was just queued */ + void signalJobsStarted(); + + /** emitted when all jobs were finished or aborted and no further jobs are queued */ + void signalJobsDone(); + + void signalActivated(bool); + + protected: + + SpeechClient(); + void setupSpeechSystem(); + + ASYNC textRemoved(const QCString& appId, uint jobNum); + + private: + + class SpeechClientPrivate; + SpeechClientPrivate* d; + + static SpeechClient* m_self; +}; + +} // namespace Akregator + +#endif // AKREGATOR_SPEECHCLIENT_H diff --git a/akregator/src/storage.cpp b/akregator/src/storage.cpp new file mode 100644 index 000000000..b2fbf8952 --- /dev/null +++ b/akregator/src/storage.cpp @@ -0,0 +1,44 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#include "storage.h" + +namespace Akregator { +namespace Backend { + +Storage* Storage::m_instance = 0; + +Storage* Storage::getInstance() +{ + return m_instance; +} + +void Storage::setInstance(Storage* instance) +{ + m_instance = instance; +} + + + +} +} diff --git a/akregator/src/storage.h b/akregator/src/storage.h new file mode 100644 index 000000000..59dd410a4 --- /dev/null +++ b/akregator/src/storage.h @@ -0,0 +1,123 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_BACKEND_STORAGE_H +#define AKREGATOR_BACKEND_STORAGE_H + +#include <qobject.h> + +#include "akregator_export.h" + +class QString; +class QStringList; + +namespace Akregator { +namespace Backend { + +class FeedStorage; + +/** \brief Storage is the main interface to the article archive. It creates and manages FeedStorage objects handling the article list for a feed. + + An archive implementation must implement Storage, FeedStorage and StorageFactory. See mk4storage for an example. +*/ +class AKREGATOR_EXPORT Storage : public QObject +{ + public: + + static Storage* getInstance(); + static void setInstance(Storage* instance); + + virtual ~Storage() {} + + /** initializes the storage object with given parameters */ + + virtual void initialize(const QStringList& params) = 0; + + /** + * Open storage and prepare it for work. + * @return true on success. + */ + virtual bool open(bool autoCommit = false) = 0; + + /** + * Commit changes made in feeds and articles, making them persistent. + * @return true on success. + */ + virtual bool commit() = 0; + + /** + * Rollback changes made in feeds and articles, reverting to last committed values. + * @returns true on success. + */ + virtual bool rollback() = 0; + + /** + * Closes storage, freeing all allocated resources. Called from destructor, so you don't need to call it directly. + * @return true on success. + */ + virtual bool close() = 0; + + /** + * @return Article archive for feed at given url. + */ + virtual FeedStorage* archiveFor(const QString &url) = 0; + virtual bool autoCommit() const = 0; + virtual int unreadFor(const QString &url) = 0; + virtual void setUnreadFor(const QString &url, int unread) = 0; + virtual int totalCountFor(const QString &url) = 0; + virtual void setTotalCountFor(const QString &url, int total) = 0; + virtual int lastFetchFor(const QString& url) = 0; + virtual void setLastFetchFor(const QString& url, int lastFetch) = 0; + + /** stores the feed list in the storage backend. This is a fallback for the case that the + feeds.opml file gets corrupted + @param opmlStr the feed list in OPML format + */ + virtual void storeFeedList(const QString& opmlStr) = 0; + virtual QString restoreFeedList() const = 0; + + virtual void storeTagSet(const QString& xmlStr) = 0; + virtual QString restoreTagSet() const = 0; + + /** returns a list of all feeds (URLs) stored in this archive */ + + virtual QStringList feeds() const = 0; + + /** adds all feed storages from a source to this storage + existing articles are replaced + */ + virtual void add(Storage* source) = 0; + + /** deletes all feed storages in this archive */ + virtual void clear() = 0; + + private: + + static Storage *m_instance; +}; + +} +} + +#endif diff --git a/akregator/src/storagedummyimpl.cpp b/akregator/src/storagedummyimpl.cpp new file mode 100644 index 000000000..cca08d75a --- /dev/null +++ b/akregator/src/storagedummyimpl.cpp @@ -0,0 +1,200 @@ +/* + This file is part of Akregator. + + 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#include "storagedummyimpl.h" +#include "feedstoragedummyimpl.h" + +#include <qmap.h> +#include <qstring.h> +#include <qstringlist.h> + +namespace Akregator { +namespace Backend { + +class StorageDummyImpl::StorageDummyImplPrivate +{ + public: + class Entry + { + public: + int unread; + int totalCount; + int lastFetch; + FeedStorage* feedStorage; + }; + + void addEntry(const QString& url, int unread, int totalCount, int lastFetch) + { + Entry entry; + entry.unread = unread; + entry.totalCount = totalCount; + entry.lastFetch = lastFetch; + entry.feedStorage = 0; + feeds[url] = entry; + + } + QString tagSet; + QString feedList; + QMap<QString, Entry> feeds; +}; + +StorageDummyImpl::StorageDummyImpl() : d(new StorageDummyImplPrivate) +{ +} + +StorageDummyImpl::~StorageDummyImpl() +{ + delete d; d = 0; +} +void StorageDummyImpl::initialize(const QStringList&) {} + +bool StorageDummyImpl::open(bool /*autoCommit*/) +{ + return true; +} + +bool StorageDummyImpl::autoCommit() const +{ + return false; +} + +bool StorageDummyImpl::close() +{ + for (QMap<QString, StorageDummyImplPrivate::Entry>::ConstIterator it = d->feeds.begin(); it != d->feeds.end(); ++it) + { + (*it).feedStorage->close(); + delete (*it).feedStorage; + } + return true; +} + +bool StorageDummyImpl::commit() +{ + return true; +} + +bool StorageDummyImpl::rollback() +{ + return true; +} + +int StorageDummyImpl::unreadFor(const QString &url) +{ + return d->feeds.contains(url) ? d->feeds[url].unread : 0; +} + +void StorageDummyImpl::setUnreadFor(const QString &url, int unread) +{ + if (!d->feeds.contains(url)) + d->addEntry(url, unread, unread, 0); + else + d->feeds[url].unread = unread; +} + +int StorageDummyImpl::totalCountFor(const QString &url) +{ + return d->feeds.contains(url) ? d->feeds[url].totalCount : 0; +} + +void StorageDummyImpl::setTotalCountFor(const QString &url, int total) +{ + if (!d->feeds.contains(url)) + d->addEntry(url, 0, total, 0); + else + d->feeds[url].totalCount = total; +} + +int StorageDummyImpl::lastFetchFor(const QString& url) +{ + return d->feeds.contains(url) ? d->feeds[url].lastFetch : 0; +} + +void StorageDummyImpl::setLastFetchFor(const QString& url, int lastFetch) +{ + if (!d->feeds.contains(url)) + d->addEntry(url, 0, 0, lastFetch); + else + d->feeds[url].lastFetch = lastFetch; +} + +void StorageDummyImpl::slotCommit() +{ +} + +FeedStorage* StorageDummyImpl::archiveFor(const QString& url) +{ + if (!d->feeds.contains(url)) + d->feeds[url].feedStorage = new FeedStorageDummyImpl(url, this); + + return d->feeds[url].feedStorage; +} + +QStringList StorageDummyImpl::feeds() const +{ + return d->feeds.keys(); +} + +void StorageDummyImpl::add(Storage* source) +{ + QStringList feeds = source->feeds(); + for (QStringList::ConstIterator it = feeds.begin(); it != feeds.end(); ++it) + { + FeedStorage* fa = archiveFor(*it); + fa->add(source->archiveFor(*it)); + } +} + +void StorageDummyImpl::clear() +{ + for (QMap<QString, StorageDummyImplPrivate::Entry>::ConstIterator it = d->feeds.begin(); it != d->feeds.end(); ++it) + { + delete (*it).feedStorage; + } + d->feeds.clear(); + +} + +void StorageDummyImpl::storeFeedList(const QString& opmlStr) +{ + d->feedList = opmlStr; +} + +QString StorageDummyImpl::restoreFeedList() const +{ + return d->feedList; +} + +void StorageDummyImpl::storeTagSet(const QString& xmlStr) +{ + d->tagSet = xmlStr; +} + +QString StorageDummyImpl::restoreTagSet() const +{ + return d->tagSet; +} + +} // namespace Backend +} // namespace Akregator + +#include "storagedummyimpl.moc" diff --git a/akregator/src/storagedummyimpl.h b/akregator/src/storagedummyimpl.h new file mode 100644 index 000000000..497bcf01c --- /dev/null +++ b/akregator/src/storagedummyimpl.h @@ -0,0 +1,109 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_STORAGEDUMMYIMPL_H +#define AKREGATOR_STORAGEDUMMYIMPL_H + +#include "storage.h" + +namespace Akregator { +namespace Backend { + +/** + * Metakit implementation of Storage interface + */ +class StorageDummyImpl : public Storage +{ + Q_OBJECT + public: + + StorageDummyImpl(); + StorageDummyImpl(const StorageDummyImpl&); + StorageDummyImpl &operator =(const StorageDummyImpl&); + virtual ~StorageDummyImpl(); + virtual void initialize(const QStringList& params); + /** + * Open storage and prepare it for work. + * @return true on success. + */ + virtual bool open(bool autoCommit = false); + + /** + * Commit changes made in feeds and articles, making them persistent. + * @return true on success. + */ + virtual bool commit(); + + /** + * Rollback changes made in feeds and articles, reverting to last committed values. + * @returns true on success. + */ + virtual bool rollback(); + + /** + * Closes storage, freeing all allocated resources. Called from destructor, so you don't need to call it directly. + * @return true on success. + */ + virtual bool close(); + + /** + * @return Article archive for feed at given url. + */ + virtual FeedStorage* archiveFor(const QString &url); + virtual bool autoCommit() const; + virtual int unreadFor(const QString &url); + virtual void setUnreadFor(const QString &url, int unread); + virtual int totalCountFor(const QString &url); + virtual void setTotalCountFor(const QString &url, int total); + virtual int lastFetchFor(const QString& url); + virtual void setLastFetchFor(const QString& url, int lastFetch); + virtual QStringList feeds() const; + + virtual void storeFeedList(const QString& opmlStr); + virtual QString restoreFeedList() const; + + virtual void storeTagSet(const QString& xmlStr); + virtual QString restoreTagSet() const; + + /** adds all feed storages from a source to this storage + existing articles are replaced + */ + virtual void add(Storage* source); + + /** deletes all feed storages in this archive */ + virtual void clear(); + + protected slots: + virtual void slotCommit(); + + private: + class StorageDummyImplPrivate; + StorageDummyImplPrivate *d; +}; + +} +} + +#endif // STORAGEDummyIMPL_H diff --git a/akregator/src/storagefactory.h b/akregator/src/storagefactory.h new file mode 100644 index 000000000..f95ab7d13 --- /dev/null +++ b/akregator/src/storagefactory.h @@ -0,0 +1,72 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_BACKEND_STORAGEFACTORY_H +#define AKREGATOR_BACKEND_STORAGEFACTORY_H + +#include "akregator_export.h" + +class QString; +class QStringList; +class QWidget; +namespace Akregator { +namespace Backend { + +class Storage; + +class AKREGATOR_EXPORT StorageFactory +{ + public: + + /** identifier of the storage type, like "metakit", "postgres" etc. For use in + configuration files. Must not contain spaces or special characters. + */ + virtual QString key() const = 0; + + /** returns the (i18n'd) name of the storage type. */ + virtual QString name() const = 0; + + /** true if the plugin is configurable via a config dialog */ + virtual bool isConfigurable() const = 0; + + /** shows the plugin's configuration dialog */ + virtual void configure() = 0; + + /** + * returns wether the backend allows multiple writers at the same time + * If not, Akregator must use a lock to ensure that only one process gains + * write access. + */ + virtual bool allowsMultipleWriteAccess() const = 0; + + /** creates a storage object with given parameters + @param params list of implementation-specific parameters + */ + virtual Storage* createStorage(const QStringList& params) const = 0; +}; + +} +} + +#endif diff --git a/akregator/src/storagefactorydummyimpl.cpp b/akregator/src/storagefactorydummyimpl.cpp new file mode 100644 index 000000000..2f0bccfb9 --- /dev/null +++ b/akregator/src/storagefactorydummyimpl.cpp @@ -0,0 +1,57 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#include "storagefactorydummyimpl.h" +#include "storagedummyimpl.h" + +#include <klocale.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qwidget.h> + +namespace Akregator { +namespace Backend { + +Storage* StorageFactoryDummyImpl::createStorage(const QStringList& params) const +{ + Storage* storage = new StorageDummyImpl; + storage->initialize(params); + return storage; +} + +QString StorageFactoryDummyImpl::key() const +{ + return "dummy"; +} + +QString StorageFactoryDummyImpl::name() const +{ + return i18n("No Archive"); +} + +void StorageFactoryDummyImpl::configure() +{ +} + +} +} diff --git a/akregator/src/storagefactorydummyimpl.h b/akregator/src/storagefactorydummyimpl.h new file mode 100644 index 000000000..ff04d6f67 --- /dev/null +++ b/akregator/src/storagefactorydummyimpl.h @@ -0,0 +1,52 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_STORAGEFACTORYDUMMYIMPL_H +#define AKREGATOR_STORAGEFACTORYDUMMYIMPL_H + +#include "storagefactory.h" + +class QString; +class QStringList; + +namespace Akregator { +namespace Backend { + +class Storage; + +class StorageFactoryDummyImpl : public StorageFactory +{ + public: + virtual QString key() const; + virtual QString name() const; + virtual void configure(); + virtual bool isConfigurable() const { return false; } + virtual bool allowsMultipleWriteAccess() const { return true; } + virtual Storage* createStorage(const QStringList& params) const; +}; + +} +} + +#endif diff --git a/akregator/src/storagefactoryregistry.cpp b/akregator/src/storagefactoryregistry.cpp new file mode 100644 index 000000000..afe4422f2 --- /dev/null +++ b/akregator/src/storagefactoryregistry.cpp @@ -0,0 +1,92 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "storagefactory.h" +#include "storagefactoryregistry.h" + +#include <kstaticdeleter.h> + +#include <qmap.h> +#include <qstring.h> +#include <qstringlist.h> + +namespace Akregator { +namespace Backend { + +class StorageFactoryRegistry::StorageFactoryRegistryPrivate +{ + public: + QMap<QString, StorageFactory*> map; +}; + +StorageFactoryRegistry* StorageFactoryRegistry::m_instance = 0; +static KStaticDeleter<StorageFactoryRegistry> storagefactoryregistrysd; + +StorageFactoryRegistry* StorageFactoryRegistry::self() +{ + if (!m_instance) + m_instance = storagefactoryregistrysd.setObject(m_instance, new StorageFactoryRegistry); + return m_instance; +} + +bool StorageFactoryRegistry::registerFactory(StorageFactory* factory, const QString& typestr) +{ + if (containsFactory(typestr)) + return false; + d->map[typestr] = factory; + return true; +} + +void StorageFactoryRegistry::unregisterFactory(const QString& typestr) +{ + d->map.remove(typestr); +} + +StorageFactory* StorageFactoryRegistry::getFactory(const QString& typestr) +{ + return d->map[typestr]; +} + +bool StorageFactoryRegistry::containsFactory(const QString& typestr) const +{ + return d->map.contains(typestr); +} + +QStringList StorageFactoryRegistry::list() const +{ + return d->map.keys(); +} + +StorageFactoryRegistry::StorageFactoryRegistry() : d(new StorageFactoryRegistryPrivate) +{ +} + +StorageFactoryRegistry::~StorageFactoryRegistry() +{ + delete d; + d = 0; +} + +} +} diff --git a/akregator/src/storagefactoryregistry.h b/akregator/src/storagefactoryregistry.h new file mode 100644 index 000000000..e625c7e78 --- /dev/null +++ b/akregator/src/storagefactoryregistry.h @@ -0,0 +1,66 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_BACKEND_STORAGEFACTORYREGISTRY_H +#define AKREGATOR_BACKEND_STORAGEFACTORYREGISTRY_H + +#include "akregator_export.h" + +class QString; +class QStringList; + +namespace Akregator { +namespace Backend { + +class StorageFactory; + +class AKREGATOR_EXPORT StorageFactoryRegistry +{ + public: + static StorageFactoryRegistry* self(); + + ~StorageFactoryRegistry(); + + bool registerFactory(StorageFactory* factory, const QString& typestr); + void unregisterFactory(const QString& typestr); + StorageFactory* getFactory(const QString& typestr); + bool containsFactory(const QString& typestr) const; + QStringList list() const; + + + private: + static StorageFactoryRegistry* m_instance; + + StorageFactoryRegistry(); + StorageFactoryRegistry(const StorageFactoryRegistry&); + StorageFactoryRegistry& operator=(const StorageFactoryRegistry&); + + class StorageFactoryRegistryPrivate; + StorageFactoryRegistryPrivate* d; +}; + +} +} + +#endif // STORAGEFACTORYREGISTRY_H diff --git a/akregator/src/tabwidget.cpp b/akregator/src/tabwidget.cpp new file mode 100644 index 000000000..561b93e5d --- /dev/null +++ b/akregator/src/tabwidget.cpp @@ -0,0 +1,331 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "tabwidget.h" + +#include <qstyle.h> +#include <qapplication.h> +#include <qiconset.h> +#include <qclipboard.h> +#include <qmap.h> +#include <qptrdict.h> +#include <qstring.h> +#include <qtoolbutton.h> +#include <qtooltip.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <ktabwidget.h> +#include <ktabbar.h> +#include <kpopupmenu.h> +#include <krun.h> +#include <klocale.h> +#include <khtmlview.h> +#include <khtml_part.h> +#include <kiconloader.h> +#include <kurl.h> +#include <kurldrag.h> +#include <kmimetype.h> + +#include "actionmanager.h" +#include "frame.h" +#include "akregatorconfig.h" + +namespace Akregator { + +class TabWidget::TabWidgetPrivate +{ + public: + QPtrDict<Frame> frames; + uint CurrentMaxLength; + QWidget* currentItem; + QToolButton* tabsClose; +}; + +TabWidget::TabWidget(QWidget * parent, const char *name) + :KTabWidget(parent, name), d(new TabWidgetPrivate) +{ + d->CurrentMaxLength = 30; + d->currentItem = 0; + setMinimumSize(250,150); + setTabReorderingEnabled(false); + connect( this, SIGNAL( currentChanged(QWidget *) ), this, + SLOT( slotTabChanged(QWidget *) ) ); + connect(this, SIGNAL(closeRequest(QWidget*)), this, SLOT(slotCloseRequest(QWidget*))); + setHoverCloseButton(Settings::closeButtonOnTabs()); + + d->tabsClose = new QToolButton(this); + d->tabsClose->setAccel(QKeySequence("Ctrl+W")); + connect( d->tabsClose, SIGNAL( clicked() ), this, + SLOT( slotRemoveCurrentFrame() ) ); + + d->tabsClose->setIconSet( SmallIconSet( "tab_remove" ) ); + d->tabsClose->adjustSize(); + QToolTip::add(d->tabsClose, i18n("Close the current tab")); + setCornerWidget( d->tabsClose, TopRight ); +} + +TabWidget::~TabWidget() +{ + delete d; + d = 0; +} + +void TabWidget::slotSettingsChanged() +{ + if (hoverCloseButton() != Settings::closeButtonOnTabs()) + setHoverCloseButton(Settings::closeButtonOnTabs()); +} + +void TabWidget::slotNextTab() +{ + setCurrentPage((currentPageIndex()+1) % count()); +} + +void TabWidget::slotPreviousTab() +{ + if (currentPageIndex() == 0) + setCurrentPage(count()-1); + else + setCurrentPage(currentPageIndex()-1); +} + +void TabWidget::addFrame(Frame *f) +{ + if (!f || !f->widget()) + return; + d->frames.insert(f->widget(), f); + addTab(f->widget(), f->title()); + connect(f, SIGNAL(titleChanged(Frame*, const QString& )), this, SLOT(slotSetTitle(Frame*, const QString& ))); + slotSetTitle(f, f->title()); +} + +Frame *TabWidget::currentFrame() +{ + QWidget* w = currentPage(); + + return w ? d->frames[w] : 0; +} + +QPtrList<Frame> TabWidget::frames() const +{ + QPtrList<Frame> result; + QPtrDictIterator<Frame> it(d->frames); + while (it.current()) + { + result.append(it.current()); + ++it; + } + + return result; +} + +void TabWidget::slotTabChanged(QWidget *w) +{ + // FIXME: Don't hardcode the tab position of main frame + d->tabsClose->setDisabled(currentPageIndex() == 0); + emit currentFrameChanged(d->frames[w]); +} + +void TabWidget::slotRemoveCurrentFrame() +{ + removeFrame(currentFrame()); +} + +void TabWidget::removeFrame(Frame *f) +{ + f->setCompleted(); + d->frames.remove(f->widget()); + removePage(f->widget()); + delete f; + setTitle( currentFrame()->title(), currentPage() ); +} + +// copied wholesale from KonqFrameTabs +uint TabWidget::tabBarWidthForMaxChars( uint maxLength ) +{ + int hframe, overlap; + hframe = tabBar()->style().pixelMetric( QStyle::PM_TabBarTabHSpace, this ); + overlap = tabBar()->style().pixelMetric( QStyle::PM_TabBarTabOverlap, this ); + + QFontMetrics fm = tabBar()->fontMetrics(); + int x = 0; + for( int i=0; i < count(); ++i ) { + Frame *f=d->frames[page(i)]; + QString newTitle=f->title(); + if ( newTitle.length() > maxLength ) + newTitle = newTitle.left( maxLength-3 ) + "..."; + + QTab* tab = tabBar()->tabAt( i ); + int lw = fm.width( newTitle ); + int iw = 0; + if ( tab->iconSet() ) + iw = tab->iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4; + + x += ( tabBar()->style().sizeFromContents( QStyle::CT_TabBarTab, this, QSize( QMAX( lw + hframe + iw, QApplication::globalStrut().width() ), 0 ), QStyleOption( tab ) ) ).width(); + } + return x; +} + +void TabWidget::slotSetTitle(Frame* frame, const QString& title) +{ + setTitle(title, frame->widget()); +} + +void TabWidget::setTitle( const QString &title , QWidget* sender) +{ + removeTabToolTip( sender ); + + uint lcw=0, rcw=0; + int tabBarHeight = tabBar()->sizeHint().height(); + if ( cornerWidget( TopLeft ) && cornerWidget( TopLeft )->isVisible() ) + lcw = QMAX( cornerWidget( TopLeft )->width(), tabBarHeight ); + if ( cornerWidget( TopRight ) && cornerWidget( TopRight )->isVisible() ) + rcw = QMAX( cornerWidget( TopRight )->width(), tabBarHeight ); + uint maxTabBarWidth = width() - lcw - rcw; + + uint newMaxLength=30; + for ( ; newMaxLength > 3; newMaxLength-- ) +{ + if ( tabBarWidthForMaxChars( newMaxLength ) < maxTabBarWidth ) + break; + } + QString newTitle = title; + if ( newTitle.length() > newMaxLength ) + { + setTabToolTip( sender, newTitle ); + newTitle = newTitle.left( newMaxLength-3 ) + "..."; + } + + newTitle.replace( '&', "&&" ); + if ( tabLabel( sender ) != newTitle ) + changeTab( sender, newTitle ); + + if( newMaxLength != d->CurrentMaxLength ) + { + for( int i = 0; i < count(); ++i) + { + Frame *f=d->frames[page(i)]; + newTitle=f->title(); + removeTabToolTip( page( i ) ); + if ( newTitle.length() > newMaxLength ) + { + setTabToolTip( page( i ), newTitle ); + newTitle = newTitle.left( newMaxLength-3 ) + "..."; + } + + newTitle.replace( '&', "&&" ); + if ( newTitle != tabLabel( page( i ) ) ) + changeTab( page( i ), newTitle ); + } + d->CurrentMaxLength = newMaxLength; + } +} + +void TabWidget::contextMenu(int i, const QPoint &p) +{ + QWidget* w = ActionManager::getInstance()->container("tab_popup"); + d->currentItem = page(i); + //kdDebug() << indexOf(d->currentItem) << endl; + if (w && indexOf(d->currentItem) != 0) + static_cast<QPopupMenu *>(w)->exec(p); + d->currentItem = 0; +} + +void TabWidget::slotDetachTab() +{ + if (!d->currentItem || indexOf(d->currentItem) == -1) + d->currentItem = currentPage(); + + if (indexOf(d->currentItem) == 0) + return; + + KURL url; + KHTMLView* view = dynamic_cast<KHTMLView*>(d->currentItem); + + if (!view) + return; + + url = view->part()->url(); + + kapp->invokeBrowser(url.url(), "0"); + slotCloseTab(); +} + +void TabWidget::slotCopyLinkAddress() +{ + if(!d->currentItem || indexOf(d->currentItem) == -1) + d->currentItem = currentPage(); + if(indexOf(d->currentItem) == 0) + return; + + KURL url; + KHTMLView* view = dynamic_cast<KHTMLView*>(d->currentItem); + + if (!view) + return; + + url = view->part()->url(); + + kapp->clipboard()->setText(url.prettyURL(), QClipboard::Selection); + kapp->clipboard()->setText(url.prettyURL(), QClipboard::Clipboard); +} + +void TabWidget::slotCloseTab() +{ + if (!d->currentItem || indexOf(d->currentItem) == -1) + d->currentItem = currentPage(); + if (indexOf(d->currentItem) == 0) + return; + if (d->frames.find(d->currentItem) != NULL) + removeFrame(d->frames.find(d->currentItem)); + delete d->currentItem; + d->currentItem = 0; +} + +void TabWidget::initiateDrag(int tab) +{ + if (tab == 0) // don't initiate drag for the main tab + return; + + Frame* frame = d->frames[page(tab)]; + + if (frame != 0) + { + KURL::List lst; + lst.append( frame->part()->url() ); + KURLDrag* drag = new KURLDrag( lst, this ); + drag->setPixmap( KMimeType::pixmapForURL( lst.first(), 0, KIcon::Small ) ); + drag->dragCopy(); + } +} + +void TabWidget::slotCloseRequest(QWidget* widget) +{ + if (d->frames.find(widget) != NULL) + removeFrame(d->frames.find(widget)); +} +} // namespace Akregator + +#include "tabwidget.moc" diff --git a/akregator/src/tabwidget.h b/akregator/src/tabwidget.h new file mode 100644 index 000000000..854d27d2b --- /dev/null +++ b/akregator/src/tabwidget.h @@ -0,0 +1,90 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef TABWIDGET_H +#define TABWIDGET_H + +#include <qptrlist.h> +#include <ktabwidget.h> + +class QString; + +namespace Akregator +{ + +class Frame; + +class TabWidget : public KTabWidget +{ + Q_OBJECT + + public: + TabWidget(QWidget * parent = 0, const char *name = 0); + virtual ~TabWidget(); + + void addFrame(Frame *f); + Frame* currentFrame(); + void removeFrame(Frame *f); + QPtrList<Frame> frames() const; + + public slots: + + void slotSetTitle(Frame* frame, const QString& title); + void slotSettingsChanged(); + void slotNextTab(); + void slotPreviousTab(); + void slotRemoveCurrentFrame(); + + signals: + + void currentFrameChanged(Frame *); + + protected slots: + + virtual void initiateDrag(int tab); + + private: // methods + + uint tabBarWidthForMaxChars( uint maxLength ); + void setTitle( const QString &title , QWidget* sender); + + + private slots: + + void slotDetachTab(); + void slotCopyLinkAddress(); + void slotCloseTab(); + void slotCloseRequest(QWidget* widget); + void contextMenu (int item, const QPoint &p); + void slotTabChanged(QWidget *w); + + private: + + class TabWidgetPrivate; + TabWidgetPrivate* d; +}; + +} + +#endif diff --git a/akregator/src/tag.cpp b/akregator/src/tag.cpp new file mode 100644 index 000000000..251c4e633 --- /dev/null +++ b/akregator/src/tag.cpp @@ -0,0 +1,160 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "shared.h" +#include "tag.h" +#include "tagset.h" + +#include <qstring.h> +#include <qvaluelist.h> + +namespace Akregator { + +class Tag::TagPrivate : public Shared +{ + public: + QString id; + QString name; + QString scheme; + QString icon; + + QValueList<TagSet*> tagSets; + bool operator==(const TagPrivate& other) const + { + return id == other.id; // name is ignored! + } +}; + +Tag::Tag() : d(new TagPrivate) +{} + +Tag::Tag(const QString& id, const QString& name, const QString& scheme) : d(new TagPrivate) +{ + d->id = id; + d->name = name.isNull() ? id : name; + d->scheme = scheme; + d->icon = "rss_tag"; +} + +Tag Tag::fromCategory(const QString& term, const QString& scheme, const QString& name) +{ + Tag tag(scheme + "/" + term, name, scheme); + return tag; +} + + +Tag::Tag(const Tag& other) : d(0) +{ + *this = other; +} + +Tag::~Tag() +{ + if (d->deref()) + { + delete d; + d = 0; + } +} + +Tag& Tag::operator=(const Tag& other) +{ + if (this != &other) + { + other.d->ref(); + if (d && d->deref()) + delete d; + d = other.d; + } + return *this; +} + + +bool Tag::operator==(const Tag& other) const +{ + return *(other.d) == *d; +} + +bool Tag::operator<(const Tag& other) const +{ + return (name() < other.name()) || (name() == other.name() && id() < other.id()); +} + +bool Tag::isNull() const +{ + return d->id.isNull(); +} + +QString Tag::name() const +{ + return d->name; +} + +QString Tag::scheme() const +{ + return d->scheme; +} + +QString Tag::icon() const +{ + return d->icon; +} + +void Tag::setIcon(const QString& icon) +{ + if (icon != d->icon) + { + d->icon = icon; + for (QValueList<TagSet*>::ConstIterator it = d->tagSets.begin(); it != d->tagSets.end(); ++it) + (*it)->tagUpdated(*this); + } +} + + +void Tag::setName(const QString& name) +{ + if (name != d->name) + { + d->name = name; + for (QValueList<TagSet*>::ConstIterator it = d->tagSets.begin(); it != d->tagSets.end(); ++it) + (*it)->tagUpdated(*this); + } +} + +void Tag::addedToTagSet(TagSet* tagSet) const +{ + d->tagSets.append(tagSet); +} + +void Tag::removedFromTagSet(TagSet* tagSet) const +{ + d->tagSets.remove(tagSet); +} + +QString Tag::id() const +{ + return d->id; +} + +} // namespace Akregator diff --git a/akregator/src/tag.h b/akregator/src/tag.h new file mode 100644 index 000000000..ebb3cf8eb --- /dev/null +++ b/akregator/src/tag.h @@ -0,0 +1,123 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_TAG_H +#define AKREGATOR_TAG_H + +#include <qstring.h> + +namespace Akregator { + +class TagSet; + +/** represents a tag. A tag has a required identifier and optional @c scheme and @c name attributes + - The identifier is any string and must be unique in the tag set + - @c name is the human-readible name of the tag. This is the string used in the GUI + - The optional attribute @c scheme is a classification scheme the tag belongs to + +Examples: +user-generated tag with some magic in the id to make it (probably) unique: id: ACB4C7D5FFFriends name: Friends +mapped from a blog category: id: http://ablog.org/blog#Funny name: Funny +tag from some ontology: id: http://foo/ont/AnimalTaxonomy/mammals, name: Mammals, scheme: http://foo/ont/AnimalTaxonomy +*/ +class Tag +{ + friend class TagSet; + + public: + + /** creates a tag with given id, name and scheme. If name is QString::null, the id is used as name. If id is QString::null, the object is considered as NULL object (see isNull())*/ + Tag(const QString& id, const QString& name=QString::null, const QString& scheme=QString::null); + + /** creates a null tag (isNull() is @c true) */ + Tag(); + + Tag(const Tag& other); + + /** creates a tag from a Atom-1.0-like (term, scheme, label) category. + + @c term is a string that identifies the tag, Examples are: "General", "KDE", "Personal" + @c scheme (optional) classification scheme the term belongs to + @c label/name the (optinal) human-readable name of the tag, synonymous to @c name + + Example: + + @code + Create + + <category term="foo" scheme="http://del.icio.us/tag" label="Del.icio.us tag for foo"/> + + using Tag::fromCategory("foo", "http://del.icio.us/tag", "Del.icio.us tag for foo") + + The @c id is built using 'scheme + "/" + term': The example gets id = "http://del.icio.us/tag/foo" + @encode + */ + static Tag fromCategory(const QString& term, const QString& scheme=QString::null, const QString& name=QString::null); + + virtual ~Tag(); + + /** returns whether this is a null object (equal to id().isNull())*/ + + bool isNull() const; + + /** tag identifier, used as key throughout the app and archive. Must be unique in the tag set. Can be an arbitrary unicode string, an URI etc. */ + QString id() const; + + /** user-visible name of the tag */ + QString name() const; + + /** (optional) classfication scheme this tag belongs to */ + QString scheme() const; + + void setName(const QString& name); + + QString icon() const; + void setIcon(const QString& icon); + + Tag& operator=(const Tag& other); + + /** compares tags by name. If names are equal, id's are compared. + a < b iff a.name < b.name || (a.name == b.name && a.id < b.id) */ + bool operator<(const Tag& other) const; + + /** tags are equal when their id's are equal, name is ignored */ + bool operator==(const Tag& other) const; + + + protected: + /** called by TagSet */ + void addedToTagSet(TagSet* tagSet) const; + +/** called by TagSet */ + void removedFromTagSet(TagSet* tagSet) const; + private: + + class TagPrivate; + TagPrivate* d; +}; + + +} // namespace Akregator + +#endif // AKREGATOR_TAG_H diff --git a/akregator/src/tagaction.cpp b/akregator/src/tagaction.cpp new file mode 100644 index 000000000..4222bf2d7 --- /dev/null +++ b/akregator/src/tagaction.cpp @@ -0,0 +1,114 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +//#include "menuitems.h" +#include "tag.h" +#include "tagaction.h" + +#include <kapplication.h> +#include <kdebug.h> +#include <kpopupmenu.h> + +#include <qmap.h> +#include <qpopupmenu.h> + + +namespace Akregator { + +class TagAction::TagActionPrivate +{ + public: + Tag tag; + //QMap<int, QPopupMenu*> idToPopup; + //QMap<QPopupMenu*, int> popupToId; +}; + +TagAction::TagAction(const Tag& tag, const QObject *receiver, const char *slot, QObject *parent) +//KAction (const QString &text, const KShortcut &cut, const QObject *receiver, const char *slot, QObject *parent, const char *name=0) + : KToggleAction(tag.name(), KShortcut(), 0, 0, parent), d(new TagActionPrivate) +{ + d->tag = tag; + connect(this, SIGNAL(toggled(const Tag&, bool)), receiver, slot); + connect(this, SIGNAL(toggled(bool)), this, SLOT(slotToggled(bool))); +} + +TagAction::~TagAction() +{ + delete d; + d = 0; +} + +Tag TagAction::tag() const +{ + return d->tag; +} + +/* +void TagAction::unplug(QWidget* widget) +{ + KToggleAction::unplug(widget); + + QPopupMenu* popup = ::qt_cast<QPopupMenu *>(widget); + if (popup) + { + d->idToPopup.remove(d->popupToId[popup]); + d->popupToId.remove(popup); + } +}*/ + +/* +int TagAction::plug(QWidget* widget, int index) +{ + QPopupMenu* popup = ::qt_cast<QPopupMenu *>( widget ); + if (!popup) + { + kdWarning() << "Can not plug KToggleAction in " << widget->className() << endl; + return -1; + } + if (kapp && !kapp->authorizeKAction(name())) + return -1; + + TagMenuItem* item = new TagMenuItem(d->tag); + int id = popup->insertItem(TagMenuItem::checkBoxIconSet(isChecked(), popup->colorGroup()), item, -1, index); + + + popup->connectItem (id, this, SLOT(slotActivated())); + + d->popupToId[popup] = id; + d->idToPopup[id] = popup; + + if ( id == -1 ) + return id; + + return id; +} +*/ +void TagAction::slotToggled(bool enabled) +{ + emit toggled(d->tag, enabled); +} + +} // namespace Akregator + +#include "tagaction.moc" diff --git a/akregator/src/tagaction.h b/akregator/src/tagaction.h new file mode 100644 index 000000000..f4ecfe674 --- /dev/null +++ b/akregator/src/tagaction.h @@ -0,0 +1,68 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_TAGACTION_H +#define AKREGATOR_TAGACTION_H + +#include <kactionclasses.h> + +class QString; +class QWidget; + +namespace Akregator { + +class Tag; + +/** a KToggleAction for assigning and removing tags. Displays the tag name and emits the tag when activated */ +class TagAction : public KToggleAction +{ + Q_OBJECT + + public: + + TagAction(const Tag& tag, const QObject *receiver, const char *slot, QObject *parent=0); + virtual ~TagAction(); + + //virtual int plug(QWidget *widget, int index=-1); + //virtual void unplug(QWidget* widget); + + virtual Tag tag() const; + + signals: + + void toggled(const Tag&, bool); + + protected slots: + + virtual void slotToggled(bool enabled); + + private: + + class TagActionPrivate; + TagActionPrivate* d; +}; + +} // namespace Akregator + +#endif // AKREGATOR_TAGACTION_H diff --git a/akregator/src/tagfolder.cpp b/akregator/src/tagfolder.cpp new file mode 100644 index 000000000..2fb64b1c3 --- /dev/null +++ b/akregator/src/tagfolder.cpp @@ -0,0 +1,60 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "tagfolder.h" +#include "treenodevisitor.h" + +#include <qdom.h> + +namespace Akregator { + +class TagFolder::TagFolderPrivate +{ +}; + +TagFolder::TagFolder(const QString& title) : Folder(title), d(new TagFolderPrivate) +{ +} + +TagFolder::~TagFolder() +{ + emitSignalDestroyed(); + delete d; + d = 0; +} + +bool TagFolder::accept(TreeNodeVisitor* visitor) +{ + if (visitor->visitTagFolder(this)) + return true; + else + return visitor->visitFolder(this); +} + +QDomElement TagFolder::toOPML( QDomElement /*parent*/, QDomDocument /*document*/ ) const +{ + return QDomElement(); +} + +} diff --git a/akregator/src/tagfolder.h b/akregator/src/tagfolder.h new file mode 100644 index 000000000..0ec027bc8 --- /dev/null +++ b/akregator/src/tagfolder.h @@ -0,0 +1,55 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_TAGFOLDER_H +#define AKREGATOR_TAGFOLDER_H + +#include "folder.h" + +class QDomDocument; +class QDomElement; + +namespace Akregator { + +class TreeNodeVisitor; + +class TagFolder : public Folder +{ + public: + + TagFolder(const QString& title = QString::null); + + virtual ~TagFolder(); + virtual bool accept(TreeNodeVisitor* visitor); + + virtual QDomElement toOPML( QDomElement parent, QDomDocument document ) const; + + private: + class TagFolderPrivate; + TagFolderPrivate* d; +}; + +} // namespace Akregator + +#endif // AKREGATOR_TAGFOLDER_H diff --git a/akregator/src/tagfolderitem.cpp b/akregator/src/tagfolderitem.cpp new file mode 100644 index 000000000..d3f0c8d5c --- /dev/null +++ b/akregator/src/tagfolderitem.cpp @@ -0,0 +1,69 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "actionmanager.h" +#include "tagfolder.h" +#include "tagfolderitem.h" +#include "treenode.h" + +#include <qpopupmenu.h> +#include <kaction.h> +#include <kiconloader.h> + +namespace Akregator { + +TagFolderItem::TagFolderItem(FolderItem* parent, TagFolder* node) : FolderItem(parent, node) +{ +} + +TagFolderItem::TagFolderItem(FolderItem* parent, TreeNodeItem* after, TagFolder* node) : FolderItem(parent, after, node) +{ +} + +TagFolderItem::TagFolderItem(KListView* parent, TagFolder* node) : FolderItem(parent, node) +{ +} + +TagFolderItem::TagFolderItem(KListView* parent, TreeNodeItem* after, TagFolder* node) : FolderItem(parent, after, node) +{ +} + + +TagFolder* TagFolderItem::node() +{ + return static_cast<TagFolder*> (m_node); +} + +TagFolderItem::~TagFolderItem() +{} + + +void TagFolderItem::showContextMenu(const QPoint& p) +{ + QWidget* w = ActionManager::getInstance()->container("tagfolder_popup"); + if (w) + static_cast<QPopupMenu *>(w)->exec(p); +} + +} diff --git a/akregator/src/tagfolderitem.h b/akregator/src/tagfolderitem.h new file mode 100644 index 000000000..8b30f0b1c --- /dev/null +++ b/akregator/src/tagfolderitem.h @@ -0,0 +1,56 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef AKREGATOR_TAGFOLDERITEM_H +#define AKREGATOR_TAGFOLDERITEM_H + +#include "tagfolder.h" +#include "treenodeitem.h" +#include "folderitem.h" +#include "treenode.h" + +#include <qpixmap.h> + +class QPoint; + +namespace Akregator +{ + +class TagFolderItem : public FolderItem +{ + +public: + TagFolderItem(FolderItem* parent, TagFolder* node); + TagFolderItem(FolderItem* parent, TreeNodeItem* after, TagFolder* node); + TagFolderItem(KListView* parent, TagFolder* node); + TagFolderItem(KListView* parent, TreeNodeItem* after, TagFolder* node); + virtual ~TagFolderItem(); + + virtual TagFolder* node(); + + virtual void showContextMenu(const QPoint& p); +}; + +} + +#endif diff --git a/akregator/src/tagnode.cpp b/akregator/src/tagnode.cpp new file mode 100644 index 000000000..9fd1ee5e6 --- /dev/null +++ b/akregator/src/tagnode.cpp @@ -0,0 +1,304 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "article.h" +#include "articlefilter.h" +#include "fetchqueue.h" +#include "folder.h" +#include "tag.h" +#include "tagnode.h" +#include "treenode.h" +#include "treenodevisitor.h" + +#include <qdom.h> +#include <qstring.h> +#include <qvaluelist.h> + +namespace Akregator { + +class TagNode::TagNodePrivate +{ + public: + Filters::TagMatcher filter; + TreeNode* observed; + int unread; + QString icon; + Tag tag; + QValueList<Article> articles; + QValueList<Article> addedArticlesNotify; + QValueList<Article> removedArticlesNotify; + QValueList<Article> updatedArticlesNotify; +}; + +TagNode::TagNode(const Tag& tag, TreeNode* observed) : d(new TagNodePrivate) +{ + d->tag = tag; + d->icon = tag.icon(); + d->filter = Filters::TagMatcher(tag.id()); + setTitle(tag.name()); + d->observed = observed; + d->unread = 0; + + connect(observed, SIGNAL(signalDestroyed(TreeNode*)), this, SLOT(slotObservedDestroyed(TreeNode*))); + connect(observed, SIGNAL(signalArticlesAdded(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesAdded(TreeNode*, const QValueList<Article>&)) ); + connect(observed, SIGNAL(signalArticlesUpdated(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesUpdated(TreeNode*, const QValueList<Article>&)) ); + connect(observed, SIGNAL(signalArticlesRemoved(TreeNode*, const QValueList<Article>&)), this, SLOT(slotArticlesRemoved(TreeNode*, const QValueList<Article>&)) ); + + d->articles = observed->articles(tag.id()); + calcUnread(); +} + +QString TagNode::icon() const +{ + return d->icon; +} + +Tag TagNode::tag() const +{ + return d->tag; +} + +TagNode::~TagNode() +{ + emitSignalDestroyed(); + delete d; + d = 0; +} + +bool TagNode::accept(TreeNodeVisitor* visitor) +{ + if (visitor->visitTagNode(this)) + return true; + else + return visitor->visitTreeNode(this); +} + +void TagNode::calcUnread() +{ + int unread = 0; + QValueList<Article>::Iterator en = d->articles.end(); + for (QValueList<Article>::Iterator it = d->articles.begin(); it != en; ++it) + if ((*it).status() != Article::Read) + ++unread; + if (d->unread != unread) + { + d->unread = unread; + nodeModified(); + } +} + +int TagNode::unread() const +{ + return d->unread; +} + + +int TagNode::totalCount() const +{ + return d->articles.count(); +} + + +QValueList<Article> TagNode::articles(const QString& tag) +{ + return d->articles; +} + +QStringList TagNode::tags() const +{ + // TODO + return QStringList(); +} + +QDomElement TagNode::toOPML( QDomElement parent, QDomDocument document ) const +{ + return QDomElement(); +} + +TreeNode* TagNode::next() +{ + if ( nextSibling() ) + return nextSibling(); + + Folder* p = parent(); + while (p) + { + if ( p->nextSibling() ) + return p->nextSibling(); + else + p = p->parent(); + } + return 0; +} + +void TagNode::slotDeleteExpiredArticles() +{ +// not our business +} + +void TagNode::slotMarkAllArticlesAsRead() +{ + setNotificationMode(false); + QValueList<Article>::Iterator en = d->articles.end(); + for (QValueList<Article>::Iterator it = d->articles.begin(); it != en; ++it) + (*it).setStatus(Article::Read); + setNotificationMode(true); +} + +void TagNode::slotAddToFetchQueue(FetchQueue* /*queue*/, bool /*intervalFetchOnly*/) +{ +// not our business +} + +void TagNode::doArticleNotification() +{ + if (!d->addedArticlesNotify.isEmpty()) + { + emit signalArticlesAdded(this, d->addedArticlesNotify); + d->addedArticlesNotify.clear(); + } + if (!d->updatedArticlesNotify.isEmpty()) + { + emit signalArticlesUpdated(this, d->updatedArticlesNotify); + d->updatedArticlesNotify.clear(); + } + if (!d->removedArticlesNotify.isEmpty()) + { + emit signalArticlesRemoved(this, d->removedArticlesNotify); + d->removedArticlesNotify.clear(); + } + TreeNode::doArticleNotification(); +} + +void TagNode::slotArticlesAdded(TreeNode* node, const QValueList<Article>& list) +{ + bool added = false; + for (QValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it) + { + if (!d->articles.contains(*it) && d->filter.matches(*it)) + { + d->articles.append(*it); + d->addedArticlesNotify.append(*it); + added = true; + } + } + + if (added) + { + calcUnread(); + articlesModified(); + } +} + +void TagNode::slotArticlesUpdated(TreeNode* node, const QValueList<Article>& list) +{ + bool updated = false; + for (QValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it) + { + if (d->articles.contains(*it)) + { + if (!d->filter.matches(*it)) // articles is in list, but doesn't match our criteria anymore -> remove it + { + d->articles.remove(*it); + d->removedArticlesNotify.append(*it); + updated = true; + } + else // otherwise the article remains in the list and we just forward the update + { + d->updatedArticlesNotify.append(*it); + updated = true; + } + } + else // article not in list + { + if (d->filter.matches(*it)) // articles is not in list, but matches our criteria -> add it + { + d->articles.append(*it); + d->addedArticlesNotify.append(*it); + updated = true; + } + } + } + if (updated) + { + calcUnread(); + articlesModified(); + } +} + +void TagNode::slotArticlesRemoved(TreeNode* node, const QValueList<Article>& list) +{ + bool removed = false; + for (QValueList<Article>::ConstIterator it = list.begin(); it != list.end(); ++it) + { + if (d->articles.contains(*it)) + { + d->articles.remove(*it); + d->removedArticlesNotify.append(*it); + removed = true; + } + } + if (removed) + { + calcUnread(); + articlesModified(); + } +} + +void TagNode::setTitle(const QString& title) +{ + if (d->tag.name() != title) + d->tag.setName(title); + TreeNode::setTitle(title); +} + +void TagNode::slotObservedDestroyed(TreeNode* /*observed*/) +{ + d->removedArticlesNotify = d->articles; + d->articles.clear(); + articlesModified(); +} + +void TagNode::tagChanged() +{ + bool changed = false; + if (title() != d->tag.name()) + { + setTitle(d->tag.name()); + changed = true; + } + + if (d->icon != d->tag.icon()) + { + d->icon = d->tag.icon(); + changed = true; + } + + if (changed) + nodeModified(); +} + +} // namespace Akregator + +#include "tagnode.moc" diff --git a/akregator/src/tagnode.h b/akregator/src/tagnode.h new file mode 100644 index 000000000..51ee4879c --- /dev/null +++ b/akregator/src/tagnode.h @@ -0,0 +1,136 @@ + /* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. + */ + +#ifndef AKREGATOR_TAGNODE_H +#define AKREGATOR_TAGNODE_H + +#include <treenode.h> + +class QDomDocument; +class QDomElement; +class QString; +class QStringList; +template <class T> class QValueList; + +namespace Akregator +{ + +class Article; +class Folder; +class FetchQueue; +class Tag; +class TreeNodeVisitor; + +class TagNode : public TreeNode +{ +Q_OBJECT + +public: + + /** creates a tag node, showing articles with a specific tag, from a given node. + @param tagId the identifier of the tag, as stored in the archive + @param observed the tag node will list the tagged articles of @c observed (usually "All Feeds" root node) + + */ + TagNode(const Tag& tag, TreeNode* observed); + + virtual ~TagNode(); + + virtual bool accept(TreeNodeVisitor* visitor); + + virtual Tag tag() const; + + virtual QString icon() const; + + /** The unread count, returns the number of new/unread articles in the node (for groups: the accumulated count of the subtree) + @return number of new/unread articles */ + + virtual int unread() const; + + + /** returns the number of total articles in the node (for groups: the accumulated count of the subtree) + @return number of articles */ + + virtual int totalCount() const; + + + /** Returns a sequence of the articles this node contains. For feed groups, this returns a concatenated list of all articles in the sub tree. + If @c tag is not null, only articles tagged with @c tag are returned + @return sequence of articles */ + + virtual QValueList<Article> articles(const QString& tag=QString::null); + + /** returns a list of all tags occurring in this node (sub tree for folders) */ + + virtual QStringList tags() const; + + /** Helps the rest of the app to decide if node should be handled as group or not. Only use where necessary, use polymorphism where possible. + @return whether the node is a feed group or not */ + + virtual bool isGroup() const {return false;} + + /** reimplemented to return an invalid element */ + + virtual QDomElement toOPML( QDomElement parent, QDomDocument document ) const; + + /** returns the next node in the tree. + Calling next() unless it returns 0 iterates through the tree in pre-order + */ + virtual TreeNode* next(); + + virtual void setTitle(const QString& title); + + /** called by @c TagNodeList when the tag of this node was changed */ + virtual void tagChanged(); + +public slots: + + + /** does nothing for TagNode */ + virtual void slotDeleteExpiredArticles(); + + /** marks all articles as read */ + virtual void slotMarkAllArticlesAsRead(); + + /** does nothing for TagNode */ + virtual void slotAddToFetchQueue(FetchQueue* queue, bool intervalFetchOnly=false); + + virtual void slotArticlesAdded(TreeNode* node, const QValueList<Article>& list); + virtual void slotArticlesUpdated(TreeNode* node, const QValueList<Article>& list); + virtual void slotArticlesRemoved(TreeNode* node, const QValueList<Article>& list); + virtual void slotObservedDestroyed(TreeNode* node); + +protected: + + virtual void doArticleNotification(); + virtual void calcUnread(); + +private: + class TagNodePrivate; + TagNodePrivate* d; +}; + +} + +#endif // AKREGATOR_TAGNODE_H diff --git a/akregator/src/tagnodeitem.cpp b/akregator/src/tagnodeitem.cpp new file mode 100644 index 000000000..70f2c90fd --- /dev/null +++ b/akregator/src/tagnodeitem.cpp @@ -0,0 +1,91 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "actionmanager.h" +#include "tagnode.h" +#include "tagnodeitem.h" +#include "treenode.h" + +#include <kaction.h> +#include <kiconloader.h> + +#include <qpoint.h> +#include <qpopupmenu.h> + +namespace Akregator { + +TagNodeItem::TagNodeItem(FolderItem* parent, TagNode* node) : TreeNodeItem(parent, node) +{ + initialize(node); +} + +TagNodeItem::TagNodeItem(FolderItem* parent, TreeNodeItem* after, TagNode* node) : TreeNodeItem(parent, after, node) +{ + initialize(node); +} + +TagNodeItem::TagNodeItem(KListView* parent, TagNode* node) : TreeNodeItem(parent, node) +{ + initialize(node); +} + +TagNodeItem::TagNodeItem(KListView* parent, TreeNodeItem* after, TagNode* node) : TreeNodeItem(parent, after, node) +{ + initialize(node); +} + +void TagNodeItem::initialize(TagNode* node) +{ + setExpandable(false); + + if (node) + { + setText(0, node->title()); + setPixmap ( 0, KGlobal::iconLoader()->loadIcon(node->icon(), KIcon::Small) ); + } +} + +void TagNodeItem::nodeChanged() +{ + setPixmap ( 0, KGlobal::iconLoader()->loadIcon(node()->icon(), KIcon::Small)); + TreeNodeItem::nodeChanged(); +} + +TagNode* TagNodeItem::node() +{ + return static_cast<TagNode*> (m_node); +} + +void TagNodeItem::showContextMenu(const QPoint& p) +{ + QWidget* w = ActionManager::getInstance()->container("tagnode_popup"); + if (w) + static_cast<QPopupMenu *>(w)->exec(p); +} + +TagNodeItem::~TagNodeItem() +{} + +} // namespace Akregator + diff --git a/akregator/src/tagnodeitem.h b/akregator/src/tagnodeitem.h new file mode 100644 index 000000000..95a995c45 --- /dev/null +++ b/akregator/src/tagnodeitem.h @@ -0,0 +1,56 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef AKREGATOR_TAGNODEITEM_H +#define AKREGATOR_TAGNODEITEM_H + +#include "treenodeitem.h" +#include "tagnode.h" + +namespace Akregator +{ + +class Folder; +class FolderItem; + +class TagNodeItem : public TreeNodeItem +{ + +public: + TagNodeItem(FolderItem* parent, TagNode* node); + TagNodeItem(FolderItem* parent, TreeNodeItem* after, TagNode* node); + TagNodeItem(KListView* parent, TagNode* node); + TagNodeItem(KListView* parent, TreeNodeItem* after, TagNode* node); + virtual ~TagNodeItem(); + virtual void nodeChanged(); + + virtual TagNode* node(); + virtual void showContextMenu(const QPoint& p); + +private: + void initialize(TagNode* node); +}; + +} + +#endif diff --git a/akregator/src/tagnodelist.cpp b/akregator/src/tagnodelist.cpp new file mode 100644 index 000000000..5a2822bbb --- /dev/null +++ b/akregator/src/tagnodelist.cpp @@ -0,0 +1,206 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "feedlist.h" +#include "tag.h" +#include "tagnode.h" +#include "tagnodelist.h" +#include "tagset.h" +#include "folder.h" +#include "tagfolder.h" + +#include <qdom.h> +#include <qmap.h> +#include <qstring.h> +#include <qvaluelist.h> + +#include <kapplication.h> +#include <klocale.h> + +namespace Akregator { + +class TagNodeList::TagNodeListPrivate +{ + public: + FeedList* feedList; + TagSet* tagSet; + QMap<QString, TagNode*> tagIdToNodeMap; +}; + +FeedList* TagNodeList::feedList() const +{ + return d->feedList; +} + +TagNodeList::TagNodeList(FeedList* feedList, TagSet* tagSet) : NodeList(), d(new TagNodeListPrivate) +{ + d->feedList = feedList; + d->tagSet = tagSet; + + connect(d->tagSet, SIGNAL(signalTagAdded(const Tag&)), this, SLOT(slotTagAdded(const Tag&))); + connect(d->tagSet, SIGNAL(signalTagRemoved(const Tag&)), this, SLOT(slotTagRemoved(const Tag&))); + connect(d->tagSet, SIGNAL(signalTagUpdated(const Tag&)), this, SLOT(slotTagUpdated(const Tag&))); + + setRootNode(new TagFolder(i18n("My Tags"))); + + QValueList<Tag> list = tagSet->toMap().values(); + for (QValueList<Tag>::ConstIterator it = list.begin(); it != list.end(); ++it) + { + insert(new TagNode(*it, d->feedList->rootNode())); + } +} + +TagNodeList::~TagNodeList() +{ + emit signalDestroyed(this); + delete d; + d = 0; +} + +TagFolder* TagNodeList::rootNode() const +{ + return static_cast<TagFolder*>(NodeList::rootNode()); +} + +TagNode* TagNodeList::findByTagID(const QString& tagID) +{ + return d->tagIdToNodeMap[tagID]; +} + +bool TagNodeList::insert(TagNode* tagNode) +{ + tagNode->setId(KApplication::random()); + QString id = tagNode->tag().id(); + if (!containsTagId(id)) + { + rootNode()->appendChild(tagNode); // TODO: maintain sorting + d->tagIdToNodeMap[id] = tagNode; + emit signalTagNodeAdded(tagNode); + return true; + } + return false; +} + +bool TagNodeList::remove(TagNode* tagNode) +{ + QString id = tagNode->tag().id(); + if (containsTagId(id)) + { + rootNode()->removeChild(tagNode); + d->tagIdToNodeMap.remove(id); + emit signalTagNodeRemoved(tagNode); + return true; + } + return false; +} + +void TagNodeList::slotNodeDestroyed(TreeNode* node) +{ + TagNode* tagNode = dynamic_cast<TagNode*>(node); + QString id = tagNode ? tagNode->tag().id() : QString::null; + + if (tagNode != 0 && containsTagId(id)) + { + rootNode()->removeChild(tagNode); + d->tagIdToNodeMap.remove(id); + emit signalTagNodeRemoved(tagNode); + } +} + +void TagNodeList::slotNodeAdded(TreeNode* node) +{ + NodeList::slotNodeAdded(node); + + TagNode* tagNode = dynamic_cast<TagNode*>(node); + QString id = tagNode ? tagNode->tag().id() : QString::null; + + if (tagNode != 0L && !containsTagId(id)) + { + d->tagIdToNodeMap[id] = tagNode; + emit signalTagNodeAdded(tagNode); + } +} + +void TagNodeList::slotNodeRemoved(Folder* parent, TreeNode* node) +{ + NodeList::slotNodeRemoved(parent, node); + + TagNode* tagNode = dynamic_cast<TagNode*>(node); + QString id = tagNode ? tagNode->tag().id() : QString::null; + + if (parent == rootNode() && tagNode != 0L && containsTagId(id)) + { + d->tagIdToNodeMap.remove(id); + emit signalTagNodeRemoved(tagNode); + } +} + +bool TagNodeList::containsTagId(const QString& tagId) +{ + return d->tagIdToNodeMap.contains(tagId); +} + +QValueList<TagNode*> TagNodeList::toList() const +{ + return d->tagIdToNodeMap.values(); +} + +bool TagNodeList::readFromXML(const QDomDocument& doc) +{ + return false; // TODO +} + +QDomDocument TagNodeList::toXML() const +{ + return QDomDocument(); +} + +void TagNodeList::slotTagAdded(const Tag& tag) +{ + if (!containsTagId(tag.id())) + { + insert(new TagNode(tag, d->feedList->rootNode())); + } +} + +void TagNodeList::slotTagUpdated(const Tag& tag) +{ + if (containsTagId(tag.id())) + { + d->tagIdToNodeMap[tag.id()]->tagChanged(); + } +} +void TagNodeList::slotTagRemoved(const Tag& tag) +{ + if (containsTagId(tag.id())) + { + delete d->tagIdToNodeMap[tag.id()]; + d->tagIdToNodeMap[tag.id()] = 0; + } +} + + +} // namespace Akregator + +#include "tagnodelist.moc" diff --git a/akregator/src/tagnodelist.h b/akregator/src/tagnodelist.h new file mode 100644 index 000000000..421506db4 --- /dev/null +++ b/akregator/src/tagnodelist.h @@ -0,0 +1,92 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_TAGNODELIST_H +#define AKREGATOR_TAGNODELIST_H + +#include "tagfolder.h" + +class QDomDocument; +template <class T> class QValueList; + +namespace Akregator { + + class FeedList; + class Folder; + class TagFolder; + class TagNode; + class TreeNode; + class Tag; + class TagSet; + + + class TagNodeList : public NodeList + { + Q_OBJECT + public: + + + TagNodeList(FeedList* feedList, TagSet* tagSet); + virtual ~TagNodeList(); + + FeedList* feedList() const; + bool insert(TagNode* tagNode); + bool remove(TagNode* tagNode); + bool containsTagId(const QString& tagId); + QValueList<TagNode*> toList() const; + + TagNode* findByTagID(const QString& tagID); + + virtual bool readFromXML(const QDomDocument& doc); + virtual QDomDocument toXML() const; + + virtual TagFolder* rootNode() const; + + public slots: + + void slotTagAdded(const Tag& tag); + void slotTagUpdated(const Tag& tag); + void slotTagRemoved(const Tag& tag); + + signals: + + void signalDestroyed(TagNodeList*); + void signalTagNodeAdded(TagNode* node); + void signalTagNodeRemoved(TagNode* node); + + protected slots: + + virtual void slotNodeDestroyed(TreeNode* node); + virtual void slotNodeAdded(TreeNode* node); + virtual void slotNodeRemoved(Folder* parent, TreeNode* node); + + private: + + class TagNodeListPrivate; + TagNodeListPrivate* d; + }; + +} // namespace Akregator + +#endif diff --git a/akregator/src/tagpropertiesdialog.cpp b/akregator/src/tagpropertiesdialog.cpp new file mode 100644 index 000000000..62f432275 --- /dev/null +++ b/akregator/src/tagpropertiesdialog.cpp @@ -0,0 +1,90 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <kicondialog.h> +#include <klocale.h> + +#include <qlineedit.h> + +#include "tag.h" +#include "tagpropertiesdialog.h" +#include "tagpropertieswidgetbase.h" + +namespace Akregator { + +class TagPropertiesDialog::TagPropertiesDialogPrivate +{ + public: + Tag tag; + TagPropertiesWidgetBase* widget; +}; + +TagPropertiesDialog::TagPropertiesDialog(QWidget *parent, const char *name) : KDialogBase(KDialogBase::Swallow, Qt::WStyle_DialogBorder, parent, name, true, i18n("Tag Properties"), KDialogBase::Ok|KDialogBase::Cancel|KDialogBase::Apply), d(new TagPropertiesDialogPrivate) +{ + d->widget = new TagPropertiesWidgetBase(this); + setMainWidget(d->widget); + d->widget->le_title->setFocus(); + enableButtonOK(false); + enableButtonApply(false); + connect(d->widget->le_title, SIGNAL(textChanged(const QString&)), this, SLOT(slotTextChanged(const QString& ))); +} + +TagPropertiesDialog::~TagPropertiesDialog() +{ + delete d; + d = 0; +} + +void TagPropertiesDialog::setTag(const Tag& tag) +{ + d->tag = tag; + d->widget->le_title->setText(tag.name()); + d->widget->iconButton->setIcon(tag.icon()); + enableButtonOK(!tag.name().isEmpty()); + enableButtonApply(!tag.name().isEmpty()); +} + +void TagPropertiesDialog::slotOk() +{ + d->tag.setName(d->widget->le_title->text()); + d->tag.setIcon(d->widget->iconButton->icon()); + KDialogBase::slotOk(); +} + +void TagPropertiesDialog::slotTextChanged(const QString& text) +{ + enableButtonOK(!text.isEmpty()); + enableButtonApply(!text.isEmpty()); +} + +void TagPropertiesDialog::slotApply() +{ + d->tag.setName(d->widget->le_title->text()); + d->tag.setIcon(d->widget->iconButton->icon()); + KDialogBase::slotApply(); +} + +} // namespace Akregator + +#include "tagpropertiesdialog.moc" diff --git a/akregator/src/tagpropertiesdialog.h b/akregator/src/tagpropertiesdialog.h new file mode 100644 index 000000000..e9bcdb30c --- /dev/null +++ b/akregator/src/tagpropertiesdialog.h @@ -0,0 +1,58 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_TAGPROPERTIESDIALOG_H +#define AKREGATOR_TAGPROPERTIESDIALOG_H + +#include <kdialogbase.h> + +namespace Akregator { + +class Tag; + +class TagPropertiesDialog : public KDialogBase +{ + Q_OBJECT + + public: + + TagPropertiesDialog(QWidget *parent=0, const char *name=0); + virtual ~TagPropertiesDialog(); + + void setTag(const Tag& tag); + + protected slots: + + virtual void slotOk(); + virtual void slotApply(); + virtual void slotTextChanged(const QString& text); + + private: + class TagPropertiesDialogPrivate; + TagPropertiesDialogPrivate* d; +}; + +} // namespace Akregator + +#endif // AKREGATOR_TAGPROPERTIESDIALOG_H diff --git a/akregator/src/tagpropertieswidgetbase.ui b/akregator/src/tagpropertieswidgetbase.ui new file mode 100644 index 000000000..68bc09c10 --- /dev/null +++ b/akregator/src/tagpropertieswidgetbase.ui @@ -0,0 +1,135 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>TagPropertiesWidgetBase</class> +<author>Frank Osterfeld</author> +<widget class="QWidget"> + <property name="name"> + <cstring>Form1</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>286</width> + <height>144</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Title:</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>le_title</cstring> + </property> + </widget> + </hbox> + </widget> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>31</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget" row="1" column="0"> + <property name="name"> + <cstring>layout9</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Icon:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>iconButton</cstring> + </property> + </widget> + <widget class="KIconButton"> + <property name="name"> + <cstring>iconButton</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>40</width> + <height>40</height> + </size> + </property> + <property name="text"> + <string></string> + </property> + <property name="iconSize"> + <number>32</number> + </property> + <property name="strictIconSize"> + <bool>true</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>200</width> + <height>21</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kicondialog.h</includehint> +</includehints> +</UI> diff --git a/akregator/src/tagset.cpp b/akregator/src/tagset.cpp new file mode 100644 index 000000000..24cb09faf --- /dev/null +++ b/akregator/src/tagset.cpp @@ -0,0 +1,161 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "tag.h" +#include "tagset.h" + +#include <qdom.h> +#include <qmap.h> +#include <qstring.h> +#include <qstringlist.h> + +namespace Akregator { + +class TagSet::TagSetPrivate +{ + public: + QMap<QString,Tag> map; +}; + +TagSet::TagSet(QObject* parent) : QObject(parent), d(new TagSetPrivate) +{ +} + +TagSet::~TagSet() +{ + QValueList<Tag> tags = d->map.values(); + for (QValueList<Tag>::Iterator it = tags.begin(); it != tags.end(); ++it) + (*it).removedFromTagSet(this); + + delete d; + d = 0; +} + +void TagSet::insert(const Tag& tag) +{ + if (!d->map.contains(tag.id())) + { + d->map.insert(tag.id(), tag); + tag.addedToTagSet(this); + emit signalTagAdded(tag); + } +} + +void TagSet::remove(const Tag& tag) +{ + if (d->map.contains(tag.id())) + { + d->map.remove(tag.id()); + tag.removedFromTagSet(this); + emit signalTagRemoved(tag); + } +} + +bool TagSet::containsID(const QString& id) const +{ + return d->map.contains(id); +} + +bool TagSet::contains(const Tag& tag) const +{ + return d->map.contains(tag.id()); +} + +Tag TagSet::findByID(const QString& id) const +{ + return d->map.contains(id) ? d->map[id] : Tag(); +} + +QMap<QString,Tag> TagSet::toMap() const +{ + return d->map; +} + +void TagSet::readFromXML(const QDomDocument& doc) +{ + QDomElement root = doc.documentElement(); + + if (root.isNull()) + return; + + QDomNodeList list = root.elementsByTagName(QString::fromLatin1("tag")); + + for (uint i = 0; i < list.length(); ++i) + { + QDomElement e = list.item(i).toElement(); + if (!e.isNull()) + { + if (e.hasAttribute(QString::fromLatin1("id"))) + { + QString id = e.attribute(QString::fromLatin1("id")); + QString name = e.text(); + QString scheme = e.attribute(QString::fromLatin1("scheme")); + Tag tag(id, name, scheme); + + QString icon = e.attribute(QString::fromLatin1("icon")); + if (!icon.isEmpty()) + tag.setIcon(icon); + + insert(tag); + + } + } + } + +} +void TagSet::tagUpdated(const Tag& tag) +{ + emit signalTagUpdated(tag); +} + +QDomDocument TagSet::toXML() const +{ + QDomDocument doc; + doc.appendChild( doc.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ) ); + + QDomElement root = doc.createElement("tagSet"); + root.setAttribute( "version", "0.1" ); + doc.appendChild(root); + + QValueList<Tag> list = d->map.values(); + for (QValueList<Tag>::ConstIterator it = list.begin(); it != list.end(); ++it) + { + + QDomElement tn = doc.createElement("tag"); + + QDomText text = doc.createTextNode((*it).name()); + tn.setAttribute(QString::fromLatin1("id"),(*it).id()); + if (!(*it).scheme().isEmpty()) + tn.setAttribute(QString::fromLatin1("scheme"),(*it).scheme()); + if (!(*it).icon().isEmpty()) + tn.setAttribute(QString::fromLatin1("icon"),(*it).icon()); + tn.appendChild(text); + root.appendChild(tn); + } + return doc; +} + +} // namespace Akregator + +#include "tagset.moc" diff --git a/akregator/src/tagset.h b/akregator/src/tagset.h new file mode 100644 index 000000000..0c0a2009e --- /dev/null +++ b/akregator/src/tagset.h @@ -0,0 +1,111 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_TAGSET_H +#define AKREGATOR_TAGSET_H + +#include <qobject.h> + +class QDomDocument; +template <class K,class T> class QMap; +class QString; +class QStringList; + +namespace Akregator { + +class Tag; + +/** \brief represents a set of tags (see Tag) + In an application, there is usually one central tag set that is used. + The tag set is not necessarily managed by the application itself, it might also be part of + a desktop-wide framework (like Tenor) managing a common tag set for all applications + + @author Frank Osterfeld +*/ + +class TagSet : public QObject +{ + friend class Tag; + Q_OBJECT + public: + + TagSet(QObject* parent=0); + virtual ~TagSet(); + + /** adds a tag to the tag set. The tag set will emit signalTagAdded */ + void insert(const Tag& tag); + + /** removes a tag from the tag set. The tag set will emit signalTagRemoved */ + void remove(const Tag& tag); + + /** returns the tag set as map ((id, Tag) pairs) */ + QMap<QString,Tag> toMap() const; + + /** returns @c true if this set contains @c tag */ + bool contains(const Tag& tag) const; + + /** returns @c true if this set contains a tag with a given ID */ + bool containsID(const QString& id) const; + + /** returns the tag with the given ID if the tag is element of the set, or a null tag if not */ + Tag findByID(const QString& id) const; + + /** reads tag set from XML + see toXML() for an explanation of the format */ + void readFromXML(const QDomDocument& doc); + + /** returns an XML representation of the tag set. + The root element is @c <tagSet>, a tag ("someID", "someName") is represented as + \code <tag id="someID">someName</tag> \endcode + Full example: + \code + <?xml version="1.0" encoding="UTF-8"?> + <tagSet version="0.1" > + <tag id="http://akregator.sf.net/tags/Interesting" >Interesting</tag> + <tag id="AFs3SdaD" >Pretty boring</tag> + </tagSet> + \endcode + */ + QDomDocument toXML() const; + + signals: + /** emitted when a tag was added to this tag set */ + void signalTagAdded(const Tag&); + /** emitted when a tag was removed from this set */ + void signalTagRemoved(const Tag&); + /** emitted when a tag in this set was changed (e.g. renamed) */ + void signalTagUpdated(const Tag&); + + protected: + /** called by the tag (Tag is friend class) after a change */ + void tagUpdated(const Tag& tag); + + private: + class TagSetPrivate; + TagSetPrivate* d; +}; + +} // namespace Akregator + +#endif // AKREGATOR_TAGSET_H diff --git a/akregator/src/trayicon.cpp b/akregator/src/trayicon.cpp new file mode 100644 index 000000000..4b4f3153b --- /dev/null +++ b/akregator/src/trayicon.cpp @@ -0,0 +1,192 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "akregatorconfig.h" +#include "trayicon.h" + +#include <kapplication.h> +#include <kwin.h> +#include <kiconeffect.h> +#include <kdebug.h> +#include <klocale.h> +#include <kglobalsettings.h> +#include <dcopclient.h> +#include <dcopref.h> +#include <kpopupmenu.h> +#include <kiconloader.h> + +#include <qbitmap.h> +#include <qpainter.h> +#include <qfont.h> +#include <qtooltip.h> + + +namespace Akregator { + +TrayIcon* TrayIcon::m_instance = 0; + +TrayIcon* TrayIcon::getInstance() +{ + return m_instance; +} + +void TrayIcon::setInstance(TrayIcon* trayIcon) +{ + m_instance = trayIcon; +} + + +TrayIcon::TrayIcon(QWidget *parent, const char *name) + : KSystemTray(parent, name), m_unread(0) +{ + m_defaultIcon=KSystemTray::loadIcon("akregator"); + QPixmap m_unreadIcon=KSystemTray::loadIcon("akregator_empty"); + m_lightIconImage=m_unreadIcon.convertToImage(); + KIconEffect::deSaturate(m_lightIconImage, 0.60); + setPixmap(m_defaultIcon); + QToolTip::add(this, i18n("Akregator - RSS Feed Reader")); +} + + +TrayIcon::~TrayIcon() +{} + + +void TrayIcon::mousePressEvent(QMouseEvent *e) { + if (e->button() == LeftButton) { + emit showPart(); + } + + KSystemTray::mousePressEvent(e); +} + + +QPixmap TrayIcon::takeScreenshot() const +{ + QPoint g = mapToGlobal(pos()); + int desktopWidth = kapp->desktop()->width(); + int desktopHeight = kapp->desktop()->height(); + int tw = width(); + int th = height(); + int w = desktopWidth / 4; + int h = desktopHeight / 9; + int x = g.x() + tw/2 - w/2; // Center the rectange in the systray icon + int y = g.y() + th/2 - h/2; + if (x < 0) + x = 0; // Move the rectangle to stay in the desktop limits + if (y < 0) + y = 0; + if (x + w > desktopWidth) + x = desktopWidth - w; + if (y + h > desktopHeight) + y = desktopHeight - h; + + // Grab the desktop and draw a circle arround the icon: + QPixmap shot = QPixmap::grabWindow(qt_xrootwin(), x, y, w, h); + QPainter painter(&shot); + const int MARGINS = 6; + const int WIDTH = 3; + int ax = g.x() - x - MARGINS -1; + int ay = g.y() - y - MARGINS -1; + painter.setPen( QPen(Qt::red/*KApplication::palette().active().highlight()*/, WIDTH) ); + painter.drawArc(ax, ay, tw + 2*MARGINS, th + 2*MARGINS, 0, 16*360); + painter.end(); + + // Paint the border + const int BORDER = 1; + QPixmap finalShot(w + 2*BORDER, h + 2*BORDER); + finalShot.fill(KApplication::palette().active().foreground()); + painter.begin(&finalShot); + painter.drawPixmap(BORDER, BORDER, shot); + painter.end(); + return shot; // not finalShot?? -fo +} + +void TrayIcon::slotSetUnread(int unread) +{ + if (unread==m_unread) + return; + + m_unread=unread; + + QToolTip::remove(this); + QToolTip::add(this, i18n("Akregator - 1 unread article", "Akregator - %n unread articles", unread > 0 ? unread : 0)); + + if (unread <= 0) + { + setPixmap(m_defaultIcon); + } + else + { + // from KMSystemTray + int oldW = pixmap()->size().width(); + int oldH = pixmap()->size().height(); + + QString uStr=QString::number( unread ); + QFont f=KGlobalSettings::generalFont(); + f.setBold(true); + float pointSize=f.pointSizeFloat(); + QFontMetrics fm(f); + int w=fm.width(uStr); + if( w > (oldW) ) + { + pointSize *= float(oldW) / float(w); + f.setPointSizeFloat(pointSize); + } + + QPixmap pix(oldW, oldH); + pix.fill(Qt::white); + QPainter p(&pix); + p.setFont(f); + p.setPen(Qt::blue); + p.drawText(pix.rect(), Qt::AlignCenter, uStr); + + pix.setMask(pix.createHeuristicMask()); + QImage img=pix.convertToImage(); + + // overlay + QImage overlayImg=m_lightIconImage.copy(); + KIconEffect::overlay(overlayImg, img); + + QPixmap icon; + icon.convertFromImage(overlayImg); + setPixmap(icon); + } +} + +void TrayIcon::viewButtonClicked() +{ + QWidget *p=static_cast<QWidget*>(parent()); + KWin::forceActiveWindow(p->winId()); +} + +void TrayIcon::settingsChanged() +{ + if ( Settings::showTrayIcon() ) + show(); + else + hide(); +} +} +#include "trayicon.moc" diff --git a/akregator/src/trayicon.h b/akregator/src/trayicon.h new file mode 100644 index 000000000..f294ab9b6 --- /dev/null +++ b/akregator/src/trayicon.h @@ -0,0 +1,64 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATORTRAYICON_H +#define AKREGATORTRAYICON_H + +#include <ksystemtray.h> + +#include <qimage.h> +#include <qpixmap.h> + +namespace Akregator +{ + class TrayIcon : public KSystemTray + { + Q_OBJECT + public: + static TrayIcon* getInstance(); + static void setInstance(TrayIcon* trayIcon); + + TrayIcon(QWidget *parent = 0, const char *name = 0); + ~TrayIcon(); + + QPixmap takeScreenshot() const; + virtual void mousePressEvent(QMouseEvent *); + public slots: + void settingsChanged(); + void slotSetUnread(int unread); + void viewButtonClicked(); + + signals: + void showPart(); + + private: + static TrayIcon* m_instance; + + QPixmap m_defaultIcon; + QImage m_lightIconImage; + int m_unread; + }; +} + +#endif diff --git a/akregator/src/treenode.cpp b/akregator/src/treenode.cpp new file mode 100644 index 000000000..85bf3c5ee --- /dev/null +++ b/akregator/src/treenode.cpp @@ -0,0 +1,177 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "folder.h" +#include "treenode.h" + +#include <qstring.h> +#include <qvaluelist.h> + +#include <kdebug.h> + +namespace Akregator { + +class TreeNode::TreeNodePrivate +{ + public: + + bool doNotify; + bool nodeChangeOccurred; + bool articleChangeOccurred; + QString title; + Folder* parent; + uint id; + bool signalDestroyedEmitted; +}; + +TreeNode::TreeNode() + : QObject(0, 0), d(new TreeNodePrivate) +{ + d->doNotify = true; + d->nodeChangeOccurred = false; + d->articleChangeOccurred = false; + d->title = ""; + d->parent = 0; + d->id = 0; + d->signalDestroyedEmitted = false; + +} + +void TreeNode::emitSignalDestroyed() +{ + if (!d->signalDestroyedEmitted) + { + emit signalDestroyed(this); + d->signalDestroyedEmitted = true; + } +} + +TreeNode::~TreeNode() +{ + + delete d; + d = 0; +} + +const QString& TreeNode::title() const +{ + return d->title; +} + +void TreeNode::setTitle(const QString& title) +{ + + if (d->title != title) + { + d->title = title; + nodeModified(); + } +} + +TreeNode* TreeNode::nextSibling() const +{ + if (!d->parent) + return 0; + QValueList<TreeNode*> children = d->parent->children(); + TreeNode* me = (TreeNode*)this; + + int idx = children.findIndex(me); + + return idx+1 < children.size() ? *(children.at(idx+1)) : 0L; +} + +TreeNode* TreeNode::prevSibling() const +{ + if (!d->parent) + return 0; + QValueList<TreeNode*> children = d->parent->children(); + TreeNode* me = (TreeNode*)this; + + int idx = children.findIndex(me); + return idx > 0 ? *(d->parent->children().at(idx-1)) : 0L; +} + +Folder* TreeNode::parent() const +{ + return d->parent; +} + +void TreeNode::setParent(Folder* parent) +{ + d->parent = parent; +} + +void TreeNode::setNotificationMode(bool doNotify, bool notifyOccurredChanges) +{ + if (doNotify && !d->doNotify) // turned on + { + d->doNotify = true; + if (d->nodeChangeOccurred && notifyOccurredChanges) + emit signalChanged(this); + if (d->articleChangeOccurred && notifyOccurredChanges) + doArticleNotification(); + d->nodeChangeOccurred = false; + d->articleChangeOccurred = false; + } + if (!doNotify && d->doNotify) //turned off + { + d->nodeChangeOccurred = false; + d->articleChangeOccurred = false; + d->doNotify = false; + } +} + +uint TreeNode::id() const +{ + return d->id; +} + +void TreeNode::setId(uint id) +{ + d->id = id; +} + +void TreeNode::nodeModified() +{ + if (d->doNotify) + emit signalChanged(this); + else + d->nodeChangeOccurred = true; +} + +void TreeNode::articlesModified() +{ + if (d->doNotify) + doArticleNotification(); + else + d->articleChangeOccurred = true; +} + +void TreeNode::doArticleNotification() +{ +} + +} // namespace Akregator + +#include "treenode.moc" diff --git a/akregator/src/treenode.h b/akregator/src/treenode.h new file mode 100644 index 000000000..fd25f05cb --- /dev/null +++ b/akregator/src/treenode.h @@ -0,0 +1,218 @@ + +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATORTREENODE_H +#define AKREGATORTREENODE_H + +#include <qobject.h> + +class QDomDocument; +class QDomElement; +class QString; +class QStringList; +template <class T> class QValueList; + +namespace Akregator +{ + +class TreeNodeVisitor; +class Article; +class Folder; +class FetchQueue; + + +/** + \brief Abstract base class for all kind of elements in the feed tree, like feeds and feed groups (and search folders later). + + TODO: detailed description goes here +*/ +class TreeNode : public QObject +{ +Q_OBJECT + +public: + + /** Standard constructor */ + TreeNode(); + + /** Standard destructor */ + virtual ~TreeNode(); + + virtual bool accept(TreeNodeVisitor* visitor) = 0; + + /** The unread count, returns the number of new/unread articles in the node (for groups: the accumulated count of the subtree) + @return number of new/unread articles */ + + virtual int unread() const = 0; + + + /** returns the number of total articles in the node (for groups: the accumulated count of the subtree) + @return number of articles */ + + virtual int totalCount() const = 0; + + + /** Get title of node. + @return the title of the node */ + + virtual const QString& title() const; + + + /** Sets the title of the node. + @c title should not contain entities. + @param title the title string */ + + virtual void setTitle(const QString& title); + + + /** Get the next sibling. + @return the next sibling, 0 if there is none */ + + virtual TreeNode* nextSibling() const; + + + /** Get the previous sibling. + @return the previous sibling, 0 if there is none */ + + virtual TreeNode* prevSibling() const; + + + /** Returns the parent node. + @return the parent feed group, 0 if there is none */ + + virtual Folder* parent() const; + + + /** Sets parent node; Don't call this directly, is done automatically by + insertChild-methods in @ref Folder. */ + + virtual void setParent(Folder* parent); + + + /** Returns a sequence of the articles this node contains. For feed groups, this returns a concatenated list of all articles in the sub tree. + If @c tag is not null, only articles tagged with @c tag are returned + @return sequence of articles */ + + virtual QValueList<Article> articles(const QString& tag=QString::null) = 0; + + /** returns a list of all tags occurring in this node (sub tree for folders) */ + + virtual QStringList tags() const = 0; + + /** Helps the rest of the app to decide if node should be handled as group or not. Only use where necessary, use polymorphism where possible. + @return whether the node is a feed group or not */ + + virtual bool isGroup() const = 0; + + /** exports node and child nodes to OPML (with akregator settings) + @param parent the dom element the child node will be attached to + @param document the opml document */ + + virtual QDomElement toOPML( QDomElement parent, QDomDocument document ) const = 0; + + /** + @param doNotify notification on changes on/off flag + @param notifyOccurredChanges notify changes occurred while turn off when set to true again */ + + virtual void setNotificationMode(bool doNotify, bool notifyOccurredChanges = true); + /** returns the next node in the tree. + Calling next() unless it returns 0 iterates through the tree in pre-order + */ + virtual TreeNode* next() = 0; + + /** returns the ID of this node. IDs are managed by @ref FeedList objects and must be unique within the list. Some IDs have a special meaning: + @c 0 is the default value and indicates that no ID was set + @c 1 is reserved for the "All Feeds" root node */ + + virtual uint id() const; + + /** sets the ID */ + virtual void setId(uint id); + +public slots: + + /** Deletes all expired articles in the node (depending on the expiry settings). + Works recursively for feed groups. */ + + virtual void slotDeleteExpiredArticles() = 0; + + + /** Marks all articles in this node as read. + Works recursively for feed groups. */ + + virtual void slotMarkAllArticlesAsRead() = 0; + + /** adds node to a fetch queue + @param a fetch queue + @param intervalFetchesOnly */ + + virtual void slotAddToFetchQueue(FetchQueue* queue, bool intervalFetchesOnly=false) = 0; + +signals: + + /** Emitted when this object is deleted. */ + void signalDestroyed(TreeNode*); + + /** Notification mechanism: emitted, when the node was modified and notification is enabled. A node change is renamed title, icon, unread count. Added, updated or removed articles are not notified via this signal */ + void signalChanged(TreeNode*); + + /** emitted when new articles were added to this node or any node in the subtree (for folders). Note that this has nothing to do with fetching, the article might have been moved from somewhere else in the tree into this subtree, e.g. by moving the feed the article is in. For listening to newly fetched articles, you have to register yourself at @ref ArticleInterceptorManager + @param node the node articles were added to + @param guids the guids of the articles added + */ + void signalArticlesAdded(TreeNode* node, const QValueList<Article>& guids); + + /** emitted when articles were updated */ + void signalArticlesUpdated(TreeNode*, const QValueList<Article>& guids); + + /** emitted when articles were removed from this subtree. Note that this has nothing to do with actual article deletion! The article might have moved somewhere else in the tree, e.g. if the user moved the feed */ + void signalArticlesRemoved(TreeNode*, const QValueList<Article>& guids); + +protected: + + /** call this if you modified the actual node (title, unread count). + Call this only when the _actual_ _node_ has changed, i.e. title, unread count. Don't use for article changes! + Will do notification immediately or cache it, depending on @c m_doNotify. */ + virtual void nodeModified(); + + /** call this if the articles in the node were changed. Sends signalArticlesAdded/Updated/Removed signals + Will do notification immediately or cache it, depending on @c m_doNotify. */ + virtual void articlesModified(); + + /** reimplement this in subclasses to do the actual notification + called by articlesModified + */ + virtual void doArticleNotification(); + + void emitSignalDestroyed(); + +private: + class TreeNodePrivate; + TreeNodePrivate* d; +}; + +} + +#endif diff --git a/akregator/src/treenodeitem.cpp b/akregator/src/treenodeitem.cpp new file mode 100644 index 000000000..8dd7ebdb4 --- /dev/null +++ b/akregator/src/treenodeitem.cpp @@ -0,0 +1,165 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "treenode.h" +#include "treenodeitem.h" +#include "folderitem.h" +#include <qfont.h> +#include <qheader.h> +#include <qpainter.h> +#include <qstring.h> + +#include <kstringhandler.h> + +#include <kdebug.h> + +namespace Akregator { + +TreeNodeItem::TreeNodeItem(FolderItem* parent, TreeNode* node) + : KListViewItem(parent), m_node(node) +{ + initialize(node); +} + +TreeNodeItem::TreeNodeItem(KListView* parent, TreeNode* node) + : KListViewItem(parent), m_node(node) +{ + initialize(node); +} + +TreeNodeItem::TreeNodeItem(KListView* parent, TreeNodeItem* after, TreeNode* node) : KListViewItem(parent, after), m_node(node) +{ + initialize(node); +} + +TreeNodeItem::TreeNodeItem(FolderItem* parent, TreeNodeItem* after, TreeNode* node) + : KListViewItem(parent, after), m_node(node) +{ + initialize(node); +} + +void TreeNodeItem::initialize(TreeNode* node) +{ + setRenameEnabled(0, true); + if (node) + setText(0, node->title() ); +} + +TreeNodeItem::~TreeNodeItem() +{} + +QString TreeNodeItem::toolTip() const +{ + return QString::null; +} + +TreeNode* TreeNodeItem::node() +{ + return m_node; +} + +void TreeNodeItem::nodeChanged() +{ +// kdDebug() << "enter TreeNodeItem::nodeChanged item" << text(0) << endl; + if (!node()) + return; + if (text(0) != node()->title()) + setText(0, node()->title()); +// kdDebug() << "leave TreeNodeItem::nodeChanged item" << text(0) << endl; +} + +TreeNodeItem* TreeNodeItem::firstChild() const +{ + return static_cast<TreeNodeItem*>(KListViewItem::firstChild()); +} + +TreeNodeItem* TreeNodeItem::nextSibling() const +{ + return static_cast<TreeNodeItem*>(KListViewItem::nextSibling()); +} + +FolderItem* TreeNodeItem::parent() const +{ + return static_cast<FolderItem*>(KListViewItem::parent()); +} + + +// TODO: reverse for reverse layout +void TreeNodeItem::paintCell( QPainter * p, const QColorGroup & cg, + int column, int width, int align ) + +{ + int u = node() ? node()->unread() : 0; + + if (u <= 0) + { + KListViewItem::paintCell(p,cg,column,width,align); + return; + } + + // from kfoldertree + QString oldText = text(column); + setText( column, " " ); + + // draw bg + KListViewItem::paintCell(p,cg,column,width,align); + + setText( column, oldText); + + // draw fg + QFont f = p->font(); + f.setWeight(QFont::Bold); + p->setFont(f); + + QFontMetrics fm( p->fontMetrics() ); + QListView *lv = listView(); + int x = lv ? lv->itemMargin() : 1; + int m=x; + const QPixmap *icon = pixmap( column ); + QRect br; + + if (icon) + x += icon->width() + m; + + QString txt = " (" + QString::number(u) + ")"; + int txtW=fm.width( txt ); + + if (fm.width( oldText ) + txtW + x > width) + oldText=KStringHandler::rPixelSqueeze(oldText,fm, width - txtW - x); + + p->drawText( x, 0, width-m-x, height(), align | AlignVCenter, oldText, -1, &br ); + + if ( !isSelected() ) + p->setPen( Qt::blue ); // TODO: configurable + + p->drawText( br.right(), 0, width-m-br.right(), height(), + align | AlignVCenter, txt ); + + /*if ( isSelected() ) + p->setPen( cg.highlightedText() ); + else + p->setPen( cg.text() );*/ +} + +} // namespace Akregator diff --git a/akregator/src/treenodeitem.h b/akregator/src/treenodeitem.h new file mode 100644 index 000000000..1c2d535bb --- /dev/null +++ b/akregator/src/treenodeitem.h @@ -0,0 +1,75 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATORTREENODEITEM_H +#define AKREGATORTREENODEITEM_H + +#include <klistview.h> + +class QPoint; +class QString; + +namespace Akregator { + +class FolderItem; +class TreeNode; + +/** + abstract base class for all items in the feeds tree +*/ +class TreeNodeItem : public KListViewItem +{ + +public: + + TreeNodeItem(FolderItem* parent, TreeNode* node); + TreeNodeItem(FolderItem* parent, TreeNodeItem* after, TreeNode* node); + TreeNodeItem(KListView* parent, TreeNode* node); + TreeNodeItem(KListView* parent, TreeNodeItem* after, TreeNode* node); + virtual ~TreeNodeItem(); + virtual TreeNode* node(); + + virtual void nodeChanged(); + + virtual QString toolTip() const; + virtual TreeNodeItem* firstChild() const; + virtual TreeNodeItem* nextSibling() const; + virtual FolderItem* parent() const; + + virtual void showContextMenu(const QPoint& p) = 0; + + protected: + + TreeNode* m_node; + + virtual void paintCell( QPainter * p, const QColorGroup & cg, int column, int width, int align ); + + private: + + void initialize(TreeNode* node); +}; + +} + +#endif diff --git a/akregator/src/treenodevisitor.cpp b/akregator/src/treenodevisitor.cpp new file mode 100644 index 000000000..b35d31136 --- /dev/null +++ b/akregator/src/treenodevisitor.cpp @@ -0,0 +1,35 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "treenode.h" +#include "treenodevisitor.h" + +namespace Akregator { + +bool TreeNodeVisitor::visit(TreeNode* node) +{ + return node->accept(this); +} + +} // namespace Akregator diff --git a/akregator/src/treenodevisitor.h b/akregator/src/treenodevisitor.h new file mode 100644 index 000000000..5b3149b3d --- /dev/null +++ b/akregator/src/treenodevisitor.h @@ -0,0 +1,49 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ +#ifndef AKREGATOR_TREENODEVISITOR_H +#define AKREGATOR_TREENODEVISITOR_H + +namespace Akregator +{ + +class TreeNode; +class Folder; +class Feed; +class TagNode; +class TagFolder; + +class TreeNodeVisitor +{ + public: + virtual bool visit(TreeNode* node); + virtual bool visitTreeNode(TreeNode* /*node*/) { return false; } + virtual bool visitFolder(Folder* /*node*/) { return false; } + virtual bool visitTagFolder(TagFolder* /*node*/) { return false; } + virtual bool visitFeed(Feed* /*node*/) { return false; } + virtual bool visitTagNode(TagNode* /*node*/) { return false; } +}; + +} + +#endif diff --git a/akregator/src/utils.cpp b/akregator/src/utils.cpp new file mode 100644 index 000000000..c91a44976 --- /dev/null +++ b/akregator/src/utils.cpp @@ -0,0 +1,59 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include "utils.h" +#include <qregexp.h> +#include <qstring.h> + +namespace Akregator { + +QString Utils::stripTags(const QString& str) +{ + return QString(str).replace(QRegExp("<[^>]*>"), ""); +} + +uint Utils::calcHash(const QString& str) +{ + if (str.isNull()) // handle null string as "", prevents crash + return calcHash(""); + const char* s = str.ascii(); + uint hash = 5381; + int c; + while ( ( c = *s++ ) ) hash = ((hash << 5) + hash) + c; // hash*33 + c + return hash; +} + +QString Utils::fileNameForUrl(const QString& url_p) +{ + QString url2(url_p); + + url2 = url2.replace("/", "_").replace(":", "_"); + + if (url2.length() > 255) + url2 = url2.left(200) + QString::number(Akregator::Utils::calcHash(url2), 16); + + return url2; +} + +} diff --git a/akregator/src/utils.h b/akregator/src/utils.h new file mode 100644 index 000000000..9ee4eaaa9 --- /dev/null +++ b/akregator/src/utils.h @@ -0,0 +1,58 @@ +/* + This file is part of Akregator. + + Copyright (C) 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef AKREGATOR_UTILS_H +#define AKREGATOR_UTILS_H + +class QString; +typedef unsigned int uint; + +namespace Akregator { + +class Utils +{ + public: + /** removes HTML/XML tags (everything between < and >, that is) from a string. "<p><strong>foo</strong> bar</p>" becomes "foo bar" */ + static QString stripTags(const QString& str); + + /** taken from some website... -fo + * djb2 + * This algorithm was first reported by Dan Bernstein + * many years ago in comp.lang.c + */ + + static uint calcHash(const QString& str); + + /** + * returns a file name for a URL, with chars like "/" ":" + * replaced by "_". Too long URLs (>255 chars) are shortened and + * appended with a hash value. + * + */ + static QString fileNameForUrl(const QString& url); +}; + +} + +#endif // AKREGATOR_UTILS_H diff --git a/akregator/src/viewer.cpp b/akregator/src/viewer.cpp new file mode 100644 index 000000000..6490fb82a --- /dev/null +++ b/akregator/src/viewer.cpp @@ -0,0 +1,336 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Teemu Rytilahti <tpr@d5k.net> + 2005 Frank Osterfeld <frank.osterfeld at kdemail.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#include <kaction.h> +#include <kapplication.h> +#include <kfiledialog.h> +#include <khtmlview.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <kprocess.h> +#include <krun.h> +#include <kshell.h> +#include <kurl.h> +#include <kparts/browserextension.h> + +#include <qaccel.h> +#include <qclipboard.h> +#include <qpaintdevicemetrics.h> + +#include "viewer.h" +#include "akregator_run.h" +#include "akregatorconfig.h" + +namespace Akregator { + +Viewer::Viewer(QWidget *parent, const char *name) + : KHTMLPart(parent, name), m_url(0) +{ + setZoomFactor(100); + setMetaRefreshEnabled(true); + setDNDEnabled(true); + setAutoloadImages(true); + setStatusMessagesEnabled(true); + + // change the cursor when loading stuff... + connect( this, SIGNAL(started(KIO::Job *)), + this, SLOT(slotStarted(KIO::Job *))); + connect( this, SIGNAL(completed()), + this, SLOT(slotCompleted())); + + connect( browserExtension(), SIGNAL(popupMenu (KXMLGUIClient*, const QPoint&, const KURL&, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t)), this, SLOT(slotPopupMenu(KXMLGUIClient*, const QPoint&, const KURL&, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t))); + + KStdAction::print(this, SLOT(slotPrint()), actionCollection(), "viewer_print"); + KStdAction::copy(this, SLOT(slotCopy()), actionCollection(), "viewer_copy"); + + new KAction( i18n("&Increase Font Sizes"), "viewmag+", "Ctrl+Plus", this, SLOT(slotZoomIn()), actionCollection(), "incFontSizes" ); + new KAction( i18n("&Decrease Font Sizes"), "viewmag-", "Ctrl+Minus", this, SLOT(slotZoomOut()), actionCollection(), "decFontSizes" ); + + connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged())); + + connect( browserExtension(), SIGNAL(openURLRequestDelayed(const KURL&, const KParts::URLArgs&)), this, SLOT(slotOpenURLRequest(const KURL&, const KParts::URLArgs& )) ); + + new KAction(i18n("Copy &Link Address"), "", 0, + this, SLOT(slotCopyLinkAddress()), + actionCollection(), "copylinkaddress"); + new KAction(i18n("&Save Link As..."), "", 0, + this, SLOT(slotSaveLinkAs()), + actionCollection(), "savelinkas"); +} + +Viewer::~Viewer() +{} + +bool Viewer::closeURL() +{ + emit browserExtension()->loadingProgress(-1); + emit canceled(QString::null); + return KHTMLPart::closeURL(); +} + +int Viewer::pointsToPixel(int pointSize) const +{ + const QPaintDeviceMetrics metrics(view()); + return ( pointSize * metrics.logicalDpiY() + 36 ) / 72 ; +} + +void Viewer::displayInExternalBrowser(const KURL &url, const QString &mimetype) +{ + if (!url.isValid()) return; + if (Settings::externalBrowserUseKdeDefault()) + { + if (mimetype.isEmpty()) + kapp->invokeBrowser(url.url(), "0"); + else + KRun::runURL(url, mimetype, false, false); + } + else + { + QString cmd = Settings::externalBrowserCustomCommand(); + QString urlStr = url.url(); + cmd.replace(QRegExp("%u"), urlStr); + KProcess *proc = new KProcess; + QStringList cmdAndArgs = KShell::splitArgs(cmd); + *proc << cmdAndArgs; + proc->start(KProcess::DontCare); + delete proc; + } +} + +void Viewer::slotOpenURLRequest(const KURL& /*url*/, const KParts::URLArgs& /*args*/) +{ + +} + +void Viewer::urlSelected(const QString &url, int button, int state, const QString &_target, KParts::URLArgs args) +{ + m_url = completeURL(url); + browserExtension()->setURLArgs(args); + if (button == LeftButton) + { + switch (Settings::lMBBehaviour()) + { + case Settings::EnumLMBBehaviour::OpenInExternalBrowser: + slotOpenLinkInBrowser(); + break; + case Settings::EnumLMBBehaviour::OpenInBackground: + slotOpenLinkInBackgroundTab(); + break; + default: + slotOpenLinkInForegroundTab(); + break; + } + return; + } + else if (button == MidButton) + { + switch (Settings::mMBBehaviour()) + { + case Settings::EnumMMBBehaviour::OpenInExternalBrowser: + slotOpenLinkInBrowser(); + break; + case Settings::EnumMMBBehaviour::OpenInBackground: + slotOpenLinkInBackgroundTab(); + break; + default: + slotOpenLinkInForegroundTab(); + break; + } + return; + } + KHTMLPart::urlSelected(url,button,state,_target,args); +} + +void Viewer::slotPopupMenu(KXMLGUIClient*, const QPoint& p, const KURL& kurl, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags kpf, mode_t) +{ + const bool isLink = (kpf & (KParts::BrowserExtension::ShowNavigationItems | KParts::BrowserExtension::ShowTextSelectionItems)) == 0; + const bool isSelection = (kpf & KParts::BrowserExtension::ShowTextSelectionItems) != 0; + + QString url = kurl.url(); + + m_url = url; + KPopupMenu popup; + + if (isLink && !isSelection) + { + popup.insertItem(SmallIcon("tab_new"), i18n("Open Link in New &Tab"), this, SLOT(slotOpenLinkInForegroundTab())); + popup.insertItem(SmallIcon("window_new"), i18n("Open Link in External &Browser"), this, SLOT(slotOpenLinkInBrowser())); + popup.insertSeparator(); + action("savelinkas")->plug(&popup); + action("copylinkaddress")->plug(&popup); + } + else + { + if (isSelection) + { + action("viewer_copy")->plug(&popup); + popup.insertSeparator(); + } + action("viewer_print")->plug(&popup); + //KAction *ac = action("setEncoding"); + //if (ac) + // ac->plug(&popup); + } + popup.exec(p); +} + +// taken from KDevelop +void Viewer::slotCopy() +{ + QString text = selectedText(); + text.replace( QChar( 0xa0 ), ' ' ); + QClipboard *cb = QApplication::clipboard(); + disconnect( cb, SIGNAL( selectionChanged() ), this, SLOT( slotClearSelection() ) ); + cb->setText(text); + connect( cb, SIGNAL( selectionChanged() ), this, SLOT( slotClearSelection() ) ); +} + +void Viewer::slotCopyLinkAddress() +{ + if(m_url.isEmpty()) return; + QClipboard *cb = QApplication::clipboard(); + cb->setText(m_url.prettyURL(), QClipboard::Clipboard); + cb->setText(m_url.prettyURL(), QClipboard::Selection); +} + +void Viewer::slotSelectionChanged() +{ + action("viewer_copy")->setEnabled(!selectedText().isEmpty()); +} + +void Viewer::slotOpenLinkInternal() +{ + openURL(m_url); +} + +void Viewer::slotOpenLinkInForegroundTab() +{ + emit urlClicked(m_url, this, true, false); +} + +void Viewer::slotOpenLinkInBackgroundTab() +{ + emit urlClicked(m_url, this, true, true); +} + +void Viewer::slotOpenLinkInThisTab() +{ + emit urlClicked(m_url, this, false, false); +} + +void Viewer::slotOpenLinkInBrowser() +{ + displayInExternalBrowser(m_url, QString::null); +} + +void Viewer::slotSaveLinkAs() +{ + KURL tmp( m_url ); + + if ( tmp.fileName(false).isEmpty() ) + tmp.setFileName( "index.html" ); + KParts::BrowserRun::simpleSave(tmp, tmp.fileName()); +} + +void Viewer::slotStarted(KIO::Job *) +{ + widget()->setCursor( waitCursor ); +} + +void Viewer::slotCompleted() +{ + widget()->unsetCursor(); +} + +void Viewer::slotScrollUp() +{ + view()->scrollBy(0,-10); +} + +void Viewer::slotScrollDown() +{ + view()->scrollBy(0,10); +} + +void Viewer::slotZoomIn() +{ + int zf = zoomFactor(); + if (zf < 100) + { + zf = zf - (zf % 20) + 20; + setZoomFactor(zf); + } + else + { + zf = zf - (zf % 50) + 50; + setZoomFactor(zf < 300 ? zf : 300); + } +} + +void Viewer::slotZoomOut() +{ + int zf = zoomFactor(); + if (zf <= 100) + { + zf = zf - (zf % 20) - 20; + setZoomFactor(zf > 20 ? zf : 20); + } + else + { + zf = zf - (zf % 50) - 50; + setZoomFactor(zf); + } +} + +void Viewer::slotSetZoomFactor(int percent) +{ + setZoomFactor(percent); +} + +// some code taken from KDevelop (lib/widgets/kdevhtmlpart.cpp) +void Viewer::slotPrint( ) +{ + view()->print(); +} + + +void Viewer::setSafeMode() +{ + //setJScriptEnabled(false); + setJavaEnabled(false); + setMetaRefreshEnabled(false); + setPluginsEnabled(false); + setDNDEnabled(true); + setAutoloadImages(true); + setStatusMessagesEnabled(false); +} + +} // namespace Akregator + +#include "viewer.moc" + +// vim: set et ts=4 sts=4 sw=4: diff --git a/akregator/src/viewer.h b/akregator/src/viewer.h new file mode 100644 index 000000000..364aeb9e6 --- /dev/null +++ b/akregator/src/viewer.h @@ -0,0 +1,123 @@ +/* + This file is part of Akregator. + + Copyright (C) 2004 Teemu Rytilahti <tpr@d5k.net> + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, permission is given to link this program + with any edition of Qt, and distribute the resulting executable, + without including the source code for Qt in the source distribution. +*/ + +#ifndef VIEWER_H +#define VIEWER_H + +#include <khtml_part.h> + +class KURL; + +namespace KIO +{ + class MetaData; +} + +namespace Akregator +{ + class Viewer : public KHTMLPart + { + Q_OBJECT + public: + + /** + * Display article in external browser. + */ + static void displayInExternalBrowser(const KURL &url, const QString& mimetype=QString::null); + + Viewer(QWidget* parent, const char* name); + virtual ~Viewer(); + + virtual bool closeURL(); + + + public slots: + + void slotScrollUp(); + void slotScrollDown(); + void slotZoomIn(); + void slotZoomOut(); + void slotSetZoomFactor(int percent); + void slotPrint(); + void setSafeMode(); + + virtual void slotPaletteOrFontChanged() = 0; + + signals: + /** This gets emitted when url gets clicked */ + void urlClicked(const KURL& url, Viewer* viewer, bool newTab, bool background); + + protected: // methods + int pointsToPixel(int points) const; + + protected slots: + + // FIXME: Sort out how things are supposed to work and clean up the following slots + + /** reimplemented to handle url selection according to the settings for LMB and MMB */ + virtual void urlSelected(const QString &url, int button, int state, const QString &_target, KParts::URLArgs args); + + /** slot for handling openURLRequestDelayed() signal from the browserextension. Handles POST requests (for forms) only, other link handling goes to urlSelected(). Does nothing in Viewer, reimplemented in PageViewer to make forms working */ + virtual void slotOpenURLRequest(const KURL& url, const KParts::URLArgs& args); + + virtual void slotPopupMenu(KXMLGUIClient*, const QPoint&, const KURL&, const KParts::URLArgs&, KParts::BrowserExtension::PopupFlags, mode_t); + + /** Copies current link to clipboard. */ + void slotCopyLinkAddress(); + + /** Copies currently selected text to clipboard */ + virtual void slotCopy(); + + /** Opens @c m_url inside this viewer */ + virtual void slotOpenLinkInternal(); + + /** Opens @c m_url in external viewer, eg. Konqueror */ + virtual void slotOpenLinkInBrowser(); + + /** Opens @c m_url in foreground tab */ + virtual void slotOpenLinkInForegroundTab(); + + /** Opens @c m_url in background tab */ + virtual void slotOpenLinkInBackgroundTab(); + + virtual void slotOpenLinkInThisTab(); + + virtual void slotSaveLinkAs(); + + /** This changes cursor to wait cursor */ + void slotStarted(KIO::Job *); + + /** This reverts cursor back to normal one */ + void slotCompleted(); + + virtual void slotSelectionChanged(); + + protected: // attributes + KURL m_url; + + }; +} + +#endif // VIEWER_H +// vim: ts=4 sw=4 et |