diff --git a/src/doc/user/usermanual.sgml b/src/doc/user/usermanual.sgml index 67386052..26ce2a9f 100644 --- a/src/doc/user/usermanual.sgml +++ b/src/doc/user/usermanual.sgml @@ -930,17 +930,18 @@ fvwm - The alternate result table + The result table - In &RCL; 1.15 and newer, the results can now be shown in a - spreadsheet-like display. You can switch to this presentation by + In &RCL; 1.15 and newer, the results can be displayed in + spreadsheet-like fashion. You can switch to this presentation by clicking the table-like icon in the toolbar (this is a toggle, click again to restore the list). Clicking on the column headers will allow sorting by the values in the column. You can click again to invert the order, and use the header right-click menu to reset sorting to the default - relevance order. + relevance order (you can also use the sort-by-date arrows to do + this). Both the list and the table display the same underlying results. The sort order set from the table is still active if you @@ -957,7 +958,8 @@ fvwm the row to freeze the display. The bottom area is equivalent to a classical result list paragraph, with links for starting a preview or a native application, and an equivalent - right-click menu. + right-click menu. Typing Esc (the Escape key) will + unfreeze the display. diff --git a/src/qtgui/rclmain_w.cpp b/src/qtgui/rclmain_w.cpp index 9ddfcbf6..a89cb6ad 100644 --- a/src/qtgui/rclmain_w.cpp +++ b/src/qtgui/rclmain_w.cpp @@ -268,6 +268,14 @@ void RclMain::init() this, SLOT(startNativeViewer(Rcl::Doc))); connect(restable, SIGNAL(docPreviewClicked(int, Rcl::Doc, int)), this, SLOT(startPreview(int, Rcl::Doc, int))); + connect(restable, SIGNAL(docEditClicked(Rcl::Doc)), + this, SLOT(startNativeViewer(Rcl::Doc))); + connect(restable, SIGNAL(docExpand(Rcl::Doc)), + this, SLOT(docExpand(Rcl::Doc))); + connect(restable, SIGNAL(docEditClicked(Rcl::Doc)), + this, SLOT(startNativeViewer(Rcl::Doc))); + connect(restable, SIGNAL(editRequested(Rcl::Doc)), + this, SLOT(startNativeViewer(Rcl::Doc))); connect(this, SIGNAL(docSourceChanged(RefCntr)), reslist, SLOT(setDocSource(RefCntr))); diff --git a/src/qtgui/restable.cpp b/src/qtgui/restable.cpp index aee2ab79..02710c5a 100644 --- a/src/qtgui/restable.cpp +++ b/src/qtgui/restable.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include "recoll.h" #include "refcntr.h" @@ -47,8 +49,22 @@ static const int ROWHEIGHTPAD = 2; static const int TEXTINCELLVTRANS = -4; -////////////////////////////////// -// Restable "pager". We use it to display a single doc details in the +////////////////////////////////////////////////////////////////////////////// +// Restable hiliter: to highlight search term in the table. This is actually +// the same as reslist's, could be shared. +class PlainToRichQtReslist : public PlainToRich { +public: + virtual ~PlainToRichQtReslist() {} + virtual string startMatch() { + return string(""); + } + virtual string endMatch() {return string("");} +}; +static PlainToRichQtReslist g_hiliter; + +////////////////////////////////////////////////////////////////////////// +// Restable "pager". We use it to print details for a document in the // detail area /// class ResTablePager : public ResListPager { @@ -64,28 +80,12 @@ private: ResTable *m_parent; }; -////////////////////////// -// Restable hiliter: to highlight search term in the table. This is actually -// the same as reslist's, could be shared. -class PlainToRichQtReslist : public PlainToRich { -public: - virtual ~PlainToRichQtReslist() {} - virtual string startMatch() { - return string(""); - } - virtual string endMatch() {return string("");} -}; -static PlainToRichQtReslist g_hiliter; -///////////////////////////////////// - -bool ResTablePager::append(const string& data, int docnum, const Rcl::Doc&) +bool ResTablePager::append(const string& data, int, const Rcl::Doc&) { - m_parent->textBrowser->moveCursor(QTextCursor::End, + m_parent->m_detail->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor); - m_parent->textBrowser->textCursor().insertBlock(); - m_parent->textBrowser->insertHtml(QString::fromUtf8(data.c_str())); - m_parent->m_detaildocnum = docnum; + m_parent->m_detail->textCursor().insertBlock(); + m_parent->m_detail->insertHtml(QString::fromUtf8(data.c_str())); return true; } @@ -106,7 +106,39 @@ string ResTablePager::iconPath(const string& mtype) return iconpath; } -////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +/// Detail text area methods + +ResTableDetailArea::ResTableDetailArea(ResTable* parent) + : QTextBrowser(parent), m_table(parent) +{ + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(createPopupMenu(const QPoint&))); +} + +void ResTableDetailArea::createPopupMenu(const QPoint& pos) +{ + if (!m_table || m_table->m_detaildocnum < 0) { + LOGDEB(("ResTableDetailArea::createPopupMenu: no table/detaildoc\n")); + return; + } + QMenu *popup = new QMenu(this); + popup->addAction(tr("&Preview"), m_table, SLOT(menuPreview())); + popup->addAction(tr("&Open"), m_table, SLOT(menuEdit())); + popup->addAction(tr("Copy &File Name"), m_table, SLOT(menuCopyFN())); + popup->addAction(tr("Copy &URL"), m_table, SLOT(menuCopyURL())); + if (!m_table->m_detaildoc.ipath.empty()) + popup->addAction(tr("&Write to File"), m_table, SLOT(menuSaveToFile())); + popup->addAction(tr("Find &similar documents"), m_table, SLOT(menuExpand())); + popup->addAction(tr("Preview P&arent document/folder"), + m_table, SLOT(menuPreviewParent())); + popup->addAction(tr("&Open Parent document/folder"), + m_table, SLOT(menuOpenParent())); + popup->popup(mapToGlobal(pos)); +} + +////////////////////////////////////////////////////////////////////////////// //// Data model methods //// @@ -403,6 +435,9 @@ void ResTable::init() tableView->setSelectionBehavior(QAbstractItemView::SelectRows); tableView->setSelectionMode(QAbstractItemView::SingleSelection); tableView->setItemDelegate(new ResTableDelegate(this)); + tableView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(tableView, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(createPopupMenu(const QPoint&))); QHeaderView *header = tableView->horizontalHeader(); if (header) { @@ -446,13 +481,16 @@ void ResTable::init() splitter->setSizes(sizes); } - textBrowser->setReadOnly(TRUE); - textBrowser->setUndoRedoEnabled(FALSE); - textBrowser->setOpenLinks(FALSE); + delete textBrowser; + m_detail = new ResTableDetailArea(this); + m_detail->setReadOnly(TRUE); + m_detail->setUndoRedoEnabled(FALSE); + m_detail->setOpenLinks(FALSE); // signals and slots connections - connect(textBrowser, SIGNAL(anchorClicked(const QUrl &)), + connect(m_detail, SIGNAL(anchorClicked(const QUrl &)), this, SLOT(linkWasClicked(const QUrl &))); - + splitter->addWidget(m_detail); + splitter->setOrientation(Qt::Vertical); } // This is called by rclmain_w prior to exiting @@ -494,9 +532,12 @@ void ResTable::onTableView_currentChanged(const QModelIndex& index) return; Rcl::Doc doc; if (m_model->getDocSource()->getDoc(index.row(), doc)) { - textBrowser->clear(); + m_detail->clear(); m_detaildocnum = index.row(); + m_detaildoc = doc; m_pager->displayDoc(rclconfig, index.row(), doc, m_model->m_hdata); + } else { + m_detaildocnum = -1; } } @@ -515,8 +556,9 @@ void ResTable::setDocSource(RefCntr nsource) m_model->setDocSource(nsource); if (m_pager) m_pager->setDocSource(nsource, 0); - if (textBrowser) - textBrowser->clear(); + if (m_detail) + m_detail->clear(); + m_detaildocnum = -1; } void ResTable::resetSource() @@ -569,13 +611,15 @@ void ResTable::readDocSource(bool resetPos) tableView->verticalScrollBar()->setSliderPosition(0); m_model->readDocSource(); - textBrowser->clear(); + m_detail->clear(); + m_detaildocnum = -1; } void ResTable::linkWasClicked(const QUrl &url) { - if (!m_model || m_model->getDocSource().isNull()) + if (m_detaildocnum < 0) { return; + } QString s = url.toString(); const char *ascurl = s.toAscii(); LOGDEB(("ResTable::linkWasClicked: [%s]\n", ascurl)); @@ -586,23 +630,137 @@ void ResTable::linkWasClicked(const QUrl &url) case 'P': case 'E': { - Rcl::Doc doc; - if (!m_model->getDocSource()->getDoc(i, doc)) { - LOGERR(("ResTable::linkWasClicked: can't get doc for %d\n", i)); - return; - } if (what == 'P') - emit docPreviewClicked(i, doc, 0); + emit docPreviewClicked(i, m_detaildoc, 0); else - emit docEditClicked(doc); + emit docEditClicked(m_detaildoc); } break; default: - LOGERR(("ResList::linkWasClicked: bad link [%s]\n", ascurl)); + LOGERR(("ResTable::linkWasClicked: bad link [%s]\n", ascurl)); break;// ?? } } +void ResTable::createPopupMenu(const QPoint& pos) +{ + LOGDEB(("ResTable::createPopupMenu: m_detaildocnum %d\n", m_detaildocnum)); + if (m_detaildocnum < 0) + return; + QMenu *popup = new QMenu(this); + popup->addAction(tr("&Preview"), this, SLOT(menuPreview())); + popup->addAction(tr("&Open"), this, SLOT(menuEdit())); + popup->addAction(tr("Copy &File Name"), this, SLOT(menuCopyFN())); + popup->addAction(tr("Copy &URL"), this, SLOT(menuCopyURL())); + if (m_detaildoc.ipath.empty()) + popup->addAction(tr("&Write to File"), this, SLOT(menuSaveToFile())); + popup->addAction(tr("Find &similar documents"), this, SLOT(menuExpand())); + popup->addAction(tr("Preview P&arent document/folder"), + this, SLOT(menuPreviewParent())); + popup->addAction(tr("&Open Parent document/folder"), + this, SLOT(menuOpenParent())); + popup->popup(mapToGlobal(pos)); +} + +void ResTable::menuPreview() +{ + if (m_detaildocnum < 0) + return; + emit docPreviewClicked(m_detaildocnum, m_detaildoc, 0); +} + +void ResTable::menuSaveToFile() +{ + if (m_detaildocnum < 0) + return; + emit docSaveToFileClicked(m_detaildoc); +} + +void ResTable::menuPreviewParent() +{ + if (!m_model || m_detaildocnum < 0) + return; + RefCntr source = m_model->getDocSource(); + if (source.isNull()) + return; + Rcl::Doc& doc = m_detaildoc; + Rcl::Doc pdoc; + if (source->getEnclosing(doc, pdoc)) { + emit previewRequested(pdoc); + } else { + // No parent doc: show enclosing folder with app configured for + // directories + pdoc.url = path_getfather(doc.url); + pdoc.mimetype = "application/x-fsdirectory"; + emit editRequested(pdoc); + } +} + +void ResTable::menuOpenParent() +{ + if (!m_model || m_detaildocnum < 0) + return; + RefCntr source = m_model->getDocSource(); + if (source.isNull()) + return; + Rcl::Doc& doc = m_detaildoc; + Rcl::Doc pdoc; + if (source->getEnclosing(doc, pdoc)) { + emit editRequested(pdoc); + } else { + // No parent doc: show enclosing folder with app configured for + // directories + pdoc.url = path_getfather(doc.url); + pdoc.mimetype = "application/x-fsdirectory"; + emit editRequested(pdoc); + } +} + +void ResTable::menuEdit() +{ + if (m_detaildocnum < 0) + return; + emit docEditClicked(m_detaildoc); +} + +void ResTable::menuCopyFN() +{ + if (m_detaildocnum < 0) + return; + Rcl::Doc &doc = m_detaildoc; + + // Our urls currently always begin with "file://" + // + // Problem: setText expects a QString. Passing a (const char*) + // as we used to do causes an implicit conversion from + // latin1. File are binary and the right approach would be no + // conversion, but it's probably better (less worse...) to + // make a "best effort" tentative and try to convert from the + // locale's charset than accept the default conversion. + QString qfn = QString::fromLocal8Bit(doc.url.c_str()+7); + QApplication::clipboard()->setText(qfn, QClipboard::Selection); + QApplication::clipboard()->setText(qfn, QClipboard::Clipboard); +} + +void ResTable::menuCopyURL() +{ + if (m_detaildocnum < 0) + return; + Rcl::Doc &doc = m_detaildoc; + string url = url_encode(doc.url, 7); + QApplication::clipboard()->setText(url.c_str(), + QClipboard::Selection); + QApplication::clipboard()->setText(url.c_str(), + QClipboard::Clipboard); +} + +void ResTable::menuExpand() +{ + if (m_detaildocnum < 0) + return; + emit docExpand(m_detaildoc); +} + void ResTable::createHeaderPopupMenu(const QPoint& pos) { LOGDEB(("ResTable::createHeaderPopupMenu(%d, %d)\n", pos.x(), pos.y())); diff --git a/src/qtgui/restable.h b/src/qtgui/restable.h index fdf4ec49..fe21bd93 100644 --- a/src/qtgui/restable.h +++ b/src/qtgui/restable.h @@ -78,6 +78,23 @@ private: HiliteData m_hdata; }; +class ResTable; + +// Modified textBrowser for the detail area +class ResTableDetailArea : public QTextBrowser { + Q_OBJECT; + + public: + ResTableDetailArea(ResTable* parent = 0); + + public slots: + virtual void createPopupMenu(const QPoint& pos); + +private: + ResTable *m_table; +}; + + class ResTablePager; class QUrl; @@ -88,7 +105,7 @@ class ResTable : public QWidget, public Ui::ResTable public: ResTable(QWidget* parent = 0) : QWidget(parent), - m_model(0), m_pager(0), m_detaildocnum(-1) + m_model(0), m_pager(0), m_detail(0), m_detaildocnum(-1) { setupUi(this); init(); @@ -96,7 +113,7 @@ public: virtual ~ResTable() {} virtual RecollModel *getModel() {return m_model;} - + virtual ResTableDetailArea* getDetailArea() {return m_detail;} public slots: virtual void onTableView_currentChanged(const QModelIndex&); virtual void on_tableView_entered(const QModelIndex& index); @@ -105,21 +122,39 @@ public slots: virtual void resetSource(); virtual void readDocSource(bool resetPos = true); virtual void onSortDataChanged(DocSeqSortSpec); - virtual void linkWasClicked(const QUrl&); + virtual void createPopupMenu(const QPoint& pos); + virtual void menuPreview(); + virtual void menuSaveToFile(); + virtual void menuEdit(); + virtual void menuCopyFN(); + virtual void menuCopyURL(); + virtual void menuExpand(); + virtual void menuPreviewParent(); + virtual void menuOpenParent(); virtual void createHeaderPopupMenu(const QPoint&); virtual void deleteColumn(); virtual void addColumn(); virtual void resetSort(); // Revert to natural (relevance) order + virtual void linkWasClicked(const QUrl&); + signals: void docPreviewClicked(int, Rcl::Doc, int); void docEditClicked(Rcl::Doc); + void docSaveToFileClicked(Rcl::Doc); + void previewRequested(Rcl::Doc); + void editRequested(Rcl::Doc); + void headerClicked(); + void docExpand(Rcl::Doc); friend class ResTablePager; + friend class ResTableDetailArea; private: void init(); RecollModel *m_model; ResTablePager *m_pager; + ResTableDetailArea *m_detail; int m_detaildocnum; + Rcl::Doc m_detaildoc; int m_popcolumn; };