AutoSave.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. /**
  2. * Copyright (c) 2011-2018 by Andrew Mustun. All rights reserved.
  3. *
  4. * This file is part of the QCAD project.
  5. *
  6. * QCAD is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * QCAD is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with QCAD.
  18. */
  19. include("scripts/File/File.js");
  20. include("scripts/sprintf.js");
  21. var documentCounter;
  22. if (documentCounter == undefined) {
  23. documentCounter = 0;
  24. }
  25. function AutoSave(guiAction) {
  26. File.call(this, guiAction);
  27. }
  28. AutoSave.prototype = new File();
  29. AutoSave.getPreferencesCategory = function() {
  30. return [ qsTr("File"), qsTr("Autosave")];
  31. };
  32. /**
  33. * Restarts or stopes the timer after preferences have been changed.
  34. */
  35. AutoSave.applyPreferences = function(doc, mdiChild) {
  36. EAction.handleUserMessage("autosave applyPreferences");
  37. var interval = RSettings.getIntValue("AutoSave/Interval", 5) * 60 * 1000;
  38. var enableAutoSave = RSettings.getBoolValue("AutoSave/EnableAutoSave", true);
  39. AutoSave.autoSaveTimer.interval = interval;
  40. if (enableAutoSave) {
  41. if (!AutoSave.autoSaveTimer.active) {
  42. AutoSave.autoSaveTimer.start(interval);
  43. }
  44. }
  45. else {
  46. if (AutoSave.autoSaveTimer.active) {
  47. AutoSave.autoSaveTimer.stop();
  48. }
  49. }
  50. };
  51. /*
  52. AutoSave.initPreferences = function(pageWidget, calledByPrefDialog, document) {
  53. var browseButton = pageWidget.findChild("AutoSaveBrowse");
  54. var pathEdit = pageWidget.findChild("AutoSavePath");
  55. var autoPathLabel = pageWidget.findChild("AutoPath");
  56. autoPathLabel.text = QDir.homePath();
  57. browseButton.clicked.connect(function() {
  58. var path = QFileDialog.getExistingDirectory(pageWidget, qsTr("Choose Autosave Directory"), QDir.homePath());
  59. RSettings.setValue("AutoSave/AutoSavePath", path);
  60. pathEdit.text = path;
  61. });
  62. pathEdit.textChanged.connect(function(text) {
  63. if (text.length===0) {
  64. autoPathLabel.text = QDir.homePath();
  65. }
  66. else {
  67. autoPathLabel.text = "";
  68. }
  69. });
  70. };
  71. */
  72. AutoSave.getTimestamp = function() {
  73. return new Date().toLocaleTimeString();
  74. };
  75. AutoSave.init = function(basePath) {
  76. EAction.handleUserMessage("autosave init");
  77. AutoSave.autoSaveTimer = new QTimer(this);
  78. AutoSave.autoSaveTimer.timeout.connect(AutoSave.autoSave);
  79. // start autosave timer for the first time:
  80. AutoSave.applyPreferences();
  81. };
  82. /**
  83. * Called periodically to perform an auto save of the current document.
  84. */
  85. AutoSave.autoSave = function() {
  86. var appWin = EAction.getMainWindow();
  87. if (appWin.property("DisableAutoSave")==true) {
  88. return;
  89. }
  90. var filterStrings = RFileExporterRegistry.getFilterStrings();
  91. if (filterStrings.length===0) {
  92. // don't attempt autosave if no filters are available to save:
  93. return;
  94. }
  95. var fi;
  96. var document = EAction.getDocument();
  97. var documentInterface = EAction.getDocumentInterface();
  98. if (isNull(document) || isNull(documentInterface)) {
  99. qDebug("autosave skipped [" + AutoSave.getTimestamp() + "]: no document");
  100. return undefined;
  101. }
  102. if (!document.isModified()) {
  103. qDebug("autosave skipped [" + AutoSave.getTimestamp() + "]: no modifications");
  104. return undefined;
  105. }
  106. var bakFileName = AutoSave.getAutoSaveFileNameCurrent();
  107. if (isNull(bakFileName)) {
  108. return undefined;
  109. }
  110. qDebug("autosaving [" + AutoSave.getTimestamp() + "]: file: ", bakFileName);
  111. var fileVersion = "R2012 DXF";
  112. EAction.handleUserMessage("[" + AutoSave.getTimestamp() + "] "
  113. + qsTr("Autosaving to:") + " "
  114. + QDir.toNativeSeparators(bakFileName) + "...");
  115. fi = new QFileInfo(bakFileName);
  116. var failed = true;
  117. // temp file name to make sure that a failed save does not overwrite
  118. // a good save (e.g. /home/tux/data/~~file.dxf):
  119. var tmpBakFileName = fi.absolutePath() + QDir.separator + "~" + fi.fileName();
  120. var fiDir = new QFileInfo(fi.absolutePath());
  121. if (fiDir.isWritable()) {
  122. if (documentInterface.exportFile(tmpBakFileName, fileVersion, false)) {
  123. var bakFile = new QFile(bakFileName);
  124. var tmpBakFile = new QFile(tmpBakFileName);
  125. if ((!bakFile.exists() || bakFile.remove()) && tmpBakFile.rename(bakFileName)) {
  126. failed = false;
  127. }
  128. }
  129. }
  130. EAction.handleUserMessage("call autosave");
  131. if (failed) {
  132. EAction.handleUserWarning("[" + AutoSave.getTimestamp() + "] "
  133. + qsTr("Autosave failed:") + " "
  134. + QDir.toNativeSeparators(bakFileName));
  135. return undefined;
  136. }
  137. else {
  138. EAction.handleUserMessage("[" + AutoSave.getTimestamp() + "] "
  139. + qsTr("Autosave complete."));
  140. return bakFileName;
  141. }
  142. };
  143. /**
  144. * Checks if there are backup files available for the given drawing file and
  145. * offers the user a choice to recover them if desired.
  146. *
  147. * \return true if files were recovered.
  148. */
  149. AutoSave.recoverUntitled = function() {
  150. // var dir = new QDir(RSettings.getPath());
  151. // //EAction.handleUserMessage(RSettings.getPath());
  152. // var list = dir.entryList(["~*"]);
  153. // if (list.length===0) {
  154. // return false;
  155. // }
  156. // EAction.handleUserMessage("call recoverUntitled");
  157. // var msg = qsTr("%n autosave backup file(s) for (an) untitled drawing(s) was/were found.\n" +
  158. // "Do you wish to recover it/them?", "", list.length);
  159. // var buttons = makeQMessageBoxStandardButtons(QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel);
  160. // var ret = QMessageBox.warning(null,
  161. // qsTr("Recover Files?"),
  162. // msg,
  163. // buttons);
  164. // if (ret===QMessageBox.Yes) {
  165. // for (var i=0; i<list.length; i++) {
  166. // var fileName = RSettings.getPath() + QDir.separator + list[i];
  167. // var mdiChild = NewFile.createMdiChild(fileName);
  168. // if (isNull(mdiChild)) {
  169. // qWarning("Cannot recover file " + fileName);
  170. // continue;
  171. // }
  172. // var document = mdiChild.getDocument();
  173. // document.setFileName("");
  174. // document.setModified(true);
  175. // mdiChild.setWindowTitle(addDirtyFlag(qsTr("Recovered") + " %1".arg(documentCounter)));
  176. // mdiChild.objectName = "Recovered%1".arg(documentCounter);
  177. // documentCounter++;
  178. // var autoSaveFileName = AutoSave.autoSave();
  179. // if (fileName!==autoSaveFileName) {
  180. // var file = new QFile(fileName);
  181. // file.remove();
  182. // }
  183. // }
  184. // return true;
  185. // }
  186. // else if (ret===QMessageBox.No) {
  187. // for (var i=0; i<list.length; i++) {
  188. // var fileName = RSettings.getPath() + QDir.separator + list[i];
  189. // var file = new QFile(fileName);
  190. // file.remove();
  191. // }
  192. // return false;
  193. // }
  194. return false;
  195. };
  196. /**
  197. * Checks if there is a backup file available for the given drawing file and
  198. * offers the user a choice to recover it if desired.
  199. *
  200. * \return true no auto save file or auto save file handled. False: cancel.
  201. */
  202. AutoSave.recover = function(fileName) {
  203. // var bakFileName = AutoSave.getAutoSaveFileName(fileName);
  204. // var fi = new QFileInfo(fileName);
  205. // //fi.absolutePath() + QDir.separator + "~" + fi.fileName();
  206. // var bakFi = new QFileInfo(bakFileName);
  207. // // no backup file found,
  208. // // tell caller to open the original file:
  209. // if (!bakFi.exists()) {
  210. // return true;
  211. // }
  212. // // backup file found: ask user what to do:
  213. // var buttons = makeQMessageBoxStandardButtons(QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel);
  214. // var ret = QMessageBox.warning(null,
  215. // qsTr("Recover File?"),
  216. // qsTr("An autosave backup file for \"%1\" exists.\n" +
  217. // "Do you wish to recover it?").arg(fi.fileName()),
  218. // buttons);
  219. // // canceled:
  220. // if (ret===QMessageBox.Cancel) {
  221. // return false;
  222. // }
  223. // // find an available name for a numbered backup file (e.g. file.001.dxf):
  224. // var file = new QFile(fileName);
  225. // var bakFile = new QFile(bakFileName);
  226. // var bakFileNameNumbered;
  227. // var num = 0;
  228. // do {
  229. // var postfix = sprintf("bak%03d", num++);
  230. // bakFileNameNumbered =
  231. // fi.absolutePath() + QDir.separator +
  232. // fi.completeBaseName() + "." + postfix + "." + fi.suffix();
  233. // } while(new QFileInfo(bakFileNameNumbered).exists());
  234. // // move backup file away and open regular file:
  235. // if (ret===QMessageBox.No) {
  236. // if (!bakFile.rename(bakFileNameNumbered)) {
  237. // EAction.handleUserWarning("[" + AutoSave.getTimestamp() + "] "
  238. // + qsTr("Cannot rename file:") + ' ' + bakFileName);
  239. // return false;
  240. // }
  241. // EAction.handleUserMessage(qsTr("Moved autosave backup file to:") + ' ' + bakFileNameNumbered);
  242. // return true;
  243. // }
  244. // // move regular file away and make backup file regular file to open:
  245. // else if (ret===QMessageBox.Yes) {
  246. // if (!file.rename(bakFileNameNumbered)) {
  247. // EAction.handleUserWarning("[" + AutoSave.getTimestamp() + "] "
  248. // + qsTr("Cannot rename file:") + ' ' + fileName);
  249. // return false;
  250. // }
  251. // if (!bakFile.rename(fileName)) {
  252. // EAction.handleUserWarning("[" + AutoSave.getTimestamp() + "] "
  253. // + qsTr("Cannot rename file:") + ' ' + bakFileName);
  254. // return false;
  255. // }
  256. // EAction.handleUserMessage(qsTr("Moved original file to:") + ' ' + bakFileNameNumbered);
  257. // return true;
  258. // }
  259. return false;
  260. };
  261. /**
  262. * Called when the user saves a file. Removes the auto save file (if available).
  263. */
  264. AutoSave.cleanUp = function(bakFileName) {
  265. //var bakFileName = AutoSave.getAutoSaveFileNameCurrent();
  266. var file = new QFile(bakFileName);
  267. if (file.exists()) {
  268. if (!file.remove()) {
  269. EAction.handleUserWarning("[" + AutoSave.getTimestamp() + "] "
  270. + qsTr("Cannot remove file:") + ' ' + bakFileName);
  271. }
  272. }
  273. };
  274. /**
  275. * \return File name of autosave file for the current document.
  276. */
  277. AutoSave.getAutoSaveFileNameCurrent = function() {
  278. var document = EAction.getDocument();
  279. if (isNull(document)) {
  280. return undefined;
  281. }
  282. var bakFileName = document.getFileName();
  283. if (bakFileName!=="") {
  284. return AutoSave.getAutoSaveFileName(bakFileName);
  285. }
  286. // file not saved yet:
  287. var mdiChild = EAction.getMdiChild();
  288. var counter = 0;
  289. //var postfix = "";
  290. var title = mdiChild.windowTitle;
  291. title = stripDirtyFlag(title);
  292. title = title.replace(" ", "_");
  293. bakFileName = RSettings.getPath() + QDir.separator + "~" + title + ".dxf";
  294. // do {
  295. // // e.g. /home/tux/data/~file.dxf or /home/tux/data/~file_001.dxf
  296. // bakFileName = RSettings.getPath() + QDir.separator + "~" + title + postfix + ".dxf";
  297. // if (!new QFileInfo(bakFileName).exists()) {
  298. // break;
  299. // }
  300. // postfix = "_%1".arg(counter++, 3, 10, '0');
  301. // } while(true);
  302. return bakFileName;
  303. };
  304. /**
  305. * \return Auto save file name for the given file name.
  306. */
  307. AutoSave.getAutoSaveFileName = function(fileName) {
  308. var fi = new QFileInfo(fileName);
  309. var suffix = fi.suffix();
  310. if (suffix.toLowerCase()!=="dxf") {
  311. suffix = "dxf";
  312. }
  313. return fi.absolutePath() + QDir.separator + "~" + fi.completeBaseName() + "." + suffix;
  314. };