HolonView.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  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, true);
  162. control.updateHolarchyWindow();
  163. }
  164. @Override
  165. public Transferable getTransferable(HolonTransferHandler handler) {
  166. return handler.new HolonTransferable(holon);
  167. }
  168. @Override
  169. public Icon getIcon() {
  170. return holon.isPhysical? holonPhysicalIcon: holonIcon;
  171. }
  172. @Override
  173. public String getToolTip() {
  174. return holonToolTip;
  175. }
  176. @Override
  177. public void SetName(String name) {
  178. holon.name = name;
  179. }
  180. }
  181. //Context Menu
  182. class HolonMouseListener extends MouseAdapter{
  183. JPopupMenu menu = new JPopupMenu ();
  184. JCheckBoxMenuItem showElements = new JCheckBoxMenuItem("Show Elements");
  185. JMenuItem newHolonAsParentItem = new JMenuItem("Create new Holon as Parent");
  186. JMenuItem newHolonAsChildItem = new JMenuItem("Create new Holon as Child");
  187. JMenuItem deleteThisHolon = new JMenuItem("Delete this Holon");
  188. public HolonMouseListener() {
  189. //Initialize MenuItems
  190. showElements.setState(showHolonElementsInTree);
  191. showElements.addActionListener(clicked -> {
  192. showHolonElementsInTree = showElements.getState();
  193. updateTreeStructure();
  194. });
  195. newHolonAsParentItem.addActionListener(clicked -> {
  196. TreePath path = holonTree.getSelectionPath();
  197. HolonInfo info = getInfoFromPath(path);
  198. if(info == null) return;
  199. Holon parent = info.holon.getParent();
  200. parent.removeChildHolon(info.holon);
  201. Holon newHolon = new Holon("New Holon", control.getModel());
  202. control.getModel().getHolonsByID().put(newHolon.getUniqueID(), newHolon);
  203. newHolon.merge(info.holon, true);
  204. parent.merge(newHolon, true);
  205. updateTreeStructure();
  206. });
  207. newHolonAsChildItem.addActionListener(clicked -> {
  208. TreePath path = holonTree.getSelectionPath();
  209. HolonInfo info = getInfoFromPath(path);
  210. if(info == null) return;
  211. Holon newHolon = new Holon("New Holon", control.getModel());
  212. info.holon.merge(newHolon, true);
  213. control.getModel().getHolonsByID().put(newHolon.getUniqueID(), newHolon);
  214. updateTreeStructure();
  215. });
  216. deleteThisHolon.addActionListener(clicked -> {
  217. TreePath path = holonTree.getSelectionPath();
  218. HolonInfo info = getInfoFromPath(path);
  219. if(info == null) return;
  220. HolonObject obj = info.holon.getHolonObject();
  221. control.getModel().getHolonsByID().replace(info.holon.getUniqueID(), info.holon);
  222. if(obj != null) {
  223. info.holon.reassignAllChildren(info.holon.getParent());
  224. }else {
  225. info.holon.reassignAllChildren(info.holon.getParent());
  226. info.holon.removeFromParent();
  227. info.holon.removeAllRefrences();
  228. }
  229. updateTreeStructure();
  230. });
  231. //CreateMenu
  232. menu.add(showElements);
  233. menu.addSeparator();
  234. menu.add(newHolonAsParentItem);
  235. menu.add(newHolonAsChildItem);
  236. menu.add(deleteThisHolon);
  237. }
  238. public void mousePressed ( MouseEvent e )
  239. {
  240. if (!SwingUtilities.isRightMouseButton(e))
  241. {
  242. return;
  243. }
  244. TreePath path = holonTree.getSelectionPath();
  245. boolean pathIsViable = (path != null);
  246. newHolonAsParentItem.setVisible(pathIsViable);
  247. newHolonAsChildItem.setVisible(pathIsViable);
  248. deleteThisHolon.setVisible(pathIsViable);
  249. menu.show(holonTree, e.getX(), e.getY());
  250. }
  251. HolonInfo getInfoFromPath(TreePath path) {
  252. if(path == null) {
  253. return null;
  254. }
  255. DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
  256. if(!(node.getUserObject() instanceof HolonInfo)) {
  257. return null;
  258. }
  259. return (HolonInfo)node.getUserObject();
  260. }
  261. }
  262. //Drag and Drop
  263. //Make drag and drop von Holons possible.
  264. //Implement HolonElement drag and drop too, but its disabled.
  265. class HolonTransferHandler extends TransferHandler{
  266. DataFlavor holonFlavor;
  267. DataFlavor[] holonFlavorArray = new DataFlavor[1];
  268. DataFlavor elementFlavor;
  269. DataFlavor[] elementFlavorArray = new DataFlavor[1];
  270. boolean allowHolonElementDragAndDrop = false;
  271. HolonTransferHandler(){
  272. try {
  273. String mimeType = DataFlavor.javaJVMLocalObjectMimeType +
  274. ";class=\"" +
  275. classes.Holon.class.getName() +
  276. "\"";
  277. holonFlavor = new DataFlavor(mimeType);
  278. holonFlavorArray[0] = holonFlavor;
  279. mimeType = DataFlavor.javaJVMLocalObjectMimeType +
  280. ";class=\"" +
  281. classes.HolonElement.class.getName() +
  282. "\"";
  283. elementFlavor = new DataFlavor(mimeType);
  284. elementFlavorArray[0] = elementFlavor;
  285. } catch (ClassNotFoundException e) {
  286. e.printStackTrace();
  287. }
  288. }
  289. @Override
  290. public boolean canImport(TransferHandler.TransferSupport support) {
  291. if(!support.isDrop()) {
  292. return false;
  293. }
  294. support.setShowDropLocation(true);
  295. //Only accept Element and Holon as Drop
  296. if(!support.isDataFlavorSupported(holonFlavor) && !support.isDataFlavorSupported(elementFlavor)) {
  297. return false;
  298. }
  299. JTree.DropLocation location = (JTree.DropLocation)support.getDropLocation();
  300. TreePath pathDrop = location.getPath();
  301. DefaultMutableTreeNode node = (DefaultMutableTreeNode) pathDrop.getLastPathComponent();
  302. //Element cannot be parent of a holon
  303. if(!(node.getUserObject() instanceof HolonInfo)) {
  304. return false;
  305. }
  306. JTree tree = (JTree) support.getComponent();
  307. TreePath pathDrag = tree.getSelectionPath();
  308. //Dont allow Cycles
  309. if(pathDrag.isDescendant(pathDrop)) {
  310. return false;
  311. }
  312. //Check physical connected
  313. try {
  314. Holon dropHolon = ((HolonInfo) node.getUserObject()).holon;
  315. Holon dragHolon = (Holon) support.getTransferable().getTransferData(holonFlavor);
  316. boolean physicalConnected = control.getModel().checkHolonObjectsAreConnected(dragHolon, dropHolon, 0f);
  317. boolean isAllStateHolon = dropHolon.getParent() == null;
  318. return physicalConnected || isAllStateHolon;
  319. } catch (UnsupportedFlavorException | IOException e) {
  320. e.printStackTrace();
  321. return false;
  322. }
  323. }
  324. @Override
  325. public boolean importData(TransferHandler.TransferSupport support) {
  326. if(!canImport(support)) {
  327. return false;
  328. }
  329. JTree.DropLocation location = (JTree.DropLocation)support.getDropLocation();
  330. TreePath path = location.getPath();
  331. DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
  332. if(node.getUserObject() instanceof HolonInfo) {
  333. HolonInfo info = (HolonInfo) node.getUserObject();
  334. try {
  335. if(support.isDataFlavorSupported(holonFlavor)) {
  336. info.AddHolon((Holon) support.getTransferable().getTransferData(holonFlavor));
  337. }else if (support.isDataFlavorSupported(elementFlavor)){
  338. info.AddElement((HolonElement) support.getTransferable().getTransferData(elementFlavor));
  339. }
  340. } catch (UnsupportedFlavorException | IOException e) {
  341. e.printStackTrace();
  342. }
  343. }
  344. return false;
  345. }
  346. protected void exportDone(JComponent source,
  347. Transferable data,
  348. int action) {
  349. updateTreeStructure();
  350. }
  351. @Override
  352. public int getSourceActions(JComponent c) {
  353. return MOVE;
  354. }
  355. @Override
  356. protected Transferable createTransferable(JComponent c) {
  357. JTree tree = (JTree)c;
  358. TreePath path = tree.getSelectionPath();
  359. if(path != null) {
  360. DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
  361. Object userObject = node.getUserObject();
  362. //Only allow Hollon to move
  363. if(this.allowHolonElementDragAndDrop) {
  364. //Both
  365. if(userObject instanceof HolonNode) {
  366. return ((HolonNode) userObject).getTransferable(this);
  367. }
  368. }
  369. else {
  370. //Only Holons
  371. if(userObject instanceof HolonInfo) {
  372. return ((HolonInfo) userObject).getTransferable(this);
  373. }
  374. }
  375. }
  376. return null;
  377. }
  378. public class HolonTransferable implements Transferable{
  379. Holon holon;
  380. public HolonTransferable(Holon holon) {
  381. this.holon = holon;
  382. }
  383. @Override
  384. public DataFlavor[] getTransferDataFlavors() {
  385. return holonFlavorArray;
  386. }
  387. @Override
  388. public boolean isDataFlavorSupported(DataFlavor flavor) {
  389. return holonFlavor.equals(flavor);
  390. }
  391. @Override
  392. public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
  393. if(!isDataFlavorSupported(flavor))
  394. throw new UnsupportedFlavorException(flavor);
  395. return holon;
  396. }
  397. }
  398. public class ElementTransferable implements Transferable{
  399. HolonElement element;
  400. public ElementTransferable(HolonElement element) {
  401. this.element = element;
  402. }
  403. @Override
  404. public DataFlavor[] getTransferDataFlavors() {
  405. return elementFlavorArray;
  406. }
  407. @Override
  408. public boolean isDataFlavorSupported(DataFlavor flavor) {
  409. return elementFlavor.equals(flavor);
  410. }
  411. @Override
  412. public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
  413. if(!isDataFlavorSupported(flavor))
  414. throw new UnsupportedFlavorException(flavor);
  415. return element;
  416. }
  417. }
  418. }
  419. //Cell Display
  420. class HolonJtreeRenderer extends DefaultTreeCellRenderer {
  421. public Component getTreeCellRendererComponent(
  422. JTree tree,
  423. Object value,
  424. boolean isSelected,
  425. boolean expanded,
  426. boolean leaf,
  427. int row,
  428. boolean hasFocus) {
  429. super.getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, hasFocus);
  430. DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
  431. HolonNode Node = (HolonNode) node.getUserObject();
  432. if(row != 0) {
  433. this.setIcon(Node.getIcon());
  434. this.setToolTipText(Node.getToolTip());
  435. }
  436. return this;
  437. }
  438. }
  439. //Edit Names of Nodes
  440. class HolonNodeEditor extends AbstractCellEditor implements TreeCellEditor{
  441. private HolonNode holonNode;
  442. private JTextField tf= new JTextField(){
  443. @Override
  444. public Dimension getPreferredSize() {
  445. Dimension dim = super.getPreferredSize();
  446. int length = getText().length();
  447. dim.width = Math.max(300, dim.width + length * 10) ;
  448. return dim;
  449. }
  450. };
  451. public HolonNodeEditor() {
  452. super();
  453. }
  454. @Override
  455. public Object getCellEditorValue() {
  456. holonNode.SetName(tf.getText());
  457. return tf.getText();
  458. }
  459. @Override
  460. public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded,
  461. boolean leaf, int row) {
  462. JLabel newString = (JLabel) holonTree.getCellRenderer().getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, leaf);
  463. DefaultMutableTreeNode default_node = (DefaultMutableTreeNode) value;
  464. holonNode = (HolonNode)default_node.getUserObject();
  465. tf.setText(newString.getText());
  466. return tf;
  467. }
  468. }
  469. }