UniversalEntry: add z to search content of note in all notebooks

This commit is contained in:
Le Tan 2018-03-29 19:48:03 +08:00
parent b46a8f4f39
commit a2c2d57570
12 changed files with 301 additions and 64 deletions

View File

@ -25,6 +25,7 @@
#include <QStyledItemDelegate>
#include <QWebEngineView>
#include <QAction>
#include <QTreeWidgetItem>
#include "vorphanfile.h"
#include "vnote.h"
@ -1390,3 +1391,16 @@ QStringList VUtils::parseCombinedArgString(const QString &p_program)
return args;
}
const QTreeWidgetItem *VUtils::topLevelTreeItem(const QTreeWidgetItem *p_item)
{
if (!p_item) {
return NULL;
}
if (p_item->parent()) {
return p_item->parent();
} else {
return p_item;
}
}

View File

@ -19,6 +19,7 @@ class QWidget;
class QComboBox;
class QWebEngineView;
class QAction;
class QTreeWidgetItem;
#if !defined(V_ASSERT)
#define V_ASSERT(cond) ((!(cond)) ? qt_assert(#cond, __FILE__, __LINE__) : qt_noop())
@ -314,6 +315,8 @@ public:
// From QProcess code.
static QStringList parseCombinedArgString(const QString &p_program);
static const QTreeWidgetItem *topLevelTreeItem(const QTreeWidgetItem *p_item);
// Regular expression for image link.
// ![image title]( http://github.com/tamlok/vnote.jpg "alt \" text" )
// Captured texts (need to be trimmed):

View File

