Gui restable: add/remove columns

This commit is contained in:
Jean-Francois Dockes 2010-12-24 15:48:44 +01:00
parent 1e6fde221d
commit 3bd39d893e
11 changed files with 293 additions and 75 deletions

View File

@ -56,17 +56,6 @@ const char *v114reslistformat = "<img src=\"%I\" align=\"left\">"
"%A %K";
bool getStemLangs(list<string>& 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 \
(); \
}
/**

View File

@ -49,13 +49,6 @@ using std::list;
using std::vector;
#endif
/** Retrieve configured stemming languages */
bool getStemLangs(list<string>& 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:

View File

@ -96,6 +96,17 @@ bool maybeOpenDb(string &reason, bool force)
return true;
}
bool getStemLangs(list<string>& 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"));

View File

@ -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()) + "<br>";
desc += tr("Query details") + ": " + QString::fromUtf8(oq.c_str());
QMessageBox::information(this, tr("Query details"), desc);
}

View File

@ -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();

View File

@ -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<string>& langs);
extern RclConfig *rclconfig;
extern Rcl::Db *rcldb;
extern int recollNeedsExit;

View File

@ -7,10 +7,13 @@ static char rcsid[] = "@(#$Id: reslist.cpp,v 1.52 2008-12-17 15:12:08 dockes Exp
#include <stdlib.h>
#include <time.h>
#include <algorithm>
#include <Qt>
#include <QShortcut>
#include <QAbstractTableModel>
#include <QSettings>
#include <QMenu>
#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<string, string>::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<string, string> RecollModel::o_displayableFields =
create_map<string, string>
("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<string>& stored = config->getStoredFields();
for (set<string>::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<DocSequence> nsource)
{
LOGDEB(("RecollModel::setDocSource\n"));
@ -143,16 +197,37 @@ void RecollModel::setDocSource(RefCntr<DocSequence> nsource)
m_source = RefCntr<DocSequence>();
else
m_source = RefCntr<DocSequence>(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<string>::iterator it = m_fields.begin();
it += col;
m_fields.erase(it);
vector<FieldGetter*>::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<string>::iterator it = m_fields.begin();
vector<FieldGetter*>::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<string, string>::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<int> 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<string>& 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<DocSequence>());
}
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<string, string>& allfields = m_model->getAllFields();
const vector<string>& fields = m_model->getFields();
QMenu *popup = new QMenu(this);
popup->addAction(tr("&Delete column"), this, SLOT(deleteColumn()));
QAction *act;
for (map<string, string>::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);
}

View File

@ -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<DocSequence> nsource);
virtual bool getdoc(int index, Rcl::Doc &doc);
friend class ResTable;
virtual RefCntr<DocSequence> getDocSource() {return m_source;}
virtual void deleteColumn(int);
virtual const vector<string>& getFields() {return m_fields;}
virtual const map<string, string>& getAllFields()
{
return o_displayableFields;
}
virtual void addColumn(int, const string&);
signals:
void sortDataChanged(DocSeqSortSpec);
@ -55,6 +63,8 @@ private:
mutable RefCntr<DocSequence> m_source;
vector<string> m_fields;
vector<FieldGetter*> m_getters;
static map<string, string> 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;
};

View File

@ -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;
}
}
}

View File

@ -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:

View File

@ -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<int, int> m = map_list_of (1,2) (3,4) (5,6) (7,8);
template <typename T, typename U>
class create_map
{
private:
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
#endif /* _SMALLUT_H_INCLUDED_ */