Java tutorial
/* * Copyright (c) 2011 MetaCase Consulting * Released under the MIT license. See the file license.txt for details. */ package com.metacase.graphbrowser.views; import java.awt.Dimension; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.lang.reflect.Array; import java.net.MalformedURLException; import java.net.URL; import java.rmi.RemoteException; import java.util.*; import javax.swing.JComponent; import javax.swing.JFrame; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.eclipse.ui.part.*; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.graphics.Image; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.*; import org.eclipse.jface.action.*; import org.eclipse.ui.*; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.SWT; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.Platform; import com.metacase.API.*; import com.metacase.graphbrowser.*; import com.metacase.objects.*; /** * This class provides the treeview that shows the graphs that * are open in MetaEdit+. * <p> * The view uses a label provider to define how model * objects should be presented in the view. Each * view can present the same model objects using * different labels and icons, if needed. Alternatively, * a single label provider can be shared between views * in order to ensure that objects of the same type are * presented in the same way everywhere. * <p> */ public class GraphView extends ViewPart implements Observer { /** * The ID of the view as specified by the extension. */ public static final String ID = "com.metacase.graphbrowser.views.GraphView"; private TreeViewer treeViewer; private Composite errorView; private Composite container; private StackLayout layout; private static Action actionOpenInMetaEdit; private static Action actionRunAutobuild; private static Action actionCallGenerator; private static Action actionUpdateGraphList; private static Action actionStartMetaEdit; private static Action actionOpenSettings; private static Action actionOpenCreateGraphDialog; private static Action actionOpenEditPropertiesDialog; private static Action actionToggleGraphTypeText; private static Action doubleClickAction; private ViewContentProvider viewContentProvider; private static boolean isGraphTypeText; public Graph[] graphs; class TreeObject implements IAdaptable { private Graph graph; private TreeObject parent; private ArrayList<TreeObject> children; public TreeObject(Graph _graph) { this.graph = _graph; this.children = new ArrayList<TreeObject>(); } public TreeObject() { this.graph = null; this.children = new ArrayList<TreeObject>(); } public String getName() { if (this.getGraph() == null) return ""; return this.getGraph().toString(); } public void setParent(TreeObject parent) { this.parent = parent; } public TreeObject getParent() { return parent; } public Graph getGraph() { return this.graph; } public String toString() { String s = isGraphTypeText ? getName() + ": " + getGraph().getTypeName() : getName(); return s; } public Object getAdapter(@SuppressWarnings("rawtypes") Class key) { return null; } public void addChild(TreeObject child) { children.add(child); child.setParent(this); } public void removeChild(TreeObject child) { children.remove(child); child.setParent(null); } public TreeObject[] getChildren() { return children.toArray(new TreeObject[children.size()]); } public boolean hasChildren() { return children.size() > 0; } public void populate(Graph[] graphs, ArrayList<Graph> stack) { if (!stack.contains(this.getGraph())) { stack.add(this.getGraph()); for (Graph g : graphs) { TreeObject to = new TreeObject(g); this.addChild(to); Graph[] children = g.getChildren(); if (children != null && children.length > 0) { Arrays.sort(children, Graph.GraphComparator); to.populate(children, stack); } } stack.remove(stack.size() - 1); } } } class ViewContentProvider implements IStructuredContentProvider, ITreeContentProvider { private TreeObject invisibleRoot; public void inputChanged(Viewer v, Object oldInput, Object newInput) { v.refresh(); } public void dispose() { } public Object[] getElements(Object parent) { if (parent.equals(getViewSite())) { if (invisibleRoot == null) try { initialize(); } catch (Exception e) { } return getChildren(invisibleRoot); } return getChildren(parent); } public Object getParent(Object child) { if (child instanceof TreeObject) { return ((TreeObject) child).getParent(); } return null; } public Object[] getChildren(Object parent) { if (parent instanceof TreeObject) { return ((TreeObject) parent).getChildren(); } return new Object[0]; } public boolean hasChildren(Object parent) { if (parent instanceof TreeObject) return ((TreeObject) parent).hasChildren(); return false; } /** * Initializes the tree by first calling the graphs from MetaEdit+ and * creating a tree from the graph set. Shows busy cursor while the work is being done. */ public void initialize() { Runnable init = new Runnable() { public void run() { invisibleRoot = new TreeObject(); Graph.resetCaches(); graphs = GraphHandler.init(); invisibleRoot.populate(graphs, new ArrayList<Graph>()); } }; BusyIndicator.showWhile(getSite().getShell().getDisplay(), init); } } class ViewLabelProvider extends LabelProvider { public String getText(Object obj) { return obj.toString(); } public Image getImage(Object obj) { String imagePath = "icons/graph_icon.png"; ImageDescriptor image; URL url = null; try { url = new URL(Platform.getBundle(Activator.PLUGIN_ID).getEntry("/"), imagePath); } catch (MalformedURLException e) { } image = ImageDescriptor.createFromURL(url); return image.createImage(); } } class NameSorter extends ViewerSorter { } /** * The constructor. */ public GraphView() { } /** * This is a callback that will allow us * to create the viewer and initialize it. */ public void createPartControl(Composite parent) { Settings.getSettings().addObserver(this); layout = new StackLayout(); container = new Composite(parent, SWT.NONE); container.setLayout(layout); createErrorView(container); createTreeView(container); makeActions(); hookContextMenu(); hookDoubleClickAction(); contributeToActionBars(); setToolBarButtonsEnabled(); setView(); } /** * Sets a view on top of StackLayout. * Shows the treeview if API connection found. If not * shows the errorview. */ private void setView() { layout.topControl = this.isAPI() ? treeViewer.getTree() : errorView; container.layout(); setToolBarButtonsEnabled(); } /** * Creates the treeview that is shown if API connection is available. * @param parent The parent composite for the view. */ private void createTreeView(Composite parent) { treeViewer = new TreeViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL); viewContentProvider = new ViewContentProvider(); treeViewer.setContentProvider(viewContentProvider); treeViewer.setLabelProvider(new ViewLabelProvider()); treeViewer.setInput(getViewSite()); treeViewer.expandToLevel(2); treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { setToolBarButtonsEnabled(); setView(); } }); } /** * Creates an error view that is shown when no API connection is available. * @param parent The parent composite for the error view. */ private void createErrorView(Composite parent) { GridLayout gridLayout = new GridLayout(); GridData gridData; errorView = new Composite(parent, SWT.NONE); errorView.setLayout(gridLayout); // Add an empty space above the label. new Label(errorView, SWT.NONE).setText(""); Label errorLabel = new Label(errorView, SWT.NONE); errorLabel.setAlignment(SWT.CENTER); errorLabel.setText("No API connection found."); gridData = new GridData(); gridData.horizontalAlignment = SWT.FILL; gridData.grabExcessHorizontalSpace = true; errorLabel.setLayoutData(gridData); Listener listener = new Listener() { public void handleEvent(Event event) { actionStartMetaEdit.run(); } }; Button errorButton = new Button(errorView, SWT.PUSH); errorButton.addListener(SWT.Selection, listener); errorButton.setText("Start MetaEdit+"); gridData = new GridData(); gridData.horizontalAlignment = SWT.CENTER; gridData.grabExcessHorizontalSpace = true; errorButton.setLayoutData(gridData); errorView.layout(); } private void hookContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { GraphView.this.fillContextMenu(manager); } }); Menu menu = menuMgr.createContextMenu(treeViewer.getControl()); treeViewer.getControl().setMenu(menu); getSite().registerContextMenu(menuMgr, treeViewer); } private void contributeToActionBars() { IActionBars bars = getViewSite().getActionBars(); fillLocalToolBar(bars.getToolBarManager()); } private void fillContextMenu(IMenuManager manager) { if (!treeViewer.getSelection().isEmpty()) { manager.add(actionRunAutobuild); if (this.is50OrLater()) manager.add(actionCallGenerator); manager.add(new Separator()); manager.add(actionOpenInMetaEdit); if (this.is50OrLater()) manager.add(actionOpenEditPropertiesDialog); if (this.is50OrLater()) manager.add(actionOpenCreateGraphDialog); } else { if (this.is50OrLater()) manager.add(actionOpenCreateGraphDialog); } manager.add(new Separator()); // Other plug-ins can contribute their actions here manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } private void fillLocalToolBar(IToolBarManager manager) { manager.add(actionRunAutobuild); manager.add(actionCallGenerator); manager.add(new Separator()); manager.add(actionOpenInMetaEdit); manager.add(actionOpenCreateGraphDialog); manager.add(new Separator()); manager.add(actionUpdateGraphList); manager.add(actionOpenSettings); manager.add(new Separator()); manager.add(actionToggleGraphTypeText); } /** * Check if set MetaEdit+ program version is 4.5 or 5.0 * @return true if version is 5.0 false if not. */ private boolean is50OrLater() { Settings s = Settings.getSettings(); return (s.getVersion().isAtLeast("5.0")); } /** * Check if API is ok * @return true if connection available. */ private boolean isAPI() { return Launcher.isApiOK(); } /** * Sets the toolbar buttons enabled or disabled. */ private void setToolBarButtonsEnabled() { boolean _is50OrLater = this.is50OrLater(); boolean _isAPI = this.isAPI(); boolean _isSelection = !treeViewer.getSelection().isEmpty(); actionRunAutobuild.setEnabled(_isAPI && _isSelection); actionCallGenerator.setEnabled(_is50OrLater && _isAPI && _isSelection); actionOpenInMetaEdit.setEnabled(_isAPI && _isSelection); actionOpenCreateGraphDialog.setEnabled(_is50OrLater && _isAPI); actionUpdateGraphList.setEnabled(true); actionOpenSettings.setEnabled(true); actionToggleGraphTypeText.setEnabled(isAPI()); IActionBars bars = getViewSite().getActionBars(); IToolBarManager manager = bars.getToolBarManager(); manager.update(true); } /** * Gets the selected graph from treeview. * @return selected Graph or null */ private Graph getSelectedGraph() { ISelection selection = treeViewer.getSelection(); TreeObject to = (TreeObject) ((IStructuredSelection) selection).getFirstElement(); if (to == null) return null; return to.getGraph(); } /** * Creates the action methods for toolbar and context menu items. */ private void makeActions() { // This action opens selected graph in MetaEdit+. // SHows user a dialog in case of issues. actionOpenInMetaEdit = new Action() { public void run() { Graph _graph = getSelectedGraph(); if (_graph == null) return; MetaEditAPIPortType port = Launcher.getPort(); try { port.open(_graph.toMEOop()); } catch (RemoteException e) { e.printStackTrace(); DialogProvider.showMessageDialog("API error: " + e.toString(), "API error"); } } }; this.setActionDetails(actionOpenInMetaEdit, "Open Graph in MetaEdit+", "icons/open_graph_in_metaedit_icon.png"); // RunsAutobuild for the selected graph. actionRunAutobuild = new Action() { public void run() { Graph _graph = getSelectedGraph(); if (_graph == null) return; _graph.executeGenerator("Autobuild"); } }; this.setActionDetails(actionRunAutobuild, "Run Autobuild", "icons/run_generator_icon.png"); // Runs seleceted generator for graph. Shows all available generators to user in // a list where user can choose one to be run. actionCallGenerator = new Action() { public void run() { final Graph _graph = getSelectedGraph(); if (_graph == null) return; // Creates dialog that shows available generators and lets user to select one. String okString = "<HTML><p>Select the generator to run.</p></HTML>"; String notOkString = "<HTML><p>No generators found for the the graph</p></HTML>"; MetaEditAPIPortType port = Launcher.getPort(); JFrame frame = new JFrame(""); String[] generators = null; try { String line = port.generatorNames(_graph.getMEType()); generators = line.split("\r"); } catch (RemoteException e) { e.printStackTrace(); } ArrayList<String> generatorList = new ArrayList<String>(); for (int i = generators.length - 1; i >= 0; i--) { if (!generators[i].startsWith("_") && !generators[i].startsWith("!")) { generatorList.add(generators[i]); } } final SelectionDialog p = new SelectionDialog(frame, generatorList, true, okString, notOkString); JComponent newContentPane = p; frame.setContentPane(newContentPane); frame.addWindowListener(new WindowListener() { public void windowOpened(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } public void windowClosing(WindowEvent e) { } public void windowClosed(WindowEvent e) { if (p.getIsOKd()) { _graph.executeGenerator(p.getItemsAsArray()[0]); } } public void windowActivated(WindowEvent e) { } }); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setResizable(true); frame.setVisible(true); frame.setSize(new Dimension(250, 300)); frame.setIconImage(SettingsDialog.getImage("icons/metaedit_logo.png")); frame.setLocation(300, 300); } }; this.setActionDetails(actionCallGenerator, "Select Generator to Run", "icons/select_generator_to_run_icon.png"); // Open settings dialog. actionOpenSettings = new Action() { public void run() { DialogProvider.showSettingsDialog(false); } }; this.setActionDetails(actionOpenSettings, "MetaEdit+ Launch Parameters", "icons/settings_icon.png"); // Double click opens Graph in MetaEdit+ doubleClickAction = new Action() { public void run() { actionOpenInMetaEdit.run(); } }; actionUpdateGraphList = new Action() { public void run() { Object oldInput = treeViewer.getInput(); viewContentProvider.initialize(); viewContentProvider.inputChanged(treeViewer, oldInput, treeViewer.getInput()); treeViewer.expandToLevel(2); setView(); } }; this.setActionDetails(actionUpdateGraphList, "Update Graph List", "icons/update_graph_list_icon.png"); // This action looks for existing API connection and start new MetaEdit+ instance if // no API connection is found. actionStartMetaEdit = new Action() { public void run() { boolean runUpdate; if (isAPI()) { DialogProvider.showMessageDialog("Found an existing API connection.", "API connection found."); runUpdate = true; } else { runUpdate = Launcher.doInitialLaunch(); } if (runUpdate) actionUpdateGraphList.run(); } }; // Open graph creation dialog in MetaEdit+ actionOpenCreateGraphDialog = new Action() { public void run() { MEDialog md = new MEDialog(MEDialog.CREATE_NEW_GRAPH, getSelectedGraph()); md.start(); } }; this.setActionDetails(actionOpenCreateGraphDialog, "Create a New Graph", "icons/create_graph_icon.png"); // Opens properties dialog for the selected graph in MetaEdit+. actionOpenEditPropertiesDialog = new Action() { public void run() { MEDialog md = new MEDialog(MEDialog.EDIT_GRAPH_PROPERTIES, getSelectedGraph()); md.start(); } }; this.setActionDetails(actionOpenEditPropertiesDialog, "Edit Graph Properties", "icons/edit_properties_icon.png"); actionToggleGraphTypeText = new Action("", Action.AS_CHECK_BOX) { public void run() { isGraphTypeText = actionToggleGraphTypeText.isChecked() ? true : false; viewContentProvider.inputChanged(treeViewer, null, null); } }; this.setActionDetails(actionToggleGraphTypeText, "Show/Hide Graph Type", "icons/folder_explore.png"); } /** * Sets action label text, tooltiptext and icon. * @param a Action for setting the texts and icon. * @param text Text shows in menus. * @param toolTipText Tooltiptext for menu. * @param iconPath path for icon loader. */ public void setActionDetails(Action a, String text, String iconPath) { a.setText(text); a.setToolTipText(text); a.setImageDescriptor(getImageDescriptor(iconPath)); } private void hookDoubleClickAction() { treeViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { doubleClickAction.run(); } }); } /** * Passing the focus request to the viewer's control. */ public void setFocus() { layout.topControl.setFocus(); } /** * Loads images for action buttons. * @param iconPath * @return loaded image */ public static ImageDescriptor getImageDescriptor(String iconPath) { ImageDescriptor image; URL url = null; try { url = new URL(Platform.getBundle(Activator.PLUGIN_ID).getEntry("/"), iconPath); } catch (MalformedURLException e) { e.printStackTrace(); } image = ImageDescriptor.createFromURL(url); return image; } /** * Implementation of java.util.Observer interface method. Checks if * the observable class is Settings and calls the method for checking * the toolbar buttons enabling/disabling. */ @Override public void update(Observable arg0, Object arg1) { if (arg0.getClass() == Settings.class) { Display.getDefault().syncExec(new Runnable() { public void run() { setToolBarButtonsEnabled(); } }); } } /** * Updated the view by calling the update action. For use outside the UI thread. */ public static synchronized void updateView() { actionUpdateGraphList.run(); } }