/******************************************************************************* * Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * The Chisel Group, University of Victoria *******************************************************************************/ package ca.uvic.cs.tagsea.ui.views; import java.util.Arrays; import java.util.HashMap; import java.util.Vector; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.util.OpenStrategy; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; import org.eclipse.ui.ide.IDE; import ca.uvic.cs.tagsea.ITagseaImages; import ca.uvic.cs.tagsea.TagSEAPlugin; import ca.uvic.cs.tagsea.actions.GoToTagAction; import ca.uvic.cs.tagsea.core.Route; import ca.uvic.cs.tagsea.core.RouteCollection; import ca.uvic.cs.tagsea.core.Waypoint; import ca.uvic.cs.tagsea.editing.RoutesTreeItemListener; import ca.uvic.cs.tagsea.editing.TreeItemWorker; import ca.uvic.cs.tagsea.util.RouteNameValidator; /** * The composite which contains the routes TreeViewer, header label and navigation composite. * * @author Suzanne Thompson, Jie Zhang */ public class RoutesComposite extends BaseTagsViewComposite { private ToolBar toolBar; private ToolBarManager barManager; private ImageDescriptor imgDescriptor; private TreeItemWorker treeWorker; private RoutesTreeItemListener routesTreeListener; private static Action backAction; private static Action forwardAction; private Action newRouteAction; private Action deleteAction; private Action removeStaleWaypointsAction; private TreeViewer routesTreeViewer; private final int WIDTH = 200; private final int UP = -1; private final int DOWN = 1; public RoutesComposite(Composite parent, int style) { super(parent, style); GridLayout layout = new GridLayout(2, false); layout.marginLeft = 0; layout.marginRight = 0; layout.marginBottom = 0; layout.marginTop = 0; this.setLayout(layout); GridData data = new GridData(SWT.END, SWT.FILL, false, true); data.widthHint = WIDTH; this.setLayoutData(data); Label header = new Label(this, SWT.LEFT); header.setFont(getHeaderLabelFont()); //@tag bug(177) : use default font size //header.setForeground(getHeaderLabelColor()); header.setText("Routes"); data = new GridData(SWT.LEFT, SWT.CENTER, false, false); //@tag bug(177) : use default font size //data.heightHint = 24; header.setLayoutData(data); toolBar = new ToolBar(this, SWT.FLAT | SWT.LEFT); data = new GridData(SWT.FILL, SWT.CENTER, true, false); data.heightHint = 24; toolBar.setLayoutData(data); barManager = new ToolBarManager(toolBar); fillToolBar(); initTreeViewer(); createContextMenu(); } private void fillToolBar() { ITagseaImages images = TagSEAPlugin.getDefault().getTagseaImages(); //backAction imgDescriptor = images.getDescriptor(ITagseaImages.IMG_UP_ARROW); backAction = new Action("Previous waypoint", imgDescriptor) { public void run() { goToWaypoint(UP); } }; backAction.setToolTipText("Previous waypoint"); imgDescriptor = images.getDescriptor(ITagseaImages.IMG_UP_ARROW_DISABLED); backAction.setDisabledImageDescriptor(imgDescriptor); //forwardAction imgDescriptor = images.getDescriptor(ITagseaImages.IMG_DOWN_ARROW); forwardAction = new Action("Next waypoint", imgDescriptor) { public void run() { goToWaypoint(DOWN); } }; forwardAction.setToolTipText("Next waypoint"); imgDescriptor = images.getDescriptor(ITagseaImages.IMG_DOWN_ARROW_DISABLED); forwardAction.setDisabledImageDescriptor(imgDescriptor); //newRouteAction final ImageDescriptor routeImgDescriptor = images.getDescriptor(ITagseaImages.IMG_ROUTE); newRouteAction = new Action("&Add A New Route", routeImgDescriptor) { public void run() { addNewRoute(); // RouteXMLUtil.recordRoutes(Activator.getDefault().getRouteCollection()); } }; newRouteAction.setToolTipText("Add a new route"); ImageDescriptor routeImgDescriptorDis = images.getDescriptor(ITagseaImages.IMG_ROUTE_DISABLED); newRouteAction.setDisabledImageDescriptor(routeImgDescriptorDis); //delete Route/Waypoint Action ImageDescriptor deleteDescriptor = images.getDescriptor(ITagseaImages.IMG_TOOL_DELETE); ImageDescriptor deleteDescriptorDis = images.getDescriptor(ITagseaImages.IMG_TOOL_DELETE_DISABLED); deleteAction = new Action("&Delete Route/Waypoint", deleteDescriptor) { public void run() { deleteRouteWaypoint(); refreshRoutesViewer(); // RouteXMLUtil.recordRoutes(Activator.getDefault().getRouteCollection()); } }; deleteAction.setToolTipText("Delete selected waypoints/routes"); deleteAction.setDisabledImageDescriptor(deleteDescriptorDis); //delete Route/Waypoint Action ImageDescriptor removeStaleWaypointsDescriptor = images.getDescriptor(ITagseaImages.IMG_CLEAR); removeStaleWaypointsAction = new Action("&Clear stale waypoints", removeStaleWaypointsDescriptor) { public void run() { // Delete the stale waypoints TreeItem[] selections = routesTreeViewer.getTree().getSelection(); for(TreeItem item : selections) { if(item.getData() instanceof Route) { Route r = (Route)item.getData(); Vector waypoints = r.getWaypoints(); for(int i = waypoints.size() -1 ; i >= 0; i--) { Waypoint w = (Waypoint)waypoints.elementAt(i); if(w.isStale()) waypoints.removeElementAt(i); } } } TagSEAPlugin.getDefault().getRouteCollection().updateView(); } }; removeStaleWaypointsAction.setToolTipText("Remove stale waypoints"); removeStaleWaypointsAction.setEnabled(false); deleteAction.setEnabled(false); backAction.setEnabled(false); forwardAction.setEnabled(false); barManager.add(newRouteAction); barManager.add(deleteAction); barManager.add(new Separator()); barManager.add(removeStaleWaypointsAction); barManager.add(new Separator()); barManager.add(backAction); barManager.add(forwardAction); barManager.update(false); } protected void addNewRoute() { RouteCollection routeCollection = TagSEAPlugin.getDefault().getRouteCollection(); routeCollection = TagSEAPlugin.getDefault().getRouteCollection(); RouteNameValidator validator = new RouteNameValidator(routeCollection); InputDialog dlg = new InputDialog(routesTreeViewer.getTree().getShell(), "New Route Name", "Enter a name for the new route:", "", validator); if (dlg.open() == InputDialog.OK) { Route route = routeCollection.addRoute(dlg.getValue()); refreshRoutesViewer(); routesTreeViewer.setSelection(new StructuredSelection(route), true); routesTreeViewer.getControl().setFocus(); } } @SuppressWarnings("restriction") protected void deleteRouteWaypoint() { RouteCollection routes = TagSEAPlugin.getDefault().getRouteCollection(); TreeItem[] selections = routesTreeViewer.getTree().getSelection(); if(selections.length>0) { HashMap<Route, int[]> deleteMap = new HashMap<Route, int[]>(); for (int i = 0; i < selections.length; i++) { Object item = selections[i].getData(); if (item instanceof Route) { routes.removeRoute((Route)item); }else if(item instanceof Waypoint){ TreeItem routeItem = selections[i].getParentItem(); int index = routeItem.indexOf(selections[i]); Route route = (Route)routeItem.getData(); // for each route, save the indeces to delete int[] indeces; if (deleteMap.containsKey(route)) { int[] oldIndeces = (int[])deleteMap.get(route); indeces = new int[oldIndeces.length + 1]; System.arraycopy(oldIndeces, 0, indeces, 1, oldIndeces.length); indeces[0] = index; } else { indeces = new int[] { index }; } deleteMap.put(route, indeces); } } // now delete the waypoints for each route for (Route route : deleteMap.keySet()) { int[] indeces = (int[]) deleteMap.get(route); // sort the indeces to remove Arrays.sort(indeces); // must do this in reverse order so that the index stays valid for (int i = indeces.length - 1; i >= 0; i--) { route.removeWaypoint(indeces[i]); //System.out.println("Deleting " + indeces[i] + " ok? " + ok); } } } } /** * Initializes the tree viewer, setting the content and label providers. */ private void initTreeViewer() { RouteCollection rc = TagSEAPlugin.getDefault().getRouteCollection(); routesTreeViewer = new TreeViewer(this, SWT.MULTI| SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); routesTreeViewer.setContentProvider(rc); routesTreeViewer.setLabelProvider(rc); routesTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { RoutesComposite.this.selectionChanged(event); } }); GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); data.horizontalSpan = 2; routesTreeViewer.getControl().setLayoutData(data); treeWorker = new TreeItemWorker(routesTreeViewer.getTree(), true); routesTreeListener = new RoutesTreeItemListener(routesTreeViewer); treeWorker.addListener(routesTreeListener); treeWorker.setRenameValidator(new RouteNameValidator(rc)); //new RoutesTreeDragAndDropManager(routesTreeViewer); } public TreeViewer getRoutesTreeViewer() { return routesTreeViewer; } public TreeItemWorker getRoutesTreeItemWorker() { return treeWorker; } public MenuManager createContextMenu() { MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { RoutesComposite.this.fillContextMenu(manager); } }); Menu menu = menuMgr.createContextMenu(routesTreeViewer.getControl()); routesTreeViewer.getControl().setMenu(menu); return menuMgr; } private void fillContextMenu(IMenuManager manager) { manager.add(newRouteAction); manager.add(deleteAction); manager.add(new Separator()); manager.add(removeStaleWaypointsAction); manager.add(new Separator()); manager.add(backAction); manager.add(forwardAction); manager.add(new Separator()); // Other plug-ins can contribute there actions here manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } /** * Adds a double click listener to the waypoints table viewer which will open an editor * showing the double clicked waypoint. * @param page the active page */ public void hookDoubleClickAction(IWorkbenchPage page) { routesTreeViewer.addDoubleClickListener(new GoToTagAction(routesTreeViewer, page)); } public void goToWaypoint(int direction) { Waypoint wp = null; TreeItem routeItem = null; int index = -1; int waypointsInRoute; //determine the index of previous/next waypoints in the route TreeItem[] selections = routesTreeViewer.getTree().getSelection(); if(selections.length>0){ TreeItem selection = selections[0]; if (selection.getData() instanceof Route) { routeItem = selection; waypointsInRoute = routeItem.getItemCount(); if(waypointsInRoute>0){ if(direction == -1){ index = waypointsInRoute-1; }else{ index = 0; } } } else if (selection.getData() instanceof Waypoint) { routeItem = selection.getParentItem(); index = routeItem.indexOf(selection); waypointsInRoute = routeItem.getItemCount(); if(direction == -1){ index = (index+waypointsInRoute-1)%waypointsInRoute; }else{ index = (index+1)%waypointsInRoute; } } } if(index!=-1){ //get the waypoint in the route TreeItem wpItem = routeItem.getItem(index); wp = (Waypoint)wpItem.getData(); //go to the tag in resource RoutesView routesView = TagSEAPlugin.getDefault().getRoutesView(); IMarker marker = wp.getMarker(); if ((marker != null) && marker.exists()) { IResource resource = marker.getResource(); if (marker.exists() && resource instanceof IFile) { try { IDE.openEditor(routesView.getSite().getPage(), marker, OpenStrategy.activateOnOpen()); } catch (PartInitException e) { TagSEAPlugin.log("Couldn't open editor to show the tag", e); } } } //highlight the waypoint in routeTreeViewer //routesTreeViewer.setSelection(new StructuredSelection(wpItem.getData()), true); routesTreeViewer.getTree().setSelection(wpItem); routesTreeViewer.getControl().setFocus(); } } /** * Gets the expanded list of route ids from the view. * @return An array of string's representing expanded tag ids. */ public String[] getExpandedRoutesList() { Object[] expandedElements = routesTreeViewer.getExpandedElements(); String[] expandedIdList = new String[expandedElements.length]; int i = 0; for ( Object o : expandedElements ) { expandedIdList[i++] = ((Route)o).getName(); } return expandedIdList; } /** * Returns the selected Route. If a waypoint is selected then its parent route is returned. * If nothing is selected, then the first route is returned. If no routes exist then null is returned. * @return Route or null if none selected. */ public Route getSelectedRoute() { Route route = null; Tree tree = routesTreeViewer.getTree(); TreeItem[] sel = tree.getSelection(); if (sel.length > 0) { TreeItem item = sel[0]; Object obj = item.getData(); if ((obj instanceof Waypoint) && (item.getParentItem() != null)) { obj = item.getParentItem().getData(); } if (obj instanceof Route) { route = (Route) obj; } } else if (tree.getItemCount() > 0) { // get the first item TreeItem first = tree.getItem(0); Object obj = first.getData(); if (obj instanceof Route) { route = (Route) obj; } } return route; } /** * Refreshes the routes tree viewer */ public void refreshRoutesViewer(){ refreshRoutesViewer(null); } /** * Refreshes just the given element. * @param element the Route or Waypoint to refresh */ public void refreshRoutesViewer(Object element) { // get expanded id's so we can maintain the view state String[] expandedIdList = getExpandedRoutesList(); if (element == null) { routesTreeViewer.refresh(); } else { routesTreeViewer.refresh(element); } // reselect the tags that were selected prior to the refresh routesTreeViewer.setExpandedElements(TagSEAPlugin.getDefault().getRouteCollection().getRoutes(expandedIdList)); } //enable and disable the actions according to the selction protected void selectionChanged(SelectionChangedEvent event) { if(event.getSelection().isEmpty()) { enableAllActions(false); }else{ enableAllActions(true); IStructuredSelection selections = (IStructuredSelection)event.getSelection(); if (selections.size()==1 && selections.getFirstElement()instanceof Route) { Route route = (Route) selections.getFirstElement(); if (route.getWaypoints().size()==0) { backAction.setEnabled(false); forwardAction.setEnabled(false); } } } boolean routesSelected = false; TreeItem[] selections = routesTreeViewer.getTree().getSelection(); for(TreeItem item : selections) { if(item.getData() instanceof Route) routesSelected = true; } if(routesSelected) removeStaleWaypointsAction.setEnabled(true); else removeStaleWaypointsAction.setEnabled(false); } private void enableAllActions(boolean enable){ deleteAction.setEnabled(enable); backAction.setEnabled(enable); forwardAction.setEnabled(enable); } }