diff --git a/src/resources/icons/cart.svg b/src/resources/icons/cart.svg index 9b28db7f..27e63e70 100644 --- a/src/resources/icons/cart.svg +++ b/src/resources/icons/cart.svg @@ -4,7 +4,7 @@ - + + + + + diff --git a/src/resources/icons/history.svg b/src/resources/icons/history.svg new file mode 100644 index 00000000..3f9d0fe2 --- /dev/null +++ b/src/resources/icons/history.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/resources/icons/pin.svg b/src/resources/icons/pin.svg new file mode 100644 index 00000000..9036a5de --- /dev/null +++ b/src/resources/icons/pin.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/resources/themes/v_moonlight/v_moonlight.palette b/src/resources/themes/v_moonlight/v_moonlight.palette index 12d65b7d..9c0d9293 100644 --- a/src/resources/themes/v_moonlight/v_moonlight.palette +++ b/src/resources/themes/v_moonlight/v_moonlight.palette @@ -119,6 +119,10 @@ search_hit_item_bg=@master_dark_bg ue_cmd_busy_border=#3F51B5 ue_cmd_fail_border=@danger_bg +; VListWidget/VTreeWidget separator. +item_separator_fg=@master_light_bg +item_separator_bg=@separator_bg + [widgets] ; Widget color attributes. @@ -138,6 +142,7 @@ menubar_item_selected_bg=@selected_bg ; Menu. menu_bg=@base_bg menu_fg=@base_fg +menu_border_bg=@border_bg menu_item_disabled_fg=@disabled_fg menu_item_selected_fg=@selected_fg menu_item_selected_bg=@selected_bg diff --git a/src/resources/themes/v_moonlight/v_moonlight.qss b/src/resources/themes/v_moonlight/v_moonlight.qss index 498987bf..0bdbe4e6 100644 --- a/src/resources/themes/v_moonlight/v_moonlight.qss +++ b/src/resources/themes/v_moonlight/v_moonlight.qss @@ -40,7 +40,7 @@ QMenuBar::item:selected { QMenu { background: @menu_bg; color: @menu_fg; - margin: 2px; + border: 2px solid @menu_border_bg; } QMenu::icon { @@ -402,10 +402,10 @@ VButtonMenuItem:disabled { /* QComboBox */ QComboBox#NotebookSelector { border: none; - font-size: 13pt; - padding-top: 3px; - padding-bottom: 3px; - icon-size: $30px; + font-size: 12pt; + padding-top: 5px; + padding-bottom: 5px; + icon-size: $24px; font-weight: bold; color: @combobox_notebookselector_fg; background: @combobox_notebookselector_bg; @@ -424,14 +424,14 @@ QComboBox#NotebookSelector:hover { QComboBox#NotebookSelector QListWidget { border: 1px solid @combobox_view_border; background-color: @combobox_bg; - font-size: 13pt; + font-size: 12pt; font-weight: normal; - icon-size: $30px; + icon-size: $24px; } QComboBox#NotebookSelector QListWidget::item { - padding-top: $10px; - padding-bottom: $10px; + padding-top: $5px; + padding-bottom: $5px; } QComboBox#NotebookSelector QListWidget::item:hover { @@ -851,6 +851,10 @@ QListView::item:selected:!active { color: @listview_item_selected_inactive_fg; background: @listview_item_selected_inactive_bg; } + +QListView::item:disabled { + background: transparent; +} /* End QListView */ /* QSplitter */ @@ -1250,6 +1254,10 @@ QWidget[ToolBoxTitle="true"] { border-bottom: 2px solid @toolbox_title_border; } +QWidget[ToolBoxTitle="true"] QPushButton[ToolBoxTitleBtn="true"] { + height: $20px; +} + QWidget[MainEditor="true"] { border: none; } diff --git a/src/resources/themes/v_native/v_native.palette b/src/resources/themes/v_native/v_native.palette index 7cc808c0..f5048174 100644 --- a/src/resources/themes/v_native/v_native.palette +++ b/src/resources/themes/v_native/v_native.palette @@ -87,6 +87,10 @@ search_hit_item_bg=#80CBC4 ue_cmd_busy_border=#3F51B5 ue_cmd_fail_border=@danger_bg +; VListWidget/VTreeWidget separator. +item_separator_fg=@content_fg +item_separator_bg=@separator_bg + [widgets] ; Widget color attributes. diff --git a/src/resources/themes/v_native/v_native.qss b/src/resources/themes/v_native/v_native.qss index 6f2c7787..69f71b9a 100644 --- a/src/resources/themes/v_native/v_native.qss +++ b/src/resources/themes/v_native/v_native.qss @@ -294,10 +294,10 @@ VButtonMenuItem:disabled { /* QComboBox */ QComboBox#NotebookSelector { border: none; - font-size: 13pt; - padding-top: 3px; - padding-bottom: 3px; - icon-size: $30px; + font-size: 12pt; + padding-top: 5px; + padding-bottom: 5px; + icon-size: $24px; background: @combobox_bg; } @@ -328,13 +328,13 @@ QComboBox#NotebookSelector::down-arrow:disabled { QComboBox#NotebookSelector QListWidget { border: 1px solid @combobox_view_border; background-color: @combobox_bg; - font-size: 13pt; - icon-size: $30px; + font-size: 12pt; + icon-size: $24px; } QComboBox#NotebookSelector QListWidget::item { - padding-top: $10px; - padding-bottom: $10px; + padding-top: $5px; + padding-bottom: $5px; } QComboBox#NotebookSelector QListWidget::item:hover { diff --git a/src/resources/themes/v_pure/v_pure.palette b/src/resources/themes/v_pure/v_pure.palette index c96eced6..04ace16f 100644 --- a/src/resources/themes/v_pure/v_pure.palette +++ b/src/resources/themes/v_pure/v_pure.palette @@ -113,6 +113,10 @@ search_hit_item_bg=#CCE7E4 ue_cmd_busy_border=#3F51B5 ue_cmd_fail_border=@danger_bg +; VListWidget/VTreeWidget separator. +item_separator_fg=@master_dark_bg +item_separator_bg=@separator_bg + [widgets] ; Widget color attributes. @@ -120,7 +124,7 @@ ue_cmd_fail_border=@danger_bg widget_fg=@base_fg ; Separator of dock widgets. -dock_separator_bg=@border_bg +dock_separator_bg=@separator_bg dock_separator_hover_bg=@hover_bg dock_separator_pressed_bg=@pressed_bg @@ -132,6 +136,7 @@ menubar_item_selected_bg=@selected_bg ; Menu. menu_bg=@base_bg menu_fg=@base_fg +menu_border_bg=@border_bg menu_item_disabled_fg=@disabled_fg menu_item_selected_fg=@selected_fg menu_item_selected_bg=@selected_bg diff --git a/src/resources/themes/v_pure/v_pure.qss b/src/resources/themes/v_pure/v_pure.qss index 0d171801..de0faa2b 100644 --- a/src/resources/themes/v_pure/v_pure.qss +++ b/src/resources/themes/v_pure/v_pure.qss @@ -40,7 +40,7 @@ QMenuBar::item:selected { QMenu { background: @menu_bg; color: @menu_fg; - margin: 2px; + border: 2px solid @menu_border_bg; } QMenu::icon { @@ -402,10 +402,10 @@ VButtonMenuItem:disabled { /* QComboBox */ QComboBox#NotebookSelector { border: none; - font-size: 13pt; - padding-top: 3px; - padding-bottom: 3px; - icon-size: $30px; + font-size: 12pt; + padding-top: 5px; + padding-bottom: 5px; + icon-size: $24px; font-weight: bold; color: @combobox_notebookselector_fg; background: @combobox_notebookselector_bg; @@ -424,14 +424,14 @@ QComboBox#NotebookSelector:hover { QComboBox#NotebookSelector QListWidget { border: 1px solid @combobox_view_border; background-color: @combobox_bg; - font-size: 13pt; + font-size: 12pt; font-weight: normal; - icon-size: $30px; + icon-size: $24px; } QComboBox#NotebookSelector QListWidget::item { - padding-top: $10px; - padding-bottom: $10px; + padding-top: $5px; + padding-bottom: $5px; } QComboBox#NotebookSelector QListWidget::item:hover { @@ -851,6 +851,10 @@ QListView::item:selected:!active { color: @listview_item_selected_inactive_fg; background: @listview_item_selected_inactive_bg; } + +QListView::item:disabled { + background: transparent; +} /* End QListView */ /* QSplitter */ @@ -1249,6 +1253,10 @@ QWidget[ToolBoxTitle="true"] { border-bottom: $2px solid @toolbox_title_border; } +QWidget[ToolBoxTitle="true"] QPushButton[ToolBoxTitleBtn="true"] { + height: $20px; +} + QWidget[MainEditor="true"] { border: none; } diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index a2b42a5d..dee9bf79 100644 --- a/src/resources/vnote.ini +++ b/src/resources/vnote.ini @@ -209,6 +209,10 @@ enable_wildcard_in_simple_search=true ; scope,object,target,engine,option,pattern search_options=4,2,7,0,0,"" +; Number of items in history +; 0 to disable history +history_size=50 + [export] ; Path of the wkhtmltopdf tool wkhtmltopdf=wkhtmltopdf diff --git a/src/src.pro b/src/src.pro index bed00917..d7e3ebf6 100644 --- a/src/src.pro +++ b/src/src.pro @@ -131,7 +131,8 @@ SOURCES += main.cpp\ vlivepreviewhelper.cpp \ vmathjaxpreviewhelper.cpp \ vmathjaxwebdocument.cpp \ - vmathjaxinplacepreviewhelper.cpp + vmathjaxinplacepreviewhelper.cpp \ + vhistorylist.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -254,7 +255,9 @@ HEADERS += vmainwindow.h \ vmathjaxpreviewhelper.h \ vmathjaxwebdocument.h \ vmathjaxinplacepreviewhelper.h \ - markdownitoption.h + markdownitoption.h \ + vhistorylist.h \ + vhistoryentry.h RESOURCES += \ vnote.qrc \ diff --git a/src/vcart.cpp b/src/vcart.cpp index d36de566..e4983d61 100644 --- a/src/vcart.cpp +++ b/src/vcart.cpp @@ -41,7 +41,7 @@ void VCart::setupUI() g_mainWin, MessageBoxType::Danger); if (ret == QMessageBox::Ok) { - m_itemList->clear(); + m_itemList->clearAll(); updateNumberLabel(); } } @@ -55,7 +55,7 @@ void VCart::setupUI() btnLayout->addWidget(m_numLabel); btnLayout->setContentsMargins(0, 0, 0, 0); - m_itemList = new QListWidget(); + m_itemList = new VListWidget(); m_itemList->setAttribute(Qt::WA_MacShowFocusRect, false); m_itemList->setContextMenuPolicy(Qt::CustomContextMenu); m_itemList->setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -109,9 +109,9 @@ void VCart::handleContextMenuRequested(QPoint p_pos) menu.setToolTipsVisible(true); if (item) { - int itemCount = m_itemList->selectedItems().size(); - if (itemCount == 1) { - menu.addAction(m_openAct); + menu.addAction(m_openAct); + + if (m_itemList->selectedItems().size() == 1) { menu.addAction(m_locateAct); } @@ -174,7 +174,15 @@ void VCart::deleteSelectedItems() void VCart::openSelectedItems() const { - openItem(m_itemList->currentItem()); + QStringList files; + QList selectedItems = m_itemList->selectedItems(); + for (auto it : selectedItems) { + files << getFilePath(it); + } + + if (!files.isEmpty()) { + g_mainWin->openFiles(files); + } } void VCart::locateCurrentItem() @@ -266,3 +274,22 @@ void VCart::updateNumberLabel() const m_numLabel->setText(tr("%1 %2").arg(cnt) .arg(cnt > 1 ? tr("Items") : tr("Item"))); } + +void VCart::showNavigation() +{ + VNavigationMode::showNavigation(m_itemList); +} + +bool VCart::handleKeyNavigation(int p_key, bool &p_succeed) +{ + static bool secondKey = false; + return VNavigationMode::handleKeyNavigation(m_itemList, + secondKey, + p_key, + p_succeed); +} + +QWidget *VCart::getContentWidget() const +{ + return m_itemList; +} diff --git a/src/vcart.h b/src/vcart.h index dc36ec6f..95380a72 100644 --- a/src/vcart.h +++ b/src/vcart.h @@ -7,14 +7,12 @@ #include "vnavigationmode.h" class QPushButton; -class QListWidget; +class VListWidget; class QListWidgetItem; class QLabel; class QAction; -class QKeyEvent; -class QFocusEvent; -class VCart : public QWidget +class VCart : public QWidget, public VNavigationMode { Q_OBJECT public: @@ -26,6 +24,12 @@ public: QVector getFiles() const; + QWidget *getContentWidget() const; + + // Implementations for VNavigationMode. + void showNavigation() Q_DECL_OVERRIDE; + bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE; + private slots: void handleContextMenuRequested(QPoint p_pos); @@ -55,7 +59,7 @@ private: QPushButton *m_clearBtn; QLabel *m_numLabel; - QListWidget *m_itemList; + VListWidget *m_itemList; QAction *m_openAct; QAction *m_locateAct; diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index 4ab3421f..ab5709ea 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -293,6 +293,11 @@ void VConfigManager::initialize() m_enableGraphviz = getConfigFromSettings("global", "enable_graphviz").toBool(); m_graphvizDot = getConfigFromSettings("web", "graphviz_dot").toString(); + + m_historySize = getConfigFromSettings("global", "history_size").toInt(); + if (m_historySize < 0) { + m_historySize = 0; + } } void VConfigManager::initSettings() @@ -1210,9 +1215,42 @@ void VConfigManager::setLastOpenedFiles(const QVector &p_files } m_sessionSettings->endArray(); - qDebug() << "write" << p_files.size() - << "items in [last_opened_files] section"; +} +void VConfigManager::getHistory(QLinkedList &p_history) +{ + p_history.clear(); + + int size = m_sessionSettings->beginReadArray("history"); + for (int i = 0; i < size; ++i) { + m_sessionSettings->setArrayIndex(i); + p_history.append(VHistoryEntry::fromSettings(m_sessionSettings)); + } + + m_sessionSettings->endArray(); +} + +void VConfigManager::setHistory(const QLinkedList &p_history) +{ + if (m_hasReset) { + return; + } + + const QString section("history"); + + // Clear it first + m_sessionSettings->beginGroup(section); + m_sessionSettings->remove(""); + m_sessionSettings->endGroup(); + + m_sessionSettings->beginWriteArray(section); + int i = 0; + for (auto it = p_history.begin(); it != p_history.end(); ++it, ++i) { + m_sessionSettings->setArrayIndex(i); + it->toSettings(m_sessionSettings); + } + + m_sessionSettings->endArray(); } QVector VConfigManager::getCustomMagicWords() diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index fb8789b4..6787c983 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "vnotebook.h" #include "hgmarkdownhighlighter.h" @@ -15,6 +16,7 @@ #include "vfilesessioninfo.h" #include "utils/vmetawordmanager.h" #include "markdownitoption.h" +#include "vhistoryentry.h" class QJsonObject; class QString; @@ -356,6 +358,13 @@ public: // Write last opened files to [last_opened_files] of session.ini. void setLastOpenedFiles(const QVector &p_files); + // Read history from [history] of session.ini. + void getHistory(QLinkedList &p_history); + + void setHistory(const QLinkedList &p_history); + + int getHistorySize() const; + // Read custom magic words from [magic_words] section. QVector getCustomMagicWords(); @@ -884,6 +893,9 @@ private: QString m_plantUMLCmd; + // Size of history. + int m_historySize; + // The name of the config file in each directory, obsolete. // Use c_dirConfigFile instead. static const QString c_obsoleteDirConfigFile; @@ -2284,4 +2296,9 @@ inline void VConfigManager::setGraphvizDot(const QString &p_dotPath) m_graphvizDot = p_dotPath; setConfigToSettings("web", "graphviz_dot", p_dotPath); } + +inline int VConfigManager::getHistorySize() const +{ + return m_historySize; +} #endif // VCONFIGMANAGER_H diff --git a/src/vdirectorytree.cpp b/src/vdirectorytree.cpp index 7c99729b..c53f5a4d 100644 --- a/src/vdirectorytree.cpp +++ b/src/vdirectorytree.cpp @@ -13,6 +13,7 @@ #include "utils/vimnavigationforwidget.h" #include "utils/viconutils.h" #include "vfilelist.h" +#include "vhistorylist.h" extern VMainWindow *g_mainWin; @@ -146,6 +147,13 @@ void VDirectoryTree::initActions() connect(m_openLocationAct, &QAction::triggered, this, &VDirectoryTree::openDirectoryLocation); + m_pinToHistoryAct = new QAction(VIconUtils::menuIcon(":/resources/icons/pin.svg"), + tr("Pin To History"), + this); + m_pinToHistoryAct->setToolTip(tr("Pin selected folder to History")); + connect(m_pinToHistoryAct, &QAction::triggered, + this, &VDirectoryTree::pinDirectoryToHistory); + m_reloadAct = new QAction(tr("&Reload From Disk"), this); m_reloadAct->setToolTip(tr("Reload the content of this folder (or notebook) from disk")); connect(m_reloadAct, &QAction::triggered, @@ -435,6 +443,7 @@ void VDirectoryTree::contextMenuRequested(QPoint pos) if (item) { menu.addAction(m_openLocationAct); + menu.addAction(m_pinToHistoryAct); menu.addAction(dirInfoAct); } @@ -1211,3 +1220,11 @@ VDirectory *VDirectoryTree::currentDirectory() const return NULL; } + +void VDirectoryTree::pinDirectoryToHistory() +{ + QTreeWidgetItem *curItem = currentItem(); + V_ASSERT(curItem); + g_mainWin->getHistoryList()->pinFolder(getVDirectory(curItem)->fetchPath()); + g_mainWin->showStatusMessage(tr("1 folder pinned to History")); +} diff --git a/src/vdirectorytree.h b/src/vdirectorytree.h index 8453620e..c7e3ba24 100644 --- a/src/vdirectorytree.h +++ b/src/vdirectorytree.h @@ -93,6 +93,9 @@ private slots: // Sort sub-folders of current item's folder. void sortItems(); + // Pin selected directory to History. + void pinDirectoryToHistory(); + protected: void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; @@ -176,6 +179,7 @@ private: QAction *cutAct; QAction *pasteAct; QAction *m_openLocationAct; + QAction *m_pinToHistoryAct; QAction *m_sortAct; // Reload content from disk. diff --git a/src/veditarea.cpp b/src/veditarea.cpp index c1fef62e..2acf922c 100644 --- a/src/veditarea.cpp +++ b/src/veditarea.cpp @@ -1131,7 +1131,8 @@ void VEditArea::recordClosedFile(const VFileSessionInfo &p_file) } m_lastClosedFiles.push(p_file); - qDebug() << "pushed closed file" << p_file.m_file; + + emit fileClosed(p_file.m_file); } void VEditArea::handleFileTimerTimeout() diff --git a/src/veditarea.h b/src/veditarea.h index 49c2bf6f..1a880e23 100644 --- a/src/veditarea.h +++ b/src/veditarea.h @@ -105,6 +105,9 @@ signals: // Emit when Vim status updated. void vimStatusUpdated(const VVim *p_vim); + // Emit when @p_file is closed. + void fileClosed(const QString &p_file); + protected: void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; diff --git a/src/veditwindow.cpp b/src/veditwindow.cpp index 315fc093..4c2089cb 100644 --- a/src/veditwindow.cpp +++ b/src/veditwindow.cpp @@ -14,6 +14,7 @@ #include "vconfigmanager.h" #include "utils/viconutils.h" #include "vcart.h" +#include "vhistorylist.h" extern VConfigManager *g_config; extern VMainWindow *g_mainWin; @@ -167,7 +168,9 @@ void VEditWindow::initTabActions() } }); - m_addToCartAct = new QAction(tr("Add To Cart"), this); + m_addToCartAct = new QAction(VIconUtils::menuIcon(":/resources/icons/cart.svg"), + tr("Add To Cart"), + this); m_addToCartAct->setToolTip(tr("Add this note to Cart for further processing")); connect(m_addToCartAct, &QAction::triggered, this, [this](){ @@ -181,6 +184,24 @@ void VEditWindow::initTabActions() g_mainWin->showStatusMessage(tr("1 note added to Cart")); }); + m_pinToHistoryAct = new QAction(VIconUtils::menuIcon(":/resources/icons/pin.svg"), + tr("Pin To History"), + this); + m_pinToHistoryAct->setToolTip(tr("Pin this note to History")); + connect(m_pinToHistoryAct, &QAction::triggered, + this, [this](){ + int tab = this->m_pinToHistoryAct->data().toInt(); + Q_ASSERT(tab != -1); + + VEditTab *editor = getTab(tab); + QPointer file = editor->getFile(); + Q_ASSERT(file); + QStringList files; + files << file->fetchPath(); + g_mainWin->getHistoryList()->pinFiles(files); + g_mainWin->showStatusMessage(tr("1 note pinned to History")); + }); + m_openLocationAct = new QAction(tr("Open Note Location"), this); m_openLocationAct->setToolTip(tr("Open the folder containing this note in operating system")); connect(m_openLocationAct, &QAction::triggered, @@ -692,6 +713,9 @@ void VEditWindow::tabbarContextMenuRequested(QPoint p_pos) m_addToCartAct->setData(tab); menu.addAction(m_addToCartAct); + m_pinToHistoryAct->setData(tab); + menu.addAction(m_pinToHistoryAct); + m_noteInfoAct->setData(tab); menu.addAction(m_noteInfoAct); } else if (file->getType() == FileType::Orphan @@ -708,6 +732,9 @@ void VEditWindow::tabbarContextMenuRequested(QPoint p_pos) m_addToCartAct->setData(tab); menu.addAction(m_addToCartAct); + m_pinToHistoryAct->setData(tab); + menu.addAction(m_pinToHistoryAct); + m_noteInfoAct->setData(tab); menu.addAction(m_noteInfoAct); } diff --git a/src/veditwindow.h b/src/veditwindow.h index 085d3460..42ccfb69 100644 --- a/src/veditwindow.h +++ b/src/veditwindow.h @@ -231,6 +231,9 @@ private: // Add this note to Cart. QAction *m_addToCartAct; + // Pin this note to History. + QAction *m_pinToHistoryAct; + // Open the location (the folder containing this file) of this note. QAction *m_openLocationAct; diff --git a/src/vfilelist.cpp b/src/vfilelist.cpp index df0409fd..401aff4c 100644 --- a/src/vfilelist.cpp +++ b/src/vfilelist.cpp @@ -20,6 +20,7 @@ #include "utils/viconutils.h" #include "dialog/vtipsdialog.h" #include "vcart.h" +#include "vhistorylist.h" extern VConfigManager *g_config; extern VNote *g_vnote; @@ -190,11 +191,20 @@ void VFileList::initActions() connect(m_openLocationAct, &QAction::triggered, this, &VFileList::openFileLocation); - m_addToCartAct = new QAction(tr("Add To Cart"), this); + m_addToCartAct = new QAction(VIconUtils::menuIcon(":/resources/icons/cart.svg"), + tr("Add To Cart"), + this); m_addToCartAct->setToolTip(tr("Add selected notes to Cart for further processing")); connect(m_addToCartAct, &QAction::triggered, this, &VFileList::addFileToCart); + m_pinToHistoryAct = new QAction(VIconUtils::menuIcon(":/resources/icons/pin.svg"), + tr("Pin To History"), + this); + m_pinToHistoryAct->setToolTip(tr("Pin selected notes to History")); + connect(m_pinToHistoryAct, &QAction::triggered, + this, &VFileList::pinFileToHistory); + m_sortAct = new QAction(VIconUtils::menuIcon(":/resources/icons/sort.svg"), tr("&Sort"), this); @@ -276,6 +286,22 @@ void VFileList::addFileToCart() const .arg(items.size() > 1 ? tr("notes") : tr("note"))); } +void VFileList::pinFileToHistory() const +{ + QList items = fileList->selectedItems(); + + QStringList files; + for (int i = 0; i < items.size(); ++i) { + files << getVFile(items[i])->fetchPath(); + } + + 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 VFileList::fileInfo(VNoteFile *p_file) { if (!p_file) { @@ -621,6 +647,7 @@ void VFileList::contextMenuRequested(QPoint pos) } menu.addAction(m_addToCartAct); + menu.addAction(m_pinToHistoryAct); if (selectedSize == 1) { menu.addAction(fileInfoAct); diff --git a/src/vfilelist.h b/src/vfilelist.h index 71e7dfeb..76f6d873 100644 --- a/src/vfilelist.h +++ b/src/vfilelist.h @@ -89,6 +89,9 @@ private slots: // Add selected files to Cart. void addFileToCart() const; + // Add selected files to History and pin them. + void pinFileToHistory() const; + // Copy selected files to clipboard. // Will put a Json string into the clipboard which contains the information // about copied files. @@ -195,6 +198,8 @@ private: QAction *m_addToCartAct; + QAction *m_pinToHistoryAct; + // Context sub-menu of Open With. QMenu *m_openWithMenu; diff --git a/src/vhistoryentry.h b/src/vhistoryentry.h new file mode 100644 index 00000000..84d25354 --- /dev/null +++ b/src/vhistoryentry.h @@ -0,0 +1,64 @@ +#ifndef VHISTORYENTRY_H +#define VHISTORYENTRY_H + +#include +#include + +namespace HistoryConfig +{ + static const QString c_file = "file"; + static const QString c_date = "date"; + static const QString c_pinned = "pinned"; + static const QString c_isFolder = "is_folder"; +} + +class VHistoryEntry +{ +public: + VHistoryEntry() + : m_isPinned(false), + m_isFolder(false) + { + } + + VHistoryEntry(const QString &p_file, + const QDate &p_date, + bool p_isPinned = false, + bool p_isFolder = false) + : m_file(p_file), m_isPinned(p_isPinned), m_isFolder(p_isFolder) + { + m_date = p_date.toString(Qt::ISODate); + } + + // Fetch VHistoryEntry from @p_settings. + static VHistoryEntry fromSettings(const QSettings *p_settings) + { + VHistoryEntry entry; + entry.m_file = p_settings->value(HistoryConfig::c_file).toString(); + entry.m_date = p_settings->value(HistoryConfig::c_date).toString(); + entry.m_isPinned = p_settings->value(HistoryConfig::c_pinned).toBool(); + entry.m_isFolder = p_settings->value(HistoryConfig::c_isFolder).toBool(); + return entry; + } + + void toSettings(QSettings *p_settings) const + { + p_settings->setValue(HistoryConfig::c_file, m_file); + p_settings->setValue(HistoryConfig::c_date, m_date); + p_settings->setValue(HistoryConfig::c_pinned, m_isPinned); + p_settings->setValue(HistoryConfig::c_isFolder, m_isFolder); + } + + // File path. + QString m_file; + + // Accessed date. + // UTC in Qt::ISODate format. + QString m_date; + + bool m_isPinned; + + bool m_isFolder; +}; + +#endif // VHISTORYENTRY_H diff --git a/src/vhistorylist.cpp b/src/vhistorylist.cpp new file mode 100644 index 00000000..8d553c80 --- /dev/null +++ b/src/vhistorylist.cpp @@ -0,0 +1,507 @@ +#include "vhistorylist.h" + +#include +#include + +#include "utils/viconutils.h" +#include "utils/vutils.h" +#include "vlistwidget.h" +#include "vconfigmanager.h" +#include "vmainwindow.h" +#include "vcart.h" +#include "vnote.h" +#include "vnotefile.h" +#include "vdirectory.h" + +extern VMainWindow *g_mainWin; + +extern VConfigManager *g_config; + +extern VNote *g_vnote; + +VHistoryList::VHistoryList(QWidget *p_parent) + : QWidget(p_parent), + m_initialized(false), + m_updatePending(true), + m_currentDate(QDate::currentDate()) +{ + setupUI(); +} + +void VHistoryList::setupUI() +{ + m_clearBtn = new QPushButton(VIconUtils::buttonDangerIcon(":/resources/icons/clear_history.svg"), ""); + m_clearBtn->setToolTip(tr("Clear")); + m_clearBtn->setProperty("FlatBtn", true); + connect(m_clearBtn, &QPushButton::clicked, + this, [this]() { + init(); + + if (m_histories.size() > 0) { + int ret = VUtils::showMessage(QMessageBox::Warning, + tr("Warning"), + tr("Are you sure to clear History?"), + "", + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok, + g_mainWin, + MessageBoxType::Danger); + if (ret == QMessageBox::Ok) { + m_histories.clear(); + g_config->setHistory(m_histories); + m_updatePending = true; + updateList(); + } + } + }); + + QHBoxLayout *btnLayout = new QHBoxLayout; + btnLayout->addWidget(m_clearBtn); + btnLayout->addStretch(); + btnLayout->setContentsMargins(0, 0, 0, 0); + + m_itemList = new VListWidget(); + m_itemList->setAttribute(Qt::WA_MacShowFocusRect, false); + m_itemList->setContextMenuPolicy(Qt::CustomContextMenu); + m_itemList->setSelectionMode(QAbstractItemView::ExtendedSelection); + connect(m_itemList, &QListWidget::customContextMenuRequested, + this, &VHistoryList::handleContextMenuRequested); + connect(m_itemList, &QListWidget::itemActivated, + this, &VHistoryList::openItem); + + QVBoxLayout *mainLayout = new QVBoxLayout(); + mainLayout->addLayout(btnLayout); + mainLayout->addWidget(m_itemList); + mainLayout->setContentsMargins(3, 0, 3, 0); + + setLayout(mainLayout); +} + +void VHistoryList::initActions() +{ + m_openAct = new QAction(tr("&Open"), this); + m_openAct->setToolTip(tr("Open selected notes")); + connect(m_openAct, &QAction::triggered, + this, &VHistoryList::openSelectedItems); + + m_locateAct = new QAction(VIconUtils::menuIcon(":/resources/icons/locate_note.svg"), + tr("&Locate To Folder"), + this); + m_locateAct->setToolTip(tr("Locate the folder of current note")); + connect(m_locateAct, &QAction::triggered, + this, &VHistoryList::locateCurrentItem); + + m_pinAct = new QAction(VIconUtils::menuIcon(":/resources/icons/pin.svg"), + tr("Pin"), + this); + m_pinAct->setToolTip(tr("Pin selected notes in History")); + connect(m_pinAct, &QAction::triggered, + this, &VHistoryList::pinSelectedItems); + + m_unpinAct = new QAction(tr("Unpin"), this); + m_unpinAct->setToolTip(tr("Unpin selected notes in History")); + connect(m_unpinAct, &QAction::triggered, + this, &VHistoryList::unpinSelectedItems); + + m_addToCartAct = new QAction(VIconUtils::menuIcon(":/resources/icons/cart.svg"), + tr("Add To Cart"), + this); + m_addToCartAct->setToolTip(tr("Add selected notes to Cart for further processing")); + connect(m_addToCartAct, &QAction::triggered, + this, &VHistoryList::addFileToCart); +} + +void VHistoryList::addFile(const QString &p_filePath) +{ + init(); + + QStringList files; + files << p_filePath; + addFilesInternal(files, false); + + if (isVisible()) { + updateList(); + } +} + +void VHistoryList::pinFiles(const QStringList &p_files) +{ + init(); + + addFilesInternal(p_files, true); + if (isVisible()) { + updateList(); + } +} + +void VHistoryList::unpinFiles(const QStringList &p_files) +{ + init(); + + for (auto const & file : p_files) { + auto it = findFileInHistory(file); + if (it != m_histories.end()) { + it->m_isPinned = false; + } + } + + g_config->setHistory(m_histories); + m_updatePending = true; +} + +void VHistoryList::pinFolder(const QString &p_folder) +{ + init(); + + auto it = findFileInHistory(p_folder); + if (it != m_histories.end()) { + return; + } + + m_histories.append(VHistoryEntry(p_folder, QDate::currentDate(), true, true)); + + checkHistorySize(); + + g_config->setHistory(m_histories); + m_updatePending = true; + + if (isVisible()) { + updateList(); + } +} + +void VHistoryList::addFilesInternal(const QStringList &p_files, bool p_isPinned) +{ + for (auto const & file : p_files) { + // Find it in existing enries. + bool pinnedBefore = false; + auto it = findFileInHistory(file); + if (it != m_histories.end()) { + pinnedBefore = it->m_isPinned; + m_histories.erase(it); + } + + // Append an entry at the end. + bool pin = p_isPinned ? true : (pinnedBefore ? true : false); + m_histories.append(VHistoryEntry(file, QDate::currentDate(), pin)); + } + + checkHistorySize(); + + g_config->setHistory(m_histories); + m_updatePending = true; +} + +void VHistoryList::checkHistorySize() +{ + int numToRemove = m_histories.size() - g_config->getHistorySize(); + for (auto rit = m_histories.begin(); numToRemove > 0 && rit != m_histories.end();) { + if (rit->m_isPinned) { + ++rit; + continue; + } + + rit = m_histories.erase(rit); + --numToRemove; + } +} + +void VHistoryList::init() +{ + if (m_initialized) { + return; + } + + m_folderIcon = VIconUtils::treeViewIcon(":/resources/icons/dir_item.svg"); + + initActions(); + + g_config->getHistory(m_histories); + + m_initialized = true; + m_updatePending = true; +} + +void VHistoryList::showEvent(QShowEvent *p_event) +{ + init(); + + if (m_currentDate != QDate::currentDate()) { + m_currentDate = QDate::currentDate(); + m_updatePending = true; + } + + updateList(); + + QWidget::showEvent(p_event); +} + +QLinkedList::iterator VHistoryList::findFileInHistory(const QString &p_file) +{ + for (QLinkedList::iterator it = m_histories.begin(); + it != m_histories.end(); + ++it) { + if (VUtils::equalPath(p_file, it->m_file)) { + return it; + } + } + + return m_histories.end(); +} + +struct SeparatorItem +{ + SeparatorItem() + : m_valid(false) + { + } + + QString m_date; + QListWidgetItem *m_item; + bool m_valid; +}; + +void VHistoryList::updateList() +{ + if (!m_updatePending) { + return; + } + + m_updatePending = false; + + m_itemList->clearAll(); + + if (m_histories.isEmpty()) { + return; + } + + // Add separators. + // Pinned separator. + SeparatorItem pinItem; + pinItem.m_item = VListWidget::createSeparatorItem(tr("Pinned")); + m_itemList->addItem(pinItem.m_item); + + const int sepSize = 4; + QString sepText[4] = {tr("Today"), tr("Yesterday"), tr("Last 7 Days"), tr("Older")}; + QVector seps(sepSize); + + seps[0].m_date = m_currentDate.toString(Qt::ISODate); + seps[1].m_date = m_currentDate.addDays(-1).toString(Qt::ISODate); + seps[2].m_date = m_currentDate.addDays(-7).toString(Qt::ISODate); + // Leave the last string empty. + + for (int i = 0; i < sepSize; ++i) { + seps[i].m_item = VListWidget::createSeparatorItem(sepText[i]); + m_itemList->addItem(seps[i].m_item); + } + + for (auto it = m_histories.cbegin(); it != m_histories.cend(); ++it) { + QListWidgetItem *item = new QListWidgetItem(VUtils::fileNameFromPath(it->m_file)); + item->setToolTip(it->m_file); + item->setData(Qt::UserRole, (qulonglong)&(*it)); + + if (it->m_isFolder) { + item->setIcon(m_folderIcon); + } + + if (it->m_isPinned) { + m_itemList->insertItem(m_itemList->row(pinItem.m_item) + 1, item); + pinItem.m_valid = true; + continue; + } + + for (int i = 0; i < sepSize; ++i) { + if (it->m_date >= seps[i].m_date) { + m_itemList->insertItem(m_itemList->row(seps[i].m_item) + 1, item); + seps[i].m_valid = true; + break; + } + } + } + + // We always display pinned separator. + + for (int i = 0; i < sepSize; ++i) { + if (!seps[i].m_valid) { + delete seps[i].m_item; + } + } + + seps.clear(); +} + +QWidget *VHistoryList::getContentWidget() const +{ + return m_itemList; +} + +void VHistoryList::handleContextMenuRequested(QPoint p_pos) +{ + QListWidgetItem *item = m_itemList->itemAt(p_pos); + if (!item || VListWidget::isSeparatorItem(item)) { + return; + } + + QMenu menu(this); + menu.setToolTipsVisible(true); + + menu.addAction(m_openAct); + + QList selectedItems = m_itemList->selectedItems(); + if (selectedItems.size() == 1) { + menu.addAction(m_locateAct); + } + + bool allPinned = true, allUnpinned = true; + for (auto const & it : selectedItems) { + if (getHistoryEntry(it)->m_isPinned) { + allUnpinned = false; + } else { + allPinned = false; + } + } + + if (allUnpinned) { + menu.addAction(m_pinAct); + } else if (allPinned) { + menu.addAction(m_unpinAct); + } + + menu.addSeparator(); + + menu.addAction(m_addToCartAct); + + menu.exec(m_itemList->mapToGlobal(p_pos)); +} + +bool VHistoryList::isFolder(const QListWidgetItem *p_item) const +{ + return getHistoryEntry(p_item)->m_isFolder; +} + +VHistoryEntry *VHistoryList::getHistoryEntry(const QListWidgetItem *p_item) const +{ + return (VHistoryEntry *)p_item->data(Qt::UserRole).toULongLong(); +} + +QString VHistoryList::getFilePath(const QListWidgetItem *p_item) const +{ + return getHistoryEntry(p_item)->m_file; +} + +void VHistoryList::pinSelectedItems() +{ + QStringList files; + QList selectedItems = m_itemList->selectedItems(); + for (auto it : selectedItems) { + files << getFilePath(it); + } + + pinFiles(files); +} + +void VHistoryList::unpinSelectedItems() +{ + QStringList files; + QList selectedItems = m_itemList->selectedItems(); + for (auto it : selectedItems) { + files << getFilePath(it); + } + + unpinFiles(files); + + if (isVisible()) { + updateList(); + } +} + +void VHistoryList::openSelectedItems() const +{ + QStringList files; + QList selectedItems = m_itemList->selectedItems(); + + if (selectedItems.size() == 1 && isFolder(selectedItems.first())) { + // Locate to the folder. + VDirectory *dir = g_vnote->getInternalDirectory(getFilePath(selectedItems.first())); + if (dir) { + g_mainWin->locateDirectory(dir); + } + + return; + } + + for (auto it : selectedItems) { + if (isFolder(it)) { + // Skip folders. + continue; + } + + files << getFilePath(it); + } + + if (!files.isEmpty()) { + g_mainWin->openFiles(files); + } +} + +void VHistoryList::openItem(const QListWidgetItem *p_item) const +{ + if (!p_item) { + return; + } + + if (isFolder(p_item)) { + // Locate to the folder. + VDirectory *dir = g_vnote->getInternalDirectory(getFilePath(p_item)); + if (dir) { + g_mainWin->locateDirectory(dir); + } + + return; + } + + QStringList files; + files << getFilePath(p_item); + g_mainWin->openFiles(files); +} + +void VHistoryList::locateCurrentItem() +{ + auto item = m_itemList->currentItem(); + if (!item) { + return; + } + + VFile *file = g_vnote->getInternalFile(getFilePath(item)); + if (file) { + g_mainWin->locateFile(file); + } +} + +void VHistoryList::showNavigation() +{ + VNavigationMode::showNavigation(m_itemList); +} + +bool VHistoryList::handleKeyNavigation(int p_key, bool &p_succeed) +{ + static bool secondKey = false; + return VNavigationMode::handleKeyNavigation(m_itemList, + secondKey, + p_key, + p_succeed); +} + +void VHistoryList::addFileToCart() const +{ + QList items = m_itemList->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"))); +} diff --git a/src/vhistorylist.h b/src/vhistorylist.h new file mode 100644 index 00000000..8761e7be --- /dev/null +++ b/src/vhistorylist.h @@ -0,0 +1,104 @@ +#ifndef VHISTORYLIST_H +#define VHISTORYLIST_H + +#include +#include +#include + +#include "vhistoryentry.h" +#include "vnavigationmode.h" + +class QPushButton; +class VListWidget; +class QListWidgetItem; +class QLabel; +class QAction; +class QShowEvent; + +class VHistoryList : public QWidget, public VNavigationMode +{ + Q_OBJECT +public: + explicit VHistoryList(QWidget *p_parent = nullptr); + + QWidget *getContentWidget() const; + + void pinFiles(const QStringList &p_files); + + void pinFolder(const QString &p_folder); + + // Implementations for VNavigationMode. + void showNavigation() Q_DECL_OVERRIDE; + bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE; + +public slots: + void addFile(const QString &p_filePath); + +protected: + void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE; + +private slots: + void handleContextMenuRequested(QPoint p_pos); + + void openSelectedItems() const; + + void openItem(const QListWidgetItem *p_item) const; + + void pinSelectedItems(); + + void unpinSelectedItems(); + + void locateCurrentItem(); + + // Add selected files to Cart. + void addFileToCart() const; + +private: + void setupUI(); + + void initActions(); + + // Read data from config file. + void init(); + + void updateList(); + + QLinkedList::iterator findFileInHistory(const QString &p_file); + + QString getFilePath(const QListWidgetItem *p_item) const; + + VHistoryEntry *getHistoryEntry(const QListWidgetItem *p_item) const; + + bool isFolder(const QListWidgetItem *p_item) const; + + // @p_isPinned won't change it if an item is pinned already. + void addFilesInternal(const QStringList &p_files, bool p_isPinned); + + void unpinFiles(const QStringList &p_files); + + void checkHistorySize(); + + QPushButton *m_clearBtn; + VListWidget *m_itemList; + + QAction *m_openAct; + QAction *m_locateAct; + QAction *m_pinAct; + QAction *m_unpinAct; + QAction *m_addToCartAct; + + // Whether data is loaded. + bool m_initialized; + + // New files are appended to the end. + QLinkedList m_histories; + + // Whether we need to update the list. + bool m_updatePending; + + QDate m_currentDate; + + QIcon m_folderIcon; +}; + +#endif // VHISTORYLIST_H diff --git a/src/vlistwidget.cpp b/src/vlistwidget.cpp index 52b9093d..c7e399d7 100644 --- a/src/vlistwidget.cpp +++ b/src/vlistwidget.cpp @@ -8,6 +8,9 @@ #include "utils/vutils.h" #include "utils/vimnavigationforwidget.h" #include "vstyleditemdelegate.h" +#include "vpalette.h" + +extern VPalette *g_palette; VListWidget::VListWidget(QWidget *p_parent) : QListWidget(p_parent), @@ -20,7 +23,7 @@ VListWidget::VListWidget(QWidget *p_parent) m_searchInput->hide(); - m_delegate = new VStyledItemDelegate(this); + m_delegate = new VStyledItemDelegate(this, NULL); setItemDelegate(m_delegate); } @@ -108,6 +111,10 @@ QList VListWidget::searchItems(const QString &p_text, QList res; res.reserve(items.size()); for (int i = 0; i < items.size(); ++i) { + if (items[i]->type() == ItemTypeSeparator) { + continue; + } + res.append(items[i]); } @@ -212,3 +219,14 @@ QSize VListWidget::sizeHint() const } } +QListWidgetItem *VListWidget::createSeparatorItem(const QString &p_text) +{ + QListWidgetItem *item = new QListWidgetItem(p_text, NULL, ItemTypeSeparator); + item->setFlags(Qt::NoItemFlags); + return item; +} + +bool VListWidget::isSeparatorItem(const QListWidgetItem *p_item) +{ + return p_item->type() == ItemTypeSeparator; +} diff --git a/src/vlistwidget.h b/src/vlistwidget.h index 88a64878..92543bd7 100644 --- a/src/vlistwidget.h +++ b/src/vlistwidget.h @@ -34,10 +34,14 @@ public: virtual QSize sizeHint() const Q_DECL_OVERRIDE; + void setFitContent(bool p_enabled); + // Sort @p_list according to @p_sortedIdx. static void sortListWidget(QListWidget *p_list, const QVector &p_sortedIdx); - void setFitContent(bool p_enabled); + static QListWidgetItem *createSeparatorItem(const QString &p_text); + + static bool isSeparatorItem(const QListWidgetItem *p_item); private slots: void handleSearchModeTriggered(bool p_inSearchMode, bool p_focus); diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 01fe2de5..bad02952 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -45,6 +45,7 @@ #include "vhelpue.h" #include "vlistfolderue.h" #include "dialog/vfixnotebookdialog.h" +#include "vhistorylist.h" extern VConfigManager *g_config; @@ -64,6 +65,8 @@ extern QFile g_logFile; #define COLOR_PIXMAP_ICON_SIZE 64 +#define NAVI_BOX_NOTEBOOKS_IDX 0 + VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent) : QMainWindow(p_parent), @@ -145,10 +148,12 @@ void VMainWindow::registerCaptainAndNavigationTargets() m_captain->registerNavigationTarget(m_notebookSelector); m_captain->registerNavigationTarget(m_dirTree); m_captain->registerNavigationTarget(m_fileList); + m_captain->registerNavigationTarget(m_historyList); m_captain->registerNavigationTarget(m_editArea); m_captain->registerNavigationTarget(m_toolBox); m_captain->registerNavigationTarget(outline); m_captain->registerNavigationTarget(m_snippetList); + m_captain->registerNavigationTarget(m_cart); m_captain->registerNavigationTarget(m_searcher); // Register Captain mode targets. @@ -196,14 +201,7 @@ void VMainWindow::registerCaptainAndNavigationTargets() void VMainWindow::setupUI() { - m_naviBox = new VToolBox(); - - setupNotebookPanel(); - - m_naviBox->addItem(m_nbSplitter, - ":/resources/icons/notebook.svg", - tr("Notebooks"), - m_dirTree); + setupNaviBox(); m_editArea = new VEditArea(); m_editArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -211,6 +209,9 @@ void VMainWindow::setupUI() m_fileList->setEditArea(m_editArea); m_dirTree->setEditArea(m_editArea); + connect(m_editArea, &VEditArea::fileClosed, + m_historyList, &VHistoryList::addFile); + // Main Splitter m_mainSplitter = new QSplitter(); m_mainSplitter->setObjectName("MainSplitter"); @@ -265,6 +266,23 @@ void VMainWindow::setupUI() initTrayIcon(); } +void VMainWindow::setupNaviBox() +{ + m_naviBox = new VToolBox(); + + setupNotebookPanel(); + m_naviBox->addItem(m_nbSplitter, + ":/resources/icons/notebook.svg", + tr("Notebooks"), + m_dirTree); + + m_historyList = new VHistoryList(); + m_naviBox->addItem(m_historyList, + ":/resources/icons/history.svg", + tr("History"), + m_historyList->getContentWidget()); +} + void VMainWindow::setupNotebookPanel() { m_notebookSelector = new VNotebookSelector(); @@ -1265,7 +1283,8 @@ void VMainWindow::initToolsDock() tr("Snippets")); m_toolBox->addItem(m_cart, ":/resources/icons/cart.svg", - tr("Cart")); + tr("Cart"), + m_cart->getContentWidget()); m_toolDock->setWidget(m_toolBox); addDockWidget(Qt::RightDockWidgetArea, m_toolDock); @@ -3134,5 +3153,5 @@ void VMainWindow::kickOffStartUpTimer(const QStringList &p_files) void VMainWindow::showNotebookPanel() { changePanelView(PanelViewState::VerticalMode); - m_naviBox->setCurrentIndex(0, false); + m_naviBox->setCurrentIndex(NAVI_BOX_NOTEBOOKS_IDX, false); } diff --git a/src/vmainwindow.h b/src/vmainwindow.h index b36da0bf..86159688 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -42,6 +42,7 @@ class VCart; class VSearcher; class QPrinter; class VUniversalEntry; +class VHistoryList; enum class PanelViewState { @@ -79,6 +80,8 @@ public: VNotebookSelector *getNotebookSelector() const; + VHistoryList *getHistoryList() const; + // View and edit the information of @p_file, which is an orphan file. void editOrphanFileInfo(VFile *p_file); @@ -199,6 +202,8 @@ protected: private: void setupUI(); + void setupNaviBox(); + void setupNotebookPanel(); void initToolBar(); @@ -442,6 +447,8 @@ private: VUniversalEntry *m_ue; + VHistoryList *m_historyList; + // Interval of the shared memory timer in ms. static const int c_sharedMemTimerInterval; }; @@ -481,6 +488,11 @@ inline VCart *VMainWindow::getCart() const return m_cart; } +inline VHistoryList *VMainWindow::getHistoryList() const +{ + return m_historyList; +} + inline VDirectoryTree *VMainWindow::getDirectoryTree() const { return m_dirTree; diff --git a/src/vnavigationmode.cpp b/src/vnavigationmode.cpp index d66c3bac..71a6347c 100644 --- a/src/vnavigationmode.cpp +++ b/src/vnavigationmode.cpp @@ -70,7 +70,7 @@ QList VNavigationMode::getVisibleItems(const QListWidget *p_w QList items; for (int i = 0; i < p_widget->count(); ++i) { QListWidgetItem *item = p_widget->item(i); - if (!item->isHidden()) { + if (!item->isHidden() && item->flags() != Qt::NoItemFlags) { items.append(item); } } diff --git a/src/vnote.qrc b/src/vnote.qrc index 268d9110..b0470a78 100644 --- a/src/vnote.qrc +++ b/src/vnote.qrc @@ -214,5 +214,8 @@ utils/markdown-it/markdown-it-imsize.min.js utils/markdown-it/markdown-it-emoji.min.js resources/icons/notebook.svg + resources/icons/history.svg + resources/icons/clear_history.svg + resources/icons/pin.svg diff --git a/src/vsearchresulttree.cpp b/src/vsearchresulttree.cpp index 3f2ab43c..f2b97cd0 100644 --- a/src/vsearchresulttree.cpp +++ b/src/vsearchresulttree.cpp @@ -9,6 +9,7 @@ #include "vmainwindow.h" #include "vnotefile.h" #include "vcart.h" +#include "vhistorylist.h" extern VNote *g_vnote; @@ -53,10 +54,19 @@ void VSearchResultTree::initActions() connect(m_locateAct, &QAction::triggered, this, &VSearchResultTree::locateCurrentItem); - m_addToCartAct = new QAction(tr("Add To Cart"), this); + m_addToCartAct = new QAction(VIconUtils::menuIcon(":/resources/icons/cart.svg"), + tr("Add To Cart"), + this); m_addToCartAct->setToolTip(tr("Add selected notes to Cart for further processing")); connect(m_addToCartAct, &QAction::triggered, this, &VSearchResultTree::addSelectedItemsToCart); + + m_pinToHistoryAct = new QAction(VIconUtils::menuIcon(":/resources/icons/pin.svg"), + tr("Pin To History"), + this); + m_pinToHistoryAct->setToolTip(tr("Pin selected notes to History")); + connect(m_pinToHistoryAct, &QAction::triggered, + this, &VSearchResultTree::pinSelectedItemsToHistory); } void VSearchResultTree::updateResults(const QList > &p_items) @@ -165,6 +175,7 @@ void VSearchResultTree::handleContextMenuRequested(QPoint p_pos) if (hasNote) { menu.addAction(m_addToCartAct); + menu.addAction(m_pinToHistoryAct); } menu.exec(mapToGlobal(p_pos)); @@ -207,6 +218,25 @@ void VSearchResultTree::addSelectedItemsToCart() } } +void VSearchResultTree::pinSelectedItemsToHistory() +{ + QList items = selectedItems(); + QStringList files; + for (int i = 0; i < items.size(); ++i) { + const QSharedPointer &resItem = itemResultData(items[i]); + if (resItem->m_type == VSearchResultItem::Note) { + files << resItem->m_path; + } + } + + if (!files.isEmpty()) { + g_mainWin->getHistoryList()->pinFiles(files); + g_mainWin->showStatusMessage(tr("%1 %2 pinned to History") + .arg(files.size()) + .arg(files.size() > 1 ? tr("notes") : tr("note"))); + } +} + VSearchResultItem::ItemType VSearchResultTree::itemResultType(const QTreeWidgetItem *p_item) const { Q_ASSERT(p_item); diff --git a/src/vsearchresulttree.h b/src/vsearchresulttree.h index 154caaae..f6e1216e 100644 --- a/src/vsearchresulttree.h +++ b/src/vsearchresulttree.h @@ -34,6 +34,8 @@ private slots: void addSelectedItemsToCart(); + void pinSelectedItemsToHistory(); + private: void appendItem(const QSharedPointer &p_item); @@ -56,6 +58,8 @@ private: QAction *m_locateAct; QAction *m_addToCartAct; + + QAction *m_pinToHistoryAct; }; #endif // VSEARCHRESULTTREE_H diff --git a/src/vsnippetlist.cpp b/src/vsnippetlist.cpp index ae911e25..5887f103 100644 --- a/src/vsnippetlist.cpp +++ b/src/vsnippetlist.cpp @@ -10,6 +10,7 @@ #include "dialog/vconfirmdeletiondialog.h" #include "vmainwindow.h" #include "utils/viconutils.h" +#include "vlistwidget.h" extern VConfigManager *g_config; @@ -70,7 +71,7 @@ void VSnippetList::setupUI() btnLayout->addWidget(m_numLabel); btnLayout->setContentsMargins(0, 0, 0, 0); - m_snippetList = new QListWidget(); + m_snippetList = new VListWidget(); m_snippetList->setAttribute(Qt::WA_MacShowFocusRect, false); m_snippetList->setContextMenuPolicy(Qt::CustomContextMenu); m_snippetList->setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -380,7 +381,7 @@ void VSnippetList::makeSureFolderExist() const void VSnippetList::updateContent() { - m_snippetList->clear(); + m_snippetList->clearAll(); for (int i = 0; i < m_snippets.size(); ++i) { const VSnippet &snip = m_snippets[i]; diff --git a/src/vsnippetlist.h b/src/vsnippetlist.h index af396082..979af7ae 100644 --- a/src/vsnippetlist.h +++ b/src/vsnippetlist.h @@ -9,7 +9,7 @@ #include "vnavigationmode.h" class QPushButton; -class QListWidget; +class VListWidget; class QListWidgetItem; class QLabel; class QAction; @@ -92,7 +92,7 @@ private: QPushButton *m_addBtn; QPushButton *m_locateBtn; QLabel *m_numLabel; - QListWidget *m_snippetList; + VListWidget *m_snippetList; QAction *m_applyAct; QAction *m_infoAct; diff --git a/src/vstyleditemdelegate.cpp b/src/vstyleditemdelegate.cpp index d0dd0a36..8cf3d172 100644 --- a/src/vstyleditemdelegate.cpp +++ b/src/vstyleditemdelegate.cpp @@ -1,17 +1,25 @@ #include "vstyleditemdelegate.h" #include -#include +#include +#include +#include #include "vpalette.h" +#include "vtreewidget.h" extern VPalette *g_palette; -VStyledItemDelegate::VStyledItemDelegate(QObject *p_parent) - : QStyledItemDelegate(p_parent) +VStyledItemDelegate::VStyledItemDelegate(QListWidget *p_list, VTreeWidget *p_tree) + : QStyledItemDelegate(p_list ? (QObject *)p_list : (QObject *)p_tree), + m_list(p_list), + m_tree(p_tree) { + Q_ASSERT(!(m_list && m_tree)); m_itemHitBg = QBrush(QColor(g_palette->color("search_hit_item_bg"))); m_itemHitFg = QBrush(QColor(g_palette->color("search_hit_item_fg"))); + m_itemSeparatorBg = QBrush(QColor(g_palette->color("item_separator_bg"))); + m_itemSeparatorFg = QBrush(QColor(g_palette->color("item_separator_fg"))); } void VStyledItemDelegate::paint(QPainter *p_painter, @@ -25,7 +33,30 @@ void VStyledItemDelegate::paint(QPainter *p_painter, // option.palette.setBrush(QPalette::Base, m_itemHitBg); option.palette.setBrush(QPalette::Text, m_itemHitFg); QStyledItemDelegate::paint(p_painter, option, p_index); + } else if (itemType(p_index) == ItemTypeSeparator) { + QStyleOptionViewItem option(p_option); + p_painter->fillRect(option.rect, m_itemSeparatorBg); + option.palette.setBrush(QPalette::Text, m_itemSeparatorFg); + QStyledItemDelegate::paint(p_painter, option, p_index); } else { QStyledItemDelegate::paint(p_painter, p_option, p_index); } } + +int VStyledItemDelegate::itemType(const QModelIndex &p_index) const +{ + int type = 0; + if (m_list) { + QListWidgetItem *item = m_list->item(p_index.row()); + if (item) { + type = item->type(); + } + } else if (m_tree) { + QTreeWidgetItem *item = m_tree->getItemFromIndex(p_index); + if (item) { + type = item->type(); + } + } + + return type; +} diff --git a/src/vstyleditemdelegate.h b/src/vstyleditemdelegate.h index a6c062ca..e3b86c5c 100644 --- a/src/vstyleditemdelegate.h +++ b/src/vstyleditemdelegate.h @@ -5,11 +5,16 @@ #include #include +class QListWidget; +class VTreeWidget; + +// Should be larger then QListWidgetItem::UserType and QTreeWidgetItem::UserType. +#define ItemTypeSeparator 2000 class VStyledItemDelegate : public QStyledItemDelegate { public: - explicit VStyledItemDelegate(QObject *p_parent = Q_NULLPTR); + explicit VStyledItemDelegate(QListWidget *p_list = nullptr, VTreeWidget *p_tree = nullptr); virtual void paint(QPainter *p_painter, const QStyleOptionViewItem &p_option, @@ -22,11 +27,19 @@ public: private: bool isHit(const QModelIndex &p_index) const; - QBrush m_itemHitBg; + int itemType(const QModelIndex &p_index) const; + QBrush m_itemHitBg; QBrush m_itemHitFg; + QBrush m_itemSeparatorBg; + QBrush m_itemSeparatorFg; + QSet m_hitItems; + + // m_list OR m_tree (but not both) could be not NULL. + const QListWidget *m_list; + const VTreeWidget *m_tree; }; inline void VStyledItemDelegate::setHitItems(const QSet &p_hitItems) diff --git a/src/vtoolbox.cpp b/src/vtoolbox.cpp index d3e9b83d..1fc0a389 100644 --- a/src/vtoolbox.cpp +++ b/src/vtoolbox.cpp @@ -52,6 +52,7 @@ int VToolBox::addItem(QWidget *p_widget, QPushButton *btn = new QPushButton(icon, ""); btn->setToolTip(p_text); btn->setProperty("FlatBtn", true); + btn->setProperty("ToolBoxTitleBtn", true); connect(btn, &QPushButton::clicked, this, [this]() { QObject *btn = sender(); diff --git a/src/vtreewidget.cpp b/src/vtreewidget.cpp index aec84fe1..d98c7886 100644 --- a/src/vtreewidget.cpp +++ b/src/vtreewidget.cpp @@ -44,7 +44,7 @@ VTreeWidget::VTreeWidget(QWidget *p_parent) effect->setOpacity(SEARCH_INPUT_IDLE_OPACITY); }); - m_delegate = new VStyledItemDelegate(this); + m_delegate = new VStyledItemDelegate(NULL, this); setItemDelegate(m_delegate); connect(this, &VTreeWidget::itemExpanded, @@ -155,6 +155,10 @@ QList VTreeWidget::searchItems(const QString &p_text, QList res; res.reserve(items.size()); for (int i = 0; i < items.size(); ++i) { + if (items[i]->type() == ItemTypeSeparator) { + continue; + } + res.append(items[i]); } diff --git a/src/vtreewidget.h b/src/vtreewidget.h index 72a95a3b..d39075f8 100644 --- a/src/vtreewidget.h +++ b/src/vtreewidget.h @@ -43,6 +43,8 @@ public: void setFitContent(bool p_enabled); + QTreeWidgetItem *getItemFromIndex(const QModelIndex &p_index) const; + protected: void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE; @@ -95,4 +97,9 @@ inline void VTreeWidget::setFitContent(bool p_enabled) setSizeAdjustPolicy(m_fitContent ? QAbstractScrollArea::AdjustToContents : QAbstractScrollArea::AdjustIgnored); } + +inline QTreeWidgetItem *VTreeWidget::getItemFromIndex(const QModelIndex &p_index) const +{ + return itemFromIndex(p_index); +} #endif // VTREEWIDGET_H diff --git a/src/vvimindicator.cpp b/src/vvimindicator.cpp index ea5ec127..866e6939 100644 --- a/src/vvimindicator.cpp +++ b/src/vvimindicator.cpp @@ -16,8 +16,6 @@ extern VConfigManager *g_config; -extern VPalette *g_palette; - VVimIndicator::VVimIndicator(QWidget *p_parent) : QWidget(p_parent), m_vim(NULL) {