Browse Source

add Cart to collect notes for further processing

Le Tan 8 years ago
parent
commit
8dfcda0e51

+ 14 - 0
src/resources/icons/cart.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<g>
+	<path fill="#010101" d="M169.6,377.6c-22.882,0-41.6,18.718-41.6,41.601c0,22.882,18.718,41.6,41.6,41.6s41.601-18.718,41.601-41.6
+		C211.2,396.317,192.481,377.6,169.6,377.6z M48,51.2v41.6h41.6l74.883,151.682l-31.308,50.954c-3.118,5.2-5.2,12.482-5.2,19.765
+		c0,27.85,19.025,41.6,44.825,41.6H416v-40H177.893c-3.118,0-5.2-2.082-5.2-5.2c0-1.036,2.207-5.2,2.207-5.2l20.782-32.8h154.954
+		c15.601,0,29.128-8.317,36.4-21.836l74.882-128.8c1.237-2.461,2.082-6.246,2.082-10.399c0-11.446-9.364-19.765-20.8-19.765H135.364
+		L115.6,51.2H48z M374.399,377.6c-22.882,0-41.6,18.718-41.6,41.601c0,22.882,18.718,41.6,41.6,41.6S416,442.082,416,419.2
+		C416,396.317,397.281,377.6,374.399,377.6z"/>
+</g>
+</svg>

+ 10 - 0
src/resources/icons/clear_cart.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<path style="fill:#C9302C" d="M443.6,387.1L312.4,255.4l131.5-130c5.4-5.4,5.4-14.2,0-19.6l-37.4-37.6c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4
+	L256,197.8L124.9,68.3c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4L68,105.9c-5.4,5.4-5.4,14.2,0,19.6l131.5,130L68.4,387.1
+	c-2.6,2.6-4.1,6.1-4.1,9.8c0,3.7,1.4,7.2,4.1,9.8l37.4,37.6c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1L256,313.1l130.7,131.1
+	c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1l37.4-37.6c2.6-2.6,4.1-6.1,4.1-9.8C447.7,393.2,446.2,389.7,443.6,387.1z"/>
+</svg>

+ 10 - 0
src/resources/icons/delete_cart_item.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<path style="fill:#000000" d="M341,128V99c0-19.1-14.5-35-34.5-35H205.4C185.5,64,171,79.9,171,99v29H80v32h9.2c0,0,5.4,0.6,8.2,3.4c2.8,2.8,3.9,9,3.9,9
+	l19,241.7c1.5,29.4,1.5,33.9,36,33.9h199.4c34.5,0,34.5-4.4,36-33.8l19-241.6c0,0,1.1-6.3,3.9-9.1c2.8-2.8,8.2-3.4,8.2-3.4h9.2v-32
+	h-91V128z M192,99c0-9.6,7.8-15,17.7-15h91.7c9.9,0,18.6,5.5,18.6,15v29H192V99z M183.5,384l-10.3-192h20.3L204,384H183.5z
+	 M267.1,384h-22V192h22V384z M328.7,384h-20.4l10.5-192h20.3L328.7,384z"/>
+</svg>

+ 1 - 1
src/resources/themes/v_moonlight/v_moonlight.palette

@@ -11,7 +11,7 @@ codeblock_css_file=v_moonlight_codeblock.css
 ; without background. You could just specify the foreground colors mapping here.
 ; It is useful for dark mode theme. '#aabbcc' or 'red' formats are supported.
 ; col1:col1_new,col2:col2_new
-css_color_mapping=#abb2bf:#363636,#282c34:#f5f5f5,#61afef:#0099ff,#98c379:#8e24aa,#2c313a:#e0e0e0,#5c6370:#666666,#373e47:#999999,#6c6c6c:#444444,#c678dd:#0000ee,#e06c75:#880000,#56b6c2:#af00d7,#e6c07b:#008700,#d19a66:#bc6060,#61aeee:#bc6060
+css_color_mapping=#abb2bf:#222222,#282c34:#f5f5f5,#61afef:#0099ff,#98c379:#8e24aa,#2c313a:#e0e0e0,#5c6370:#666666,#373e47:#999999,#6c6c6c:#444444,#c678dd:#0000ee,#e06c75:#880000,#56b6c2:#af00d7,#e6c07b:#008700,#d19a66:#bc6060,#61aeee:#bc6060
 
 [phony]
 ; Abstract color attributes.

