diff --git a/src/qtgui/guiutils.cpp b/src/qtgui/guiutils.cpp
index 9cece549..86127d87 100644
--- a/src/qtgui/guiutils.cpp
+++ b/src/qtgui/guiutils.cpp
@@ -56,17 +56,6 @@ const char *v114reslistformat = "
"
"%A %K";
-bool getStemLangs(list& langs)
-{
- string reason;
- if (!maybeOpenDb(reason)) {
- LOGERR(("getStemLangs: %s\n", reason.c_str()));
- return false;
- }
- langs = rcldb->getStemLangs();
- return true;
-}
-
// The global preferences structure
PrefsPack prefs;
@@ -77,8 +66,8 @@ PrefsPack prefs;
if (writing) { \
settings.setValue(nm , var); \
} else { \
- var = settings.value(nm, def).to##tp \
- (); \
+ var = settings.value(nm, def).to##tp \
+ (); \
}
/**
diff --git a/src/qtgui/guiutils.h b/src/qtgui/guiutils.h
index 7742ee0c..6fd42652 100644
--- a/src/qtgui/guiutils.h
+++ b/src/qtgui/guiutils.h
@@ -49,13 +49,6 @@ using std::list;
using std::vector;
#endif
-/** Retrieve configured stemming languages */
-bool getStemLangs(list& langs);
-
-/** Start a browser on the help document */
-extern bool startHelpBrowser(const string& url = "");
-
-
/** Holder for preferences (gets saved to user Qt prefs) */
class PrefsPack {
public:
diff --git a/src/qtgui/main.cpp b/src/qtgui/main.cpp
index be0155cf..b05ac7d7 100644
--- a/src/qtgui/main.cpp
+++ b/src/qtgui/main.cpp
@@ -96,6 +96,17 @@ bool maybeOpenDb(string &reason, bool force)
return true;
}
+bool getStemLangs(list& langs)
+{
+ string reason;
+ if (!maybeOpenDb(reason)) {
+ LOGERR(("getStemLangs: %s\n", reason.c_str()));
+ return false;
+ }
+ langs = rcldb->getStemLangs();
+ return true;
+}
+
static void recollCleanup()
{
LOGDEB(("recollCleanup: writing settings\n"));
diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp
index f62727f3..f02b9bd6 100644
--- a/src/qtgui/rclmain_w.cpp
+++ b/src/qtgui/rclmain_w.cpp
@@ -456,7 +456,7 @@ void RclMain::fileExit()
prefs.mainwidth = width();
prefs.mainheight = height();
}
- restable->saveSizeState();
+ restable->saveColState();
prefs.ssearchTyp = sSearch->searchTypCMB->currentIndex();
if (asearchform)
@@ -923,8 +923,8 @@ void RclMain::saveDocToFile(Rcl::Doc doc)
{
QString s =
QFileDialog::getSaveFileName(this, //parent
- tr("Save file"), // caption
- QString::fromLocal8Bit(path_home().c_str()) //dir
+ tr("Save file"),
+ QString::fromLocal8Bit(path_home().c_str())
);
string tofile((const char *)s.toLocal8Bit());
TempFile temp; // not used
@@ -1295,7 +1295,9 @@ void RclMain::showQueryDetails()
if (m_source.isNull())
return;
string oq = breakIntoLines(m_source->getDescription(), 100, 50);
- QString desc = tr("Query details") + ": " + QString::fromUtf8(oq.c_str());
+ QString str;
+ QString desc = tr("Result count (est.)") + ": " + str.setNum(m_source->getResCnt()) + "
";
+ desc += tr("Query details") + ": " + QString::fromUtf8(oq.c_str());
QMessageBox::information(this, tr("Query details"), desc);
}
diff --git a/src/qtgui/rclmain_w.h b/src/qtgui/rclmain_w.h
index ebac3c36..854029ca 100644
--- a/src/qtgui/rclmain_w.h
+++ b/src/qtgui/rclmain_w.h
@@ -78,7 +78,7 @@ public slots:
virtual void enableNextPage(bool);
virtual void enablePrevPage(bool);
virtual void docExpand(Rcl::Doc);
- virtual void startPreview(int doc, Rcl::Doc doc, int keymods);
+ virtual void startPreview(int docnum, Rcl::Doc doc, int keymods);
virtual void startPreview(Rcl::Doc);
virtual void startNativeViewer(Rcl::Doc);
virtual void saveDocToFile(Rcl::Doc);
@@ -87,9 +87,7 @@ public slots:
virtual void previewExposed(Preview *, int sid, int docnum);
virtual void resetSearch();
virtual void eraseDocHistory();
- // Callback for setting the stemming language through the prefs menu
virtual void setStemLang(QAction *id);
- // Prefs menu about to show, set the checked lang entry
virtual void adjustPrefsMenu();
virtual void catgFilter(int);
virtual void initDbOpen();
diff --git a/src/qtgui/recoll.h b/src/qtgui/recoll.h
index 89b7b9e8..3a75d88b 100644
--- a/src/qtgui/recoll.h
+++ b/src/qtgui/recoll.h
@@ -29,6 +29,9 @@
// Open the database if needed. We now force a close/open by default
extern bool maybeOpenDb(std::string &reason, bool force = true);
+/** Retrieve configured stemming languages */
+bool getStemLangs(list& langs);
+
extern RclConfig *rclconfig;
extern Rcl::Db *rcldb;
extern int recollNeedsExit;
diff --git a/src/qtgui/restable.cpp b/src/qtgui/restable.cpp
index fcc8eff9..73a75da6 100644
--- a/src/qtgui/restable.cpp
+++ b/src/qtgui/restable.cpp
@@ -7,10 +7,13 @@ static char rcsid[] = "@(#$Id: reslist.cpp,v 1.52 2008-12-17 15:12:08 dockes Exp
#include
#include
+#include
+
#include
#include
#include
#include
+#include
#include "refcntr.h"
#include "docseq.h"
@@ -23,7 +26,7 @@ static char rcsid[] = "@(#$Id: reslist.cpp,v 1.52 2008-12-17 15:12:08 dockes Exp
#include "plaintorich.h"
//////////////////////////////////
-// Restable "pager". We use it to display doc details in the detail area
+// Restable "pager". We use it to display a single doc details in the detail area
///
class ResTablePager : public ResListPager {
public:
@@ -68,6 +71,9 @@ string ResTablePager::iconPath(const string& mtype)
//////////////////////////////////////////////
//// Data model methods
////
+
+// Routines used to extract named data from an Rcl::Doc. The basic one just uses the meta map. Others
+// (ie: the date ones) need to do a little processing
static string gengetter(const string& fld, const Rcl::Doc& doc)
{
map::const_iterator it = doc.meta.find(fld);
@@ -107,18 +113,60 @@ static string datetimegetter(const string&, const Rcl::Doc& doc)
return datebuf;
}
+// Static map to translate from internal column names to displayable ones
+map RecollModel::o_displayableFields =
+ create_map
+ ("abstract", QT_TR_NOOP("Abstract"))
+ ("author", QT_TR_NOOP("Author"))
+ ("dbytes", QT_TR_NOOP("Document size"))
+ ("dmtime", QT_TR_NOOP("Document date"))
+ ("fbytes", QT_TR_NOOP("File size"))
+ ("filename", QT_TR_NOOP("File name"))
+ ("fmtime", QT_TR_NOOP("File date"))
+ ("ipath", QT_TR_NOOP(" Ipath"))
+ ("keywords", QT_TR_NOOP("Keywords"))
+ ("mtype", QT_TR_NOOP("Mime type"))
+ ("origcharset", QT_TR_NOOP("Original character set"))
+ ("relevancyrating", QT_TR_NOOP("Relevancy rating"))
+ ("title", QT_TR_NOOP("Title"))
+ ("url", QT_TR_NOOP("URL"))
+ ("mtime", QT_TR_NOOP("Mtime"))
+ ("date", QT_TR_NOOP("Date"))
+ ("datetime", QT_TR_NOOP("Date and time"))
+ ;
+
+FieldGetter *RecollModel::chooseGetter(const string& field)
+{
+ if (!stringlowercmp("date", field))
+ return dategetter;
+ else if (!stringlowercmp("datetime", field))
+ return datetimegetter;
+ else
+ return gengetter;
+}
+
RecollModel::RecollModel(const QStringList fields, QObject *parent)
: QAbstractTableModel(parent)
{
+ // Add dynamic "stored" fields to the full column list. This
+ // could be protected to be done only once, but it's no real
+ // problem
+ RclConfig *config = RclConfig::getMainConfig();
+ if (config) {
+ const set& stored = config->getStoredFields();
+ for (set::const_iterator it = stored.begin();
+ it != stored.end(); it++) {
+ if (o_displayableFields.find(*it) == o_displayableFields.end()) {
+ o_displayableFields[*it] = *it;
+ }
+ }
+ }
+
+ // Construct the actual list of column names
for (QStringList::const_iterator it = fields.begin();
it != fields.end(); it++) {
m_fields.push_back((const char *)(it->toUtf8()));
- if (!stringlowercmp("date", m_fields[m_fields.size()-1]))
- m_getters.push_back(dategetter);
- else if (!stringlowercmp("datetime", m_fields[m_fields.size()-1]))
- m_getters.push_back(datetimegetter);
- else
- m_getters.push_back(gengetter);
+ m_getters.push_back(chooseGetter(m_fields[m_fields.size()-1]));
}
}
@@ -136,6 +184,12 @@ int RecollModel::columnCount(const QModelIndex&) const
return m_fields.size();
}
+void RecollModel::readDocSource()
+{
+ beginResetModel();
+ endResetModel();
+}
+
void RecollModel::setDocSource(RefCntr nsource)
{
LOGDEB(("RecollModel::setDocSource\n"));
@@ -143,16 +197,37 @@ void RecollModel::setDocSource(RefCntr nsource)
m_source = RefCntr();
else
m_source = RefCntr(new DocSource(nsource));
- beginResetModel();
- endResetModel();
+ readDocSource();
}
-bool RecollModel::getdoc(int index, Rcl::Doc &doc)
+void RecollModel::deleteColumn(int col)
{
- LOGDEB(("RecollModel::getDoc\n"));
- if (m_source.isNull())
- return false;
- return m_source->getDoc(index, doc);
+ if (col > 0 && col < int(m_fields.size())) {
+ vector::iterator it = m_fields.begin();
+ it += col;
+ m_fields.erase(it);
+ vector::iterator it1 = m_getters.begin();
+ it1 += col;
+ m_getters.erase(it1);
+ readDocSource();
+ }
+}
+
+void RecollModel::addColumn(int col, const string& field)
+{
+ LOGDEB(("AddColumn: col %d fld [%s]\n", col, field.c_str()));
+ if (col >= 0 && col < int(m_fields.size())) {
+ col++;
+ vector::iterator it = m_fields.begin();
+ vector::iterator it1 = m_getters.begin();
+ if (col) {
+ it += col;
+ it1 += col;
+ }
+ m_fields.insert(it, field);
+ m_getters.insert(it1, chooseGetter(field));
+ readDocSource();
+ }
}
QVariant RecollModel::headerData(int idx, Qt::Orientation orientation,
@@ -164,7 +239,12 @@ QVariant RecollModel::headerData(int idx, Qt::Orientation orientation,
}
if (orientation == Qt::Horizontal && role == Qt::DisplayRole &&
idx < int(m_fields.size())) {
- return QString::fromUtf8(m_fields[idx].c_str());
+ map::const_iterator it =
+ o_displayableFields.find(m_fields[idx]);
+ if (it == o_displayableFields.end())
+ return QString::fromUtf8(m_fields[idx].c_str());
+ else
+ return QString::fromUtf8(it->second.c_str());
}
return QVariant();
}
@@ -195,7 +275,7 @@ void RecollModel::sort(int column, Qt::SortOrder order)
{
LOGDEB(("RecollModel::sort(%d, %d)\n", column, int(order)));
- if (column >= 0 && column < int(m_fields.size())) {
+ if (m_source.isNotNull() && column >= 0 && column < int(m_fields.size())) {
DocSeqSortSpec spec;
spec.field = m_fields[column];
if (!stringlowercmp("date", spec.field) ||
@@ -203,12 +283,11 @@ void RecollModel::sort(int column, Qt::SortOrder order)
spec.field = "mtime";
spec.desc = order == Qt::AscendingOrder ? false : true;
m_source->setSortSpec(spec);
- setDocSource(m_source);
+ readDocSource();
emit sortDataChanged(spec);
}
}
-
///////////////////////////
// ResTable panel methods
void ResTable::init()
@@ -229,9 +308,13 @@ void ResTable::init()
}
header->setSortIndicatorShown(true);
header->setSortIndicator(-1, Qt::AscendingOrder);
+ header->setContextMenuPolicy(Qt::CustomContextMenu);
connect(header, SIGNAL(sectionResized(int,int,int)),
this, SLOT(saveColWidths()));
+ connect(header, SIGNAL(customContextMenuRequested(const QPoint&)),
+ this, SLOT(createHeaderPopupMenu(const QPoint&)));
}
+ header->setMovable(true);
header = tableView->verticalHeader();
if (header) {
@@ -257,10 +340,45 @@ void ResTable::init()
}
-void ResTable::saveSizeState()
+// This is called by rclmain_w prior to exiting
+void ResTable::saveColState()
{
QSettings settings;
settings.setValue("resTableSplitterSizes", splitter->saveState());
+
+ QHeaderView *header = tableView->horizontalHeader();
+ if (header && header->sectionsMoved()) {
+ // Remember the current column order. Walk in visual order and
+ // create new list
+ QStringList newfields;
+ vector newwidths;
+ for (int vi = 0; vi < header->count(); vi++) {
+ int li = header->logicalIndex(vi);
+ newfields.push_back(prefs.restableFields.at(li));
+ newwidths.push_back(header->sectionSize(li));
+ }
+ prefs.restableFields = newfields;
+ prefs.restableColWidths = newwidths;
+ } else {
+ const vector& vf = m_model->getFields();
+ prefs.restableFields.clear();
+ for (int i = 0; i < int(vf.size()); i++) {
+ prefs.restableFields.push_back(QString::fromUtf8(vf[i].c_str()));
+ }
+ saveColWidths();
+ }
+}
+
+void ResTable::saveColWidths()
+{
+ LOGDEB(("ResTable::saveColWidths()\n"));
+ QHeaderView *header = tableView->horizontalHeader();
+ if (!header)
+ return;
+ prefs.restableColWidths.clear();
+ for (int i = 0; i < header->count(); i++) {
+ prefs.restableColWidths.push_back(header->sectionSize(i));
+ }
}
void ResTable::onTableView_currentChanged(const QModelIndex& index)
@@ -268,12 +386,12 @@ void ResTable::onTableView_currentChanged(const QModelIndex& index)
LOGDEB(("ResTable::onTableView_currentChanged(%d, %d)\n",
index.row(), index.column()));
- if (!m_model || m_model->m_source.isNull())
+ if (!m_model || m_model->getDocSource().isNull())
return;
HiliteData hdata;
- m_model->m_source->getTerms(hdata.terms, hdata.groups, hdata.gslks);
+ m_model->getDocSource()->getTerms(hdata.terms, hdata.groups, hdata.gslks);
Rcl::Doc doc;
- if (m_model->getdoc(index.row(), doc)) {
+ if (m_model->getDocSource()->getDoc(index.row(), doc)) {
textBrowser->clear();
m_detaildocnum = index.row();
m_pager->displayDoc(index.row(), doc, hdata);
@@ -305,18 +423,6 @@ void ResTable::resetSource()
setDocSource(RefCntr());
}
-void ResTable::saveColWidths()
-{
- LOGDEB(("ResTable::saveColWidths()\n"));
- QHeaderView *header = tableView->horizontalHeader();
- if (!header)
- return;
- prefs.restableColWidths.clear();
- for (int i = 0; i < header->count(); i++) {
- prefs.restableColWidths.push_back(header->sectionSize(i));
- }
-}
-
// This is called when the sort order is changed from another widget
void ResTable::onSortDataChanged(DocSeqSortSpec)
{
@@ -328,11 +434,14 @@ void ResTable::onSortDataChanged(DocSeqSortSpec)
void ResTable::readDocSource()
{
- m_model->setDocSource(m_model->m_source);
+ m_model->readDocSource();
+ textBrowser->clear();
}
void ResTable::linkWasClicked(const QUrl &url)
{
+ if (!m_model || m_model->getDocSource().isNull())
+ return;
QString s = url.toString();
const char *ascurl = s.toAscii();
LOGDEB(("ResTable::linkWasClicked: [%s]\n", ascurl));
@@ -344,7 +453,7 @@ void ResTable::linkWasClicked(const QUrl &url)
case 'E':
{
Rcl::Doc doc;
- if (!m_model->getdoc(i, doc)) {
+ if (!m_model->getDocSource()->getDoc(i, doc)) {
LOGERR(("ResTable::linkWasClicked: can't get doc for %d\n", i));
return;
}
@@ -359,3 +468,50 @@ void ResTable::linkWasClicked(const QUrl &url)
break;// ??
}
}
+
+void ResTable::createHeaderPopupMenu(const QPoint& pos)
+{
+ LOGDEB(("ResTable::createHeaderPopupMenu(%d, %d)\n", pos.x(), pos.y()));
+ QHeaderView *header = tableView->horizontalHeader();
+ if (!header || !m_model)
+ return;
+
+ m_popcolumn = header->logicalIndexAt(pos);
+ if (m_popcolumn < 0)
+ return;
+
+ const map& allfields = m_model->getAllFields();
+ const vector& fields = m_model->getFields();
+ QMenu *popup = new QMenu(this);
+ popup->addAction(tr("&Delete column"), this, SLOT(deleteColumn()));
+ QAction *act;
+ for (map::const_iterator it = allfields.begin();
+ it != allfields.end(); it++) {
+ if (std::find(fields.begin(), fields.end(), it->first) != fields.end())
+ continue;
+ act = new QAction(tr("Add ") + tr(it->second.c_str()), popup);
+ act->setData(QString::fromUtf8(it->first.c_str()));
+ connect(act, SIGNAL(triggered(bool)), this , SLOT(addColumn()));
+ popup->addAction(act);
+ }
+ popup->popup(mapToGlobal(pos));
+}
+
+void ResTable::deleteColumn()
+{
+ if (m_model)
+ m_model->deleteColumn(m_popcolumn);
+}
+
+void ResTable::addColumn()
+{
+ if (!m_model)
+ return;
+ QAction *action = (QAction *)sender();
+ LOGDEB(("addColumn: text %s, data %s\n",
+ (const char *)action->text().toUtf8(),
+ (const char *)action->data().toString().toUtf8()
+ ));
+ string field((const char *)action->data().toString().toUtf8());
+ m_model->addColumn(m_popcolumn, field);
+}
diff --git a/src/qtgui/restable.h b/src/qtgui/restable.h
index 10340c02..75cc78af 100644
--- a/src/qtgui/restable.h
+++ b/src/qtgui/restable.h
@@ -34,19 +34,27 @@ class RecollModel : public QAbstractTableModel {
public:
RecollModel(const QStringList fields, QObject *parent = 0);
+
+ // Reimplemented methods
virtual int rowCount (const QModelIndex& = QModelIndex()) const;
virtual int columnCount(const QModelIndex& = QModelIndex()) const;
- virtual QVariant headerData (int col,
- Qt::Orientation orientation,
+ virtual QVariant headerData (int col, Qt::Orientation orientation,
int role = Qt::DisplayRole ) const;
virtual QVariant data(const QModelIndex& index,
int role = Qt::DisplayRole ) const;
virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+ // Specific methods
+ virtual void readDocSource();
virtual void setDocSource(RefCntr nsource);
- virtual bool getdoc(int index, Rcl::Doc &doc);
-
- friend class ResTable;
+ virtual RefCntr getDocSource() {return m_source;}
+ virtual void deleteColumn(int);
+ virtual const vector& getFields() {return m_fields;}
+ virtual const map& getAllFields()
+ {
+ return o_displayableFields;
+ }
+ virtual void addColumn(int, const string&);
signals:
void sortDataChanged(DocSeqSortSpec);
@@ -55,6 +63,8 @@ private:
mutable RefCntr m_source;
vector m_fields;
vector m_getters;
+ static map o_displayableFields;
+ FieldGetter* chooseGetter(const string&);
};
class ResTablePager;
@@ -75,7 +85,7 @@ public:
virtual ~ResTable() {}
virtual RecollModel *getModel() {return m_model;}
- virtual void saveSizeState();
+ virtual void saveColState();
public slots:
virtual void onTableView_currentChanged(const QModelIndex&);
@@ -86,6 +96,9 @@ public slots:
virtual void readDocSource();
virtual void onSortDataChanged(DocSeqSortSpec);
virtual void linkWasClicked(const QUrl&);
+ virtual void createHeaderPopupMenu(const QPoint&);
+ virtual void deleteColumn();
+ virtual void addColumn();
signals:
void docPreviewClicked(int, Rcl::Doc, int);
@@ -97,6 +110,7 @@ private:
RecollModel *m_model;
ResTablePager *m_pager;
int m_detaildocnum;
+ int m_popcolumn;
};
diff --git a/src/rcldb/rcldb_p.h b/src/rcldb/rcldb_p.h
index ab9524af..d07ca2d4 100644
--- a/src/rcldb/rcldb_p.h
+++ b/src/rcldb/rcldb_p.h
@@ -104,7 +104,14 @@ class Db::Native {
inline const string& docfToDatf(const string& df)
{
static const string keycap("caption");
- return df.compare(Doc::keytt) ? df : keycap;
+ static const string keydmtime("dmtime");
+ if (!df.compare(Doc::keytt)) {
+ return keycap;
+ } else if (!df.compare(Doc::keymt)) {
+ return keydmtime;
+ } else {
+ return df;
+ }
}
}
diff --git a/src/rcldb/rclquery.cpp b/src/rcldb/rclquery.cpp
index b5a84a76..e4d72aa1 100644
--- a/src/rcldb/rclquery.cpp
+++ b/src/rcldb/rclquery.cpp
@@ -19,6 +19,7 @@ static char rcsid[] = "@(#$Id: rclquery.cpp,v 1.11 2008-12-19 09:55:36 dockes Ex
#include "smallut.h"
#include "searchdata.h"
#include "rclconfig.h"
+#include "unacpp.h"
#ifndef NO_NAMESPACES
namespace Rcl {
@@ -30,10 +31,7 @@ public:
QSorter(const string& f)
: m_fld(docfToDatf(f) + "=")
{
- m_ismtime = !m_fld.compare("mtime=");
- if (m_ismtime) {
- m_fld = "dmtime=";
- }
+ m_ismtime = !m_fld.compare("dmtime=");
}
virtual std::string operator()(const Xapian::Document& xdoc) const
@@ -61,7 +59,27 @@ public:
i2 = data.find_first_of("\n\r", i1);
if (i2 == string::npos)
return string();
- return data.substr(i1, i2-i1);
+
+ // Process data for better sorting. We should actually do the
+ // unicode thing
+ // (http://unicode.org/reports/tr10/#Introduction), but just
+ // removing accents and majuscules will remove the most
+ // glaring weirdnesses (or not, depending on your national
+ // approach to collating...)
+ string term = data.substr(i1, i2-i1);
+ string sortterm;
+ // We're not even sure the term is utf8 here (ie: url)
+ if (!unacmaybefold(term, sortterm, "UTF-8", true)) {
+ sortterm = term;
+ }
+ // Also remove some common uninteresting starting characters
+ i1 = sortterm.find_first_not_of(" \t\\\"([*+,");
+ if (i1 != 0 && i1 != string::npos) {
+ sortterm = sortterm.substr(i1, sortterm.size()-i1);
+ }
+
+ LOGDEB2(("QSorter: [%s] -> [%s]\n", term.c_str(), sortterm.c_str()));
+ return sortterm;
}
private:
diff --git a/src/utils/smallut.h b/src/utils/smallut.h
index ee8f89be..431348d0 100644
--- a/src/utils/smallut.h
+++ b/src/utils/smallut.h
@@ -186,4 +186,31 @@ struct TempBuf {
#define deleteZ(X) {delete X;X = 0;}
#endif
+// Code for static initialization of an stl map. Somewhat like Boost.assign.
+// Ref: http://stackoverflow.com/questions/138600/initializing-a-static-stdmapint-int-in-c
+// Example use: map m = map_list_of (1,2) (3,4) (5,6) (7,8);
+
+template
+class create_map
+{
+private:
+ std::map m_map;
+public:
+ create_map(const T& key, const U& val)
+ {
+ m_map[key] = val;
+ }
+
+ create_map& operator()(const T& key, const U& val)
+ {
+ m_map[key] = val;
+ return *this;
+ }
+
+ operator std::map()
+ {
+ return m_map;
+ }
+};
+
#endif /* _SMALLUT_H_INCLUDED_ */