NewFile.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  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/File/Save/Save.js");
  21. include("scripts/Widgets/ViewportWidget/ViewportWidget.js");
  22. include("scripts/File/AutoSave/AutoSave.js");
  23. include("scripts/Reset/Reset.js");
  24. if (exists("scripts/DefaultAction.js")) {
  25. include("scripts/DefaultAction.js");
  26. }
  27. if (exists("scripts/Navigation/DefaultNavigation.js")) {
  28. include("scripts/Navigation/DefaultNavigation.js");
  29. }
  30. // counter for default name of new documents:
  31. var documentCounter;
  32. if (documentCounter == undefined) {
  33. documentCounter = 1;
  34. }
  35. /**
  36. * \class NewFile
  37. * \brief Creates a new document.
  38. * \ingroup ecma_file
  39. */
  40. function NewFile(guiAction) {
  41. File.call(this, guiAction);
  42. }
  43. NewFile.prototype = new File();
  44. NewFile.includeBasePath = includeBasePath;
  45. // list of actions to trigger after opening file:
  46. NewFile.postOpenActions = [];
  47. NewFile.postNewActions = [];
  48. NewFile.getScriptFileName = function(scriptFile) {
  49. if (scriptFile.startsWith(":/")) {
  50. return ":" + scriptFile.mid(2);
  51. }
  52. return scriptFile;
  53. };
  54. /**
  55. * Adds the given script file as action to be triggered after a document
  56. * has been opened.
  57. */
  58. NewFile.addPostOpenAction = function(scriptFile) {
  59. var sf = NewFile.getScriptFileName(scriptFile);
  60. if (!NewFile.postOpenActions.contains(sf)) {
  61. NewFile.postOpenActions.push(sf);
  62. }
  63. };
  64. /**
  65. * Removes the given script file as action to be triggered after a document
  66. * has been opened.
  67. */
  68. NewFile.removePostOpenAction = function(scriptFile) {
  69. var sf = NewFile.getScriptFileName(scriptFile)
  70. NewFile.postOpenActions.remove(sf);
  71. };
  72. /**
  73. * Adds the given script file as action to be triggered after a new document
  74. * has been created.
  75. */
  76. NewFile.addPostNewAction = function(scriptFile) {
  77. var sf = NewFile.getScriptFileName(scriptFile);
  78. if (!NewFile.postNewActions.contains(sf)) {
  79. NewFile.postNewActions.push(sf);
  80. }
  81. };
  82. /**
  83. * Removes the given script file as action to be triggered after a new document
  84. * has been created.
  85. */
  86. NewFile.removePostNewAction = function(scriptFile) {
  87. var sf = NewFile.getScriptFileName(scriptFile)
  88. NewFile.postNewActions.remove(sf);
  89. };
  90. /**
  91. * \param isOpen True if new is called from a file open function.
  92. */
  93. NewFile.prototype.beginEvent = function() {
  94. var appWin = EAction.getMainWindow();
  95. var mdiArea = appWin.centralWidget();
  96. //var nfileCounter = mdiArea.GetSubWindowNum();
  97. //for (var i = 0; i < nfileCounter;i++) {
  98. // var closeEvent = new RCloseCurrentEvent();
  99. // QCoreApplication.postEvent(appWin, closeEvent);
  100. //}
  101. var w = NewFile.createMdiChild();
  102. var w1 = NewFile.createMdiChild();
  103. //var w2 = NewFile.createMdiChild();
  104. mdiArea.tileSubWindows();
  105. };
  106. /**
  107. * Creates a new MDI child based on the given UI file and adds it to the
  108. * MDI area.
  109. *
  110. * \param fileName File name of file to open.
  111. * \param nameFilter Filter to use to import file.
  112. * \param uiFile UI file to use for view port (defaults to ViewportWidgetQt.ui).
  113. * \param graphicsSceneClass Class to use for graphics scene (defaults to "RGraphicsSceneQt")
  114. */
  115. NewFile.createMdiChild = function (fileName, nameFilter, uiFile, graphicsSceneClass, silent) {
  116. var isOpen = !isNull(fileName);
  117. if (isNull(nameFilter)) {
  118. nameFilter = "";
  119. }
  120. if (isNull(silent)) {
  121. silent = false;
  122. }
  123. if (isOpen && !isUrl(fileName)) {
  124. if (!isNull(AutoSave) && !new QFileInfo(fileName).baseName().startsWith("~")) {
  125. if (!AutoSave.recover(fileName)) {
  126. // canceled file recovering:
  127. return undefined;
  128. }
  129. }
  130. }
  131. var appWin = EAction.getMainWindow();
  132. var mdiArea = appWin.centralWidget();
  133. if (isOpen) {
  134. appWin.handleUserMessage(qsTr("Opening drawing:") + " " + fileName + "...");
  135. }
  136. // create document:
  137. var storage = new RMemoryStorage();
  138. var spatialIndex = createSpatialIndex();
  139. var document = new RDocument(storage, spatialIndex);
  140. var documentInterface = new RDocumentInterface(document);
  141. if (isOpen) {
  142. //appWin.setProgressText(qsTr("Loading..."));
  143. var errorCode;
  144. if (isUrl(fileName)) {
  145. errorCode = documentInterface.importUrl(new QUrl(fileName), nameFilter);
  146. document.setFileName("");
  147. document.setModified(true);
  148. }
  149. else {
  150. errorCode = documentInterface.importFile(fileName, nameFilter);
  151. }
  152. if (errorCode !== RDocumentInterface.IoErrorNoError) {
  153. var dialog = undefined;
  154. var path = fileName;
  155. if (!silent) {
  156. dialog = new QMessageBox(
  157. QMessageBox.Warning,
  158. qsTr("Import Error"),
  159. "",
  160. QMessageBox.OK
  161. );
  162. path = fileName.elidedText(dialog.font, 500);
  163. }
  164. var text = qsTr("Cannot open file") + "\n\n'%1'.\n\n".arg(path);
  165. switch (errorCode) {
  166. case RDocumentInterface.IoErrorNoImporterFound:
  167. text += qsTr("No suitable Importer found. "
  168. + "Please check file format and extension.");
  169. break;
  170. case RDocumentInterface.IoErrorNotFound:
  171. text += qsTr("File not found.");
  172. break;
  173. case RDocumentInterface.IoErrorPermission:
  174. text += qsTr("Please check your access rights, "
  175. + "the file format and file extension.");
  176. break;
  177. case RDocumentInterface.IoErrorGeneralImportError:
  178. text += qsTr("Import failed.");
  179. break;
  180. case RDocumentInterface.IoErrorZeroSize:
  181. text += qsTr("File is empty.");
  182. break;
  183. }
  184. appWin.handleUserWarning(text);
  185. if (!isNull(dialog)) {
  186. dialog.text = text;
  187. dialog.exec();
  188. destr(dialog);
  189. EAction.activateMainWindow();
  190. }
  191. RSettings.removeRecentFile(fileName);
  192. return undefined;
  193. }
  194. else {
  195. appWin.handleUserMessage(qsTr("Drawing loaded successfully:") + " " + fileName);
  196. }
  197. if (document.getFileVersion().length!==0) {
  198. appWin.handleUserMessage(qsTr("Format:") + " " + document.getFileVersion());
  199. }
  200. RSettings.removeRecentFile(fileName);
  201. RSettings.addRecentFile(fileName);
  202. }
  203. if (!isOpen) {
  204. // color of layer 0 defaults to black if background color is bright:
  205. var bgColor = RSettings.getColor("GraphicsViewColors/BackgroundColor", new RColor("black"));
  206. if (bgColor.lightness()>200) {
  207. var layer0 = document.queryLayer("0");
  208. layer0.setColor(new RColor("black"));
  209. var op = new RModifyObjectOperation(layer0, false);
  210. documentInterface.applyOperation(op);
  211. document.setModified(false);
  212. }
  213. }
  214. var uiFileName = undefined;
  215. // get viewport template from drawing settings
  216. if (isOpen) {
  217. uiFileName = document.getVariable("Viewport/ViewportList.data");
  218. }
  219. if (!isOpen || isNull(uiFileName)) {
  220. uiFileName = RSettings.getStringValue("Viewport/ViewportList.data", "00_Single.ui");
  221. }
  222. var mdiChild = new RMdiChildQt();
  223. mdiChild.windowIcon = new QIcon(autoPath("scripts/qcad_icon.png"));
  224. mdiChild.setDocumentInterface(documentInterface);
  225. // var flags = makeQtWindowFlags(Qt.Window & Qt.WindowMinMaxButtonsHint);
  226. var flags = makeQtWindowFlags(Qt.CustomizeWindowHint & ~Qt.WindowMinimizeButtonHint & ~Qt.WindowContextHelpButtonHint);
  227. mdiChild.setWindowFlags(flags);
  228. mdiArea.addSubWindow(mdiChild);
  229. mdiChild.updatesEnabled = false;
  230. // load ui file and set the MDI content widget:
  231. //qDebug("initMdiChild");
  232. ViewportWidget.initMdiChild(mdiChild, uiFileName);
  233. // fix Qt bug in which Qt icon is shown at the top left if tab bar is off:
  234. // mdiChild.showMaximized();
  235. mdiChild.show();
  236. var viewports = ViewportWidget.getViewports(mdiChild, documentInterface);
  237. mdiChild.viewports = viewports;
  238. ViewportWidget.initializeViewports(viewports, uiFile, graphicsSceneClass);
  239. NewFile.updateTitle(mdiChild);
  240. NewFile.setupDefaultAction(documentInterface);
  241. ViewportWidget.initEventHandler(viewports);
  242. RGuiAction.triggerGroupDefaults();
  243. mdiChild.closeRequested.connect(NewFile.closeRequested);
  244. mdiChild.modifiedStatusChanged.connect(NewFile.updateTitle);
  245. appWin.resumedTab.connect(NewFile.updateTitle);
  246. // make sure the MDI widget is maximized before performing an auto zoom:
  247. // Qt 5 / Unity bug workaround:
  248. // breaks Ubuntu Unity menu on start:
  249. if (RS.getSystemId()!=="linux" || !RSettings.isQt(5) || appWin.property("starting")!==true) {
  250. appWin.disable();
  251. }
  252. for (var i=0; i<5; i++) {
  253. QCoreApplication.processEvents();
  254. }
  255. if (!appWin.enabled) {
  256. appWin.enable();
  257. }
  258. //qDebug("subWindowActivated");
  259. appWin.subWindowActivated(mdiChild);
  260. //qDebug("updateViewports");
  261. if (!isDeleted(mdiChild)) {
  262. mdiChild.updatesEnabled = true;
  263. ViewportWidget.updateViewports(viewports);
  264. }
  265. var k, action;
  266. if (isOpen) {
  267. // trigger post open actions (actions configured by the user to be
  268. // run whenever a file is opened, for example to show a font
  269. // replacement dialog, etc):
  270. for (k=0; k<NewFile.postOpenActions.length; k++) {
  271. if (!isNull(NewFile.postOpenActions[k])) {
  272. action = RGuiAction.getByScriptFile(NewFile.postOpenActions[k]);
  273. if (!isNull(action)) {
  274. action.slotTrigger();
  275. }
  276. }
  277. }
  278. }
  279. else {
  280. // trigger post new action (e.g. to load template file):
  281. for (k=0; k<NewFile.postNewActions.length; k++) {
  282. if (!isNull(NewFile.postNewActions[k])) {
  283. include(NewFile.postNewActions[k]);
  284. if (typeof(initNewFile)!=="undefined" && isFunction(initNewFile)) {
  285. initNewFile(mdiChild);
  286. }
  287. action = RGuiAction.getByScriptFile(NewFile.postNewActions[k]);
  288. if (!isNull(action)) {
  289. action.slotTrigger();
  290. }
  291. }
  292. }
  293. }
  294. if (!isNull(mdiChild) && !isDeleted(mdiChild)) {
  295. mdiChild.setFocus();
  296. }
  297. /*mdiArea.setViewMode(0);*/
  298. return mdiChild;
  299. };
  300. NewFile.updateTitle = function (mdiChild, RoutefileType) {
  301. var appWin = EAction.getMainWindow();
  302. var tabBar = appWin.getTabBar();
  303. if (isNull(mdiChild)) {
  304. mdiChild = appWin.getMdiChild();
  305. }
  306. if (isNull(mdiChild)) {
  307. return;
  308. }
  309. var document = mdiChild.getDocument();
  310. var fileName = document.getFileName();
  311. var title = "";
  312. var titleAppWin = "";
  313. // untitled:
  314. if (fileName==="") {
  315. if (mdiChild.windowTitle==="") {
  316. //title = addDirtyFlag(qsTr("Untitled %1").arg(documentCounter));
  317. //if (!isNull(tabBar)) {
  318. // tabBar.setTabToolTip(tabBar.currentIndex, title);
  319. //}
  320. //mdiChild.objectName = "Untitled%1".arg(documentCounter);
  321. //documentCounter++;
  322. if (documentCounter%3 == 0) {
  323. title = "纵断面视图";
  324. }
  325. else if (documentCounter % 3 == 2) {
  326. title = "Profile View";
  327. }
  328. else if (documentCounter % 3 == 1) {
  329. title = "平面视图";
  330. }
  331. else {
  332. title = addDirtyFlag(qsTr("Untitled %1").arg(documentCounter));
  333. }
  334. if (!isNull(tabBar)) {
  335. tabBar.setTabToolTip(tabBar.currentIndex, title);
  336. }
  337. mdiChild.objectName = title;
  338. documentCounter++;
  339. mdiChild.setWindowTitle(title);
  340. }
  341. titleAppWin = mdiChild.windowTitle;
  342. fileName = "Untitled.dxf";
  343. }
  344. else {
  345. var fi = new QFileInfo(fileName);
  346. var name = fi.fileName();
  347. if (fi.isWritable()) {
  348. title = addDirtyFlag(name);
  349. }
  350. else {
  351. var roStr = qsTr("read-only");
  352. title = name + " " + roStr;
  353. }
  354. mdiChild.objectName = name;
  355. if (!isNull(tabBar)) {
  356. tabBar.setTabToolTip(tabBar.currentIndex, fileName);
  357. }
  358. titleAppWin = title;
  359. title = title.replace(/&/g, "&&");
  360. mdiChild.setWindowTitle(title);
  361. }
  362. // TODO: implement proxy icon with drag and drop
  363. // triggers QIconDragEvent
  364. //if (RS.getSystemId()==="osx") {
  365. //appWin.windowFilePath = fileName;
  366. //appWin.windowModified = document.isModified();
  367. //}
  368. if("" == RSettings.getStringValue("OpenProject/Current", "")) {
  369. EAction.handleUserMessage("xxx");
  370. appWin.setWindowTitle(
  371. stripDirtyFlag(titleAppWin) +
  372. (document.isModified() ? " *" : "") +
  373. " - " + qApp.applicationName);
  374. }
  375. else {
  376. // EAction.handleUserMessage(RSettings.getStringValue("OpenProject/Current", ""));
  377. var pjname = RSettings.getStringValue("OpenProject/Current", "");
  378. //var displayname = pjname.substring(pjname.lastIndexOf("/")+1);
  379. appWin.setWindowTitle(
  380. stripDirtyFlag(titleAppWin) +
  381. (document.isModified() ? " *" : "") +
  382. " - " + qApp.applicationName + " - " + pjname //displayname
  383. );
  384. }
  385. // appWin.setWindowTitle(
  386. // stripDirtyFlag(titleAppWin) +
  387. // (document.isModified() ? " *" : "") +
  388. // " - " + qApp.applicationName + " - xxx"
  389. // );
  390. var mdiArea = EAction.getMdiArea();
  391. if (!isNull(mdiArea)) {
  392. mdiArea.updateTabBar();
  393. }
  394. };
  395. /**
  396. * Called when the user is about to close the drawing.
  397. */
  398. NewFile.closeRequested = function(mdiChild) {
  399. // make sure the closing MDI child is the active one
  400. // (not the case if inactive tab is closed using close button on tab):
  401. // 清空历史的工程地址,重新选择工程路径
  402. // RSettings.setValue("OpenProject/Current", "");
  403. RSettings.setValue("OpenDisplayFile/PMFile", "");
  404. RSettings.setValue("OpenDisplayFile/ZDMFile", "");
  405. var appWin = EAction.getMainWindow();
  406. var mdiArea = EAction.getMdiArea();
  407. mdiArea.setActiveSubWindow(mdiChild);
  408. var di = mdiChild.getDocumentInterface();
  409. var document = mdiChild.getDocument();
  410. if (!document.isModified()) {
  411. mdiChild.setCloseEventAccepted();
  412. return;
  413. }
  414. if (!isNull(appWin) && appWin.property("NewFile/DiscardChanges")===true) {
  415. mdiChild.setCloseEventAccepted();
  416. return;
  417. }
  418. // var dialog = WidgetFactory.createDialog(NewFile.includeBasePath, "CloseDialog.ui", mdiChild);
  419. // var fileName = new QFileInfo(document.getFileName()).fileName();
  420. // if (fileName.length===0) {
  421. // fileName = stripDirtyFlag(mdiChild.windowTitle);
  422. // }
  423. // var label1 = dialog.findChild("Text1");
  424. // label1.text = "<b>" + qsTr("Do you want to save the changes you made in<br>" +
  425. // "the document '%1'?").arg(fileName) + "</b>";
  426. // var label2 = dialog.findChild("Text2");
  427. // label2.text = qsTr("Your changes will be lost if you don't save them.");
  428. // var buttonBox = dialog.findChild("ButtonBox");
  429. // var discardButton = buttonBox.button(QDialogButtonBox.Discard);
  430. // discardButton.autoDefault = false;
  431. // discardButton['default'] = false;
  432. // // this would allow the user to not save by pressing the space bar
  433. // // seems dangerous though
  434. // //discardButton.setFocus();
  435. // var saveButton = buttonBox.button(QDialogButtonBox.Save);
  436. // saveButton.autoDefault = true;
  437. // saveButton['default'] = true;
  438. // var bakFileName = AutoSave.getAutoSaveFileNameCurrent();
  439. // buttonBox.clicked.connect(function(button) {
  440. // var standardButton = buttonBox.standardButton(button);
  441. // switch (standardButton) {
  442. // // cancel:
  443. // case QDialogButtonBox.Cancel:
  444. // dialog.reject();
  445. // break;
  446. // // discard (don't save):
  447. // case QDialogButtonBox.Discard:
  448. // dialog.accept();
  449. // break;
  450. // // save:
  451. // case QDialogButtonBox.Save:
  452. // var saveAction = new Save();
  453. // di.setCurrentAction(saveAction);
  454. // // if the 'save' or 'save as' was successful, the document is now unmodified:
  455. // if (!document.isModified()) {
  456. // dialog.accept();
  457. // }
  458. // break;
  459. // // save all:
  460. // //case QDialogButtonBox.SaveAll:
  461. // // dialog.accept();
  462. // // break;
  463. // }
  464. // });
  465. // if (dialog.exec()) {
  466. // mdiChild.setCloseEventAccepted();
  467. // AutoSave.cleanUp(bakFileName);
  468. // }
  469. // else {
  470. // mdiChild.setCloseEventRejected();
  471. // }
  472. // destr(dialog);
  473. mdiChild.setCloseEventAccepted();
  474. AutoSave.cleanUp(bakFileName);
  475. EAction.activateMainWindow();
  476. };
  477. /**
  478. * Setup default action for the given document interface.
  479. */
  480. NewFile.setupDefaultAction = function(documentInterface) {
  481. var defaultAction = NewFile.getDefaultAction();
  482. if (!isNull(defaultAction)) {
  483. documentInterface.setDefaultAction(defaultAction);
  484. }
  485. };
  486. /**
  487. * Returns name of default action class.
  488. */
  489. NewFile.getDefaultActionClass = function() {
  490. var defaultActionClass = "DefaultAction";
  491. var defaultActionFile = RSettings.getStringValue("NewFile/DefaultAction", "");
  492. if (defaultActionFile.length===0) {
  493. return defaultActionClass;
  494. }
  495. var defaultActionFileInfo = new QFileInfo(defaultActionFile);
  496. if (!defaultActionFileInfo.exists()) {
  497. defaultActionFileInfo = new QFileInfo(":/" + defaultActionFile);
  498. }
  499. if (defaultActionFileInfo.exists()) {
  500. include(defaultActionFile);
  501. defaultActionClass = new QFileInfo(defaultActionFile).baseName();
  502. }
  503. else {
  504. qWarning("custom default action class not found:", defaultActionFile);
  505. }
  506. return defaultActionClass;
  507. };
  508. /**
  509. * Returns new default action as configured in settings ("NewFile/DefaultAction")
  510. * or instance of DefaultAction.
  511. */
  512. NewFile.getDefaultAction = function(useGuiAction) {
  513. if (isNull(useGuiAction)) {
  514. useGuiAction = true;
  515. }
  516. // set up default action:
  517. var defaultGuiAction = undefined;
  518. if (useGuiAction) {
  519. defaultGuiAction = RGuiAction.getByScriptFile("scripts/Reset/Reset.js");
  520. }
  521. var defaultActionClass = NewFile.getDefaultActionClass();
  522. var defaultAction = undefined;
  523. if (typeof(global[defaultActionClass])!=="undefined") {
  524. defaultAction = new global[defaultActionClass](defaultGuiAction);
  525. if (isFunction(global[defaultActionClass].init)) {
  526. global[defaultActionClass].init();
  527. }
  528. }
  529. return defaultAction;
  530. };