vfilelist.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #include <QtDebug>
  2. #include <QtWidgets>
  3. #include "vfilelist.h"
  4. #include "vconfigmanager.h"
  5. #include "vnewfiledialog.h"
  6. VFileList::VFileList(QWidget *parent)
  7. : QListWidget(parent)
  8. {
  9. setContextMenuPolicy(Qt::CustomContextMenu);
  10. initActions();
  11. connect(this, &VFileList::customContextMenuRequested,
  12. this, &VFileList::contextMenuRequested);
  13. connect(this, &VFileList::currentItemChanged,
  14. this, &VFileList::currentFileItemChanged);
  15. }
  16. void VFileList::initActions()
  17. {
  18. newFileAct = new QAction(tr("&New note"), this);
  19. newFileAct->setStatusTip(tr("Create a new note in current directory"));
  20. connect(newFileAct, &QAction::triggered,
  21. this, &VFileList::newFile);
  22. deleteFileAct = new QAction(tr("&Delete"), this);
  23. deleteFileAct->setStatusTip(tr("Delete selected note"));
  24. connect(deleteFileAct, &QAction::triggered,
  25. this, &VFileList::deleteFile);
  26. }
  27. void VFileList::setDirectory(QJsonObject dirJson)
  28. {
  29. if (dirJson.isEmpty()) {
  30. clearDirectoryInfo();
  31. return;
  32. }
  33. directoryName = dirJson["name"].toString();
  34. rootPath = dirJson["root_path"].toString();
  35. relativePath = QDir(dirJson["relative_path"].toString()).filePath(directoryName);
  36. qDebug() << "FileList update:" << rootPath << relativePath << directoryName;
  37. updateFileList();
  38. }
  39. void VFileList::clearDirectoryInfo()
  40. {
  41. directoryName = rootPath = relativePath = "";
  42. clear();
  43. }
  44. void VFileList::updateFileList()
  45. {
  46. clear();
  47. QString path = QDir(rootPath).filePath(relativePath);
  48. if (!QDir(path).exists()) {
  49. qDebug() << "invalid notebook directory:" << path;
  50. QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook directory."));
  51. msgBox.setInformativeText(QString("Notebook directory \"%1\" either does not exist or is not valid.")
  52. .arg(path));
  53. msgBox.exec();
  54. return;
  55. }
  56. QJsonObject configJson = VConfigManager::readDirectoryConfig(path);
  57. if (configJson.isEmpty()) {
  58. qDebug() << "invalid notebook configuration for directory:" << path;
  59. QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook directory configuration."));
  60. msgBox.setInformativeText(QString("Notebook directory \"%1\" does not contain a valid configuration file.")
  61. .arg(path));
  62. msgBox.exec();
  63. return;
  64. }
  65. // Handle files section
  66. QJsonArray filesJson = configJson["files"].toArray();
  67. for (int i = 0; i < filesJson.size(); ++i) {
  68. QJsonObject fileItem = filesJson[i].toObject();
  69. insertFileListItem(fileItem);
  70. }
  71. }
  72. QListWidgetItem* VFileList::insertFileListItem(QJsonObject fileJson, bool atFront)
  73. {
  74. Q_ASSERT(!fileJson.isEmpty());
  75. QListWidgetItem *item = new QListWidgetItem(fileJson["name"].toString());
  76. if (!fileJson["description"].toString().isEmpty()) {
  77. item->setToolTip(fileJson["description"].toString());
  78. }
  79. item->setData(Qt::UserRole, fileJson);
  80. if (atFront) {
  81. insertItem(0, item);
  82. } else {
  83. addItem(item);
  84. }
  85. qDebug() << "add new list item:" << fileJson["name"].toString();
  86. return item;
  87. }
  88. void VFileList::removeFileListItem(QListWidgetItem *item)
  89. {
  90. // Qt ensures it will be removed from QListWidget automatically
  91. delete item;
  92. }
  93. void VFileList::newFile()
  94. {
  95. QString text("&Note name:");
  96. QString defaultText("new_note");
  97. QString defaultDescription("");
  98. do {
  99. VNewFileDialog dialog(QString("Create a new note under %1").arg(directoryName), text,
  100. defaultText, tr("&Description:"), defaultDescription, this);
  101. if (dialog.exec() == QDialog::Accepted) {
  102. QString name = dialog.getNameInput();
  103. QString description = dialog.getDescriptionInput();
  104. if (isConflictNameWithExisting(name)) {
  105. text = "Name already exists.\nPlease choose another name:";
  106. defaultText = name;
  107. defaultDescription = description;
  108. continue;
  109. }
  110. QListWidgetItem *newItem = createFileAndUpdateList(name, description);
  111. if (newItem) {
  112. this->setCurrentItem(newItem);
  113. }
  114. }
  115. break;
  116. } while (true);
  117. }
  118. void VFileList::deleteFile()
  119. {
  120. QListWidgetItem *curItem = currentItem();
  121. QJsonObject curItemJson = curItem->data(Qt::UserRole).toJsonObject();
  122. QString curItemName = curItemJson["name"].toString();
  123. QMessageBox msgBox(QMessageBox::Warning, tr("Warning"),
  124. QString("Are you sure you want to delete note \"%1\"?")
  125. .arg(curItemName));
  126. msgBox.setInformativeText(tr("This may be not recoverable."));
  127. msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
  128. msgBox.setDefaultButton(QMessageBox::Ok);
  129. if (msgBox.exec() == QMessageBox::Ok) {
  130. deleteFileAndUpdateList(curItem);
  131. }
  132. }
  133. void VFileList::contextMenuRequested(QPoint pos)
  134. {
  135. QListWidgetItem *item = itemAt(pos);
  136. QMenu menu(this);
  137. if (directoryName.isEmpty()) {
  138. return;
  139. }
  140. menu.addAction(newFileAct);
  141. if (item) {
  142. menu.addAction(deleteFileAct);
  143. }
  144. menu.exec(mapToGlobal(pos));
  145. }
  146. bool VFileList::isConflictNameWithExisting(const QString &name)
  147. {
  148. int nrChild = this->count();
  149. for (int i = 0; i < nrChild; ++i) {
  150. QListWidgetItem *item = this->item(i);
  151. QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject();
  152. Q_ASSERT(!itemJson.isEmpty());
  153. if (itemJson["name"].toString() == name) {
  154. return true;
  155. }
  156. }
  157. return false;
  158. }
  159. QListWidgetItem* VFileList::createFileAndUpdateList(const QString &name,
  160. const QString &description)
  161. {
  162. QString path = QDir(rootPath).filePath(relativePath);
  163. QString filePath = QDir(path).filePath(name);
  164. QFile file(filePath);
  165. if (!file.open(QIODevice::WriteOnly)) {
  166. qWarning() << "error: fail to create file:" << filePath;
  167. QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Could not create file \"%1\" under \"%2\".")
  168. .arg(name).arg(path));
  169. msgBox.setInformativeText(QString("Please check if there already exists a file named \"%1\".").arg(name));
  170. msgBox.exec();
  171. return NULL;
  172. }
  173. file.close();
  174. qDebug() << "create file:" << filePath;
  175. // Update current directory's config file to include this new file
  176. QJsonObject dirJson = VConfigManager::readDirectoryConfig(path);
  177. Q_ASSERT(!dirJson.isEmpty());
  178. QJsonObject fileJson;
  179. fileJson["name"] = name;
  180. fileJson["description"] = description;
  181. QJsonArray fileArray = dirJson["files"].toArray();
  182. fileArray.push_front(fileJson);
  183. dirJson["files"] = fileArray;
  184. if (!VConfigManager::writeDirectoryConfig(path, dirJson)) {
  185. qWarning() << "error: fail to update directory's configuration file to add a new file"
  186. << name;
  187. file.remove();
  188. return NULL;
  189. }
  190. return insertFileListItem(fileJson, true);
  191. }
  192. void VFileList::deleteFileAndUpdateList(QListWidgetItem *item)
  193. {
  194. Q_ASSERT(item);
  195. QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject();
  196. QString path = QDir(rootPath).filePath(relativePath);
  197. QString fileName = itemJson["name"].toString();
  198. QString filePath = QDir(path).filePath(fileName);
  199. // Update current directory's config file to exclude this file
  200. QJsonObject dirJson = VConfigManager::readDirectoryConfig(path);
  201. Q_ASSERT(!dirJson.isEmpty());
  202. QJsonArray fileArray = dirJson["files"].toArray();
  203. bool deleted = false;
  204. for (int i = 0; i < fileArray.size(); ++i) {
  205. QJsonObject ele = fileArray[i].toObject();
  206. if (ele["name"].toString() == fileName) {
  207. fileArray.removeAt(i);
  208. deleted = true;
  209. break;
  210. }
  211. }
  212. if (!deleted) {
  213. qWarning() << "error: fail to find" << fileName << "to delete";
  214. return;
  215. }
  216. dirJson["files"] = fileArray;
  217. if (!VConfigManager::writeDirectoryConfig(path, dirJson)) {
  218. qWarning() << "error: fail to update directory's configuration file to delete"
  219. << fileName;
  220. return;
  221. }
  222. // Delete the file
  223. QFile file(filePath);
  224. if (!file.remove()) {
  225. qWarning() << "error: fail to delete" << filePath;
  226. } else {
  227. qDebug() << "delete" << filePath;
  228. }
  229. removeFileListItem(item);
  230. }
  231. void VFileList::currentFileItemChanged(QListWidgetItem *currentItem)
  232. {
  233. if (!currentItem) {
  234. emit currentFileChanged(QJsonObject());
  235. return;
  236. }
  237. QJsonObject itemJson = currentItem->data(Qt::UserRole).toJsonObject();
  238. Q_ASSERT(!itemJson.isEmpty());
  239. itemJson["path"] = QDir::cleanPath(QDir(rootPath).filePath(relativePath));
  240. qDebug() << "click file:" << itemJson;
  241. emit currentFileChanged(itemJson);
  242. }