restable: add menu entry to save table as csv file
This commit is contained in:
parent
4318891b48
commit
9c5886c7c4
@ -32,6 +32,8 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QSplitter>
|
#include <QSplitter>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include "recoll.h"
|
#include "recoll.h"
|
||||||
#include "refcntr.h"
|
#include "refcntr.h"
|
||||||
@ -143,8 +145,9 @@ void ResTableDetailArea::createPopupMenu(const QPoint& pos)
|
|||||||
//// Data model methods
|
//// Data model methods
|
||||||
////
|
////
|
||||||
|
|
||||||
// Routines used to extract named data from an Rcl::Doc. The basic one just uses the meta map. Others
|
// Routines used to extract named data from an Rcl::Doc. The basic one
|
||||||
// (ie: the date ones) need to do a little processing
|
// 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)
|
static string gengetter(const string& fld, const Rcl::Doc& doc)
|
||||||
{
|
{
|
||||||
map<string, string>::const_iterator it = doc.meta.find(fld);
|
map<string, string>::const_iterator it = doc.meta.find(fld);
|
||||||
@ -362,6 +365,38 @@ QVariant RecollModel::data(const QModelIndex& index, int role) const
|
|||||||
return QString::fromUtf8(lr.front().c_str());
|
return QString::fromUtf8(lr.front().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RecollModel::saveAsCSV(FILE *fp)
|
||||||
|
{
|
||||||
|
if (m_source.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int cols = columnCount();
|
||||||
|
int rows = rowCount();
|
||||||
|
vector<string> tokens;
|
||||||
|
|
||||||
|
for (int col = 0; col < cols; col++) {
|
||||||
|
QString qs = headerData(col, Qt::Horizontal,Qt::DisplayRole).toString();
|
||||||
|
tokens.push_back((const char *)qs.toUtf8());
|
||||||
|
}
|
||||||
|
string csv;
|
||||||
|
stringsToCSV(tokens, csv);
|
||||||
|
fprintf(fp, "%s\n", csv.c_str());
|
||||||
|
tokens.clear();
|
||||||
|
|
||||||
|
for (int row = 0; row < rows; row++) {
|
||||||
|
Rcl::Doc doc;
|
||||||
|
if (!m_source->getDoc(row, doc)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int col = 0; col < cols; col++) {
|
||||||
|
tokens.push_back(m_getters[col](m_fields[col], doc));
|
||||||
|
}
|
||||||
|
stringsToCSV(tokens, csv);
|
||||||
|
fprintf(fp, "%s\n", csv.c_str());
|
||||||
|
tokens.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This gets called when the column headers are clicked
|
// This gets called when the column headers are clicked
|
||||||
void RecollModel::sort(int column, Qt::SortOrder order)
|
void RecollModel::sort(int column, Qt::SortOrder order)
|
||||||
{
|
{
|
||||||
@ -594,6 +629,27 @@ void ResTable::resetSource()
|
|||||||
setDocSource(RefCntr<DocSequence>());
|
setDocSource(RefCntr<DocSequence>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResTable::saveAsCSV()
|
||||||
|
{
|
||||||
|
LOGDEB(("ResTable::saveAsCSV\n"));
|
||||||
|
if (!m_model)
|
||||||
|
return;
|
||||||
|
QString s =
|
||||||
|
QFileDialog::getSaveFileName(this, //parent
|
||||||
|
tr("Save table to CSV file"),
|
||||||
|
QString::fromLocal8Bit(path_home().c_str())
|
||||||
|
);
|
||||||
|
const char *tofile = s.toLocal8Bit();
|
||||||
|
FILE *fp = fopen(tofile, "w");
|
||||||
|
if (fp == 0) {
|
||||||
|
QMessageBox::warning(0, "Recoll",
|
||||||
|
tr("Can't open/create file: ") + s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_model->saveAsCSV(fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
// This is called when the sort order is changed from another widget
|
// This is called when the sort order is changed from another widget
|
||||||
void ResTable::onSortDataChanged(DocSeqSortSpec spec)
|
void ResTable::onSortDataChanged(DocSeqSortSpec spec)
|
||||||
{
|
{
|
||||||
@ -806,6 +862,9 @@ void ResTable::createHeaderPopupMenu(const QPoint& pos)
|
|||||||
popup->addAction(tr("&Reset sort"), this, SLOT(resetSort()));
|
popup->addAction(tr("&Reset sort"), this, SLOT(resetSort()));
|
||||||
popup->addSeparator();
|
popup->addSeparator();
|
||||||
|
|
||||||
|
popup->addAction(tr("&Save as CSV"), this, SLOT(saveAsCSV()));
|
||||||
|
popup->addSeparator();
|
||||||
|
|
||||||
popup->addAction(tr("&Delete column"), this, SLOT(deleteColumn()));
|
popup->addAction(tr("&Delete column"), this, SLOT(deleteColumn()));
|
||||||
popup->addSeparator();
|
popup->addSeparator();
|
||||||
|
|
||||||
|
|||||||
@ -42,8 +42,8 @@ public:
|
|||||||
int role = Qt::DisplayRole ) const;
|
int role = Qt::DisplayRole ) const;
|
||||||
virtual QVariant data(const QModelIndex& index,
|
virtual QVariant data(const QModelIndex& index,
|
||||||
int role = Qt::DisplayRole ) const;
|
int role = Qt::DisplayRole ) const;
|
||||||
|
virtual void saveAsCSV(FILE *fp);
|
||||||
virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
|
virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
|
||||||
|
|
||||||
// Specific methods
|
// Specific methods
|
||||||
virtual void readDocSource();
|
virtual void readDocSource();
|
||||||
virtual void setDocSource(RefCntr<DocSequence> nsource);
|
virtual void setDocSource(RefCntr<DocSequence> nsource);
|
||||||
@ -136,6 +136,7 @@ public slots:
|
|||||||
virtual void deleteColumn();
|
virtual void deleteColumn();
|
||||||
virtual void addColumn();
|
virtual void addColumn();
|
||||||
virtual void resetSort(); // Revert to natural (relevance) order
|
virtual void resetSort(); // Revert to natural (relevance) order
|
||||||
|
virtual void saveAsCSV();
|
||||||
virtual void linkWasClicked(const QUrl&);
|
virtual void linkWasClicked(const QUrl&);
|
||||||
virtual void makeRowVisible(int row);
|
virtual void makeRowVisible(int row);
|
||||||
|
|
||||||
|
|||||||
@ -320,6 +320,36 @@ template void stringsToString<list<string> >(const list<string> &, string &);
|
|||||||
template void stringsToString<vector<string> >(const vector<string> &,string &);
|
template void stringsToString<vector<string> >(const vector<string> &,string &);
|
||||||
template void stringsToString<set<string> >(const set<string> &, string &);
|
template void stringsToString<set<string> >(const set<string> &, string &);
|
||||||
|
|
||||||
|
template <class T> void stringsToCSV(const T &tokens, string &s,
|
||||||
|
char sep)
|
||||||
|
{
|
||||||
|
s.erase();
|
||||||
|
for (typename T::const_iterator it = tokens.begin();
|
||||||
|
it != tokens.end(); it++) {
|
||||||
|
bool needquotes = false;
|
||||||
|
if (it->empty() ||
|
||||||
|
it->find_first_of(string(1, sep) + "\"\n") != string::npos)
|
||||||
|
needquotes = true;
|
||||||
|
if (it != tokens.begin())
|
||||||
|
s.append(1, sep);
|
||||||
|
if (needquotes)
|
||||||
|
s.append(1, '"');
|
||||||
|
for (unsigned int i = 0; i < it->length(); i++) {
|
||||||
|
char car = it->at(i);
|
||||||
|
if (car == '"') {
|
||||||
|
s.append(2, '"');
|
||||||
|
} else {
|
||||||
|
s.append(1, car);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (needquotes)
|
||||||
|
s.append(1, '"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template void stringsToCSV<list<string> >(const list<string> &, string &, char);
|
||||||
|
template void stringsToCSV<vector<string> >(const vector<string> &,string &,
|
||||||
|
char);
|
||||||
|
|
||||||
void stringToTokens(const string& str, vector<string>& tokens,
|
void stringToTokens(const string& str, vector<string>& tokens,
|
||||||
const string& delims, bool skipinit)
|
const string& delims, bool skipinit)
|
||||||
{
|
{
|
||||||
@ -1045,7 +1075,7 @@ int main(int argc, char **argv)
|
|||||||
cerr << "[" << *it << "] ";
|
cerr << "[" << *it << "] ";
|
||||||
cerr << endl;
|
cerr << endl;
|
||||||
exit(0);
|
exit(0);
|
||||||
#elif 1
|
#elif 0
|
||||||
if (argc <=0 ) {
|
if (argc <=0 ) {
|
||||||
cerr << "Usage: smallut <dateinterval>" << endl;
|
cerr << "Usage: smallut <dateinterval>" << endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -1126,7 +1156,15 @@ int main(int argc, char **argv)
|
|||||||
in = "a: %a title: %(title) pcpc: %% %";
|
in = "a: %a title: %(title) pcpc: %% %";
|
||||||
pcSubst(in, out, substs);
|
pcSubst(in, out, substs);
|
||||||
cout << "After map clear: " << in << " => " << out << endl;
|
cout << "After map clear: " << in << " => " << out << endl;
|
||||||
|
#elif 1
|
||||||
|
list<string> tokens;
|
||||||
|
tokens.push_back("");
|
||||||
|
tokens.push_back("a,b");
|
||||||
|
tokens.push_back("simple value");
|
||||||
|
tokens.push_back("with \"quotes\"");
|
||||||
|
string out;
|
||||||
|
stringsToCSV(tokens, out);
|
||||||
|
cout << "CSV line: [" << out << "]" << endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,6 +93,13 @@ template <class T> bool stringToStrings(const string& s, T &tokens,
|
|||||||
*/
|
*/
|
||||||
template <class T> void stringsToString(const T &tokens, string &s);
|
template <class T> void stringsToString(const T &tokens, string &s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strings to CSV string. tokens containing the separator are quoted (")
|
||||||
|
* " inside tokens is escaped as "" ([word "quote"] =>["word ""quote"""]
|
||||||
|
*/
|
||||||
|
template <class T> void stringsToCSV(const T &tokens, string &s,
|
||||||
|
char sep = ',');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split input string. No handling of quoting
|
* Split input string. No handling of quoting
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user