HolonView.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. package ui.view.outliner;
  2. import java.awt.BorderLayout;
  3. import java.awt.Component;
  4. import java.awt.Dimension;
  5. import java.awt.datatransfer.DataFlavor;
  6. import java.awt.datatransfer.Transferable;
  7. import java.awt.datatransfer.UnsupportedFlavorException;
  8. import java.awt.event.MouseAdapter;
  9. import java.awt.event.MouseEvent;
  10. import java.io.IOException;
  11. import javax.swing.AbstractCellEditor;
  12. import javax.swing.DropMode;
  13. import javax.swing.Icon;
  14. import javax.swing.ImageIcon;
  15. import javax.swing.JCheckBoxMenuItem;
  16. import javax.swing.JComponent;
  17. import javax.swing.JLabel;
  18. import javax.swing.JMenuItem;
  19. import javax.swing.JPanel;
  20. import javax.swing.JPopupMenu;
  21. import javax.swing.JTextField;
  22. import javax.swing.JTree;
  23. import javax.swing.SwingUtilities;
  24. import javax.swing.ToolTipManager;
  25. import javax.swing.TransferHandler;
  26. import javax.swing.tree.DefaultMutableTreeNode;
  27. import javax.swing.tree.DefaultTreeCellRenderer;
  28. import javax.swing.tree.TreeCellEditor;
  29. import javax.swing.tree.TreePath;
  30. import javax.swing.tree.TreeSelectionModel;
  31. import classes.Holon;
  32. import classes.HolonElement;
  33. import classes.HolonObject;
  34. import ui.controller.Control;
  35. import util.ImageImport;
  36. /**
  37. * An Outliner Tab.
  38. * A high level view on the Model with respect to the Holon Structure.
  39. * @author Tom
  40. *
  41. */
  42. public class HolonView extends JPanel{
  43. private Control control;
  44. private JTree holonTree;
  45. private DefaultMutableTreeNode root;
  46. private boolean showHolonElementsInTree = true;
  47. public HolonView(Outliner parent, Control control) {
  48. this.control = control;
  49. this.setLayout(new BorderLayout());
  50. initilizePanel();
  51. parent.outlinerUpdate.addListener(() -> updateTreeStructure());
  52. }
  53. public void initilizePanel() {
  54. holonTree = generateTree();
  55. holonTree.setCellRenderer(new HolonJtreeRenderer());
  56. holonTree.setDragEnabled(true);
  57. holonTree.setDropMode(DropMode.ON_OR_INSERT);
  58. holonTree.setTransferHandler(new HolonTransferHandler());
  59. holonTree.getSelectionModel().setSelectionMode(
  60. TreeSelectionModel.SINGLE_TREE_SELECTION);
  61. ToolTipManager.sharedInstance().registerComponent(holonTree);
  62. TreeUtils.expand(holonTree);
  63. holonTree.setRootVisible(true);
  64. holonTree.addMouseListener(new HolonMouseListener());
  65. holonTree.setEditable(true);
  66. holonTree.setCellEditor(new HolonNodeEditor());
  67. this.add(TreeUtils.makePanelFromTree(holonTree));
  68. }
  69. private JTree generateTree() {
  70. root = generateFromHolon(control.getModel().getStateHolon());
  71. return new JTree(root);
  72. }
  73. private DefaultMutableTreeNode generateFromHolon(Holon holon) {
  74. DefaultMutableTreeNode node = new DefaultMutableTreeNode(new HolonInfo(holon));
  75. for(Holon child : holon.getChildView()) {
  76. node.add(generateFromHolon(child));
  77. }
  78. if(this.showHolonElementsInTree) {
  79. for(HolonElement ele : holon.getElementView()) {
  80. node.add(new DefaultMutableTreeNode(new ElementInfo(ele)));
  81. }
  82. }
  83. return node;
  84. }
  85. private void updateTreeStructure() {
  86. holonTree.clearSelection();
  87. root.removeAllChildren();
  88. Holon holon = control.getModel().getStateHolon();
  89. for(Holon child : holon.getChildView()) {
  90. root.add(generateFromHolon(child));
  91. }
  92. if(this.showHolonElementsInTree) {
  93. for(HolonElement ele : holon.getElementView()) {
  94. root.add(new DefaultMutableTreeNode(new ElementInfo(ele)));
  95. }
  96. }
  97. holonTree.updateUI();
  98. TreeUtils.expand(holonTree);
  99. }
  100. interface HolonNode{
  101. public Transferable getTransferable(HolonTransferHandler handler);
  102. public Icon getIcon();
  103. public String getToolTip();
  104. public void SetName(String name);
  105. }
  106. static final Icon elementIcon = new ImageIcon(ImageImport.loadImage("/Button_Images/element.png",16,16));
  107. static final String elementToolTip = "Element";
  108. class ElementInfo implements HolonNode{
  109. private HolonElement element;
  110. ElementInfo(HolonElement element){
  111. this.element = element;
  112. }
  113. @Override
  114. public String toString(){
  115. return element.getName();
  116. }
  117. public HolonElement getElement()
  118. {
  119. return element;
  120. }
  121. @Override
  122. public Transferable getTransferable(HolonTransferHandler handler) {
  123. return handler.new ElementTransferable(element);
  124. }
  125. @Override
  126. public Icon getIcon() {
  127. return elementIcon;
  128. }
  129. @Override
  130. public String getToolTip() {
  131. return elementToolTip;
  132. }
  133. @Override
  134. public void SetName(String name) {
  135. element.setEleName(name);
  136. }
  137. }
  138. static final Icon holonIcon = new ImageIcon(ImageImport.loadImage("/Button_Images/holon_logic.png",16,16));
  139. static final Icon holonPhysicalIcon = new ImageIcon(ImageImport.loadImage("/Button_Images/holon_physical.png",16,16));
  140. static final String holonToolTip = "Holon";
  141. class HolonInfo implements HolonNode{
  142. private Holon holon;
  143. HolonInfo(Holon holon){
  144. this.holon = holon;
  145. }
  146. @Override
  147. public String toString(){
  148. return holon.name;
  149. }
  150. public Holon getElement()
  151. {
  152. return holon;
  153. }
  154. public void AddElement(HolonElement element) {
  155. if(element.holon != null) {
  156. element.holon.removeElement(element);
  157. }
  158. holon.addElement(element);
  159. }
  160. public void AddHolon(Holon holon) {
  161. holon.split(this.holon);
  162. }
  163. @Override
  164. public Transferable getTransferable(HolonTransferHandler handler) {
  165. return handler.new HolonTransferable(holon);
  166. }
  167. @Override
  168. public Icon getIcon() {
  169. return holon.isPhysical? holonPhysicalIcon: holonIcon;
  170. }
  171. @Override
  172. public String getToolTip() {
  173. return holonToolTip;
  174. }
  175. @Override
  176. public void SetName(String name) {
  177. holon.name = name;
  178. }
  179. }
  180. //Context Menu
  181. class HolonMouseListener extends MouseAdapter{
  182. JPopupMenu menu = new JPopupMenu ();
  183. JCheckBoxMenuItem showElements = new JCheckBoxMenuItem("Show Elements");
  184. JMenuItem newHolonAsParentItem = new JMenuItem("Create new Holon as Parent");
  185. JMenuItem newHolonAsChildItem = new JMenuItem("Create new Holon as Child");
  186. JMenuItem deleteThisHolon = new JMenuItem("Delete this Holon");
  187. public HolonMouseListener() {
  188. //Initialize MenuItems
  189. showElements.setState(showHolonElementsInTree);
  190. showElements.addActionListener(clicked -> {
  191. showHolonElementsInTree = showElements.getState();
  192. updateTreeStructure();
  193. });
  194. newHolonAsParentItem.addActionListener(clicked -> {
  195. TreePath path = holonTree.getSelectionPath();
  196. HolonInfo info = getInfoFromPath(path);
  197. if(info == null) return;
  198. Holon parent = info.holon.getParent();
  199. parent.removeChildHolon(info.holon);
  200. Holon newHolon = new Holon("New Holon", control.getModel());
  201. control.getModel().getHolonsByID().put(newHolon.getUniqueID(), newHolon);
  202. newHolon.merge(info.holon);
  203. parent.merge(newHolon);
  204. updateTreeStructure();
  205. });
  206. newHolonAsChildItem.addActionListener(clicked -> {
  207. TreePath path = holonTree.getSelectionPath();
  208. HolonInfo info = getInfoFromPath(path);
  209. if(info == null) return;
  210. Holon newHolon = new Holon("New Holon", control.getModel());
  211. info.holon.merge(newHolon);
  212. control.getModel().getHolonsByID().put(newHolon.getUniqueID(), newHolon);
  213. updateTreeStructure();
  214. });
  215. deleteThisHolon.addActionListener(clicked -> {
  216. TreePath path = holonTree.getSelectionPath();
  217. HolonInfo info = getInfoFromPath(path);
  218. if(info == null) return;
  219. HolonObject obj = info.holon.getHolonObject();
  220. control.getModel().getHolonsByID().replace(info.holon.getUniqueID(), info.holon);
  221. if(obj != null) {
  222. info.holon.reassignAllChildren(info.holon.getParent());
  223. }else {
  224. info.holon.reassignAllChildren(info.holon.getParent());
  225. info.holon.removeFromParent();
  226. info.holon.removeAllRefrences();
  227. }
  228. updateTreeStructure();
  229. });
  230. //CreateMenu
  231. menu.add(showElements);
  232. menu.addSeparator();
  233. menu.add(newHolonAsParentItem);
  234. menu.add(newHolonAsChildItem);
  235. menu.add(deleteThisHolon);
  236. }
  237. public void mousePressed ( MouseEvent e )
  238. {
  239. if (!SwingUtilities.isRightMouseButton(e))
  240. {
  241. return;
  242. }
  243. TreePath path = holonTree.getSelectionPath();
  244. boolean pathIsViable = (path != null);
  245. newHolonAsParentItem.setVisible(pathIsViable);
  246. newHolonAsChildItem.setVisible(pathIsViable);
  247. deleteThisHolon.setVisible(pathIsViable);
  248. menu.show(holonTree, e.getX(), e.getY());
  249. }
  250. HolonInfo getInfoFromPath(TreePath path) {
  251. if(path == null) {
  252. return null;
  253. }
  254. DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
  255. if(!(node.getUserObject() instanceof HolonInfo)) {
  256. return null;
  257. }
  258. return (HolonInfo)node.getUserObject();
  259. }
  260. }
  261. //Drag and Drop
  262. //Make drag and drop von Holons possible.
  263. //Implement HolonElement drag and drop too, but its disabled.
  264. class HolonTransferHandler extends TransferHandler{
  265. DataFlavor holonFlavor;
  266. DataFlavor[] holonFlavorArray = new DataFlavor[1];
  267. DataFlavor elementFlavor;
  268. DataFlavor[] elementFlavorArray = new DataFlavor[1];
  269. boolean allowHolonElementDragAndDrop = false;
  270. HolonTransferHandler(){
  271. try {
  272. String mimeType = DataFlavor.javaJVMLocalObjectMimeType +
  273. ";class=\"" +
  274. classes.Holon.class.getName() +
  275. "\"";
  276. holonFlavor = new DataFlavor(mimeType);
  277. holonFlavorArray[0] = holonFlavor;
  278. mimeType = DataFlavor.javaJVMLocalObjectMimeType +
  279. ";class=\"" +
  280. classes.HolonElement.class.getName() +
  281. "\"";
  282. elementFlavor = new DataFlavor(mimeType);
  283. elementFlavorArray[0] = elementFlavor;
  284. } catch (ClassNotFoundException e) {
  285. e.printStackTrace();
  286. }
  287. }
  288. @Override
  289. public boolean canImport(TransferHandler.TransferSupport support) {
  290. if(!support.isDrop()) {
  291. return false;
  292. }
  293. support.setShowDropLocation(true);
  294. //Only accept Element and Holon as Drop
  295. if(!support.isDataFlavorSupported(holonFlavor) && !support.isDataFlavorSupported(elementFlavor)) {
  296. return false;
  297. }
  298. JTree.DropLocation location = (JTree.DropLocation)support.getDropLocation();
  299. TreePath pathDrop = location.getPath();
  300. DefaultMutableTreeNode node = (DefaultMutableTreeNode) pathDrop.getLastPathComponent();
  301. //Element cannot be parent of a holon
  302. if(!(node.getUserObject() instanceof HolonInfo)) {
  303. return false;
  304. }
  305. JTree tree = (JTree) support.getComponent();
  306. TreePath pathDrag = tree.getSelectionPath();
  307. //Dont allow Cycles
  308. if(pathDrag.isDescendant(pathDrop)) {
  309. return false;
  310. }
  311. //Check physical connected
  312. try {
  313. Holon dropHolon = ((HolonInfo) node.getUserObject()).holon;
  314. Holon dragHolon = (Holon) support.getTransferable().getTransferData(holonFlavor);
  315. boolean physicalConnected = control.getModel().checkHolonObjectsAreConnected(dragHolon, dropHolon);
  316. boolean isAllStateHolon = dropHolon.getLayer() == 0;
  317. return physicalConnected || isAllStateHolon;
  318. } catch (UnsupportedFlavorException | IOException e) {
  319. e.printStackTrace();
  320. return false;
  321. }
  322. }
  323. @Override
  324. public boolean importData(TransferHandler.TransferSupport support) {
  325. if(!canImport(support)) {
  326. return false;
  327. }
  328. JTree.DropLocation location = (JTree.DropLocation)support.getDropLocation();
  329. TreePath path = location.getPath();
  330. DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
  331. if(node.getUserObject() instanceof HolonInfo) {
  332. HolonInfo info = (HolonInfo) node.getUserObject();
  333. try {
  334. if(support.isDataFlavorSupported(holonFlavor)) {
  335. info.AddHolon((Holon) support.getTransferable().getTransferData(holonFlavor));
  336. }else if (support.isDataFlavorSupported(elementFlavor)){
  337. info.AddElement((HolonElement) support.getTransferable().getTransferData(elementFlavor));
  338. }
  339. } catch (UnsupportedFlavorException | IOException e) {
  340. e.printStackTrace();
  341. }
  342. }
  343. return false;
  344. }
  345. protected void exportDone(JComponent source,
  346. Transferable data,
  347. int action) {
  348. updateTreeStructure();
  349. }
  350. @Override
  351. public int getSourceActions(JComponent c) {
  352. return MOVE;
  353. }
  354. @Override
  355. protected Transferable createTransferable(JComponent c) {
  356. JTree tree = (JTree)c;
  357. TreePath path = tree.getSelectionPath();
  358. if(path != null) {
  359. DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
  360. Object userObject = node.getUserObject();
  361. //Only allow Hollon to move
  362. if(this.allowHolonElementDragAndDrop) {
  363. //Both
  364. if(userObject instanceof HolonNode) {
  365. return ((HolonNode) userObject).getTransferable(this);
  366. }
  367. }
  368. else {
  369. //Only Holons
  370. if(userObject instanceof HolonInfo) {
  371. return ((HolonInfo) userObject).getTransferable(this);
  372. }
  373. }
  374. }
  375. return null;
  376. }
  377. public class HolonTransferable implements Transferable{
  378. Holon holon;
  379. public HolonTransferable(Holon holon) {
  380. this.holon = holon;
  381. }
  382. @Override
  383. public DataFlavor[] getTransferDataFlavors() {
  384. return holonFlavorArray;
  385. }
  386. @Override
  387. public boolean isDataFlavorSupported(DataFlavor flavor) {
  388. return holonFlavor.equals(flavor);
  389. }
  390. @Override
  391. public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
  392. if(!isDataFlavorSupported(flavor))
  393. throw new UnsupportedFlavorException(flavor);
  394. return holon;
  395. }
  396. }
  397. public class ElementTransferable implements Transferable{
  398. HolonElement element;
  399. public ElementTransferable(HolonElement element) {
  400. this.element = element;
  401. }
  402. @Override
  403. public DataFlavor[] getTransferDataFlavors() {
  404. return elementFlavorArray;
  405. }
  406. @Override
  407. public boolean isDataFlavorSupported(DataFlavor flavor) {
  408. return elementFlavor.equals(flavor);
  409. }
  410. @Override
  411. public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
  412. if(!isDataFlavorSupported(flavor))
  413. throw new UnsupportedFlavorException(flavor);
  414. return element;
  415. }
  416. }
  417. }
  418. //Cell Display
  419. class HolonJtreeRenderer extends DefaultTreeCellRenderer {
  420. public Component getTreeCellRendererComponent(
  421. JTree tree,
  422. Object value,
  423. boolean isSelected,
  424. boolean expanded,
  425. boolean leaf,
  426. int row,
  427. boolean hasFocus) {
  428. super.getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, hasFocus);
  429. DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
  430. HolonNode Node = (HolonNode) node.getUserObject();
  431. if(row != 0) {
  432. this.setIcon(Node.getIcon());
  433. this.setToolTipText(Node.getToolTip());
  434. }
  435. return this;
  436. }
  437. }
  438. //Edit Names of Nodes
  439. class HolonNodeEditor extends AbstractCellEditor implements TreeCellEditor{
  440. private HolonNode holonNode;
  441. private JTextField tf= new JTextField(){
  442. @Override
  443. public Dimension getPreferredSize() {
  444. Dimension dim = super.getPreferredSize();
  445. int length = getText().length();
  446. dim.width = Math.max(300, dim.width + length * 10) ;
  447. return dim;
  448. }
  449. };
  450. public HolonNodeEditor() {
  451. super();
  452. }
  453. @Override
  454. public Object getCellEditorValue() {
  455. holonNode.SetName(tf.getText());
  456. return tf.getText();
  457. }
  458. @Override
  459. public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded,
  460. boolean leaf, int row) {
  461. JLabel newString = (JLabel) holonTree.getCellRenderer().getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, leaf);
  462. DefaultMutableTreeNode default_node = (DefaultMutableTreeNode) value;
  463. holonNode = (HolonNode)default_node.getUserObject();
  464. tf.setText(newString.getText());
  465. return tf;
  466. }
  467. }
  468. }