+ 3 - 3
src/resources/themes/v_pure/v_pure.css

@@ -1,14 +1,14 @@
 body {
     margin: 0 auto;
     font-family: Helvetica, sans-serif, Tahoma, Arial, Verdana, Geneva, Georgia, Palatino, "Times New Roman", "Hiragino Sans GB", "冬青黑体", "Microsoft YaHei", "微软雅黑", "Microsoft YaHei UI", "WenQuanYi Micro Hei", "文泉驿雅黑", Dengxian, "等线体", STXihei, "华文细黑", "Liberation Sans", "Droid Sans", NSimSun, "新宋体", SimSun, "宋体";
-    color: #363636;
+    color: #222222;
     line-height: 1;
     padding: 15px;
     background: #F5F5F5;
 }
 
 h1, h2, h3, h4, h5, h6 {
-    color: #363636;
+    color: #222222;
     font-weight: bold;
     margin-top: 20px;
     margin-bottom: 10px;
@@ -93,7 +93,7 @@ pre code {
     display: block;
     overflow-x: auto;
     padding: 0.5em;
-    color: #363636;
+    color: #222222;
     background-color: #E0E0E0;
     line-height: 1.5;
 }

+ 3 - 3
src/resources/themes/v_white/v_white.css

@@ -1,13 +1,13 @@
 body {
     margin: 0 auto;
     font-family: Helvetica, sans-serif, Tahoma, Arial, Verdana, Geneva, Georgia, Palatino, "Times New Roman", "Hiragino Sans GB", "冬青黑体", "Microsoft YaHei", "微软雅黑", "Microsoft YaHei UI", "WenQuanYi Micro Hei", "文泉驿雅黑", Dengxian, "等线体", STXihei, "华文细黑", "Liberation Sans", "Droid Sans", NSimSun, "新宋体", SimSun, "宋体";
-    color: #363636;
+    color: #222222;
     line-height: 1;
     padding: 15px;
 }
 
 h1, h2, h3, h4, h5, h6 {
-    color: #363636;
+    color: #222222;
     font-weight: bold;
     margin-top: 20px;
     margin-bottom: 10px;
@@ -92,7 +92,7 @@ pre code {
     display: block;
     overflow-x: auto;
     padding: 0.5em;
-    color: #363636;
+    color: #222222;
     background-color: #E0E0E0;
     line-height: 1.5;
 }

+ 4 - 2
src/src.pro

@@ -106,7 +106,8 @@ SOURCES += main.cpp\
     dialog/vcopytextashtmldialog.cpp \
     vwaitingwidget.cpp \
     utils/vwebutils.cpp \
-    vlineedit.cpp
+    vlineedit.cpp \
+    vcart.cpp
 
 HEADERS  += vmainwindow.h \
     vdirectorytree.h \
@@ -199,7 +200,8 @@ HEADERS  += vmainwindow.h \
     dialog/vcopytextashtmldialog.h \
     vwaitingwidget.h \
     utils/vwebutils.h \
-    vlineedit.h
+    vlineedit.h \
+    vcart.h
 
 RESOURCES += \
     vnote.qrc \

+ 193 - 0
src/vcart.cpp

@@ -0,0 +1,193 @@
+#include "vcart.h"
+
+#include <QtWidgets>
+
+#include "utils/viconutils.h"
+#include "utils/vutils.h"
+#include "vmainwindow.h"
+#include "vnote.h"
+#include "vnotefile.h"
+
+extern VMainWindow *g_mainWin;
+
+extern VNote *g_vnote;
+
+VCart::VCart(QWidget *p_parent)
+    : QWidget(p_parent)
+{
+    setupUI();
+
+    initActions();
+}
+
+void VCart::setupUI()
+{
+    m_clearBtn = new QPushButton(VIconUtils::buttonDangerIcon(":/resources/icons/clear_cart.svg"), "");
+    m_clearBtn->setToolTip(tr("Clear"));
+    m_clearBtn->setProperty("FlatBtn", true);
+    connect(m_clearBtn, &QPushButton::clicked,
+            this, [this]() {
+                if (m_itemList->count() > 0) {
+                    int ret = VUtils::showMessage(QMessageBox::Warning,
+                                                  tr("Warning"),
+                                                  tr("Are you sure to clear Cart?"),
+                                                  "",
+                                                  QMessageBox::Ok | QMessageBox::Cancel,
+                                                  QMessageBox::Ok,
+                                                  g_mainWin,
+                                                  MessageBoxType::Danger);
+                    if (ret == QMessageBox::Ok) {
+                        m_itemList->clear();
+                    }
+                }
+            });
+
+    m_numLabel = new QLabel();
+
+    QHBoxLayout *btnLayout = new QHBoxLayout;
+    btnLayout->addWidget(m_clearBtn);
+    btnLayout->addStretch();
+    btnLayout->addWidget(m_numLabel);
+    btnLayout->setContentsMargins(0, 0, 3, 0);
+
+    m_itemList = new QListWidget();
+    m_itemList->setAttribute(Qt::WA_MacShowFocusRect, false);
+    m_itemList->setContextMenuPolicy(Qt::CustomContextMenu);
+    m_itemList->setSelectionMode(QAbstractItemView::ExtendedSelection);
+    connect(m_itemList, &QListWidget::customContextMenuRequested,
+            this, &VCart::handleContextMenuRequested);
+    connect(m_itemList, &QListWidget::itemActivated,
+            this, &VCart::openItem);
+
+    QVBoxLayout *mainLayout = new QVBoxLayout();
+    mainLayout->addLayout(btnLayout);
+    mainLayout->addWidget(m_itemList);
+    mainLayout->setContentsMargins(0, 0, 0, 0);
+
+    setLayout(mainLayout);
+}
+
+void VCart::initActions()
+{
+    m_openAct = new QAction(tr("&Open"), this);
+    m_openAct->setToolTip(tr("Open selected notes"));
+    connect(m_openAct, &QAction::triggered,
+            this, &VCart::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, &VCart::locateCurrentItem);
+
+    m_deleteAct = new QAction(VIconUtils::menuDangerIcon(":/resources/icons/delete_cart_item.svg"),
+                              tr("&Delete"),
+                              this);
+    m_deleteAct->setToolTip(tr("Delete selected items from Cart"));
+    connect(m_deleteAct, &QAction::triggered,
+            this, &VCart::deleteSelectedItems);
+}
+
+void VCart::handleContextMenuRequested(QPoint p_pos)
+{
+    QListWidgetItem *item = m_itemList->itemAt(p_pos);
+    QMenu menu(this);
+    menu.setToolTipsVisible(true);
+
+    if (item) {
+        int itemCount = m_itemList->selectedItems().size();
+        if (itemCount == 1) {
+            menu.addAction(m_openAct);
+            menu.addAction(m_locateAct);
+        }
+
+        menu.addAction(m_deleteAct);
+    }
+
+    if (!menu.actions().isEmpty()) {
+        menu.exec(m_itemList->mapToGlobal(p_pos));
+    }
+}
+
+void VCart::addFile(const QString &p_filePath)
+{
+    if (p_filePath.isEmpty()
+        || findFileInCart(p_filePath) != -1) {
+        return;
+    }
+
+    addItem(p_filePath);
+}
+
+int VCart::findFileInCart(const QString &p_file) const
+{
+    int cnt = m_itemList->count();
+    for (int i = 0; i < cnt; ++i) {
+        if (VUtils::equalPath(m_itemList->item(i)->data(Qt::UserRole).toString(),
+                              p_file)) {
+            return i;
+        }
+    }
+
+    return -1;
+}
+
+void VCart::addItem(const QString &p_path)
+{
+    QListWidgetItem *item = new QListWidgetItem(VUtils::fileNameFromPath(p_path));
+    item->setToolTip(p_path);
+    item->setData(Qt::UserRole, p_path);
+
+    m_itemList->addItem(item);
+
+    int cnt = m_itemList->count();
+    if (cnt > 0) {
+        m_numLabel->setText(tr("%1 %2").arg(cnt)
+                                       .arg(cnt > 1 ? tr("Items") : tr("Item")));
+    } else {
+        m_numLabel->setText("");
+    }
+}
+
+void VCart::deleteSelectedItems()
+{
+    QList<QListWidgetItem *> selectedItems = m_itemList->selectedItems();
+    for (auto it : selectedItems) {
+        delete it;
+    }
+}
+
+void VCart::openSelectedItems() const
+{
+    openItem(m_itemList->currentItem());
+}
+
+void VCart::locateCurrentItem()
+{
+    auto item = m_itemList->currentItem();
+    if (!item) {
+        return;
+    }
+
+    VFile *file = g_vnote->getInternalFile(getFilePath(item));
+    if (file) {
+        g_mainWin->locateFile(file);
+    }
+}
+
+void VCart::openItem(const QListWidgetItem *p_item) const
+{
+    if (!p_item) {
+        return;
+    }
+
+    QStringList files;
+    files << getFilePath(p_item);
+    g_mainWin->openFiles(files);
+}
+
+QString VCart::getFilePath(const QListWidgetItem *p_item) const
+{
+    return p_item->data(Qt::UserRole).toString();
+}

+ 57 - 0
src/vcart.h

@@ -0,0 +1,57 @@
+#ifndef VCART_H
+#define VCART_H
+
+#include <QWidget>
+#include <QVector>
+
+#include "vnavigationmode.h"
+
+class QPushButton;
+class QListWidget;
+class QListWidgetItem;
+class QLabel;
+class QAction;
+class QKeyEvent;
+class QFocusEvent;
+
+class VCart : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit VCart(QWidget *p_parent = nullptr);
+
+    void addFile(const QString &p_filePath);
+
+private slots:
+    void handleContextMenuRequested(QPoint p_pos);
+
+    void deleteSelectedItems();
+
+    void openSelectedItems() const;
+
+    void openItem(const QListWidgetItem *p_item) const;
+
+    void locateCurrentItem();
+
+private:
+    void setupUI();
+
+    void initActions();
+
+    // Return index of item.
+    int findFileInCart(const QString &p_file) const;
+
+    void addItem(const QString &p_path);
+
+    QString getFilePath(const QListWidgetItem *p_item) const;
+
+    QPushButton *m_clearBtn;
+    QLabel *m_numLabel;
+    QListWidget *m_itemList;
+
+    QAction *m_openAct;
+    QAction *m_locateAct;
+    QAction *m_deleteAct;
+};
+
+#endif // VCART_H

+ 28 - 6
src/veditwindow.cpp

@@ -1,5 +1,6 @@
 #include <QtWidgets>
 #include <QtDebug>
+
 #include "veditwindow.h"
 #include "vedittab.h"
 #include "utils/vutils.h"
@@ -12,6 +13,7 @@
 #include "vfilelist.h"
 #include "vconfigmanager.h"
 #include "utils/viconutils.h"
+#include "vcart.h"
 
 extern VConfigManager *g_config;
 extern VMainWindow *g_mainWin;
@@ -90,7 +92,7 @@ void VEditWindow::initTabActions()
     m_closeOthersAct->setToolTip(tr("Close all other note tabs"));
     connect(m_closeOthersAct, &QAction::triggered,
             this, [this](){
-                int tab = this->m_closeTabAct->data().toInt();
+                int tab = this->m_closeOthersAct->data().toInt();
                 Q_ASSERT(tab != -1);
 
                 for (int i = tab - 1; i >= 0; --i) {
@@ -114,7 +116,7 @@ void VEditWindow::initTabActions()
     m_closeRightAct->setToolTip(tr("Close all the note tabs to the right of current tab"));
     connect(m_closeRightAct, &QAction::triggered,
             this, [this](){
-                int tab = this->m_closeTabAct->data().toInt();
+                int tab = this->m_closeRightAct->data().toInt();
                 Q_ASSERT(tab != -1);
 
                 for (int i = tab + 1; i < this->count();) {
@@ -131,7 +133,7 @@ void VEditWindow::initTabActions()
     m_noteInfoAct->setToolTip(tr("View and edit information of the note"));
     connect(m_noteInfoAct, &QAction::triggered,
             this, [this](){
-                int tab = this->m_closeTabAct->data().toInt();
+                int tab = this->m_noteInfoAct->data().toInt();
                 Q_ASSERT(tab != -1);
 
                 VEditTab *editor = getTab(tab);
@@ -145,11 +147,25 @@ void VEditWindow::initTabActions()
                 }
             });
 
+    m_addToCartAct = new QAction(tr("Add To Cart"), this);
+    m_addToCartAct->setToolTip(tr("Add this note to Cart for further processing"));
+    connect(m_addToCartAct, &QAction::triggered,
+            this, [this](){
+                int tab = this->m_addToCartAct->data().toInt();
+                Q_ASSERT(tab != -1);
+
+                VEditTab *editor = getTab(tab);
+                QPointer<VFile> file = editor->getFile();
+                Q_ASSERT(file);
+                g_mainWin->getCart()->addFile(file->fetchPath());
+                g_mainWin->showStatusMessage(tr("1 note added to Cart"));
+            });
+
     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,
             this, [this](){
-                int tab = this->m_closeTabAct->data().toInt();
+                int tab = this->m_openLocationAct->data().toInt();
                 Q_ASSERT(tab != -1);
 
                 VEditTab *editor = getTab(tab);
@@ -163,7 +179,7 @@ void VEditWindow::initTabActions()
     m_reloadAct->setToolTip(tr("Reload the content of this note from disk"));
     connect(m_reloadAct, &QAction::triggered,
             this, [this](){
-                int tab = this->m_closeTabAct->data().toInt();
+                int tab = this->m_reloadAct->data().toInt();
                 Q_ASSERT(tab != -1);
 
                 VEditTab *editor = getTab(tab);
@@ -175,7 +191,7 @@ void VEditWindow::initTabActions()
     m_recycleBinAct->setToolTip(tr("Open the recycle bin of this note"));
     connect(m_recycleBinAct, &QAction::triggered,
             this, [this]() {
-                int tab = this->m_closeTabAct->data().toInt();
+                int tab = this->m_recycleBinAct->data().toInt();
                 Q_ASSERT(tab != -1);
 
                 VEditTab *editor = getTab(tab);
@@ -647,6 +663,9 @@ void VEditWindow::tabbarContextMenuRequested(QPoint p_pos)
         m_reloadAct->setData(tab);
         menu.addAction(m_reloadAct);
 
+        m_addToCartAct->setData(tab);
+        menu.addAction(m_addToCartAct);
+
         m_noteInfoAct->setData(tab);
         menu.addAction(m_noteInfoAct);
     } else if (file->getType() == FileType::Orphan
@@ -660,6 +679,9 @@ void VEditWindow::tabbarContextMenuRequested(QPoint p_pos)
         m_reloadAct->setData(tab);
         menu.addAction(m_reloadAct);
 
+        m_addToCartAct->setData(tab);
+        menu.addAction(m_addToCartAct);
+
         m_noteInfoAct->setData(tab);
         menu.addAction(m_noteInfoAct);
     }

+ 3 - 0
src/veditwindow.h

@@ -205,6 +205,9 @@ private:
     // View and edit info about this note.
     QAction *m_noteInfoAct;
 
+    // Add this note to Cart.
+    QAction *m_addToCartAct;
+
     // Open the location (the folder containing this file) of this note.
     QAction *m_openLocationAct;
 

+ 29 - 3
src/vfilelist.cpp

@@ -18,6 +18,7 @@
 #include "utils/vimnavigationforwidget.h"
 #include "utils/viconutils.h"
 #include "dialog/vtipsdialog.h"
+#include "vcart.h"
 
 extern VConfigManager *g_config;
 extern VNote *g_vnote;
@@ -157,6 +158,11 @@ void VFileList::initActions()
     connect(m_openLocationAct, &QAction::triggered,
             this, &VFileList::openFileLocation);
 
+    m_addToCartAct = new QAction(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_sortAct = new QAction(VIconUtils::menuIcon(":/resources/icons/sort.svg"),
                             tr("&Sort"),
                             this);
@@ -220,6 +226,20 @@ void VFileList::openFileLocation() const
     }
 }
 
+void VFileList::addFileToCart() const
+{
+    QList<QListWidgetItem *> items = fileList->selectedItems();
+    VCart *cart = g_mainWin->getCart();
+
+    for (int i = 0; i < items.size(); ++i) {
+        cart->addFile(getVFile(items[i])->fetchPath());
+    }
+
+    g_mainWin->showStatusMessage(tr("%1 %2 added to Cart")
+                                   .arg(items.size())
+                                   .arg(items.size() > 1 ? tr("notes") : tr("note")));
+}
+
 void VFileList::fileInfo(VNoteFile *p_file)
 {
     if (!p_file) {
@@ -522,7 +542,9 @@ void VFileList::contextMenuRequested(QPoint pos)
         return;
     }
 
-    if (item && fileList->selectedItems().size() == 1) {
+    int selectedSize = fileList->selectedItems().size();
+
+    if (item && selectedSize == 1) {
         VNoteFile *file = getVFile(item);
         if (file) {
             if (file->getDocType() == DocType::Markdown) {
@@ -558,9 +580,13 @@ void VFileList::contextMenuRequested(QPoint pos)
 
     if (item) {
         menu.addSeparator();
-        menu.addAction(m_openLocationAct);
+        if (selectedSize == 1) {
+            menu.addAction(m_openLocationAct);
+        }
+
+        menu.addAction(m_addToCartAct);
 
-        if (fileList->selectedItems().size() == 1) {
+        if (selectedSize == 1) {
             menu.addAction(fileInfoAct);
         }
     }

+ 7 - 0
src/vfilelist.h

@@ -83,6 +83,9 @@ private slots:
     // Valid only when there is only one selected file.
     void openFileLocation() const;
 
+    // Add selected files to Cart.
+    void addFileToCart() const;
+
     // Copy selected files to clipboard.
     // Will put a Json string into the clipboard which contains the information
     // about copied files.
@@ -174,9 +177,13 @@ private:
     QAction *copyAct;
     QAction *cutAct;
     QAction *pasteAct;
+
     QAction *m_openLocationAct;
+
     QAction *m_sortAct;
 
+    QAction *m_addToCartAct;
+
     // Context sub-menu of Open With.
     QMenu *m_openWithMenu;
 

+ 7 - 0
src/vmainwindow.cpp

@@ -35,6 +35,7 @@
 #include "vpalette.h"
 #include "utils/viconutils.h"
 #include "dialog/vtipsdialog.h"
+#include "vcart.h"
 
 extern VConfigManager *g_config;
 
@@ -1259,6 +1260,9 @@ void VMainWindow::initDockWindows()
     // Snippets.
     m_snippetList = new VSnippetList(this);
 
+    // Cart.
+    m_cart = new VCart(this);
+
     m_toolBox = new VToolBox(this);
     m_toolBox->addItem(outline,
                        ":/resources/icons/outline.svg",
@@ -1266,6 +1270,9 @@ void VMainWindow::initDockWindows()
     m_toolBox->addItem(m_snippetList,
                        ":/resources/icons/snippets.svg",
                        tr("Snippets"));
+    m_toolBox->addItem(m_cart,
+                       ":/resources/icons/cart.svg",
+                       tr("Cart"));
 
     toolDock->setWidget(m_toolBox);
     addDockWidget(Qt::RightDockWidgetArea, toolDock);

+ 11 - 0
src/vmainwindow.h

@@ -37,6 +37,7 @@ class QSystemTrayIcon;
 class VButtonWithWidget;
 class VAttachmentList;
 class VSnippetList;
+class VCart;
 
 enum class PanelViewState
 {
@@ -63,6 +64,8 @@ public:
 
     VSnippetList *getSnippetList() const;
 
+    VCart *getCart() const;
+
     // View and edit the information of @p_file, which is an orphan file.
     void editOrphanFileInfo(VFile *p_file);
 
@@ -304,6 +307,9 @@ private:
     // View and manage snippets.
     VSnippetList *m_snippetList;
 
+    // View and manage cart.
+    VCart *m_cart;
+
     VFindReplaceDialog *m_findReplaceDialog;
     VVimIndicator *m_vimIndicator;
     VTabIndicator *m_tabIndicator;
@@ -413,4 +419,9 @@ inline VSnippetList *VMainWindow::getSnippetList() const
     return m_snippetList;
 }
 
+inline VCart *VMainWindow::getCart() const
+{
+    return m_cart;
+}
+
 #endif // VMAINWINDOW_H

+ 3 - 0
src/vnote.qrc

@@ -234,5 +234,8 @@
         <file>translations/qdialogbuttonbox_zh_CN.qm</file>
         <file>translations/qwebengine_zh_CN.qm</file>
         <file>translations/qt_zh_CN.qm</file>
+        <file>resources/icons/clear_cart.svg</file>
+        <file>resources/icons/cart.svg</file>
+        <file>resources/icons/delete_cart_item.svg</file>
     </qresource>
 </RCC>

+ 0 - 1
src/vsnippetlist.cpp

@@ -72,7 +72,6 @@ void VSnippetList::setupUI()
     m_snippetList->setAttribute(Qt::WA_MacShowFocusRect, false);
     m_snippetList->setContextMenuPolicy(Qt::CustomContextMenu);
     m_snippetList->setSelectionMode(QAbstractItemView::ExtendedSelection);
-    m_snippetList->setEditTriggers(QAbstractItemView::SelectedClicked);
     connect(m_snippetList, &QListWidget::customContextMenuRequested,
             this, &VSnippetList::handleContextMenuRequested);
     connect(m_snippetList, &QListWidget::itemActivated,