@ -176,11 +176,11 @@ void VListWidget::sortListWidget(QListWidget *p_list, const QVector<int> &p_sort
QSize VListWidget::sizeHint() const
{
if (count() == 0 || !m_fitContent) {
int cnt = count();
if (cnt == 0 || !m_fitContent) {
return QListWidget::sizeHint();
} else {
// Adjust size to content.
int cnt = count();
int hei = 0;
int wid = sizeHintForColumn(0) + 10;
for (int i = 0; i < cnt; ++i) {

View File

@ -32,7 +32,7 @@ public:
virtual void selectNextItem(bool p_forward) Q_DECL_OVERRIDE;
QSize sizeHint() const Q_DECL_OVERRIDE;
virtual QSize sizeHint() const Q_DECL_OVERRIDE;
// Sort @p_list according to @p_sortedIdx.
static void sortListWidget(QListWidget *p_list, const QVector<int> &p_sortedIdx);

View File

@ -3201,4 +3201,5 @@ void VMainWindow::initUniversalEntry()
VSearchUE *searchUE = new VSearchUE(this);
m_ue->registerEntry('q', searchUE, VSearchUE::Name_Notebook_AllNotebook);
m_ue->registerEntry('a', searchUE, VSearchUE::Name_FolderNote_AllNotebook);
m_ue->registerEntry('z', searchUE, VSearchUE::Content_Note_AllNotebook);
}

View File

@ -259,6 +259,16 @@ struct VSearchConfig
compileToken(p_keyword);
}
// We support some magic switch in the keyword which will suppress the specified
// options:
// \c: Case insensitive;
// \C: Case sensitive;
// \r: Turn off regular expression;
// \R: Turn on regular expression;
// \f: Turn off fuzzy search;
// \F: Turn on fuzzy search (invalid when searching content);
// \w: Turn off whole word only;
// \W: Turn on whole word only;
void compileToken(const QString &p_keyword)
{
m_token.clear();
@ -267,12 +277,48 @@ struct VSearchConfig
return;
}
// """ to input a ";
// && for AND, || for OR;
QStringList args = VUtils::parseCombinedArgString(p_keyword);
Qt::CaseSensitivity cs = m_option & VSearchConfig::CaseSensitive
? Qt::CaseSensitive : Qt::CaseInsensitive;
bool useReg = m_option & VSearchConfig::RegularExpression;
bool wwo = m_option & VSearchConfig::WholeWordOnly;
bool fuzzy = m_option & VSearchConfig::Fuzzy;
// Read magic switch from keyword.
for (int i = 0; i < args.size();) {
const QString &arg = args[i];
if (arg.size() != 2 || arg[0] != '\\') {
++i;
continue;
}
if (arg == "\\c") {
cs = Qt::CaseInsensitive;
} else if (arg == "\\C") {
cs = Qt::CaseSensitive;
} else if (arg == "\\r") {
useReg = false;
} else if (arg == "\\R") {
useReg = true;
} else if (arg == "\\f") {
fuzzy = false;
} else if (arg == "\\F") {
fuzzy = true;
} else if (arg == "\\w") {
wwo = false;
} else if (arg == "\\W") {
wwo = true;
} else {
++i;
continue;
}
args.removeAt(i);
}
m_token.m_caseSensitivity = cs;
m_contentToken.m_caseSensitivity = cs;
@ -293,10 +339,6 @@ struct VSearchConfig
}
VSearchToken::Operator op = VSearchToken::And;
// """ to input a ";
// && for AND, || for OR;
QStringList args = VUtils::parseCombinedArgString(p_keyword);
for (auto const & arg : args) {
if (arg == QStringLiteral("&&")) {
op = VSearchToken::And;

View File

@ -3,6 +3,7 @@
#include <QAction>
#include <QMenu>
#include "utils/vutils.h"
#include "utils/viconutils.h"
#include "vnote.h"
#include "vmainwindow.h"
@ -43,7 +44,7 @@ void VSearchResultTree::initActions()
m_openAct->setToolTip(tr("Open selected notes"));
connect(m_openAct, &QAction::triggered,
this, [this]() {
activateItem(topLevelItem(currentItem()));
activateItem(currentItem());
});
m_locateAct = new QAction(VIconUtils::menuIcon(":/resources/icons/locate_note.svg"),
@ -207,7 +208,7 @@ VSearchResultItem::ItemType VSearchResultTree::itemResultType(const QTreeWidgetI
const QSharedPointer<VSearchResultItem> &VSearchResultTree::itemResultData(const QTreeWidgetItem *p_item) const
{
Q_ASSERT(p_item);
const QTreeWidgetItem *topItem = topLevelItem(p_item);
const QTreeWidgetItem *topItem = VUtils::topLevelTreeItem(p_item);
int idx = topItem->data(0, Qt::UserRole).toInt();
Q_ASSERT(idx >= 0 && idx < m_data.size());
return m_data[idx];

View File

@ -39,8 +39,6 @@ private:
VSearchResultItem::ItemType itemResultType(const QTreeWidgetItem *p_item) const;
const QTreeWidgetItem *topLevelItem(const QTreeWidgetItem *p_item) const;
void activateItem(const QTreeWidgetItem *p_item) const;
const QSharedPointer<VSearchResultItem> &itemResultData(const QTreeWidgetItem *p_item) const;
@ -58,16 +56,4 @@ private:
QAction *m_addToCartAct;
};
inline const QTreeWidgetItem *VSearchResultTree::topLevelItem(const QTreeWidgetItem *p_item) const
{
if (!p_item) {
return NULL;
}
if (p_item->parent()) {
return p_item->parent();
} else {
return p_item;
}
}
#endif // VSEARCHRESULTTREE_H

View File

@ -4,10 +4,12 @@
#include <QVector>
#include "vlistwidgetdoublerows.h"
#include "vtreewidget.h"
#include "vnotebook.h"
#include "vnote.h"
#include "vsearch.h"
#include "utils/viconutils.h"
#include "utils/vutils.h"
#include "vmainwindow.h"
#include "vnotebookselector.h"
#include "vnotefile.h"
@ -20,8 +22,9 @@ VSearchUE::VSearchUE(QObject *p_parent)
: IUniversalEntry(p_parent),
m_search(NULL),
m_inSearch(false),
m_id(ID::Name_Notebook_AllNotebook),
m_listWidget(NULL),
m_id(ID::Name_Notebook_AllNotebook)
m_treeWidget(NULL)
{
}
@ -34,6 +37,9 @@ QString VSearchUE::description(int p_id) const
case ID::Name_FolderNote_AllNotebook:
return tr("Search the name of folders/notes in all notebooks");
case ID::Content_Note_AllNotebook:
return tr("Search the content of notes in all notebooks");
default:
Q_ASSERT(false);
return tr("Invalid ID %1").arg(p_id);
@ -63,8 +69,20 @@ void VSearchUE::init()
m_listWidget = new VListWidgetDoubleRows(m_widgetParent);
m_listWidget->setFitContent(true);
m_listWidget->hide();
connect(m_listWidget, &VListWidgetDoubleRows::itemActivated,
this, &VSearchUE::activateItem);
connect(m_listWidget, SIGNAL(itemActivated(QListWidgetItem *)),
this, SLOT(activateItem(QListWidgetItem *)));
m_treeWidget = new VTreeWidget(m_widgetParent);
m_treeWidget->setColumnCount(1);
m_treeWidget->setHeaderHidden(true);
m_treeWidget->setExpandsOnDoubleClick(false);
m_treeWidget->setSimpleSearchMatchFlags(m_treeWidget->getSimpleSearchMatchFlags() & ~Qt::MatchRecursive);
m_treeWidget->setFitContent(true);
m_treeWidget->hide();
connect(m_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem *, int)),
this, SLOT(activateItem(QTreeWidgetItem *, int)));
connect(m_treeWidget, &VTreeWidget::itemExpanded,
this, &VSearchUE::widgetUpdated);
}
QWidget *VSearchUE::widget(int p_id)
@ -73,10 +91,12 @@ QWidget *VSearchUE::widget(int p_id)
switch (p_id) {
case ID::Name_Notebook_AllNotebook:
V_FALLTHROUGH;
case ID::Name_FolderNote_AllNotebook:
return m_listWidget;
case ID::Content_Note_AllNotebook:
return m_treeWidget;
default:
Q_ASSERT(false);
return NULL;
@ -103,6 +123,10 @@ void VSearchUE::processCommand(int p_id, const QString &p_cmd)
searchNameOfFolderNoteInAllNotebooks(p_cmd);
break;
case ID::Content_Note_AllNotebook:
searchContentOfNoteInAllNotebooks(p_cmd);
break;
default:
Q_ASSERT(false);
break;
@ -165,6 +189,27 @@ void VSearchUE::searchNameOfFolderNoteInAllNotebooks(const QString &p_cmd)
}
}
void VSearchUE::searchContentOfNoteInAllNotebooks(const QString &p_cmd)
{
const QVector<VNotebook *> &notebooks = g_vnote->getNotebooks();
if (p_cmd.isEmpty()) {
m_inSearch = false;
emit stateUpdated(State::Success);
} else {
VSearchConfig::Option opt = VSearchConfig::NoneOption;
QSharedPointer<VSearchConfig> config(new VSearchConfig(VSearchConfig::AllNotebooks,
VSearchConfig::Content,
VSearchConfig::Note,
VSearchConfig::Internal,
opt,
p_cmd,
QString()));
m_search->setConfig(config);
QSharedPointer<VSearchResult> result = m_search->search(notebooks);
handleSearchFinished(result);
}
}
void VSearchUE::clear(int p_id)
{
Q_UNUSED(p_id);
@ -172,6 +217,7 @@ void VSearchUE::clear(int p_id)
m_data.clear();
m_listWidget->clearAll();
m_treeWidget->clearAll();
}
void VSearchUE::entryHidden(int p_id)
@ -183,11 +229,14 @@ void VSearchUE::handleSearchItemAdded(const QSharedPointer<VSearchResultItem> &p
{
switch (m_id) {
case ID::Name_Notebook_AllNotebook:
V_FALLTHROUGH;
case ID::Name_FolderNote_AllNotebook:
appendItemToList(p_item);
break;
case ID::Content_Note_AllNotebook:
appendItemToTree(p_item);
break;
default:
break;
}
@ -228,17 +277,71 @@ void VSearchUE::appendItemToList(const QSharedPointer<VSearchResultItem> &p_item
item->setData(Qt::UserRole, m_data.size() - 1);
item->setToolTip(p_item->m_path);
++itemAdded;
if (m_listWidget->currentRow() == -1) {
m_listWidget->setCurrentRow(0);
}
if (++itemAdded >= 10) {
m_listWidget->updateGeometry();
emit widgetUpdated();
} else if (itemAdded >= 20) {
itemAdded = 0;
m_listWidget->updateGeometry();
emit widgetUpdated();
}
}
void VSearchUE::appendItemToTree(const QSharedPointer<VSearchResultItem> &p_item)
{
static int itemAdded = 0;
m_data.append(p_item);
QTreeWidgetItem *item = new QTreeWidgetItem(m_treeWidget);
item->setData(0, Qt::UserRole, m_data.size() - 1);
item->setText(0, p_item->m_text.isEmpty() ? p_item->m_path : p_item->m_text);
item->setToolTip(0, p_item->m_path);
switch (p_item->m_type) {
case VSearchResultItem::Note:
item->setIcon(0, m_noteIcon);
break;
case VSearchResultItem::Folder:
item->setIcon(0, m_folderIcon);
break;
case VSearchResultItem::Notebook:
item->setIcon(0, m_notebookIcon);
break;
default:
break;
}
for (auto const & it: p_item->m_matches) {
QTreeWidgetItem *subItem = new QTreeWidgetItem(item);
QString text;
if (it.m_lineNumber > -1) {
text = QString("[%1] %2").arg(it.m_lineNumber).arg(it.m_text);
} else {
text = it.m_text;
}
subItem->setText(0, text);
subItem->setToolTip(0, it.m_text);
}
++itemAdded;
if (!m_treeWidget->currentItem()) {
m_treeWidget->setCurrentItem(item);
m_treeWidget->resizeColumnToContents(0);
m_treeWidget->updateGeometry();
emit widgetUpdated();
} else if (itemAdded >= 20) {
itemAdded = 0;
m_treeWidget->updateGeometry();
emit widgetUpdated();
}
}
void VSearchUE::handleSearchFinished(const QSharedPointer<VSearchResult> &p_result)
{
Q_ASSERT(m_inSearch);
@ -248,11 +351,13 @@ void VSearchUE::handleSearchFinished(const QSharedPointer<VSearchResult> &p_resu
IUniversalEntry::State state = State::Idle;
bool finished = true;
switch (p_result->m_state) {
case VSearchState::Busy:
qDebug() << "search is ongoing";
state = State::Busy;
return;
finished = false;
break;
case VSearchState::Success:
qDebug() << "search succeeded";
@ -273,10 +378,17 @@ void VSearchUE::handleSearchFinished(const QSharedPointer<VSearchResult> &p_resu
break;
}
m_search->clear();
m_inSearch = false;
if (finished) {
m_search->clear();
m_inSearch = false;
}
widget(m_id)->updateGeometry();
QWidget *wid = widget(m_id);
if (wid == m_treeWidget) {
m_treeWidget->resizeColumnToContents(0);
}
wid->updateGeometry();
emit widgetUpdated();
emit stateUpdated(state);
@ -302,26 +414,28 @@ const QSharedPointer<VSearchResultItem> &VSearchUE::itemResultData(const QListWi
return m_data[idx];
}
void VSearchUE::activateItem(const QListWidgetItem *p_item)
const QSharedPointer<VSearchResultItem> &VSearchUE::itemResultData(const QTreeWidgetItem *p_item) const
{
if (!p_item) {
return;
}
Q_ASSERT(p_item);
const QTreeWidgetItem *topItem = VUtils::topLevelTreeItem(p_item);
int idx = topItem->data(0, Qt::UserRole).toInt();
Q_ASSERT(idx >= 0 && idx < m_data.size());
return m_data[idx];
}
emit requestHideUniversalEntry();
const QSharedPointer<VSearchResultItem> &resItem = itemResultData(p_item);
switch (resItem->m_type) {
void VSearchUE::activateItem(const QSharedPointer<VSearchResultItem> &p_item)
{
switch (p_item->m_type) {
case VSearchResultItem::Note:
{
QStringList files(resItem->m_path);
QStringList files(p_item->m_path);
g_mainWin->openFiles(files);
break;
}
case VSearchResultItem::Folder:
{
VDirectory *dir = g_vnote->getInternalDirectory(resItem->m_path);
VDirectory *dir = g_vnote->getInternalDirectory(p_item->m_path);
if (dir) {
g_mainWin->locateDirectory(dir);
}
@ -331,7 +445,7 @@ void VSearchUE::activateItem(const QListWidgetItem *p_item)
case VSearchResultItem::Notebook:
{
VNotebook *nb = g_vnote->getNotebook(resItem->m_path);
VNotebook *nb = g_vnote->getNotebook(p_item->m_path);
if (nb) {
g_mainWin->getNotebookSelector()->locateNotebook(nb);
}
@ -344,11 +458,31 @@ void VSearchUE::activateItem(const QListWidgetItem *p_item)
}
}
void VSearchUE::activateItem(QListWidgetItem *p_item)
{
if (!p_item) {
return;
}
emit requestHideUniversalEntry();
activateItem(itemResultData(p_item));
}
void VSearchUE::activateItem(QTreeWidgetItem *p_item, int p_col)
{
Q_UNUSED(p_col);
if (!p_item) {
return;
}
emit requestHideUniversalEntry();
activateItem(itemResultData(p_item));
}
void VSearchUE::selectNextItem(int p_id, bool p_forward)
{
switch (p_id) {
case ID::Name_Notebook_AllNotebook:
V_FALLTHROUGH;
case ID::Name_FolderNote_AllNotebook:
{
// Could not use postEvent method here which will induce infinite recursion.
@ -356,6 +490,12 @@ void VSearchUE::selectNextItem(int p_id, bool p_forward)
break;
}
case ID::Content_Note_AllNotebook:
{
m_treeWidget->selectNextItem(p_forward);
break;
}
default:
Q_ASSERT(false);
}
@ -365,13 +505,18 @@ void VSearchUE::activate(int p_id)
{
switch (p_id) {
case ID::Name_Notebook_AllNotebook:
V_FALLTHROUGH;
case ID::Name_FolderNote_AllNotebook:
{
activateItem(m_listWidget->currentItem());
break;
}
case ID::Content_Note_AllNotebook:
{
activateItem(m_treeWidget->currentItem(), 0);
break;
}
default:
Q_ASSERT(false);
}

View File

@ -10,6 +10,8 @@
class VListWidgetDoubleRows;
class QListWidgetItem;
class VTreeWidget;
class QTreeWidgetItem;
// Universal Entry to list and search all the notebooks.
@ -23,7 +25,10 @@ public:
Name_Notebook_AllNotebook = 0,
// Search the name of the folder/note in all the notebooks.
Name_FolderNote_AllNotebook
Name_FolderNote_AllNotebook,
// Search content of the note in all the notebooks.
Content_Note_AllNotebook,
};
explicit VSearchUE(QObject *p_parent = nullptr);
@ -52,20 +57,30 @@ private slots:
void handleSearchFinished(const QSharedPointer<VSearchResult> &p_result);
void activateItem(QListWidgetItem *p_item);
void activateItem(QTreeWidgetItem *p_item, int p_col);
private:
void searchNameOfAllNotebooks(const QString &p_cmd);
void searchNameOfFolderNoteInAllNotebooks(const QString &p_cmd);
void searchContentOfNoteInAllNotebooks(const QString &p_cmd);
// Stop the search synchronously.
void stopSearch();
void appendItemToList(const QSharedPointer<VSearchResultItem> &p_item);
void activateItem(const QListWidgetItem *p_item);
void appendItemToTree(const QSharedPointer<VSearchResultItem> &p_item);
void activateItem(const QSharedPointer<VSearchResultItem> &p_item);
const QSharedPointer<VSearchResultItem> &itemResultData(const QListWidgetItem *p_item) const;
const QSharedPointer<VSearchResultItem> &itemResultData(const QTreeWidgetItem *p_item) const;
VSearch *m_search;
bool m_inSearch;
@ -80,6 +95,8 @@ private:
QIcon m_notebookIcon;
VListWidgetDoubleRows *m_listWidget;
VTreeWidget *m_treeWidget;
};
#endif // VSEARCHUE_H

View File

@ -18,7 +18,8 @@
VTreeWidget::VTreeWidget(QWidget *p_parent)
: QTreeWidget(p_parent),
ISimpleSearch()
ISimpleSearch(),
m_fitContent(false)
{
setAttribute(Qt::WA_MacShowFocusRect, false);
@ -45,6 +46,13 @@ VTreeWidget::VTreeWidget(QWidget *p_parent)
m_delegate = new VStyledItemDelegate(this);
setItemDelegate(m_delegate);
connect(this, &VTreeWidget::itemExpanded,
this, [this]() {
if (m_fitContent) {
resizeColumnToContents(0);
}
});
}
void VTreeWidget::keyPressEvent(QKeyEvent *p_event)
@ -288,26 +296,32 @@ void VTreeWidget::selectNextItem(bool p_forward)
return;
}
QTreeWidgetItem *nextItem = NULL;
QTreeWidgetItem *nItem = nextItem(item, p_forward);
if (nItem) {
setCurrentItem(nItem);
}
}
QTreeWidgetItem *VTreeWidget::nextItem(QTreeWidgetItem *p_item, bool p_forward)
{
QTreeWidgetItem *nItem = NULL;
if (p_forward) {
if (item->isExpanded()) {
nextItem = item->child(0);
if (p_item->isExpanded()) {
nItem = p_item->child(0);
} else {
while (!nextItem && item) {
nextItem = nextSibling(item, true);
item = item->parent();
while (!nItem && p_item) {
nItem = nextSibling(p_item, true);
p_item = p_item->parent();
}
}
} else {
nextItem = nextSibling(item, false);
if (!nextItem) {
nextItem = item->parent();
nItem = nextSibling(p_item, false);
if (!nItem) {
nItem = p_item->parent();
} else {
nextItem = lastItemOfTree(nextItem);
nItem = lastItemOfTree(nItem);
}
}
if (nextItem) {
setCurrentItem(nextItem);
}
return nItem;
}

View File

@ -39,6 +39,8 @@ public:
virtual void selectNextItem(bool p_forward) Q_DECL_OVERRIDE;
void setFitContent(bool p_enabled);
protected:
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
@ -63,11 +65,16 @@ private:
QTreeWidgetItem *nextSibling(QTreeWidgetItem *p_item, bool p_forward);
// Next visible item.
QTreeWidgetItem *nextItem(QTreeWidgetItem *p_item, bool p_forward);
VSimpleSearchInput *m_searchInput;
VStyledItemDelegate *m_delegate;
QTimer *m_searchColdTimer;
bool m_fitContent;
};
inline void VTreeWidget::setSimpleSearchMatchFlags(Qt::MatchFlags p_flags)
@ -79,4 +86,11 @@ inline Qt::MatchFlags VTreeWidget::getSimpleSearchMatchFlags() const
{
return m_searchInput->getMatchFlags();
}
inline void VTreeWidget::setFitContent(bool p_enabled)
{
m_fitContent = p_enabled;
setSizeAdjustPolicy(m_fitContent ? QAbstractScrollArea::AdjustToContents
: QAbstractScrollArea::AdjustIgnored);
}
#endif // VTREEWIDGET_H