/******************************************************************************* * 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 net.sourceforge.tagsea.cloudsee.views; import net.sourceforge.tagsea.TagSEAPlugin; import net.sourceforge.tagsea.TagSEAUtils; import net.sourceforge.tagsea.cloudsee.CloudSeePlugin; import net.sourceforge.tagsea.cloudsee.ITagseaImages; import net.sourceforge.tagsea.core.ITag; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IPartListener; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.part.ViewPart; /** * This class contains methods and functionality for displaying the CloudSee visualization. * The CloudSee visualization is a hierarchical tag cloud, which can be used to navigate the * tag hierarchy. A CloudElement's text size is a representation of that tag's number of waypoints. * Also, the color is a representation of how many children that tag has. * * @author Sean Falconer * @author Del Myers */ public class CloudSeeViewOld extends ViewPart implements PaintListener, MouseListener, ControlListener { public static final String ID = "net.sourceforge.tagsea.CloudSeeView"; // total number of different font sizes used in the tag cloud private static final int FONT_DIVISIONS = 25; // starting x, y values for tag cloud elements private static final int START_X = 17; private static final int START_Y = 17; // border arc width and height private static final int BORDER_ARC_WIDTH = 10; private static final int BORDER_ARC_HEIGHT = 10; // minimum border width private static final int BORDER_MIN_WIDTH = 300; // buffer for line wrapping private static final int BORDER_BUFFER = 20; // x and y spacing for tag cloud private static final int X_BUFFER = 30; private static final int Y_BUFFER = 10; // UI components private Composite composite; private Canvas canvas; private ScrolledComposite scrolledComposite; // back buffer used for double buffering private Image backBuffer; // currently visible cloud elements private CloudElement[] cloudElements; // the previously viewed tag private String prevTag; // the currently selected cloud element private CloudElement selectedElement; // toolbar actions private Action forwardAction; private Action backAction; private Action refreshAction; // flag to say whether we loaded the cloud elements yet private boolean loaded = false; /** * The constructor. */ public CloudSeeViewOld() {} /** * This is a callback that will allow us * to create the viewer and initialize it. */ public void createPartControl(Composite parent) { parent.addControlListener(this); composite = new Composite(parent, SWT.NONE); GridLayout gridLayout = new GridLayout(1, true); composite.setLayout(gridLayout); GridData gridData = new GridData(SWT.FILL, SWT.BEGINNING, true, false); gridData.heightHint = 24; GridData scrollGridData = new GridData(SWT.FILL, SWT.FILL, true, true); scrolledComposite = new ScrolledComposite(composite, SWT.V_SCROLL | SWT.BORDER); scrolledComposite.setLayoutData(scrollGridData); canvas = new Canvas(scrolledComposite, SWT.NO_BACKGROUND); canvas.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); canvas.addPaintListener(this); canvas.addMouseListener(this); scrolledComposite.setContent(canvas); scrolledComposite.setExpandVertical(true); scrolledComposite.setExpandHorizontal(true); getSite().getWorkbenchWindow().getPartService().addPartListener(new IPartListener() { public void partClosed(IWorkbenchPart part) { //savePreferences(); // remove this listener, it will be added when the part is created getSite().getWorkbenchWindow().getPartService().removePartListener(this); } public void partActivated(IWorkbenchPart part) {} public void partBroughtToTop(IWorkbenchPart part) {} public void partDeactivated(IWorkbenchPart part) {} public void partOpened(IWorkbenchPart part) {} }); makeActions(); fillToolBar(); } public void refresh() { loaded = false; canvas.redraw(); } /** * Passing the focus request to the viewer's control. */ public void setFocus() {} public void paintControl(PaintEvent e) { doubleBufferPaint(e.gc); } /* * (non-Javadoc) * @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent) */ public void controlMoved(ControlEvent e){} /* * (non-Javadoc) * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent) */ public void controlResized(ControlEvent e) { canvas.redraw(); } /* * (non-Javadoc) * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) */ public void mouseDoubleClick(MouseEvent e) { attemptExpansion(e.x, e.y); } /* * (non-Javadoc) * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) */ public void mouseDown(MouseEvent e) { attemptSelect(e.x, e.y); } /* * (non-Javadoc) * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent) */ public void mouseUp(MouseEvent e) {} private void fillToolBar() { IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager(); toolBarManager.add(refreshAction); toolBarManager.add(backAction); toolBarManager.add(forwardAction); toolBarManager.update(true); forwardAction.setEnabled(false); backAction.setEnabled(false); } private void makeActions() { // ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages(); // ImageDescriptor descriptorBack = sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_BACK); // ImageDescriptor descriptorBackDisabled = sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_BACK_DISABLED); ITagseaImages images = CloudSeePlugin.getDefault().getImages(); ImageDescriptor descriptorBack = images.getDescriptor(ITagseaImages.IMG_UP_ARROW); ImageDescriptor descriptorBackDisabled = images.getDescriptor(ITagseaImages.IMG_UP_ARROW_DISABLED); backAction = new Action("Back", descriptorBack) { @Override public void run() { takeBackAction(); } }; backAction.setDisabledImageDescriptor(descriptorBackDisabled); ImageDescriptor descriptorForward = images.getDescriptor(ITagseaImages.IMG_DOWN_ARROW); ImageDescriptor descriptorForwardDisabled = images.getDescriptor(ITagseaImages.IMG_DOWN_ARROW_DISABLED); forwardAction = new Action("Forward", descriptorForward) { @Override public void run() { takeForwardAction(); } }; forwardAction.setDisabledImageDescriptor(descriptorForwardDisabled); refreshAction = new Action("Refresh", CloudSeePlugin.getImageDescriptor("icons/refresh_nav.gif")) { @Override public void run() { loaded = false; canvas.redraw(); } }; } /** * Expands the selected element if one exists. * @param x The x coordinate of where the user clicked. * @param y The y coordinate of where the user clicked. */ private void attemptExpansion(int x, int y) { CloudElement selectedElement = getSelected(x, y); if (selectedElement == null) return; String[] childNames = TagSEAUtils.getChildTagNames(selectedElement.getTagName()); if(selectedElement != null && childNames.length > 0) { prevTag = selectedElement.getTagName(); loadCloudElements(childNames); backAction.setEnabled(true); forwardAction.setEnabled(false); } } /** * Attempts to select a cloud element. * @param x The x coordinate of where the user clicked. * @param y The y coordinate of where the user clicked. */ private void attemptSelect(int x, int y) { forwardAction.setEnabled(false); for(int i = 0; i < cloudElements.length; i++) { cloudElements[i].setSelected(false); } selectedElement = getSelected(x, y); if(selectedElement != null) { selectedElement.setSelected(true); String[] childNames = TagSEAUtils.getChildTagNames(selectedElement.getTagName()); if(childNames.length > 0) forwardAction.setEnabled(true); } canvas.redraw(); } private CloudElement getSelected(int x, int y) { for(int i = 0; i < cloudElements.length; i++) { if(cloudElements[i].contains(x, y)) { return cloudElements[i]; } } return null; } private void takeBackAction() { if(prevTag != null) { String prevParentName = ""; int lastDot = prevTag.lastIndexOf('.'); if (lastDot != -1) { prevParentName = prevTag.substring(0, lastDot); } loadCloudElements(TagSEAUtils.getChildTagNames(prevParentName)); prevTag = prevParentName; selectedElement = null; // turn off back action at root if(prevParentName.length() == 0) backAction.setEnabled(false); canvas.redraw(); } } private void takeForwardAction() { if(selectedElement != null) { String[] childNames = TagSEAUtils.getChildTagNames(selectedElement.getTagName()); if(childNames.length > 0) { loadCloudElements(childNames); forwardAction.setEnabled(false); prevTag = selectedElement.getTagName(); backAction.setEnabled(true); canvas.redraw(); } } } /** * Loads the cloud element collection based on the root level tags. */ private void loadCloudElements(String[] tags) { cloudElements = new CloudElement[tags.length]; int maxFrequency = Integer.MIN_VALUE; int maxChildren = Integer.MIN_VALUE; for(int i = 0; i < cloudElements.length; i++) { loaded = true; ITag[] childTags = TagSEAUtils.getChildTagsForName(tags[i]); ITag realTag = TagSEAPlugin.getTagsModel().getTag(tags[i]); int frequency = 0; if (realTag != null) { frequency = realTag.getWaypointCount(); } for (int j = 0; j < childTags.length; j++) { frequency += childTags[j].getWaypointCount(); } int children = childTags.length; cloudElements[i] = new CloudElement(tags[i], frequency, children); maxFrequency = Math.max(maxFrequency, frequency); maxChildren = Math.max(maxChildren, children); } double factor = (CloudSeeViewOld.FONT_DIVISIONS - 1) / Math.log(maxFrequency+1); for(int i = 0; i < cloudElements.length; i++) cloudElements[i].calculateFont(factor, maxChildren); } /** * Draw view to the canvas. * @param dest */ private void doubleBufferPaint(GC dest) { Point size = canvas.getSize(); if(size.x <= 0 || size.y <= 0) return; if(backBuffer != null) { Rectangle r = backBuffer.getBounds(); if (r.width != size.x || r.height != size.y) { backBuffer.dispose(); backBuffer = null; } } backBuffer = new Image(canvas.getDisplay(), size.x, size.y); GC gc = new GC(backBuffer); try { gc.setBackground(canvas.getBackground()); gc.fillRectangle(0, 0, size.x, size.y); doPaint(gc); } finally { gc.dispose(); } dest.drawImage(backBuffer, 0, 0); } private void doPaint(GC gc) { if(!loaded) { loadCloudElements(TagSEAUtils.getRootTagNames()); } int width = BORDER_MIN_WIDTH, height = 0; int x = START_X, y = START_Y; int maxRowHeight = 0; for(int i = 0; i < cloudElements.length; i++) { cloudElements[i].draw(gc, x, y); maxRowHeight = Math.max(maxRowHeight, cloudElements[i].getHeight()); // wrap line if we exceed the canvas x value if(x+cloudElements[i].getWidth() >= canvas.getSize().x - BORDER_BUFFER && x > START_X) { //erase and start over. gc.fillRectangle(x, y, cloudElements[i].getWidth(), cloudElements[i].getHeight()); x = START_X; y += maxRowHeight + Y_BUFFER; maxRowHeight = 0; i--; } else { x = cloudElements[i].getX() + cloudElements[i].getWidth() + X_BUFFER; y = cloudElements[i].getY(); } width = Math.max(width, x); height = Math.max(height, y + cloudElements[i].getHeight()); } if(cloudElements.length > 0) drawTagCloudBorder(gc, width, height); } private void drawTagCloudBorder(GC gc, int width, int height) { gc.setFont(new Font(null, "Arial", 12, SWT.NORMAL)); gc.setForeground(new Color(null, 0, 0, 0)); gc.drawRoundRectangle(10, 13, width, height+12, BORDER_ARC_WIDTH, BORDER_ARC_HEIGHT); if ((prevTag != null) && (prevTag != null) && (prevTag.length() > 0)) { gc.drawString(" " + prevTag + " ", 20, 0); } } }