HolonView.java 16 KB

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