From 13c2d143bbae017e0f33d7edcdf267c9058f0c56 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Sat, 16 Jun 2018 09:01:34 +0800 Subject: [PATCH] TagExplorer: add explorer for tags --- src/resources/icons/tag.svg | 11 + src/resources/icons/tag_explorer.svg | 12 + src/src.pro | 6 +- src/vconfigmanager.h | 16 + src/vhistorylist.cpp | 2 +- src/vhistorylist.h | 2 +- src/vmainwindow.cpp | 17 +- src/vmainwindow.h | 3 + src/vnote.qrc | 2 + src/vnotebook.cpp | 15 + src/vnotebook.h | 2 + src/vtagexplorer.cpp | 497 +++++++++++++++++++++++++++ src/vtagexplorer.h | 113 ++++++ 13 files changed, 690 insertions(+), 8 deletions(-) create mode 100644 src/resources/icons/tag.svg create mode 100644 src/resources/icons/tag_explorer.svg create mode 100644 src/vtagexplorer.cpp create mode 100644 src/vtagexplorer.h diff --git a/src/resources/icons/tag.svg b/src/resources/icons/tag.svg new file mode 100644 index 00000000..743e07a3 --- /dev/null +++ b/src/resources/icons/tag.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/src/resources/icons/tag_explorer.svg b/src/resources/icons/tag_explorer.svg new file mode 100644 index 00000000..05fa0e67 --- /dev/null +++ b/src/resources/icons/tag_explorer.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/src/src.pro b/src/src.pro index e7264796..c2e8159e 100644 --- a/src/src.pro +++ b/src/src.pro @@ -139,7 +139,8 @@ SOURCES += main.cpp\ utils/vprocessutils.cpp \ vtagpanel.cpp \ valltagspanel.cpp \ - vtaglabel.cpp + vtaglabel.cpp \ + vtagexplorer.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -272,7 +273,8 @@ HEADERS += vmainwindow.h \ utils/vprocessutils.h \ vtagpanel.h \ valltagspanel.h \ - vtaglabel.h + vtaglabel.h \ + vtagexplorer.h RESOURCES += \ vnote.qrc \ diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index 116cd9d4..df79b8e3 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -194,6 +194,9 @@ public: const QByteArray getNotebookSplitterState() const; void setNotebookSplitterState(const QByteArray &p_state); + const QByteArray getTagExplorerSplitterState() const; + void setTagExplorerSplitterState(const QByteArray &p_state); + bool getFindCaseSensitive() const; void setFindCaseSensitive(bool p_enabled); @@ -1304,6 +1307,19 @@ inline void VConfigManager::setNotebookSplitterState(const QByteArray &p_state) p_state); } +inline const QByteArray VConfigManager::getTagExplorerSplitterState() const +{ + return getConfigFromSessionSettings("geometry", + "tag_explorer_splitter_state").toByteArray(); +} + +inline void VConfigManager::setTagExplorerSplitterState(const QByteArray &p_state) +{ + setConfigToSessionSettings("geometry", + "tag_explorer_splitter_state", + p_state); +} + inline bool VConfigManager::getFindCaseSensitive() const { return m_findCaseSensitive; diff --git a/src/vhistorylist.cpp b/src/vhistorylist.cpp index fd1ca7d0..df155292 100644 --- a/src/vhistorylist.cpp +++ b/src/vhistorylist.cpp @@ -460,7 +460,7 @@ void VHistoryList::openItem(const QListWidgetItem *p_item) const g_mainWin->openFiles(files); } -void VHistoryList::locateCurrentItem() +void VHistoryList::locateCurrentItem() const { auto item = m_itemList->currentItem(); if (!item) { diff --git a/src/vhistorylist.h b/src/vhistorylist.h index 4dc1d013..480973a3 100644 --- a/src/vhistorylist.h +++ b/src/vhistorylist.h @@ -48,7 +48,7 @@ private slots: void unpinSelectedItems(); - void locateCurrentItem(); + void locateCurrentItem() const; // Add selected files to Cart. void addFileToCart() const; diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 552e589b..58a72b67 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -48,6 +48,7 @@ #include "vhistorylist.h" #include "vexplorer.h" #include "vlistue.h" +#include "vtagexplorer.h" extern VConfigManager *g_config; @@ -291,6 +292,13 @@ void VMainWindow::setupNaviBox() m_naviBox->addItem(m_explorer, ":/resources/icons/explorer.svg", tr("Explorer")); + + m_tagExplorer = new VTagExplorer(); + m_naviBox->addItem(m_tagExplorer, + ":/resources/icons/tag_explorer.svg", + tr("Tags")); + connect(m_notebookSelector, &VNotebookSelector::curNotebookChanged, + m_tagExplorer, &VTagExplorer::setNotebook); } void VMainWindow::setupNotebookPanel() @@ -2177,17 +2185,18 @@ void VMainWindow::saveStateAndGeometry() g_config->setSearchDockChecked(m_searchDock->isVisible()); g_config->setNotebookSplitterState(m_nbSplitter->saveState()); g_config->setMainSplitterState(m_mainSplitter->saveState()); + m_tagExplorer->saveStateAndGeometry(); g_config->setNaviBoxCurrentIndex(m_naviBox->currentIndex()); } void VMainWindow::restoreStateAndGeometry() { - const QByteArray &geometry = g_config->getMainWindowGeometry(); + const QByteArray geometry = g_config->getMainWindowGeometry(); if (!geometry.isEmpty()) { restoreGeometry(geometry); } - const QByteArray &state = g_config->getMainWindowState(); + const QByteArray state = g_config->getMainWindowState(); if (!state.isEmpty()) { restoreState(state); } @@ -2195,12 +2204,12 @@ void VMainWindow::restoreStateAndGeometry() m_toolDock->setVisible(g_config->getToolsDockChecked()); m_searchDock->setVisible(g_config->getSearchDockChecked()); - const QByteArray &splitterState = g_config->getMainSplitterState(); + const QByteArray splitterState = g_config->getMainSplitterState(); if (!splitterState.isEmpty()) { m_mainSplitter->restoreState(splitterState); } - const QByteArray &nbSplitterState = g_config->getNotebookSplitterState(); + const QByteArray nbSplitterState = g_config->getNotebookSplitterState(); if (!nbSplitterState.isEmpty()) { m_nbSplitter->restoreState(nbSplitterState); } diff --git a/src/vmainwindow.h b/src/vmainwindow.h index b680000c..2cfdea1a 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -44,6 +44,7 @@ class QPrinter; class VUniversalEntry; class VHistoryList; class VExplorer; +class VTagExplorer; enum class PanelViewState { @@ -462,6 +463,8 @@ private: VExplorer *m_explorer; + VTagExplorer *m_tagExplorer; + // Interval of the shared memory timer in ms. static const int c_sharedMemTimerInterval; }; diff --git a/src/vnote.qrc b/src/vnote.qrc index 39e27a03..35a8dacf 100644 --- a/src/vnote.qrc +++ b/src/vnote.qrc @@ -259,5 +259,7 @@ resources/themes/v_detorte/v_detorte_codeblock.css resources/themes/v_detorte/v_detorte_mermaid.css resources/icons/tags.svg + resources/icons/tag_explorer.svg + resources/icons/tag.svg diff --git a/src/vnotebook.cpp b/src/vnotebook.cpp index 50bf81a0..227defcc 100644 --- a/src/vnotebook.cpp +++ b/src/vnotebook.cpp @@ -442,3 +442,18 @@ bool VNotebook::addTag(const QString &p_tag) return true; } + +void VNotebook::removeTag(const QString &p_tag) +{ + if (p_tag.isEmpty() || m_tags.isEmpty()) { + return; + } + + int nr = m_tags.removeAll(p_tag); + if (nr > 0) { + if (!writeConfigNotebook()) { + qWarning() << "fail to update config of notebook" << m_name + << "in directory" << m_path; + } + } +} diff --git a/src/vnotebook.h b/src/vnotebook.h index d5bdcde6..5d94d13e 100644 --- a/src/vnotebook.h +++ b/src/vnotebook.h @@ -60,6 +60,8 @@ public: bool addTag(const QString &p_tag); + void removeTag(const QString &p_tag); + bool hasTag(const QString &p_tag) const; static VNotebook *createNotebook(const QString &p_name, diff --git a/src/vtagexplorer.cpp b/src/vtagexplorer.cpp new file mode 100644 index 00000000..a0500623 --- /dev/null +++ b/src/vtagexplorer.cpp @@ -0,0 +1,497 @@ +#include "vtagexplorer.h" + +#include + +#include "utils/viconutils.h" +#include "vmainwindow.h" +#include "vlistwidget.h" +#include "vnotebook.h" +#include "vconfigmanager.h" +#include "vsearch.h" +#include "vnote.h" +#include "vcart.h" +#include "vhistorylist.h" +#include "vnotefile.h" +#include "utils/vutils.h" + +extern VMainWindow *g_mainWin; + +extern VConfigManager *g_config; + +extern VNote *g_vnote; + +#define MAX_DISPLAY_LENGTH 10 + +VTagExplorer::VTagExplorer(QWidget *p_parent) + : QWidget(p_parent), + m_uiInitialized(false), + m_notebook(NULL), + m_notebookChanged(true), + m_search(NULL) +{ +} + +void VTagExplorer::setupUI() +{ + if (m_uiInitialized) { + return; + } + + m_uiInitialized = true; + + m_notebookLabel = new QLabel(tr("Tags"), this); + m_notebookLabel->setProperty("TitleLabel", true); + + m_tagList = new VListWidget(this); + m_tagList->setAttribute(Qt::WA_MacShowFocusRect, false); + connect(m_tagList, &QListWidget::itemActivated, + this, [this](const QListWidgetItem *p_item) { + QString tag; + if (p_item) { + tag = p_item->text(); + } + + bool ret = activateTag(tag); + if (ret && !tag.isEmpty() && m_fileList->count() == 0) { + promptToRemoveEmptyTag(tag); + } + }); + + m_tagLabel = new QLabel(tr("Notes"), this); + m_tagLabel->setProperty("TitleLabel", true); + + m_fileList = new VListWidget(this); + m_fileList->setAttribute(Qt::WA_MacShowFocusRect, false); + m_fileList->setContextMenuPolicy(Qt::CustomContextMenu); + m_fileList->setSelectionMode(QAbstractItemView::ExtendedSelection); + connect(m_fileList, &QListWidget::itemActivated, + this, &VTagExplorer::openFileItem); + connect(m_fileList, &QListWidget::customContextMenuRequested, + this, &VTagExplorer::handleFileListContextMenuRequested); + + QWidget *fileWidget = new QWidget(this); + QVBoxLayout *fileLayout = new QVBoxLayout(); + fileLayout->addWidget(m_tagLabel); + fileLayout->addWidget(m_fileList); + fileLayout->setContentsMargins(0, 0, 0, 0); + fileWidget->setLayout(fileLayout); + + m_splitter = new QSplitter(this); + m_splitter->setOrientation(Qt::Vertical); + m_splitter->setObjectName("TagExplorerSplitter"); + m_splitter->addWidget(m_tagList); + m_splitter->addWidget(fileWidget); + m_splitter->setStretchFactor(0, 0); + m_splitter->setStretchFactor(1, 1); + + QVBoxLayout *mainLayout = new QVBoxLayout(); + mainLayout->addWidget(m_notebookLabel); + mainLayout->addWidget(m_splitter); + mainLayout->setContentsMargins(0, 0, 0, 0); + + setLayout(mainLayout); + + restoreStateAndGeometry(); +} + +void VTagExplorer::showEvent(QShowEvent *p_event) +{ + setupUI(); + QWidget::showEvent(p_event); + + updateContent(); +} + +void VTagExplorer::focusInEvent(QFocusEvent *p_event) +{ + setupUI(); + QWidget::focusInEvent(p_event); + m_tagList->setFocus(); +} + +void VTagExplorer::setNotebook(VNotebook *p_notebook) +{ + if (p_notebook == m_notebook) { + return; + } + + setupUI(); + + m_notebook = p_notebook; + m_notebookChanged = true; + + if (!isVisible()) { + return; + } + + updateContent(); +} + +void VTagExplorer::updateContent() +{ + if (m_notebook) { + updateNotebookLabel(); + const QStringList &tags = m_notebook->getTags(); + if (m_notebookChanged || tagListObsolete(tags)) { + updateTagList(tags); + } + } else { + clear(); + } + + m_notebookChanged = false; +} + +void VTagExplorer::clear() +{ + setupUI(); + + m_fileList->clearAll(); + m_tagList->clearAll(); + updateTagLabel(""); + updateNotebookLabel(); +} + +void VTagExplorer::updateNotebookLabel() +{ + QString text = tr("Tags"); + QString tooltip; + if (m_notebook) { + QString name = m_notebook->getName(); + tooltip = name; + + if (name.size() > MAX_DISPLAY_LENGTH) { + name = name.left(MAX_DISPLAY_LENGTH) + QStringLiteral("..."); + } + + text = tr("Tags (%1)").arg(name); + } + + m_notebookLabel->setText(text); + m_notebookLabel->setToolTip(tooltip); +} + +bool VTagExplorer::tagListObsolete(const QStringList &p_tags) const +{ + if (m_tagList->count() != p_tags.size()) { + return true; + } + + for (int i = 0; i < p_tags.size(); ++i) { + if (p_tags[i] != m_tagList->item(i)->text()) { + return true; + } + } + + return false; +} + +void VTagExplorer::updateTagLabel(const QString &p_tag) +{ + QString text = tr("Notes"); + QString tooltip; + if (!p_tag.isEmpty()) { + QString name = p_tag; + tooltip = name; + + if (name.size() > MAX_DISPLAY_LENGTH) { + name = name.left(MAX_DISPLAY_LENGTH) + QStringLiteral("..."); + } + + text = tr("Notes (%1)").arg(name); + } + + m_tagLabel->setText(text); + m_tagLabel->setToolTip(tooltip); +} + +bool VTagExplorer::activateTag(const QString &p_tag) +{ + updateTagLabel(p_tag); + + m_fileList->clearAll(); + + if (p_tag.isEmpty()) { + return false; + } + + // Search this tag within current notebook. + g_mainWin->showStatusMessage(tr("Searching for tag \"%1\"").arg(p_tag)); + + QVector notebooks; + notebooks.append(m_notebook); + getVSearch()->clear(); + QSharedPointer config(new VSearchConfig(VSearchConfig::CurrentNotebook, + VSearchConfig::Tag, + VSearchConfig::Note, + VSearchConfig::Internal, + VSearchConfig::NoneOption, + p_tag, + QString())); + getVSearch()->setConfig(config); + QSharedPointer result = getVSearch()->search(notebooks); + + bool ret = result->m_state == VSearchState::Success; + handleSearchFinished(result); + + return ret; +} + +void VTagExplorer::updateTagList(const QStringList &p_tags) +{ + // Clear. + m_tagList->clearAll(); + activateTag(""); + + for (auto const & tag : p_tags) { + addTagItem(tag); + } + + if (m_tagList->count() > 0) { + m_tagList->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); + } +} + +void VTagExplorer::addTagItem(const QString &p_tag) +{ + QListWidgetItem *item = new QListWidgetItem(VIconUtils::treeViewIcon(":/resources/icons/tag.svg"), + p_tag); + item->setToolTip(p_tag); + + m_tagList->addItem(item); +} + +void VTagExplorer::saveStateAndGeometry() +{ + if (!m_uiInitialized) { + return; + } + + g_config->setTagExplorerSplitterState(m_splitter->saveState()); +} + +void VTagExplorer::restoreStateAndGeometry() +{ + const QByteArray state = g_config->getTagExplorerSplitterState(); + if (!state.isEmpty()) { + m_splitter->restoreState(state); + } +} + +void VTagExplorer::initVSearch() +{ + m_search = new VSearch(this); + connect(m_search, &VSearch::resultItemAdded, + this, &VTagExplorer::handleSearchItemAdded); + connect(m_search, &VSearch::resultItemsAdded, + this, &VTagExplorer::handleSearchItemsAdded); + connect(m_search, &VSearch::finished, + this, &VTagExplorer::handleSearchFinished); + + m_noteIcon = VIconUtils::treeViewIcon(":/resources/icons/note_item.svg"); +} + +void VTagExplorer::handleSearchItemAdded(const QSharedPointer &p_item) +{ + appendItemToFileList(p_item); +} + +void VTagExplorer::appendItemToFileList(const QSharedPointer &p_item) +{ + Q_ASSERT(p_item->m_type == VSearchResultItem::Note); + + QListWidgetItem *item = new QListWidgetItem(m_noteIcon, + p_item->m_text.isEmpty() ? p_item->m_path : p_item->m_text); + item->setData(Qt::UserRole, p_item->m_path); + item->setToolTip(p_item->m_path); + m_fileList->addItem(item); +} + +void VTagExplorer::handleSearchItemsAdded(const QList > &p_items) +{ + for (auto const & it : p_items) { + appendItemToFileList(it); + } +} + +void VTagExplorer::handleSearchFinished(const QSharedPointer &p_result) +{ + Q_ASSERT(p_result->m_state != VSearchState::Idle); + QString msg; + switch (p_result->m_state) { + case VSearchState::Busy: + // Only synchronized search. + Q_ASSERT(false); + msg = tr("Invalid busy state when searching for tag"); + break; + + case VSearchState::Success: + qDebug() << "search succeeded"; + msg = tr("Search for tag succeeded"); + break; + + case VSearchState::Fail: + qDebug() << "search failed"; + msg = tr("Search for tag failed"); + break; + + case VSearchState::Cancelled: + qDebug() << "search cancelled"; + msg = tr("Search for tag calcelled"); + break; + + default: + break; + } + + m_search->clear(); + + if (!msg.isEmpty()) { + g_mainWin->showStatusMessage(msg); + } +} + +void VTagExplorer::openFileItem(QListWidgetItem *p_item) const +{ + if (!p_item) { + return; + } + + QStringList files; + files << getFilePath(p_item); + g_mainWin->openFiles(files); +} + +void VTagExplorer::openSelectedFileItems() const +{ + QStringList files; + QList selectedItems = m_fileList->selectedItems(); + for (auto it : selectedItems) { + files << getFilePath(it); + } + + if (!files.isEmpty()) { + g_mainWin->openFiles(files); + } +} + +QString VTagExplorer::getFilePath(const QListWidgetItem *p_item) const +{ + return p_item->data(Qt::UserRole).toString(); +} + +void VTagExplorer::handleFileListContextMenuRequested(QPoint p_pos) +{ + QListWidgetItem *item = m_fileList->itemAt(p_pos); + if (!item) { + return; + } + + QMenu menu(this); + menu.setToolTipsVisible(true); + + QAction *openAct = new QAction(tr("&Open"), &menu); + openAct->setToolTip(tr("Open selected notes")); + connect(openAct, &QAction::triggered, + this, &VTagExplorer::openSelectedFileItems); + menu.addAction(openAct); + + QList selectedItems = m_fileList->selectedItems(); + if (selectedItems.size() == 1) { + QAction *locateAct = new QAction(VIconUtils::menuIcon(":/resources/icons/locate_note.svg"), + tr("&Locate To Folder"), + &menu); + locateAct->setToolTip(tr("Locate the folder of current note")); + connect(locateAct, &QAction::triggered, + this, &VTagExplorer::locateCurrentFileItem); + menu.addAction(locateAct); + } + + menu.addSeparator(); + + QAction *addToCartAct = new QAction(VIconUtils::menuIcon(":/resources/icons/cart.svg"), + tr("Add To Cart"), + &menu); + addToCartAct->setToolTip(tr("Add selected notes to Cart for further processing")); + connect(addToCartAct, &QAction::triggered, + this, &VTagExplorer::addFileToCart); + menu.addAction(addToCartAct); + + QAction *pinToHistoryAct = new QAction(VIconUtils::menuIcon(":/resources/icons/pin.svg"), + tr("Pin To History"), + &menu); + pinToHistoryAct->setToolTip(tr("Pin selected notes to History")); + connect(pinToHistoryAct, &QAction::triggered, + this, &VTagExplorer::pinFileToHistory); + menu.addAction(pinToHistoryAct); + + menu.exec(m_fileList->mapToGlobal(p_pos)); +} + +void VTagExplorer::locateCurrentFileItem() const +{ + auto item = m_fileList->currentItem(); + if (!item) { + return; + } + + VFile *file = g_vnote->getInternalFile(getFilePath(item)); + if (file) { + g_mainWin->locateFile(file); + } +} + +void VTagExplorer::addFileToCart() const +{ + QList items = m_fileList->selectedItems(); + VCart *cart = g_mainWin->getCart(); + + for (int i = 0; i < items.size(); ++i) { + cart->addFile(getFilePath(items[i])); + } + + g_mainWin->showStatusMessage(tr("%1 %2 added to Cart") + .arg(items.size()) + .arg(items.size() > 1 ? tr("notes") : tr("note"))); +} + +void VTagExplorer::pinFileToHistory() const +{ + QList items = m_fileList->selectedItems(); + + QStringList files; + for (int i = 0; i < items.size(); ++i) { + files << getFilePath(items[i]); + } + + g_mainWin->getHistoryList()->pinFiles(files); + + g_mainWin->showStatusMessage(tr("%1 %2 pinned to History") + .arg(items.size()) + .arg(items.size() > 1 ? tr("notes") : tr("note"))); +} + +void VTagExplorer::promptToRemoveEmptyTag(const QString &p_tag) +{ + Q_ASSERT(!p_tag.isEmpty()); + + int ret = VUtils::showMessage(QMessageBox::Warning, + tr("Warning"), + tr("Empty tag detected! Do you want to remove it?"), + tr("The tag %2 seems not to " + "be assigned to any note currently.") + .arg(g_config->c_dataTextStyle) + .arg(p_tag), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Cancel, + this, + MessageBoxType::Danger); + + if (ret == QMessageBox::Cancel) { + return; + } + + // Remove the tag from m_notebook. + m_notebook->removeTag(p_tag); + updateContent(); +} diff --git a/src/vtagexplorer.h b/src/vtagexplorer.h new file mode 100644 index 00000000..e2e5e03a --- /dev/null +++ b/src/vtagexplorer.h @@ -0,0 +1,113 @@ +#ifndef VTAGEXPLORER_H +#define VTAGEXPLORER_H + +#include +#include + +#include "vsearchconfig.h" + +class QLabel; +class VListWidget; +class QListWidgetItem; +class QSplitter; +class VNotebook; +class VSearch; + +class VTagExplorer : public QWidget +{ + Q_OBJECT +public: + explicit VTagExplorer(QWidget *p_parent = nullptr); + + void clear(); + + void setNotebook(VNotebook *p_notebook); + + void saveStateAndGeometry(); + +protected: + void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE; + + void focusInEvent(QFocusEvent *p_event) Q_DECL_OVERRIDE; + +private slots: + void handleSearchItemAdded(const QSharedPointer &p_item); + + void handleSearchItemsAdded(const QList > &p_items); + + void handleSearchFinished(const QSharedPointer &p_result); + + void openFileItem(QListWidgetItem *p_item) const; + + void openSelectedFileItems() const; + + void locateCurrentFileItem() const; + + void addFileToCart() const; + + void pinFileToHistory() const; + + void handleFileListContextMenuRequested(QPoint p_pos); + +private: + void setupUI(); + + void updateNotebookLabel(); + + void updateTagLabel(const QString &p_tag); + + bool tagListObsolete(const QStringList &p_tags) const; + + void updateTagList(const QStringList &p_tags); + + void updateContent(); + + // Return ture if succeeded. + bool activateTag(const QString &p_tag); + + void addTagItem(const QString &p_tag); + + void restoreStateAndGeometry(); + + VSearch *getVSearch() const; + + void initVSearch(); + + void appendItemToFileList(const QSharedPointer &p_item); + + QString getFilePath(const QListWidgetItem *p_item) const; + + void promptToRemoveEmptyTag(const QString &p_tag); + + bool m_uiInitialized; + + QLabel *m_notebookLabel; + + QLabel *m_tagLabel; + + VListWidget *m_tagList; + + VListWidget *m_fileList; + + QSplitter *m_splitter; + + VNotebook *m_notebook; + + bool m_notebookChanged; + + QIcon m_noteIcon; + + VSearch *m_search; +}; + +inline VSearch *VTagExplorer::getVSearch() const +{ + if (m_search) { + return m_search; + } + + const_cast(this)->initVSearch(); + return m_search; +} + +#endif // VTAGEXPLORER_H