BlockList.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  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/Widgets/Widgets.js");
  20. include("scripts/WidgetFactory.js");
  21. /**
  22. * Block list widget class (QWidget).
  23. */
  24. function RBlockListQt(parent, addListener, showHeader) {
  25. RTreeWidget.call(this, parent);
  26. if (isNull(addListener)) {
  27. addListener = true;
  28. }
  29. if (isNull(showHeader)) {
  30. showHeader = false;
  31. }
  32. this.di = undefined;
  33. if (!showHeader) {
  34. this.header().close();
  35. }
  36. this.iconSize = new QSize(16, 16);
  37. this.indentation = 0;
  38. this.rootIsDecorated = false;
  39. this.setSelectableColumn(2);
  40. this.columnCount = 3;
  41. this.header().stretchLastSection = false;
  42. if (RSettings.isQt(5)) {
  43. this.header().minimumSectionSize = 22;
  44. this.header().setSectionResizeMode(BlockList.colName, QHeaderView.Stretch);
  45. this.header().setSectionResizeMode(BlockList.colVisible, QHeaderView.Interactive);
  46. this.header().setSectionResizeMode(BlockList.colEdit, QHeaderView.Interactive);
  47. }
  48. else {
  49. this.header().setResizeMode(BlockList.colName, QHeaderView.Stretch);
  50. this.header().setResizeMode(BlockList.colVisible, QHeaderView.Interactive);
  51. this.header().setResizeMode(BlockList.colEdit, QHeaderView.Interactive);
  52. }
  53. this.setColumnWidth(BlockList.colVisible, 22);
  54. this.setColumnWidth(BlockList.colEdit, 22);
  55. var self = this;
  56. if (addListener) {
  57. var appWin = EAction.getMainWindow();
  58. var adapter = new RBlockListenerAdapter();
  59. appWin.addBlockListener(adapter);
  60. adapter.blocksUpdated.connect(function(di) { self.updateBlocks(di); });
  61. adapter.currentBlockSet.connect(function(di) { self.updateCurrentBlock(di); });
  62. adapter.blocksCleared.connect(function() { self.clearBlocks(); });
  63. }
  64. this.itemDoubleClicked.connect(function(item, col) { self.editBlock(item, col); });
  65. this.itemColumnClicked.connect(function(item, col) { self.itemColumnClickedSlot(item, col); });
  66. this.itemSelectionChanged.connect(function() { self.blockActivated(); });
  67. this.basePath = includeBasePath;
  68. this.currentBlockItem = undefined;
  69. }
  70. RBlockListQt.prototype = new RTreeWidget();
  71. RBlockListQt.getWidget = function() {
  72. var appWin = EAction.getMainWindow();
  73. return appWin.findChild("BlockList");
  74. };
  75. /**
  76. * Add an additional action to be used in the context menu.
  77. * Can be used by plugins to hook into the block list context menu.
  78. */
  79. RBlockListQt.addContextMenuScriptFile = function(scriptFile, blockList) {
  80. //var blockList = RBlockListQt.getWidget();
  81. if (isNull(blockList)) {
  82. return;
  83. }
  84. var scriptFiles = RBlockListQt.getContextMenuScriptFiles(blockList);
  85. scriptFiles.push(scriptFile);
  86. blockList.setProperty("ContextMenuScriptFiles", scriptFiles);
  87. };
  88. RBlockListQt.getContextMenuScriptFiles = function(blockList) {
  89. //var blockList = RBlockListQt.getWidget();
  90. if (isNull(blockList)) {
  91. return [];
  92. }
  93. var scriptFiles = blockList.property("ContextMenuScriptFiles");
  94. if (isNull(scriptFiles)) {
  95. scriptFiles = [];
  96. }
  97. return scriptFiles;
  98. };
  99. RBlockListQt.prototype.contextMenuEvent = function(e) {
  100. var item = this.itemAt(e.pos());
  101. if (!isNull(item)) {
  102. this.setCurrentItem(item);
  103. }
  104. var menu = new QMenu();
  105. RGuiAction.getByScriptFile("scripts/Block/ToggleBlockVisibility/ToggleBlockVisibility.js").addToMenu(menu);
  106. RGuiAction.getByScriptFile("scripts/Block/ShowAllBlocks/ShowAllBlocks.js").addToMenu(menu);
  107. RGuiAction.getByScriptFile("scripts/Block/HideAllBlocks/HideAllBlocks.js").addToMenu(menu);
  108. RGuiAction.getByScriptFile("scripts/Block/AddBlock/AddBlock.js").addToMenu(menu);
  109. RGuiAction.getByScriptFile("scripts/Block/RemoveBlock/RemoveBlock.js").addToMenu(menu);
  110. RGuiAction.getByScriptFile("scripts/Block/RenameBlock/RenameBlock.js").addToMenu(menu);
  111. RGuiAction.getByScriptFile("scripts/Block/EditBlock/EditBlock.js").addToMenu(menu);
  112. RGuiAction.getByScriptFile("scripts/Block/InsertBlock/InsertBlock.js").addToMenu(menu);
  113. RGuiAction.getByScriptFile("scripts/Block/SelectBlockReferences/SelectBlockReferences.js").addToMenu(menu);
  114. RGuiAction.getByScriptFile("scripts/Block/DeselectBlockReferences/DeselectBlockReferences.js").addToMenu(menu);
  115. RBlockListQt.complementContextMenu(menu, RBlockListQt.getWidget());
  116. menu.exec(QCursor.pos());
  117. e.ignore();
  118. };
  119. RBlockListQt.complementContextMenu = function(menu, blockListWidget) {
  120. // add additional context menu actions provided by plugins:
  121. var scriptFiles = RBlockListQt.getContextMenuScriptFiles(blockListWidget);
  122. for (var i=0; i<scriptFiles.length; i++) {
  123. var scriptFile = scriptFiles[i];
  124. var a = RGuiAction.getByScriptFile(scriptFile);
  125. if (!isNull(a)) {
  126. a.addToMenu(menu);
  127. }
  128. }
  129. };
  130. RBlockListQt.prototype.filter = function(block) {
  131. var hideInternal = RSettings.getBoolValue("BlockList/HideInternalBlocks", false);
  132. if (hideInternal===true) {
  133. var blockNameLower = block.getName().toLowerCase();
  134. // hide anonymous blocks:
  135. if (blockNameLower.startsWith("a$c") &&
  136. blockNameLower.length===13) {
  137. return false;
  138. }
  139. // hide blocks tagged as resources
  140. // such blocks may be added by add-ons to provide
  141. // additional resources such as CAM tools, etc.
  142. if (block.hasCustomProperty("QCAD", "ResourceFlag")) {
  143. return false;
  144. }
  145. }
  146. return true;
  147. };
  148. RBlockListQt.prototype.updateCurrentBlock = function(documentInterface) {
  149. var doc = documentInterface.getDocument();
  150. // find item which was representing the current block before:
  151. if (!isNull(this.currentBlockItem)) {
  152. // clear pen icon from item:
  153. //var blockName = this.currentBlockItem.data(BlockList.colName, Qt.UserRole);
  154. //var block = doc.queryBlock(blockName);
  155. //this.updateItemIcons(this.currentBlockItem, block);
  156. this.currentBlockItem.setIcon(BlockList.colEdit, BlockList.iconEdit[0]);
  157. }
  158. // find item of current block:
  159. this.currentBlockItem = RBlockListQt.getItem(this, doc.getCurrentBlockName());
  160. //for (var i=0; i<this.topLevelItemCount; i++) {
  161. // var item = this.topLevelItem(i);
  162. //}
  163. // add pen icon to item:
  164. if (!isNull(this.currentBlockItem)) {
  165. this.currentBlockItem.setIcon(BlockList.colEdit, BlockList.iconEdit[1]);
  166. }
  167. };
  168. /**
  169. * Called when blocks are added, edited or deleted. Updates the block list.
  170. */
  171. RBlockListQt.prototype.updateBlocks = function(documentInterface) {
  172. this.currentBlockItem = undefined;
  173. this.di = documentInterface;
  174. var pos = this.verticalScrollBar().sliderPosition;
  175. var selectedBlockName = undefined;
  176. var selectedItems = this.selectedItems();
  177. if (selectedItems.length===1) {
  178. selectedBlockName = selectedItems[0].data(BlockList.colName, Qt.UserRole);
  179. }
  180. this.clear();
  181. if (isNull(documentInterface)) {
  182. return;
  183. }
  184. var doc = documentInterface.getDocument();
  185. var blockCandidateIds = doc.queryAllBlocks();
  186. var i;
  187. // filter block names:
  188. var blockIds = [];
  189. for (i=0; i<blockCandidateIds.length; i++) {
  190. var blockCandidateId = blockCandidateIds[i];
  191. var blockCandidate = doc.queryBlockDirect(blockCandidateId);
  192. if (isFunction(blockCandidate.isNull) && blockCandidate.isNull()) {
  193. continue;
  194. }
  195. if (!this.filter(blockCandidate)) {
  196. continue;
  197. }
  198. blockIds.push(blockCandidateId);
  199. }
  200. blockIds = this.sortBlocks(doc, blockIds);
  201. var currentBlockId = doc.getCurrentBlockId();
  202. var selectedItem = undefined;
  203. for (i=0; i<blockIds.length; ++i) {
  204. var blockId = blockIds[i];
  205. var block = doc.queryBlockDirect(blockId);
  206. if (isFunction(block.isNull) && block.isNull()) {
  207. continue;
  208. }
  209. var blockName = block.getName();
  210. var item = this.createBlockItem(block);
  211. this.addTopLevelItem(item);
  212. if (blockName===selectedBlockName) {
  213. selectedItem = item;
  214. }
  215. if (currentBlockId===block.getId()) {
  216. this.currentBlockItem = item;
  217. }
  218. }
  219. this.verticalScrollBar().sliderPosition = pos;
  220. if (!isNull(selectedItem)) {
  221. this.blockSignals(true);
  222. selectedItem.setSelected(true);
  223. this.blockSignals(false);
  224. }
  225. this.blockActivated();
  226. };
  227. RBlockListQt.prototype.sortBlocks = function(doc, blockIds) {
  228. return doc.sortBlocks(blockIds);
  229. };
  230. RBlockListQt.getBlockTitle = function(block) {
  231. var title = block.getName();
  232. var layoutName = block.getLayoutName();
  233. if (layoutName.length>0) {
  234. title = layoutName + " (" + title + ")";
  235. }
  236. return title;
  237. };
  238. RBlockListQt.prototype.createBlockItem = function(block) {
  239. var item = new QTreeWidgetItem();
  240. var name = block.getName();
  241. var flags = makeQtItemFlags(Qt.ItemIsSelectable, Qt.ItemIsEnabled);
  242. item.setFlags(flags);
  243. var title = RBlockListQt.getBlockTitle(block);
  244. item.setText(BlockList.colName, title);
  245. item.setData(BlockList.colName, Qt.UserRole, name);
  246. this.updateItemIcons(item, block);
  247. return item;
  248. };
  249. RBlockListQt.prototype.updateItemIcons = function(item, block) {
  250. //var iconName = autoIconPath(BlockList.includeBasePath + "/Visible%1.svg".arg(Number(!block.isFrozen())));
  251. item.setIcon(BlockList.colVisible, BlockList.iconVisible[Number(!block.isFrozen())]);
  252. var doc = this.di.getDocument();
  253. var currentBlockId = doc.getCurrentBlockId();
  254. //iconName = autoIconPath(BlockList.includeBasePath + "/Edit%1.svg".arg(Number(block.getId()===currentBlockId)));
  255. item.setIcon(BlockList.colEdit, BlockList.iconEdit[Number(block.getId()===currentBlockId)]);
  256. };
  257. /**
  258. * Called when no document is active. Clears the block list.
  259. */
  260. RBlockListQt.prototype.clearBlocks = function() {
  261. this.clear();
  262. };
  263. /**
  264. * Called when the user clicks the icon beside the block name to hide
  265. * all block inserts of that block.
  266. */
  267. RBlockListQt.prototype.itemColumnClickedSlot = function(item, column) {
  268. if (isNull(this.di) || isNull(item)) {
  269. return;
  270. }
  271. var doc = this.di.getDocument();
  272. var block = doc.queryBlock(this.getBlockName(item));
  273. if (isFunction(block.isNull) && block.isNull()) {
  274. return;
  275. }
  276. if (column===BlockList.colVisible) {
  277. block.setFrozen(!block.isFrozen());
  278. var op = new RModifyObjectOperation(block, false);
  279. this.di.applyOperation(op);
  280. //this.updateBlocks(this.di);
  281. } else if (column===BlockList.colEdit) {
  282. this.setCurrentItem(item);
  283. this.editBlock();
  284. //this.updateBlocks(this.di);
  285. }
  286. this.blockActivated();
  287. };
  288. /**
  289. * Called when a block name is activated (clicked, highlighted) in the block list.
  290. */
  291. RBlockListQt.prototype.blockActivated = function() {
  292. if (isNull(this.di)) {
  293. this.enableActions(false);
  294. return;
  295. }
  296. var list = this.selectedItems();
  297. if (list.length===0) {
  298. this.enableActions(false);
  299. return;
  300. }
  301. var item = list[0];
  302. if (isNull(item)) {
  303. this.enableActions(false);
  304. return;
  305. }
  306. var document = this.di.getDocument();
  307. if (isNull(document)) {
  308. this.enableActions(false);
  309. return;
  310. }
  311. var currentBlock = document.queryCurrentBlock();
  312. if (isNull(currentBlock)) {
  313. this.enableActions(false);
  314. return;
  315. }
  316. var blockName = this.getBlockName(item);
  317. // prevent removal or insertion of current block:
  318. var insertable = currentBlock.getName().toLowerCase()!==blockName.toLowerCase() && !blockName.startsWith("*");
  319. var renamable = document.getBlockId(blockName)!==document.getModelSpaceBlockId();
  320. var removable = document.getBlockId(blockName)!==document.getModelSpaceBlockId();
  321. this.enableActions(insertable, renamable, removable);
  322. };
  323. /**
  324. * \return The block name for the given tree widget item.
  325. */
  326. RBlockListQt.prototype.getBlockName = function(item) {
  327. return item.data(BlockList.colName, Qt.UserRole);
  328. };
  329. /**
  330. * \return The list item for the block with the given block name.
  331. */
  332. RBlockListQt.getItem = function(widget, blockName) {
  333. for (var i=0; i<widget.topLevelItemCount; i++) {
  334. var item = widget.topLevelItem(i);
  335. if (item.data(BlockList.colName, Qt.UserRole)===blockName) {
  336. return item;
  337. }
  338. }
  339. return undefined;
  340. };
  341. /**
  342. * Enable / disable buttons that edit, remove or insert the active block.
  343. */
  344. RBlockListQt.prototype.enableActions = function(insertable, renamable, removable) {
  345. if (isNull(renamable)) {
  346. renamable = insertable;
  347. }
  348. if (isNull(removable)) {
  349. removable = insertable;
  350. }
  351. var override;
  352. if (insertable===true) {
  353. override = 1;
  354. }
  355. else {
  356. override = 0;
  357. }
  358. var action;
  359. action = RGuiAction.getByScriptFile("scripts/Block/ToggleBlockVisibility/ToggleBlockVisibility.js");
  360. action.setEnabledOverride(insertable, override);
  361. action = RGuiAction.getByScriptFile("scripts/Block/InsertBlock/InsertBlock.js");
  362. action.setEnabledOverride(insertable, override);
  363. action = RGuiAction.getByScriptFile("scripts/Block/RemoveBlock/RemoveBlock.js");
  364. action.setEnabledOverride(removable, removable ? 1 : 0);
  365. action = RGuiAction.getByScriptFile("scripts/Block/RenameBlock/RenameBlock.js");
  366. action.setEnabledOverride(renamable, renamable ? 1 : 0);
  367. };
  368. /**
  369. * Called when the user wants to edit the currently selected block
  370. * (double click).
  371. */
  372. RBlockListQt.prototype.editBlock = function() {
  373. var action = RGuiAction.getByScriptFile("scripts/Block/EditBlock/EditBlock.js");
  374. if (!isNull(action)) {
  375. action.slotTrigger();
  376. }
  377. this.blockActivated();
  378. };
  379. /**
  380. * \class BlockList
  381. * \brief Initializes the block list widget (RBlockListQt).
  382. * \ingroup ecma_widgets
  383. */
  384. function BlockList(guiAction) {
  385. Widgets.call(this, guiAction);
  386. }
  387. BlockList.prototype = new Widgets();
  388. BlockList.includeBasePath = includeBasePath;
  389. BlockList.colVisible=0;
  390. BlockList.colEdit=1;
  391. BlockList.colName=2;
  392. BlockList.iconVisible = [
  393. new QIcon(autoIconPath(BlockList.includeBasePath + "/Visible0.svg")),
  394. new QIcon(autoIconPath(BlockList.includeBasePath + "/Visible1.svg"))
  395. ];
  396. BlockList.iconEdit = [
  397. new QIcon(autoIconPath(BlockList.includeBasePath + "/Edit0.svg")),
  398. new QIcon(autoIconPath(BlockList.includeBasePath + "/Edit1.svg"))
  399. ];
  400. BlockList.getPreferencesCategory = function() {
  401. return [ qsTr("Widgets"), qsTr("Block List") ];
  402. };
  403. BlockList.applyPreferences = function(doc, mdiChild) {
  404. var appWin = RMainWindowQt.getMainWindow();
  405. appWin.notifyBlockListeners(EAction.getDocumentInterface());
  406. var blockList = appWin.findChild("BlockList");
  407. WidgetFactory.initList(blockList, "BlockList");
  408. };
  409. /**
  410. * \return Active item.
  411. */
  412. BlockList.getActiveItem = function() {
  413. var appWin = RMainWindowQt.getMainWindow();
  414. var blockList = appWin.findChild("BlockList");
  415. if (isNull(blockList)) {
  416. return undefined;
  417. }
  418. return blockList.getActiveItem();
  419. };
  420. /**
  421. * Shows / hides the block list.
  422. */
  423. BlockList.prototype.beginEvent = function() {
  424. Widgets.prototype.beginEvent.call(this);
  425. var appWin = RMainWindowQt.getMainWindow();
  426. var dock = appWin.findChild("BlockListDock");
  427. if (!RSettings.getOriginalArguments().contains("-no-show")) {
  428. dock.visible = !dock.visible;
  429. if (dock.visible) dock.raise();
  430. }
  431. };
  432. BlockList.prototype.finishEvent = function() {
  433. Widgets.prototype.finishEvent.call(this);
  434. var appWin = RMainWindowQt.getMainWindow();
  435. var dock = appWin.findChild("BlockListDock");
  436. this.getGuiAction().setChecked(dock.visible);
  437. };
  438. /**
  439. * Initializes the block list widget.
  440. */
  441. BlockList.init = function(basePath) {
  442. var appWin = RMainWindowQt.getMainWindow();
  443. var action = new RGuiAction(qsTr("Block List"), appWin);
  444. action.setRequiresDocument(false);
  445. action.setScriptFile(basePath + "/BlockList.js");
  446. action.setIcon(basePath + "/BlockList.svg");
  447. action.setDefaultShortcut(new QKeySequence("g,b"));
  448. action.setDefaultCommands(["gb"]);
  449. action.setGroupSortOrder(3600);
  450. action.setSortOrder(200);
  451. action.setWidgetNames(["ViewMenu", "WidgetsToolBar", "ViewToolsPanel", "WidgetsMatrixPanel"]);
  452. var formWidget = WidgetFactory.createWidget(basePath, "BlockList.ui");
  453. var layout = formWidget.findChild("verticalLayout");
  454. var blockList = new RBlockListQt(formWidget);
  455. blockList.objectName = "BlockList";
  456. if (RSettings.getQtVersion()>0x060000) {
  457. layout.addWidget(blockList);
  458. }
  459. else {
  460. layout.addWidget(blockList, 1, 0);
  461. }
  462. RSettings.setValue("BlockList/AlternatingRowColor", new RColor(230, 235, 250), false);
  463. WidgetFactory.initList(blockList, "BlockList");
  464. var button;
  465. button = formWidget.findChild("ShowAllBlocks");
  466. button.setDefaultAction(RGuiAction.getByScriptFile("scripts/Block/ShowAllBlocks/ShowAllBlocks.js"));
  467. button = formWidget.findChild("HideAllBlocks");
  468. button.setDefaultAction(RGuiAction.getByScriptFile("scripts/Block/HideAllBlocks/HideAllBlocks.js"));
  469. button = formWidget.findChild("AddBlock");
  470. button.setDefaultAction(RGuiAction.getByScriptFile("scripts/Block/AddBlock/AddBlock.js"));
  471. button = formWidget.findChild("RemoveBlock");
  472. button.setDefaultAction(RGuiAction.getByScriptFile("scripts/Block/RemoveBlock/RemoveBlock.js"));
  473. button = formWidget.findChild("RenameBlock");
  474. button.setDefaultAction(RGuiAction.getByScriptFile("scripts/Block/RenameBlock/RenameBlock.js"));
  475. button = formWidget.findChild("EditBlock");
  476. button.setDefaultAction(RGuiAction.getByScriptFile("scripts/Block/EditBlock/EditBlock.js"));
  477. button = formWidget.findChild("InsertBlock");
  478. button.setDefaultAction(RGuiAction.getByScriptFile("scripts/Block/InsertBlock/InsertBlock.js"));
  479. // button = formWidget.findChild("CreateBlock");
  480. // button.setDefaultAction(RGuiAction.getByScriptFile("scripts/Block/CreateBlock/CreateBlock.js"));
  481. button = formWidget.findChild("CloseBlock");
  482. button.setDefaultAction(RGuiAction.getByScriptFile("scripts/Block/EditMainDrawing/EditMainDrawing.js"));
  483. var dock = new RDockWidget(qsTr("Block List"), appWin);
  484. dock.objectName = "BlockListDock";
  485. //dock.setProperty("Category", Widgets.getListContextMenuCategory());
  486. dock.setWidget(formWidget);
  487. appWin.addDockWidget(Qt.RightDockWidgetArea, dock);
  488. dock.shown.connect(function() { action.setChecked(true); });
  489. dock.hidden.connect(function() { action.setChecked(false); });
  490. dock.visible = false;
  491. };