vexplorer.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. #include "vexplorer.h"
  2. #include <QtWidgets>
  3. #include <QFileSystemModel>
  4. #include <QDesktopServices>
  5. #include "utils/viconutils.h"
  6. #include "utils/vutils.h"
  7. #include "vconfigmanager.h"
  8. #include "vmainwindow.h"
  9. #include "vcart.h"
  10. #include "vlineedit.h"
  11. #include "vhistorylist.h"
  12. #include "vorphanfile.h"
  13. #include "utils/vimnavigationforwidget.h"
  14. extern VMainWindow *g_mainWin;
  15. extern VConfigManager *g_config;
  16. const QString VExplorer::c_infoShortcutSequence = "F2";
  17. VExplorer::VExplorer(QWidget *p_parent)
  18. : QWidget(p_parent),
  19. m_initialized(false),
  20. m_uiInitialized(false),
  21. m_index(-1)
  22. {
  23. }
  24. void VExplorer::setupUI()
  25. {
  26. if (m_uiInitialized) {
  27. return;
  28. }
  29. m_uiInitialized = true;
  30. QLabel *dirLabel = new QLabel(tr("Directory"), this);
  31. m_openBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/dir_item.svg"),
  32. "",
  33. this);
  34. m_openBtn->setToolTip(tr("Open"));
  35. m_openBtn->setProperty("FlatBtn", true);
  36. connect(m_openBtn, &QPushButton::clicked,
  37. this, [this]() {
  38. static QString defaultPath = g_config->getDocumentPathOrHomePath();
  39. QString dirPath = QFileDialog::getExistingDirectory(this,
  40. tr("Select Root Directory To Explore"),
  41. defaultPath,
  42. QFileDialog::ShowDirsOnly
  43. | QFileDialog::DontResolveSymlinks);
  44. if (!dirPath.isEmpty()) {
  45. defaultPath = VUtils::basePathFromPath(dirPath);
  46. int idx = addEntry(dirPath);
  47. updateDirectoryComboBox();
  48. if (idx != -1) {
  49. setCurrentEntry(idx);
  50. }
  51. }
  52. });
  53. m_upBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/up.svg"),
  54. "",
  55. this);
  56. m_upBtn->setToolTip(tr("Up"));
  57. m_upBtn->setProperty("FlatBtn", true);
  58. connect(m_upBtn, &QPushButton::clicked,
  59. this, [this]() {
  60. if (checkIndex()) {
  61. QDir dir(m_entries[m_index].m_directory);
  62. if (dir.cdUp()) {
  63. int idx = addEntry(dir.absolutePath());
  64. updateDirectoryComboBox();
  65. if (idx != -1) {
  66. setCurrentEntry(idx);
  67. }
  68. }
  69. }
  70. });
  71. m_openLocationBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/open_location.svg"),
  72. "",
  73. this);
  74. m_openLocationBtn->setToolTip(tr("Open Directory Location"));
  75. m_openLocationBtn->setProperty("FlatBtn", true);
  76. connect(m_openLocationBtn, &QPushButton::clicked,
  77. this, [this]() {
  78. if (checkIndex()) {
  79. QUrl url = QUrl::fromLocalFile(m_entries[m_index].m_directory);
  80. QDesktopServices::openUrl(url);
  81. }
  82. });
  83. m_starBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/star.svg"),
  84. "",
  85. this);
  86. m_starBtn->setToolTip(tr("Star"));
  87. m_starBtn->setProperty("FlatBtn", true);
  88. connect(m_starBtn, &QPushButton::clicked,
  89. this, [this]() {
  90. if (checkIndex()) {
  91. m_entries[m_index].m_isStarred = !m_entries[m_index].m_isStarred;
  92. g_config->setExplorerEntries(m_entries);
  93. updateExplorerEntryIndexInConfig();
  94. updateStarButton();
  95. }
  96. });
  97. QHBoxLayout *dirLayout = new QHBoxLayout();
  98. dirLayout->addWidget(dirLabel);
  99. dirLayout->addStretch();
  100. dirLayout->addWidget(m_openBtn);
  101. dirLayout->addWidget(m_upBtn);
  102. dirLayout->addWidget(m_openLocationBtn);
  103. dirLayout->addWidget(m_starBtn);
  104. dirLayout->setContentsMargins(0, 0, 0, 0);
  105. m_dirCB = VUtils::getComboBox(this);
  106. m_dirCB->setEditable(true);
  107. m_dirCB->setLineEdit(new VLineEdit(this));
  108. m_dirCB->setToolTip(tr("Path of the root directory to explore"));
  109. m_dirCB->lineEdit()->setPlaceholderText(tr("Root path to explore"));
  110. m_dirCB->lineEdit()->setProperty("EmbeddedEdit", true);
  111. connect(m_dirCB->lineEdit(), &QLineEdit::editingFinished,
  112. this, [this]() {
  113. QString text = m_dirCB->currentText();
  114. int idx = addEntry(text);
  115. updateDirectoryComboBox();
  116. if (idx != -1) {
  117. setCurrentEntry(idx);
  118. }
  119. });
  120. QCompleter *completer = new QCompleter(this);
  121. QFileSystemModel *fsModel = new QFileSystemModel(completer);
  122. fsModel->setRootPath("");
  123. completer->setModel(fsModel);
  124. // Enable styling the popup list via QListView::item.
  125. completer->popup()->setItemDelegate(new QStyledItemDelegate(this));
  126. completer->setCompletionMode(QCompleter::PopupCompletion);
  127. #if defined(Q_OS_WIN)
  128. completer->setCaseSensitivity(Qt::CaseInsensitive);
  129. #else
  130. completer->setCaseSensitivity(Qt::CaseSensitive);
  131. #endif
  132. m_dirCB->lineEdit()->setCompleter(completer);
  133. connect(m_dirCB, SIGNAL(activated(int)),
  134. this, SLOT(handleEntryActivated(int)));
  135. m_dirCB->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
  136. QLabel *imgLabel = new QLabel(tr("Image Folder"), this);
  137. m_imgFolderEdit = new VLineEdit(this);
  138. m_imgFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
  139. .arg(g_config->getImageFolderExt()));
  140. QString imgFolderTip = tr("Set the path of the image folder to store images "
  141. "of files within the root directory.\nIf absolute path is used, "
  142. "VNote will not manage those images."
  143. "(empty to use global configuration)");
  144. m_imgFolderEdit->setToolTip(imgFolderTip);
  145. connect(m_imgFolderEdit, &QLineEdit::editingFinished,
  146. this, [this]() {
  147. if (checkIndex()) {
  148. QString folder = m_imgFolderEdit->text();
  149. if (folder.isEmpty() || VUtils::checkPathLegal(folder)) {
  150. m_entries[m_index].m_imageFolder = folder;
  151. if (m_entries[m_index].m_isStarred) {
  152. g_config->setExplorerEntries(m_entries);
  153. }
  154. } else {
  155. m_imgFolderEdit->setText(m_entries[m_index].m_imageFolder);
  156. }
  157. }
  158. });
  159. // New file/dir.
  160. m_newFileBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/create_note_tb.svg"),
  161. "",
  162. this);
  163. m_newFileBtn->setToolTip(tr("New File"));
  164. m_newFileBtn->setProperty("FlatBtn", true);
  165. connect(m_newFileBtn, &QPushButton::clicked,
  166. this, &VExplorer::newFile);
  167. m_newDirBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/create_rootdir_tb.svg"),
  168. "",
  169. this);
  170. m_newDirBtn->setToolTip(tr("New Folder"));
  171. m_newDirBtn->setProperty("FlatBtn", true);
  172. connect(m_newDirBtn, &QPushButton::clicked,
  173. this, &VExplorer::newFolder);
  174. QHBoxLayout *btnLayout = new QHBoxLayout();
  175. btnLayout->addStretch();
  176. btnLayout->addWidget(m_newFileBtn);
  177. btnLayout->addWidget(m_newDirBtn);
  178. btnLayout->setContentsMargins(0, 0, 0, 0);
  179. QFileSystemModel *dirModel = new QFileSystemModel(this);
  180. dirModel->setRootPath("");
  181. m_tree = new QTreeView(this);
  182. m_tree->setModel(dirModel);
  183. m_tree->setSelectionMode(QAbstractItemView::ExtendedSelection);
  184. m_tree->setContextMenuPolicy(Qt::CustomContextMenu);
  185. connect(m_tree, &QTreeView::customContextMenuRequested,
  186. this, &VExplorer::handleContextMenuRequested);
  187. connect(m_tree, &QTreeView::activated,
  188. this, [this](const QModelIndex &p_index) {
  189. QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
  190. if (!model->isDir(p_index)) {
  191. QStringList files;
  192. files << model->filePath(p_index);
  193. // If there is no directory entry currently, new one using the parent dir.
  194. if (m_index == -1 || m_entries.isEmpty()) {
  195. setRootDirectory(VUtils::basePathFromPath(files[0]));
  196. }
  197. openFiles(files, g_config->getNoteOpenMode());
  198. }
  199. });
  200. connect(m_tree, &QTreeView::expanded,
  201. this, &VExplorer::resizeTreeToContents);
  202. connect(m_tree, &QTreeView::collapsed,
  203. this, &VExplorer::resizeTreeToContents);
  204. connect(dirModel, &QFileSystemModel::directoryLoaded,
  205. this, &VExplorer::resizeTreeToContents);
  206. m_tree->setHeaderHidden(true);
  207. // Show only the Name column.
  208. for (int i = 1; i < dirModel->columnCount(); ++i) {
  209. m_tree->hideColumn(i);
  210. }
  211. QVBoxLayout *mainLayout = new QVBoxLayout();
  212. mainLayout->addLayout(dirLayout);
  213. mainLayout->addWidget(m_dirCB);
  214. mainLayout->addWidget(imgLabel);
  215. mainLayout->addWidget(m_imgFolderEdit);
  216. mainLayout->addLayout(btnLayout);
  217. mainLayout->addWidget(m_tree);
  218. mainLayout->setContentsMargins(3, 0, 3, 0);
  219. setLayout(mainLayout);
  220. }
  221. void VExplorer::init()
  222. {
  223. if (m_initialized) {
  224. return;
  225. }
  226. m_initialized = true;
  227. setupUI();
  228. QShortcut *infoShortcut = new QShortcut(QKeySequence(c_infoShortcutSequence), this);
  229. infoShortcut->setContext(Qt::WidgetWithChildrenShortcut);
  230. connect(infoShortcut, &QShortcut::activated,
  231. this, [this]() {
  232. QModelIndexList selectedIdx = m_tree->selectionModel()->selectedRows();
  233. if (selectedIdx.size() == 1) {
  234. QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
  235. QString filePath = model->filePath(selectedIdx[0]);
  236. renameFile(filePath);
  237. }
  238. });
  239. g_config->getExplorerEntries(m_entries);
  240. m_index = g_config->getExplorerCurrentIndex();
  241. if (m_entries.isEmpty()) {
  242. m_index = -1;
  243. g_config->setExplorerCurrentIndex(m_index);
  244. } else if (m_index < 0 || m_index >= m_entries.size()) {
  245. m_index = 0;
  246. g_config->setExplorerCurrentIndex(m_index);
  247. }
  248. updateDirectoryComboBox();
  249. setCurrentEntry(m_index);
  250. }
  251. void VExplorer::showEvent(QShowEvent *p_event)
  252. {
  253. init();
  254. QWidget::showEvent(p_event);
  255. }
  256. void VExplorer::focusInEvent(QFocusEvent *p_event)
  257. {
  258. init();
  259. QWidget::focusInEvent(p_event);
  260. if (m_index < 0) {
  261. m_openBtn->setFocus();
  262. } else {
  263. m_tree->setFocus();
  264. }
  265. }
  266. void VExplorer::updateUI()
  267. {
  268. }
  269. void VExplorer::updateDirectoryComboBox()
  270. {
  271. m_dirCB->clear();
  272. for (int i = 0; i < m_entries.size(); ++i) {
  273. m_dirCB->addItem(QDir::toNativeSeparators(m_entries[i].m_directory));
  274. m_dirCB->setItemData(i, m_entries[i].m_directory, Qt::ToolTipRole);
  275. }
  276. m_dirCB->setCurrentIndex(m_index);
  277. }
  278. int VExplorer::addEntry(const QString &p_dir)
  279. {
  280. if (p_dir.isEmpty() || !QFileInfo::exists(p_dir) || !QDir::isAbsolutePath(p_dir)) {
  281. return -1;
  282. }
  283. QString dir(QDir::cleanPath(p_dir));
  284. int idx = -1;
  285. for (idx = 0; idx < m_entries.size(); ++idx) {
  286. if (VUtils::equalPath(dir, m_entries[idx].m_directory)) {
  287. break;
  288. }
  289. }
  290. if (idx < m_entries.size()) {
  291. return idx;
  292. }
  293. m_entries.append(VExplorerEntry(QFileInfo(dir).absoluteFilePath(), ""));
  294. return m_entries.size() - 1;
  295. }
  296. void VExplorer::handleEntryActivated(int p_idx)
  297. {
  298. bool valid = false;
  299. QString imgFolder;
  300. if (p_idx >= 0 && p_idx < m_entries.size()) {
  301. valid = true;
  302. imgFolder = m_entries[p_idx].m_imageFolder;
  303. } else {
  304. p_idx = -1;
  305. }
  306. m_index = p_idx;
  307. updateExplorerEntryIndexInConfig();
  308. m_imgFolderEdit->setText(imgFolder);
  309. m_imgFolderEdit->setEnabled(valid);
  310. m_openLocationBtn->setEnabled(valid);
  311. m_upBtn->setEnabled(valid);
  312. m_newFileBtn->setEnabled(valid);
  313. m_newDirBtn->setEnabled(valid);
  314. updateStarButton();
  315. // Check if exists.
  316. if (checkIndex() && !QFileInfo::exists(m_entries[m_index].m_directory)) {
  317. VUtils::showMessage(QMessageBox::Warning,
  318. tr("Warning"),
  319. tr("Fail to open directory <span style=\"%1\">%2</span>.")
  320. .arg(g_config->c_dataTextStyle)
  321. .arg(m_entries[m_index].m_directory),
  322. tr("Please check if the directory exists."),
  323. QMessageBox::Ok,
  324. QMessageBox::Ok,
  325. g_mainWin);
  326. }
  327. updateTree();
  328. }
  329. void VExplorer::updateStarButton()
  330. {
  331. // -1 for disabled, 0 for star, 1 for unstar.
  332. int star = -1;
  333. if (checkIndex()) {
  334. star = m_entries[m_index].m_isStarred ? 1 : 0;
  335. }
  336. bool enabled = true;
  337. switch (star) {
  338. default:
  339. enabled = false;
  340. V_FALLTHROUGH;
  341. case 0:
  342. m_starBtn->setEnabled(enabled);
  343. m_starBtn->setIcon(VIconUtils::buttonIcon(":/resources/icons/star.svg"));
  344. m_starBtn->setToolTip(tr("Star"));
  345. break;
  346. case 1:
  347. m_starBtn->setEnabled(enabled);
  348. m_starBtn->setIcon(VIconUtils::buttonIcon(":/resources/icons/unstar.svg"));
  349. m_starBtn->setToolTip(tr("Unstar"));
  350. break;
  351. }
  352. }
  353. void VExplorer::setCurrentEntry(int p_index)
  354. {
  355. m_dirCB->setCurrentIndex(p_index);
  356. handleEntryActivated(p_index);
  357. }
  358. void VExplorer::updateExplorerEntryIndexInConfig()
  359. {
  360. int idx = -1;
  361. if (m_index >= 0 && m_index < m_entries.size()) {
  362. int starIdx = -1;
  363. for (int j = 0; j <= m_index; ++j) {
  364. if (m_entries[j].m_isStarred) {
  365. ++starIdx;
  366. }
  367. }
  368. if (starIdx > -1) {
  369. idx = starIdx;
  370. }
  371. }
  372. g_config->setExplorerCurrentIndex(idx);
  373. }
  374. void VExplorer::updateTree()
  375. {
  376. if (checkIndex()) {
  377. QString pa = QDir::cleanPath(m_entries[m_index].m_directory);
  378. QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
  379. model->setRootPath(pa);
  380. const QModelIndex rootIndex = model->index(pa);
  381. if (rootIndex.isValid()) {
  382. m_tree->setRootIndex(rootIndex);
  383. resizeTreeToContents();
  384. return;
  385. } else {
  386. model->setRootPath("");
  387. }
  388. }
  389. m_tree->reset();
  390. resizeTreeToContents();
  391. }
  392. void VExplorer::handleContextMenuRequested(QPoint p_pos)
  393. {
  394. QModelIndex index = m_tree->indexAt(p_pos);
  395. if (!index.isValid()) {
  396. return;
  397. }
  398. QMenu menu(this);
  399. menu.setToolTipsVisible(true);
  400. QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
  401. QModelIndexList selectedIdx = m_tree->selectionModel()->selectedRows();
  402. if (selectedIdx.size() == 1 && model->isDir(selectedIdx[0])) {
  403. QString filePath = model->filePath(selectedIdx[0]);
  404. QAction *setRootAct = new QAction(VIconUtils::menuIcon(":/resources/icons/explore_root.svg"),
  405. tr("Set As Root"),
  406. &menu);
  407. setRootAct->setToolTip(tr("Set current folder as the root directory to explore"));
  408. connect(setRootAct, &QAction::triggered,
  409. this, [this, filePath]() {
  410. setRootDirectory(filePath);
  411. });
  412. menu.addAction(setRootAct);
  413. QAction *openLocationAct = new QAction(VIconUtils::menuIcon(":/resources/icons/open_location.svg"),
  414. tr("Open Folder Location"),
  415. &menu);
  416. openLocationAct->setToolTip(tr("Explore this folder in operating system"));
  417. connect(openLocationAct, &QAction::triggered,
  418. this, [this, filePath]() {
  419. QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
  420. });
  421. menu.addAction(openLocationAct);
  422. menu.addSeparator();
  423. QAction *fileInfoAct = new QAction(VIconUtils::menuIcon(":/resources/icons/note_info.svg"),
  424. tr("&Info (Rename)\t%1").arg(VUtils::getShortcutText(c_infoShortcutSequence)),
  425. &menu);
  426. fileInfoAct->setToolTip(tr("View and edit current folder's information"));
  427. connect(fileInfoAct, &QAction::triggered,
  428. this, [this, filePath]() {
  429. renameFile(filePath);
  430. });
  431. menu.addAction(fileInfoAct);
  432. menu.exec(m_tree->mapToGlobal(p_pos));
  433. return;
  434. }
  435. bool allFiles = true;
  436. QStringList selectedFiles;
  437. for (auto const & it : selectedIdx) {
  438. if (model->isDir(it)) {
  439. allFiles = false;
  440. break;
  441. }
  442. selectedFiles << model->filePath(it);
  443. }
  444. if (!allFiles) {
  445. return;
  446. }
  447. // Only files are selected.
  448. // We do not support copy/cut/deletion of files.
  449. QAction *openInReadAct = new QAction(VIconUtils::menuIcon(":/resources/icons/reading.svg"),
  450. tr("Open In Read Mode"),
  451. &menu);
  452. openInReadAct->setToolTip(tr("Open selected files in read mode"));
  453. connect(openInReadAct, &QAction::triggered,
  454. this, [this, selectedFiles]() {
  455. openFiles(selectedFiles, OpenFileMode::Read, true);
  456. });
  457. menu.addAction(openInReadAct);
  458. QAction *openInEditAct = new QAction(VIconUtils::menuIcon(":/resources/icons/editing.svg"),
  459. tr("Open In Edit Mode"),
  460. &menu);
  461. openInEditAct->setToolTip(tr("Open selected files in edit mode"));
  462. connect(openInEditAct, &QAction::triggered,
  463. this, [this, selectedFiles]() {
  464. openFiles(selectedFiles, OpenFileMode::Edit, true);
  465. });
  466. menu.addAction(openInEditAct);
  467. menu.addSeparator();
  468. if (selectedFiles.size() == 1) {
  469. QAction *openLocationAct = new QAction(VIconUtils::menuIcon(":/resources/icons/open_location.svg"),
  470. tr("Open File Location"),
  471. &menu);
  472. openLocationAct->setToolTip(tr("Explore the folder containing this file in operating system"));
  473. connect(openLocationAct, &QAction::triggered,
  474. this, [this, filePath = selectedFiles[0]] () {
  475. QDesktopServices::openUrl(QUrl::fromLocalFile(VUtils::basePathFromPath(filePath)));
  476. });
  477. menu.addAction(openLocationAct);
  478. menu.addSeparator();
  479. }
  480. QAction *addToCartAct = new QAction(VIconUtils::menuIcon(":/resources/icons/cart.svg"),
  481. tr("Add To Cart"),
  482. &menu);
  483. addToCartAct->setToolTip(tr("Add selected files to Cart for further processing"));
  484. connect(addToCartAct, &QAction::triggered,
  485. this, [this, selectedFiles]() {
  486. VCart *cart = g_mainWin->getCart();
  487. for (int i = 0; i < selectedFiles.size(); ++i) {
  488. cart->addFile(selectedFiles[i]);
  489. }
  490. g_mainWin->showStatusMessage(tr("%1 %2 added to Cart")
  491. .arg(selectedFiles.size())
  492. .arg(selectedFiles.size() > 1 ? tr("files") : tr("file")));
  493. });
  494. menu.addAction(addToCartAct);
  495. QAction *pinToHistoryAct = new QAction(VIconUtils::menuIcon(":/resources/icons/pin.svg"),
  496. tr("Pin To History"),
  497. &menu);
  498. pinToHistoryAct->setToolTip(tr("Pin selected files to History"));
  499. connect(pinToHistoryAct, &QAction::triggered,
  500. this, [this, selectedFiles]() {
  501. g_mainWin->getHistoryList()->pinFiles(selectedFiles);
  502. g_mainWin->showStatusMessage(tr("%1 %2 pinned to History")
  503. .arg(selectedFiles.size())
  504. .arg(selectedFiles.size() > 1 ? tr("files") : tr("file")));
  505. });
  506. menu.addAction(pinToHistoryAct);
  507. if (selectedFiles.size() == 1) {
  508. QAction *fileInfoAct = new QAction(VIconUtils::menuIcon(":/resources/icons/note_info.svg"),
  509. tr("&Info (Rename)\t%1").arg(VUtils::getShortcutText(c_infoShortcutSequence)),
  510. &menu);
  511. fileInfoAct->setToolTip(tr("View and edit current file's information"));
  512. connect(fileInfoAct, &QAction::triggered,
  513. this, [this, filePath = selectedFiles[0]]() {
  514. renameFile(filePath);
  515. });
  516. menu.addAction(fileInfoAct);
  517. }
  518. menu.exec(m_tree->mapToGlobal(p_pos));
  519. }
  520. void VExplorer::openFiles(const QStringList &p_files,
  521. OpenFileMode p_mode,
  522. bool p_forceMode)
  523. {
  524. if (!p_files.isEmpty()) {
  525. Q_ASSERT(checkIndex());
  526. QString imgFolder = m_entries[m_index].m_imageFolder;
  527. QVector<VFile *> vfiles = g_mainWin->openFiles(p_files, false, p_mode, p_forceMode, false);
  528. // Set image folder.
  529. for (auto it : vfiles) {
  530. if (it->getType() == FileType::Orphan) {
  531. static_cast<VOrphanFile *>(it)->setImageFolder(imgFolder);
  532. }
  533. }
  534. }
  535. }
  536. void VExplorer::resizeTreeToContents()
  537. {
  538. m_tree->resizeColumnToContents(0);
  539. }
  540. // 1. When only one folder is selected, create a file in that folder.
  541. // 2. Otherwise, create a file in the root directory.
  542. void VExplorer::newFile()
  543. {
  544. Q_ASSERT(checkIndex());
  545. QString parentDir;
  546. QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
  547. QModelIndexList selectedIdx = m_tree->selectionModel()->selectedRows();
  548. if (selectedIdx.size() == 1 && model->isDir(selectedIdx[0])) {
  549. parentDir = model->filePath(selectedIdx[0]);
  550. } else {
  551. parentDir = m_entries[m_index].m_directory;
  552. }
  553. qDebug() << "new file in" << parentDir;
  554. QString name = VUtils::promptForFileName(tr("New File"),
  555. tr("File name (%1):").arg(parentDir),
  556. "",
  557. parentDir,
  558. g_mainWin);
  559. if (name.isEmpty()) {
  560. return;
  561. }
  562. QDir paDir(parentDir);
  563. QString filePath = paDir.filePath(name);
  564. QFile file(filePath);
  565. if (!file.open(QIODevice::WriteOnly)) {
  566. qWarning() << "Fail to create file" << filePath;
  567. VUtils::showMessage(QMessageBox::Warning,
  568. tr("New File"),
  569. tr("Fail to create file <span style=\"%1\">%2</span>.")
  570. .arg(g_config->c_dataTextStyle)
  571. .arg(filePath),
  572. "",
  573. QMessageBox::Ok,
  574. QMessageBox::Ok,
  575. g_mainWin);
  576. return;
  577. }
  578. file.close();
  579. m_tree->setFocus();
  580. // Select the file.
  581. const QModelIndex fileIndex = model->index(filePath);
  582. Q_ASSERT(fileIndex.isValid());
  583. m_tree->scrollTo(fileIndex);
  584. m_tree->clearSelection();
  585. m_tree->setCurrentIndex(fileIndex);
  586. }
  587. // 1. When only one folder is selected, create a folder in that folder.
  588. // 2. Otherwise, create a folder in the root directory.
  589. void VExplorer::newFolder()
  590. {
  591. Q_ASSERT(checkIndex());
  592. QString parentDir;
  593. QFileSystemModel *model = static_cast<QFileSystemModel *>(m_tree->model());
  594. QModelIndexList selectedIdx = m_tree->selectionModel()->selectedRows();
  595. if (selectedIdx.size() == 1 && model->isDir(selectedIdx[0])) {
  596. parentDir = model->filePath(selectedIdx[0]);
  597. } else {
  598. parentDir = m_entries[m_index].m_directory;
  599. }
  600. qDebug() << "new folder in" << parentDir;
  601. QString name = VUtils::promptForFileName(tr("New Folder"),
  602. tr("Folder name (%1):").arg(parentDir),
  603. "",
  604. parentDir,
  605. g_mainWin);
  606. if (name.isEmpty()) {
  607. return;
  608. }
  609. QDir paDir(parentDir);
  610. QString folderPath = paDir.filePath(name);
  611. if (!paDir.mkdir(name)) {
  612. qWarning() << "Fail to create folder" << folderPath;
  613. VUtils::showMessage(QMessageBox::Warning,
  614. tr("New Folder"),
  615. tr("Fail to create folder <span style=\"%1\">%2</span>.")
  616. .arg(g_config->c_dataTextStyle)
  617. .arg(folderPath),
  618. "",
  619. QMessageBox::Ok,
  620. QMessageBox::Ok,
  621. g_mainWin);
  622. return;
  623. }
  624. m_tree->setFocus();
  625. // Select the folder.
  626. const QModelIndex folderIndex = model->index(folderPath);
  627. Q_ASSERT(folderIndex.isValid());
  628. m_tree->scrollTo(folderIndex);
  629. m_tree->clearSelection();
  630. m_tree->setCurrentIndex(folderIndex);
  631. }
  632. void VExplorer::renameFile(const QString &p_filePath)
  633. {
  634. Q_ASSERT(checkIndex());
  635. QFileInfo fi(p_filePath);
  636. QString parentDir = fi.path();
  637. QString oldName = fi.fileName();
  638. QString name = VUtils::promptForFileName(tr("File Information"),
  639. tr("Rename file (%1):").arg(p_filePath),
  640. oldName,
  641. parentDir,
  642. g_mainWin);
  643. if (name.isEmpty()) {
  644. return;
  645. }
  646. QDir paDir(parentDir);
  647. if (!paDir.rename(oldName, name)) {
  648. qWarning() << "Fail to rename" << p_filePath;
  649. VUtils::showMessage(QMessageBox::Warning,
  650. tr("File Information"),
  651. tr("Fail to rename file <span style=\"%1\">%2</span>.")
  652. .arg(g_config->c_dataTextStyle)
  653. .arg(p_filePath),
  654. "",
  655. QMessageBox::Ok,
  656. QMessageBox::Ok,
  657. g_mainWin);
  658. return;
  659. }
  660. }
  661. void VExplorer::setRootDirectory(const QString &p_path)
  662. {
  663. if (p_path.isEmpty()) {
  664. return;
  665. }
  666. init();
  667. qDebug() << "set new root directory" << p_path;
  668. int idx = addEntry(p_path);
  669. updateDirectoryComboBox();
  670. if (idx != -1) {
  671. setCurrentEntry(idx);
  672. }
  673. }
  674. void VExplorer::keyPressEvent(QKeyEvent *p_event)
  675. {
  676. if (VimNavigationForWidget::injectKeyPressEventForVim(m_tree, p_event)) {
  677. return;
  678. }
  679. QWidget::keyPressEvent(p_event);
  680. }
  681. QString VExplorer::getRootDirectory() const
  682. {
  683. const_cast<VExplorer *>(this)->init();
  684. if (checkIndex()) {
  685. return m_entries[m_index].m_directory;
  686. }
  687. return QString();
  688. }