vutils.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. #include "vutils.h"
  2. #include <QFile>
  3. #include <QDir>
  4. #include <QDebug>
  5. #include <QRegExp>
  6. #include <QClipboard>
  7. #include <QApplication>
  8. #include <QMimeData>
  9. #include <QJsonObject>
  10. #include <QJsonDocument>
  11. #include <QDateTime>
  12. #include <QFileInfo>
  13. #include <QImageReader>
  14. #include <QKeyEvent>
  15. #include <QScreen>
  16. const QVector<QPair<QString, QString>> VUtils::c_availableLanguages = {QPair<QString, QString>("en_US", "Englisth(US)"),
  17. QPair<QString, QString>("zh_CN", "Chinese")};
  18. VUtils::VUtils()
  19. {
  20. }
  21. QString VUtils::readFileFromDisk(const QString &filePath)
  22. {
  23. QFile file(filePath);
  24. if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
  25. qWarning() << "fail to read file" << filePath;
  26. return QString();
  27. }
  28. QString fileText(file.readAll());
  29. file.close();
  30. qDebug() << "read file content:" << filePath;
  31. return fileText;
  32. }
  33. bool VUtils::writeFileToDisk(const QString &filePath, const QString &text)
  34. {
  35. QFile file(filePath);
  36. if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
  37. qWarning() << "fail to open file" << filePath << "to write";
  38. return false;
  39. }
  40. QTextStream stream(&file);
  41. stream << text;
  42. file.close();
  43. qDebug() << "write file content:" << filePath;
  44. return true;
  45. }
  46. QRgb VUtils::QRgbFromString(const QString &str)
  47. {
  48. Q_ASSERT(str.length() == 6);
  49. QString rStr = str.left(2);
  50. QString gStr = str.mid(2, 2);
  51. QString bStr = str.right(2);
  52. bool ok, ret = true;
  53. int red = rStr.toInt(&ok, 16);
  54. ret = ret && ok;
  55. int green = gStr.toInt(&ok, 16);
  56. ret = ret && ok;
  57. int blue = bStr.toInt(&ok, 16);
  58. ret = ret && ok;
  59. if (ret) {
  60. return qRgb(red, green, blue);
  61. }
  62. qWarning() << "fail to construct QRgb from string" << str;
  63. return QRgb();
  64. }
  65. QString VUtils::generateImageFileName(const QString &path, const QString &title,
  66. const QString &format)
  67. {
  68. Q_ASSERT(!title.isEmpty());
  69. QRegExp regExp("\\W");
  70. QString baseName(title.toLower());
  71. baseName.remove(regExp);
  72. baseName.prepend('_');
  73. // Add current time and random number to make the name be most likely unique
  74. baseName = baseName + '_' + QString::number(QDateTime::currentDateTime().toTime_t());
  75. baseName = baseName + '_' + QString::number(qrand());
  76. QString imageName = baseName + "." + format.toLower();
  77. QString filePath = QDir(path).filePath(imageName);
  78. int index = 1;
  79. while (QFile(filePath).exists()) {
  80. imageName = QString("%1_%2.%3").arg(baseName).arg(index++)
  81. .arg(format.toLower());
  82. filePath = QDir(path).filePath(imageName);
  83. }
  84. return imageName;
  85. }
  86. void VUtils::processStyle(QString &style, const QVector<QPair<QString, QString> > &varMap)
  87. {
  88. // Process style
  89. for (int i = 0; i < varMap.size(); ++i) {
  90. const QPair<QString, QString> &map = varMap[i];
  91. style.replace("@" + map.first, map.second);
  92. }
  93. }
  94. bool VUtils::isMarkdown(const QString &name)
  95. {
  96. const QVector<QString> mdPostfix({"md", "markdown", "mkd"});
  97. QStringList list = name.split('.', QString::SkipEmptyParts);
  98. if (list.isEmpty()) {
  99. return false;
  100. }
  101. const QString &postfix = list.last();
  102. for (int i = 0; i < mdPostfix.size(); ++i) {
  103. if (postfix == mdPostfix[i]) {
  104. return true;
  105. }
  106. }
  107. return false;
  108. }
  109. QString VUtils::fileNameFromPath(const QString &path)
  110. {
  111. if (path.isEmpty()) {
  112. return path;
  113. }
  114. return QFileInfo(QDir::cleanPath(path)).fileName();
  115. }
  116. QString VUtils::basePathFromPath(const QString &path)
  117. {
  118. return QFileInfo(path).path();
  119. }
  120. // Collect image links like ![](images/xx.jpg)
  121. QVector<QString> VUtils::imagesFromMarkdownFile(const QString &filePath)
  122. {
  123. Q_ASSERT(isMarkdown(filePath));
  124. QVector<QString> images;
  125. if (filePath.isEmpty()) {
  126. return images;
  127. }
  128. QString basePath = basePathFromPath(filePath);
  129. QString text = readFileFromDisk(filePath);
  130. QRegExp regExp("\\!\\[[^\\]]*\\]\\((images/[^/\\)]+)\\)");
  131. int pos = 0;
  132. while (pos < text.size() && (pos = regExp.indexIn(text, pos)) != -1) {
  133. Q_ASSERT(regExp.captureCount() == 1);
  134. qDebug() << regExp.capturedTexts()[0] << regExp.capturedTexts()[1];
  135. images.append(QDir(basePath).filePath(regExp.capturedTexts()[1]));
  136. pos += regExp.matchedLength();
  137. }
  138. return images;
  139. }
  140. void VUtils::makeDirectory(const QString &path)
  141. {
  142. if (path.isEmpty()) {
  143. return;
  144. }
  145. // mkdir will return false if it already exists
  146. QString basePath = basePathFromPath(path);
  147. QString dirName = directoryNameFromPath(path);
  148. QDir dir(basePath);
  149. if (dir.mkdir(dirName)) {
  150. qDebug() << "mkdir" << path;
  151. }
  152. }
  153. ClipboardOpType VUtils::opTypeInClipboard()
  154. {
  155. QClipboard *clipboard = QApplication::clipboard();
  156. const QMimeData *mimeData = clipboard->mimeData();
  157. if (mimeData->hasText()) {
  158. QString text = mimeData->text();
  159. QJsonObject clip = QJsonDocument::fromJson(text.toLocal8Bit()).object();
  160. if (clip.contains("operation")) {
  161. return (ClipboardOpType)clip["operation"].toInt();
  162. }
  163. }
  164. return ClipboardOpType::Invalid;
  165. }
  166. bool VUtils::copyFile(const QString &p_srcFilePath, const QString &p_destFilePath, bool p_isCut)
  167. {
  168. QString srcPath = QDir::cleanPath(p_srcFilePath);
  169. QString destPath = QDir::cleanPath(p_destFilePath);
  170. if (srcPath == destPath) {
  171. return true;
  172. }
  173. if (p_isCut) {
  174. QFile file(srcPath);
  175. if (!file.rename(destPath)) {
  176. qWarning() << "fail to copy file" << srcPath << destPath;
  177. return false;
  178. }
  179. } else {
  180. if (!QFile::copy(srcPath, destPath)) {
  181. qWarning() << "fail to copy file" << srcPath << destPath;
  182. return false;
  183. }
  184. }
  185. return true;
  186. }
  187. // Copy @p_srcDirPath to be @p_destDirPath.
  188. bool VUtils::copyDirectory(const QString &p_srcDirPath, const QString &p_destDirPath, bool p_isCut)
  189. {
  190. QString srcPath = QDir::cleanPath(p_srcDirPath);
  191. QString destPath = QDir::cleanPath(p_destDirPath);
  192. if (srcPath == destPath) {
  193. return true;
  194. }
  195. // Make a directory
  196. QDir parentDir(VUtils::basePathFromPath(p_destDirPath));
  197. QString dirName = VUtils::fileNameFromPath(p_destDirPath);
  198. if (!parentDir.mkdir(dirName)) {
  199. qWarning() << QString("fail to create target directory %1: already exists").arg(p_destDirPath);
  200. return false;
  201. }
  202. // Handle sub-dirs recursively and copy files.
  203. QDir srcDir(p_srcDirPath);
  204. QDir destDir(p_destDirPath);
  205. Q_ASSERT(srcDir.exists() && destDir.exists());
  206. QFileInfoList nodes = srcDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::Hidden
  207. | QDir::NoSymLinks | QDir::NoDotAndDotDot);
  208. for (int i = 0; i < nodes.size(); ++i) {
  209. const QFileInfo &fileInfo = nodes.at(i);
  210. QString name = fileInfo.fileName();
  211. if (fileInfo.isDir()) {
  212. if (!copyDirectory(srcDir.filePath(name), destDir.filePath(name), p_isCut)) {
  213. return false;
  214. }
  215. } else {
  216. Q_ASSERT(fileInfo.isFile());
  217. if (!copyFile(srcDir.filePath(name), destDir.filePath(name), p_isCut)) {
  218. return false;
  219. }
  220. }
  221. }
  222. // Delete the src dir if p_isCut
  223. if (p_isCut) {
  224. if (!srcDir.removeRecursively()) {
  225. qWarning() << "fail to remove directory" << p_srcDirPath;
  226. return false;
  227. }
  228. }
  229. return true;
  230. }
  231. int VUtils::showMessage(QMessageBox::Icon p_icon, const QString &p_title, const QString &p_text, const QString &p_infoText,
  232. QMessageBox::StandardButtons p_buttons, QMessageBox::StandardButton p_defaultBtn, QWidget *p_parent)
  233. {
  234. QMessageBox msgBox(p_icon, p_title, p_text, p_buttons, p_parent);
  235. msgBox.setInformativeText(p_infoText);
  236. msgBox.setDefaultButton(p_defaultBtn);
  237. return msgBox.exec();
  238. }
  239. QString VUtils::generateCopiedFileName(const QString &p_dirPath, const QString &p_fileName)
  240. {
  241. QString suffix;
  242. QString base = p_fileName;
  243. int dotIdx = p_fileName.lastIndexOf('.');
  244. if (dotIdx != -1) {
  245. // .md
  246. suffix = p_fileName.right(p_fileName.size() - dotIdx);
  247. base = p_fileName.left(dotIdx);
  248. }
  249. QDir dir(p_dirPath);
  250. QString name = p_fileName;
  251. QString filePath = dir.filePath(name);
  252. int index = 0;
  253. while (QFile(filePath).exists()) {
  254. QString seq;
  255. if (index > 0) {
  256. seq = QString::number(index);
  257. }
  258. index++;
  259. name = QString("%1_copy%2%3").arg(base).arg(seq).arg(suffix);
  260. filePath = dir.filePath(name);
  261. }
  262. return name;
  263. }
  264. QString VUtils::generateCopiedDirName(const QString &p_parentDirPath, const QString &p_dirName)
  265. {
  266. QDir dir(p_parentDirPath);
  267. QString name = p_dirName;
  268. QString dirPath = dir.filePath(name);
  269. int index = 0;
  270. while (QDir(dirPath).exists()) {
  271. QString seq;
  272. if (index > 0) {
  273. seq = QString::number(index);
  274. }
  275. index++;
  276. name = QString("%1_copy%2").arg(p_dirName).arg(seq);
  277. dirPath = dir.filePath(name);
  278. }
  279. return name;
  280. }
  281. const QVector<QPair<QString, QString>>& VUtils::getAvailableLanguages()
  282. {
  283. return c_availableLanguages;
  284. }
  285. bool VUtils::isValidLanguage(const QString &p_lang)
  286. {
  287. for (auto lang : c_availableLanguages) {
  288. if (lang.first == p_lang) {
  289. return true;
  290. }
  291. }
  292. return false;
  293. }
  294. bool VUtils::isImageURL(const QUrl &p_url)
  295. {
  296. QString urlStr;
  297. if (p_url.isLocalFile()) {
  298. urlStr = p_url.toLocalFile();
  299. } else {
  300. urlStr = p_url.toString();
  301. }
  302. return isImageURLText(urlStr);
  303. }
  304. bool VUtils::isImageURLText(const QString &p_url)
  305. {
  306. QFileInfo info(p_url);
  307. return QImageReader::supportedImageFormats().contains(info.suffix().toLower().toLatin1());
  308. }
  309. qreal VUtils::calculateScaleFactor()
  310. {
  311. // const qreal refHeight = 1152;
  312. // const qreal refWidth = 2048;
  313. const qreal refDpi = 96;
  314. qreal dpi = QGuiApplication::primaryScreen()->logicalDotsPerInch();
  315. qreal factor = dpi / refDpi;
  316. return factor < 1 ? 1 : factor;
  317. }