diff --git a/src/iuniversalentry.h b/src/iuniversalentry.h index 4369047f..1c7425f2 100644 --- a/src/iuniversalentry.h +++ b/src/iuniversalentry.h @@ -90,6 +90,14 @@ public: Q_UNUSED(p_id); } + // Ask the UE to return current item folder path or the folder containing current + // item note. + virtual QString currentItemFolder(int p_id) + { + Q_UNUSED(p_id); + return QString(); + } + void setWidgetParent(QWidget *p_parent) { m_widgetParent = p_parent; diff --git a/src/src.pro b/src/src.pro index a2a97324..d2b8754b 100644 --- a/src/src.pro +++ b/src/src.pro @@ -123,7 +123,8 @@ SOURCES += main.cpp\ vdoublerowitemwidget.cpp \ vsearchue.cpp \ voutlineue.cpp \ - vhelpue.cpp + vhelpue.cpp \ + vlistfolderue.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -237,7 +238,8 @@ HEADERS += vmainwindow.h \ vdoublerowitemwidget.h \ vsearchue.h \ voutlineue.h \ - vhelpue.h + vhelpue.h \ + vlistfolderue.h RESOURCES += \ vnote.qrc \ diff --git a/src/vhelpue.cpp b/src/vhelpue.cpp index f9dcba54..967306d3 100644 --- a/src/vhelpue.cpp +++ b/src/vhelpue.cpp @@ -63,12 +63,13 @@ bool VHelpUE::initListWidget() m_listWidget->addItem(tr("Esc/Ctrl+[: Hide Universal Entry")); m_listWidget->addItem(tr("Ctrl+U: Clear the command input")); m_listWidget->addItem(tr("Ctrl+E: Clear the command input except the entry key")); - m_listWidget->addItem(tr("Ctrl+J: Select next item")); - m_listWidget->addItem(tr("Ctrl+K: Select previous item")); - m_listWidget->addItem(tr("Enter: Activate current item")); - m_listWidget->addItem(tr("Ctrl+R: Select current item's parent item")); + m_listWidget->addItem(tr("Ctrl+J: Go to next item")); + m_listWidget->addItem(tr("Ctrl+K: Go to previous item")); + m_listWidget->addItem(tr("Ctrl+R: Go to current item's parent item")); m_listWidget->addItem(tr("Ctrl+T: Expand/Collapse current item")); - m_listWidget->addItem(tr("Ctrl+S: Sort the items")); + m_listWidget->addItem(tr("Ctrl+S: Sort items")); + m_listWidget->addItem(tr("Enter: Activate current item")); + m_listWidget->addItem(tr("Ctrl+M: Browse current item folder or the folder containing current item")); return true; } diff --git a/src/vlistfolderue.cpp b/src/vlistfolderue.cpp new file mode 100644 index 00000000..fbd1cd25 --- /dev/null +++ b/src/vlistfolderue.cpp @@ -0,0 +1,346 @@ +#include "vlistfolderue.h" + +#include +#include +#include + +#include "vlistwidgetdoublerows.h" +#include "vdirectory.h" +#include "vdirectorytree.h" +#include "vmainwindow.h" +#include "vnote.h" +#include "utils/viconutils.h" +#include "vnotefile.h" +#include "vsearchue.h" +#include "utils/vutils.h" +#include "vnotebook.h" + +extern VMainWindow *g_mainWin; + +extern VNote *g_vnote; + +VListFolderPanel::VListFolderPanel(QWidget *p_contentWidget, + QWidget *p_parent) + : QWidget(p_parent) +{ + m_titleLabel = new QLabel(this); + m_titleLabel->setProperty("TitleLabel", true); + + p_contentWidget->setParent(this); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->addWidget(m_titleLabel); + layout->addWidget(p_contentWidget); + + layout->setContentsMargins(0, 0, 0, 0); + + setLayout(layout); +} + +void VListFolderPanel::setTitleLabel(const QString &p_title) +{ + m_titleLabel->setText(p_title); +} + +void VListFolderPanel::clearTitle() +{ + m_titleLabel->clear(); +} + + +VListFolderUE::VListFolderUE(QObject *p_parent) + : IUniversalEntry(p_parent), + m_listWidget(NULL) +{ +} + +QString VListFolderUE::description(int p_id) const +{ + Q_UNUSED(p_id); + + return tr("List and search the folders and notes of current folder"); +} + +void VListFolderUE::init() +{ + if (m_initialized) { + return; + } + + m_initialized = true; + + m_noteIcon = VIconUtils::treeViewIcon(":/resources/icons/note_item.svg"); + m_folderIcon = VIconUtils::treeViewIcon(":/resources/icons/dir_item.svg"); + + m_listWidget = new VListWidgetDoubleRows(m_widgetParent); + m_listWidget->setFitContent(true); + connect(m_listWidget, SIGNAL(itemActivated(QListWidgetItem *)), + this, SLOT(activateItem(QListWidgetItem *))); + + m_panel = new VListFolderPanel(m_listWidget, m_widgetParent); + m_panel->hide(); +} + +QWidget *VListFolderUE::widget(int p_id) +{ + Q_UNUSED(p_id); + + init(); + + return m_panel; +} + +void VListFolderUE::processCommand(int p_id, const QString &p_cmd) +{ + Q_UNUSED(p_id); + + init(); + + QString folderPath = m_folderPath; + if (folderPath.isEmpty()) { + VDirectory *dir = g_mainWin->getDirectoryTree()->currentDirectory(); + folderPath = dir->fetchPath(); + } + + listFolder(folderPath, p_cmd); + + m_listWidget->updateGeometry(); + emit widgetUpdated(); + emit stateUpdated(State::Success); +} + +void VListFolderUE::clear(int p_id) +{ + Q_UNUSED(p_id); + + m_panel->clearTitle(); + m_listWidget->clearAll(); + m_data.clear(); + + m_folderPath.clear(); + m_currentFolderPath.clear(); +} + +void VListFolderUE::selectNextItem(int p_id, bool p_forward) +{ + Q_UNUSED(p_id); + + m_listWidget->selectNextItem(p_forward); +} + +void VListFolderUE::selectParentItem(int p_id) +{ + Q_UNUSED(p_id); + + if (m_currentFolderPath.isEmpty()) { + return; + } + + if (listFolder(VUtils::basePathFromPath(m_currentFolderPath), QString())) { + m_folderPath = m_currentFolderPath; + } + + m_listWidget->updateGeometry(); + emit widgetUpdated(); +} + +void VListFolderUE::activate(int p_id) +{ + Q_UNUSED(p_id); + activateItem(m_listWidget->currentItem()); +} + +const QSharedPointer &VListFolderUE::itemResultData(const QListWidgetItem *p_item) const +{ + Q_ASSERT(p_item); + int idx = p_item->data(Qt::UserRole).toInt(); + Q_ASSERT(idx >= 0 && idx < m_data.size()); + return m_data[idx]; +} + +void VListFolderUE::activateItem(QListWidgetItem *p_item) +{ + if (!p_item) { + return; + } + + emit requestHideUniversalEntry(); + + VSearchUE::activateItem(itemResultData(p_item)); +} + +void VListFolderUE::sort(int p_id) +{ + Q_UNUSED(p_id); + static bool noteFirst = false; + + int cnt = m_listWidget->count(); + if (noteFirst) { + int idx = cnt - 1; + while (true) { + if (itemResultData(m_listWidget->item(idx))->m_type != VSearchResultItem::Note) { + // Move it to the first row. + m_listWidget->moveItem(idx, 0); + } else { + break; + } + } + } else { + int idx = 0; + while (true) { + if (itemResultData(m_listWidget->item(idx))->m_type != VSearchResultItem::Note) { + // Move it to the last row. + m_listWidget->moveItem(idx, cnt - 1); + } else { + break; + } + } + } + + if (cnt) { + m_listWidget->setCurrentRow(0); + } + + noteFirst = !noteFirst; +} + +void VListFolderUE::addResultItem(const QSharedPointer &p_item) +{ + m_data.append(p_item); + + QString first, second; + if (p_item->m_text.isEmpty()) { + first = p_item->m_path; + } else { + first = p_item->m_text; + second = p_item->m_path; + } + + QIcon *icon = NULL; + switch (p_item->m_type) { + case VSearchResultItem::Note: + icon = &m_noteIcon; + break; + + case VSearchResultItem::Folder: + icon = &m_folderIcon; + break; + + default: + break; + } + + QListWidgetItem *item = m_listWidget->addDoubleRowsItem(*icon, first, second); + item->setData(Qt::UserRole, m_data.size() - 1); + item->setToolTip(p_item->m_path); + + if (m_listWidget->count() == 1) { + m_listWidget->setCurrentRow(0); + } +} + +void VListFolderUE::entryShown(int p_id, const QString &p_cmd) +{ + processCommand(p_id, p_cmd); +} + +void VListFolderUE::setFolderPath(const QString &p_path) +{ + m_folderPath = p_path; +} + +QString VListFolderUE::currentItemFolder(int p_id) +{ + Q_UNUSED(p_id); + QString folder; + QListWidgetItem *item = m_listWidget->currentItem(); + if (item) { + const QSharedPointer &resItem = itemResultData(item); + if (resItem->m_type == VSearchResultItem::Folder) { + folder = resItem->m_path; + } + } + + return folder; +} + +bool VListFolderUE::listFolder(const QString &p_path, const QString &p_cmd) +{ + VDirectory *dir = g_vnote->getInternalDirectory(p_path); + if (!dir) { + // See if it is a notebook. + VNotebook *nb = g_vnote->getNotebook(p_path); + if (!nb) { + return false; + } + + dir = nb->getRootDir(); + } + + m_panel->clearTitle(); + m_listWidget->clearAll(); + m_data.clear(); + + m_currentFolderPath = dir->fetchPath(); + m_panel->setTitleLabel(m_currentFolderPath); + + if (!dir->open()) { + return true; + } + + if (p_cmd.isEmpty()) { + // List the content. + for (auto const & it : dir->getSubDirs()) { + QSharedPointer item(new VSearchResultItem(VSearchResultItem::Folder, + VSearchResultItem::LineNumber, + it->getName(), + it->fetchPath())); + addResultItem(item); + } + + for (auto const & file : dir->getFiles()) { + QSharedPointer item(new VSearchResultItem(VSearchResultItem::Note, + VSearchResultItem::LineNumber, + file->getName(), + file->fetchPath())); + addResultItem(item); + } + } else { + // Search the content. + VSearchConfig config(VSearchConfig::CurrentFolder, + VSearchConfig::Name, + VSearchConfig::Note | VSearchConfig::Folder, + VSearchConfig::Internal, + VSearchConfig::NoneOption, + p_cmd, + QString()); + + for (auto const & it : dir->getSubDirs()) { + QString name = it->getName(); + if (!config.m_token.matched(name)) { + continue; + } + + QSharedPointer item(new VSearchResultItem(VSearchResultItem::Folder, + VSearchResultItem::LineNumber, + name, + it->fetchPath())); + addResultItem(item); + } + + for (auto const & file : dir->getFiles()) { + QString name = file->getName(); + if (!config.m_token.matched(name)) { + continue; + } + + QSharedPointer item(new VSearchResultItem(VSearchResultItem::Note, + VSearchResultItem::LineNumber, + name, + file->fetchPath())); + addResultItem(item); + } + } + + return true; +} diff --git a/src/vlistfolderue.h b/src/vlistfolderue.h new file mode 100644 index 00000000..1002b197 --- /dev/null +++ b/src/vlistfolderue.h @@ -0,0 +1,89 @@ +#ifndef VLISTFOLDERUE_H +#define VLISTFOLDERUE_H + +#include "iuniversalentry.h" +#include +#include + +#include "vsearchconfig.h" + +class VListWidgetDoubleRows; +class QListWidgetItem; +class QLabel; + +class VListFolderPanel : public QWidget +{ + Q_OBJECT +public: + explicit VListFolderPanel(QWidget *p_contentWidget, + QWidget *p_parent = nullptr); + + void setTitleLabel(const QString &p_title); + + void clearTitle(); + +private: + QLabel *m_titleLabel; +}; + + +// Universal Entry to list contents of folder. +class VListFolderUE : public IUniversalEntry +{ + Q_OBJECT +public: + explicit VListFolderUE(QObject *p_parent = nullptr); + + QString description(int p_id) const Q_DECL_OVERRIDE; + + QWidget *widget(int p_id) Q_DECL_OVERRIDE; + + void processCommand(int p_id, const QString &p_cmd) Q_DECL_OVERRIDE; + + void clear(int p_id) Q_DECL_OVERRIDE; + + void selectNextItem(int p_id, bool p_forward) Q_DECL_OVERRIDE; + + void selectParentItem(int p_id) Q_DECL_OVERRIDE; + + void activate(int p_id) Q_DECL_OVERRIDE; + + void sort(int p_id) Q_DECL_OVERRIDE; + + void entryShown(int p_id, const QString &p_cmd) Q_DECL_OVERRIDE; + + QString currentItemFolder(int p_id) Q_DECL_OVERRIDE; + + void setFolderPath(const QString &p_path); + +protected: + void init() Q_DECL_OVERRIDE; + +private slots: + void activateItem(QListWidgetItem *p_item); + +private: + void addResultItem(const QSharedPointer &p_item); + + const QSharedPointer &itemResultData(const QListWidgetItem *p_item) const; + + bool listFolder(const QString &p_path, const QString &p_cmd); + + QVector > m_data; + + QIcon m_noteIcon; + QIcon m_folderIcon; + + // Folder path to list. + // If empty, use current folder. + QString m_folderPath; + + // Current folder path. + QString m_currentFolderPath; + + VListWidgetDoubleRows *m_listWidget; + + VListFolderPanel *m_panel; +}; + +#endif // VLISTFOLDERUE_H diff --git a/src/vlistwidgetdoublerows.cpp b/src/vlistwidgetdoublerows.cpp index aef79141..529bf4c5 100644 --- a/src/vlistwidgetdoublerows.cpp +++ b/src/vlistwidgetdoublerows.cpp @@ -11,6 +11,16 @@ VListWidgetDoubleRows::VListWidgetDoubleRows(QWidget *p_parent) { } +QListWidgetItem *VListWidgetDoubleRows::addDoubleRowsItem(const QIcon &p_icon, + const QString &p_firstRow, + const QString &p_secondRow) +{ + return insertDoubleRowsItem(count(), + p_icon, + p_firstRow, + p_secondRow); +} + QListWidgetItem *VListWidgetDoubleRows::insertDoubleRowsItem(int p_row, const QIcon &p_icon, const QString &p_firstRow, diff --git a/src/vlistwidgetdoublerows.h b/src/vlistwidgetdoublerows.h index 02faab7e..824ac558 100644 --- a/src/vlistwidgetdoublerows.h +++ b/src/vlistwidgetdoublerows.h @@ -14,6 +14,10 @@ class VListWidgetDoubleRows : public VListWidget public: explicit VListWidgetDoubleRows(QWidget *p_parent = nullptr); + QListWidgetItem *addDoubleRowsItem(const QIcon &p_icon, + const QString &p_firstRow, + const QString &p_secondRow); + QListWidgetItem *insertDoubleRowsItem(int p_row, const QIcon &p_icon, const QString &p_firstRow, diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 758ed8e7..339e22ba 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -43,6 +43,7 @@ #include "vsearchue.h" #include "voutlineue.h" #include "vhelpue.h" +#include "vlistfolderue.h" extern VConfigManager *g_config; @@ -3214,5 +3215,6 @@ void VMainWindow::initUniversalEntry() m_ue->registerEntry('y', new VOutlineUE(this), 0); m_ue->registerEntry('h', searchUE, VSearchUE::Path_FolderNote_AllNotebook); m_ue->registerEntry('n', searchUE, VSearchUE::Path_FolderNote_CurrentNotebook); + m_ue->registerEntry('m', new VListFolderUE(this), 0); m_ue->registerEntry('?', new VHelpUE(this), 0); } diff --git a/src/voutlineue.cpp b/src/voutlineue.cpp index c360d7a3..c159661b 100644 --- a/src/voutlineue.cpp +++ b/src/voutlineue.cpp @@ -106,15 +106,15 @@ void VOutlineUE::processCommand(int p_id, const QString &p_cmd) // Search the outline. m_listOutline = false; - VSearchConfig config(VSearchConfig::CurrentNote, - VSearchConfig::Content, - VSearchConfig::Note, - VSearchConfig::Internal, - VSearchConfig::NoneOption, - p_cmd, - QString()); - if (tab) { + VSearchConfig config(VSearchConfig::CurrentNote, + VSearchConfig::Content, + VSearchConfig::Note, + VSearchConfig::Internal, + VSearchConfig::NoneOption, + p_cmd, + QString()); + const VTableOfContent &outline = tab->getOutline(); const QVector &table = outline.getTable(); for (auto const & it : table) { diff --git a/src/vsearchue.cpp b/src/vsearchue.cpp index d6aa5f29..2ea3980c 100644 --- a/src/vsearchue.cpp +++ b/src/vsearchue.cpp @@ -981,3 +981,62 @@ void VSearchUE::sort(int p_id) break; } } + +QString VSearchUE::currentItemFolder(int p_id) +{ + QString folder; + QSharedPointer resItem; + + switch (p_id) { + case ID::Name_Notebook_AllNotebook: + case ID::Name_FolderNote_AllNotebook: + case ID::Name_FolderNote_CurrentNotebook: + case ID::Name_FolderNote_CurrentFolder: + case ID::Name_Note_Buffer: + case ID::Path_FolderNote_AllNotebook: + case ID::Path_FolderNote_CurrentNotebook: + { + QListWidgetItem *item = m_listWidget->currentItem(); + if (item) { + resItem = itemResultData(item); + } + + break; + } + + case ID::Content_Note_AllNotebook: + case ID::Content_Note_CurrentNotebook: + case ID::Content_Note_CurrentFolder: + case ID::Content_Note_Buffer: + case ID::Outline_Note_Buffer: + { + QTreeWidgetItem *item = m_treeWidget->currentItem(); + if (item) { + resItem = itemResultData(item); + } + + break; + } + + default: + Q_ASSERT(false); + } + + if (!resItem.isNull()) { + switch (resItem->m_type) { + case VSearchResultItem::Note: + folder = VUtils::basePathFromPath(resItem->m_path); + break; + + case VSearchResultItem::Folder: + case VSearchResultItem::Notebook: + folder = resItem->m_path; + break; + + default: + break; + } + } + + return folder; +} diff --git a/src/vsearchue.h b/src/vsearchue.h index 93dd8ee6..e09f999e 100644 --- a/src/vsearchue.h +++ b/src/vsearchue.h @@ -79,6 +79,10 @@ public: void sort(int p_id) Q_DECL_OVERRIDE; + QString currentItemFolder(int p_id) Q_DECL_OVERRIDE; + + static void activateItem(const QSharedPointer &p_item); + protected: void init() Q_DECL_OVERRIDE; @@ -125,8 +129,6 @@ private: void appendItemToTree(const QSharedPointer &p_item); - void activateItem(const QSharedPointer &p_item); - const QSharedPointer &itemResultData(const QListWidgetItem *p_item) const; const QSharedPointer &itemResultData(const QTreeWidgetItem *p_item) const; diff --git a/src/vuniversalentry.cpp b/src/vuniversalentry.cpp index 8cc67f5d..f2bcb11a 100644 --- a/src/vuniversalentry.cpp +++ b/src/vuniversalentry.cpp @@ -17,11 +17,12 @@ #include "utils/vutils.h" #include "vlistwidget.h" #include "vpalette.h" +#include "vlistfolderue.h" #define MINIMUM_WIDTH 200 #define CMD_EDIT_INTERVAL 500 -#define CMD_EDIT_IDLE_INTERVAL 200 +#define CMD_EDIT_IDLE_INTERVAL 100 extern VPalette *g_palette; @@ -108,7 +109,7 @@ void VUniversalEntry::setupUI() connect(m_cmdEdit, &VMetaWordLineEdit::textEdited, this, [this](const QString &p_text) { m_cmdTimer->stop(); - if (p_text.isEmpty() || p_text.size() == 1) { + if (p_text.size() <= 1) { m_cmdTimer->start(CMD_EDIT_IDLE_INTERVAL); } else { m_cmdTimer->start(CMD_EDIT_INTERVAL); @@ -190,6 +191,12 @@ void VUniversalEntry::registerEntry(QChar p_key, IUniversalEntry *p_entry, int p m_infoWidget->addItem(QString("%1: %2").arg(p_key) .arg(p_entry->description(p_id))); m_infoWidget->updateGeometry(); + + if (m_listEntryKey.isNull()) { + if (dynamic_cast(p_entry)) { + m_listEntryKey = p_key; + } + } } void VUniversalEntry::processCommand() @@ -368,6 +375,27 @@ void VUniversalEntry::keyPressEvent(QKeyEvent *p_event) break; + case Qt::Key_M: + if (VUtils::isControlModifierForVim(modifiers)) { + // Ctrl+M to browse current item folder or the folder containing current + // item. + if (m_lastEntry && !m_listEntryKey.isNull()) { + QString folderPath = m_lastEntry->m_entry->currentItemFolder(m_lastEntry->m_id); + if (!folderPath.isEmpty()) { + m_cmdTimer->stop(); + Q_ASSERT(m_entries.contains(m_listEntryKey)); + const Entry &entry = m_entries[m_listEntryKey]; + static_cast(entry.m_entry)->setFolderPath(folderPath); + m_cmdEdit->setText(m_listEntryKey); + processCommand(); + } + } + + return; + } + + break; + default: break; } diff --git a/src/vuniversalentry.h b/src/vuniversalentry.h index 0b47d10b..36cc18f2 100644 --- a/src/vuniversalentry.h +++ b/src/vuniversalentry.h @@ -114,6 +114,9 @@ private: // Last used Entry. const Entry *m_lastEntry; + // Entry to list folder content. + QChar m_listEntryKey; + // The CMD edit's original style sheet. QString m_cmdStyleSheet;