|
|
@@ -0,0 +1,507 @@
|
|
|
+#include "vhistorylist.h"
|
|
|
+
|
|
|
+#include <QtWidgets>
|
|
|
+#include <QDebug>
|
|
|
+
|
|
|
+#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<VHistoryEntry>::iterator VHistoryList::findFileInHistory(const QString &p_file)
|
|
|
+{
|
|
|
+ for (QLinkedList<VHistoryEntry>::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<SeparatorItem> 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<QListWidgetItem *> 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<QListWidgetItem *> selectedItems = m_itemList->selectedItems();
|
|
|
+ for (auto it : selectedItems) {
|
|
|
+ files << getFilePath(it);
|
|
|
+ }
|
|
|
+
|
|
|
+ pinFiles(files);
|
|
|
+}
|
|
|
+
|
|
|
+void VHistoryList::unpinSelectedItems()
|
|
|
+{
|
|
|
+ QStringList files;
|
|
|
+ QList<QListWidgetItem *> selectedItems = m_itemList->selectedItems();
|
|
|
+ for (auto it : selectedItems) {
|
|
|
+ files << getFilePath(it);
|
|
|
+ }
|
|
|
+
|
|
|
+ unpinFiles(files);
|
|
|
+
|
|
|
+ if (isVisible()) {
|
|
|
+ updateList();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void VHistoryList::openSelectedItems() const
|
|
|
+{
|
|
|
+ QStringList files;
|
|
|
+ QList<QListWidgetItem *> 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<QListWidgetItem *> 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")));
|
|
|
+}
|