#include "vnotebookselector.h" #include #include #include #include #include #include #include #include #include #include #include "vnotebook.h" #include "vconfigmanager.h" #include "dialog/vnewnotebookdialog.h" #include "dialog/vnotebookinfodialog.h" #include "dialog/vdeletenotebookdialog.h" #include "vnotebook.h" #include "vdirectory.h" #include "utils/vutils.h" #include "vnote.h" #include "veditarea.h" #include "vnofocusitemdelegate.h" #include "vmainwindow.h" extern VConfigManager *g_config; extern VNote *g_vnote; extern VMainWindow *g_mainWin; VNotebookSelector::VNotebookSelector(QWidget *p_parent) : QComboBox(p_parent), VNavigationMode(), m_notebooks(g_vnote->getNotebooks()), m_lastValidIndex(-1), m_muted(false), m_naviLabel(NULL) { m_listWidget = new QListWidget(this); m_listWidget->setItemDelegate(new VNoFocusItemDelegate(this)); m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_listWidget, &QListWidget::customContextMenuRequested, this, &VNotebookSelector::popupListContextMenuRequested); setModel(m_listWidget->model()); setView(m_listWidget); m_listWidget->viewport()->installEventFilter(this); m_listWidget->installEventFilter(this); initActions(); connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(handleCurIndexChanged(int))); } void VNotebookSelector::initActions() { m_deleteNotebookAct = new QAction(QIcon(":/resources/icons/delete_notebook.svg"), tr("&Delete"), this); m_deleteNotebookAct->setToolTip(tr("Delete current notebook")); connect(m_deleteNotebookAct, SIGNAL(triggered(bool)), this, SLOT(deleteNotebook())); m_notebookInfoAct = new QAction(QIcon(":/resources/icons/notebook_info.svg"), tr("&Info"), this); m_notebookInfoAct->setToolTip(tr("View and edit current notebook's information")); connect(m_notebookInfoAct, SIGNAL(triggered(bool)), this, SLOT(editNotebookInfo())); m_openLocationAct = new QAction(tr("&Open Notebook Location"), this); m_openLocationAct->setToolTip(tr("Open the root folder of this notebook in operating system")); connect(m_openLocationAct, &QAction::triggered, this, [this]() { QList items = this->m_listWidget->selectedItems(); if (items.isEmpty()) { return; } Q_ASSERT(items.size() == 1); VNotebook *notebook = getNotebook(items[0]); QUrl url = QUrl::fromLocalFile(notebook->getPath()); QDesktopServices::openUrl(url); }); m_recycleBinAct = new QAction(QIcon(":/resources/icons/recycle_bin.svg"), tr("&Recycle Bin"), this); m_recycleBinAct->setToolTip(tr("Open the recycle bin of this notebook")); connect(m_recycleBinAct, &QAction::triggered, this, [this]() { QList items = this->m_listWidget->selectedItems(); if (items.isEmpty()) { return; } Q_ASSERT(items.size() == 1); VNotebook *notebook = getNotebook(items[0]); QUrl url = QUrl::fromLocalFile(notebook->getRecycleBinFolderPath()); QDesktopServices::openUrl(url); }); m_emptyRecycleBinAct = new QAction(QIcon(":/resources/icons/empty_recycle_bin.svg"), tr("&Empty Recycle Bin"), this); m_emptyRecycleBinAct->setToolTip(tr("Empty the recycle bin of this notebook")); connect(m_emptyRecycleBinAct, &QAction::triggered, this, [this]() { QList items = this->m_listWidget->selectedItems(); if (items.isEmpty()) { return; } Q_ASSERT(items.size() == 1); VNotebook *notebook = getNotebook(items[0]); QString binPath = notebook->getRecycleBinFolderPath(); int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"), tr("Are you sure to empty recycle bin of notebook " "%2?") .arg(g_config->c_dataTextStyle) .arg(notebook->getName()), tr("WARNING: " "VNote will delete all the files in directory " "%3." "
It may be UNRECOVERABLE!") .arg(g_config->c_warningTextStyle) .arg(g_config->c_dataTextStyle) .arg(binPath), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok, this, MessageBoxType::Danger); if (ret == QMessageBox::Ok) { QString info; if (VUtils::emptyDirectory(notebook, binPath, true)) { info = tr("Successfully emptied recycle bin of notebook " "%2!") .arg(g_config->c_dataTextStyle) .arg(notebook->getName()); } else { info = tr("Fail to empty recycle bin of notebook " "%2!") .arg(g_config->c_dataTextStyle) .arg(notebook->getName()); } VUtils::showMessage(QMessageBox::Information, tr("Information"), info, "", QMessageBox::Ok, QMessageBox::Ok, this); } }); } void VNotebookSelector::updateComboBox() { m_muted = true; int index = g_config->getCurNotebookIndex(); clear(); m_listWidget->clear(); insertAddNotebookItem(); for (int i = 0; i < m_notebooks.size(); ++i) { addNotebookItem(m_notebooks[i]); } setCurrentIndex(-1); m_muted = false; if (m_notebooks.isEmpty()) { g_config->setCurNotebookIndex(-1); setCurrentIndex(0); } else { const VNotebook *nb = NULL; if (index >= 0 && index < m_notebooks.size()) { nb = m_notebooks[index]; } setCurrentItemToNotebook(nb); } qDebug() << "notebooks" << m_notebooks.size() << "current index" << index; } void VNotebookSelector::setCurrentItemToNotebook(const VNotebook *p_notebook) { setCurrentIndex(itemIndexOfNotebook(p_notebook)); } int VNotebookSelector::itemIndexOfNotebook(const VNotebook *p_notebook) const { if (!p_notebook) { return -1; } qulonglong ptr = (qulonglong)p_notebook; int cnt = m_listWidget->count(); for (int i = 0; i < cnt; ++i) { QListWidgetItem *item = m_listWidget->item(i); if (item->data(Qt::UserRole).toULongLong() == ptr) { return i; } } return -1; } void VNotebookSelector::insertAddNotebookItem() { QListWidgetItem *item = new QListWidgetItem(); item->setIcon(QIcon(":/resources/icons/create_notebook.svg")); item->setText(tr("Add Notebook")); QFont font; font.setItalic(true); item->setData(Qt::FontRole, font); item->setToolTip(tr("Create or import a notebook")); m_listWidget->insertItem(0, item); } void VNotebookSelector::handleCurIndexChanged(int p_index) { if (m_muted) { return; } QString tooltip = tr("View and edit notebooks"); VNotebook *nb = NULL; if (p_index > -1) { nb = getNotebook(p_index); if (!nb) { // Add notebook. setToolTip(tooltip); if (m_lastValidIndex != p_index && m_lastValidIndex > -1) { setCurrentIndex(m_lastValidIndex); } newNotebook(); return; } } m_lastValidIndex = p_index; int nbIdx = -1; if (nb) { tooltip = nb->getName(); nbIdx = m_notebooks.indexOf(nb); Q_ASSERT(nbIdx > -1); } g_config->setCurNotebookIndex(nbIdx); setToolTip(tooltip); emit curNotebookChanged(nb); } void VNotebookSelector::update() { updateComboBox(); } bool VNotebookSelector::newNotebook() { QString info(tr("Please type the name of the notebook and " "choose a folder as the Root Folder of the notebook.")); info += "\n"; info += tr("* The root folder should be used EXCLUSIVELY by VNote and " "it is recommended to be EMPTY."); info += "\n"; info += tr("* A previously created notebook could be imported into VNote " "by choosing its root folder."); // Use empty default name and path to let the dialog to auto generate a name // under the default VNote notebook folder. VNewNotebookDialog dialog(tr("Add Notebook"), info, "", "", m_notebooks, this); if (dialog.exec() == QDialog::Accepted) { createNotebook(dialog.getNameInput(), dialog.getPathInput(), dialog.isImportExistingNotebook(), dialog.getImageFolder(), dialog.getAttachmentFolder()); emit notebookCreated(dialog.getNameInput(), dialog.isImportExistingNotebook()); return true; } return false; } void VNotebookSelector::createNotebook(const QString &p_name, const QString &p_path, bool p_import, const QString &p_imageFolder, const QString &p_attachmentFolder) { VNotebook *nb = VNotebook::createNotebook(p_name, p_path, p_import, p_imageFolder, p_attachmentFolder, g_vnote); if (!nb) { VUtils::showMessage(QMessageBox::Warning, tr("Warning"), tr("Fail to create notebook " "%2 in %3.") .arg(g_config->c_dataTextStyle).arg(p_name).arg(p_path), "", QMessageBox::Ok, QMessageBox::Ok, this); return; } m_notebooks.append(nb); g_config->setNotebooks(m_notebooks); addNotebookItem(nb); setCurrentItemToNotebook(nb); } void VNotebookSelector::deleteNotebook() { QList items = m_listWidget->selectedItems(); if (items.isEmpty()) { return; } Q_ASSERT(items.size() == 1); VNotebook *notebook = getNotebook(items[0]); Q_ASSERT(notebook); VDeleteNotebookDialog dialog(tr("Delete Notebook"), notebook, this); if (dialog.exec() == QDialog::Accepted) { bool deleteFiles = dialog.getDeleteFiles(); g_mainWin->getEditArea()->closeFile(notebook, true); deleteNotebook(notebook, deleteFiles); } } void VNotebookSelector::deleteNotebook(VNotebook *p_notebook, bool p_deleteFiles) { Q_ASSERT(p_notebook); m_notebooks.removeOne(p_notebook); g_config->setNotebooks(m_notebooks); int idx = itemIndexOfNotebook(p_notebook); QListWidgetItem *item = m_listWidget->takeItem(idx); Q_ASSERT(item); delete item; QString name(p_notebook->getName()); QString path(p_notebook->getPath()); bool ret = VNotebook::deleteNotebook(p_notebook, p_deleteFiles); if (!ret) { // Notebook could not be deleted completely. int cho = VUtils::showMessage(QMessageBox::Information, tr("Delete Notebook Folder From Disk"), tr("Fail to delete the root folder of notebook " "%2 from disk. You may open " "the folder and check it manually.") .arg(g_config->c_dataTextStyle).arg(name), "", QMessageBox::Open | QMessageBox::Ok, QMessageBox::Ok, this); if (cho == QMessageBox::Open) { // Open the notebook location. QUrl url = QUrl::fromLocalFile(path); QDesktopServices::openUrl(url); } } } void VNotebookSelector::editNotebookInfo() { QList items = m_listWidget->selectedItems(); if (items.isEmpty()) { return; } Q_ASSERT(items.size() == 1); VNotebook *notebook = getNotebook(items[0]); VNotebookInfoDialog dialog(tr("Notebook Information"), "", notebook, m_notebooks, this); if (dialog.exec() == QDialog::Accepted) { bool updated = false; bool configUpdated = false; QString name = dialog.getName(); if (name != notebook->getName()) { updated = true; notebook->rename(name); g_config->setNotebooks(m_notebooks); } QString imageFolder = dialog.getImageFolder(); if (imageFolder != notebook->getImageFolderConfig()) { configUpdated = true; notebook->setImageFolder(imageFolder); } if (configUpdated) { updated = true; notebook->writeConfigNotebook(); } if (updated) { fillItem(items[0], notebook); emit notebookUpdated(notebook); } } } void VNotebookSelector::addNotebookItem(const VNotebook *p_notebook) { QListWidgetItem *item = new QListWidgetItem(m_listWidget); fillItem(item, p_notebook); } void VNotebookSelector::fillItem(QListWidgetItem *p_item, const VNotebook *p_notebook) const { p_item->setText(p_notebook->getName()); p_item->setToolTip(p_notebook->getName()); p_item->setIcon(QIcon(":/resources/icons/notebook_item.svg")); p_item->setData(Qt::UserRole, (qulonglong)p_notebook); } void VNotebookSelector::popupListContextMenuRequested(QPoint p_pos) { QListWidgetItem *item = m_listWidget->itemAt(p_pos); if (!item) { return; } const VNotebook *nb = getNotebook(item); if (!nb) { return; } m_listWidget->clearSelection(); item->setSelected(true); QMenu menu(this); menu.setToolTipsVisible(true); menu.addAction(m_deleteNotebookAct); if (nb->isValid()) { menu.addSeparator(); menu.addAction(m_recycleBinAct); menu.addAction(m_emptyRecycleBinAct); } menu.addSeparator(); menu.addAction(m_openLocationAct); if (nb->isValid()) { menu.addAction(m_notebookInfoAct); } menu.exec(m_listWidget->mapToGlobal(p_pos)); } bool VNotebookSelector::eventFilter(QObject *watched, QEvent *event) { QEvent::Type type = event->type(); if (type == QEvent::KeyPress && watched == m_listWidget) { if (handlePopupKeyPress(static_cast(event))) { return true; } } else if (type == QEvent::MouseButtonRelease) { if (static_cast(event)->button() == Qt::RightButton) { return true; } } return QComboBox::eventFilter(watched, event); } bool VNotebookSelector::locateNotebook(const VNotebook *p_notebook) { bool ret = false; int index = itemIndexOfNotebook(p_notebook); if (index > -1) { setCurrentIndex(index); ret = true; } return ret; } void VNotebookSelector::showPopup() { if (m_notebooks.isEmpty()) { // No normal notebook items. Just add notebook. newNotebook(); return; } resizeListWidgetToContent(); QComboBox::showPopup(); } void VNotebookSelector::resizeListWidgetToContent() { static QRect screenRect = QGuiApplication::primaryScreen()->geometry(); static int maxMinWidth = screenRect.width() < 400 ? screenRect.width() : screenRect.width() / 2; static int maxMinHeight = screenRect.height() < 400 ? screenRect.height() : screenRect.height() / 2; int minWidth = 0; int minHeight = 0; if (m_listWidget->count() > 0) { // Width minWidth = m_listWidget->sizeHintForColumn(0); minWidth = qMin(minWidth, maxMinWidth); // Height minHeight = m_listWidget->sizeHintForRow(0) * m_listWidget->count() + 10; minHeight = qMin(minHeight, maxMinHeight); } m_listWidget->setMinimumSize(minWidth, minHeight); } void VNotebookSelector::registerNavigation(QChar p_majorKey) { Q_ASSERT(!m_naviLabel); m_majorKey = p_majorKey; } void VNotebookSelector::showNavigation() { if (!isVisible()) { return; } V_ASSERT(!m_naviLabel); m_naviLabel = new QLabel(m_majorKey, this); m_naviLabel->setStyleSheet(g_vnote->getNavigationLabelStyle(m_majorKey)); m_naviLabel->show(); } void VNotebookSelector::hideNavigation() { if (m_naviLabel) { delete m_naviLabel; m_naviLabel = NULL; } } bool VNotebookSelector::handleKeyNavigation(int p_key, bool &p_succeed) { bool ret = false; p_succeed = false; QChar keyChar = VUtils::keyToChar(p_key); if (keyChar == m_majorKey) { // Hit. p_succeed = true; ret = true; if (m_naviLabel) { showPopup(); } } return ret; } bool VNotebookSelector::handlePopupKeyPress(QKeyEvent *p_event) { int key = p_event->key(); int modifiers = p_event->modifiers(); switch (key) { case Qt::Key_BracketLeft: { if (modifiers == Qt::ControlModifier) { p_event->accept(); hidePopup(); return true; } break; } case Qt::Key_J: { if (modifiers == Qt::ControlModifier) { p_event->accept(); QKeyEvent *downEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier); QCoreApplication::postEvent(m_listWidget, downEvent); return true; } break; } case Qt::Key_K: { if (modifiers == Qt::ControlModifier) { p_event->accept(); QKeyEvent *upEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier); QCoreApplication::postEvent(m_listWidget, upEvent); return true; } break; } default: break; } return false; } VNotebook *VNotebookSelector::getNotebook(int p_itemIdx) const { VNotebook *nb = NULL; QListWidgetItem *item = m_listWidget->item(p_itemIdx); if (item) { nb = (VNotebook *)item->data(Qt::UserRole).toULongLong(); } return nb; } VNotebook *VNotebookSelector::getNotebook(const QListWidgetItem *p_item) const { if (p_item) { return (VNotebook *)p_item->data(Qt::UserRole).toULongLong(); } return NULL; }