zest.core
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/META-INF/MANIFEST.MF b/visualization/plugins/org.eclipse.mylar.zest.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..bf1db02
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/META-INF/MANIFEST.MF
@@ -0,0 +1,30 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Core Plug-in
+Bundle-SymbolicName: org.eclipse.mylar.zest.core; singleton:=true
+Bundle-Version: 0.3.1
+Bundle-Activator: org.eclipse.mylar.zest.core.ZestPlugin
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.mylar.zest.layouts,
+ org.eclipse.gef
+Eclipse-LazyStart: true
+Export-Package: org.eclipse.mylar.zest.core,
+ org.eclipse.mylar.zest.core.internal.gefx;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.graphmodel;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.graphmodel.nested;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.graphviewer;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.graphviewer.parts;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.graphviewer.policies;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.nestedgraphviewer;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.nestedgraphviewer.parts;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.nestedgraphviewer.policies;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.viewers;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.viewers.commands;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.viewers.figures;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.internal.viewers.trackers;x-friends:="org.eclipse.mylar.zest.custom",
+ org.eclipse.mylar.zest.core.messages,
+ org.eclipse.mylar.zest.core.viewers,
+ org.eclipse.mylar.zest.core.widgets
+Import-Package: com.ibm.icu.text
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/about.html b/visualization/plugins/org.eclipse.mylar.zest.core/about.html
new file mode 100644
index 0000000..1aeb16b
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/about.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<html>
+<head>
+<title>About</title>
+<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>May 2, 2006</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, "Program" will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party ("Redistributor") and different terms and conditions may
+apply to your use of any object code in the Content. Check the Redistributor's license that was
+provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="/">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/build.properties b/visualization/plugins/org.eclipse.mylar.zest.core/build.properties
new file mode 100644
index 0000000..a9cbb06
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/build.properties
@@ -0,0 +1,7 @@
+bin.includes = plugin.xml,\
+ META-INF/,\
+ .,\
+ src/icons/
+jars.compile.order = .
+source.. = src/
+output.. = bin/
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/currentTasks.txt b/visualization/plugins/org.eclipse.mylar.zest.core/currentTasks.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/currentTasks.txt
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/license.txt b/visualization/plugins/org.eclipse.mylar.zest.core/license.txt
new file mode 100644
index 0000000..f61d34d
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/license.txt
@@ -0,0 +1,88 @@
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
+
+c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
+
+ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
+
+iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
+
+iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program.
+
+Contributors may not remove or alter any copyright notices contained within the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
+
+
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/plugin.xml b/visualization/plugins/org.eclipse.mylar.zest.core/plugin.xml
new file mode 100644
index 0000000..8e49b52
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/plugin.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+
+</plugin>
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/home.gif b/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/home.gif
new file mode 100644
index 0000000..38e8b54
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/home.gif
Binary files differ
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_hanging.gif b/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_hanging.gif
new file mode 100644
index 0000000..9c050ed
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_hanging.gif
Binary files differ
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_hanging_inverse.gif b/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_hanging_inverse.gif
new file mode 100644
index 0000000..22e017d
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_hanging_inverse.gif
Binary files differ
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_normal.gif b/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_normal.gif
new file mode 100644
index 0000000..4a7cb1a
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_normal.gif
Binary files differ
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_normal_inverse.gif b/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_normal_inverse.gif
new file mode 100644
index 0000000..2c216e7
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/icons/tree_normal_inverse.gif
Binary files differ
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/IZestColorConstants.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/IZestColorConstants.java
new file mode 100644
index 0000000..5c8f499
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/IZestColorConstants.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core;
+
+/**
+ * Provides constants for default colors used in ZEST.
+ * @author Del Myers
+ * @tag zest(bug(151332-Colors)) : resolution
+ */
+public interface IZestColorConstants {
+
+ public static final String LIGHT_BLUE = "org.eclipse.mylar.zest.colors.lightblue"; //$NON-NLS-1$
+ public static final String LIGHT_BLUE_CYAN = "org.eclipse.mylar.zest.colors.lightbluecyan"; //$NON-NLS-1$
+ public static final String GREY_BLUE = "org.eclipse.mylar.zest.colors.greyblue"; //$NON-NLS-1$
+ public static final String DARK_BLUE = "org.eclipse.mylar.zest.colors.darkblue"; //$NON-NLS-1$
+ public static final String LIGHT_YELLOW = "org.eclipse.mylar.zest.colors.lightyellow"; //$NON-NLS-1$
+ public static final String WHITE = "org.eclipse.mylar.zest.colors.white"; //$NON-NLS-1$
+ public static final String LIGHT_GRAY = "org.eclipse.mylar.zest.colors.lightgray"; //$NON-NLS-1$
+ public static final String DISABLED = "org.eclipse.mylar.zest.colors.disabled"; //$NON-NLS-1$
+ public static final String GRAY = "org.eclipse.mylar.zest.colors.gray"; //$NON-NLS-1$
+ public static final String BLACK = "org.eclipse.mylar.zest.colors.black"; //$NON-NLS-1$
+ public static final String RED = "org.eclipse.mylar.zest.colors.red"; //$NON-NLS-1$
+ public static final String DARK_RED = "org.eclipse.mylar.zest.colors.darkred";
+ public static final String ORANGE = "org.eclipse.mylar.zest.colors.orange"; //$NON-NLS-1$
+ public static final String YELLOW = "org.eclipse.mylar.zest.colors.yellow"; //$NON-NLS-1$
+ public static final String GREEN = "org.eclipse.mylar.zest.colors.green"; //$NON-NLS-1$
+ public static final String LIGHT_GREEN = "org.eclipse.mylar.zest.colors.lightgreen"; //$NON-NLS-1$
+ public static final String DARK_GREEN = "org.eclipse.mylar.zest.colors.darkgreen"; //$NON-NLS-1$
+ public static final String CYAN = "org.eclipse.mylar.zest.colors.cyan"; //$NON-NLS-1$
+ public static final String BLUE = "org.eclipse.mylar.zest.colors.blue"; //$NON-NLS-1$;
+ public static final String NODE_DEFAULT_FOREGROUND = BLACK;
+ public static final String NODE_DEFAULT_BACKGROUND = LIGHT_BLUE;
+ public static final String NODE_DEFAULT_HIGHLIGHT = YELLOW;
+ public static final String NODE_DEFAULT_ADJACENT = ORANGE;
+ public static final String NODE_DEFAULT_BORDER = BLACK;
+ public static final String NODE_DEFAULT_BORDER_HIGHLIGHT = BLUE;
+ public static final String EDGE_WEIGHT_0 = "edge.0"; //$NON-NLS-1$
+ public static final String EDGE_WEIGHT_01 = "edge.01"; //$NON-NLS-1$
+ public static final String EDGE_WEIGHT_02 = "edge.02"; //$NON-NLS-1$
+ public static final String EDGE_WEIGHT_03 = "edge.03"; //$NON-NLS-1$
+ public static final int EDGE_WEIGHTS = 4;
+
+ public static final String EDGE_DEFAULT = "org.eclipse.mylar.zest.colors.edgedefault"; //$NON-NLS-1$
+ public static final String EDGE_HIGHLIGHT = "org.eclipse.mylar.zest.colors.edgehighlight"; //$NON-NLS-1$
+
+
+
+
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/IZestImageConstants.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/IZestImageConstants.java
new file mode 100644
index 0000000..971f960
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/IZestImageConstants.java
@@ -0,0 +1,9 @@
+package org.eclipse.mylar.zest.core;
+
+public interface IZestImageConstants {
+
+ public static String TREE_HANGING_ICON = "src/icons/tree_hanging.gif";
+ public static String TREE_HANGING_INVERSE_ICON = "src/icons/tree_hanging_inverse.gif";
+ public static String TREE_NORMAL_ICON = "src/icons/tree_normal.gif";
+ public static String TREE_NORMAL_INVERSE_ICON = "src/icons/tree_normal_inverse.gif";
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestColors.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestColors.java
new file mode 100644
index 0000000..253e250
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestColors.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core;
+
+import org.eclipse.swt.graphics.Color;
+
+/**
+ * Color constants used in Zest.
+ *
+ * @author Chris Callendar
+ * @deprecated
+ */
+public final class ZestColors {
+
+ public static final Color LIGHT_BLUE = new Color(null, 216, 228, 248);
+
+ public static final Color LIGHT_BLUE_CYAN = new Color(null, 213, 243, 255);
+
+ public static final Color GREY_BLUE = new Color(null, 139, 150, 171);
+
+ public static final Color DARK_BLUE = new Color(null, 1, 70, 122);
+
+ public static final Color LIGHT_YELLOW = new Color(null, 255, 255, 206);
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestException.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestException.java
new file mode 100644
index 0000000..6dd6981
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestException.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core;
+
+import org.eclipse.mylar.zest.core.messages.ZestErrorMessages;
+
+/**
+ * Exceptions for Zest-specific code.
+ * @author Del Myers
+ *
+ */
+public class ZestException extends RuntimeException {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ /**
+ * An invalid input was given to a zest component.
+ */
+ public static final int ERROR_INVALID_INPUT = 0;
+ /**
+ * A style was set on a viewer while the input wasn't null.
+ */
+ public static final int ERROR_CANNOT_SET_STYLE = 1;
+
+ /**
+ * An invalid style was set for a part.
+ */
+ public static final int ERROR_INVALID_STYLE = 2;
+
+ public ZestException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ protected static final void throwError(int code, String info, Throwable cause) {
+ String message = "";
+ if (info == null) info = "";
+ switch(code) {
+ case ERROR_INVALID_INPUT:
+ message = ZestErrorMessages.ERROR_INVALID_INPUT;
+ break;
+ case ERROR_CANNOT_SET_STYLE:
+ message = ZestErrorMessages.ERROR_CANNOT_SET_STYLE;
+ break;
+ case ERROR_INVALID_STYLE:
+ message = ZestErrorMessages.ERROR_INVALID_STYLE;
+ break;
+ default:
+ message = info;
+ break;
+ }
+ throw new ZestException(message + ":" + info, cause);
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestPlugin.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestPlugin.java
new file mode 100644
index 0000000..a82fe1c
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestPlugin.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core;
+
+import org.eclipse.jface.resource.ColorRegistry;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The main plugin class to be used in the desktop.
+ * @author Ian Bull
+ */
+public class ZestPlugin extends AbstractUIPlugin {
+
+ //The shared instance.
+ private static ZestPlugin plugin;
+
+ //colors used by Zest.
+ private ColorRegistry colors;
+
+ /**
+ * The constructor.
+ */
+ public ZestPlugin() {
+ plugin = this;
+ }
+
+ /**
+ * This method is called upon plug-in activation
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ addImage(IZestImageConstants.TREE_HANGING_ICON, getImageDescriptor(IZestImageConstants.TREE_HANGING_ICON).createImage());
+ addImage(IZestImageConstants.TREE_NORMAL_ICON, getImageDescriptor(IZestImageConstants.TREE_NORMAL_ICON).createImage());
+ addImage(IZestImageConstants.TREE_HANGING_INVERSE_ICON, getImageDescriptor(IZestImageConstants.TREE_HANGING_INVERSE_ICON).createImage());
+ addImage(IZestImageConstants.TREE_NORMAL_INVERSE_ICON, getImageDescriptor(IZestImageConstants.TREE_NORMAL_INVERSE_ICON).createImage());
+
+ }
+
+ /**
+ * This method is called when the plug-in is stopped
+ */
+ public void stop(BundleContext context) throws Exception {
+ super.stop(context);
+ plugin = null;
+ }
+
+ /**
+ * Returns the shared instance.
+ */
+ public static ZestPlugin getDefault() {
+ return plugin;
+ }
+
+ /**
+ * Returns an image descriptor for the image file at the given
+ * plug-in relative path.
+ *
+ * @param path the path
+ * @return the image descriptor
+ */
+ public static ImageDescriptor getImageDescriptor(String path) {
+ return AbstractUIPlugin.imageDescriptorFromPlugin("org.eclipse.mylar.zest.core", path);
+ }
+
+ public void addImage( String key, Image image ) {
+ super.createImageRegistry();
+ getImageRegistry().put(key, image);
+ }
+
+ public void removeImage( String key ) {
+ super.createImageRegistry();
+ getImageRegistry().remove(key);
+ }
+
+ public Image getImage( String key ) {
+ super.createImageRegistry();
+ return getImageRegistry().get(key);
+ }
+
+
+ /**
+ * Gets the color registered for the given key. Keys can be found in
+ * org.eclipse.mylar.zest.core.IZestColorConstants.
+ * @param key the key used to reference the color.
+ * @return the color, or null if no color can be found.
+ * @see org.eclipse.mylar.zest.core.IZestColorConstants.
+ */
+ //@tag zest(bug(151332-Colors)) : resolution
+ public Color getColor(String key) {
+ if (colors == null) {
+ Display display = getWorkbench().getDisplay();
+ colors = new ColorRegistry(display);
+ populateColors(colors);
+ }
+ return colors.get(key);
+ }
+
+ /**
+ * Sends an error from the ZestPlugin.
+ * @param code the error code.
+ * @see #ZestException
+ */
+ public static void error(int code) {
+ ZestException.throwError(code, "", null);
+ }
+
+ private void populateColors(ColorRegistry colors) {
+ colors.put(IZestColorConstants.LIGHT_BLUE, new RGB(216, 228, 248));
+ colors.put(IZestColorConstants.DARK_BLUE, new RGB(1, 70, 122));
+ colors.put(IZestColorConstants.GREY_BLUE, new RGB(139, 150, 171));
+ colors.put(IZestColorConstants.LIGHT_BLUE_CYAN, new RGB(213, 243, 255));
+ colors.put(IZestColorConstants.LIGHT_YELLOW, new RGB(255, 255, 206));
+ colors.put(IZestColorConstants.GRAY, new RGB(128, 128, 128));
+ colors.put(IZestColorConstants.LIGHT_GRAY, new RGB(220, 220, 220));
+ colors.put(IZestColorConstants.BLACK, new RGB(0,0,0));
+ colors.put(IZestColorConstants.RED, new RGB(255,0,0));
+ colors.put(IZestColorConstants.DARK_RED, new RGB(127,0,0));
+ colors.put(IZestColorConstants.ORANGE, new RGB(255, 196, 0));
+ colors.put(IZestColorConstants.YELLOW, new RGB(255,255,0));
+ colors.put(IZestColorConstants.GREEN, new RGB(0,255,0));
+ colors.put(IZestColorConstants.DARK_GREEN, new RGB(0,127,0));
+ colors.put(IZestColorConstants.LIGHT_GREEN, new RGB(96,255,96));
+ colors.put(IZestColorConstants.CYAN, new RGB(0,255,255));
+ colors.put(IZestColorConstants.BLUE, new RGB(0,0,255));
+ colors.put(IZestColorConstants.DARK_BLUE, new RGB(0,0,127));
+ colors.put(IZestColorConstants.WHITE, new RGB(255,255,255));
+ colors.put(IZestColorConstants.EDGE_WEIGHT_0, new RGB(192, 192, 255));
+ colors.put(IZestColorConstants.EDGE_WEIGHT_01, new RGB(64, 128, 225));
+ colors.put(IZestColorConstants.EDGE_WEIGHT_02, new RGB(32, 32, 128));
+ colors.put(IZestColorConstants.EDGE_WEIGHT_03, new RGB(0, 0, 128));
+ colors.put(IZestColorConstants.EDGE_DEFAULT, new RGB(64, 64, 128));
+ colors.put(IZestColorConstants.EDGE_HIGHLIGHT, new RGB(192, 32, 32));
+ colors.put(IZestColorConstants.DISABLED,new RGB(230, 240, 250));
+
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestStyles.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestStyles.java
new file mode 100644
index 0000000..a78d029
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/ZestStyles.java
@@ -0,0 +1,295 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core;
+
+import org.eclipse.mylar.zest.core.viewers.IConnectionStyleBezierExtension;
+
+/**
+ * Style constants used in Zest.
+ *
+ * @author Chris Callendar
+ */
+public final class ZestStyles {
+
+ /**
+ * A constant known to be zero (0), used in operations which
+ * take bit flags to indicate that "no bits are set".
+ */
+ //@tag style(graph)
+ public static final int NONE = 0;
+
+ /**
+ * Style constant indicating if marquee selection (multiple selection)
+ * is allowed.
+ */
+ //@tag style(graph)
+ public static final int MARQUEE_SELECTION = 1 << 0;
+
+ /**
+ * Style constant indicating if panning (moving root edit part canvas) is allowed.
+ * You cannot have marquee selection and panning enabled at the same time.
+ */
+ //@tag style(graph)
+ public static final int PANNING = 1 << 1;
+
+ /**
+ * Style constant for nested graphs which indicates that real zooming
+ * will be done when navigating in or out of nodes.
+ */
+ //@tag style(graph(later))
+ public static final int ZOOM_REAL = 1 << 5;
+
+ /**
+ * Style constant for nested graphs which indicates that expand and collapse zooming
+ * will be done when navigating in or out of nodes. This is not real zooming, instead
+ * the nodes will expand to fill the area giving the impression of zooming.
+ */
+ //@tag style(graph(later))
+ public static final int ZOOM_EXPAND = 1 << 6;
+
+ /**
+ * Style constant for nested graphs which indicates that fake zooming
+ * will be done when navigating in or out of nodes. This consists of a
+ * dotted rectangle expanding to fill the area when zooming in, or a rectangle shrinking
+ * down to the size of the node.
+ */
+ //@tag style(graph(later))
+ public static final int ZOOM_FAKE = 1 << 7;
+
+
+ /**
+ * A style constant which indicates that nodes aren't
+ * allowed to overlap. Note: this is a hint.
+ */
+ //@tag style(graph(hint))
+ public static final int NO_OVERLAPPING_NODES = 1 << 10;
+
+ /**
+ * Style constant indicating whether figures can be moved outside the bounding
+ * rectangle. If enforce bounds is not set then scrollbars will probably be used.
+ */
+ //@tag style(graph)
+ public static final int ENFORCE_BOUNDS = 1 << 11;
+
+ /**
+ * Style constant indicating that invisible nodes should be ignored for layouts.
+ */
+ //@tag style.graph
+ public static final int IGNORE_INVISIBLE_LAYOUT = 1 << 12;
+
+ /**
+ * Style constant indicating if the selected node's neighbors
+ * should be highlighted. Note: this is a node-level style. It should not
+ * be applied to graph views during construction.
+ * @see
+ */
+ //@tag style(node)
+ public static final int NODES_HIGHLIGHT_ADJACENT = 1 << 1;
+
+ /**
+ * Style constant indicating that node labels should be cached.
+ * This is important under GTK+ because font drawing is slower than Windows.
+ */
+ public static final int NODES_CACHE_LABEL = 1 << 2;
+
+
+
+ /**
+ * Style indicating that connections should show their direction by default.
+ */
+ //@tag style(edge) : use a new name... this may be useful later for algorithms.
+ public static final int CONNECTIONS_DIRECTED = 1 << 5;
+
+ /**
+ * Style indicating that the arrow indicating the direction of the connection
+ * should be open.
+ */
+ //@tag style(edge)
+ public static final int CONNECTIONS_OPEN = 1 << 4;
+
+
+ /**
+ * Style indicating that lables should be placed in the middle of connections.
+ * This is the default.
+ */
+ //@tag style.arcs
+ //@tag zest.bug.160368-ConnectionAlign.fix
+ public static final int CONNECTIONS_VALIGN_MIDDLE = 1 << 11;
+
+ /**
+ * Style indicating that labels should be placed above connections.
+ */
+ //@tag style.arcs
+// @tag zest.bug.160368-ConnectionAlign.fix
+ public static final int CONNECTIONS_VALIGN_TOP = 1 << 12;
+
+ /**
+ * Style indicating that labels should be placed below connections.
+ */
+ //@tag style.arcs
+// @tag zest.bug.160368-ConnectionAlign.fix
+ public static final int CONNECTIONS_VALIGN_BOTTOM = 1 << 13;
+
+ /**
+ * "Horizontal" alignment constant. Labels should be placed at the beginning
+ * of the line. Figures will be anchored so that they have one end at the
+ * beginning of the connection, not so that they are centered at the start
+ * point. Which end of the figure is placed at that point will depend
+ * on the direction of the first two points.
+ */
+ public static final int CONNECTIONS_HALIGN_START = 1 << 14;
+
+ /**
+ * "Horizontal" alignment constant. Labels should be placed at the end of
+ * the line. Figures will be anchored so that they have one end at the
+ * beginning of the connection, not so that they are centered at the end
+ * point. Which end of the figure is placed at that point will depend
+ * on the direction of the last two points.
+ */
+ public static final int CONNECTIONS_HALIGN_END = 1 << 15;
+
+ /**
+ * "Horizontal" alignment constant. Figures should be placed in the center
+ * of the points on the line.
+ */
+ public static final int CONNECTIONS_HALIGN_CENTER = 1 << 16;
+
+ /**
+ * "Horizontal" alignment constant. Labels should be centered between the
+ * first two points on the connection. For connections with only two points,
+ * this will behave the same as CENTER.
+ */
+ public static final int CONNECTIONS_HALIGN_CENTER_START = 1 << 17;
+ /**
+ * "Horizontal" alignment constant. Labels should be centered between the
+ * last two points on the connection. For connections with only two points,
+ * this will behave the same as CENTER.
+ */
+ public static final int CONNECTIONS_HALIGN_CENTER_END = 1 << 18;
+ /**
+ * Style constant to indicate that connections between nodes should be curved
+ * by default. A connection cannot by styled with any of CONNECTIONS_CURVED,
+ * CONNECTIONS_STRAIGHT, or CONNECTIONS_BEZIER at the same time.
+ */
+ //@tag style(arcs)
+ public static final int CONNECTIONS_CURVED = 1 << 1;
+
+ /**
+ * Style constant to indicate that connections between nodes should be straight by
+ * default. A connection cannot by styled with any of CONNECTIONS_CURVED,
+ * CONNECTIONS_STRAIGHT, or CONNECTIONS_BEZIER at the same time.
+ */
+ //@tag style(arcs)
+ public static final int CONNECTIONS_STRAIGHT = 1 << 0;
+
+ /**
+ * Style constant to indicate that connections should be drawn with solid
+ * lines (this is the default).
+ */
+// @tag style(arcs)
+ public static final int CONNECTIONS_SOLID = 1 << 6;
+
+ /**
+ * Style constant to indicate that connections should be drawn with dashed
+ * lines.
+ */
+// @tag style(arcs)
+ public static final int CONNECTIONS_DASH = 1 << 7;
+
+ /**
+ * Style constant to indicate that connections should be drawn with
+ * dotted lines.
+ */
+// @tag style(arcs)
+ public static final int CONNECTIONS_DOT = 1 << 8;
+
+ /**
+ * Style constant to indicate that connections should be drawn with
+ * dash-dotted lines.
+ */
+// @tag style(arcs)
+ public static final int CONNECTIONS_DASH_DOT = 1 << 9;
+
+ /**
+ * Style constant to indicate that connections between nodes should be bezier-S
+ * shaped. A connection cannot by styled with any of CONNECTIONS_CURVED,
+ * CONNECTIONS_STRAIGHT, or CONNECTIONS_BEZIER at the same time.
+ *
+ * Bezier curves are defined by a set of four points: two point in the layout
+ * (start and end), and two related control points (also start and end). The
+ * control points are defined relative to their corresponding layout point.
+ * This definition includes an angle between the layout point and the line
+ * between the two layout points, as well as a ratio distance from the corresponding
+ * layout point. The ratio distance is defined as a fraction between 0 and 1
+ * of the distance between the two layout points. Using this definition
+ * allows bezier curves to have a consistant look regardless of the actual
+ * positions of the nodes in the layouts.
+ *
+ * @see IConnectionStyleBezierExtension
+ * @see IConnectionEntityStyleBezierExtension
+ */
+ //@tag style(arcs)
+ public static final int CONNECTIONS_BEZIER = 1 << 2;
+
+ /**
+ * Style constant to indicate that the Tree Graph Viewer should be
+ * compressed.
+ * @tag style(tree_graph)
+ */
+ public static final int TREE_GRAPH_COMPRESS = 1 << 0;
+
+
+ /**
+ * Style constant to indicate that the Tree Graph Viewer should use
+ * a default "hanging" layout instead of the bushy "Normal" tree layout.
+ * @tag style(tree_graph)
+ */
+ public static final int TREE_GRAPH_HANGING_LAYOUT = 1 << 1;
+
+ /**
+ * Bitwise ANDs the styleToCheck integer with the given style.
+ * @param style
+ * @param styleToCheck
+ * @return boolean if styleToCheck is part of the style
+ */
+ public static boolean checkStyle(int style, int styleToCheck) {
+ return ((style & styleToCheck) == styleToCheck);
+ }
+
+ /**
+ * Validates the given style for connections to see if it is legal. Returns
+ * false if not.
+ * @param style the style to check.
+ * @return true iff the given style is legal.
+ */
+ public static boolean validateConnectionStyle(int styleToValidate) {
+ int style = styleToValidate;
+ int illegal = CONNECTIONS_CURVED | CONNECTIONS_STRAIGHT | CONNECTIONS_BEZIER;
+ style &= illegal;
+ int rightBit = style & (-style);
+ boolean okay = (style == rightBit);
+ if (!okay) return okay;
+
+ illegal = CONNECTIONS_DASH_DOT | CONNECTIONS_DASH | CONNECTIONS_DOT | CONNECTIONS_SOLID;
+ style = styleToValidate;
+ style &=illegal;
+ rightBit = style & (-style);
+ okay = (style == rightBit);
+ if (!okay) return okay;
+
+ //@tag zest.bug.160368-ConnectionAlign.fix : must check the connections to make sure that there isnt' an illegal combination of alignments.
+ illegal = CONNECTIONS_VALIGN_BOTTOM | CONNECTIONS_VALIGN_MIDDLE | CONNECTIONS_VALIGN_TOP;
+ style = styleToValidate;
+ style &=illegal;
+ rightBit = style & (-style);
+ return (style == rightBit);
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/AligningBendpointLocator.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/AligningBendpointLocator.java
new file mode 100644
index 0000000..2da18f3
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/AligningBendpointLocator.java
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.AbstractLocator;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.PositionConstants;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+
+/**
+ * A locator that finds the middle of a connection based on the bendpoints.
+ * @author Del Myers
+ *
+ */
+//@tag zest(bug(154391-ArcEnds(fix))) : use this locator to ensure that labels will be in the middle of connections.
+//@tag zest.bug.160368-ConnectionAlign : replaces MidBenpointLocator in order to have finer control over alignment.
+public class AligningBendpointLocator extends AbstractLocator {
+ /**
+ * "Vertical" alignment contstant. Figures should be placed in the middle
+ * of the line.
+ */
+ public static final int MIDDLE = 0;
+ /**
+ * "Vertical" alignment constant. Figures should be placed above the line.
+ */
+ public static final int ABOVE = 1;
+ /**
+ * "Vertical" alignment constant. Figures should be placed below the line.
+ */
+ public static final int BELOW = 2;
+
+ /**
+ * "Horizontal" alignment constant. Figures should be placed in the center
+ * of the points on the line.
+ */
+ public static final int CENTER = 0;
+
+ /**
+ * "Horizontal" alignment constant. Figures should be placed at the beginning
+ * of the line. Figures will be anchored so that they have one end at the
+ * beginning of the connection, not so that they are centered at the start
+ * point. Which end of the figure is placed at that point will depend
+ * on the direction of the first two points.
+ */
+ public static final int BEGINNING = 1;
+
+ /**
+ * "Horizontal" alignment constant. Figures should be placed at the end of
+ * the line. Figures will be anchored so that they have one end at the
+ * beginning of the connection, not so that they are centered at the end
+ * point. Which end of the figure is placed at that point will depend
+ * on the direction of the last two points.
+ */
+ public static final int END = 2;
+
+ /**
+ * "Horizontal" alignment constant. Figures should be centered between the
+ * first two points on the connection. For connections with only two points,
+ * this will behave the same as CENTER.
+ */
+ public static final int CENTER_BEGINNING = 3;
+
+ /**
+ * "Horizontal" alignment constant. Figures should be centered between the
+ * last two points on the connection. For connections with only two points,
+ * this will behave the same as CENTER.
+ */
+ public static final int CENTER_END = 4;
+ private int horizontal;
+ private int vertical;
+ private Connection connection;
+ /**
+ * @param connection
+ */
+ public AligningBendpointLocator(Connection connection) {
+ this(connection, CENTER, MIDDLE);
+ }
+
+ public AligningBendpointLocator(Connection connection, int horizontal, int vertical) {
+ this.connection = connection;
+ this.horizontal = horizontal;
+ this.vertical = vertical;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.ConnectionLocator#getReferencePoint()
+ */
+ protected Point getReferencePoint() {
+ PointList points = getConnection().getPoints();
+ Point p = points.getMidpoint().getCopy();
+ PointList tempPoints = new PointList();
+ switch (horizontal) {
+ case BEGINNING:
+ p = points.getFirstPoint().getCopy();
+ break;
+ case END:
+ p = points.getLastPoint().getCopy();
+ break;
+ case CENTER_BEGINNING:
+ tempPoints.addPoint(points.getFirstPoint().getCopy());
+ tempPoints.addPoint(points.getPoint(1).getCopy());
+ p = tempPoints.getMidpoint().getCopy();
+ break;
+ case CENTER_END:
+ tempPoints = new PointList();
+ int s = points.size();
+ tempPoints.addPoint(points.getLastPoint().getCopy());
+ tempPoints.addPoint(points.getPoint(s-2).getCopy());
+ p = tempPoints.getMidpoint().getCopy();
+ case CENTER:
+ default:
+ p = points.getMidpoint().getCopy();
+ }
+ return p;
+ }
+ /**
+ * Recalculates the position of the figure and returns the updated bounds.
+ * @param target The figure to relocate
+ */
+ public void relocate(IFigure target) {
+ Dimension prefSize = target.getPreferredSize();
+ Point center = getReferencePoint();
+ calculatePosition();
+ //@tag zest(bug(GEFProblem)) : there seems to be a bug in GEF that if the following is done, then labels get printed in the wrong location
+ //target.translateToRelative(center);
+ target.setBounds(getNewBounds(prefSize, center));
+ }
+
+ /**
+ * Translates the center point depending on the horizontal and veritical
+ * alignments based on the given bounds.
+ * @param center
+ */
+ private void calculatePosition() {
+ PointList points = getConnection().getPoints();
+ int position = 0;
+ switch(horizontal) {
+ case BEGINNING:
+ Point first = points.getFirstPoint();
+ Point next = points.getPoint(1);
+ if (first.x <= next.x)
+ position |= PositionConstants.EAST;
+ else
+ position |= PositionConstants.WEST;
+ break;
+ case END:
+ Point last = points.getLastPoint();
+ int s = points.size();
+ Point before = points.getPoint(s-1);
+ if (last.x <= before.x)
+ position |= PositionConstants.EAST;
+ else
+ position |= PositionConstants.WEST;
+ break;
+ }
+ if (position == 0) position = PositionConstants.CENTER;
+ switch (vertical) {
+ case ABOVE:
+ position |= PositionConstants.NORTH;
+ break;
+ case BELOW:
+ position |= PositionConstants.SOUTH;
+ break;
+ case MIDDLE:
+ position |= PositionConstants.MIDDLE;
+ }
+ setRelativePosition(position);
+
+ }
+
+ /**
+ * @return the horizontal alignment.
+ */
+ public int getHorizontalAlignment() {
+ return horizontal;
+ }
+
+ /**
+ * @param horizontal the horizontal alignment to set. One of CENTER,
+ * BEGINNING, END, CENTER_BEGINNING, or CENTER_END.
+ */
+ public void setHorizontalAlignment(int horizontal) {
+ this.horizontal = horizontal;
+ }
+
+ /**
+ * @param vertical the vertical alignment to set. One of ABOVE, MIDDLE, or
+ * BELOW.
+ */
+ public void setVerticalAlginment(int vertical) {
+ this.vertical = vertical;
+ }
+
+ /**
+ * @return the vertical alginment.
+ */
+ public int getVerticalAlignment() {
+ return vertical;
+ }
+
+ /**
+ * @return the connection
+ */
+ public Connection getConnection() {
+ return connection;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/Arc.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/Arc.java
new file mode 100644
index 0000000..9f65eb1
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/Arc.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.Shape;
+import org.eclipse.draw2d.geometry.Rectangle;
+
+/**
+ * A shape representation of an arc, so that arcs can be used in editparts.
+ * @tag bug(114452)
+ * @author Del Myers
+ */
+public class Arc extends Shape {
+ private int offset;
+ private int length;
+ /**
+ * The closure type for an arc closed by drawing straight line
+ * segments from the start of the arc segment to the center
+ * of the full ellipse and from that point to the end of the arc segment.
+ */
+ public final static int PIE = 2;
+ /**
+ * There are times when the clipping bounds and the bounds of the arc will be different
+ * allow for this.
+ */
+ private Rectangle arcBounds;
+
+ public Arc() {
+ super();
+ setBounds(new Rectangle(0,0,0,0));
+ arcBounds = new Rectangle(0,0,0,0);
+ }
+
+
+ public Arc(Rectangle r, int offset, int length) {
+ super();
+ setBounds(r);
+ arcBounds = new Rectangle(r);
+ this.offset = offset;
+ this.length = length;
+ }
+
+ public Arc(int x, int y, int width, int height, int offset, int length) {
+ this (new Rectangle(x, y, width, height), offset, length);
+ }
+
+
+ protected void fillShape(Graphics graphics) {
+ //graphics.setBackgroundColor(PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_BLUE));
+ graphics.fillArc(getArcBounds(), offset, length);
+ /*for (int i = 0; i < 1000; i++) {
+ int x = (int)(Math.random()*getBounds().width+getBounds().x);
+ int y = (int)(Math.random()*getBounds().height+getBounds().x);
+ if (containsPoint(x,y)) {
+ graphics.drawPoint(x, y);
+ }
+ }*/
+ }
+
+ protected void outlineShape(Graphics graphics) {
+ graphics.drawArc(getArcBounds(), offset, length);
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ }
+
+ //@tag taken : from java.awt.geom.Arc2D
+ public boolean containsPoint(int x, int y) {
+ // Normalize the coordinates compared to the ellipse
+ // having a center at 0,0 and a radius of 0.5.
+ Rectangle r = getArcBounds();
+ double ellw = r.width;
+ if (ellw <= 0.0) {
+ return false;
+ }
+ double normx = (x - r.x) / ellw - 0.5;
+ double ellh = r.height;
+ if (ellh <= 0.0) {
+ return false;
+ }
+ double normy = (y - r.y) / ellh - 0.5;
+ double distSq = (normx * normx + normy * normy);
+ if (distSq >= 0.25) {
+ return false;
+ }
+ double angExt = Math.abs(getLength());
+ if (angExt >= 360.0) {
+ return true;
+ }
+ boolean inarc = containsAngle(-Math.toDegrees(Math.atan2(normy,
+ normx)));
+ return inarc;
+
+ /*// CHORD and OPEN behave the same way
+ if (inarc) {
+ if (angExt >= 180.0) {
+ return true;
+ }
+ // point must be outside the "pie triangle"
+ } else {
+ if (angExt <= 180.0) {
+ return false;
+ }
+ // point must be inside the "pie triangle"
+ }
+ // The point is inside the pie triangle iff it is on the same
+ // side of the line connecting the ends of the arc as the center.
+ double angle = Math.toRadians(-getAngleStart());
+ double x1 = Math.cos(angle);
+ double y1 = Math.sin(angle);
+ angle += Math.toRadians(-getAngleExtent());
+ double x2 = Math.cos(angle);
+ double y2 = Math.sin(angle);
+ boolean inside = (Line2D.relativeCCW(x1, y1, x2, y2, 2*normx, 2*normy) *
+ Line2D.relativeCCW(x1, y1, x2, y2, 0, 0) >= 0);
+ return inarc ? !inside : inside;
+ */
+ }
+
+ public boolean containsAngle(double angle) {
+ double angExt = getLength();
+ boolean backwards = (angExt < 0.0);
+ if (backwards) {
+ angExt = -angExt;
+ }
+ if (angExt >= 360.0) {
+ return true;
+ }
+ angle = normalizeDegrees(angle) - normalizeDegrees(getOffset());
+ if (backwards) {
+ angle = -angle;
+ }
+ if (angle < 0.0) {
+ angle += 360.0;
+ }
+
+
+ return (angle >= 0.0) && (angle < angExt);
+ }
+
+ /*
+ * Normalizes the specified angle into the range -180 to 180.
+ */
+ static double normalizeDegrees(double angle) {
+ if (angle > 180.0) {
+ if (angle <= (180.0 + 360.0)) {
+ angle = angle - 360.0;
+ } else {
+ angle = Math.IEEEremainder(angle, 360.0);
+ // IEEEremainder can return -180 here for some input values...
+ if (angle == -180.0) {
+ angle = 180.0;
+ }
+ }
+ } else if (angle <= -180.0) {
+ if (angle > (-180.0 - 360.0)) {
+ angle = angle + 360.0;
+ } else {
+ angle = Math.IEEEremainder(angle, 360.0);
+ // IEEEremainder can return -180 here for some input values...
+ if (angle == -180.0) {
+ angle = 180.0;
+ }
+ }
+ }
+ return angle;
+ }
+
+ public Rectangle getArcBounds() {
+ return arcBounds;
+ }
+
+ protected void setArcBounds(Rectangle bounds) {
+ this.arcBounds = bounds;
+ }
+
+}
+
+
+
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ArcConnection.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ArcConnection.java
new file mode 100644
index 0000000..80e6c25
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ArcConnection.java
@@ -0,0 +1,609 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.AbstractRouter;
+import org.eclipse.draw2d.AnchorListener;
+import org.eclipse.draw2d.ArrowLocator;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.draw2d.ConnectionAnchor;
+import org.eclipse.draw2d.ConnectionLocator;
+import org.eclipse.draw2d.ConnectionRouter;
+import org.eclipse.draw2d.DelegatingLayout;
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.RotatableDecoration;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+
+/**
+ *
+ * @author Del Myers
+ *
+ * A connection between two anchor points. This connection is drawn as an arc,
+ * defined as the circular arc with the chord (ax, ay) - (bx, by) (where a and b
+ * are the anchors) and a depth d defined as the maximum distance from any point on
+ * the chord (i.e. a vector normal to the chord with magnitude d).
+ *
+ * @tag bug(114452)
+ */
+public class ArcConnection extends Arc implements Connection, AnchorListener {
+
+ /**
+ * Routes Connections directly from the source anchor to the target anchor with no
+ * bendpoints in between.
+ */
+ static class NullConnectionRouter
+ extends AbstractRouter
+ {
+
+ /**
+ * Constructs a new NullConnectionRouter.
+ */
+ NullConnectionRouter() { }
+
+ /**
+ * Routes the given Connection directly between the source and target anchors.
+ * @param conn the connection to be routed
+ */
+ public void route(Connection conn) {
+ PointList points = conn.getPoints();
+ points.removeAllPoints();
+ Point p;
+ p = getStartPoint(conn);
+ conn.translateToRelative(p = getStartPoint(conn));
+ points.addPoint(p);
+ p = getEndPoint(conn);
+ conn.translateToRelative(p = getEndPoint(conn));
+ points.addPoint(p);
+ conn.setPoints(points);
+ }
+
+ }
+
+ /** The depth of the arc */
+ private int depth;
+
+ //just for reference purposes: a point on the radius of the arc, halfway
+ //between the endpoints.
+ private double px;
+ private double py;
+ //decorations for the start and end of the arc.
+ private RotatableDecoration endArrow;
+ private RotatableDecoration startArrow;
+
+ //list of points in the arc. This will include just the end points.
+ private PointList pointList;
+
+ //method for routing connections.
+ private ConnectionRouter connectionRouter;
+
+ //source and destination anchors.
+ private ConnectionAnchor source;
+ private ConnectionAnchor dest;
+
+ //default connection router.
+ private static ConnectionRouter NullConnectionRouter = new NullConnectionRouter();
+
+
+ public ArcConnection() {
+ super();
+ this.pointList = new PointList();
+ connectionRouter = NullConnectionRouter;
+ this.depth = 0;
+ pointList.addPoint(new Point(0,0));
+ pointList.addPoint(new Point(100,100));
+ setLayoutManager(new DelegatingLayout());
+ }
+
+ public ConnectionRouter getConnectionRouter() {
+ return connectionRouter;
+ }
+
+ /**
+ * Hooks the source and target anchors.
+ * @see Figure#addNotify()
+ */
+ public void addNotify() {
+ super.addNotify();
+ hookSourceAnchor();
+ hookTargetAnchor();
+ }
+
+ /**
+ * Returns the list of points in this arc. Note: the points can't be changed using
+ * setPoints, nor by changing the PointList. The PointList is fixed within this connection.
+ */
+ public final PointList getPoints() {
+ return pointList.getCopy();
+ }
+
+ /**
+ * Returns the bounds which holds all the points in this. Returns any
+ * previously existing bounds, else calculates by unioning all the children's
+ * dimensions.
+ * @return the bounds
+ */
+ //@tag bug(154176-ConnectionClip(fix)) : add children to the bounds.
+ public Rectangle getBounds() {
+ if (bounds == null) {
+ super.getBounds();
+ for (int i = 0; i < getChildren().size(); i++) {
+ IFigure child = (IFigure)getChildren().get(i);
+ bounds.union(child.getBounds());
+ }
+ }
+ return bounds;
+ }
+
+ public Object getRoutingConstraint() {
+ if (this.connectionRouter != null)
+ return connectionRouter.getConstraint(this);
+ return null;
+ }
+
+ public ConnectionAnchor getSourceAnchor() {
+ return source;
+ }
+
+ public ConnectionAnchor getTargetAnchor() {
+ return dest;
+ }
+
+ public void setConnectionRouter(ConnectionRouter router) {
+ if (this.connectionRouter == router) return;
+ ConnectionRouter oldRouter = connectionRouter;
+ if (router == null) router = NullConnectionRouter;
+ this.connectionRouter = router;
+ firePropertyChange(Connection.PROPERTY_CONNECTION_ROUTER, oldRouter, this.connectionRouter);
+ }
+
+
+
+ /**
+ * Updates the arc for when point or depth changes occur.
+ */
+ protected void updateArc(Point src, Point dest, int depth) {
+ double workingDepth = depth;
+ this.depth = depth;
+ //get cartesian coordinates: they are easier to work with.
+ //cartesian coordinates are easier to use for translations.
+ double x1 = src.x;
+ double y1 = -src.y;
+ double x2 = dest.x;
+ double y2 = -dest.y;
+ // the center of the chord
+ double cartChordX = (x2 + x1)/2;
+ double cartChordY = (y2 + y1)/2;
+
+ //special cases
+ if (depth == 0) {
+ doStraightLine(src, dest);
+ return;
+ } else if (src.equals(dest)) {
+ doCircle(src);
+ return;
+ }
+
+
+ if (x1 >= x2) {
+ workingDepth = -workingDepth;
+ }
+
+ double chordLength = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
+ if (Math.abs(workingDepth) >= chordLength/2) {
+ workingDepth = (chordLength/3)*(workingDepth/Math.abs(workingDepth));
+ }
+ double r = (((chordLength/2)*(chordLength/2)) + ((double)workingDepth*workingDepth))/((double)2*workingDepth);
+
+ //assume that the chord is perpendicular to the origin
+ //to find the angle of the chord.
+ double cartCenterX = 0;
+ double cartCenterY = 0;
+
+ //Find a vector normal to the chord. This will be used for translating the
+ //circle back to screen coordinates.
+ double chordNormal = 0;
+
+ if (Math.abs(x1 - x2) <= .001) {
+ //slope of 0. NaN is easier to detect than 0.
+ chordNormal = Double.NaN;
+ } else if (Math.abs(y1-y2) <= 0.001) {
+ //infinite slope.
+ chordNormal = Double.POSITIVE_INFINITY;
+ } else {
+ chordNormal = -(double)(y2 - y1)/(x2 - x1);
+ }
+
+ double th1;
+ if (Double.isNaN(chordNormal)) {
+ cartCenterX = (y1 > y2) ? (cartChordX-r+(workingDepth)) : (cartChordX+r-(workingDepth));
+ cartCenterY = cartChordY;
+ this.px = cartChordX + ((y1 > y2) ? (workingDepth) : (-workingDepth));
+ this.py = (-cartChordY);
+ th1 = Math.PI/2;
+ } else if (Double.isInfinite(chordNormal)) {
+ cartCenterX = cartChordX;
+ cartCenterY = cartChordY+r-(workingDepth);
+ this.px = cartChordX;
+ this.py = (-cartChordY)+workingDepth;
+ th1 = 0.0;
+ } else {
+ //assume that the center of the chord is on the origin.
+ th1 = Math.atan(chordNormal);
+ cartCenterX = (r-(double)(workingDepth))*Math.sin(th1)+cartChordX;//cartChordX+r -depth;
+ cartCenterY = (r-(double)(workingDepth))*Math.cos(th1)+cartChordY;//cartChordY+r-depth;
+ this.px = (-workingDepth)*Math.sin(th1)+cartChordX;
+ this.py = -((-workingDepth)*Math.cos(th1)+cartChordY);
+ }
+
+ //update the points and depth
+ pointList.removeAllPoints();
+ pointList.addPoint(src);
+ pointList.addPoint(new Point(Math.round(px), Math.round(py)));
+ pointList.addPoint(dest);
+
+ //figure out the new angles
+ //translate the points to the center of the circle
+ double cartArcX1 = x1 - cartCenterX;
+ double cartArcY1 = y1 - cartCenterY;
+ double cartArcX2 = x2 - cartCenterX;
+ double cartArcY2 = y2 - cartCenterY;
+
+ double arcStart = angle360(cartArcX1, cartArcY1);
+ double arcEnd = angle360(cartArcX2, cartArcY2);
+ if (arcEnd < arcStart) {
+ arcEnd = arcEnd + 360;
+ }
+ double arcLength = arcEnd-arcStart;//findAngleDegrees(arcStart, arcEnd);
+ int padding = (chordLength > 100) ? 4 : 0;
+ if (depth < 0) padding = -padding;
+ setOffset((int)(Math.round(arcStart - padding)));
+ setLength((int)(Math.round(arcLength+padding*(chordLength/100))));
+
+ //update the bounds
+ double br = Math.abs(r);
+ double topx = cartCenterX - br;
+ double topy = cartCenterY + br;
+ double width = br*2;
+
+ Rectangle b = new Rectangle((int)x1, -(int)y1, (int)(px-x1), (int)(py+y1));
+ if (b.width < 0) {
+ b.width = -b.width;
+ b.x = b.x - b.width;
+ }
+ if (b.height < 0){
+ b.height = -b.height;
+ b.y = b.y-b.height;
+ }
+ Rectangle b2 = new Rectangle((int)x2, (int)-y2, (int)(px-x2), (int)(py+y2));
+ if (b2.width < 0) {
+ b2.width = -b2.width;
+ b2.x = b2.x - b2.width;
+ }
+ if (b2.height < 0){
+ b2.height = -b2.height;
+ b2.y = b2.y-b2.height;
+ }
+ int b3x = Math.min(b2.x, b.x);
+ int b3y = Math.min(b.y, b2.y);
+ arcEnd = arcEnd % 360;
+ int boundsTop = b3y;
+ int boundsLeft = b3x;
+ int boundsBottom = boundsTop + (b.height+b2.height);
+ int boundsRight = boundsLeft + (b.width+b2.width);
+ if (arcEnd >= 90 && (arcEnd-arcLength) <= 90) {
+ boundsTop = -(int)topy;
+ }
+ if (arcEnd >= 180 && (arcEnd - arcLength) <= 180) {
+ boundsLeft = (int)topx;
+ }
+ if ((arcEnd >= 270 && (arcEnd -arcLength) <= 270) || (arcEnd-arcLength+360 <= 270)) {
+ boundsBottom = (int)(width-topy);
+ }
+ if (arcEnd >=0 && (arcEnd-arcLength) <= 0){
+ boundsRight = (int)(topx + width);
+ }
+ Rectangle b3 = new Rectangle(boundsLeft-5, boundsTop-5, boundsRight-boundsLeft+10, boundsBottom-boundsTop+10);
+ setBounds(b3);
+ setArcBounds(new Rectangle((int)topx, -(int)topy, (int)width, (int)width));
+
+
+ //this.bounds = null;
+
+ }
+
+ //source and destination are the same. create a circle that passes through the point.
+
+ private void doCircle(Point src) {
+ setOffset(0);
+ setLength(360);
+ Rectangle circleBounds = new Rectangle(src.x - depth/2, src.y - depth, depth, depth);
+ setArcBounds(circleBounds);
+ setBounds(circleBounds);
+ pointList.removeAllPoints();
+ pointList.addPoint(src.getCopy());
+ pointList.addPoint(new Point(src.x, src.y-depth));
+ pointList.addPoint(src.getCopy());
+ }
+
+ private void doStraightLine(Point src, Point dest) {
+ int x1 = src.x;
+ int y1 = src.y;
+ int x2 = dest.x;
+ int y2 = dest.y;
+ //just a straight line.
+ if (x1 > x2) {
+ int t = x2;
+ x2 = x1;
+ x1 = t;
+ }
+ if (y1 > y2) {
+ int t = y2;
+ y2 = y1;
+ y1 = t;
+ }
+ Rectangle bounds = new Rectangle((int)x1-2, (int)y1-2, (int)(x2-x1)+4, (int)(y2-y1)+4);
+ setBounds(bounds);
+ setArcBounds(bounds);
+
+ pointList.removeAllPoints();
+ pointList.addPoint(src.getCopy());
+ pointList.addPoint(new Point((x1+x2)/2, (y1+y2)/2));
+ pointList.addPoint(dest.getCopy());
+
+ }
+
+ /**
+ * This implementation only pays attention to the first and the last points in the
+ * list because Arcs don't allow for bendpoints. The new point list will consist of
+ * the two end-points and a point that describes the depth of the arc.
+ */
+ public final void setPoints(PointList list) {
+ Point src = list.getFirstPoint();
+ Point dest = list.getLastPoint();
+ //create the new arc.
+ updateArc(src, dest, getDepth());
+
+ firePropertyChange(Connection.PROPERTY_POINTS, null, pointList);
+ repaint();
+
+ }
+
+ public int getDepth() {
+ return depth;
+ }
+
+ /**
+ * Sets the depth of the arc. This is defined as the maximum distance from the
+ * line that connects the source and target of this connection. The sign of the
+ * depth (+ve or -ve) will define which side of the line the arc will appear on.
+ * @param depth the depth of the arc.
+ */
+ public void setDepth(int depth) {
+ updateArc(getPoints().getFirstPoint(), getPoints().getLastPoint(), depth);
+ repaint();
+ }
+
+ /**
+ * Takes the polar coordinates x1, y1 and returns their angle between 0 and 360 degrees.
+ * @param x1 polar coordinate.
+ * @param y1 polar coordinate
+ * @return polar angle in 360 degrees.
+ */
+ protected double angle360(double x1, double y1) {
+ double theta = Math.toDegrees(Math.atan(y1/x1));
+ switch (findQuadrant(x1,y1)) {
+ case 1: return theta;
+ case 2:
+ case 3: return theta+180;
+ case 4: return theta+360;
+ default: return theta;
+ }
+ }
+
+
+ /**
+ *
+ * finds the shortest angle between the two given angles. Assumes that the angles
+ * are between 0-360 degrees.
+ *
+ */
+ protected double findAngleDegrees(double a1, double a2) {
+ double diff = a2-a1;
+ double diff2 = diff-360;
+ if (diff < 0) {
+ diff2 = 360+diff;
+ }
+ if (Math.abs(diff) < Math.abs(diff2)) {
+ return diff;
+ }
+ return diff2;
+ }
+
+
+ //find the quadrant, assume points are centered at 0,0
+ protected int findQuadrant (double x, double y) {
+ if (y > 0) {
+ if (x > 0) {
+ return 1;
+ } else {
+ return 2;
+ }
+ } else {
+ if (x > 0) {
+ return 4;
+ } else {
+ return 3;
+ }
+ }
+ }
+
+
+ public void setRoutingConstraint(Object cons) {
+ if (this.connectionRouter != null) connectionRouter.setConstraint(this, cons);
+ }
+
+ public void setSourceAnchor(ConnectionAnchor anchor) {
+ if (anchor == this.source) return;
+ unhookSourceAnchor();
+ this.source = anchor;
+ if (getParent()!=null) hookSourceAnchor();
+ revalidate();
+ }
+
+ public void setTargetAnchor(ConnectionAnchor anchor) {
+ if (anchor == this.dest) return;
+ unhookTargetAnchor();
+ this.dest = anchor;
+ if (getParent()!=null) hookTargetAnchor();
+ revalidate();
+ }
+
+ private void unhookSourceAnchor() {
+ if (getSourceAnchor() != null)
+ getSourceAnchor().removeAnchorListener(this);
+ }
+
+ private void unhookTargetAnchor() {
+ if (getTargetAnchor() != null)
+ getTargetAnchor().removeAnchorListener(this);
+ }
+
+ public void revalidate() {
+ super.revalidate();
+ if (connectionRouter != null)
+ connectionRouter.invalidate(this);
+ }
+
+
+ protected final void fillShape(Graphics graphics) {
+
+ }
+
+ protected void outlineShape(Graphics graphics) {
+ Color c = graphics.getForegroundColor();
+ graphics.setForegroundColor(c.getDevice().getSystemColor(SWT.COLOR_GRAY));
+ //graphics.drawOval(getBounds());
+ graphics.setForegroundColor(c);
+ Point source =getPoints().getFirstPoint();
+ Point target = getPoints().getLastPoint();
+ Rectangle bounds = getBounds();
+ if (depth == 0) {
+ graphics.drawLine(source, target);
+ return;
+ }
+ graphics.setClip(new Rectangle(bounds.x-1, bounds.y-1, bounds.width+2, bounds.height+2));
+ super.outlineShape(graphics);
+ }
+
+ /*public boolean containsPoint(int x, int y) {
+ if (depth == 0) {
+ //calculate for a straight line.
+ Point source = getPoints().getFirstPoint();
+ Point target = getPoints().getLastPoint();
+ double m = ((double)target.y - (double)source.y)/((double)target.x-(double)source.x);
+ double dy = m*(source.x-x) + y;
+ return (Math.abs(dy) <= .01);
+ }
+ if (!super.containsPoint(x, y))
+ return false;
+
+ Rectangle r = getArcBounds();
+ double ellw = r.width;
+ double normx = (x - r.x) / ellw - 0.5;
+ double ellh = r.height;
+ double normy = (y - r.y) / ellh - 0.5;
+ double distSq = (normx * normx + normy * normy);
+ return (Math.abs(1-distSq) <= .001);
+
+ }*/
+
+ public boolean containsPoint(int x, int y) {
+ return false;
+ }
+
+ private void hookSourceAnchor() {
+ if (getSourceAnchor() != null)
+ getSourceAnchor().addAnchorListener(this);
+ }
+
+ private void hookTargetAnchor() {
+ if (getTargetAnchor() != null)
+ getTargetAnchor().addAnchorListener(this);
+ }
+
+
+ public void anchorMoved(ConnectionAnchor anchor) {
+ revalidate();
+ }
+
+ /**
+ * Layouts this polyline. If the start and end anchors are present, the connection router
+ * is used to route this, after which it is laid out. It also fires a moved method.
+ */
+ public void layout() {
+ if (getSourceAnchor() != null && getTargetAnchor() != null)
+ connectionRouter.route(this);
+ Rectangle oldBounds = bounds;
+ super.layout();
+
+ if (!getBounds().contains(oldBounds)) {
+ getParent().translateToParent(oldBounds);
+ getUpdateManager().addDirtyRegion(getParent(), oldBounds);
+ }
+
+ repaint();
+ fireFigureMoved();
+ }
+
+ /**
+ * Sets the decoration to be used at the end of the {@link Connection}.
+ * @param dec the new target decoration
+ */
+ public void setTargetDecoration(RotatableDecoration dec) {
+ if (endArrow == dec)
+ return;
+ if (endArrow != null)
+ remove(endArrow);
+ endArrow = dec;
+ if (endArrow != null) {
+ if (depth != 0)
+ add(endArrow, new ArcLocator(this, ConnectionLocator.TARGET));
+ else
+ add(endArrow, new ArrowLocator(this,ConnectionLocator.TARGET));
+ }
+ }
+
+ /**
+ * Sets the decoration to be used at the start of the {@link Connection}.
+ * @param dec the new source decoration
+ * @since 2.0
+ */
+ public void setSourceDecoration(RotatableDecoration dec) {
+ if (startArrow == dec)
+ return;
+ if (startArrow != null)
+ remove(startArrow);
+ startArrow = dec;
+ if (startArrow != null) {
+ if (depth != 0)
+ add(startArrow, new ArcLocator(this, ConnectionLocator.SOURCE));
+ else
+ add(startArrow, new ArrowLocator(this,ConnectionLocator.SOURCE));
+ }
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ArcLocator.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ArcLocator.java
new file mode 100644
index 0000000..83371ae
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ArcLocator.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.Connection;
+import org.eclipse.draw2d.ConnectionLocator;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.RotatableDecoration;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+
+/**
+ * A locator for arcs, so that child figures can properly position themselves on arcs.
+ * @tag bug(114452)
+ * @author Del Myers
+ */
+class ArcLocator extends ConnectionLocator {
+
+
+ public ArcLocator(Connection connection, int location) {
+ super(connection, location);
+ }
+
+
+ public void relocate(IFigure target) {
+ if (!(target instanceof RotatableDecoration)) return;
+ if (!(getConnection() instanceof ArcConnection)) return;
+ Point alignmentPoint = getAlignementPoint();
+ ((RotatableDecoration)target).setLocation(alignmentPoint);
+ rotate((RotatableDecoration)target);
+ }
+
+ protected int getQuadrant(Point p) {
+ ArcConnection conn = (ArcConnection) getConnection();
+ int centerx = conn.getArcBounds().x + conn.getArcBounds().width/2;
+ int centery = conn.getArcBounds().y + conn.getArcBounds().height/2;
+ if (p.y > centery) {
+ if (p.x > centerx) {
+ return 4;
+ } else {
+ return 3;
+ }
+ } else {
+ if (p.x < centerx) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+ }
+
+ protected Point getAlignementPoint() {
+ Point point = null;
+ ArcConnection connection = (ArcConnection)getConnection();
+ if (getAlignment() == SOURCE) {
+ point = connection.getPoints().getFirstPoint().getCopy();
+ } else if (getAlignment() == TARGET) {
+ point = connection.getPoints().getLastPoint().getCopy();
+ } else {
+ point = connection.getPoints().getMidpoint().getCopy();
+ }
+ return point;
+ }
+ protected void rotate(RotatableDecoration target) {
+ ArcConnection connection = (ArcConnection)getConnection();
+ Point point = getAlignementPoint();
+ Rectangle arcBounds = connection.getArcBounds();
+ //normalize the coordinates.
+ double bx = arcBounds.x;
+ double by = arcBounds.y;
+ double bw = arcBounds.width;
+ double bh = arcBounds.height;
+
+ int q = getQuadrant(point);
+ Rectangle tbounds = target.getBounds();
+ //the new location of the arrow will depend on what quadrant it is in
+ switch (q) {
+ case 1:
+ point.x = point.x+tbounds.width;
+ point.y = point.y+tbounds.height;
+ break;
+ case 2:
+ point.x = point.x+tbounds.width;
+ point.y = point.y-tbounds.height;
+ break;
+ case 3:
+ point.x = point.x-tbounds.width;
+ point.y = point.y-tbounds.height;
+ break;
+ case 4:
+ point.x = point.x-tbounds.width;
+ point.y = point.y+tbounds.height;
+ break;
+ }
+
+
+ double normx = (point.x - (bx + bw/2));
+ double normy = (point.y - (by+ bh/2));
+ double th = Math.atan(normy/normx);
+ //adjust theta according to quadrant
+ switch (q) {
+ case 4:
+ th = th + Math.PI;
+ case 2:
+ case 3:
+ th = th + Math.PI;
+ break;
+ }
+
+ //translate back from polar coordinates.
+ double nx = (bw/2)*Math.cos(th) + bx + bw/2;
+ double ny = (bh/2)*Math.sin(th) + by + bh/2;
+ target.setReferencePoint(new Point(Math.round(nx), Math.round(ny)));
+ }
+
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/Bezier.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/Bezier.java
new file mode 100644
index 0000000..f67fba0
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/Bezier.java
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.Polyline;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+
+/**
+ * Simple Bezier curve shape.
+ * @author Del Myers
+ *
+ */
+
+//@tag bug(152530-Bezier(fix)) : a basic shape for drawing bezier curves.
+public class Bezier extends Polyline {
+ //the control attached to the start point.
+ private Point startControl;
+ //the control attached to the end point.
+ private Point endControl;
+ private boolean isValid;
+
+
+ private static Object lock = new Object();
+
+
+ // the number of line segments between the start and end points.
+ public static int SEGMENTS;
+ //@tag performance(optimization) : pre-compute the weights for faster updates of bezier curves. Greatly reduces the number of multiplications.
+ private static float[] WEIGHTS1;
+ private static float[] WEIGHTS2;
+
+ //allow users to change the number of segments
+ static {
+ synchronized (lock) {
+ SEGMENTS = 58;
+ WEIGHTS1 = COMPUTE_WEIGHTS_1();
+ WEIGHTS2 = COMPUTE_WEIGHTS_2();
+ }
+ }
+
+ public Bezier(Point start, Point end, Point startControl, Point endControl) {
+ super();
+ setStart(start);
+ setEnd(end);
+ this.startControl = startControl;
+ this.endControl = endControl;
+ this.isValid = false;
+
+ }
+
+
+ //set the number of segments to be used in drawing.
+ public static void setSegments(int numSegments) {
+ synchronized (lock) {
+ SEGMENTS = numSegments;
+ WEIGHTS1 = COMPUTE_WEIGHTS_1();
+ WEIGHTS2 = COMPUTE_WEIGHTS_2();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Shape#fillShape(org.eclipse.draw2d.Graphics)
+ */
+ protected void fillShape(Graphics graphics) {
+ reCompute();
+ super.fillShape(graphics);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Shape#outlineShape(org.eclipse.draw2d.Graphics)
+ */
+ protected void outlineShape(Graphics graphics) {
+ reCompute();
+ graphics.setClip(new Rectangle(0,0,3000,3000));
+ graphics.drawPolyline(getPoints().toIntArray());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Polyline#setStart(org.eclipse.draw2d.geometry.Point)
+ */
+ public void setStart(Point start) {
+ super.setStart(start);
+ isValid = false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Polyline#setEnd(org.eclipse.draw2d.geometry.Point)
+ */
+ public void setEnd(Point end) {
+ super.setEnd(end);
+ isValid = false;
+ }
+
+ /**
+ * @param endControl the endControl to set
+ */
+ public void setEndControl(Point endControl) {
+ this.endControl = endControl;
+ isValid = false;
+ }
+
+ /**
+ * @param startControl the startControl to set
+ */
+ public void setStartControl(Point startControl) {
+ this.startControl = startControl;
+ isValid = false;
+ }
+
+ /**
+ * @return the startControl
+ */
+ public Point getStartControl() {
+ return startControl;
+ }
+
+ /**
+ * @return the endControl
+ */
+ public Point getEndControl() {
+ return endControl;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#revalidate()
+ */
+ public void revalidate() {
+ reCompute();
+ super.revalidate();
+ }
+
+ public final void reCompute() {
+ if (isValid) return;
+ Point start = getStart();
+ Point end = getEnd();
+ PointList points = getPoints();
+ points.removeAllPoints();
+ points.addPoint(start);
+ float lengthSquared = (float)(end.x - start.x)*(end.x-start.x)+(start.y-end.y)*(start.y-end.y);
+ float segmentsSquared = SEGMENTS*SEGMENTS*64;
+ int step = 1;
+ //optimize the number of line segments to use. If it isn't going to look
+ //bad, use less.
+ if (lengthSquared < .0001 && lengthSquared > -.0001) {
+ //may as well be 0
+ step = SEGMENTS;
+ } else if (lengthSquared < segmentsSquared) {
+ step = (int)Math.round(((float)segmentsSquared)/lengthSquared);
+ }
+ //always have at least 8 line segments
+ if (step > SEGMENTS/8) step = SEGMENTS/8;
+ if (step <= 0) step = 1;
+ synchronized(lock) {
+ for (int i = step; i < SEGMENTS; i+=step) {
+ Point p = new Point(
+ Math.round(t0(i)*start.x + t1(i)*startControl.x + t2(i)*endControl.x+t3(i)*end.x),
+ Math.round(t0(i)*start.y + t1(i)*startControl.y + t2(i)*endControl.y+t3(i)*end.y)
+ );
+ points.addPoint(p);
+ }
+ }
+ points.addPoint(end);
+ super.setPoints(points);
+ isValid = true;
+ }
+
+ private static final float[] COMPUTE_WEIGHTS_1() {
+ float[] weights = new float[SEGMENTS+1];
+ float t = 0;
+ weights[0] = 1;
+ weights[SEGMENTS] = 0;
+ for (int i = 1; i < SEGMENTS; i++) {
+ t = ((float)i)/((float)SEGMENTS);
+ float oneMinusT = (1-t);
+ weights[i] = oneMinusT*oneMinusT*oneMinusT;
+ }
+ return weights;
+ }
+
+ private static final float[] COMPUTE_WEIGHTS_2() {
+ float[] weights = new float[SEGMENTS+1];
+ float t = 0;
+ weights[0] = 1;
+ weights[SEGMENTS] = 0;
+ for (int i = 1; i < SEGMENTS; i++) {
+ t = ((float)i)/((float)SEGMENTS);
+ float oneMinusT = (1-t);
+ weights[i] = t*oneMinusT*oneMinusT*3;
+ }
+ return weights;
+ }
+
+ private static final float t0(int s) {
+ return WEIGHTS1[s];
+ }
+
+ private static final float t1(int s) {
+ return WEIGHTS2[s];
+ }
+
+ private static final float t2(int s) {
+ return WEIGHTS2[SEGMENTS-s];
+ }
+
+ private static final float t3(int s) {
+ return WEIGHTS1[SEGMENTS-s];
+ }
+
+}
\ No newline at end of file
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/BezierConnection.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/BezierConnection.java
new file mode 100644
index 0000000..379bcc6
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/BezierConnection.java
@@ -0,0 +1,391 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.AbstractRouter;
+import org.eclipse.draw2d.AnchorListener;
+import org.eclipse.draw2d.ArrowLocator;
+import org.eclipse.draw2d.Connection;
+import org.eclipse.draw2d.ConnectionAnchor;
+import org.eclipse.draw2d.ConnectionLocator;
+import org.eclipse.draw2d.ConnectionRouter;
+import org.eclipse.draw2d.DelegatingLayout;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.RotatableDecoration;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+
+
+
+/**
+ * A connection that uses Bezier curves.
+ *
+ * Bezier curves are defined by a set of four points: two point in the layout
+ * (start and end), and two related control points (also start and end). The
+ * control points are defined relative to their corresponding layout point.
+ * This definition includes an angle between the layout point and the line
+ * between the two layout points, as well as a ratio distance from the corresponding
+ * layout point. The ratio distance is defined as a fraction between 0 and 1
+ * of the distance between the two layout points. Using this definition
+ * allows bezier curves to have a consistant look regardless of the actual
+ * positions of the nodes in the layouts.
+ * @author Del Myers
+ *
+ */
+//@tag zest(bug(152530-Bezier(fix)))
+public class BezierConnection extends Bezier implements Connection, AnchorListener {
+
+ private double startAngle;
+ private double startLength;
+ private double endAngle;
+ private double endLength;
+ private RotatableDecoration startArrow, endArrow;
+
+
+ /**
+ * @param startAngle the angle from the line that the first control point is on.
+ * @param startLength the distance from the start point that the first control point is as a percentage of the distance between the endpoints.
+ * @param endAngle the angle from the line that the second control point is on.
+ * @param endLength the distance from the endpoint that the last control point is as a percentage of the distance between the endpoints.
+ */
+ public BezierConnection(double startAngle, double startLength, double endAngle, double endLength) {
+ super(new Point(0,0), new Point(0,0), new Point(0,0), new Point(0,0));
+ this.startAngle = startAngle;
+ this.startLength = startLength;
+ this.endAngle = endAngle;
+ this.endLength = endLength;
+ setConnectionRouter(null);
+ setLayoutManager(new DelegatingLayout());
+ resetControls();
+ }
+
+ private static ConnectionRouter NullConnectionRouter = new NullConnectionRouter();
+
+ /**
+ * Routes Connections directly from the source anchor to the target anchor with no
+ * bendpoints in between.
+ */
+ static class NullConnectionRouter
+ extends AbstractRouter
+ {
+
+ /**
+ * Constructs a new NullConnectionRouter.
+ */
+ NullConnectionRouter() { }
+
+ /**
+ * Routes the given Connection directly between the source and target anchors.
+ * @param conn the connection to be routed
+ */
+ public void route(Connection conn) {
+ PointList points = conn.getPoints();
+ points.removeAllPoints();
+ Point p;
+ p = getStartPoint(conn);
+ conn.translateToRelative(p = getStartPoint(conn));
+ points.addPoint(p);
+ p = getEndPoint(conn);
+ conn.translateToRelative(p = getEndPoint(conn));
+ points.addPoint(p);
+ conn.setPoints(points);
+ }
+
+ }
+
+ private ConnectionRouter connectionRouter;
+ private ConnectionAnchor source;
+ private ConnectionAnchor dest;
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Connection#getConnectionRouter()
+ */
+ public ConnectionRouter getConnectionRouter() {
+ return connectionRouter;
+ }
+
+ private void unhookSourceAnchor() {
+ if (getSourceAnchor() != null)
+ getSourceAnchor().removeAnchorListener(this);
+ }
+
+ private void unhookTargetAnchor() {
+ if (getTargetAnchor() != null)
+ getTargetAnchor().removeAnchorListener(this);
+ }
+
+ private void hookSourceAnchor() {
+ if (getSourceAnchor() != null)
+ getSourceAnchor().addAnchorListener(this);
+ }
+
+ private void hookTargetAnchor() {
+ if (getTargetAnchor() != null)
+ getTargetAnchor().addAnchorListener(this);
+ }
+
+ public void setRoutingConstraint(Object cons) {
+ if (this.connectionRouter != null) connectionRouter.setConstraint(this, cons);
+ }
+
+ public void setSourceAnchor(ConnectionAnchor anchor) {
+ if (anchor == this.source) return;
+ unhookSourceAnchor();
+ this.source = anchor;
+ if (getParent()!=null) hookSourceAnchor();
+ revalidate();
+ }
+
+
+
+
+
+ public void setTargetAnchor(ConnectionAnchor anchor) {
+ if (anchor == this.dest) return;
+ unhookTargetAnchor();
+ this.dest = anchor;
+ if (getParent()!=null) hookTargetAnchor();
+ revalidate();
+ }
+
+
+ /**
+ * Sets the decoration to be used at the start of the {@link Connection}.
+ * @param dec the new source decoration
+ * @since 2.0
+ */
+ public void setSourceDecoration(RotatableDecoration dec) {
+ if (startArrow == dec)
+ return;
+ if (startArrow != null)
+ remove(startArrow);
+ startArrow = dec;
+ if (startArrow != null)
+ add(startArrow, new ArrowLocator(this, ConnectionLocator.SOURCE));
+ }
+
+ /**
+ * @return the source decoration (may be null)
+ */
+ protected RotatableDecoration getSourceDecoration() {
+ return startArrow;
+ }
+
+ /**
+ * Sets the decoration to be used at the end of the {@link Connection}.
+ * @param dec the new target decoration
+ */
+ public void setTargetDecoration(RotatableDecoration dec) {
+ if (endArrow == dec)
+ return;
+ if (endArrow != null)
+ remove(endArrow);
+ endArrow = dec;
+ if (endArrow != null)
+ add(endArrow, new ArrowLocator(this, ConnectionLocator.TARGET));
+ }
+
+ /**
+ * @return the target decoration (may be null)
+ *
+ * @since 2.0
+ */
+ protected RotatableDecoration getTargetDecoration() {
+ return endArrow;
+ }
+
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.AnchorListener#anchorMoved(org.eclipse.draw2d.ConnectionAnchor)
+ */
+ public void anchorMoved(ConnectionAnchor anchor) {
+ revalidate();
+ }
+
+ /**
+ * Returns the list of points in this arc. Note: the points can't be changed using
+ * setPoints, nor by changing the PointList. The PointList is fixed within this connection.
+ */
+ public final PointList getPoints() {
+ return super.getPoints().getCopy();
+ }
+
+ public Object getRoutingConstraint() {
+ if (this.connectionRouter != null)
+ return connectionRouter.getConstraint(this);
+ return null;
+ }
+
+ public ConnectionAnchor getSourceAnchor() {
+ return source;
+ }
+
+ public ConnectionAnchor getTargetAnchor() {
+ return dest;
+ }
+
+
+
+ public void setConnectionRouter(ConnectionRouter router) {
+ if (router == null) router = NullConnectionRouter;
+ if (this.connectionRouter == router) return;
+ ConnectionRouter oldRouter = connectionRouter;
+ this.connectionRouter = router;
+ firePropertyChange(Connection.PROPERTY_CONNECTION_ROUTER, oldRouter, this.connectionRouter);
+ }
+
+ public void revalidate() {
+ super.revalidate();
+ if (connectionRouter != null)
+ connectionRouter.invalidate(this);
+ }
+
+
+ /**
+ * Returns the bounds which holds all the points in this polyline connection. Returns any
+ * previously existing bounds, else calculates by unioning all the children's
+ * dimensions.
+ * @return the bounds
+ */
+// @tag zest(bug(154176-ConnectionClip(fix))) : add children to the bounds.
+ public Rectangle getBounds() {
+ if (bounds == null) {
+ super.getBounds();
+ for (int i = 0; i < getChildren().size(); i++) {
+ IFigure child = (IFigure)getChildren().get(i);
+ bounds.union(child.getBounds());
+ }
+ }
+ return bounds;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Polyline#setPoints(org.eclipse.draw2d.geometry.PointList)
+ */
+ public void setPoints(PointList points) {
+ setStart(points.getFirstPoint());
+ setEnd(points.getLastPoint());
+ resetControls();
+ repaint();
+ }
+
+ /**
+ * @param firstPoint
+ * @param lastPoint
+ */
+ private void resetControls() {
+ Point firstPoint = getStart();
+ Point lastPoint = getEnd();
+ Point startControl;
+ Point endControl;
+ double distance = Math.sqrt((lastPoint.x-firstPoint.x)*(lastPoint.x-firstPoint.x)+(firstPoint.y-lastPoint.y)*(firstPoint.y-lastPoint.y));
+ double m = (firstPoint.y - lastPoint.y)/(double)(lastPoint.x - firstPoint.x);
+ double theta = Math.atan(m);
+ if (firstPoint.x > lastPoint.x) {theta = theta + Math.PI;}
+ double sar = (Math.toRadians(getStartAngle()) + theta);
+ double ear = (theta-Math.toRadians(getEndAngle()));
+
+ double length = distance*getStartLength();
+ double controlX = length*Math.cos(sar);
+ double controlY = length*Math.sin(sar);
+ startControl = new Point(firstPoint.x + Math.round(controlX), firstPoint.y - Math.round(controlY));
+
+ length = distance*getEndLength();
+ controlX =length*Math.cos(ear);
+ controlY = length*Math.sin(ear);
+ endControl = new Point(lastPoint.x - Math.round(controlX), lastPoint.y + Math.round(controlY));
+
+ setStartControl(startControl);
+ setEndControl(endControl);
+
+ }
+
+ /**
+ * @return the startLength
+ */
+ public double getStartLength() {
+ return startLength;
+ }
+
+ /**
+ * @return the endLength
+ */
+ public double getEndLength() {
+ return endLength;
+ }
+
+ /**
+ * @param startLength the startLength to set
+ */
+ public void setStartLength(double startLength) {
+ this.startLength = startLength;
+ }
+
+ /**
+ * @param endLength the endLength to set
+ */
+ public void setEndLength(double endLength) {
+ this.endLength = endLength;
+ }
+
+ /**
+ * @return the startAngle
+ */
+ public double getStartAngle() {
+ return startAngle;
+ }
+
+ /**
+ * @return the endAngle
+ */
+ public double getEndAngle() {
+ return endAngle;
+ }
+
+ /**
+ * @param startAngle the startAngle to set
+ */
+ public void setStartAngle(int startAngle) {
+ this.startAngle = startAngle;
+ resetControls();
+ }
+
+ /**
+ * @param endAngle the endAngle to set
+ */
+ public void setEndAngle(int endAngle) {
+ this.endAngle = endAngle;
+ resetControls();
+ }
+
+ /**
+ * Layouts this polyline. If the start and end anchors are present, the connection router
+ * is used to route this, after which it is laid out. It also fires a moved method.
+ */
+ public void layout() {
+ if (getSourceAnchor() != null && getTargetAnchor() != null)
+ connectionRouter.route(this);
+ Rectangle oldBounds = bounds;
+ super.layout();
+
+ if (!getBounds().contains(oldBounds)) {
+ getParent().translateToParent(oldBounds);
+ getUpdateManager().addDirtyRegion(getParent(), oldBounds);
+ }
+
+ repaint();
+ fireFigureMoved();
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/CachedLabel.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/CachedLabel.java
new file mode 100644
index 0000000..667c116
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/CachedLabel.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.Animation;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.Label;
+import org.eclipse.draw2d.SWTGraphics;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.mylar.zest.core.ZestPlugin;
+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.widgets.Display;
+
+/**
+ * A cached label to improve performance of text drawing under linux
+ * @author Ian Bull
+ *
+ */
+public abstract class CachedLabel extends Label {
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
+ */
+ Image cachedImage = null;
+ boolean cacheLabel = false;
+ boolean invalidationRequired = false;
+
+
+ /**
+ * CachedLabel constructor.
+ * @param cacheLabel Should the label be cached, or should the text be re-layedout each time
+ */
+ public CachedLabel(boolean cacheLabel) {
+ this.cacheLabel = cacheLabel;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.draw2d.Label#setIcon(org.eclipse.swt.graphics.Image)
+ */
+ public void setIcon(Image image) {
+ updateInvalidation();
+ super.setIcon(image);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#setForegroundColor(org.eclipse.swt.graphics.Color)
+ */
+ public void setForegroundColor(Color fg) {
+ updateInvalidation();
+ super.setForegroundColor(fg);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#setBackgroundColor(org.eclipse.swt.graphics.Color)
+ */
+ public void setBackgroundColor(Color bg) {
+ updateInvalidation();
+ super.setBackgroundColor(bg);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#setFont(org.eclipse.swt.graphics.Font)
+ */
+ public void setFont(Font f) {
+ updateInvalidation();
+ super.setFont(f);
+ }
+
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.draw2d.Label#setText(java.lang.String)
+ */
+ public void setText(String s) {
+ updateInvalidation();
+ super.setText(s);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#setSize(int, int)
+ */
+ public void setSize(int w, int h) {
+ updateInvalidation();
+
+ if ( cachedImage != null && shouldInvalidateCache() ) {
+ cleanImage();
+ }
+ super.setSize(w, h);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle)
+ */
+ public void setBounds(Rectangle rect) {
+ boolean resize = (rect.width != bounds.width) || (rect.height != bounds.height);
+
+ if ( resize && Animation.isAnimating() ) {
+ updateInvalidation();
+ }
+ if ( resize && shouldInvalidateCache() && cachedImage != null) {
+ cleanImage();
+ }
+
+ super.setBounds(rect);
+ }
+
+
+ /**
+ * Override this method to return the background colour for the text
+ * Note: Text must have a background color since it is being stored in
+ * an image (You can set it to white if you want)
+ * @return
+ */
+ protected abstract Color getBackgroundTextColor();
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
+ */
+ protected void paintFigure(Graphics graphics) {
+ if ( !cacheLabel ) {
+ super.paintFigure(graphics);
+ return;
+ }
+
+ if (isOpaque())
+ graphics.fillRectangle(getBounds());
+ Rectangle bounds = getBounds();
+ graphics.translate(bounds.x, bounds.y);
+
+ Image icon = getIcon();
+
+ if (icon != null)
+ graphics.drawImage(icon, getIconLocation());
+
+ int width = getSubStringTextSize().width;
+ int height = getSubStringTextSize().height;
+
+ if (cachedImage == null || shouldInvalidateCache()) {
+ invalidationRequired = false;
+ cleanImage();
+ cachedImage = new Image(Display.getCurrent(), width, height);
+
+ ZestPlugin.getDefault().addImage(cachedImage.toString(), cachedImage);
+
+ GC gc = new GC(cachedImage);
+
+ Graphics graphics2 = new SWTGraphics(gc);
+ graphics2.setBackgroundColor(getBackgroundTextColor());
+ graphics2.fillRectangle(0,0,width,height);
+ graphics2.setForegroundColor(getForegroundColor());
+ graphics2.drawText(getSubStringText(),new Point(0,0));
+ gc.dispose();
+
+ }
+ graphics.drawImage(cachedImage, getTextLocation());
+ graphics.translate(-bounds.x, -bounds.y);
+ this.paintBorder(graphics);
+
+ }
+
+ /**
+ * Determine if the image should be remade or not
+ * @return
+ */
+ private boolean shouldInvalidateCache() {
+ if ( invalidationRequired && !Animation.isAnimating())
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Notifies the cache that the image will need updating.
+ */
+ private void updateInvalidation() {
+ invalidationRequired = true;
+ }
+
+ protected void cleanImage() {
+ if ( cachedImage != null ) {
+
+ ZestPlugin.getDefault().removeImage(cachedImage.toString());
+ cachedImage.dispose();
+ cachedImage = null;
+ }
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/GraphRootEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/GraphRootEditPart.java
new file mode 100644
index 0000000..3f29fc3
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/GraphRootEditPart.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import java.util.List;
+
+import org.eclipse.draw2d.ConnectionLayer;
+import org.eclipse.draw2d.FreeformLayer;
+import org.eclipse.draw2d.FreeformLayeredPane;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.LayeredPane;
+import org.eclipse.draw2d.ScalableFigure;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.gef.DragTracker;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.Request;
+import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
+import org.eclipse.gef.editparts.ZoomManager;
+import org.eclipse.mylar.zest.core.internal.graphviewer.parts.GraphEditPart;
+import org.eclipse.mylar.zest.core.internal.viewers.trackers.PanningTracker;
+import org.eclipse.mylar.zest.core.internal.viewers.trackers.SingleSelectionTracker;
+
+
+
+/**
+ * Extends {@link org.eclipse.gef.editparts.ScalableFreeformRootEditPart ScalableFreeformRootEditPart}
+ * to give the option of using a {@link SingleSelectionTracker SingleSelectionTracker}
+ * instead of a marquee drag tracker. A PanningDragTracker will be used if the background is
+ * dragged and marquee selection is not
+ *
+ * @author Chris Callendar
+ */
+public class GraphRootEditPart extends ScalableFreeformRootEditPart implements ZestRootEditPart {
+
+
+
+ private IPanningListener panningListener;
+ private boolean allowMarqueeSelection;
+ private boolean allowPanning;
+ protected GraphEditPart graphEditPart = null;
+ private ZoomManager zoomManager;
+
+ public GraphRootEditPart() {
+ this(null, false, false);
+ }
+
+ /**
+ * Initializes this root edit part.
+ * @param panningListener the listener to be notified of panning events (dragging the canvas)
+ * @param allowMarqueeSelection if marquee selection is allowed - multiple node selection
+ * @param allowPanning if panning is allowed. Only one of panning OR marquee selection is allowed.
+ */
+ public GraphRootEditPart(IPanningListener panningListener, boolean allowMarqueeSelection, boolean allowPanning) {
+ super();
+ this.panningListener = panningListener;
+ this.allowMarqueeSelection = allowMarqueeSelection;
+ this.allowPanning = allowPanning;
+ //@tag zest.bug.163481-NPE.fix : moved initialization from the no-arg constructor to here.
+ this.zoomManager = new RectangleZoomManager((ScalableFigure)getScaledLayers(),(Viewport)getFigure());
+ }
+
+ protected LayeredPane createPrintableLayers() {
+ FreeformLayeredPane layeredPane = new FreeformLayeredPane();
+
+ layeredPane.add(new ConnectionLayer(), CONNECTION_LAYER);
+ layeredPane.add(new FreeformLayer(), PRIMARY_LAYER);
+ layeredPane.add(new ConnectionLayer(), CONNECTION_FEEDBACK_LAYER);
+
+ return layeredPane;
+ }
+
+ /**
+ * Returns a drag tracker. If panning is allowed then a PanningTracker is returned.
+ * Otherwise either a {@link SingleSelectionTracker} (marqueeSelection disabled)
+ * or a {@link org.eclipse.gef.tools.MarqueeDragTracker} is returned.
+ * @see org.eclipse.gef.editparts.ScalableRootEditPart#getDragTracker(org.eclipse.gef.Request)
+ */
+ public DragTracker getDragTracker(Request req) {
+ if (allowPanning && (panningListener != null)) {
+ return new PanningTracker(this, panningListener, allowPanning);
+ } else if (!allowMarqueeSelection) {
+ return new SingleSelectionTracker(this);
+ }
+ return super.getDragTracker(req);
+ }
+
+ /**
+ * Sets the main edit part for the model. You should be able to
+ * fire changes off here and see the effect
+ */
+ public void setModelRootEditPart(Object modelRootEditPart) {
+ this.graphEditPart = (GraphEditPart) modelRootEditPart;
+ }
+
+ public void clear() {
+// force revmoval of the edit parts.
+ EditPart[] children = (EditPart[])getChildren().toArray(new EditPart[] {});
+ for (int i = 0; i < children.length; i++) {
+ EditPart child = children[i];
+ removeChild(child);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#unregisterVisuals()
+ */
+ //@tag bug(154412-ClearStatic(fix)) : make sure that all the visuals are deregistered before recreating the parts.
+ protected void unregisterVisuals() {
+ super.unregisterVisuals();
+ List children = getFigure().getChildren();
+ //remove all the child figures for the root, which
+ //don't necessarilly have edit parts.
+ for (int i = 0; i < children.size(); i++) {
+ IFigure child = (IFigure) children.get(i);
+ getViewer().getVisualPartMap().remove(child);
+ }
+ getViewer().getVisualPartMap().remove(figure);
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#unregisterModel()
+ */
+ //@tag bug(154412-ClearStatic(fix)) : make sure that all edit parts are removed before creating new ones.
+ protected void unregisterModel() {
+ clear();
+ super.unregisterModel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.ScalableFreeformRootEditPart#getZoomManager()
+ */
+ public ZoomManager getZoomManager() {
+ return zoomManager;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/IPanningListener.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/IPanningListener.java
new file mode 100644
index 0000000..82d34db
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/IPanningListener.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+/**
+ * An interface used for handling panning events.
+ *
+ * @author Chris Callendar
+ */
+public interface IPanningListener {
+
+ /**
+ * Indicates that panning has started.
+ */
+ public void panningStart();
+
+ /**
+ * Handles a change in position due to panning.
+ * @param dx the change in x position
+ * @param dy the change in y position
+ */
+ public void panning(int dx, int dy);
+
+ /**
+ * Indicates that panning has ceased.
+ */
+ public void panningEnd();
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/IZestViewerProperties.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/IZestViewerProperties.java
new file mode 100644
index 0000000..e560f1d
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/IZestViewerProperties.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.gefx;
+
+/**
+ * An experimental interface used for publishing property changes on Zest graphical edit
+ * part viewers.
+ * @author Del Myers
+ *
+ */
+//@tag zest.experimental
+public interface IZestViewerProperties {
+ /**
+ * Allows viewer listeners to know when the contents of the viewer has changed.
+ */
+ //@tag zest.experimental.contents.
+ static final String GRAPH_VIEWER_CONTENTS = "zest.viewer.contents"; //$NON-NLS-1$
+
+}
\ No newline at end of file
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/NestedNonThreadedGraphicalViewer.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/NestedNonThreadedGraphicalViewer.java
new file mode 100644
index 0000000..3950f5f
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/NestedNonThreadedGraphicalViewer.java
@@ -0,0 +1,212 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.gef.DefaultEditDomain;
+import org.eclipse.gef.EditDomain;
+import org.eclipse.gef.MouseWheelHandler;
+import org.eclipse.gef.MouseWheelZoomHandler;
+import org.eclipse.gef.ui.parts.GraphicalViewerImpl;
+import org.eclipse.mylar.zest.layouts.progress.ProgressEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ *
+ * @author Ian Bull
+ * @author Chris Callendar
+ */
+public abstract class NestedNonThreadedGraphicalViewer extends GraphicalViewerImpl {
+
+ private List revealListeners = null;
+
+
+ /**
+ * @see org.eclipse.gef.EditPartViewer#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public Control createControl(Composite composite) {
+ setControl(new Canvas(composite, SWT.DOUBLE_BUFFERED));
+ return getControl();
+ }
+
+ /**
+ * ThreadedGraphicalViewer constructor.
+ * @param parent The composite that this viewer will be added to.
+ */
+ public NestedNonThreadedGraphicalViewer(Composite parent) {
+ super();
+ init(parent);
+ EditDomain ed = new DefaultEditDomain( null );
+ ed.addViewer( this );
+ setEditDomain( ed );
+
+ }
+
+ protected void init(Composite parent) {
+ // create the FigureCanvas
+ revealListeners = new ArrayList(1);
+ Control oldControl = getControl();
+ if ( oldControl != null ) oldControl.dispose();
+
+ createControl(parent);
+ hookControl();
+
+ getControl().addPaintListener(new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ if (!revealListeners.isEmpty()) {
+ // Go through the reveal list and let everyone know that the view
+ // is now available. Remove the listeners so they are only called once!
+ Iterator iterator = revealListeners.iterator();
+ while (iterator.hasNext() ) {
+ RevealListener reveallisetner = (RevealListener) iterator.next();
+ reveallisetner.revealed(getControl());
+ iterator.remove();
+ }
+ }
+ }
+
+ });
+ getControl().addControlListener(new ControlListener() {
+
+ public void controlMoved(ControlEvent e) {
+ fireControlMovedEvent( e );
+ }
+
+ public void controlResized(ControlEvent e) {
+ fireControlResizedEvent( e );
+ }
+ });
+
+ getControl().addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ }
+ });
+
+
+ ((Canvas)getControl()).setBackground( ColorConstants.white );
+
+ // Scroll-wheel Zoom
+ setProperty(MouseWheelHandler.KeyGenerator.getKey(SWT.MOD1), MouseWheelZoomHandler.SINGLETON);
+ }
+
+ private List controlListeners = new LinkedList();
+
+ public void addControlListener( ControlListener controlListener ) {
+ controlListeners.add( controlListener );
+ }
+
+
+ /**
+ * Adds a reveal listener to the view. Note: A reveal listener will
+ * only every be called ONCE!!! even if a view comes and goes. There
+ * is no remove reveal listener.
+ * @param revealListener
+ */
+ public void addRevealListener( RevealListener revealListener ) {
+ if ( getControl().isVisible() ) revealListener.revealed( (Composite)getControl() );
+ else {
+ revealListeners.add(revealListener);
+ }
+ }
+
+ public boolean removeControlListener( ControlListener controlListener ) {
+ return controlListeners.remove( controlListener );
+ }
+
+ protected void fireControlMovedEvent( ControlEvent e ) {
+ for ( Iterator iterator = controlListeners.iterator(); iterator.hasNext(); ) {
+ ((ControlListener)iterator.next()).controlMoved( e );
+ }
+ }
+
+ protected void fireControlResizedEvent( ControlEvent e ) {
+ for ( Iterator iterator = controlListeners.iterator(); iterator.hasNext(); ) {
+ ((ControlListener)iterator.next()).controlResized( e );
+ }
+ }
+
+
+
+
+ /**
+ * Does some initializing of the viewer.
+ */
+ protected abstract void configureGraphicalViewer();
+
+ /**
+ * Sets the contents of the viewer and configures the graphical viewer.
+ * @param model
+ */
+ public void setContents(Object model) {
+ this.configureGraphicalViewer();
+ super.setContents(model);
+ }
+
+ /**
+ * Updates the contents <b>without</b> configuring the graphical viewer.
+ * Only call this if the graphical viewer has already be configured.
+ * @param model
+ */
+ public void updateContents(Object model) {
+ super.setContents(model);
+ }
+
+ /**
+ * Gets the absolute size of the canvas.
+ * @return Dimension in absolute coords
+ */
+ public Dimension getCanvasSize() {
+ Dimension dim = new Dimension(this.getControl().getSize());
+ //dim.shrink(getRootFigure().getBorderWidth(), getRootFigure().getBorderWidth());
+ return dim;
+ }
+
+ /**
+ * Gets the translated size of the canvas.
+ * @return Dimension relative
+ */
+ public Dimension getTranslatedCanvasSize() {
+ Dimension dim = getCanvasSize();
+ //mainCanvas.getViewport().translateToRelative(dim);
+ return dim;
+ }
+
+ /**
+ *
+ */
+ public void progressUpdated(ProgressEvent e) {
+ //TODO: Make this use the display thread
+ }
+
+ public void progressEnded(ProgressEvent e) {
+ // TODO Auto-generated method stub
+ }
+
+ public void progressStarted(ProgressEvent e) {
+ // TODO Auto-generated method stub
+ }
+
+}
\ No newline at end of file
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/NonThreadedGraphicalViewer.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/NonThreadedGraphicalViewer.java
new file mode 100644
index 0000000..ec9e512
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/NonThreadedGraphicalViewer.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.gef.DefaultEditDomain;
+import org.eclipse.gef.EditDomain;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.MouseWheelHandler;
+import org.eclipse.gef.MouseWheelZoomHandler;
+import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ *
+ * @author Ian Bull
+ * @author Chris Callendar
+ */
+public abstract class NonThreadedGraphicalViewer extends ScrollingGraphicalViewer {
+
+ private List revealListeners = null;
+
+ /**
+ * ThreadedGraphicalViewer constructor.
+ * @param parent The composite that this viewer will be added to.
+ */
+ public NonThreadedGraphicalViewer(Composite parent) {
+ super();
+
+ revealListeners = new ArrayList(1);
+
+ // create the FigureCanvas
+ createControl(parent);
+ EditDomain ed = new DefaultEditDomain( null );
+ ed.addViewer( this );
+ setEditDomain( ed );
+ hookControl();
+ //getFigureCanvas().setScrollBarVisibility(FigureCanvas.NEVER);
+
+
+ getControl().addPaintListener(new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ if (!revealListeners.isEmpty()) {
+ // Go through the reveal list and let everyone know that the view
+ // is now available. Remove the listeners so they are only called once!
+ Iterator iterator = revealListeners.iterator();
+ while (iterator.hasNext() ) {
+ RevealListener reveallisetner = (RevealListener) iterator.next();
+ reveallisetner.revealed(getControl());
+ iterator.remove();
+ }
+ }
+ }
+
+ });
+
+ getControl().addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ }
+ });
+
+ ((Canvas)getControl()).setBackground( ColorConstants.white );
+
+ // Scroll-wheel Zoom
+ setProperty(MouseWheelHandler.KeyGenerator.getKey(SWT.MOD1), MouseWheelZoomHandler.SINGLETON);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.gef.ui.parts.ScrollingGraphicalViewer#reveal(org.eclipse.gef.EditPart)
+ */
+ public void reveal(EditPart part) {
+ // @tag rootEditPart : Don't reveal root edit parts
+ if (!(part instanceof GraphRootEditPart))
+ super.reveal(part);
+ }
+
+ private class MyRunnable implements Runnable{
+ boolean isVisible;
+ public void run() { isVisible = getControl().isVisible();}
+ }
+
+ /**
+ * Adds a reveal listener to the view. Note: A reveal listener will
+ * only every be called ONCE!!! even if a view comes and goes. There
+ * is no remove reveal listener.
+ * @param revealListener
+ */
+ public void addRevealListener( final RevealListener revealListener ) {
+
+ MyRunnable myRunnable = new MyRunnable();
+ PlatformUI.getWorkbench().getDisplay().syncExec( myRunnable);
+
+ if ( myRunnable.isVisible )
+ revealListener.revealed( (Composite)getControl() );
+ else
+ revealListeners.add(revealListener);
+ }
+
+ /**
+ * Does some initializing of the viewer.
+ */
+ protected abstract void configureGraphicalViewer();
+
+ /**
+ * Sets the contents of the viewer and configures the graphical viewer.
+ * @param model
+ */
+ public void setContents(Object model) {
+ this.configureGraphicalViewer();
+ super.setContents(model);
+ //@tag zest.experimental.contents : publish a property change that the model has changed. This will allow linked viewers to update.
+ setProperty(IZestViewerProperties.GRAPH_VIEWER_CONTENTS, model);
+
+ }
+
+ /**
+ * Updates the contents <b>without</b> configuring the graphical viewer.
+ * Only call this if the graphical viewer has already be configured.
+ * @param model
+ */
+ public void updateContents(Object model) {
+ super.setContents(model);
+ //@tag zest.experimental.contents : publish a property change that the model has changed. This will allow linked viewers to update.
+ setProperty(IZestViewerProperties.GRAPH_VIEWER_CONTENTS, model);
+ }
+
+ /**
+ * Gets the absolute size of the canvas.
+ * @return Dimension in absolute coords
+ */
+ public Dimension getCanvasSize() {
+ Dimension dim = new Dimension(getFigureCanvas().getSize());
+ dim.shrink(getFigureCanvas().getBorderWidth(), getFigureCanvas().getBorderWidth());
+ return dim;
+ }
+
+ /**
+ * Gets the translated size of the canvas.
+ * @return Dimension relative
+ */
+ public Dimension getTranslatedCanvasSize() {
+ Dimension dim = getCanvasSize();
+ //mainCanvas.getViewport().translateToRelative(dim);
+ return dim;
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/PolylineArcConnection.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/PolylineArcConnection.java
new file mode 100644
index 0000000..7b60173
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/PolylineArcConnection.java
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.PolylineConnection;
+import org.eclipse.draw2d.RectangleFigure;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+
+/**
+ * A connection that draws an arc between nodes, based on a given depth for the
+ * arc. This connection is drawn as an arc,
+ * defined as the circular arc with the chord (ax, ay) - (bx, by) (where a and b
+ * are the anchors) and a depth d defined as the maximum distance from any point on
+ * the chord (i.e. a vector normal to the chord with magnitude d).
+ *
+ * @author Del Myers
+ *
+ */
+//@tag zest(bug(154391-ArcEnds(fix))) : force the endpoints to match by using a polyline connection.
+//This will be more accurate than the regular ArcConnection, but it may be slower.
+public class PolylineArcConnection extends PolylineConnection {
+ private int depth;
+ private static final float PI = (float)3.14;
+ private RectangleFigure center;
+
+
+ {
+ this.depth=0;
+ center = new RectangleFigure();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Polyline#setPoints(org.eclipse.draw2d.geometry.PointList)
+ */
+ public void setPoints(PointList points) {
+ updateArc(points);
+ }
+
+ /**
+ * This method is not supported by this kind of connection. Points are
+ * calculated based on the arc definition.
+ */
+ public void addPoint(Point pt) {
+ }
+
+ /**
+ * @param depth the depth to set
+ */
+ public void setDepth(int depth) {
+ this.depth = depth;
+ updateArc(getPoints());
+ }
+
+ protected void updateArc(PointList pointList) {
+ if (pointList.size() < 2) return;
+ if (center.getParent() == this) remove(center);
+ Point start = pointList.getFirstPoint();
+ Point end = pointList.getLastPoint();
+
+ if (depth == 0) {
+ super.setPoints(pointList);
+ return;
+ }
+
+ PointList points = new PointList();
+
+ float arcStart = 0;
+ float arcLength = 0;
+ float cartCenterX = 0;
+ float cartCenterY = 0;
+ float r = 0;
+
+ float x1 = start.x;
+ float y1 = -start.y;
+ float x2 = end.x;
+ float y2 = -end.y;
+ float depth = this.depth;
+
+ if (start.equals(end)) {
+ //do a circle
+ arcStart = -PI/2;
+ arcLength = PI*2;
+// @tag drawing(arcs) : try making the center on a line from the center of the parent figure.
+
+ cartCenterX = x1;
+ cartCenterY = y1+depth/2;
+ r = depth/2;
+
+ } else {
+ if (x1 >= x2) {
+ depth = -depth;
+ }
+ // the center of the chord
+ float cartChordX = (x2 + x1)/2;
+ float cartChordY = (y2 + y1)/2;
+ float chordLength = (float)Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
+ if (Math.abs(depth) >= chordLength/2) {
+ depth = (chordLength/3)*(depth/Math.abs(depth));
+ }
+ r = ((((chordLength/2)*(chordLength/2)) + ((float)depth*depth))/((float)2*depth));
+
+ //Find a vector normal to the chord. This will be used for translating the
+ //circle back to screen coordinates.
+ float chordNormal = 0;
+
+ if (Math.abs(x1 - x2) <= .001) {
+ //slope of 0. NaN is easier to detect than 0.
+ chordNormal = Float.NaN;
+ } else if (Math.abs(y1-y2) <= 0.001) {
+ //infinite slope.
+ chordNormal = Float.POSITIVE_INFINITY;
+ } else {
+ chordNormal = -(float)(y2 - y1)/(x2 - x1);
+ }
+
+ float th1;
+ if (Float.isNaN(chordNormal)) {
+ cartCenterX = (y1 > y2) ? (cartChordX-r+(depth)) : (cartChordX+r-(depth));
+ cartCenterY = cartChordY;
+ th1 = PI/2;
+ } else if (Float.isInfinite(chordNormal)) {
+ cartCenterX = cartChordX;
+ cartCenterY = cartChordY+r-(depth);
+ th1 = 0;
+ } else {
+ //assume that the center of the chord is on the origin.
+ th1 = (float)Math.atan(chordNormal);
+ cartCenterX = (r-(depth))*(float)Math.sin(th1)+cartChordX;//cartChordX+r -depth;
+ cartCenterY = (r-(depth))*(float)Math.cos(th1)+cartChordY;//cartChordY+r-depth;
+
+ }
+ //figure out the new angles
+ //translate the points to the center of the circle
+ float cartArcX1 = x1 - cartCenterX;
+ float cartArcY1 = y1 - cartCenterY;
+ float cartArcX2 = x2 - cartCenterX;
+ float cartArcY2 = y2 - cartCenterY;
+
+ //calculate the length of the arc
+ arcStart = angleRadians(cartArcX1, cartArcY1);
+ float arcEnd = angleRadians(cartArcX2, cartArcY2);
+
+ if (arcEnd < arcStart) {
+ arcEnd = arcEnd + PI + PI;
+ }
+
+ //make sure that we are between the two nodes.
+ arcLength = arcEnd-arcStart;
+ float pad = PI/(float)Math.abs(r);
+ arcStart += pad;
+ arcEnd -= pad;
+ arcLength = (arcEnd)-(arcStart);
+ }
+ //calculate the points
+ r = Math.abs(r);
+ float x=0, y=0;
+ Point p=null;
+ points.addPoint(start);
+ float length = arcLength*r;
+
+ int steps = (int)length/16;
+ if (steps < 10 && length > 10) steps = 10;
+ if (arcLength < PI/4 && steps > 6) steps = 6;
+ if (steps < 4 && length > 4)
+ steps = 4;
+ float stepSize = arcLength/steps;
+ float step = stepSize+arcStart;
+ for (int i = 1; i < steps; i++, step+= stepSize) {
+ x = (r)*(float)Math.cos(step) + cartCenterX;
+ y = (r)*(float)Math.sin(step) + cartCenterY;
+ p = new Point((int)Math.round(x), (int)Math.round(-y));
+ points.addPoint(p);
+ }
+ points.addPoint(end);
+
+
+ super.setPoints(points);
+ }
+
+
+ /*
+ * Gets an angle in radians for the x, y coordinates. The angle will be between 0 and 2PI.
+ */
+ float angleRadians(float x, float y) {
+ float theta = (float)Math.atan(y/x);
+ switch (findQuadrant(x,y)) {
+ case 1: return theta;
+ case 2: return (theta+PI);
+ case 4: theta = (theta+PI);
+ case 3: return (theta+PI);
+ default: return theta;
+ }
+
+ }
+
+ //find the quadrant, assume points are centered at 0,0
+ protected int findQuadrant (float x, float y) {
+ if (y > 0) {
+ if (x > 0) {
+ return 1;
+ } else {
+ return 2;
+ }
+ } else {
+ if (x > 0) {
+ return 4;
+ } else {
+ return 3;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/RectangleZoomManager.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/RectangleZoomManager.java
new file mode 100644
index 0000000..4d59726
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/RectangleZoomManager.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.ScalableFigure;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.gef.editparts.ZoomManager;
+
+/**
+ * A zoom manager that has support for the zoomTo method.
+ * @author Del Myers
+ *
+ */
+public class RectangleZoomManager extends ZoomManager {
+
+ /**
+ * @param pane
+ * @param viewport
+ */
+ public RectangleZoomManager(ScalableFigure pane, Viewport viewport) {
+ super(pane, viewport);
+ }
+
+ /**
+ * Takes a rectangle in the viewport coordinates, that is not scaled according
+ * to the current viewport scale. Scales the viewport to which ever axis of the
+ * rectangle holds the most information.
+ */
+ public void zoomTo(Rectangle rect) {
+ //figure out the scale.
+ Rectangle vbounds = getViewport().getBounds().getCopy();
+ Point center = rect.getCenter();
+ Rectangle copy = rect.getCopy();
+
+ double scale = 1;
+ if (rect.isEmpty()) {
+ //do nothing
+ } else if (rect.width < rect.height) {
+ scale = ((double)vbounds.height)/copy.height;
+ copy.scale(scale,1);
+ } else {
+ scale = ((double)vbounds.width)/copy.width;
+ copy.scale(1,scale);
+ }
+ center.scale(scale);
+ primSetZoom(scale);
+ Rectangle clientArea = getViewport().getClientArea();
+ setViewLocation(new Point(center.x - clientArea.width/2, center.y - clientArea.height/2));
+ }
+
+
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/RevealListener.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/RevealListener.java
new file mode 100644
index 0000000..d8d6c6e
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/RevealListener.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.swt.widgets.Control;
+
+/**
+ *
+ * A Listener to indicate when a view has become visible.
+ * @author Ian Bull
+ *
+ */
+public interface RevealListener {
+
+ public void revealed( Control c );
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/StaticGraphRootEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/StaticGraphRootEditPart.java
new file mode 100644
index 0000000..9921b44
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/StaticGraphRootEditPart.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.ConnectionLayer;
+import org.eclipse.draw2d.FreeformLayer;
+import org.eclipse.draw2d.FreeformLayeredPane;
+import org.eclipse.draw2d.LayeredPane;
+import org.eclipse.gef.DragTracker;
+import org.eclipse.gef.Request;
+import org.eclipse.gef.tools.MarqueeDragTracker;
+import org.eclipse.mylar.zest.core.internal.graphviewer.parts.GraphEditPart;
+import org.eclipse.mylar.zest.core.internal.viewers.trackers.PanningTracker;
+import org.eclipse.mylar.zest.core.internal.viewers.trackers.SingleSelectionTracker;
+
+
+
+/**
+ * Extends {@link org.eclipse.gef.editparts.ScalableFreeformRootEditPart ScalableFreeformRootEditPart}
+ * to give the option of using a {@link SingleSelectionTracker SingleSelectionTracker}
+ * instead of a marquee drag tracker. A PanningDragTracker will be used if the background is
+ * dragged and marquee selection is not
+ *
+ * @author Chris Callendar
+ */
+public class StaticGraphRootEditPart extends GraphRootEditPart implements ZestRootEditPart {
+
+ public static final String CONNECTION_FEEDBACK_LAYER = "Connection Feedback Layer"; //$NON-NLS-1$
+
+ private IPanningListener panningListener;
+ private boolean allowMarqueeSelection;
+ private boolean allowPanning;
+ private GraphEditPart modelGraphEditPart = null;
+
+ public StaticGraphRootEditPart() {
+ super();
+ }
+ /**
+ * Initializes this root edit part.
+ * @param panningListener the listener to be notified of panning events (dragging the canvas)
+ * @param allowMarqueeSelection if marquee selection is allowed - multiple node selection
+ * @param allowPanning if panning is allowed. Only one of panning OR marquee selection is allowed.
+ */
+ //@tag zest.bug.156617ClearViewer.fix : have to configure the edit part after it is created.
+ public void configure(IPanningListener panningListener, boolean allowMarqueeSelection, boolean allowPanning) {
+ this.panningListener = panningListener;
+ this.allowMarqueeSelection = allowMarqueeSelection;
+ this.allowPanning = allowPanning;
+
+ }
+
+ protected LayeredPane createPrintableLayers() {
+ FreeformLayeredPane layeredPane = new FreeformLayeredPane();
+ layeredPane.add(new ConnectionLayer(), CONNECTION_LAYER);
+ layeredPane.add(new FreeformLayer(), PRIMARY_LAYER);
+ layeredPane.add(new ConnectionLayer(), CONNECTION_FEEDBACK_LAYER);
+
+ return layeredPane;
+ }
+
+ /**
+ * Returns a drag tracker. If panning is allowed then a PanningTracker is returned.
+ * Otherwise either a {@link SingleSelectionTracker} (marqueeSelection disabled)
+ * or a {@link org.eclipse.gef.tools.MarqueeDragTracker} is returned.
+ * @see org.eclipse.gef.editparts.ScalableRootEditPart#getDragTracker(org.eclipse.gef.Request)
+ */
+ public DragTracker getDragTracker(Request req) {
+ if (allowPanning && (panningListener != null)) {
+ return new PanningTracker(this, panningListener, allowPanning);
+ } else if (!allowMarqueeSelection) {
+ // If we don't allow the marquee selection use our drag tracker
+ return new ViewerDragTracker(this);
+ }
+ return new MarqueeDragTracker();
+ }
+ /**
+ * Sets the scale for the Static Graph Viewer
+ * @param x
+ * @param y
+ */
+ public void setScale( double x, double y ) {
+ this.modelGraphEditPart.setScale(x, y);
+
+ }
+
+ /**
+ * Gets the scale in the X Direction
+ * @return
+ */
+ public double getXScale() {
+ return this.modelGraphEditPart.getXScale();
+ }
+
+
+ /**
+ * Gets the scale in the Y Direction
+ * @return
+ */
+ public double getYScale() {
+ return this.modelGraphEditPart.getYScale();
+ }
+
+
+ /**
+ * Sets the model root edit part. You should be able to set
+ * changes here and see the effect.
+ */
+ public void setModelRootEditPart(Object modelRootEditPart) {
+ this.modelGraphEditPart = (GraphEditPart) modelRootEditPart;
+ }
+
+ //@tag zest.bug.156286-Zooming : expose the graph edit part so that the zoom manager can be found.
+ public GraphEditPart getModelRootEditPart() {
+ return modelGraphEditPart;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ThreadedGraphicalEditor.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ThreadedGraphicalEditor.java
new file mode 100644
index 0000000..caaa338
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ThreadedGraphicalEditor.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.draw2d.DeferredUpdateManager;
+import org.eclipse.draw2d.LightweightSystem;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.gef.GraphicalViewer;
+import org.eclipse.gef.ui.parts.GraphicalEditor;
+import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
+import org.eclipse.mylar.zest.layouts.progress.ProgressEvent;
+import org.eclipse.mylar.zest.layouts.progress.ProgressListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ *
+ * @author Ian Bull
+ *
+ */
+public abstract class ThreadedGraphicalEditor extends GraphicalEditor implements ProgressListener {
+
+
+ class MyScrollingGraphicalViewer extends ScrollingGraphicalViewer {
+
+
+ class MyLightWeightSystem extends LightweightSystem {
+
+ class DisplaySynchronize implements Runnable {
+ GC _gc = null;
+ public DisplaySynchronize( GC gc ) {
+ _gc = gc;
+ }
+
+ public void run() {
+ MyLightWeightSystem.this._paint( _gc );
+ }
+ }
+
+ public void paint(GC gc) {
+ Display.getDefault().syncExec(new DisplaySynchronize( gc ) );
+ }
+
+ private void _paint( GC gc ) {
+ super.paint( gc );
+ }
+ }
+
+ class MyUpdateManager extends DeferredUpdateManager {
+ class DisplaySynchronize implements Runnable {
+
+ public void run() {
+ MyUpdateManager.this._queueWork( );
+ }
+
+ }
+
+ public void queueWork( ) {
+ //Display.getDefault().asyncExec(new DisplaySynchronize() );
+ Display.getDefault().syncExec(new DisplaySynchronize() );
+ }
+
+ public void _queueWork() {
+ super.queueWork();
+ }
+ }
+
+
+ protected LightweightSystem createLightweightSystem() {
+ LightweightSystem lws = new MyLightWeightSystem();
+ lws.setUpdateManager( new MyUpdateManager() );
+ return lws;
+ }
+
+ }
+
+ public void progressUpdated(ProgressEvent e) {
+ //TODO: Make this use the display thread
+ }
+
+ public Dimension getCanvasSize() {
+ //TODO: I don't think this really gets the canvas size
+ Point p = this.getGraphicalViewer().getControl().getSize();
+ return new Dimension(p.x, p.y );
+ }
+
+ /**
+ * Creates the GraphicalViewer on the specified <code>Composite</code>.
+ * @param parent the parent composite
+ */
+ protected void createGraphicalViewer(Composite parent) {
+ GraphicalViewer viewer = new MyScrollingGraphicalViewer();
+ viewer.createControl(parent);
+ setGraphicalViewer(viewer);
+ configureGraphicalViewer();
+ hookGraphicalViewer();
+ initializeGraphicalViewer();
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ThreadedGraphicalViewer.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ThreadedGraphicalViewer.java
new file mode 100644
index 0000000..8ecc264
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ThreadedGraphicalViewer.java
@@ -0,0 +1,401 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.DeferredUpdateManager;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.LightweightSystem;
+import org.eclipse.draw2d.UpdateManager;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.gef.DefaultEditDomain;
+import org.eclipse.gef.EditDomain;
+import org.eclipse.gef.ui.parts.GraphicalViewerImpl;
+import org.eclipse.mylar.zest.core.internal.viewers.figures.Graph;
+import org.eclipse.mylar.zest.layouts.Stoppable;
+import org.eclipse.mylar.zest.layouts.progress.ProgressEvent;
+import org.eclipse.mylar.zest.layouts.progress.ProgressListener;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * A Graphical Viewer Imple which is thread safe. The update manager syncs with the default thread
+ * before updating anything. This viewer also allows other threads to be added to it. The threads are added as
+ * "Stoppable" Objects. The stoppables are started when they are added, and when the view
+ * is disposed they are removed.
+ *
+ * @author Ian Bull
+ *
+ */
+public abstract class ThreadedGraphicalViewer extends GraphicalViewerImpl implements ProgressListener {
+
+ /** Holds all the threads added to this viewer */
+ private Map listOfThreads = null;
+ Composite parent = null;
+
+
+ /**
+ * ThreadedGraphicalViewer constructor.
+ * @param parent The composite that this viewer will be added to.
+ */
+ public ThreadedGraphicalViewer(Composite parent) {
+ super();
+ this.parent = parent;
+ listOfThreads = new LinkedHashMap();
+
+ updateManager = getLightweightSystem().getUpdateManager();
+
+ // Creates a new graph (a FigureCanvas)
+ Canvas canvas = new Graph(parent);
+ canvas.setLayout(new FillLayout());
+ setControl(canvas);
+
+ EditDomain ed = new DefaultEditDomain( null );
+ ed.addViewer( this );
+ //ed.setDefaultTool(new ZoomableSelectionTool());
+ setEditDomain( ed );
+
+ hookControl();
+
+ getControl().addControlListener(new ControlListener() {
+
+ public void controlMoved(ControlEvent e) {
+ fireControlMovedEvent( e );
+ }
+
+ public void controlResized(ControlEvent e) {
+ fireControlResizedEvent( e );
+ }
+ });
+
+ getControl().addDisposeListener(new DisposeListener() {
+
+ public void widgetDisposed(DisposeEvent e) {
+ removeAllThreads();
+ }
+
+ });
+
+ // TODO is this needed? A focus listener is added inside hookControl() above...
+ getControl().addFocusListener(new FocusListener() {
+ public void focusGained(FocusEvent e) {
+ //handleFocusGained(e);
+ }
+ public void focusLost(FocusEvent e) {
+ }
+ });
+
+ ((Canvas)getControl()).setBackground( ColorConstants.white );
+
+ FreqUpdater updater = new FreqUpdater();
+ updater.addProgressListener( new ProgressListener() {
+
+ public void progressStarted(ProgressEvent e) {
+ // TODO Auto-generated method stub
+
+
+ }
+
+ public void progressUpdated(ProgressEvent e) {
+ // TODO Auto-generated method stub
+ IFigure rootFigure = null;
+ try {
+ rootFigure = ThreadedGraphicalViewer.this.getLightweightSystem().getRootFigure();
+ }
+ catch ( Exception exception ) {
+ exception.printStackTrace();
+ }
+
+ if (updateManager != null && rootFigure != null )
+ ((MyUpdateManager)updateManager)._addInvalidFigure( rootFigure );
+ }
+
+ public void progressEnded(ProgressEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ });
+
+ addThread( updater );
+
+
+ }
+
+ private List controlListeners = new LinkedList();
+
+ public void addControlListener( ControlListener controlListener ) {
+ controlListeners.add( controlListener );
+ }
+
+ public boolean removeControlListener( ControlListener controlListener ) {
+ return controlListeners.remove( controlListener );
+ }
+
+ protected void fireControlMovedEvent( ControlEvent e ) {
+ for ( Iterator iterator = controlListeners.iterator(); iterator.hasNext(); ) {
+ ((ControlListener)iterator.next()).controlMoved( e );
+ }
+ }
+
+ protected void fireControlResizedEvent( ControlEvent e ) {
+ for ( Iterator iterator = controlListeners.iterator(); iterator.hasNext(); ) {
+ ((ControlListener)iterator.next()).controlResized( e );
+ }
+ }
+
+ /**
+ * Does some initializing of the viewer.
+ */
+ protected abstract void configureGraphicalViewer();
+
+ /**
+ * Sets the contents of the viewer and configures the graphical viewer.
+ * @param model
+ */
+ public void setContents(Object model) {
+ this.configureGraphicalViewer();
+ super.setContents(model);
+// @tag zest.experimental.contents : publish a property change that the model has changed. This will allow linked viewers to update.
+ setProperty(IZestViewerProperties.GRAPH_VIEWER_CONTENTS, model);
+
+ }
+
+ /**
+ * Updates the contents <b>without</b> configuring the graphical viewer.
+ * Only call this if the graphical viewer has already be configured.
+ * @param model
+ */
+ public void updateContents(Object model) {
+ super.setContents(model);
+// @tag zest.experimental.contents : publish a property change that the model has changed. This will allow linked viewers to update.
+ setProperty(IZestViewerProperties.GRAPH_VIEWER_CONTENTS, model);
+ }
+
+ /**
+ * Adds a new thread to this viewer and starts it.
+ * @param r
+ */
+ public void addThread( Stoppable r ) {
+ Thread thread = new Thread( r );
+ r.addProgressListener( this );
+ listOfThreads.put( r, thread );
+ thread.setPriority( java.lang.Thread.MAX_PRIORITY );
+ thread.start();
+ }
+
+ /**
+ * Gets the absolute size of the canvas.
+ * @return Dimension in absolute coords
+ */
+ public Dimension getCanvasSize() {
+ return new Dimension( getFigureCanvas().getSize() );
+ }
+
+ /**
+ * Gets the translated size of the canvas.
+ * @return Dimension relative
+ */
+ public Dimension getTranslatedCanvasSize() {
+ Dimension dim = getCanvasSize();
+ //getCanvas().get
+ //mainCanvas.getViewport().translateToRelative(dim);
+ return dim;
+ }
+
+ public Canvas getFigureCanvas() {
+ return (Canvas)getControl();
+ }
+
+ /**
+ * Removes and stop the thread
+ * @param r
+ */
+ public void removeThread( Stoppable r ) {
+ Thread t = (Thread) listOfThreads.get( r );
+ r.stop();
+
+ try {
+ t.join( 1000 );
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ if ( t.isAlive() ) {
+ StackTraceElement[] elements = t.getStackTrace();
+ String stackTrace = "";
+ for (int i = 0; i < elements.length; i++) {
+ stackTrace += elements[i].toString() + "\n";
+ }
+ throw new RuntimeException("Thread didn't stop, stack was: " + stackTrace);
+ }
+ listOfThreads.remove( r );
+ }
+
+
+ public void removeAllThreads () {
+ Set keySet = listOfThreads.keySet();
+ Stoppable[] keySetArray = (Stoppable[])keySet.toArray(new Stoppable[keySet.size()]);
+
+ for ( int i = 0; i < keySetArray.length; i++) {
+ removeThread( keySetArray[i] );
+
+ }
+ }
+
+ /**
+ *
+ */
+ public void progressUpdated(ProgressEvent e) {
+ //TODO: Make this use the display thread
+ }
+
+ public void progressEnded(ProgressEvent e) {
+ // TODO Auto-generated method stub
+ }
+
+ public void progressStarted(ProgressEvent e) {
+ // TODO Auto-generated method stub
+ }
+
+ protected LightweightSystem createLightweightSystem() {
+ //return new LightweightSystem();
+
+ LightweightSystem lws = new MyLightWeightSystem();
+ lws.setUpdateManager( new MyUpdateManager() );
+
+
+
+ return lws;
+
+ }
+
+
+class MyLightWeightSystem extends LightweightSystem {
+
+ class DisplaySynchronize implements Runnable {
+ GC _gc = null;
+ public DisplaySynchronize( GC gc ) {
+ _gc = gc;
+ }
+
+ public void run() {
+
+ MyLightWeightSystem.this._paint( _gc );
+
+ }
+ }
+
+ public void paint(GC gc) {
+ //DebugPrint.println("My LWS Paint!");
+ if ( getControl().isVisible() )
+ Display.getDefault().syncExec(new DisplaySynchronize( gc ) );
+ }
+
+ private void _paint( GC gc ) {
+ super.paint( gc );
+ }
+ }
+
+ class MyUpdateManager extends DeferredUpdateManager {
+ class DisplaySynchronize implements Runnable {
+
+ public void run() {
+ MyUpdateManager.this._queueWork( );
+ }
+
+ }
+
+ public synchronized void performUpdate() {
+ // TODO Auto-generated method stub
+// DebugPrint.println("Perform update called");
+ super.performUpdate();
+ }
+
+ public synchronized void performUpdate(Rectangle exposed) {
+ // TODO Auto-generated method stub
+// System.out.println("Perform Updated");
+ super.performUpdate(exposed);
+ }
+
+ public void queueWork( ) {
+ Display.getDefault().asyncExec(new DisplaySynchronize() );
+ }
+
+ public void _queueWork() {
+ super.queueWork();
+ }
+
+ public synchronized void _addInvalidFigure( IFigure f ) {
+ super.addInvalidFigure( f );
+ }
+
+ public synchronized void addInvalidFigure(IFigure f) {
+ // TODO Auto-generated method stub
+ super.addInvalidFigure(f);
+ // do nothing
+ }
+
+ }
+
+ UpdateManager updateManager = null;
+
+
+ public class FreqUpdater implements Stoppable {
+
+ //TODO: Why is this here? It doesn't do anything?
+ ArrayList listOfProgressListeners = new ArrayList();
+
+ public void addProgressListener(ProgressListener listener) {
+ listOfProgressListeners.add( listener );
+ }
+
+ boolean keepGoing = true;
+
+ public void stop() {
+ keepGoing = false;
+ }
+
+ public void run() {
+ while (keepGoing) {
+ if (listOfProgressListeners != null) {
+ //TODO: This is not thread safe
+ for ( int i = 0; i < listOfProgressListeners.size(); i++ ) {
+ ((ProgressListener) listOfProgressListeners.get(i)).progressUpdated( null );
+ }
+ }
+ try {
+ Thread.sleep(15);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ViewerDragTracker.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ViewerDragTracker.java
new file mode 100644
index 0000000..7a1f8e1
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ViewerDragTracker.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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 org.eclipse.mylar.zest.core.internal.gefx;
+
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.EditPartViewer;
+import org.eclipse.gef.tools.SelectEditPartTracker;
+import org.eclipse.swt.events.MouseEvent;
+
+/**
+ * @author Ian Bull
+ *
+ * This is our basic drag tracker that we use in our graph views.
+ * The mouse events are handled here.
+ */
+public class ViewerDragTracker extends SelectEditPartTracker {
+
+ public ViewerDragTracker(EditPart owner) {
+ super(owner);
+ }
+
+ /**
+ * A Mouse down event has happened on the canvas. If it is mouse button 1, then
+ * we can handle it, otherwise just ignore it. This means that users can add
+ * context menus with mouse button 2.
+ */
+ public void mouseDown(MouseEvent me, EditPartViewer viewer) {
+ if (me.button == 1 )
+ super.mouseDown(me, viewer);
+ return;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/XYScaledGraphics.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/XYScaledGraphics.java
new file mode 100644
index 0000000..962252c
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/XYScaledGraphics.java
@@ -0,0 +1,825 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ * Chisel Group, University of Victoria - Adapted for XY Scaled Graphics
+ *******************************************************************************/
+package org.eclipse.mylar.zest.core.internal.gefx;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.draw2d.FigureUtilities;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.ScaledGraphics;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.TextLayout;
+import org.eclipse.swt.graphics.TextStyle;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * This was adapted from the ScaledGraphics class to allow X and Y to scale
+ * independently. It won't require this level of coupling if some of these
+ * private methods were made protected. I will open a bug report on this.
+ *
+ * @author irbull
+ */
+public class XYScaledGraphics extends ScaledGraphics {
+
+ public static final double MAX_TEXT_SIZE = 0.45; // MAX size, when to stop zooming text
+
+ private static class FontHeightCache {
+ Font font;
+ int height;
+ }
+
+ static class FontKey {
+ Font font;
+ int height;
+ protected FontKey() { }
+ protected FontKey(Font font, int height) {
+ this.font = font;
+ this.height = height;
+ }
+
+ public boolean equals(Object obj) {
+ return (((FontKey)obj).font.equals(font)
+ && ((FontKey)obj).height == height);
+ }
+
+ public int hashCode() {
+ return font.hashCode() ^ height;
+ }
+
+ protected void setValues(Font font, int height) {
+ this.font = font;
+ this.height = height;
+ }
+ }
+
+ /**
+ * The internal state of the scaled graphics.
+ */
+ protected static class State {
+ private double appliedX;
+ private double appliedY;
+ private Font font;
+ private int lineWidth;
+ //private double zoom; // This has been replaced with xZoom and yZoom
+ private double xZoom;
+ private double yZoom;
+
+ /**
+ * Constructs a new, uninitialized State object.
+ */
+ protected State() { }
+
+ /**
+ * Constructs a new State object and initializes the properties based on the given
+ * values.
+ *
+ * @param zoom the zoom factor
+ * @param x the x offset
+ * @param y the y offset
+ * @param font the font
+ * @param lineWidth the line width
+ */
+ protected State(double xZoom, double yZoom, double x, double y, Font font, int lineWidth) {
+ this.xZoom = xZoom;
+ this.yZoom = yZoom;
+ this.appliedX = x;
+ this.appliedY = y;
+ this.font = font;
+ this.lineWidth = lineWidth;
+ }
+
+ /**
+ * Sets all the properties of the state object.
+ * @param zoom the zoom factor
+ * @param x the x offset
+ * @param y the y offset
+ * @param font the font
+ * @param lineWidth the line width
+ */
+ protected void setValues(double xZoom, double yZoom, double x, double y,
+ Font font, int lineWidth) {
+ this.xZoom = xZoom;
+ this.yZoom = yZoom;
+ this.appliedX = x;
+ this.appliedY = y;
+ this.font = font;
+ this.lineWidth = lineWidth;
+ }
+ }
+
+ private static int[][] intArrayCache = new int[8][];
+ private final Rectangle tempRECT = new Rectangle();
+
+ static {
+ for (int i = 0; i < intArrayCache.length; i++)
+ intArrayCache[i] = new int[i + 1];
+ }
+
+ private boolean allowText = true;
+// private static final Point PT = new Point();
+ private Map fontCache = new HashMap();
+ private Map fontDataCache = new HashMap();
+ private FontKey fontKey = new FontKey();
+ private double fractionalX;
+ private double fractionalY;
+ private Graphics graphics;
+ private FontHeightCache localCache = new FontHeightCache();
+ private Font localFont;
+ private int localLineWidth;
+ private List stack = new ArrayList();
+ private int stackPointer = 0;
+ private FontHeightCache targetCache = new FontHeightCache();
+
+ double xZoom = 1.0;
+ double yZoom = 1.0;
+
+ /**
+ * Constructs a new ScaledGraphics based on the given Graphics object.
+ * @param g the base graphics object
+ */
+ public XYScaledGraphics(Graphics g) {
+ super(g);
+ graphics = g;
+ localFont = g.getFont();
+ localLineWidth = g.getLineWidth();
+ }
+
+ /** @see Graphics#clipRect(Rectangle) */
+ public void clipRect(Rectangle r) {
+ graphics.clipRect(zoomClipRect(r));
+ }
+
+ Font createFont(FontData data) {
+ return new Font(Display.getCurrent(), data);
+ }
+
+ /** @see Graphics#dispose() */
+ public void dispose() {
+ //Remove all states from the stack
+ while (stackPointer > 0) {
+ popState();
+ }
+
+ //Dispose fonts
+ Iterator iter = fontCache.values().iterator();
+ while (iter.hasNext()) {
+ Font font = ((Font)iter.next());
+ font.dispose();
+ }
+
+ }
+
+ /** @see Graphics#drawArc(int, int, int, int, int, int) */
+ public void drawArc(int x, int y, int w, int h, int offset, int sweep) {
+ Rectangle z = zoomRect(x, y, w, h);
+ if (z.isEmpty() || sweep == 0)
+ return;
+ graphics.drawArc(z, offset, sweep);
+ }
+
+ /** @see Graphics#drawFocus(int, int, int, int) */
+ public void drawFocus(int x, int y, int w, int h) {
+ graphics.drawFocus(zoomRect(x, y, w, h));
+ }
+
+ /** @see Graphics#drawImage(Image, int, int) */
+ public void drawImage(Image srcImage, int x, int y) {
+ org.eclipse.swt.graphics.Rectangle size = srcImage.getBounds();
+ double imageZoom = Math.min(xZoom, yZoom);
+ graphics.drawImage(srcImage, 0, 0, size.width, size.height,
+ (int)(Math.floor((x * xZoom + fractionalX))),
+ (int)(Math.floor((y * yZoom + fractionalY))),
+ (int)(Math.floor((size.width * imageZoom + fractionalX))),
+ (int)(Math.floor((size.height * imageZoom + fractionalY))));
+ }
+
+ /** @see Graphics#drawImage(Image, int, int, int, int, int, int, int, int) */
+ public void drawImage(Image srcImage, int sx, int sy, int sw, int sh,
+ int tx, int ty, int tw, int th) {
+ //"t" == target rectangle, "s" = source
+
+ Rectangle t = zoomRect(tx, ty, tw, th);
+ if (!t.isEmpty())
+ graphics.drawImage(srcImage, sx, sy, sw, sh, t.x, t.y, t.width, t.height);
+ }
+
+ /** @see Graphics#drawLine(int, int, int, int) */
+ public void drawLine(int x1, int y1, int x2, int y2) {
+ graphics.drawLine(
+ (int)(Math.floor((x1 * xZoom + fractionalX))),
+ (int)(Math.floor((y1 * yZoom + fractionalY))),
+ (int)(Math.floor((x2 * xZoom + fractionalX))),
+ (int)(Math.floor((y2 * yZoom + fractionalY))));
+ }
+
+ /** @see Graphics#drawOval(int, int, int, int) */
+ public void drawOval(int x, int y, int w, int h) {
+ graphics.drawOval(zoomRect(x, y, w, h));
+ }
+
+ /** @see Graphics#drawPoint(int, int) */
+ public void drawPoint(int x, int y) {
+ graphics.drawPoint((int)Math.floor(x * xZoom + fractionalX),
+ (int)Math.floor(y * yZoom + fractionalY));
+ }
+
+ /**
+ * @see Graphics#drawPolygon(int[])
+ */
+ public void drawPolygon(int[] points) {
+ graphics.drawPolygon(zoomPointList(points));
+ }
+
+ /** @see Graphics#drawPolygon(PointList) */
+ public void drawPolygon(PointList points) {
+ graphics.drawPolygon(zoomPointList(points.toIntArray()));
+ }
+
+ /**
+ * @see Graphics#drawPolyline(int[])
+ */
+ public void drawPolyline(int[] points) {
+ graphics.drawPolyline(zoomPointList(points));
+ }
+
+ /** @see Graphics#drawPolyline(PointList) */
+ public void drawPolyline(PointList points) {
+ graphics.drawPolyline(zoomPointList(points.toIntArray()));
+ }
+
+ /** @see Graphics#drawRectangle(int, int, int, int) */
+ public void drawRectangle(int x, int y, int w, int h) {
+ graphics.drawRectangle(zoomRect(x, y, w, h));
+ }
+
+ /** @see Graphics#drawRoundRectangle(Rectangle, int, int) */
+ public void drawRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
+ graphics.drawRoundRectangle(zoomRect(r.x, r.y, r.width, r.height),
+ (int)(arcWidth * xZoom),
+ (int)(arcHeight * yZoom));
+ }
+
+ /** @see Graphics#drawString(String, int, int) */
+ public void drawString(String s, int x, int y) {
+ if (allowText)
+ graphics.drawString(s, zoomTextPoint(x, y));
+ }
+
+ /** @see Graphics#drawText(String, int, int) */
+ public void drawText(String s, int x, int y) {
+ if (allowText)
+ graphics.drawText(s, zoomTextPoint(x, y));
+ }
+
+ /**
+ * @see Graphics#drawText(String, int, int, int)
+ */
+ public void drawText(String s, int x, int y, int style) {
+ if (allowText)
+ graphics.drawText(s, zoomTextPoint(x, y), style);
+ }
+
+ /**
+ * @see Graphics#drawTextLayout(TextLayout, int, int, int, int, Color, Color)
+ */
+ public void drawTextLayout(TextLayout layout, int x, int y, int selectionStart,
+ int selectionEnd, Color selectionForeground, Color selectionBackground) {
+ TextLayout scaled = zoomTextLayout(layout);
+ graphics.drawTextLayout(scaled,
+ (int)Math.floor(x * xZoom + fractionalX),
+ (int)Math.floor(y * yZoom + fractionalY),
+ selectionStart, selectionEnd, selectionBackground, selectionForeground);
+ scaled.dispose();
+ }
+
+ /** @see Graphics#fillArc(int, int, int, int, int, int) */
+ public void fillArc(int x, int y, int w, int h, int offset, int sweep) {
+ Rectangle z = zoomFillRect(x, y, w, h);
+ if (z.isEmpty() || sweep == 0)
+ return;
+ graphics.fillArc(z, offset, sweep);
+ }
+
+ /** @see Graphics#fillGradient(int, int, int, int, boolean) */
+ public void fillGradient(int x, int y, int w, int h, boolean vertical) {
+ graphics.fillGradient(zoomFillRect(x, y, w, h), vertical);
+ }
+
+ /** @see Graphics#fillOval(int, int, int, int) */
+ public void fillOval(int x, int y, int w, int h) {
+ graphics.fillOval(zoomFillRect(x, y, w, h));
+ }
+
+ /**
+ * @see Graphics#fillPolygon(int[])
+ */
+ public void fillPolygon(int[] points) {
+ graphics.fillPolygon(zoomPointList(points));
+ }
+
+ /** @see Graphics#fillPolygon(PointList) */
+ public void fillPolygon(PointList points) {
+ graphics.fillPolygon(zoomPointList(points.toIntArray()));
+ }
+
+ /** @see Graphics#fillRectangle(int, int, int, int) */
+ public void fillRectangle(int x, int y, int w, int h) {
+ graphics.fillRectangle(zoomFillRect(x, y, w, h));
+ }
+
+ /** @see Graphics#fillRoundRectangle(Rectangle, int, int) */
+ public void fillRoundRectangle(Rectangle r, int arcWidth, int arcHeight) {
+ graphics.fillRoundRectangle(zoomFillRect(r.x, r.y, r.width, r.height),
+ (int)(arcWidth * xZoom),
+ (int)(arcHeight * yZoom));
+ }
+
+ /** @see Graphics#fillString(String, int, int) */
+ public void fillString(String s, int x, int y) {
+ if (allowText)
+ graphics.fillString(s, zoomTextPoint(x, y));
+ }
+
+ /** @see Graphics#fillText(String, int, int) */
+ public void fillText(String s, int x, int y) {
+ if (allowText)
+ graphics.fillText(s, zoomTextPoint(x, y));
+ }
+
+ /**
+ * @see Graphics#getAbsoluteScale()
+ */
+ public double getAbsoluteScale() {
+ return xZoom * graphics.getAbsoluteScale();
+ }
+
+ /**
+ * @see Graphics#getAlpha()
+ */
+ public int getAlpha() {
+ return graphics.getAlpha();
+ }
+
+ /**
+ * @see Graphics#getAntialias()
+ */
+ public int getAntialias() {
+ return graphics.getAntialias();
+ }
+
+ /** @see Graphics#getBackgroundColor() */
+ public Color getBackgroundColor() {
+ return graphics.getBackgroundColor();
+ }
+
+ Font getCachedFont(FontKey key) {
+ Font font = (Font)fontCache.get(key);
+ if (font != null)
+ return font;
+ key = new FontKey(key.font, key.height);
+ FontData data = key.font.getFontData()[0];
+ data.setHeight(key.height);
+ Font zoomedFont = createFont(data);
+ fontCache.put(key, zoomedFont);
+ return zoomedFont;
+ }
+
+ FontData getCachedFontData(Font f) {
+ FontData data = (FontData)fontDataCache.get(f);
+ if (data != null)
+ return data;
+ data = getLocalFont().getFontData()[0];
+ fontDataCache.put(f, data);
+ return data;
+ }
+
+ /** @see Graphics#getClip(Rectangle) */
+ public Rectangle getClip(Rectangle rect) {
+ graphics.getClip(rect);
+ int x = (int)(rect.x / xZoom);
+ int y = (int)(rect.y / yZoom);
+ /*
+ * If the clip rectangle is queried, perform an inverse zoom, and take the ceiling of
+ * the resulting double. This is necessary because forward scaling essentially performs
+ * a floor() function. Without this, figures will think that they don't need to paint
+ * when actually they do.
+ */
+ rect.width = (int)Math.ceil(rect.right() / xZoom) - x;
+ rect.height = (int)Math.ceil(rect.bottom() / yZoom) - y;
+ rect.x = x;
+ rect.y = y;
+ return rect;
+ }
+
+ /**
+ * @see Graphics#getFillRule()
+ */
+ public int getFillRule() {
+ return graphics.getFillRule();
+ }
+
+ /** @see Graphics#getFont() */
+ public Font getFont() {
+ return getLocalFont();
+ }
+
+ /** @see Graphics#getFontMetrics() */
+ public FontMetrics getFontMetrics() {
+ return FigureUtilities.getFontMetrics(localFont);
+ }
+
+ /** @see Graphics#getForegroundColor() */
+ public Color getForegroundColor() {
+ return graphics.getForegroundColor();
+ }
+
+ /**
+ * @see Graphics#getInterpolation()
+ */
+ public int getInterpolation() {
+ return graphics.getInterpolation();
+ }
+
+ /**
+ * @see Graphics#getLineCap()
+ */
+ public int getLineCap() {
+ return graphics.getLineCap();
+ }
+
+ /**
+ * @see Graphics#getLineJoin()
+ */
+ public int getLineJoin() {
+ return graphics.getLineJoin();
+ }
+
+ /** @see Graphics#getLineStyle() */
+ public int getLineStyle() {
+ return graphics.getLineStyle();
+ }
+
+ /** @see Graphics#getLineWidth() */
+ public int getLineWidth() {
+ return getLocalLineWidth();
+ }
+
+ private Font getLocalFont() {
+ return localFont;
+ }
+
+ private int getLocalLineWidth() {
+ return localLineWidth;
+ }
+
+ /**
+ * @see Graphics#getTextAntialias()
+ */
+ public int getTextAntialias() {
+ return graphics.getTextAntialias();
+ }
+
+ /** @see Graphics#getXORMode() */
+ public boolean getXORMode() {
+ return graphics.getXORMode();
+ }
+
+ /** @see Graphics#popState() */
+ public void popState() {
+ graphics.popState();
+ stackPointer--;
+ restoreLocalState((State)stack.get(stackPointer));
+ }
+
+ /** @see Graphics#pushState() */
+ public void pushState() {
+ State s;
+ if (stack.size() > stackPointer) {
+ s = (State)stack.get(stackPointer);
+ s.setValues(xZoom, yZoom, fractionalX, fractionalY, getLocalFont(), localLineWidth);
+ } else {
+ stack.add(new State(xZoom, yZoom, fractionalX, fractionalY, getLocalFont(),
+ localLineWidth));
+ }
+ stackPointer++;
+
+ graphics.pushState();
+ }
+
+ private void restoreLocalState(State state) {
+ this.fractionalX = state.appliedX;
+ this.fractionalY = state.appliedY;
+ setScale(state.xZoom, state.yZoom);
+ setLocalFont(state.font);
+ setLocalLineWidth(state.lineWidth);
+ }
+
+ /** @see Graphics#restoreState() */
+ public void restoreState() {
+ graphics.restoreState();
+ restoreLocalState((State)stack.get(stackPointer - 1));
+ }
+
+ public void scale( double xAmount, double yAmount ) {
+ setScale(xZoom * xAmount, yZoom* yAmount);
+
+ }
+
+ /** @see Graphics#scale(double) */
+ public void scale(double amount) {
+ //setScale(zoom * amount);
+ throw new RuntimeException("Operation not supported, use scale(x, y)");
+ }
+
+ /**
+ * @see Graphics#setAlpha(int)
+ */
+ public void setAlpha(int alpha) {
+ graphics.setAlpha(alpha);
+ }
+
+ /**
+ * @see Graphics#setAntialias(int)
+ */
+ public void setAntialias(int value) {
+ graphics.setAntialias(value);
+ }
+
+ /** @see Graphics#setBackgroundColor(Color) */
+ public void setBackgroundColor(Color rgb) {
+ graphics.setBackgroundColor(rgb);
+ }
+
+ /** @see Graphics#setClip(Rectangle) */
+ public void setClip(Rectangle r) {
+ graphics.setClip(zoomClipRect(r));
+ }
+
+ /**
+ * @see Graphics#setFillRule(int)
+ */
+ public void setFillRule(int rule) {
+ graphics.setFillRule(rule);
+ }
+
+ /** @see Graphics#setFont(Font) */
+ public void setFont(Font f) {
+ setLocalFont(f);
+ }
+
+ /** @see Graphics#setForegroundColor(Color) */
+ public void setForegroundColor(Color rgb) {
+ graphics.setForegroundColor(rgb);
+ }
+
+ /**
+ * @see org.eclipse.draw2d.Graphics#setInterpolation(int)
+ */
+ public void setInterpolation(int interpolation) {
+ graphics.setInterpolation(interpolation);
+ }
+
+ /**
+ * @see Graphics#setLineCap(int)
+ */
+ public void setLineCap(int cap) {
+ graphics.setLineCap(cap);
+ }
+
+ /**
+ * @see Graphics#setLineDash(int[])
+ */
+ public void setLineDash(int[] dash) {
+ graphics.setLineDash(dash);
+ }
+
+ /**
+ * @see Graphics#setLineJoin(int)
+ */
+ public void setLineJoin(int join) {
+ graphics.setLineJoin(join);
+ }
+
+ /** @see Graphics#setLineStyle(int) */
+ public void setLineStyle(int style) {
+ graphics.setLineStyle(style);
+ }
+
+ /** @see Graphics#setLineWidth(int) */
+ public void setLineWidth(int width) {
+ setLocalLineWidth(width);
+ }
+
+ private void setLocalFont(Font f) {
+ localFont = f;
+ graphics.setFont(zoomFont(f));
+ }
+
+ private void setLocalLineWidth(int width) {
+ localLineWidth = width;
+ graphics.setLineWidth(zoomLineWidth(width));
+ }
+
+
+ public void setScale( double xValue, double yValue ) {
+ if ( xValue == xZoom && yValue == yZoom ) {
+ return;
+ }
+ this.xZoom = xValue;
+ this.yZoom = yValue;
+ graphics.setFont(zoomFont(getLocalFont()));
+ graphics.setLineWidth(zoomLineWidth(localLineWidth));
+ }
+
+ void setScale(double value) {
+ throw new RuntimeException("Operation not supported, use setScale(x,y)");
+
+ /*
+ if (zoom == value)
+ return;
+ this.zoom = value;
+ graphics.setFont(zoomFont(getLocalFont()));
+ graphics.setLineWidth(zoomLineWidth(localLineWidth));
+ */
+ }
+
+ /**
+ * @see Graphics#setTextAntialias(int)
+ */
+ public void setTextAntialias(int value) {
+ graphics.setTextAntialias(value);
+ }
+
+ /** @see Graphics#setXORMode(boolean) */
+ public void setXORMode(boolean b) {
+ graphics.setXORMode(b);
+ }
+
+ /** @see Graphics#translate(int, int) */
+ public void translate(int dx, int dy) {
+ // fractionalX/Y is the fractional part left over from previous
+ // translates that gets lost in the integer approximation.
+ double dxFloat = dx * xZoom + fractionalX;
+ double dyFloat = dy * yZoom + fractionalY;
+ fractionalX = dxFloat - Math.floor(dxFloat);
+ fractionalY = dyFloat - Math.floor(dyFloat);
+ graphics.translate((int)Math.floor(dxFloat), (int)Math.floor(dyFloat));
+ }
+
+ private Rectangle zoomClipRect(Rectangle r) {
+ tempRECT.x = (int)(Math.floor(r.x * xZoom + fractionalX));
+ tempRECT.y = (int)(Math.floor(r.y * yZoom + fractionalY));
+ tempRECT.width = (int)(Math.ceil(((r.x + r.width) * xZoom + fractionalX))) - tempRECT.x;
+ tempRECT.height = (int)(Math.ceil(((r.y + r.height) * yZoom + fractionalY))) - tempRECT.y;
+ return tempRECT;
+ }
+
+ private Rectangle zoomFillRect(int x, int y, int w, int h) {
+ tempRECT.x = (int)(Math.floor((x * xZoom + fractionalX)));
+ tempRECT.y = (int)(Math.floor((y * yZoom + fractionalY)));
+ tempRECT.width = (int)(Math.floor(((x + w - 1) * xZoom + fractionalX))) - tempRECT.x + 1;
+ tempRECT.height = (int)(Math.floor(((y + h - 1) * yZoom + fractionalY))) - tempRECT.y + 1;
+ return tempRECT;
+ }
+
+ Font zoomFont(Font f) {
+ if (f == null)
+ f = Display.getCurrent().getSystemFont();
+ FontData data = getCachedFontData(f);
+ int zoomedFontHeight = zoomFontHeight(data.getHeight());
+ allowText = zoomedFontHeight > 0;
+ fontKey.setValues(f, zoomedFontHeight);
+ return getCachedFont(fontKey);
+ }
+
+ int zoomFontHeight(int height) {
+ if ( yZoom < MAX_TEXT_SIZE )
+ return (int)(yZoom * height);
+ else return height;
+ }
+
+ int zoomLineWidth(int w) {
+ return w;
+ }
+
+ private int[] zoomPointList(int[] points) {
+ int[] scaled = null;
+
+ // Look in cache for a integer array with the same length as 'points'
+ for (int i = 0; i < intArrayCache.length; i++) {
+ if (intArrayCache[i].length == points.length) {
+ scaled = intArrayCache[i];
+
+ // Move this integer array up one notch in the array
+ if (i != 0) {
+ int[] temp = intArrayCache[i - 1];
+ intArrayCache[i - 1] = scaled;
+ intArrayCache[i] = temp;
+ }
+ }
+ }
+
+ // If no match is found, take the one that is last and resize it.
+ if (scaled == null) {
+ intArrayCache[intArrayCache.length - 1] = new int[points.length];
+ scaled = intArrayCache[intArrayCache.length - 1];
+ }
+
+ // Scale the points
+ for (int i = 0; (i + 1) < points.length; i += 2) {
+ scaled[i] = (int)(Math.floor((points[i] * xZoom + fractionalX)));
+ scaled[i + 1] = (int)(Math.floor((points[i + 1] * yZoom + fractionalY)));
+ }
+ return scaled;
+ }
+
+ private Rectangle zoomRect(int x, int y, int w, int h) {
+ tempRECT.x = (int)(Math.floor(x * xZoom + fractionalX));
+ tempRECT.y = (int)(Math.floor(y * yZoom + fractionalY));
+ tempRECT.width = (int)(Math.floor(((x + w) * xZoom + fractionalX))) - tempRECT.x;
+ tempRECT.height = (int)(Math.floor(((y + h) * yZoom + fractionalY))) - tempRECT.y;
+ return tempRECT;
+ }
+
+ private TextLayout zoomTextLayout(TextLayout layout) {
+ TextLayout zoomed = new TextLayout(Display.getCurrent());
+ zoomed.setText(layout.getText());
+
+ int zoomWidth = -1;
+
+ if (layout.getWidth() != -1)
+ zoomWidth = ((int)(layout.getWidth() * xZoom));
+
+ if (zoomWidth < -1 || zoomWidth == 0)
+ return null;
+
+ zoomed.setFont(zoomFont(layout.getFont()));
+ zoomed.setAlignment(layout.getAlignment());
+ zoomed.setAscent(layout.getAscent());
+ zoomed.setDescent(layout.getDescent());
+ zoomed.setOrientation(layout.getOrientation());
+ zoomed.setSegments(layout.getSegments());
+ zoomed.setSpacing(layout.getSpacing());
+ zoomed.setTabs(layout.getTabs());
+
+ zoomed.setWidth(zoomWidth);
+ int length = layout.getText().length();
+ if (length > 0) {
+ int start = 0, offset = 1;
+ TextStyle style = null, lastStyle = layout.getStyle(0);
+ for (; offset <= length; offset++) {
+ if (offset != length
+ && (style = layout.getStyle(offset)) == lastStyle)
+ continue;
+ int end = offset - 1;
+
+ if (lastStyle != null) {
+ TextStyle zoomedStyle = new TextStyle(zoomFont(lastStyle.font),
+ lastStyle.foreground, lastStyle.background);
+ zoomed.setStyle(zoomedStyle, start, end);
+ }
+ lastStyle = style;
+ start = offset;
+ }
+ }
+ return zoomed;
+ }
+
+ private Point zoomTextPoint(int x, int y) {
+ if (localCache.font != localFont) {
+ //Font is different, re-calculate its height
+ FontMetrics metric = FigureUtilities.getFontMetrics(localFont);
+ localCache.height = metric.getHeight() - metric.getDescent();
+ localCache.font = localFont;
+ }
+ if (targetCache.font != graphics.getFont()) {
+ FontMetrics metric = graphics.getFontMetrics();
+ targetCache.font = graphics.getFont();
+ targetCache.height = metric.getHeight() - metric.getDescent();
+ }
+ return new Point(((int)(Math.floor((x * xZoom) + fractionalX))),
+ (int)(Math.floor((y + localCache.height - 1) * yZoom
+ - targetCache.height + 1 + fractionalY)));
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ZestRootEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ZestRootEditPart.java
new file mode 100644
index 0000000..b604cb7
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/gefx/ZestRootEditPart.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * 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:
+ * IBM Corporation - initial API and implementation
+ * Chisel Group, University of Victoria - Adapted for XY Scaled Graphics
+ *******************************************************************************/
+package org.eclipse.mylar.zest.core.internal.gefx;
+
+
+
+/**
+ * All root edit parts should enable this. This allows the GEF Factory
+ * to set the top level model element with the root edit part.
+ * @author Ian Bull
+ */
+public interface ZestRootEditPart {
+ public static final String CONNECTION_FEEDBACK_LAYER = "Connection Feedback Layer"; //$NON-NLS-1$
+
+ public void setModelRootEditPart( Object modelRootEditPart );
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/AbstractStylingModelFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/AbstractStylingModelFactory.java
new file mode 100644
index 0000000..8e892aa
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/AbstractStylingModelFactory.java
@@ -0,0 +1,307 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+/**
+ * Base class that can be used for model factories. Offers facilities to
+ * style the items that have been created by the factory.
+ * @author Del Myers
+ *
+ */
+//@tag zest.bug.160367-Refreshing.fix : update the factory to use the IStylingGraphModelFactory
+public abstract class AbstractStylingModelFactory implements IStylingGraphModelFactory {
+ private StructuredViewer viewer;
+ private int connectionStyle;
+ private int nodeStyle;
+
+ /**
+ *
+ */
+ public AbstractStylingModelFactory(StructuredViewer viewer) {
+ this.viewer = viewer;
+ this.connectionStyle = IZestGraphDefaults.CONNECTION_STYLE;
+ this.nodeStyle = IZestGraphDefaults.NODE_STYLE;
+ }
+
+ public void styleConnection(IGraphModelConnection conn) {
+ //recount the source and target connections on the node.
+ //this isn't a great way to do it, because it results in
+ //an n^2 algorithm. But, if anyone can figure out a better way
+ //go ahead and try it.
+ IGraphModelNode source = conn.getSource();
+ IGraphModelNode dest = conn.getDestination();
+ LinkedList rightList = getConnectionList(source, dest);
+
+ LinkedList leftList = null;
+
+ if (dest != source) {
+ leftList = getConnectionList(dest, source);
+ }
+
+ //adjust the arcs going from source to destination
+ adjustCurves(rightList);
+ //adjust the arcs going from destination to source
+ if (leftList != null)
+ adjustCurves(leftList);
+ }
+
+ /**
+ * Takes a list of IGraphModelConnections and adjusts the curve depths and the
+ * bezier curves based on the number of curves in the list.
+ * @param rightList
+ */
+ protected void adjustCurves(List connections) {
+ int scale = 3;
+ for (int i = 0; i < connections.size(); i++) {
+ IGraphModelConnection conn = (IGraphModelConnection) connections.get(i);
+ if (conn.getSource() == conn.getDestination()) scale = 5;
+ //even if the connection isn't curved in the style, the edit part
+ //may decide that it should be curved if source and dest are equal.
+ //@tag drawing(arcs) : check here if arcs are too close when being drawn. Adjust the constant.
+ int lineWidth = conn.getLineWidth();
+ conn.setCurveDepth((i+1)*(scale+lineWidth));
+
+// @tag zest(bug(152530-Bezier(fix))) : set the angles, etc based on the count.
+ //limit the angle to 90 degrees.
+ conn.setStartAngle(90.0 - 85.0/Math.pow(i, 1.0/9.0));
+ conn.setEndAngle(85.0/Math.pow(i, 1.0/9.0) - 90.0);
+ //limit the length to 1
+ conn.setStartLength(.75 - .25/(Math.sqrt(i)));
+ conn.setEndLength(.75 - .25/(Math.sqrt(i)));
+ }
+ }
+
+
+ /**
+ * @param source
+ * @param dest
+ * @return
+ */
+ private LinkedList getConnectionList(IGraphModelNode source, IGraphModelNode dest) {
+ LinkedList list = new LinkedList();
+ Iterator i = source.getSourceConnections().iterator();
+ while (i.hasNext()) {
+ IGraphModelConnection c = (IGraphModelConnection) i.next();
+ if (c.getDestination() == dest) {
+ list.add(c);
+ }
+ }
+ return list;
+ }
+
+ public void styleItem(IGraphItem item) {
+ GraphItemStyler.styleItem(item, getLabelProvider());
+ if (item instanceof IGraphModelConnection) styleConnection((IGraphModelConnection) item);
+ }
+
+ public StructuredViewer getViewer() {
+ return viewer;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#getLabelProvider()
+ */
+ public IBaseLabelProvider getLabelProvider() {
+ return viewer.getLabelProvider();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#getContentProvider()
+ */
+ public IStructuredContentProvider getContentProvider() {
+ return (IStructuredContentProvider) viewer.getContentProvider();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#createConnection(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object, java.lang.Object, java.lang.Object)
+ */
+ public IGraphModelConnection createConnection(GraphModel graph, Object element, Object source, Object dest) {
+ if (source == null || dest == null) return null;
+ IGraphModelConnection oldConnection = graph.getInternalConnection(element);
+ IGraphModelNode sn = graph.getInternalNode(source);
+ IGraphModelNode dn = graph.getInternalNode(dest);
+ if (oldConnection != null) {
+ if (sn != oldConnection.getSource() || dn != oldConnection.getDestination()) {
+ graph.removeConnection(oldConnection);
+ } else {
+ styleItem(oldConnection);
+ return oldConnection;
+ }
+ }
+ if (sn == null) {
+ sn = createNode(graph, source);
+ }
+ if (dn == null) {
+ dn = createNode(graph, dest);
+ }
+ GraphModelConnection c = new GraphModelConnection(graph, element, sn, dn);
+ styleItem(c);
+ graph.addConnection(element, c);
+ return c;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#createNode(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
+ */
+ public IGraphModelNode createNode(GraphModel graph, Object element) {
+ IGraphModelNode node = graph.getInternalNode(element);
+ if (node == null) {
+ node = new GraphModelNode(graph, element);
+ styleItem(node);
+ graph.addNode(element, node);
+ } else {
+ styleItem(node);
+ }
+ return node;
+ }
+
+ public void setConnectionStyle(int style) {
+ this.connectionStyle = style;
+ }
+
+ /**
+ * @return the connectionStyle
+ */
+ public int getConnectionStyle() {
+ return connectionStyle;
+ }
+
+ public void setNodeStyle(int style) {
+ this.nodeStyle = style;
+ }
+
+ /**
+ * @return the nodeStyle
+ */
+ public int getNodeStyle() {
+ return nodeStyle;
+ }
+
+ /**
+ * Default implementation simply restyles the item, regardless of the properties.
+ */
+ public void update(IGraphItem item) {
+ styleItem(item);
+ }
+
+ /**
+ * Default implementation simply restyles the items, regardless of the properties.
+ */
+ public void update(IGraphItem[] items) {
+ for (int i = 0; i < items.length; i++) {
+ styleItem(items[i]);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#refreshGraph(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel)
+ */
+ public void refreshGraph(GraphModel graph) {
+ //with this kind of graph, it is just as easy and cost-effective to
+ //rebuild the whole thing.
+
+ Map oldMap = graph.getNodesMap();
+ HashMap nodesMap = new HashMap();
+ //have to copy the Map data accross so that it doesn't get overwritten
+ for (Iterator keys = oldMap.keySet().iterator(); keys.hasNext();) {
+ Object key = keys.next();
+ nodesMap.put(key, oldMap.get(key));
+ }
+ clearGraph(graph);
+ doBuildGraph(graph);
+ //update the positions on the new nodes to match the old ones.
+ IGraphModelNode[] nodes = graph.getNodesArray();
+ //save a little time, go with the smallest list as the primary list
+ if (nodes.length < nodesMap.keySet().size()) {
+ for (int i = 0; i < nodes.length; i++) {
+ IGraphModelNode oldNode = (IGraphModelNode) nodesMap.get(nodes[i].getExternalNode());
+ if (oldNode != null) {
+ nodes[i].setPreferredLocation(oldNode.getXInLayout(), oldNode.getYInLayout());
+ }
+ }
+ } else {
+ for (Iterator i = nodesMap.keySet().iterator(); i.hasNext();) {
+ Object key = i.next();
+ IGraphModelNode node = graph.getInternalNode(key);
+ if (node != null) {
+ IGraphModelNode oldNode = (IGraphModelNode) nodesMap.get(key);
+ node.setPreferredLocation(oldNode.getXInLayout(), oldNode.getYInLayout());
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * Convenience method for clearing all the elements in the graph.
+ * @param graph
+ */
+ public void clearGraph(GraphModel graph) {
+ graph.clearProxies();
+ IGraphModelNode[] nodes = graph.getNodesArray();
+ for (int i = 0; i < nodes.length; i++) {
+ graph.removeNode(nodes[i]);
+ }
+ }
+
+ /**
+ * Builds the graph model from the viewer's content provider. There is no guarantee that the
+ * model will be cleared before this method is called.
+ * @param graph
+ */
+ protected abstract void doBuildGraph(GraphModel graph);
+
+ /**
+ * Determines if this element should be filtered or not.
+ * @param parent
+ * @param element
+ * @return
+ */
+ protected boolean filterElement(Object parent, Object element) {
+ ViewerFilter[] filters = getViewer().getFilters();
+ for (int i = 0; i < filters.length; i++) {
+ boolean selected = filters[i].select(viewer, parent, element);
+ if ( !selected ) return true;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#isFiltered(java.lang.Object)
+ */
+ protected Object[] filter(Object parent, Object[] elements) {
+ Object[] result = elements;
+ ViewerFilter[] filters = getViewer().getFilters();
+ for (int i = 0; i < filters.length; i++) {
+ result = filters[i].filter(viewer, parent, result);
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
+ */
+ public void refresh(GraphModel graph, Object element) {
+ refresh(graph, element, false);
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphItem.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphItem.java
new file mode 100644
index 0000000..bfcabf6
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphItem.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Provides support for property changes. All model elements extend this class.
+ * Also extends the Item (Widget) class to be used inside a StructuredViewer.
+ *
+ * @author Chris Callendar
+ */
+public abstract class GraphItem extends Item implements IGraphItem {
+
+ /** Delegate used to implemenent property-change-support. */
+ private transient PropertyChangeSupport pcsDelegate = new PropertyChangeSupport(this);
+ private boolean isVisible;
+ /**
+ * @param parent
+ * @param style
+ */
+ public GraphItem(Widget parent) {
+ super(parent, SWT.NO_BACKGROUND);
+ isVisible = true;
+ }
+
+
+
+ /**
+ * Attach a non-null PropertyChangeListener to this object.
+ * @param l a non-null PropertyChangeListener instance
+ * @throws IllegalArgumentException if the parameter is null
+ */
+ public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
+ if (l == null) {
+ throw new IllegalArgumentException();
+ }
+ pcsDelegate.addPropertyChangeListener(l);
+ }
+
+ /**
+ * Remove a PropertyChangeListener from this component.
+ * @param l a PropertyChangeListener instance
+ */
+ public synchronized void removePropertyChangeListener(PropertyChangeListener l) {
+ if (l != null) {
+ pcsDelegate.removePropertyChangeListener(l);
+ }
+ }
+
+ /**
+ * Report a property change to registered listeners (for example edit parts).
+ * @param property the programmatic name of the property that changed
+ * @param oldValue the old value of this property
+ * @param newValue the new value of this property
+ */
+ public void firePropertyChange(String property, Object oldValue, Object newValue) {
+ if (pcsDelegate.hasListeners(property)) {
+ pcsDelegate.firePropertyChange(property, oldValue, newValue);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphItem#isVisible()
+ */
+ public boolean isVisible() {
+ return isVisible;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphItem#setVisible(boolean)
+ */
+ public void setVisible(boolean visible) {
+ boolean old = isVisible();
+ this.isVisible = visible;
+ if (old ^ visible)
+ firePropertyChange(VISIBLE_PROP, new Boolean(old), new Boolean(visible));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Widget#dispose()
+ */
+ public void dispose() {
+ //@tag zest.bug.167132-ListenerDispose : remove all listeners.
+ pcsDelegate = new PropertyChangeSupport(this);
+ super.dispose();
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphItemStyler.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphItemStyler.java
new file mode 100644
index 0000000..1459a74
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphItemStyler.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.IFontProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.mylar.zest.core.ZestException;
+import org.eclipse.mylar.zest.core.ZestPlugin;
+import org.eclipse.mylar.zest.core.ZestStyles;
+import org.eclipse.mylar.zest.core.viewers.IConnectionStyleBezierExtension;
+import org.eclipse.mylar.zest.core.viewers.IConnectionStyleProvider;
+import org.eclipse.mylar.zest.core.viewers.IEntityConnectionStyleBezierExtension;
+import org.eclipse.mylar.zest.core.viewers.IEntityConnectionStyleProvider;
+import org.eclipse.mylar.zest.core.viewers.IEntityStyleProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+
+/**
+ * Helper class used to style graph elements based on graph element stylers.
+ * @author Del Myers
+ *
+ */
+//@tag bug(151327-Styles) : created to help resolve this bug
+public class GraphItemStyler {
+ public static void styleItem(IGraphItem item, final IBaseLabelProvider labelProvider) {
+
+ if (item instanceof IGraphModelNode) {
+ IGraphModelNode node = (IGraphModelNode)item;
+ //set defaults.
+ if (node.getGraphModel().getNodeStyle() != ZestStyles.NONE) {
+ node.setNodeStyle(node.getGraphModel().getNodeStyle());
+ } else {
+ node.setNodeStyle(IZestGraphDefaults.NODE_STYLE);
+ }
+ Object entity= node.getExternalNode();
+ if (labelProvider instanceof IEntityStyleProvider) {
+ styleNode(node, (IEntityStyleProvider)labelProvider);
+ }
+ if (labelProvider instanceof IColorProvider) {
+ IColorProvider colorProvider = (IColorProvider) labelProvider;
+ node.setForegroundColor(colorProvider.getForeground(entity));
+ node.setBackgroundColor(colorProvider.getBackground(entity));
+ }
+ if (labelProvider instanceof IFontProvider) {
+ IFontProvider fontProvider = (IFontProvider) labelProvider;
+ node.setFont(fontProvider.getFont(entity));
+ }
+ if (labelProvider instanceof ILabelProvider) {
+ String text = ((ILabelProvider)labelProvider).getText(node.getExternalNode());
+ node.setText((text != null) ? text : "");
+ node.setImage(((ILabelProvider)labelProvider).getImage(node.getExternalNode()));
+ }
+ } else if (item instanceof IGraphModelConnection) {
+ IGraphModelConnection conn = (IGraphModelConnection) item;
+
+ //set defaults
+ if (conn.getGraphModel().getConnectionStyle() != ZestStyles.NONE) {
+ int s = conn.getGraphModel().getConnectionStyle();
+ conn.setConnectionStyle(s);
+ } else {
+ conn.setConnectionStyle(IZestGraphDefaults.CONNECTION_STYLE);
+ }
+ if (labelProvider instanceof ILabelProvider) {
+ String text = ((ILabelProvider)labelProvider).getText(conn.getExternalConnection());
+ conn.setText((text != null) ? text : "");
+ conn.setImage(((ILabelProvider)labelProvider).getImage(conn.getExternalConnection()));
+ }
+ if (labelProvider instanceof IEntityConnectionStyleProvider) {
+ styleEntityConnection(conn, (IEntityConnectionStyleProvider)labelProvider);
+ } else if (labelProvider instanceof IConnectionStyleProvider) {
+ styleConnection(conn, (IConnectionStyleProvider)labelProvider);
+ }
+ int swt = getLineStyleForZestStyle(conn.getConnectionStyle());
+ conn.setLineStyle(swt);
+
+ }
+ }
+
+ /**
+ * @param conn
+ * @param provider
+ */
+ private static void styleConnection(IGraphModelConnection conn, IConnectionStyleProvider provider) {
+ Object rel = conn.getExternalConnection();
+ Color c;
+ int style = provider.getConnectionStyle(rel);
+ if (!ZestStyles.validateConnectionStyle(style)) ZestPlugin.error(ZestException.ERROR_INVALID_STYLE);
+ if (style != ZestStyles.NONE) {
+ conn.setConnectionStyle(style);
+ }
+// @tag bug(152530-Bezier(fix))
+ if (ZestStyles.checkStyle(conn.getConnectionStyle(), ZestStyles.CONNECTIONS_BEZIER) &&
+ provider instanceof IConnectionStyleBezierExtension) {
+ IConnectionStyleBezierExtension bezier = (IConnectionStyleBezierExtension)provider;
+ double d;
+ if (!Double.isNaN((d = bezier.getStartAngle(rel)))) conn.setStartAngle(d);
+ if (!Double.isNaN((d = bezier.getEndAngle(rel)))) conn.setEndAngle(d);
+ if (!Double.isNaN((d = bezier.getStartDistance(rel)))) conn.setStartLength(d);
+ if (!Double.isNaN((d = bezier.getEndDistance(rel)))) conn.setEndLength(d);
+ }
+ if ((c = provider.getHighlightColor(rel)) != null) conn.setHighlightColor(c);
+ if ((c = provider.getColor(rel)) != null) conn.setLineColor(c);
+ int w = -1;
+ if ((w = provider.getLineWidth(rel)) >= 0) conn.setLineWidth(w);
+ }
+
+ /**
+ * @param conn
+ * @param provider
+ */
+ private static void styleEntityConnection(IGraphModelConnection conn, IEntityConnectionStyleProvider provider) {
+ Object src = conn.getSource().getExternalNode();
+ Object dest = conn.getDestination().getExternalNode();
+ Color c;
+ int style = provider.getConnectionStyle(src, dest);
+ if (!ZestStyles.validateConnectionStyle(style)) ZestPlugin.error(ZestException.ERROR_INVALID_STYLE);
+ if (style != ZestStyles.NONE) {
+ conn.setConnectionStyle(style);
+ }
+ //@tag bug(152530-Bezier(fix))
+ if (ZestStyles.checkStyle(conn.getConnectionStyle(), ZestStyles.CONNECTIONS_BEZIER) &&
+ provider instanceof IEntityConnectionStyleBezierExtension) {
+ IEntityConnectionStyleBezierExtension bezier =
+ (IEntityConnectionStyleBezierExtension)provider;
+ double d;
+ if (!Double.isNaN((d = bezier.getStartAngle(src, dest)))) conn.setStartAngle(d);
+ if (!Double.isNaN((d = bezier.getEndAngle(src, dest)))) conn.setEndAngle(d);
+ if (!Double.isNaN((d = bezier.getStartDistance(src, dest)))) conn.setStartLength(d);
+ if (!Double.isNaN((d = bezier.getEndDistance(src, dest)))) conn.setEndLength(d);
+ }
+ if ((c = provider.getColor(src, dest))!=null) conn.setLineColor(c);
+ if ((c = provider.getHighlightColor(src, dest)) != null) conn.setHighlightColor(c);
+ int w = -1;
+ if ((w = provider.getLineWidth(src, dest)) >= 0) conn.setLineWidth(w);
+ }
+
+ /**
+ * Styles the given node according to the properties in the style provider.
+ * @param node the graph element to style.
+ * @param data the element that is being styled.
+ * @param provider the style provier.
+ */
+ //@tag bug(151327-Styles) : resolution
+ private static void styleNode(IGraphModelNode node, IEntityStyleProvider provider) {
+ Object entity = node.getExternalNode();
+ node.setHighlightAdjacentNodes(provider.highlightAdjacentEntities(entity));
+ if (provider.highlightAdjacentEntities(entity)) {
+ Color c = provider.getAdjacentEntityHighlightColor(entity);
+ if (c != null) node.setHighlightAdjacentColor(c);
+ }
+ Color c;
+ int width = -1;
+ if ((c = provider.getBorderColor(entity)) != null) node.setBorderColor(c);
+ if ((c = provider.getBorderHighlightColor(entity)) != null) node.setBorderHighlightColor(c);
+ if ((c = provider.getHighlightColor(entity)) != null) node.setHighlightColor(c);
+ if ((c = provider.getBackgroundColour(entity)) != null) node.setBackgroundColor(c);
+ if ((c = provider.getForegroundColour(entity)) != null) node.setForegroundColor(c);
+ if ((width = provider.getBorderWidth(entity)) >= 0) node.setBorderWidth(width);
+
+ }
+ /**
+ * Returns the SWT line style for the given zest connection style.
+ *
+ */
+ public static int getLineStyleForZestStyle(int style){
+ int lineStyles =
+ ZestStyles.CONNECTIONS_DASH_DOT |
+ ZestStyles.CONNECTIONS_DASH |
+ ZestStyles.CONNECTIONS_DOT |
+ ZestStyles.CONNECTIONS_SOLID;
+ style = style & lineStyles;
+ if (style == 0) {
+ style = ZestStyles.CONNECTIONS_SOLID;
+ }
+ switch (style) {
+ case ZestStyles.CONNECTIONS_DASH_DOT:
+ return SWT.LINE_DASHDOT;
+ case ZestStyles.CONNECTIONS_DASH:
+ return SWT.LINE_DASH;
+ case ZestStyles.CONNECTIONS_DOT:
+ return SWT.LINE_DOT;
+ }
+ return SWT.LINE_SOLID;
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModel.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModel.java
new file mode 100644
index 0000000..8f82f48
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModel.java
@@ -0,0 +1,457 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.mylar.zest.core.ZestStyles;
+import org.eclipse.swt.widgets.Canvas;
+
+
+/**
+ * Holds the nodes and connections for the graph.
+ *
+ * @author Chris Callendar
+ */
+public class GraphModel extends GraphItem {
+
+ /** Property ID to use when a child is added to this diagram. */
+ public static final String NODE_ADDED_PROP = "LayoutDiagram.NodeAdded";
+ /** Property ID to use when a child is removed from this diagram. */
+ public static final String NODE_REMOVED_PROP = "LayoutDiagram.NodeRemoved";
+ /** Property ID to use when the focus (current) node has changed in the model */
+ public static final String NODE_FOCUS_PROP = "LayoutDiagram.NodeFocus";
+ /** Property ID to use when a proxy node is removed **/
+ public static final String NODE_PROXY_REMOVED_PROP = "Proxy.NodeRemoved";
+
+
+ private List nodes;
+ protected List connections;
+
+ //@tag zest(bug(153466-NoNestedClientSupply(fix))) : keep proxy connections and nodes inside the model for easy access.
+ protected List proxyConnections;
+ protected List proxyNodes;
+
+ /** Maps user nodes to internal nodes */
+ private HashMap external2InternalNodeMap;
+
+ /** Maps user connections to internal connections */
+ private HashMap external2InternalConnectionMap;
+ private int connectionStyle;
+ private int nodeStyle;
+
+ /**
+ * Initializes this diagram.
+ * @param canvas The parent widget.
+ */
+ public GraphModel(Canvas canvas) {
+ super(canvas);
+ this.nodes = new ArrayList();
+ this.proxyNodes = new LinkedList();
+ this.proxyConnections = new LinkedList();
+ this.connectionStyle = ZestStyles.NONE;
+ this.nodeStyle = ZestStyles.NONE;
+ this.connections = new ArrayList();
+ this.external2InternalNodeMap = new HashMap();
+ this.external2InternalConnectionMap = new HashMap();
+ }
+
+ /**
+ * Gets a list of the GraphModelNode children objects under the root node in this diagram.
+ * If the root node is null then all the top level nodes are returned.
+ * @return List of GraphModelNode objects
+ */
+ public List getNodes() {
+ return nodes;
+ }
+
+ /**
+ * Converts the list of GraphModelNode objects into an array an returns it.
+ * @return GraphModelNode[]
+ */
+ public IGraphModelNode[] getNodesArray() {
+ IGraphModelNode[] nodesArray = new IGraphModelNode[nodes.size()];
+ nodesArray = (IGraphModelNode[])nodes.toArray(nodesArray);
+ return nodesArray;
+ }
+
+ /**
+ * @return the proxyConnections
+ */
+ //@tag zest(bug(153466-NoNestedClientSupply(fix))) : make proxies available from the model
+ public List getProxyConnections() {
+ return proxyConnections;
+ }
+
+ /**
+ * @return the proxyNodes
+ */
+ //@tag zest(bug(153466-NoNestedClientSupply(fix))) : make proxies available from the model
+ public List getProxyNodes() {
+ return proxyNodes;
+ }
+
+ /**
+ * Creates and reaturns a proxy node based on the given node, and adds it
+ * to the list of proxies in the model.
+ * @return the proxy node.
+ *
+ */
+ //@tag zest(bug(153466-NoNestedClientSupply(fix))) : proxies can only be made on the model. This ensures that they are properly monitored here.
+ public NonNestedProxyNode createProxyNode(IGraphModelNode node) {
+ NonNestedProxyNode proxy = new NonNestedProxyNode(node);
+ proxyNodes.add(proxy);
+ proxy.activate();
+ return proxy;
+ }
+ /**
+ * Creates and returns a proxy connection based on the given connection, and
+ * the source and target endpoints. The created proxy is also added to the
+ * list of proxies in the model. Note, only the visual elements of the
+ * proxy connection are used for display: the given source and target nodes will
+ * be the actual source and target nodes for the returned proxy. The reason
+ * for this is that the source and target nodes may themselves be proxies
+ * for the actual source and target nodes of the original connection. Some
+ * example usages are:
+ * <pre>
+ * //to make a proxy connection based exactly on the given connection
+ * graphModel.createProxyConnection(conn.getSource(), conn.getDestination(), conn);
+ * //to make a proxy using a proxy node as the source, and the original node as the target:
+ * graphModel.createProxyConnection(graphModel.createProxyNode(conn.getSource()), conn.getDestination(), conn);
+ * </pre>
+ * In general, either the original source and destination nodes, or a proxy to them, should be used.
+ * @param source the source node that this connection will be linked to. May be a proxy.
+ * @param target the target node that this connection will be linked to. May be a proxy.
+ * @param conn the connection to base this proxy on.
+ * @return the proxy connection
+ */
+ //@tag zest(bug(153466-NoNestedClientSupply(fix))) : proxies can only be made on the model. This ensures that they are properly monitored here.
+ public ProxyConnection createProxyConnection(IGraphModelNode source, IGraphModelNode target, IGraphModelConnection conn) {
+ ProxyConnection connection = new ProxyConnection(source, target, conn);
+ proxyConnections.add(connection);
+ connection.reconnect();
+ return connection;
+ }
+
+ /**
+ * Removes the given proxy node from the model, if it exists. All connections
+ * on the node will be removed as well.
+ * @param node
+ */
+ //@tag zest(bug(153466-NoNestedClientSupply(fix))) : proxies can only be made on the model. This ensures that they are properly monitored here.
+ public void removeProxyNode(NonNestedProxyNode node) {
+ if (proxyNodes.contains(node)) {
+ proxyNodes.remove(node);
+ List connections = new LinkedList();
+ connections.addAll(node.getSourceConnections());
+ connections.addAll(node.getTargetConnections());
+ IGraphModelConnection[] connectionsArray =
+ (IGraphModelConnection[])connections.toArray(new IGraphModelConnection[connections.size()]);
+ for (int i = 0; i < connectionsArray.length; i++) {
+ IGraphModelConnection conn = connectionsArray[i];
+ if (conn instanceof ProxyConnection) {
+ removeProxyConnection((ProxyConnection) conn);
+ } else {
+ removeConnection(conn);
+ }
+ }
+ node.deactivate();
+ firePropertyChange(NODE_PROXY_REMOVED_PROP, null, node);
+ }
+ }
+
+ /**
+ * Disconnects the given connection if it exists in the model.
+ * @param connection the connection to disconnect.
+ */
+ //@tag zest(bug(153466-NoNestedClientSupply(fix))) : proxies can only be made on the model. This ensures that they are properly monitored here.
+ public void removeProxyConnection(ProxyConnection connection) {
+ if (proxyConnections.contains(connection)) {
+ proxyConnections.remove(connection);
+ connection.disconnect();
+ }
+ }
+
+ /**
+ * Removes all proxie nodes and connections from the model.
+ *
+ */
+ //@tag zest(bug(153466-NoNestedClientSupply(fix))) : proxies can only be made on the model. This ensures that they are properly monitored here.
+ public void clearProxies() {
+ while (proxyNodes.size() > 0) {
+ removeProxyNode((NonNestedProxyNode)proxyNodes.get(0));
+ }
+ while (proxyConnections.size() > 0) {
+ removeProxyConnection((ProxyConnection) proxyConnections.get(0));
+ }
+ }
+
+ /**
+ * Returns the nodes map. The key is the node data and the value
+ * is the GraphModelNode.
+ * @return HashMap
+ */
+ public HashMap getNodesMap() {
+ return external2InternalNodeMap;
+ }
+
+ /**
+ * Returns the connection map. They key is the connection data and the value
+ * is te GraphModelConnection
+ * @return
+ */
+ public HashMap getConnectionMap() {
+ return external2InternalConnectionMap;
+ }
+
+
+
+ /**
+ * Sets the default connection style.
+ * @param connection style the connection style to set
+ * @see org.eclipse.mylar.zest.core.ZestStyles
+ */
+ public void setConnectionStyle(int connectionStyle) {
+ this.connectionStyle = connectionStyle;
+ }
+
+ /**
+ * Gets the default connection style.
+ * @return the connection style
+ * @see org.eclipse.mylar.zest.core.ZestStyles
+ */
+ public int getConnectionStyle() {
+ return connectionStyle;
+ }
+
+ /**
+ * Sets the default node style.
+ * @param nodeStyle the node style to set
+ * @see org.eclipse.mylar.zest.core.ZestStyles
+ */
+ public void setNodeStyle(int nodeStyle) {
+ this.nodeStyle = nodeStyle;
+ }
+
+ /**
+ * Gets the default node style.
+ * @return the node style
+ * @see org.eclipse.mylar.zest.core.ZestStyles
+ */
+ public int getNodeStyle() {
+ return nodeStyle;
+ }
+
+
+
+ /**
+ * Gets the list of GraphModelConnection objects.
+ * @return list of GraphModelConnection objects
+ */
+ public List getConnections() {
+ return this.connections;
+ }
+
+ /**
+ * Converts the list of GraphModelConnection objects into an array and returns it.
+ * @return GraphModelConnection[]
+ */
+ public IGraphModelConnection[] getConnectionsArray() {
+ IGraphModelConnection[] connsArray = new IGraphModelConnection[connections.size()];
+ connsArray = (IGraphModelConnection[])connections.toArray(connsArray);
+ return connsArray;
+ }
+
+ /**
+ * Adds a connection to this model
+ * @param connection
+ */
+ public boolean addConnection( Object externalConnection, IGraphModelConnection connection ) {
+ if ((connection != null) && connections.add(connection)) {
+ external2InternalConnectionMap.put(externalConnection, connection);
+ connection.reconnect();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Gets the internal GraphModelConnection object associated with the
+ * given external connection object.
+ * @param externalConnection
+ * @return GraphModelConnection
+ */
+ public IGraphModelConnection getInternalConnection(Object externalConnection) {
+ if (external2InternalConnectionMap.containsKey(externalConnection)) {
+ return (IGraphModelConnection)external2InternalConnectionMap.get(externalConnection);
+ }
+ return null;
+ }
+
+ /**
+ * Disconnects and removes the connection as well as notifying the graph listeners
+ * that a connection has been removed.
+ * @param connection
+ * @return boolean if removed
+ */
+ public boolean removeConnection(IGraphModelConnection connection) {
+ boolean removed = false;
+ if (connection != null) {
+ connection.disconnect();
+ external2InternalConnectionMap.remove(connection.getExternalConnection());
+ removed = connections.remove(connection);
+ if ( connection != null && !((GraphModelConnection) connection).isDisposed() ) {
+ ((GraphModelConnection)connection).dispose();
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * Removes the connection associated with the external connection object.
+ * @param externalConnection
+ * @return boolean
+ */
+ public boolean removeConnection(Object externalConnection) {
+ IGraphModelConnection connection = (IGraphModelConnection)external2InternalConnectionMap.get(externalConnection);
+ return this.removeConnection(connection);
+ }
+
+ /**
+ * Adds a new node to the diagram.
+ * @param node The node to add
+ * @return boolean if successful.
+ */
+ public boolean addNode(Object externalNode, IGraphModelNode node) {
+ boolean added = false;
+ if (node != null) {
+ addNodeToList(node);
+ external2InternalNodeMap.put( externalNode, node );
+ firePropertyChange(NODE_ADDED_PROP, null, node);
+ added = true;
+ }
+ return added;
+ }
+
+ protected void addNodeToList(IGraphModelNode node) {
+ nodes.add(node);
+ }
+
+ protected boolean removeNodeFromList(IGraphModelNode node) {
+ return nodes.remove(node);
+ }
+
+ /**
+ * Removes a node from this graph and disconnects all the connections.
+ * @param node a non-null LayoutNode instance.
+ * @return boolean If the node was removed.
+ */
+ public boolean removeNode(IGraphModelNode node) {
+ boolean removed = false;
+ if (node != null) {
+ external2InternalNodeMap.remove( node.getExternalNode() );
+ removed = removeNodeFromList(node);
+ if (removed) {
+ // remove the source and target connections & notify the graph listeners
+ for (Iterator iter = node.getSourceConnections().iterator(); iter.hasNext();) {
+ removeConnection((IGraphModelConnection)iter.next());
+ }
+ for (Iterator iter = node.getTargetConnections().iterator(); iter.hasNext();) {
+ removeConnection((IGraphModelConnection)iter.next());
+ }
+ firePropertyChange(NODE_REMOVED_PROP, null, node);
+ }
+ if ( !((GraphModelNode)node).isDisposed() ) {
+ ((GraphModelNode)node).dispose();
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * Removes the internal node from the external node
+ * @param externalNode The external node representation of this node
+ * @return true if successful
+ */
+ public boolean removeNode( Object externalNode ) {
+ IGraphModelNode node = (IGraphModelNode) external2InternalNodeMap.get( externalNode );
+ return this.removeNode(node);
+ }
+
+ /**
+ * Gets the internal node from the external node.
+ * @param o The user data.
+ * @return The internal node or null if none
+ */
+ public IGraphModelNode getInternalNode( Object o ) {
+ if ( external2InternalNodeMap.containsKey( o ) ) {
+ return (IGraphModelNode) external2InternalNodeMap.get( o );
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Widget#toString()
+ */
+ public String toString() {
+ return "GraphModel {" + nodes.size() + " nodes, " +
+ connections.size() + " connections}";
+ }
+
+
+
+ /**
+ * Fires changes to all the model elements
+ * @param property
+ * @param oldValue
+ * @param newValue
+ */
+ public void fireAllPropertyChange(String property, Object oldValue, Object newValue) {
+ for ( Iterator iter = this.connections.iterator(); iter.hasNext(); ) {
+ ((IGraphModelConnection) iter.next() ).firePropertyChange(property, oldValue, newValue );
+ }
+
+ for ( Iterator iter = this.nodes.iterator(); iter.hasNext(); ) {
+ ((IGraphModelNode) iter.next() ).firePropertyChange(property, oldValue, newValue );
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphItem#getGraphModel()
+ */
+ public GraphModel getGraphModel() {
+ return this;
+ }
+
+ /**
+ * Dispose of the nodes and edges when the graph is disposed.
+ */
+ public void dispose() {
+ for ( Iterator iter = nodes.iterator(); iter.hasNext(); ) {
+ GraphModelNode node = (GraphModelNode) iter.next();
+ if ( node != null && !node.isDisposed() ) {
+ node.dispose();
+ }
+ }
+ for ( Iterator iter = connections.iterator(); iter.hasNext(); ) {
+ GraphModelConnection connection = (GraphModelConnection) iter.next();
+ if ( connection != null && !connection.isDisposed() ) {
+ connection.dispose();
+ }
+ }
+ super.dispose();
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelConnection.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelConnection.java
new file mode 100644
index 0000000..ae8aa81
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelConnection.java
@@ -0,0 +1,641 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.HashMap;
+
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.mylar.zest.core.IZestColorConstants;
+import org.eclipse.mylar.zest.core.ZestPlugin;
+import org.eclipse.mylar.zest.core.ZestStyles;
+import org.eclipse.mylar.zest.layouts.LayoutBendPoint;
+import org.eclipse.mylar.zest.layouts.LayoutEntity;
+import org.eclipse.mylar.zest.layouts.LayoutRelationship;
+import org.eclipse.mylar.zest.layouts.constraints.LayoutConstraint;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Display;
+
+
+/**
+ * This is the graph connection model which stores the source and destination nodes and the properties
+ * of this connection (color, line width etc).
+ *
+ * @author Chris Callendar
+ */
+public class GraphModelConnection extends GraphItem implements IGraphModelConnection, LayoutRelationship {
+
+ private Font font;
+ private IGraphModelNode sourceNode;
+ private IGraphModelNode destinationNode;
+
+ private double weight;
+ private Color color;
+ private Color highlightColor;
+ private Color foreground;
+ private int lineWidth;
+ private int lineStyle;
+ private HashMap attributes;
+ private boolean isConnected;
+ private GraphModel graphModel;
+
+ private Object internalConnection;
+ private int connectionStyle;
+ private int curveDepth;
+
+
+
+ /**
+ * For bezier curves: angle between the start point, and the line.
+ * This may be a hint only. Future implementations of graph viewers may
+ * adjust the actual visual representation based on the look of the graph.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ private double startAngle;
+ /**
+ * For bezier curves: angle between the end point and the line. This may
+ * be a hint only. Future implementations of graph viewers may adjust the
+ * actual visual representation based on the look of the graph.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ private double endAngle;
+
+ /**
+ * For bezier curves: this is a value from 0-1 as a ratio of the length of the
+ * line between the start point, and the control point/the length of the connection.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ private double startLength;
+
+ /**
+ * For bezier curves: this is a value from 0-1 as a ratio of the length of the
+ * line between the end point, and the control point/the length of the connection.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ private double endLength;
+
+
+ /**
+ * Visibility based on the internal visibility of the connection, and
+ * of the nodes.
+ */
+ private boolean trueVisibility;
+
+ /**
+ * The state of visibility set by the user.
+ */
+ private boolean visible;
+ /**
+ * Listens for when the source and target nodes change visibility. If
+ * a connection's node is invisible, so should the connection.
+ * @author Del Myers
+ *
+ */
+ //@tag zest.bug.156528-Filters.follows : we need this support for filters.
+ private class NodeVisibilityListener implements PropertyChangeListener {
+ /* (non-Javadoc)
+ * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (VISIBLE_PROP.equals(evt.getPropertyName())) {
+ resetVisibility();
+ }
+ }
+ }
+
+ private NodeVisibilityListener nodeListener;
+
+ /**
+ * LayoutConnection constructor, initializes the nodes and the connection properties.
+ * Defaults to bidirectional and a weighting of 0.
+ * @param graphModel The graph model.
+ * @param data The data object for this connection.
+ * @param source The source node.
+ * @param destination The destination node.
+ */
+ public GraphModelConnection(GraphModel graphModel, Object data, IGraphModelNode source, IGraphModelNode destination) {
+ this(graphModel, data, source, destination, true, 0);
+ }
+
+ /**
+ * LayoutConnection constructor, initializes the nodes and the connection properties.
+ * @param graphModel The graph model.
+ * @param data The data object for this connection.
+ * @param source The source node.
+ * @param destination The destination node.
+ * @param bidirection If the connection is bidirectional.
+ * @param weight The connection weight.
+ */
+ public GraphModelConnection(GraphModel graphModel, Object data, IGraphModelNode source, IGraphModelNode destination, boolean bidirection, double weight) {
+ super(graphModel);
+ this.trueVisibility = super.isVisible();
+ this.visible = this.trueVisibility;
+ ZestPlugin plugin = ZestPlugin.getDefault();
+ this.setData(data);
+ this.connectionStyle = IZestGraphDefaults.CONNECTION_STYLE;
+ this.color = plugin.getColor(IZestColorConstants.EDGE_DEFAULT);
+ this.foreground = plugin.getColor(IZestColorConstants.EDGE_DEFAULT);
+ this.highlightColor = plugin.getColor(IZestColorConstants.EDGE_HIGHLIGHT);
+ this.lineWidth = 1;
+ this.lineStyle = Graphics.LINE_SOLID;
+ setWeightInLayout(weight);
+ this.attributes = new HashMap();
+ this.isConnected = false;
+ this.graphModel = graphModel;
+ this.sourceNode = source;
+ this.destinationNode = destination;
+ //@tag removed : can cause the edit parts to be created before the model is finished. Wait until the model is fully constructed to reconnect.
+ //reconnect(source, destination);
+ this.font = Display.getDefault().getSystemFont();
+ nodeListener = new NodeVisibilityListener();
+ }
+
+ /**
+ * Gets the external connection object.
+ * @return Object
+ */
+ public Object getExternalConnection() {
+ return this.getData();
+ }
+
+ /**
+ * Returns a string like 'source -> destination'
+ * @return String
+ */
+ public String toString() {
+ String arrow = (isBidirectionalInLayout() ? " <--> " : " --> ");
+ String src = (sourceNode != null ? sourceNode.getText() : "null");
+ String dest = (destinationNode != null ? destinationNode.getText() : "null");
+ String weight = " (weight=" + getWeightInLayout() + ")";
+ return ("GraphModelConnection: " + src + arrow + dest + weight);
+ }
+
+ /**
+ * Disconnect this connection from the shapes it is attached to.
+ */
+ public void disconnect() {
+ if (isConnected) {
+ sourceNode.removePropertyChangeListener(nodeListener);
+ destinationNode.removePropertyChangeListener(nodeListener);
+ sourceNode.removeConnection(this);
+ destinationNode.removeConnection(this);
+ isConnected = false;
+ }
+ }
+
+ /**
+ * Reconnect this connection.
+ * The connection will reconnect with the node it was previously attached to.
+ */
+ public void reconnect() {
+ if (!isConnected) {
+ sourceNode.addConnection(this, true);
+ destinationNode.addConnection(this, false);
+ sourceNode.addPropertyChangeListener(nodeListener);
+ destinationNode.addPropertyChangeListener(nodeListener);
+ isConnected = true;
+ }
+ }
+
+ /**
+ * Reconnect to a different source and/or destination node.
+ * The connection will disconnect from its current attachments and reconnect to
+ * the new source and destination.
+ * @param newSource a new source endpoint for this connection (non null)
+ * @param newDestination a new destination endpoint for this connection (non null)
+ * @throws IllegalArgumentException if any of the paramers are null
+ */
+ public void reconnect(IGraphModelNode newSource, IGraphModelNode newDestination) {
+ if (newSource == null || newDestination == null ) {
+ throw new IllegalArgumentException("Invalid source and/or destination nodes");
+ }
+ disconnect();
+ this.sourceNode = newSource;
+ this.destinationNode = newDestination;
+ reconnect();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#getSourceInLayout()
+ */
+ public LayoutEntity getSourceInLayout() {
+ return sourceNode;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#getDestinationInLayout()
+ */
+ public LayoutEntity getDestinationInLayout() {
+ return destinationNode;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#isBidirectionalInLayout()
+ */
+ public boolean isBidirectionalInLayout() {
+ return !ZestStyles.checkStyle(connectionStyle, ZestStyles.CONNECTIONS_DIRECTED);
+ }
+
+ /**
+ * Returns the style of this connection. Valid styles are those that begin with
+ * CONNECTION in ZestStyles.
+ * @return the style of this connection.
+ * @see #ZestStyles
+ */
+ public int getConnectionStyle() {
+ return connectionStyle;
+ }
+ /**
+ * Returns the style of this connection. Valid styles are those that begin with
+ * CONNECTION in ZestStyles.
+ * @return the style of this connection.
+ * @see #ZestStyles
+ */
+ public void setConnectionStyle(int style) {
+ this.connectionStyle = style;
+ }
+
+ /**
+ * Gets the weight of this connection. The weight must be in {-1, [0-1]}.
+ * A weight of -1 means that there is no force/tension between the nodes.
+ * A weight of 0 results in the maximum spring length being used (farthest apart).
+ * A weight of 1 results in the minimum spring length being used (closest together).
+ * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#getWeightInLayout()
+ * @return the weight: {-1, [0 - 1]}.
+ */
+ public double getWeightInLayout() {
+ return weight;
+ }
+
+ /**
+ * Gets the font for the label on this connection
+ * @return
+ */
+ public Font getFont() {
+ return this.font;
+ }
+ /**
+ * Sets the font for the label on this connection.
+ *
+ */
+ public void setFont(Font f) {
+ this.font = f;
+ }
+
+ /**
+ * Sets the weight for this connection. The weight must be in {-1, [0-1]}.
+ * A weight of -1 means that there is no force/tension between the nodes.
+ * A weight of 0 results in the maximum spring length being used (farthest apart).
+ * A weight of 1 results in the minimum spring length being used (closest together).
+ * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#setWeightInLayout(double)
+ */
+ public void setWeightInLayout(double weight) {
+ if (weight < 0) {
+ this.weight = -1;
+ } else if (weight > 1) {
+ this.weight = 1;
+ } else {
+ this.weight = weight;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#getAttributeInLayout(java.lang.String)
+ */
+ public Object getAttributeInLayout(String attribute) {
+ return attributes.get(attribute);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.layouts.LayoutRelationship#setAttributeInLayout(java.lang.String, java.lang.Object)
+ */
+ public void setAttributeInLayout(String attribute, Object value) {
+ attributes.put(attribute, value);
+ }
+
+ /**
+ * Returns the color of this connection.
+ * @return Color
+ */
+ public Color getLineColor() {
+ return color;
+ }
+
+ /**
+ * Sets the highlight color.
+ * @param color the color to use for highlighting.
+ */
+ public void setHighlightColor(Color color) {
+ this.highlightColor = color;
+ }
+
+ /**
+ * @return the highlight color
+ */
+ public Color getHighlightColor() {
+ return highlightColor;
+ }
+ /**
+ * Perminently sets the color of this line to the given color. This will become the
+ * color of the line when it is not highlighted. If you would like to temporarily change
+ * the color of the line, use changeLineColor.
+ * @param color the color to be set.
+ * @see changeLineColor(Color color)
+ */
+ public void setLineColor(Color color) {
+ this.foreground = color;
+ changeLineColor(foreground);
+ }
+
+ /**
+ * Sets the connection color.
+ * @param color
+ */
+ public void changeLineColor(Color color) {
+ Color old = this.color;
+ if (this.color != color) {
+ this.color = color;
+ firePropertyChange(LINECOLOR_PROP, old, color);
+ }
+ }
+
+ /**
+ * Returns the connection line width.
+ * @return int
+ */
+ public int getLineWidth() {
+ return lineWidth;
+ }
+
+ /**
+ * Sets the connection line width.
+ * @param lineWidth
+ */
+ public void setLineWidth(int lineWidth) {
+ int old = this.lineWidth;
+ if (this.lineWidth != lineWidth) {
+ this.lineWidth = lineWidth;
+ firePropertyChange(LINEWIDTH_PROP, new Integer(old), new Integer(lineWidth) );
+ }
+ }
+
+
+ /**
+ * Returns the connection line style.
+ * @return int
+ */
+ public int getLineStyle() {
+ return lineStyle;
+ }
+
+ /**
+ * Sets the connection line style.
+ * @param lineStyle
+ */
+ public void setLineStyle(int lineStyle) {
+ int old = this.lineStyle;
+ if (this.lineStyle != lineStyle) {
+ this.lineStyle = lineStyle;
+ firePropertyChange(LINESTYLE_PROP, new Integer(old), new Integer(lineStyle));
+ }
+ }
+
+ /**
+ * Gets the source node for this relationship
+ * @return GraphModelNode
+ */
+ public IGraphModelNode getSource() {
+ return this.sourceNode;
+ }
+
+ /**
+ * Gets the target node for this relationship
+ * @return GraphModelNode
+ */
+ public IGraphModelNode getDestination() {
+ return this.destinationNode;
+ }
+
+ /**
+ * Gets the internal relationship object.
+ * @return Object
+ */
+ public Object getLayoutInformation() {
+ return internalConnection;
+ }
+
+ /**
+ * Sets the internal relationship object.
+ * @param layoutInformation
+ */
+ public void setLayoutInformation(Object layoutInformation) {
+ this.internalConnection = layoutInformation;
+ }
+
+ /**
+ * Highlights this node. Uses the default highlight color.
+ */
+ public void highlight() {
+ changeLineColor(highlightColor);
+ firePropertyChange(HIGHLIGHT_PROP, null, null);
+ }
+
+ /**
+ * Unhighlights this node. Uses the default color.
+ */
+ public void unhighlight() {
+ changeLineColor(foreground);
+ firePropertyChange(UNHIGHLIGHT_PROP, null, null);
+ }
+
+ /**
+ * Gets the graph model that this connection is in
+ * @return The graph model that this connection is contained in
+ */
+ public GraphModel getGraphModel() {
+ return this.graphModel;
+ }
+
+
+ public void setBendPoints(LayoutBendPoint[] bendPoints) {
+
+ }
+
+ public void clearBendPoints() {
+
+ }
+
+ /**
+ * Returns the curve depth for this connection. The return value is only meaningful
+ * if the connection style has the ZestStyles.CONNECTIONS_CURVED style set.
+ * @return the curve depth.
+ */
+ public int getCurveDepth() {
+ return curveDepth;
+ }
+
+ public void setCurveDepth(int curveDepth) {
+ Integer oldDepth = new Integer(this.curveDepth);
+ this.curveDepth = curveDepth;
+ firePropertyChange(CURVE_PROP, oldDepth, new Integer(curveDepth));
+ }
+
+
+ public void populateLayoutConstraint(LayoutConstraint constraint) {
+
+ }
+
+
+ /**
+ * Gets the end angle for bezier arcs.
+ *
+ * For bezier curves: angle between the start point, and the line.
+ * This may be a hint only. Future implementations of graph viewers may
+ * adjust the actual visual representation based on the look of the graph.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ public double getEndAngle() {
+ return endAngle;
+ }
+
+ /**
+ * Sets the end angle for bezier arcs.
+ *
+ * For bezier curves: angle between the start point, and the line.
+ * This may be a hint only. Future implementations of graph viewers may
+ * adjust the actual visual representation based on the look of the graph.
+ *
+ * @param endAngle the angle to set.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ public void setEndAngle(double endAngle) {
+ Double oldAngle = new Double(this.endAngle);
+ this.endAngle = endAngle;
+ firePropertyChange(CURVE_PROP, oldAngle, new Double(endAngle));
+ }
+
+ /**
+ * For bezier curves: this is a value from 0-1 as a ratio of the length of the
+ * line between the end point, and the control point/the length of the connection.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ public double getEndLength() {
+ return endLength;
+ }
+
+ /**
+ * For bezier curves: this is a value from 0-1 as a ratio of the length of the
+ * line between the end point, and the control point/the length of the connection.
+ *
+ * @param endLength the length to set.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ public void setEndLength(double endLength) {
+ Double oldLength = new Double(this.endLength);
+ this.endLength = endLength;
+ firePropertyChange(CURVE_PROP, oldLength, new Double(endLength));
+
+ }
+
+ /**
+ * Gets the start angle for bezier arcs.
+ *
+ * For bezier curves: angle between the end point and the line. This may
+ * be a hint only. Future implementations of graph viewers may adjust the
+ * actual visual representation based on the look of the graph.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ public double getStartAngle() {
+ return startAngle;
+ }
+
+ /**
+ * Sets the start angle for bezier arcs.
+ *
+ * For bezier curves: angle between the end point and the line. This may
+ * be a hint only. Future implementations of graph viewers may adjust the
+ * actual visual representation based on the look of the graph.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ public void setStartAngle(double startAngle) {
+ Double oldAngle = new Double(this.startAngle);
+ this.startAngle = startAngle;
+ firePropertyChange(CURVE_PROP, oldAngle, new Double(startAngle));
+
+ }
+
+ /**
+ * For bezier curves: this is a value from 0-1 as a ratio of the length of the
+ * line between the start point, and the control point/the length of the connection.
+ */
+// @tag zest(bug(152530-Bezier(fix)))
+ public double getStartLength() {
+ return startLength;
+ }
+
+ /**
+ * For bezier curves: this is a value from 0-1 as a ratio of the length of the
+ * line between the start point, and the control point/the length of the connection.
+ * @param startLength the length to set.
+ */
+ //@tag zest(bug(152530-Bezier(fix)))
+ public void setStartLength(double startLength) {
+ Double oldLength = new Double(this.startLength);
+ this.startLength = startLength;
+ firePropertyChange(CURVE_PROP, oldLength, new Double(startLength));
+ }
+
+ private boolean checkVisibilityByNodes() {
+ if (!isConnected) return false;
+ boolean visible = true;
+ if (sourceNode != null) {
+ visible &= sourceNode.isVisible();
+ } else {
+ return false;
+ }
+ if (visible && destinationNode != null) {
+ visible &= destinationNode.isVisible();
+ } else {
+ return false;
+ }
+ return visible;
+ }
+
+ private void resetVisibility() {
+ boolean parent = this.visible;
+ boolean old = isVisible();
+ boolean nodes = checkVisibilityByNodes();
+ boolean visible = parent && nodes;
+ if (visible != old) {
+ trueVisibility = visible;
+ firePropertyChange(VISIBLE_PROP, new Boolean(old), new Boolean(nodes));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.GraphItem#setVisible(boolean)
+ */
+ public void setVisible(boolean visible) {
+ boolean nodeVisibility = checkVisibilityByNodes();
+ boolean old = isVisible();
+ trueVisibility = visible && nodeVisibility;
+ this.visible = visible;
+ if (old != trueVisibility) {
+ firePropertyChange(VISIBLE_PROP, new Boolean(old), new Boolean(trueVisibility));
+ }
+ }
+
+ public boolean isVisible() {
+ return trueVisibility;
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelEntityFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelEntityFactory.java
new file mode 100644
index 0000000..9bfb445
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelEntityFactory.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.mylar.zest.core.viewers.EntityConnectionData;
+import org.eclipse.mylar.zest.core.viewers.IGraphEntityContentProvider;
+import org.eclipse.swt.widgets.Canvas;
+
+
+/**
+ *
+ * @author Ian Bull
+ */
+public class GraphModelEntityFactory extends AbstractStylingModelFactory {
+
+
+ public GraphModelEntityFactory(StructuredViewer viewer) {
+ super(viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#createGraphModel()
+ */
+ public GraphModel createGraphModel() {
+ GraphModel model = new GraphModel((Canvas) getViewer().getControl());
+ doBuildGraph(model);
+ return model;
+ }
+
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.AbstractStylingModelFactory#doBuildGraph(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel)
+ */
+ protected void doBuildGraph(GraphModel model) {
+ clearGraph(model);
+ model.setConnectionStyle(getConnectionStyle());
+ model.setNodeStyle(getNodeStyle());
+ Object inputElement = getViewer().getInput();
+ Object entities[] = getContentProvider().getElements( inputElement );
+ if ( entities == null ) return;
+ for ( int i = 0; i < entities.length; i++ ) {
+ Object data = entities[ i ];
+ if ( !filterElement(inputElement, data) )
+ createNode(model, data);
+ }
+ for ( int i=0; i < entities.length; i++ ) {
+ Object data = entities[ i ];
+
+ // If this element is filtered, continue to the next one.
+ if ( filterElement(inputElement, data) ) continue;
+ Object[] related = ((IGraphEntityContentProvider)getContentProvider()).getConnectedTo( data );
+
+ if ( related != null )
+ for ( int j = 0; j < related.length; j++ ) {
+ // if the node this node is connected to is filtered,
+ // don't display this edge
+ if ( filterElement(inputElement, related[j] ) ) continue;
+ EntityConnectionData connectionData = new EntityConnectionData(data, related[j]);
+ if ( filterElement(inputElement, connectionData) ) continue;
+ createConnection(model, connectionData, data, related[j]);
+ }
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
+ */
+ public void refresh(GraphModel graph, Object element, boolean refreshLabels) {
+ if (element == null) return;
+ IGraphModelNode node = graph.getInternalNode(element);
+ if (node == null) {
+ //check to make sure that the user didn't send us an edge.
+ IGraphModelConnection conn = graph.getInternalConnection(element);
+ if (conn != null) {
+ //refresh on the connected nodes.
+ refresh(graph, conn.getSource().getExternalNode(), refreshLabels);
+ refresh(graph, conn.getDestination().getExternalNode(), refreshLabels);
+ return;
+ }
+ }
+ //can only refresh on nodes in this kind of factory.
+ if (node == null) {
+ //do nothing
+ return;
+ }
+ reconnect(graph, element, refreshLabels);
+
+ if (refreshLabels) {
+ update(node);
+ for (Iterator it = node.getSourceConnections().iterator(); it.hasNext();) {
+ update((IGraphItem) it.next());
+ }
+ for (Iterator it = node.getTargetConnections().iterator(); it.hasNext();) {
+ update((IGraphItem) it.next());
+ }
+ }
+ }
+
+
+ /**
+ * @param graph
+ * @param element
+ * @param refreshLabels
+ */
+ private void reconnect(GraphModel graph, Object element, boolean refreshLabels) {
+ IGraphModelNode node = graph.getInternalNode(element);
+ Object[] related = ((IGraphEntityContentProvider)getContentProvider()).getConnectedTo(element);
+ List connections = node.getSourceConnections();
+ LinkedList toAdd = new LinkedList();
+ LinkedList toDelete = new LinkedList();
+ LinkedList toKeep = new LinkedList();
+ HashSet oldExternalConnections = new HashSet();
+ HashSet newExternalConnections = new HashSet();
+ for (Iterator it = connections.iterator(); it.hasNext();) {
+ oldExternalConnections.add(((IGraphModelConnection)it.next()).getExternalConnection());
+ }
+ for (int i = 0; i < related.length; i++) {
+ newExternalConnections.add(new EntityConnectionData(element, related[i]));
+ }
+ for (Iterator it = oldExternalConnections.iterator(); it.hasNext();) {
+ Object next = it.next();
+ if (!newExternalConnections.contains(next)) {
+ toDelete.add(next);
+ } else {
+ toKeep.add(next);
+ }
+ }
+ for (Iterator it = newExternalConnections.iterator(); it.hasNext();) {
+ Object next = it.next();
+ if (!oldExternalConnections.contains(next)) {
+ toAdd.add(next);
+ }
+ }
+ for (Iterator it = toDelete.iterator(); it.hasNext();) {
+ graph.removeConnection(it.next());
+ }
+ toDelete.clear();
+ LinkedList newNodeList = new LinkedList();
+ for (Iterator it = toAdd.iterator(); it.hasNext();) {
+ EntityConnectionData data = (EntityConnectionData) it.next();
+ IGraphModelNode dest = graph.getInternalNode(data.dest);
+ if (dest == null) {
+ newNodeList.add(data.dest);
+ }
+ createConnection(graph, data, data.source, data.dest);
+ }
+ toAdd.clear();
+ if (refreshLabels) {
+ for (Iterator i = toKeep.iterator(); i.hasNext();) {
+ styleItem(graph.getInternalConnection(i.next()));
+ }
+ }
+ for (Iterator it = newNodeList.iterator(); it.hasNext();) {
+ //refresh the new nodes so that we get a fully-up-to-date graph.
+ refresh(graph, it.next());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object, boolean)
+ */
+ public void refresh(GraphModel graph, Object element) {
+ refresh(graph, element, false);
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelEntityRelationshipFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelEntityRelationshipFactory.java
new file mode 100644
index 0000000..a724585
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelEntityRelationshipFactory.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.mylar.zest.core.viewers.IGraphEntityRelationshipContentProvider;
+import org.eclipse.swt.widgets.Canvas;
+
+/**
+ * A factory for the IGraphEntityRelationshipContentProvider.
+ * @author Del Myers
+ *
+ */
+//@tag bug.154580-Content.fix
+//@tag bug.160367-Refreshing.fix : updated to use new AbstractStylingModelFactory
+public class GraphModelEntityRelationshipFactory extends AbstractStylingModelFactory {
+
+ public GraphModelEntityRelationshipFactory(StructuredViewer viewer) {
+ super(viewer);
+ if (!(viewer.getContentProvider() instanceof IGraphEntityRelationshipContentProvider)) {
+ throw new IllegalArgumentException("Expected IGraphEntityRelationshipContentProvider");
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.AbstractStylingModelFactory#createGraphModel()
+ */
+ public GraphModel createGraphModel() {
+ GraphModel model = new GraphModel((Canvas) getViewer().getControl());
+ doBuildGraph(model);
+ return model;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.AbstractStylingModelFactory#doBuildGraph(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel)
+ */
+ protected void doBuildGraph(GraphModel model) {
+ clearGraph(model);
+ model.setConnectionStyle(getConnectionStyle());
+ model.setNodeStyle(getNodeStyle());
+ Object[] nodes = getContentProvider().getElements(getViewer().getInput());
+ nodes = filter(getViewer().getInput(), nodes);
+ createModelNodes(model, nodes);
+ createModelRelationships(model);
+ }
+
+ /**
+ * Creates all the model relationships. Assumes that all of the model nodes
+ * have been created in the graph model already. Runtime O(n^2) + O(r).
+ * @param model the model to create the relationship on.
+ */
+ private void createModelRelationships(GraphModel model) {
+ IGraphModelNode[] modelNodes = model.getNodesArray();
+ IGraphEntityRelationshipContentProvider content = getCastedContent();
+ for (int i = 0; i < modelNodes.length; i++) {
+ for (int j = 0; j < modelNodes.length; j++) {
+ Object[] rels = content.getRelationships(
+ modelNodes[i].getExternalNode(),
+ modelNodes[j].getExternalNode()
+ );
+ if (rels != null) {
+ rels = filter(getViewer().getInput(), rels);
+ for (int r = 0; r < rels.length; r++) {
+ createConnection(model, rels[r], modelNodes[i].getExternalNode(), modelNodes[j].getExternalNode());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates the model nodes for the given external nodes.
+ *
+ * @param model
+ * the graph model.
+ * @param nodes
+ * the external nodes.
+ */
+ private void createModelNodes(GraphModel model, Object[] nodes) {
+ for (int i = 0; i < nodes.length; i++) {
+ createNode(model, nodes[i]);
+ }
+ }
+
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
+ */
+ public void refresh(GraphModel graph, Object element) {
+ refresh(graph, element, false);
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object, boolean)
+ */
+ public void refresh(GraphModel graph, Object element, boolean updateLabels) {
+ //with this kind of graph, it is just as easy and cost-effective to
+ //rebuild the whole thing.
+ refreshGraph(graph);
+ }
+
+ private IGraphEntityRelationshipContentProvider getCastedContent() {
+ return (IGraphEntityRelationshipContentProvider) getContentProvider();
+ }
+
+
+
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelFactory.java
new file mode 100644
index 0000000..e4eb060
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelFactory.java
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.mylar.zest.core.viewers.IGraphContentProvider;
+import org.eclipse.swt.widgets.Canvas;
+
+
+
+/**
+ * This factory helps make models (nodes & connections).
+ *
+ * @author Ian Bull
+ * @author Chris Callendar
+ */
+public class GraphModelFactory extends AbstractStylingModelFactory {
+
+
+
+ public GraphModelFactory(StructuredViewer viewer) {
+ super(viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see ca.uvic.cs.zest.internal.graphmodel.IGraphModelFactory#createModel()
+ */
+ public GraphModel createGraphModel() {
+ GraphModel model = new GraphModel((Canvas)getViewer().getControl());
+ doBuildGraph(model);
+ return model;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.AbstractStylingModelFactory#doBuildGraph(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel)
+ */
+ protected void doBuildGraph(GraphModel model) {
+ clearGraph(model);
+ model.setConnectionStyle(getConnectionStyle());
+ model.setNodeStyle(getNodeStyle());
+ //make the model have the same styles as the viewer
+ Object rels[] = getContentProvider().getElements(getViewer().getInput());
+ if ( rels != null ) {
+ // If rels returns null then just continue
+ // @tag zest(bug(134928(fix))) : An empty graph causes an NPE
+ for ( int i = 0; i < rels.length; i++ ) {
+ // Check the filter on the source
+ Object source = getCastedContent().getSource(rels[i]);
+ source = filterElement(getViewer().getInput(),source) ? null : source;
+
+ // Check hte filter on the dest
+ Object dest = getCastedContent().getDestination(rels[i]);
+ dest = filterElement(getViewer().getInput(),dest) ? null : dest;
+ if (source == null) {
+ //just create the node for the destination
+ if (dest != null) createNode(model, dest);
+ continue;
+ } else if (dest == null) {
+ //just create the node for the source
+ if (source != null) createNode(model, source);
+ continue;
+ }
+ // If any of the source, dest is null or the edge is filtered, don't create the graph.
+ if ( source != null && dest != null && !filterElement(getViewer().getInput(), rels[i])) {
+ createConnection(model, rels[i], getCastedContent().getSource(rels[i]), getCastedContent().getDestination(rels[i]));
+ }
+ }
+ }
+
+
+ }
+
+ private IGraphContentProvider getCastedContent() {
+ return (IGraphContentProvider)getContentProvider();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
+ */
+ public void refresh(GraphModel graph, Object element) {
+ refresh(graph, element, false);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object, boolean)
+ */
+ public void refresh(GraphModel graph, Object element, boolean updateLabels) {
+ IGraphModelConnection conn = graph.getInternalConnection(element);
+ if (conn == null) {
+ //did the user send us a node? Check all of the connections on the node.
+ IGraphModelNode node = graph.getInternalNode(element);
+ if (node != null) {
+ List connections = node.getSourceConnections();
+ for (Iterator it = connections.iterator(); it.hasNext();) {
+ IGraphModelConnection c = (IGraphModelConnection) it.next();
+ refresh(graph, c.getExternalConnection(), updateLabels);
+ }
+ connections = node.getTargetConnections();
+ for (Iterator it = connections.iterator(); it.hasNext();) {
+ IGraphModelConnection c = (IGraphModelConnection) it.next();
+ refresh(graph, c.getExternalConnection(), updateLabels);
+ }
+ }
+ return;
+ }
+ Object oldSource = conn.getSource().getExternalNode();
+ Object oldDest = conn.getDestination().getExternalNode();
+ Object newSource = getCastedContent().getSource(element);
+ Object newDest = getCastedContent().getDestination(element);
+ if (!(oldSource.equals(newSource) && oldDest.equals(newDest))) {
+ IGraphModelNode internalSource = graph.getInternalNode(newSource);
+ IGraphModelNode internalDest = graph.getInternalNode(newDest);
+ if (internalSource == null) {
+ internalSource = createNode(graph, newSource);
+ } else if (updateLabels) {
+ styleItem(internalSource);
+ }
+ if (internalDest == null) {
+ internalDest = createNode(graph, newDest);
+ } else if (updateLabels) {
+ styleItem(internalDest);
+ }
+
+ conn.disconnect();
+ conn.reconnect(internalSource, internalDest);
+ if (updateLabels) {
+ styleItem(conn);
+ }
+ }
+
+ }
+
+
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelNode.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelNode.java
new file mode 100644
index 0000000..2beb1cc
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/GraphModelNode.java
@@ -0,0 +1,668 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.draw2d.FigureUtilities;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.mylar.zest.core.IZestColorConstants;
+import org.eclipse.mylar.zest.core.ZestPlugin;
+import org.eclipse.mylar.zest.core.ZestStyles;
+import org.eclipse.mylar.zest.layouts.constraints.LayoutConstraint;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+
+
+/**
+ * Simple node class which has the following properties: color, size, location, and a label.
+ * It also has a list of connections and anchors.
+ *
+ * @author Chris Callendar
+ * @author Del Myers
+ * @author Ian Bull
+ */
+public class GraphModelNode extends GraphItem implements IGraphModelNode {
+ private int nodeStyle;
+
+ private List /*IGraphModelConnection*/ sourceConnections;
+ private List /*IGraphModelConnection*/ targetConnections;
+
+ private boolean preferredLocation;
+ private Color foreColor;
+ private Color backColor;
+ private Color highlightColor;
+ private Color highlightAdjacentColor;
+ private Color unhighlightColor;
+ private Color borderColor;
+ private Color borderHighlightColor;
+ private Color borderUnhighlightColor;
+ private int borderWidth;
+ private Point currentLocation;
+ private Dimension size;
+ private Font font;
+ private boolean cacheLabel;
+
+ protected Dimension labelSize;
+ protected GraphModel graphModel;
+
+ /** The internal node. */
+ protected Object internalNode;
+ private boolean selected;
+ private boolean highlighted;
+
+ public GraphModelNode(GraphModel graphModel, Object externalNode) {
+ super(graphModel);
+ initModel(graphModel, externalNode);
+ }
+
+ public GraphModelNode(GraphModel graphModel, String label, Object externalNode) {
+ super(graphModel);
+ setText(label);
+ initModel(graphModel, externalNode);
+ }
+
+ public GraphModelNode(GraphModel graphModel, Image i, Object externalNode) {
+ super(graphModel);
+ setImage(i);
+ initModel(graphModel, externalNode);
+ }
+
+ public GraphModelNode(GraphModel graphModel, String label, Image i, Object externalNode) {
+ super(graphModel);
+ if ( label == null ) {
+ setText(this.toString());
+ }
+ else {
+ setText(label);
+ }
+ setImage(i);
+ initModel(graphModel, externalNode);
+ }
+
+ protected void initModel(GraphModel graphModel, Object externalNode) {
+ this.setData(externalNode);
+ ZestPlugin plugin = ZestPlugin.getDefault();
+ this.sourceConnections = new ArrayList();
+ this.targetConnections = new ArrayList();
+ this.preferredLocation = false;
+ this.foreColor = plugin.getColor(IZestColorConstants.BLACK);
+ this.backColor = plugin.getColor(IZestColorConstants.LIGHT_BLUE);
+ this.highlightColor = plugin.getColor(IZestColorConstants.YELLOW);
+ this.unhighlightColor = this.backColor;
+ this.highlightAdjacentColor = plugin.getColor(IZestColorConstants.ORANGE);
+ this.nodeStyle = IZestGraphDefaults.NODE_STYLE;
+ this.borderColor = plugin.getColor(IZestColorConstants.BLACK);
+ this.borderHighlightColor = plugin.getColor(IZestColorConstants.BLUE);
+ this.borderUnhighlightColor = plugin.getColor(IZestColorConstants.BLACK);
+ this.borderWidth = 1;
+ this.currentLocation = new Point(10, 10);
+ this.size = new Dimension(20, 20);
+ this.font = Display.getDefault().getSystemFont();
+ this.graphModel = graphModel;
+
+ if (font == null) {
+ font = JFaceResources.getDefaultFont();
+ }
+ }
+
+ /**
+ * A simple toString that we can use for debugging
+ */
+ public String toString() {
+ return "GraphModelNode: " + getText();
+ }
+
+ /**
+ * Compares two nodes.
+ */
+ public int compareTo(Object otherNode) {
+ int rv = 0;
+ if (otherNode instanceof IGraphModelNode) {
+ IGraphModelNode node = (IGraphModelNode)otherNode;
+ if (this.getText() != null) {
+ rv = this.getText().compareTo(node.getText());
+ }
+ }
+ return rv;
+ }
+
+ /**
+ * Gets the user data associated with this node.
+ * @return The user data associated with this node
+ */
+ public Object getExternalNode() {
+ return this.getData();
+ }
+
+ /**
+ * Returns a new list of the source connections (GraphModelConnection objects).
+ * @return List a new list of GraphModelConnect objects
+ */
+ public List getSourceConnections() {
+ return new ArrayList(sourceConnections);
+ }
+
+ /**
+ * Returns a new list of the target connections (GraphModelConnection objects).
+ * @return List a new list of GraphModelConnect objects
+ */
+ public List getTargetConnections() {
+ return new ArrayList(targetConnections);
+ }
+
+
+ /**
+ * Adds the given connection to the list of connections.
+ * @param connection
+ * @param source true if the given connection should be added as a source.
+ */
+ public void addConnection(IGraphModelConnection connection, boolean source) {
+ if (connection != null) {
+ if (source) {
+ if (connection.getSource() == this) {
+ sourceConnections.add(connection);
+ firePropertyChange(SOURCE_CONNECTIONS_PROP, null, connection);
+ }
+ } else {
+ if (connection.getDestination() == this) {
+ targetConnections.add(connection);
+ firePropertyChange(TARGET_CONNECTIONS_PROP, null, connection);
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes the connection from the list if it exists.
+ * @param connection
+ * @return boolean if the connection was removed
+ */
+ public boolean removeConnection(IGraphModelConnection connection) {
+ boolean removed = false;
+ if (connection != null) {
+ if (connection.getSource() == this) {
+ removed = sourceConnections.remove(connection);
+ if (removed) {
+ firePropertyChange(SOURCE_CONNECTIONS_PROP, null, connection);
+ }
+ }
+ //@tag zest.bug.unreported.fix : if the connection is a self-loop we have to remove from both source and target
+ if (connection.getDestination() == this) {
+ removed = targetConnections.remove(connection);
+ if (removed) {
+ firePropertyChange(TARGET_CONNECTIONS_PROP, null, connection);
+ }
+ }
+ }
+ return removed;
+ }
+
+ public void setHasPreferredLocation( boolean preferredLocation ) {
+ this.preferredLocation = preferredLocation;
+ }
+
+ public boolean hasPreferredLocation() {
+ return preferredLocation;
+ }
+
+ public double getXInLayout() {
+ return currentLocation.x;
+ }
+
+ public double getYInLayout() {
+ return currentLocation.y;
+ }
+
+ /**
+ * Returns the bounds of this node. It is just the combination
+ * of the location and the size.
+ * @return Rectangle
+ */
+ public Rectangle getBounds() {
+ return new Rectangle(getLocation(), getSize());
+ }
+
+ /**
+ * Returns a copy of the node's location.
+ * @return Point
+ */
+ public Point getLocation() {
+ return currentLocation.getCopy();
+ }
+
+ public double getWidthInLayout() {
+ return size.width;
+ }
+
+ public double getHeightInLayout() {
+ return size.height;
+ }
+
+ public void setSelected( boolean selected ) {
+ if (selected = isSelected()) return;
+ if (selected) {
+ highlight();
+ } else {
+ unhighlight();
+ }
+ this.selected = selected;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#isSelected()
+ */
+ public boolean isSelected() {
+ return selected;
+ }
+
+ public void setPreferredLocation( double x, double y ) {
+ setLocation(x,y);
+ }
+
+
+ public void setLocation( double x, double y ) {
+ Point oldPoint = getLocation();
+ currentLocation.setLocation((int)x, (int)y);
+ firePropertyChange(LOCATION_PROP, oldPoint, currentLocation);
+ }
+
+
+ /**
+ * Sets the layout location of this node
+ */
+ public void setLocationInLayout(double x, double y) {
+ this.setLocation(x, y);
+ }
+
+ /**
+ * Returns a copy of the node's size.
+ * @return Dimension
+ */
+ public Dimension getSize() {
+ return size.getCopy();
+ }
+
+ /**
+ * Sets the size of this node
+ */
+ public void setSizeInLayout(double width, double height) {
+ setSize(width, height);
+ }
+
+ /**
+ * Get the foreground colour for this node
+ */
+ public Color getForegroundColor() {
+ return foreColor;
+ }
+
+ /**
+ * Set the foreground colour for this node
+ */
+ public void setForegroundColor(Color c) {
+ Color old = foreColor;
+ this.foreColor = c;
+ firePropertyChange(COLOR_FG_PROP, old, c);
+ }
+
+ /**
+ * Get the background colour for this node
+ */
+ public Color getBackgroundColor() {
+ return backColor;
+ }
+
+ /**
+ * Permantly sets the background color (unhighlighted).
+ * For temporary color changes call #changeBackgroundColor instead.
+ * @param c
+ */
+ public void setBackgroundColor(Color c) {
+ unhighlightColor = c;
+ changeBackgroundColor(c);
+ }
+
+
+ /**
+ * Sets the border color.
+ * @param c the border color.
+ */
+ public void setBorderColor(Color c) {
+ Color old = borderColor;
+ borderColor = c;
+ borderUnhighlightColor = c;
+ firePropertyChange(COLOR_BD_PROP, old, c);
+ }
+
+ /**
+ * Sets the highlighted border color.
+ * @param c the highlighted border color.
+ */
+ public void setBorderHighlightColor(Color c) {
+ this.borderHighlightColor =c;
+ }
+
+
+ /**
+ * Changes the background color and fires a property change event.
+ * @param c
+ */
+ public void changeBackgroundColor(Color c) {
+ Color old = backColor;
+ backColor = c;
+ firePropertyChange(COLOR_BG_PROP, old, c);
+
+ }
+
+ /**
+ * Get the highlight colour for this node
+ */
+ public Color getHighlightColor() {
+ return highlightColor;
+ }
+
+ /**
+ * Set the highlight colour for this node
+ */
+ public void setHighlightColor(Color c) {
+ this.highlightColor = c;
+ }
+
+ /**
+ * Get the highlight adjacent colour for this node. This is the colour
+ * that adjacent nodes will get
+ */
+ public Color getHighlightAdjacentColor() {
+ return highlightAdjacentColor;
+ }
+
+ /**
+ * Set the highlight adjacent colour for this node. This is the colour
+ * that adjacent node will get.
+ */
+ public void setHighlightAdjacentColor(Color c) {
+ this.highlightAdjacentColor = c;
+ }
+ /**
+ * Highlights the node changing the background color and border color.
+ * The source and destination connections are also highlighted,
+ * and the adjacent nodes are highlighted too in a different color.
+ */
+ public void highlight() {
+ if (isHighlighted()) return;
+ highlighted = true;
+ boolean fireEvent = false;
+ if (ZestStyles.checkStyle(getNodeStyle(), ZestStyles.NODES_HIGHLIGHT_ADJACENT)) {
+ fireEvent = true;
+ for (Iterator iter = sourceConnections.iterator(); iter.hasNext();) {
+ IGraphModelConnection conn = (IGraphModelConnection)iter.next();
+ conn.highlight();
+ conn.getDestination().highlightAdjacent();
+ }
+ for (Iterator iter = targetConnections.iterator(); iter.hasNext();) {
+ IGraphModelConnection conn = (IGraphModelConnection)iter.next();
+ conn.highlight();
+ conn.getSource().highlightAdjacent();
+ }
+ }
+ if (backColor != highlightColor) {
+ fireEvent = true;
+ borderColor = borderHighlightColor;
+ changeBackgroundColor(highlightColor);
+ // highlight the adjacent nodes
+ //@tag zest.bug.160367-Refreshing.fix : it was notices as a side-effect of this bug that we were never actually checking for the highlight-adjacent style. That is now fixed.
+ }
+ if (fireEvent) {
+ firePropertyChange(HIGHLIGHT_PROP, Boolean.FALSE, Boolean.TRUE);
+ }
+ }
+
+ public boolean isHighlighted() {
+ return highlighted;
+ }
+
+ /**
+ * Restores the nodes original background color and border width.
+ */
+ public void unhighlight() {
+ if (!isHighlighted()) return;
+ highlighted = false;
+ boolean fireEvent = false;
+ if (ZestStyles.checkStyle(getNodeStyle(), ZestStyles.NODES_HIGHLIGHT_ADJACENT)) {
+ fireEvent = true;
+ // unhighlight the adjacent edges
+ for (Iterator iter = sourceConnections.iterator(); iter.hasNext();) {
+ IGraphModelConnection conn = (IGraphModelConnection)iter.next();
+ conn.unhighlight();
+ conn.getDestination().unhighlight();
+ }
+ for (Iterator iter = targetConnections.iterator(); iter.hasNext();) {
+ IGraphModelConnection conn = (IGraphModelConnection)iter.next();
+ conn.unhighlight();
+ conn.getSource().unhighlight();
+ }
+ }
+ if (unhighlightColor != backColor) {
+ fireEvent = true;
+ changeBackgroundColor(unhighlightColor);
+ borderColor = borderUnhighlightColor;
+
+ }
+ if (fireEvent) {
+ firePropertyChange(HIGHLIGHT_PROP, Boolean.TRUE, Boolean.FALSE);
+ }
+ }
+
+ /**
+ * Highlights this node using the adjacent highlight color.
+ * This only does something if highlighAdjacentNodes is set to true
+ * and if the node isn't already highlighted.
+ * @see #setHighlightAdjacentNodes(boolean)
+ */
+ public void highlightAdjacent() {
+ if (isHighlightAdjacentNodes() && (backColor != highlightAdjacentColor) && (backColor != highlightColor)) {
+ borderColor = borderHighlightColor;
+ changeBackgroundColor(highlightAdjacentColor);
+ highlighted = true;
+ }
+ }
+
+ /**
+ * Returns if the nodes adjacent to this node will be highlighted when
+ * this node is selected.
+ * @return GraphModelNode
+ */
+ public boolean isHighlightAdjacentNodes() {;
+ return ZestStyles.checkStyle(nodeStyle, ZestStyles.NODES_HIGHLIGHT_ADJACENT);
+ }
+
+ /**
+ * Sets if the adjacent nodes to this one should be highlighted when
+ * this node is selected.
+ * @param highlightAdjacentNodes The highlightAdjacentNodes to set.
+ */
+ public void setHighlightAdjacentNodes(boolean highlightAdjacentNodes) {
+ if (!highlightAdjacentNodes) {
+ this.nodeStyle |= ZestStyles.NODES_HIGHLIGHT_ADJACENT;
+ this.nodeStyle ^= ZestStyles.NODES_HIGHLIGHT_ADJACENT;
+ return;
+ }
+ this.nodeStyle |= ZestStyles.NODES_HIGHLIGHT_ADJACENT;
+ }
+
+ public Color getBorderColor() {
+ return borderColor;
+ }
+
+ public int getBorderWidth() {
+ return borderWidth;
+ }
+
+ public void setBorderWidth(int width) {
+ this.borderWidth = width;
+ }
+
+ public Object getLayoutInformation() {
+ return internalNode;
+ }
+
+ public void setLayoutInformation(Object layoutInformation) {
+ this.internalNode = layoutInformation;
+ }
+
+ public Font getFont() {
+ return font;
+ }
+
+ public void setFont(Font font) {
+ this.labelSize = null;
+ this.font = font;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Item#setText(java.lang.String)
+ */
+ public void setText(String string) {
+ this.labelSize = null;
+ super.setText(string);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Item#setImage(org.eclipse.swt.graphics.Image)
+ */
+ public void setImage(Image image) {
+ this.labelSize = null;
+ super.setImage(image);
+ }
+
+ /**
+ * Returns the extent of the text and the image with some padding.
+ * @return Dimension the minimum size needed to display the text and the image
+ */
+ public Dimension calculateMinimumLabelSize() {
+ if (labelSize == null) {
+ Dimension text = calculateTextExtents();
+ Dimension icon = calculateImageExtents();
+ labelSize = new Dimension(text.width + icon.width, Math.max(text.height, icon.height));
+ labelSize.expand(12, 6);
+ }
+ return labelSize;
+ }
+
+ /**
+ * Gets the minimum size for this node. This is the minimum size of the label (text & icon)
+ * @return Dimension
+ */
+ public Dimension calculateMinimumSize() {
+ return calculateMinimumLabelSize();
+ }
+
+
+ /**
+ * Gets the graphModel that this node is contained in
+ * @return The graph model that this node is contained in
+ */
+ public GraphModel getGraphModel() {
+ return this.graphModel;
+ }
+
+
+ private Dimension calculateTextExtents() {
+ Dimension dim = new Dimension(0, 0);
+ String text = getText();
+ if (text != null) {
+ if (font == null) {
+ font = JFaceResources.getDefaultFont();
+ }
+ dim.setSize(FigureUtilities.getTextExtents(text + " ", font));
+ }
+ return dim;
+ }
+
+ private Dimension calculateImageExtents() {
+ Dimension dim = new Dimension(0, 0);
+ Image image = getImage();
+ if (image != null) {
+ dim.setSize(new Dimension(image.getBounds().width + 4, image.getBounds().height));
+ }
+ return dim;
+ }
+
+
+ /**
+ * @return the nodeStyle
+ */
+ public int getNodeStyle() {
+ return nodeStyle;
+ }
+
+ /**
+ * @param nodeStyle the nodeStyle to set
+ */
+ public void setNodeStyle(int nodeStyle) {
+ this.nodeStyle = nodeStyle;
+ this.cacheLabel = ((this.nodeStyle & ZestStyles.NODES_CACHE_LABEL) > 0) ? true : false;
+ }
+
+ public void populateLayoutConstraint(LayoutConstraint constraint) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setSize(double, double)
+ */
+ public void setSize(double width, double height) {
+ if ((width != size.width) || (height != size.height)) {
+ Object old = getSize();
+ size.width = (int)width;
+ size.height = (int)height;
+ firePropertyChange(SIZE_PROP, old, size);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getUnhiglightColor()
+ */
+ public Color getUnhiglightColor() {
+ return unhighlightColor;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getBorderHighlightColor()
+ */
+ public Color getBorderHighlightColor() {
+ return borderHighlightColor;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getBorderUnhiglightColor()
+ */
+ public Color getBorderUnhiglightColor() {
+ return borderUnhighlightColor;
+ }
+
+ public boolean cacheLabel() {
+ return this.cacheLabel;
+ }
+
+ public void setCacheLabel(boolean cacheLabel) {
+ this.cacheLabel = cacheLabel;
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphItem.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphItem.java
new file mode 100644
index 0000000..0d89d57
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphItem.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import java.beans.PropertyChangeListener;
+
+/**
+ * Simple base interface for graph items to allow property change support.
+ * The properties that change are dependant on the particular graph item.
+ * @author Del Myers
+ *
+ */
+//@tag bug(154259-Abstraction(fix))
+public interface IGraphItem {
+ /**
+ * The visibility of this item has changed.
+ */
+ public static final String VISIBLE_PROP = "GraphItem.Visible";
+ /**
+ * Attach a non-null PropertyChangeListener to this object.
+ * @param l a non-null PropertyChangeListener instance
+ * @throws IllegalArgumentException if the parameter is null
+ */
+ public void addPropertyChangeListener(PropertyChangeListener l);
+ /**
+ * Remove a PropertyChangeListener from this component.
+ * @param l a PropertyChangeListener instance
+ */
+ public void removePropertyChangeListener(PropertyChangeListener l);
+
+ /**
+ * Fires the given property change to all listeners.
+ * @param property
+ * @param oldValue
+ * @param newValue
+ */
+ void firePropertyChange(String property, Object oldValue, Object newValue);
+
+ /**
+ * The data for this item. Normally the user's model object.
+ * @return
+ */
+ public Object getData();
+
+ /**
+ * Set the visibility of this item.
+ * @param visible whether or not this item is visible.
+ */
+ public void setVisible(boolean visible);
+
+ /**
+ * Get the visibility of this item.
+ * @return the visibility of this item.
+ */
+ public boolean isVisible();
+
+ /**
+ * Gets the graph that this item is rooted on. If this item is itself a graph, then this
+ * is returned.
+ * @return the parent graph.
+ */
+ public GraphModel getGraphModel();
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphModelConnection.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphModelConnection.java
new file mode 100644
index 0000000..d89b530
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphModelConnection.java
@@ -0,0 +1,278 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+
+import org.eclipse.mylar.zest.core.ZestStyles;
+import org.eclipse.mylar.zest.layouts.LayoutRelationship;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Abstraction of connections in the graph model to allow for a common
+ * interface for different types of connections.
+ * @author Del Myers
+ *
+ */
+//@tag bug(154259-Abstraction(fix))
+public interface IGraphModelConnection extends IGraphItem, LayoutRelationship {
+ //property changes for graph model connections.
+ /** The line color has changed for this connection **/
+ public static final String LINECOLOR_PROP = "LineColor";
+ //@tag unreported(EdgeHighlight) : fire property change when the edge highlight state changes.
+ /** The connection has been highlighted **/
+ public static final String HIGHLIGHT_PROP = "Highlight";
+ /** The connection has been unhighlighted **/
+ public static final String UNHIGHLIGHT_PROP = "UnHighlight";
+ /** The line width has changed **/
+ public static final String LINEWIDTH_PROP = "LineWidth";
+ /** The line style has changed **/
+ public static final String LINESTYLE_PROP = "LineStyle";
+ /** The state of whether this line is directed has changed **/
+ public static final String DIRECTED_EDGE_PROP = "DirectedEdgeStyle";
+ /** Something has changed with the curve, i.e. its depth or its bezier coordinates */
+ public static final String CURVE_PROP = "CurveChanged";
+
+
+ /**
+ * Returns the representation of this connection in the user's model.
+ * @return the representation of this connection in the user's model.
+ */
+ Object getExternalConnection();
+ /**
+ * Reconnects the connection to its source and destination nodes.
+ *
+ */
+ void reconnect();
+ /**
+ * Reconnects this connection to the given source and destination nodes.
+ * Sets this connection's source and destination to the given nodes.
+ * @param source the source node.
+ * @param dest the destination node.
+ */
+ void reconnect(IGraphModelNode source, IGraphModelNode dest);
+ /**
+ * Disconnects this connection.
+ *
+ */
+ void disconnect();
+ /**
+ * Gets the root graph model that this connection is on.
+ * @return
+ */
+ GraphModel getGraphModel();
+ /**
+ * Returns the source node.
+ * @return the source node.
+ */
+ IGraphModelNode getSource();
+ /**
+ * Returns the destination node.
+ * @return the destination node.
+ */
+ IGraphModelNode getDestination();
+ /**
+ * Sets the style for this connection.
+ * @param style the style for this connection.
+ * @see ZestStyles
+ */
+ void setConnectionStyle(int style);
+ /**
+ * Returns the style for this connection.
+ * @return the style for this connection.
+ * @see ZestStyles
+ */
+ int getConnectionStyle();
+ /**
+ * Sets the depth of the curve on this connection. Normally ignored unless
+ * the style has ZestStyles.CONNECTIONS_CURVED set.
+ * @param depth
+ * @see ZestStyles
+ */
+ void setCurveDepth(int depth);
+ /**
+ * Returns the depth of the curve on this connection. Normally ignored unless
+ * the style has ZestStyles.CONNECTIONS_CURVED set.
+ * @return the curve depth.
+ * @see ZestStyles
+ */
+ int getCurveDepth();
+ /**
+ * Returns the end angle for this connection. Used for connections that have
+ * ZestStyles.CONNECTIONS_BEZIER set.
+ * @return the end angle.
+ * @see ZestStyles
+ */
+ double getEndAngle();
+ /**
+ * Returns the start angle for this connection. Used for connections that have
+ * ZestStyles.CONNECTIONS_BEZIER set.
+ * @return the start angle.
+ * @see ZestStyles
+ */
+ double getStartAngle();
+ /**
+ * Returns the start length for this connection. Used for connections that have
+ * ZestStyles.CONNECTIONS_BEZIER set.
+ * @return the start length.
+ * @see ZestStyles
+ */
+ double getStartLength();
+ /**
+ * Returns the end length for this connection. Used for connections that have
+ * ZestStyles.CONNECTIONS_BEZIER set.
+ * @return the end length.
+ * @see ZestStyles
+ */
+ double getEndLength();
+ /**
+ * Sets the start angle for this connection. Used for connections that have
+ * ZestStyles.CONNECTIONS_BEZIER set.
+ * @param angle the start angle.
+ * @see ZestStyles
+ */
+ void setStartAngle(double angle);
+ /**
+ * Sets the start length for this connection. Used for connections that have
+ * ZestStyles.CONNECTIONS_BEZIER set.
+ * @param length the start length.
+ * @see ZestStyles
+ */
+ void setStartLength(double length);
+ /**
+ * Sets the end angle for this connection. Used for connections that have
+ * ZestStyles.CONNECTIONS_BEZIER set.
+ * @param angle the end angle.
+ * @see ZestStyles
+ */
+ void setEndAngle(double angle);
+ /**
+ * Sets the end length for this connection. Used for connections that have
+ * ZestStyles.CONNECTIONS_BEZIER set.
+ * @param length the end length.
+ * @see ZestStyles
+ */
+ void setEndLength(double length);
+ /**
+ * Returns the font for this connection.
+ * @return the font for this connection.
+ */
+ Font getFont();
+ /**
+ * Sets the font for this connection.
+ * @param font the font for this connection.
+ */
+ void setFont(Font font);
+ /**
+ * Returns the color that is used when this connection is highlighted.
+ * @return the color that is used when this connection is highlighted.
+ */
+ Color getHighlightColor();
+ /**
+ * Sets the color that is used when this connection is highlighted.
+ * @param color the color that is used when this connection is highlighted.
+ */
+ void setHighlightColor(Color color);
+ /**
+ * Returns the line color that is used for this connection.
+ * @return the line color that is used for this connection.
+ */
+ Color getLineColor();
+ /**
+ * Permanently sets the line color that is used for this connection.
+ * @param c the line color that is used for this connection.
+ */
+ void setLineColor(Color c);
+ /**
+ * Temporarily changes the line color that is used for this connection. Used as
+ * an intermediary step for highlighting, and other graphical changes.
+ * @return the line color that is used for this connection.
+ */
+ void changeLineColor(Color c);
+ /**
+ * Returns the line style for this connection, based on the SWT line styles.
+ * @return the line style for this connection, based on the SWT line styles.
+ * @see SWT.LINE_SOLID
+ * @see SWT.LINE_DASH
+ * @see SWT.LINE_DOT
+ * @see SWT.LINE_DASHDOT
+ * @see SWT.LINE_DASHDOTDOT
+ */
+ //@tag zest(discussion) : sould this be set in the connection style instead, using custom zest styles?
+ int getLineStyle();
+ /**
+ * Returns the line width in pixels for this connection.
+ */
+ int getLineWidth();
+ /**
+ * Sets the line width in pixels for this connection.
+ * @param width the line width.
+ */
+ void setLineWidth(int width);
+ /**
+ * Changes the color of this line to the highlight color.
+ *
+ */
+ void highlight();
+ /**
+ * Reverts back from the highlighted state.
+ *
+ */
+ void unhighlight();
+ /**
+ * Sets the line style for this connection, based on the SWT line styles.
+ * @param style the line style for this connection, based on the SWT line styles.
+ * @see SWT.LINE_SOLID
+ * @see SWT.LINE_DASH
+ * @see SWT.LINE_DOT
+ * @see SWT.LINE_DASHDOT
+ * @see SWT.LINE_DASHDOTDOT
+ */
+ //@tag zest(discussion) : sould this be set in the connection style instead, using custom zest styles?
+ void setLineStyle(int style);
+
+ /**
+ * Sets the image.
+ * @param image
+ */
+ void setImage(Image image);
+
+ /**
+ * Gets the image.
+ * @return
+ */
+ Image getImage();
+
+ /**
+ * Sets the text.
+ * @param text
+ */
+ void setText(String text);
+ /**
+ * returns the text.
+ * @return the text.
+ */
+ String getText();
+
+ /**
+ * Sets the weight in the layout.
+ * @param weight the weight in the layout.
+ */
+ public void setWeightInLayout(double weight);
+
+ /**
+ *
+ * @return the wieght in the layout.
+ */
+ public double getWeightInLayout();
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphModelFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphModelFactory.java
new file mode 100644
index 0000000..5e8e3c2
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphModelFactory.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+/**
+ * @author Ian Bull
+ * @deprecated use {@link IStylingGraphModelFactory} instead.
+ */
+public interface IGraphModelFactory {
+
+ /**
+ * Creates a new graph model
+ * @return
+ */
+ public abstract GraphModel createModel();
+
+ public abstract GraphModel createModelFromContentProvider(
+ Object inputElement, int nodeStyle, int connectionStyle);
+
+ /**
+ * Creates a new relationship using the content provider to get the source and destination.
+ * @param model The graph model.
+ * @param data The data object for the new connection.
+ * @param contentProvider The content provider which will get the source and destination nodes.
+ * @return GraphModelConnection
+ */
+ public abstract IGraphModelConnection createRelationship(GraphModel model,
+ Object data);
+
+ /**
+ * Creates a new relationship and adds it to the model
+ * @param model
+ * @param source
+ * @param dest
+ * @return GraphModelConnection
+ */
+ public abstract IGraphModelConnection createRelationship(GraphModel model,
+ Object data, Object source, Object dest);
+
+ /**
+ * Creates a new node and adds it to the model. If the node already exists it is just returned.
+ * @param model The graph model
+ * @param data The new node's data object
+ * @return GraphModelNode the new or existing node
+ */
+ public abstract IGraphModelNode createNode(GraphModel model, Object data);
+
+}
\ No newline at end of file
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphModelNode.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphModelNode.java
new file mode 100644
index 0000000..812af17
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IGraphModelNode.java
@@ -0,0 +1,296 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import java.util.List;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.mylar.zest.core.ZestStyles;
+import org.eclipse.mylar.zest.layouts.LayoutEntity;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * An abstraction of nodes in the graph model to allow different kinds of nodes to have the
+ * same interface.
+ * @author Del Myers
+ *
+ */
+//@tag bug(154259-Abstraction(fix))
+public interface IGraphModelNode extends IGraphItem, LayoutEntity {
+ public static final String LOCATION_PROP = "GraphModelNode.Location";
+ public static final String SIZE_PROP = "GraphModelNode.Size";
+ public static final String FORCE_REDRAW = "GraphModelNode.Redraw";
+ public static final String COLOR_BG_PROP = "GraphModelNode.BGColor";
+ public static final String COLOR_FG_PROP = "GraphModelNode.FGColor";
+ public static final String COLOR_BD_PROP = "GraphModelNode.BDColor";
+ public static final String HIGHLIGHT_PROP = "GraphModelNode.Highlight";
+ public static final String SOURCE_CONNECTIONS_PROP = "GraphModelNode.SourceConn";
+ public static final String TARGET_CONNECTIONS_PROP = "GraphModelNode.TargetConn";
+ public static final String BRING_TO_FRONT = "GraphModelNode.BrintToFront";
+
+ /**
+ * Returns the node in the user's model.
+ * @return the node in the user's model.
+ */
+ Object getExternalNode();
+ /**
+ * The background color.
+ * @return the background color.
+ */
+ Color getBackgroundColor();
+
+ /**
+ * Should the text on the labels be cached.
+ * @return
+ */
+ boolean cacheLabel();
+
+ /**
+ * Sets should the labels be cached
+ * @param cacheLabel
+ */
+ void setCacheLabel(boolean cacheLabel);
+
+ /**
+ * Returns the color that the background will return to after unhighlighting.
+ * @return the color that the background will return to after unhighlighting.
+ */
+ Color getUnhiglightColor();
+ /**
+ * The border color.
+ * @return the border color.
+ */
+ Color getBorderColor();
+ /**
+ * Return the border highlight color;
+ * @return the border highlight color.
+ */
+ Color getBorderHighlightColor();
+
+ /***
+ * Return the border unhighlight color.
+ * @return the border unhighlight color.
+ */
+ Color getBorderUnhiglightColor();
+ /**
+ * The border width in pixels.
+ * @return the border width.
+ */
+ int getBorderWidth();
+ /**
+ * The font.
+ * @return the font.
+ */
+ Font getFont();
+ /**
+ * The forground color.
+ * @return the forground color.
+ */
+ Color getForegroundColor();
+ /**
+ * The root graph model.
+ * @return the root graph model.
+ */
+ GraphModel getGraphModel();
+ /**
+ * The color used to highlight adjacent nodes.
+ * @return the color used to highlight adjacent nodes.
+ */
+ Color getHighlightAdjacentColor();
+ /**
+ * The color used to highlight this node.
+ * @return the color used to highlight this node.
+ */
+ Color getHighlightColor();
+ /**
+ * The location of the node.
+ * @return the location of the node.
+ */
+ Point getLocation();
+ /**
+ * The style of the node.
+ * @return the style of the node.
+ * @see ZestStyles
+ */
+ int getNodeStyle();
+ /**
+ * The size of this node.
+ * @return the size of this node.
+ */
+ Dimension getSize();
+ /**
+ * Returns a list of {@link IGraphModelConnection}s that have this node as their source.
+ * @return a list of {@link IGraphModelConnection}s that have this node as their source.
+ */
+ List getSourceConnections();
+ /**
+ * Returns a list of {@link IGraphModelConnection}s that have this node as their target.
+ * @return a list of {@link IGraphModelConnection}s that have this node as their target.
+ */
+ List getTargetConnections();
+ /**
+ * Highlights this node (and its adjacent nodes/connections, if applicable).
+ *
+ */
+ void highlight();
+ /**
+ * Highlights the nodes adjacent to this one.
+ *
+ */
+ void highlightAdjacent();
+ /**
+ * Returns true iff the style is set to highlight adjacent nodes.
+ * @return true iff the style is set to highlight adjacent nodes.
+ */
+ boolean isHighlightAdjacentNodes();
+ /**
+ * Removes the given connection from this node, if it is connected to this node.
+ * @param conn the connection to remove.
+ * @return true iff the connection was removed.
+ */
+ boolean removeConnection(IGraphModelConnection conn);
+ /**
+ * Permanently sets the background color of this node.
+ * @param c the new background color.
+ */
+ void setBackgroundColor(Color c);
+ /**
+ * Temporarily changes the background color of this node. Used as an intermediary
+ * step for graphical feedback such as highlighting.
+ * @param c the color to change to.
+ */
+ void changeBackgroundColor(Color c);
+ /**
+ * Sets the border color for this node.
+ * @param c the new border color.
+ */
+ void setBorderColor(Color c);
+ /**
+ * Sets the color used on the border for when this node is highlighted.
+ * @param c the new color.
+ */
+ void setBorderHighlightColor(Color c);
+ /**
+ * Sets the border width in pixels.
+ * @param width the new width.
+ */
+ void setBorderWidth(int width);
+ /**
+ * Sets the font to be used for labels.
+ * @param f the new font.
+ */
+ void setFont(Font f);
+ /**
+ * Sets the foreground color for labels.
+ * @param c the new foreground color.
+ */
+ void setForegroundColor(Color c);
+ /**
+ * Sets whether or not this node has a preferred location.
+ * @param hasPreferredLocation whether or not this node has a preferred location.
+ */
+ void setHasPreferredLocation(boolean hasPreferredLocation);
+ /**
+ * Returns true if this node has a preferred location.
+ * @return true iff this node has a preferred location.
+ */
+ public boolean hasPreferredLocation();
+ /**
+ * Sets the color that will be used to highlight adjacent nodes.
+ * @param c the color that will be used to highlight adjacent nodes.
+ */
+ void setHighlightAdjacentColor(Color c);
+ /**
+ * Sets whether or not this node should highlight its adjacent nodes/connections.
+ * @param highlightAdjacent whether or not this node should highlight its adjacent nodes/connections.
+ */
+ void setHighlightAdjacentNodes(boolean highlightAdjacent);
+ /**
+ * Sets the highlight color for this node.
+ * @param c the highlight color for this node.
+ */
+ void setHighlightColor(Color c);
+ /**
+ * Sets the image for this node.
+ * @param image the image for this node.
+ */
+ void setImage(Image image);
+ /**
+ * Gets the image for this node.
+ * @return the image for this node.
+ */
+ Image getImage();
+ /**
+ * Sets the location of this node.
+ * @param x x location of this node.
+ * @param y y location of this node.
+ */
+ void setLocation(double x, double y);
+ /**
+ * Sets the size.
+ * @param width
+ * @param height
+ */
+ void setSize(double width, double height);
+ /**
+ * Sets the node style.
+ * @param style the node style.
+ * @see ZestStyles
+ */
+ void setNodeStyle(int style);
+ /**
+ * Sets the preferred location of this node.
+ * @param x the preferred x location.
+ * @param y the preferred y location.
+ */
+ void setPreferredLocation(double x, double y);
+ /**
+ * Sets whether or not this node is selected.
+ * @param selected whether or not this node is selected.
+ */
+ void setSelected(boolean selected);
+ /**
+ * Returns true if this node is selected.
+ * @return true iff this node is selected.
+ */
+ boolean isSelected();
+ /**
+ * Sets the text label for this node.
+ * @param text the text label for this node.
+ */
+ void setText(String text);
+ /**
+ * Unhighlights this node and its neighbors if applicable.
+ *
+ */
+ void unhighlight();
+ /**
+ * Gets the text of this node.
+ * @return
+ */
+ String getText();
+
+ /**
+ * Adds the given connection to this graph node.
+ * @param c the connection
+ * @param source true if the connection should be added as a source, false
+ * if it should be added as a target.
+ */
+ void addConnection(IGraphModelConnection c, boolean source);
+
+ Dimension calculateMinimumLabelSize();
+
+ Dimension calculateMinimumSize();
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IStylingGraphModelFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IStylingGraphModelFactory.java
new file mode 100644
index 0000000..5f7dcbd
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IStylingGraphModelFactory.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.StructuredViewer;
+
+/**
+ * A Graph model factory that supports the structural and visual refreshing of graph elements based
+ * on the content provider and label provider in the viewer that this factory is associated with.
+ * Model elements are created using the content provider supplied by getContentProvider(), and
+ * styled using the label provider supplied by getLabelProvider(). By the end of creation and
+ * refreshing, the graph model elements are expected to be styled according to the given label provider,
+ * however, default styles are dependant on the particular implementation of IStylingGraphModelFactory.
+ * Unless otherwise documented, clients should expect that the implementation of IStylingGraphModelFactory
+ * adheres to the general defaults found in {@link IZestGraphDefaults}.
+ * @author Del Myers
+ */
+
+public interface IStylingGraphModelFactory {
+ /**
+ * Returns the label provider used in this factory.
+ * @return the label provider used in this factory.
+ */
+ public IBaseLabelProvider getLabelProvider();
+
+ /**
+ * Returns the content provider used in this factory.
+ * @return the content provider used in this factory.
+ */
+ public IStructuredContentProvider getContentProvider();
+
+ /**
+ * Creates and returns the graph model from this factory based on the label provider and the
+ * label provider returned in getContentProvider() and getLabelProvider().
+ * @return the created graph model.
+ */
+ public GraphModel createGraphModel();
+
+ /**
+ * Creates and returns a node on the given graph based on the user model data, "data",
+ * using the content provider returned by getContentProvider(). They node will also be styled
+ * according to the information given by the label provider. If the node already exists in the
+ * graph, it is restyled and returned; no new node is created.
+ * @param graph the graph to create or retrieve the node on.
+ * @param element the user model data to use in the node.
+ * @return the node created or retrieved for the given graph.
+ */
+ public IGraphModelNode createNode(GraphModel graph, Object element);
+
+ /**
+ * Creates and returns a connection with the given source and destination objects from the user
+ * model. If the source and destination nodes don't exist for the given user model objects, they
+ * are created using createNode(GraphModel, Object). If a connection already exists for the given
+ * user data, but with different source or destinations, it is disconnected and reconnected to the
+ * given source and destination. It is always styled according to the label provider provided
+ * by getLabelProvider().
+ * @param graph the graph to create or retrieve the connection on.
+ * @param element the user model data to use in this connection.
+ * @param source the user model data used for the source node.
+ * @param dest the user model data used for the destination node.
+ * @return the created or retrieved connection for the given graph.
+ */
+ public IGraphModelConnection createConnection(GraphModel graph, Object element, Object source, Object dest);
+
+ /**
+ * Restyles the given graph items according to the label provider supplied by getLabelProvider().
+ * @param items the items to update.
+ */
+ public void update(IGraphItem[] items);
+
+ /**
+ * Restyles the given graph item according to the label provider supplied by getLabelProvider().
+ * @param item the item to update.
+ */
+ public void update(IGraphItem item);
+
+ /**
+ * Structurally refreshes the graph model nodes and connections associated with the given
+ * user model element. Does nothing if the element does not currently exist in the view.
+ * No restyling is done by default.
+ * @param graph
+ * @param element the element to restructure.
+ */
+ public void refresh(GraphModel graph, Object element);
+
+ /**
+ * Structurally refreshes the graph model nodes and connections associated with the given
+ * user model element. If updateLabels is true, then the labels are updated as well.
+ * Does nothing if the element does not currently exist in the view.
+ * @param graph the graph to find the element on.
+ * @param element the user model element.
+ * @param updateLabels true if the labels should be updated as well.
+ */
+ public void refresh(GraphModel graph, Object element, boolean updateLabels);
+
+ /**
+ * Structurally refreshes the entire graph.
+ * @param graph the graph to refresh;
+ */
+ public void refreshGraph(GraphModel graph);
+
+
+ /**
+ * Returns the viewer that this factory is building the model for.
+ * @return the viewer that this factory is building the model for.
+ */
+ public StructuredViewer getViewer();
+
+ public void setConnectionStyle(int style);
+
+ /**
+ * @return the connectionStyle
+ */
+ public int getConnectionStyle();
+
+ public void setNodeStyle(int style);
+
+ /**
+ * @return the nodeStyle
+ */
+ public int getNodeStyle();
+
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IZestGraphDefaults.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IZestGraphDefaults.java
new file mode 100644
index 0000000..0ddc3a7
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/IZestGraphDefaults.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import org.eclipse.mylar.zest.core.ZestStyles;
+
+/**
+ * Default Zest Style values for graphs, connections, and nodes.
+ * @author Del Myers
+ *
+ */
+public interface IZestGraphDefaults {
+ /**
+ * Default node style.
+ */
+ //@tag zest.bug.160367.fix : we have to get rid of highlighting adjacent by default because there is no way of turning it off otherwise.
+ public static final int NODE_STYLE = 0; //removing highlight adjacent by default. ZestStyles.NODES_HIGHLIGHT_ADJACENT;
+
+ /**
+ * Default connection style.
+ */
+ public static final int CONNECTION_STYLE =
+ ZestStyles.CONNECTIONS_STRAIGHT |
+ ZestStyles.CONNECTIONS_SOLID |
+ ZestStyles.CONNECTIONS_VALIGN_MIDDLE |
+ ZestStyles.CONNECTIONS_HALIGN_CENTER;
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/NonNestedProxyNode.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/NonNestedProxyNode.java
new file mode 100644
index 0000000..e2240ba
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/NonNestedProxyNode.java
@@ -0,0 +1,662 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.draw2d.FigureUtilities;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.mylar.zest.layouts.constraints.LayoutConstraint;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * A model object that indirectly references a node. It is used at times when a
+ * graph model node needs to be virtually duplicated on the display, but not in
+ * the actual model.
+ *
+ * Note: almost all calls are sent directly to the proxy. This means, color changes,
+ * font changes, etc. Will be forwarded to the proxy. Calls that are not forwarded
+ * are noted in the javadocs.
+ * @author Del Myers
+ *
+ */
+//@tag bug(153466-NoNestedClientSupply(fix)) : don't nest nodes in the client/supplier panes.
+//@tag bug(154259-Abstraction(fix)) : implement IGraphModelNode.
+public class NonNestedProxyNode extends GraphItem implements IGraphModelNode {
+ private IGraphModelNode proxy;
+ private ArrayList sourceConnections;
+ private ArrayList targetConnections;
+ private ProxyListener proxyListener;
+ private Point currentLocation;
+ private Dimension currentSize;
+ private boolean preferredLocation;
+ private boolean selected;
+ private boolean highlighted;
+ private Object internalNode;
+ private Dimension labelSize;
+
+ private class ProxyListener implements PropertyChangeListener {
+ /* (non-Javadoc)
+ * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent evt) {
+ String property = evt.getPropertyName();
+ if (
+ LOCATION_PROP.equals(property) ||
+ SIZE_PROP.equals(property) ||
+ SOURCE_CONNECTIONS_PROP.equals(property) ||
+ TARGET_CONNECTIONS_PROP.equals(property) ||
+ HIGHLIGHT_PROP.equals(property) ||
+ VISIBLE_PROP.equals(property)
+ ) return;
+ firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
+ }
+ }
+
+ /**
+ * Creates a new node that references the given proxy. Initial display information
+ * will be taken from the proxy.
+ */
+ protected NonNestedProxyNode(IGraphModelNode proxy) {
+ super(proxy.getGraphModel());
+ this.proxy = proxy;
+ setData(proxy.getExternalNode());
+ proxyListener = new ProxyListener();
+ sourceConnections = new ArrayList();
+ targetConnections = new ArrayList();
+ currentLocation = proxy.getLocation().getCopy();
+ currentSize = proxy.getSize().getCopy();
+ this.preferredLocation = proxy.hasPreferredLocation();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Widget#getData()
+ */
+ public Object getData() {
+ return getExternalNode();
+ }
+
+ /**
+ * Connects this node to its proxy to start listening for property changes on
+ * the proxy. Any overriders must call super on this methd.
+ *
+ */
+ protected void activate() {
+ proxy.addPropertyChangeListener(proxyListener);
+ }
+
+ /**
+ * Disconnects this node from its proxy and stops listening for changes.
+ * Any overriders must call super on this methd.
+ *
+ */
+ protected void deactivate() {
+ proxy.removePropertyChangeListener(proxyListener);
+ }
+
+
+ /**
+ * Adds the given connection to the list of connections. This is not forwarded
+ * to the proxy. All connections must be added manually to this node
+ * because it is impossible to infer from the model which connections should
+ * be copied from the proxy.
+ * @param connection
+ * @param source true if the given connection should be added as a source.
+ */
+ public void addConnection(IGraphModelConnection connection, boolean source) {
+ if (connection != null) {
+ if (source) {
+ if (connection.getSource() == this) {
+ sourceConnections.add(connection);
+ firePropertyChange(SOURCE_CONNECTIONS_PROP, null, connection);
+ }
+ } else {
+ if (connection.getDestination() == this) {
+ targetConnections.add(connection);
+ firePropertyChange(TARGET_CONNECTIONS_PROP, null, connection);
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#changeBackgroundColor(org.eclipse.swt.graphics.Color)
+ */
+ public void changeBackgroundColor(Color c) {
+ proxy.changeBackgroundColor(c);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getBackgroundColor()
+ */
+ public Color getBackgroundColor() {
+ return proxy.getBackgroundColor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getBorderColor()
+ */
+ public Color getBorderColor() {
+ return proxy.getBorderColor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getBorderWidth()
+ */
+ public int getBorderWidth() {
+ return proxy.getBorderWidth();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getExternalNode()
+ */
+ public Object getExternalNode() {
+ return proxy.getExternalNode();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getFont()
+ */
+ public Font getFont() {
+ return proxy.getFont();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getForegroundColor()
+ */
+ public Color getForegroundColor() {
+ return proxy.getForegroundColor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getGraphModel()
+ */
+ public GraphModel getGraphModel() {
+ return proxy.getGraphModel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getHighlightAdjacentColor()
+ */
+ public Color getHighlightAdjacentColor() {
+ return proxy.getHighlightAdjacentColor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getHighlightColor()
+ */
+ public Color getHighlightColor() {
+ return proxy.getHighlightColor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getImage()
+ */
+ public Image getImage() {
+ return proxy.getImage();
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ */
+ public Point getLocation() {
+ return currentLocation.getCopy();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getNodeStyle()
+ */
+ public int getNodeStyle() {
+ return proxy.getNodeStyle();
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getSize()
+ */
+ public Dimension getSize() {
+ return currentSize.getCopy();
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getSourceConnections()
+ */
+ public List getSourceConnections() {
+ return sourceConnections;
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getTargetConnections()
+ */
+ public List getTargetConnections() {
+ return targetConnections;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getText()
+ */
+ public String getText() {
+ return proxy.getText();
+ }
+
+ /**
+ * Highlights the node changing the background color and border color.
+ * The source and destination connections are also highlighted,
+ * and the adjacent nodes are highlighted too in a different color.
+ */
+ public void highlight() {
+ if (isHighlighted()) return;
+ changeBackgroundColor(getHighlightColor());
+ // highlight the adjacent nodes
+ for (Iterator iter = sourceConnections.iterator(); iter.hasNext();) {
+ IGraphModelConnection conn = (IGraphModelConnection)iter.next();
+ conn.highlight();
+ conn.getDestination().highlightAdjacent();
+ }
+ for (Iterator iter = targetConnections.iterator(); iter.hasNext();) {
+ IGraphModelConnection conn = (IGraphModelConnection)iter.next();
+ conn.highlight();
+ conn.getSource().highlightAdjacent();
+ }
+ highlighted = true;
+ firePropertyChange(HIGHLIGHT_PROP, Boolean.FALSE, Boolean.TRUE);
+
+ }
+
+ public boolean isHighlighted() {
+ return highlighted;
+ }
+
+ /**
+ * Restores the nodes original background color and border width.
+ */
+ public void unhighlight() {
+ if (!isHighlighted()) return;
+ changeBackgroundColor(getUnhiglightColor());
+ // unhighlight the adjacent edges
+ for (Iterator iter = sourceConnections.iterator(); iter.hasNext();) {
+ IGraphModelConnection conn = (IGraphModelConnection)iter.next();
+ conn.unhighlight();
+ conn.getDestination().unhighlight();
+ }
+ for (Iterator iter = targetConnections.iterator(); iter.hasNext();) {
+ IGraphModelConnection conn = (IGraphModelConnection)iter.next();
+ conn.unhighlight();
+ conn.getSource().unhighlight();
+ }
+ highlighted = false;
+ firePropertyChange(HIGHLIGHT_PROP, Boolean.TRUE, Boolean.FALSE);
+
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#highlightAdjacent()
+ */
+ public void highlightAdjacent() {
+ proxy.highlightAdjacent();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#isHighlightAdjacentNodes()
+ */
+ public boolean isHighlightAdjacentNodes() {
+ return proxy.isHighlightAdjacentNodes();
+ }
+
+ /**
+ * Not forwarded to the proxy
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#removeConnection(org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelConnection)
+ */
+ public boolean removeConnection(IGraphModelConnection connection) {
+ boolean removed = false;
+ if (connection != null) {
+ if (connection.getSource() == this) {
+ removed = sourceConnections.remove(connection);
+ if (removed) {
+ firePropertyChange(SOURCE_CONNECTIONS_PROP, null, connection);
+ }
+ }
+ if (connection.getDestination() == this) {
+ removed = targetConnections.remove(connection);
+ if (removed) {
+ firePropertyChange(TARGET_CONNECTIONS_PROP, null, connection);
+ }
+ }
+ }
+ return removed;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setBackgroundColor(org.eclipse.swt.graphics.Color)
+ */
+ public void setBackgroundColor(Color c) {
+ proxy.setBackgroundColor(c);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setBorderColor(org.eclipse.swt.graphics.Color)
+ */
+ public void setBorderColor(Color c) {
+ proxy.setBorderColor(c);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setBorderHighlightColor(org.eclipse.swt.graphics.Color)
+ */
+ public void setBorderHighlightColor(Color c) {
+ proxy.setBorderHighlightColor(c);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setBorderWidth(int)
+ */
+ public void setBorderWidth(int width) {
+ proxy.setBorderWidth(width);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setFont(org.eclipse.swt.graphics.Font)
+ */
+ public void setFont(Font f) {
+ proxy.setFont(f);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setForegroundColor(org.eclipse.swt.graphics.Color)
+ */
+ public void setForegroundColor(Color c) {
+ proxy.setForegroundColor(c);
+ }
+
+ /**
+ * Not forarded to the proxy.
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setHasPreferredLocation(boolean)
+ */
+ public void setHasPreferredLocation(boolean hasPreferredLocation) {
+ this.preferredLocation = hasPreferredLocation;
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#hasPreferredLocation()
+ */
+ public boolean hasPreferredLocation() {
+ return preferredLocation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setHighlightAdjacentColor(org.eclipse.swt.graphics.Color)
+ */
+ public void setHighlightAdjacentColor(Color c) {
+ proxy.setHighlightAdjacentColor(c);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setHighlightAdjacentNodes(boolean)
+ */
+ public void setHighlightAdjacentNodes(boolean highlightAdjacent) {
+ proxy.setHighlightAdjacentNodes(highlightAdjacent);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setHighlightColor(org.eclipse.swt.graphics.Color)
+ */
+ public void setHighlightColor(Color c) {
+ proxy.setHighlightColor(c);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setImage(org.eclipse.swt.graphics.Image)
+ */
+ public void setImage(Image image) {
+ proxy.setImage(image);
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setLocation(double, double)
+ */
+ public void setLocation( double x, double y ) {
+ Point oldPoint = getLocation();
+ currentLocation.setLocation((int)x, (int)y);
+ firePropertyChange(LOCATION_PROP, oldPoint, currentLocation);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setNodeStyle(int)
+ */
+ public void setNodeStyle(int style) {
+ proxy.setNodeStyle(style);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setPreferredLocation(double, double)
+ */
+ public void setPreferredLocation(double x, double y) {
+ setLocation(x,y);
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setSelected(boolean)
+ */
+ public void setSelected( boolean selected ) {
+ if (selected = isSelected()) return;
+ if (selected) {
+ highlight();
+ } else {
+ unhighlight();
+ }
+ this.selected = selected;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#isSelected()
+ */
+ public boolean isSelected() {
+ return selected;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setText(java.lang.String)
+ */
+ public void setText(String text) {
+ proxy.setText(text);
+ }
+
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.layouts.LayoutEntity#getHeightInLayout()
+ */
+ public double getHeightInLayout() {
+ return getSize().height;
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.layouts.LayoutEntity#getLayoutInformation()
+ */
+ public Object getLayoutInformation() {
+ return internalNode;
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.layouts.LayoutEntity#getWidthInLayout()
+ */
+ public double getWidthInLayout() {
+ return getSize().width;
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.layouts.LayoutEntity#getXInLayout()
+ */
+ public double getXInLayout() {
+ return getLocation().x;
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.layouts.LayoutEntity#getYInLayout()
+ */
+ public double getYInLayout() {
+ return getLocation().y;
+ }
+
+ /**
+ * Does nothing.
+ * @see org.eclipse.mylar.zest.layouts.LayoutEntity#populateLayoutConstraint(org.eclipse.mylar.zest.layouts.constraints.LayoutConstraint)
+ */
+ public void populateLayoutConstraint(LayoutConstraint constraint) {
+
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.layouts.LayoutEntity#setLayoutInformation(java.lang.Object)
+ */
+ public void setLayoutInformation(Object internalEntity) {
+ this.internalNode = internalEntity;
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.layouts.LayoutEntity#setLocationInLayout(double, double)
+ */
+ public void setLocationInLayout(double x, double y) {
+ setLocation(x, y);
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.layouts.LayoutEntity#setSizeInLayout(double, double)
+ */
+ public void setSizeInLayout(double width, double height) {
+ setSize(width, height);
+ }
+
+ /**
+ * Not forwarded to the proxy.
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setSize(double, double)
+ */
+ public void setSize(double width, double height) {
+ Object old = getSize();
+ currentSize.setSize(new Dimension((int)width, (int)height));
+ firePropertyChange(SIZE_PROP, old, currentSize);
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(Object otherNode) {
+ int rv = 0;
+ if (otherNode instanceof IGraphModelNode) {
+ IGraphModelNode node = (IGraphModelNode)otherNode;
+ if (this.getText() != null) {
+ rv = this.getText().compareTo(node.getText());
+ }
+ }
+ return rv;
+ }
+
+
+ public IGraphModelNode getProxy() {
+ return proxy;
+ }
+
+ /**
+ * Returns the extent of the text and the image with some padding.
+ * @return Dimension the minimum size needed to display the text and the image
+ */
+ public Dimension calculateMinimumLabelSize() {
+ if (labelSize == null) {
+ Dimension text = calculateTextExtents();
+ Dimension icon = calculateImageExtents();
+ labelSize = new Dimension(text.width + icon.width, Math.max(text.height, icon.height));
+ labelSize.expand(12, 6);
+ }
+ return labelSize;
+ }
+
+ private Dimension calculateTextExtents() {
+ Dimension dim = new Dimension(0, 0);
+ String text = getText();
+ if (text != null) {
+ Font font = getFont();
+ if (font == null) {
+ font = JFaceResources.getDefaultFont();
+ }
+ dim.setSize(FigureUtilities.getTextExtents(text + " ", font));
+ }
+ return dim;
+ }
+
+ private Dimension calculateImageExtents() {
+ Dimension dim = new Dimension(0, 0);
+ Image image = getImage();
+ if (image != null) {
+ dim.setSize(new Dimension(image.getBounds().width + 4, image.getBounds().height));
+ }
+ return dim;
+ }
+
+ /**
+ * Gets the minimum size for this node. This is the minimum size of the label (text & icon)
+ * @return Dimension
+ */
+ public Dimension calculateMinimumSize() {
+ return calculateMinimumLabelSize();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getUnhiglightColor()
+ */
+ public Color getUnhiglightColor() {
+ return proxy.getUnhiglightColor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getBorderHighlightColor()
+ */
+ public Color getBorderHighlightColor() {
+ return proxy.getBorderHighlightColor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#getBorderUnhiglightColor()
+ */
+ public Color getBorderUnhiglightColor() {
+ return proxy.getBorderUnhiglightColor();
+ }
+
+ public boolean cacheLabel() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void setCacheLabel(boolean cacheLabel) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/ProxyConnection.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/ProxyConnection.java
new file mode 100644
index 0000000..3821fed
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/ProxyConnection.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphmodel;
+
+
+
+/**
+ * A model class that references a connection in the model in order to conseptually
+ * duplicate them, without actually duplicating them in the model. Useful for
+ * visual elements. Uses visual information from the proxy.
+ *
+ * Unlike proxy nodes, this connection does not forward to the proxy. It simply
+ * initializes its state based on the proxy node.
+ * @author Del Myers
+ *
+ */
+
+//@tag zest(bug(153466-NoNestedClientSupply(fix))) : will be used to create multiple visual connections.
+//@tag zest(bug(154259-Abstraction(fix)))
+public class ProxyConnection extends GraphModelConnection {
+ private IGraphModelConnection proxy;
+
+ /**
+ * Creates a new proxy connection with its visuals based on the given connection.
+ */
+ protected ProxyConnection(IGraphModelNode source, IGraphModelNode dest, IGraphModelConnection proxy) {
+ super(proxy.getGraphModel(), proxy.getExternalConnection(), source, dest);
+ this.proxy = proxy;
+ init();
+ }
+
+ private final void init() {
+ setConnectionStyle(proxy.getConnectionStyle());
+ setCurveDepth(proxy.getCurveDepth());
+ setEndAngle(proxy.getEndAngle());
+ setEndLength(proxy.getEndLength());
+ setFont(proxy.getFont());
+ setHighlightColor(proxy.getHighlightColor());
+ setImage(proxy.getImage());
+ setLineColor(proxy.getLineColor());
+ setLineStyle(proxy.getLineStyle());
+ setLineWidth(proxy.getLineWidth());
+ setStartAngle(proxy.getStartAngle());
+ setStartLength(proxy.getStartLength());
+ setText(proxy.getText());
+ }
+
+ public IGraphModelConnection getProxy() {
+ return proxy;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/INestedGraphModelFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/INestedGraphModelFactory.java
new file mode 100644
index 0000000..882b1ba
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/INestedGraphModelFactory.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel.nested;
+
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel;
+
+/**
+ * @author Ian Bull
+ * @deprecated
+ */
+public interface INestedGraphModelFactory {
+
+ /**
+ * Creates a new graph model
+ * @return
+ */
+ public abstract NestedGraphModel createModel();
+
+ /**
+ * Creates the model using the content provider. Uses the default given node style and connection style.
+ * @param inputElement
+ * @param nodeStyle
+ * @param connectionStyle
+ * @return
+ */
+ //@tag zest(bug(153348-NestedStyle(fix))) : add node style and connection style
+ // @tag zest(bug(154412-ClearStatic(fix))) : made more generic so that AbstractStylingModelFactory can do some processing in this method.
+ public abstract GraphModel createModelFromContentProvider(Object inputElement, int nodeStyle, int connectionStyle);
+
+ /**
+ * Creates a new relationship using the content provider to get the source and destination.
+ * @param model The graph model.
+ * @param data The data object for the new connection.
+ * @param contentProvider The content provider which will get the source and destination nodes.
+ * @return NestedGraphModelConnection
+ */
+ public abstract NestedGraphModelConnection createRelationship(NestedGraphModel model,
+ Object data);
+
+ /**
+ * Creates a new relationship and adds it to the model
+ * @param model
+ * @param source
+ * @param dest
+ * @return NestedGraphModelConnection
+ */
+ public abstract NestedGraphModelConnection createRelationship(NestedGraphModel model,
+ Object data, Object source, Object dest);
+
+ /**
+ * Creates a new node and adds it to the model. If the node already exists it is just returned.
+ * @param model The graph model
+ * @param data The new node's data object
+ * @return NestedGraphModelNode the new or existing node
+ */
+ public abstract NestedGraphModelNode createNode(NestedGraphModel model, Object data);
+
+}
\ No newline at end of file
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModel.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModel.java
new file mode 100644
index 0000000..7bd72b7
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModel.java
@@ -0,0 +1,330 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel.nested;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode;
+import org.eclipse.swt.widgets.Canvas;
+
+
+
+/**
+ * Extends {@link GraphModel} to provider support for nested graphs.
+ * Instead of holding a list of all the nodes, the model now only
+ * holds the root node, the current node and the previous node. All other
+ * nodes can be retrieved by traversing the children/parent hierarchy.
+ *
+ * @author Chris Callendar
+ */
+public class NestedGraphModel extends GraphModel {
+
+ private final int STACK_SIZE = 10;
+
+ private NestedGraphModelNode rootNode;
+ private NestedGraphModelNode currentNode;
+ private NestedGraphModelNode previousNode;
+
+ private LinkedList backStack;
+ private LinkedList forwardStack;
+
+ private Rectangle mainArea;
+
+// @tag zest(bug(152613-Client-Supplier(fix))) : allow the model to tell the view if clients/suppliers should be closed.
+ private boolean clientClosed;
+ private boolean supplierClosed;
+
+
+ /**
+ * Initializes the model with the given canvas.
+ * @param canvas
+ */
+ public NestedGraphModel(Canvas canvas) {
+ super(canvas);
+ this.backStack = new LinkedList();
+ this.forwardStack = new LinkedList();
+ this.mainArea = new Rectangle();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Widget#toString()
+ */
+ public String toString() {
+ return "NestedGraphModel {" +
+ (rootNode != null ? "Root: " + rootNode.getText() + ", ": "") +
+ (currentNode != null ? "Current: " + currentNode.getText() + ", " : "") +
+ connections.size() + " connections}";
+ }
+
+ /**
+ * @return the clientClosed
+ */
+ public boolean isClientClosed() {
+ return clientClosed;
+ }
+
+ /**
+ * @param clientClosed the clientClosed to set
+ */
+ public void setClientClosed(boolean clientClosed) {
+ this.clientClosed = clientClosed;
+ }
+
+ /**
+ * @return the supplierClosed
+ */
+ public boolean isSupplierClosed() {
+ return supplierClosed;
+ }
+
+ /**
+ * @param supplierClosed the supplierClosed to set
+ */
+ public void setSupplierClosed(boolean supplierClosed) {
+ this.supplierClosed = supplierClosed;
+ }
+
+ /* (non-Javadoc)
+ * @see ca.uvic.cs.zest.internal.graphmodel.GraphModel#removeNodeFromList(ca.uvic.cs.zest.internal.graphmodel.GraphModelNode)
+ */
+ protected boolean removeNodeFromList(IGraphModelNode node) {
+ return true;
+ }
+
+
+ /**
+ * Returns the children nodes under the current node.
+ * @return List of NestedGraphModelNode objects which are the children of the current node
+ */
+ public List getNodes() {
+ if ( currentNode != null ) {
+ ArrayList al = new ArrayList( );
+ al.add( currentNode );
+ return al;
+ }
+ else {
+ ArrayList al = new ArrayList();
+ al.add( rootNode );
+ return al;
+ }
+
+ }
+
+ /**
+ * Gets the root node for this model. If this model is of a flat graph
+ * then the root node will be null. Otherwise the root node will be returned.
+ * @return NestedGraphModelNode
+ */
+ public NestedGraphModelNode getRootNode() {
+ return rootNode;
+ }
+
+ /**
+ * Sets the root node for this model.
+ * @param rootNode
+ */
+ public void setRootNode(NestedGraphModelNode rootNode) {
+ this.rootNode = rootNode;
+ if (currentNode == null) {
+ setCurrentNode(rootNode);
+ }
+ }
+
+ /**
+ * Returns the current node.
+ * @return NestedGraphModelNode
+ */
+ public NestedGraphModelNode getCurrentNode() {
+ return currentNode;
+ }
+
+ /**
+ * Sets the current node. The previous node is added to the back stack.
+ * @param newNode the new current node (can't be null)
+ */
+ public void setCurrentNode(NestedGraphModelNode newNode) {
+ if (newNode == null)
+ return;
+
+ addToBackStack(currentNode);
+ setCurrent(newNode);
+ }
+
+ /**
+ * Sets the current node. The back and forward stacks are not touched.
+ * @param node
+ */
+ private void setCurrent(NestedGraphModelNode node) {
+ NestedGraphModelNode old = currentNode;
+ if (currentNode != null) {
+ previousNode = currentNode;
+ previousNode.setCurrent(false);
+ }
+ currentNode = node;
+ currentNode.setCurrent(true);
+ firePropertyChange(NODE_FOCUS_PROP, old, currentNode);
+ currentNode.setChildrenVisible(true);
+
+ }
+
+ /**
+ * Gets the previous root node for this graph model
+ * @return NestedGraphModelNode
+ */
+ public NestedGraphModelNode getPreviousRootNode() {
+ return previousNode;
+ }
+
+ // BACK/FORWARD/UP methods
+
+ /**
+ * Returns true if there are entries in the Back button stack.
+ * @return boolean
+ */
+ public boolean hasBackNode() {
+ return (backStack.size() > 0);
+ }
+
+ /**
+ * Returns true if there are entries in the Forward button stack.
+ * @return boolean
+ */
+ public boolean hasForwardNode() {
+ return (forwardStack.size() > 0);
+ }
+
+ /**
+ * Returns true if the current node has a parent (which isn't the root).
+ * @return boolean
+ */
+ public boolean hasParentNode() {
+ boolean hasParent = (currentNode != null) &&
+ (currentNode != rootNode) &&
+ (currentNode.getParent() != null);
+ return hasParent;
+ }
+
+ /**
+ * Goes back to the previous node.
+ */
+ public void goBack() {
+ if (hasBackNode()) {
+ addToForwardStack(currentNode);
+ NestedGraphModelNode node = popBackStack();
+ setCurrent(node);
+ }
+ }
+
+ public void goForward() {
+ if (hasForwardNode()) {
+ addToBackStack(currentNode);
+ NestedGraphModelNode node = popForwardStack();
+ setCurrent(node);
+ }
+ }
+
+ /**
+ * Sets the parent of the current node to the be the
+ * new current node.
+ */
+ public void goUp() {
+ if (hasParentNode()) {
+ addToBackStack(currentNode);
+ setCurrent(currentNode.getCastedParent());
+ }
+ }
+
+ /**
+ * Adds the given node to the front of the list (top of the stack).
+ * @param node
+ */
+ private void addToBackStack(NestedGraphModelNode node) {
+ if (node == null)
+ return;
+
+ // check to make sure that the top isn't the same as the new one
+ if (backStack.size() > 0) {
+ NestedGraphModelNode top = (NestedGraphModelNode)backStack.getFirst();
+ if (top == node)
+ return;
+ }
+ // if the stack is too big, remove the last node
+ if (backStack.size() >= STACK_SIZE) {
+ backStack.removeLast();
+ }
+ backStack.addFirst(node);
+ }
+
+ /**
+ * Adds the given node to the front of the forward stack.
+ * @param node
+ */
+ private void addToForwardStack(NestedGraphModelNode node) {
+ if (node == null)
+ return;
+
+ // check to make sure that the top isn't the same as the new one
+ if (forwardStack.size() > 0) {
+ NestedGraphModelNode top = (NestedGraphModelNode)forwardStack.getFirst();
+ if (top == node)
+ return;
+ }
+ // if the stack is too big, remove the bottom (last) node
+ if (forwardStack.size() >= STACK_SIZE) {
+ forwardStack.removeLast();
+ }
+ forwardStack.addFirst(node);
+ }
+
+ /**
+ * Removes the top node from the back stack.
+ * @return NestedGraphModelNode
+ */
+ private NestedGraphModelNode popBackStack() {
+ if (backStack.size() > 0) {
+ return (NestedGraphModelNode)backStack.removeFirst();
+ }
+ return null;
+ }
+
+ /**
+ * Removes the top node from the forward stack.
+ * @return NestedGraphModelNode
+ */
+ private NestedGraphModelNode popForwardStack() {
+ if (forwardStack.size() > 0) {
+ return (NestedGraphModelNode)forwardStack.removeFirst();
+ }
+ return null;
+ }
+
+ /**
+ * Sets the main (scrollable) area.
+ * @param mainArea
+ */
+ public void setMainArea(Rectangle area) {
+ this.mainArea.setBounds(area);
+ }
+
+ /**
+ * Returns a copy of the main (scrollable) area.
+ * @return Rectangle
+ */
+ public Rectangle getMainArea() {
+ return mainArea.getCopy();
+ }
+
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelConnection.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelConnection.java
new file mode 100644
index 0000000..014f29d
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelConnection.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel.nested;
+
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModelConnection;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode;
+
+
+/**
+ * Extends GraphModelConnection. The only real purpose is to change the colors
+ * and the line widths.
+ * @deprecated by Del Myers. This connection is no longer needed. Use regular GraphModelConnections.
+ * @author Chris Callendar
+ */
+public class NestedGraphModelConnection extends GraphModelConnection {
+
+ /**
+ * @param graphModel
+ * @param data
+ * @param source
+ * @param destination
+ */
+ public NestedGraphModelConnection(GraphModel graphModel, Object data, IGraphModelNode source,
+ IGraphModelNode destination) {
+ super(graphModel, data, source, destination);
+ }
+
+ /**
+ * @param graphModel
+ * @param data
+ * @param source
+ * @param destination
+ * @param bidirection
+ * @param weight
+ */
+ public NestedGraphModelConnection(GraphModel graphModel, Object data, IGraphModelNode source,
+ IGraphModelNode destination, boolean bidirection, double weight) {
+ super(graphModel, data, source, destination, bidirection, weight);
+ // TODO Auto-generated constructor stub
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.GraphModelConnection#getColorFromWeight()
+ *
+ protected Color getColorFromWeight() {
+ return ColorConstants.darkBlue;
+ }*/
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.GraphModelConnection#getLineWidthFromWeight()
+ *
+ protected int getLineWidthFromWeight() {
+ return 1;
+ }*/
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelEntityFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelEntityFactory.java
new file mode 100644
index 0000000..a1574d1
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelEntityFactory.java
@@ -0,0 +1,292 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel.nested;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.mylar.zest.core.internal.graphmodel.AbstractStylingModelFactory;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelConnection;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode;
+import org.eclipse.mylar.zest.core.messages.ZestUIMessages;
+import org.eclipse.mylar.zest.core.viewers.EntityConnectionData;
+import org.eclipse.mylar.zest.core.viewers.IGraphEntityContentProvider;
+import org.eclipse.mylar.zest.core.viewers.INestedGraphEntityContentProvider;
+import org.eclipse.swt.widgets.Canvas;
+
+
+/**
+ * A factory for creating nodes and relationships for nested graphs.
+ *
+ * @author Chris Callendar
+ */
+//@tag bug(151327-Styles(todo)) : Use GraphItemStyler to style the graphs that are created.
+//@tag bug.160367-Refreshing.fix : Updated to use the new AbstractStylingModelFactory
+public class NestedGraphModelEntityFactory extends AbstractStylingModelFactory {
+
+
+ public NestedGraphModelEntityFactory(StructuredViewer viewer) {
+ super(viewer);
+ }
+
+
+ private INestedGraphEntityContentProvider getCastedContent() {
+ return (INestedGraphEntityContentProvider)getContentProvider();
+ }
+
+ //@tag bug(153348-NestedStyle(fix)) : add nodestyle and connectionstyle so that the model can be updated.
+// @tag bug(154412-ClearStatic(fix)) : renamed to allow the parent to do some processing before the model is created.
+ public GraphModel createGraphModel() {
+ NestedGraphModel model = new NestedGraphModel((Canvas)getViewer().getControl());
+ doBuildGraph(model);
+ return model;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.AbstractStylingModelFactory#doBuildGraph(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel)
+ */
+ protected void doBuildGraph(GraphModel graph) {
+ NestedGraphModel model = (NestedGraphModel) graph;
+ clearGraph(model);
+ model.setConnectionStyle(getConnectionStyle());
+ model.setNodeStyle(getNodeStyle());
+
+ // add the root element
+ // TODO this maybe should be optional...
+ NestedGraphModelNode rootNode = new NestedGraphModelNode(model, null);
+ rootNode.setData(rootNode);
+ rootNode.setHighlightAdjacentNodes(true);
+ rootNode.setText(ZestUIMessages.VIEW_NESTED_TOP_NODE);
+ model.addNode(rootNode.getData(), rootNode);
+
+ rootNode.setSizeInLayout(-1, -1);
+
+ model.setRootNode(rootNode);
+
+ Object entities[] = getContentProvider().getElements(getViewer().getInput());
+ entities = filter(getViewer().getInput(), entities);
+ // add all root the entities and recursively add their children
+ if (entities != null) {
+ for ( int i = 0; i < entities.length; i++ ) {
+ Object data = entities[i];
+ NestedGraphModelNode node = (NestedGraphModelNode) createNode(model,data);
+ node.setParent(rootNode);
+ rootNode.addChild(node);
+ node.setSizeInLayout(100, 100);
+ addChildNodes(model, data);
+ }
+ NestedGraphModelNode node = model.getRootNode();
+ //relationships have to be created only after all nodes have been
+ //otherwise, we can't connect nested nodes.
+ recursiveCreateRelationships(node);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.AbstractStylingModelFactory#createNode(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object)
+ */
+ public IGraphModelNode createNode(GraphModel graph, Object element) {
+ NestedGraphModelNode node = getNode((NestedGraphModel)graph, element);
+ if (node == null) {
+ node = new NestedGraphModelNode((NestedGraphModel)graph,element);
+ // add the parent
+ Object parent = getCastedContent().getParent(element);
+ if (parent != null) {
+ NestedGraphModelNode parentNode = getNode((NestedGraphModel)graph, parent);
+ if (parentNode != null) {
+ node.setParent(parentNode);
+ parentNode.addChild(node);
+ }
+ }
+ // add it to the model (must be done after adding the parent!)
+ graph.addNode(element, node);
+ }
+ styleItem(node);
+ return node;
+ }
+
+ /**
+ * @param node
+ */
+ private void recursiveCreateRelationships(NestedGraphModelNode node) {
+ //add the sibling, children and parent relationships
+ Object data = node.getData();
+ NestedGraphModel model = node.getNestedGraphModel();
+ Object[] related = getCastedContent().getConnectedTo(data);
+ if ( related != null ) {
+ for ( int i = 0; i < related.length; i++ ) {
+ createConnection(model, new EntityConnectionData(data, related[i]), data, related[i]);
+ }
+ }
+ for (Iterator it = node.getChildren().iterator(); it.hasNext();) {
+ recursiveCreateRelationships((NestedGraphModelNode) it.next());
+ }
+ }
+
+ /**
+ * Recusively adds the children nodes for the given data object. The children
+ * are gotten from the content provider.
+ * @param model
+ * @param data
+ */
+ private void addChildNodes(NestedGraphModel model, Object data) {
+ Object[] childrenData = getCastedContent().getChildren(data);
+ childrenData = filter(data, childrenData);
+ if (childrenData != null) {
+ for (int j = 0; j < childrenData.length; j++) {
+ Object childData = childrenData[j];
+ NestedGraphModelNode node = (NestedGraphModelNode) createNode(model, childData);
+ node.setSizeInLayout(100, 100);
+// @tag bug(153348-NestedStyle(fix))
+ addChildNodes(model, childData);
+ }
+ }
+ }
+
+
+
+ private NestedGraphModelNode getNode(NestedGraphModel model, Object data) {
+ NestedGraphModelNode node = (NestedGraphModelNode)model.getInternalNode(data);
+ return node;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory#refresh(org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel, java.lang.Object, boolean)
+ */
+ public void refresh(GraphModel graph, Object element, boolean updateLabels) {
+ NestedGraphModel model = (NestedGraphModel) graph;
+ NestedGraphModelNode node = (NestedGraphModelNode) model.getInternalNode(element);
+ if (node == null) {
+ IGraphModelConnection conn = model.getInternalConnection(element);
+ if (conn != null) {
+ refresh(graph, conn.getSource().getExternalNode(), updateLabels);
+ refresh(graph, conn.getDestination().getExternalNode(), updateLabels);
+ }
+ return;
+ }
+
+
+ Map nodesMap = graph.getNodesMap();
+// remove all of the child nodes.
+ clearChildren(node);
+ addChildNodes(model, element);
+ reconnect(node, updateLabels);
+ //update the positions on the new nodes to match the old ones.
+ List nodes = getAllChildren(node);
+
+ for (Iterator i = nodes.iterator(); i.hasNext();) {
+ IGraphModelNode next = (IGraphModelNode) i.next();
+ IGraphModelNode oldNode = (IGraphModelNode) nodesMap.get(next.getExternalNode());
+ if (oldNode != null) {
+ next.setPreferredLocation(oldNode.getXInLayout(), oldNode.getYInLayout());
+ }
+ reconnect(next, updateLabels);
+ }
+
+ }
+
+ /**
+ * @param graph
+ * @param element
+ * @param refreshLabels
+ */
+ private void reconnect(IGraphModelNode node, boolean refreshLabels) {
+ GraphModel graph = node.getGraphModel();
+ Object element = node.getExternalNode();
+ Object[] related = ((IGraphEntityContentProvider)getContentProvider()).getConnectedTo(element);
+ List connections = node.getSourceConnections();
+ LinkedList toAdd = new LinkedList();
+ LinkedList toDelete = new LinkedList();
+ LinkedList toKeep = new LinkedList();
+ HashSet oldExternalConnections = new HashSet();
+ HashSet newExternalConnections = new HashSet();
+ for (Iterator it = connections.iterator(); it.hasNext();) {
+ oldExternalConnections.add(((IGraphModelConnection)it.next()).getExternalConnection());
+ }
+ for (int i = 0; i < related.length; i++) {
+ newExternalConnections.add(new EntityConnectionData(element, related[i]));
+ }
+ for (Iterator it = oldExternalConnections.iterator(); it.hasNext();) {
+ Object next = it.next();
+ if (!newExternalConnections.contains(next)) {
+ toDelete.add(next);
+ } else {
+ toKeep.add(next);
+ }
+ }
+ for (Iterator it = newExternalConnections.iterator(); it.hasNext();) {
+ Object next = it.next();
+ if (!oldExternalConnections.contains(next)) {
+ toAdd.add(next);
+ }
+ }
+ for (Iterator it = toDelete.iterator(); it.hasNext();) {
+ graph.removeConnection(it.next());
+ }
+ toDelete.clear();
+ LinkedList newNodeList = new LinkedList();
+ for (Iterator it = toAdd.iterator(); it.hasNext();) {
+ EntityConnectionData data = (EntityConnectionData) it.next();
+ IGraphModelNode dest = graph.getInternalNode(data.dest);
+ if (dest == null) {
+ newNodeList.add(data.dest);
+ }
+ createConnection(graph, data, data.source, data.dest);
+ }
+ toAdd.clear();
+ if (refreshLabels) {
+ for (Iterator i = toKeep.iterator(); i.hasNext();) {
+ styleItem(graph.getInternalConnection(i.next()));
+ }
+ }
+ for (Iterator it = newNodeList.iterator(); it.hasNext();) {
+ //refresh the new nodes so that we get a fully-up-to-date graph.
+ refresh(graph, it.next());
+ }
+ }
+
+
+
+ /**
+ * @param node
+ * @return
+ */
+ private List getAllChildren(NestedGraphModelNode node) {
+ LinkedList nodes = new LinkedList();
+ nodes.addAll(node.getChildren());
+ for (Iterator it = node.getChildren().iterator(); it.hasNext();) {
+ nodes.addAll(getAllChildren((NestedGraphModelNode) it.next()));
+ }
+ return nodes;
+ }
+
+
+ /**
+ * @param node
+ */
+ private void clearChildren(NestedGraphModelNode node) {
+ List children = node.getChildren();
+ for (Iterator it = children.iterator(); it.hasNext();) {
+ NestedGraphModelNode child = (NestedGraphModelNode) it.next();
+ clearChildren(child);
+ }
+ node.setParent(null);
+ node.getParent().getChildren().remove(node);
+ node.getGraphModel().removeNode(node);
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelNode.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelNode.java
new file mode 100644
index 0000000..e9dfaf4
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelNode.java
@@ -0,0 +1,517 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel.nested;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModelNode;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelConnection;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode;
+import org.eclipse.mylar.zest.layouts.NestedLayoutEntity;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * Extends GraphModelNode to add methods that deal with nested graphs.
+ *
+ * @author Chris Callendar
+ * @author Ian Bull
+ */
+public class NestedGraphModelNode extends GraphModelNode implements NestedLayoutEntity {
+
+ /*
+ * Tree Constants
+ */
+ public static final int SAME_NODE = 0;
+ public static final int DESCENDANT = 1;
+ public static final int ANCESTOR = 2;
+ public static final int NO_RELATION = 3;
+ public static final int PLUS_SIZE = 16;
+ private NestedGraphModelNode parent;
+ private List children;
+ private int depth; // the depth of this node. Root nodes (null parent) are
+ // at a depth 0
+ private double widthScale;
+ private double heightScale;
+ private boolean isCurrent;
+ private boolean childrenVisible;
+ private Rectangle childrenBounds;
+ private Dimension minimizedSize;
+ private Dimension fullSize;
+
+ public NestedGraphModelNode(NestedGraphModel graphModel, Object externalNode) {
+ super(graphModel, externalNode);
+ }
+
+ public NestedGraphModelNode(NestedGraphModel graphModel, String label, Object externalNode) {
+ super(graphModel, label, externalNode);
+ }
+
+ public NestedGraphModelNode(NestedGraphModel graphModel, Image img, Object externalNode) {
+ super(graphModel, img, externalNode);
+ }
+
+ public NestedGraphModelNode(NestedGraphModel graphModel, String label, Image img, Object externalNode) {
+ super(graphModel, label, img, externalNode);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ca.uvic.cs.zest.internal.graphmodel.GraphModelNode#initModel(java.lang.Object)
+ */
+ protected void initModel(GraphModel graphModel, Object externalNode) {
+ super.initModel(graphModel, externalNode);
+ this.parent = null;
+ this.children = new ArrayList();
+ this.depth = 0;
+ this.widthScale = 1.0;
+ this.heightScale = 1.0;
+ this.isCurrent = false;
+ this.childrenVisible = false;
+ }
+
+ public String toString() {
+ // " {Parent: " + getParent().getText() + "}"
+ return "NestedGraphModelNode: " + getText() + (" {" + getChildren().size() + " children}");
+ }
+
+ /**
+ * Gets the relationship between two given nodes
+ * SAME_NODE - Same
+ * DESCENDANT - node is a child of this
+ * ANCESTOR - node is a parent of this
+ * NO_RELATION - node is not directly related
+ * @param that the node to compare with this
+ * @return int
+ * @see #SAME_NODE
+ * @see #ANCESTOR
+ * @see #DESCENDANT
+ * @see #NO_RELATION
+ */
+ public int getRelationshipBetweenNodes(NestedGraphModelNode that) {
+ if (that == this)
+ return SAME_NODE;
+ if (this.isAncestorOf(that))
+ return ANCESTOR;
+ else if (this.isDescendantOf(that))
+ return DESCENDANT;
+ else
+ return NO_RELATION;
+ }
+
+ private boolean isAncestorOf(NestedGraphModelNode that) {
+ NestedLayoutEntity parent = that.getParent();
+ while (parent != null && parent != this) {
+ parent = parent.getParent();
+ }
+ if (parent == this)
+ return true;
+ else
+ return false;
+ }
+
+ private boolean isDescendantOf(NestedGraphModelNode that) {
+ return that.isAncestorOf(this);
+ }
+
+ /**
+ * Returns the parent (or null if it is a root node).
+ *
+ * @return NestedGraphModelNode
+ */
+ public NestedLayoutEntity getParent() {
+ return parent;
+ }
+
+ /**
+ * Gets the parent casted to a NestedGraphModelNode.
+ *
+ * @return NestedGraphModelNode
+ */
+ public NestedGraphModelNode getCastedParent() {
+ if (parent instanceof NestedGraphModelNode) {
+ return (NestedGraphModelNode) parent;
+ }
+ return null;
+ }
+
+ /**
+ * Sets the parent for this node. This also sets the depth of nesting of the
+ * node by traversing up the parent hierarchy counting the levels.
+ *
+ * @param parent
+ * the parent to set
+ */
+ public void setParent(NestedGraphModelNode parent) {
+ this.parent = parent;
+ this.depth = 0;
+ for (NestedLayoutEntity node = parent; node != null; node = node.getParent()) {
+ this.depth++;
+ }
+ }
+
+ public boolean isCurrent() {
+ return isCurrent;
+ }
+
+ public void setCurrent(boolean isCurrent) {
+ this.isCurrent = isCurrent;
+ }
+
+ /**
+ * Returns the nested height of this node. Root nodes whose parent is null
+ * will be at a depth of 0.
+ *
+ * @return int the nested height
+ */
+ public int getNestedDepth() {
+ return depth;
+ }
+
+ /**
+ * Returns the list of the NestedGraphModelNode children nodes.
+ *
+ * @return List of NestedGraphModelNode objects
+ */
+ public List getChildren() {
+ return children;
+ }
+
+ public boolean hasChildren() {
+ return (children.size() > 0);
+ }
+
+ /**
+ * Adds the child node if it isn't in the parent hierarchy of this node and
+ * if it doesn't already exist in the list.
+ *
+ * @param child
+ */
+ public void addChild(NestedGraphModelNode child) {
+ if (child != null) {
+ boolean add = true;
+ for (NestedLayoutEntity node = this; node != null; node = node.getParent()) {
+ if (node.equals(child)) {
+ add = false;
+ break;
+ }
+ }
+ if (add && !children.contains(child)) {
+ children.add(child);
+ }
+ childrenBounds = null; // reset the size - will be calculated again
+ }
+ }
+
+ /**
+ * Removes the given node from the list of children.
+ *
+ * @param nodeToRemove
+ * @return boolean if it was removed
+ */
+ public boolean removeChild(IGraphModelNode nodeToRemove) {
+ boolean removed = false;
+ if (nodeToRemove != null) {
+ removed = children.remove(nodeToRemove);
+ }
+ if (removed) {
+ childrenBounds = null; // reset the size - will be calculated again
+ }
+ return removed;
+ }
+
+ /**
+ * Returns true if the children are visible.
+ *
+ * @return boolean
+ */
+ public boolean getChildrenVisible() {
+ return childrenVisible;
+ }
+
+ /**
+ * Sets if the children are visible and adjusts the size appropriately.
+ *
+ * @param visible
+ */
+ public void setChildrenVisible(boolean visible) {
+ if (visible) {
+ showChildren();
+ } else {
+ hideChildren();
+ }
+ }
+
+ /**
+ * Hides the children and collapses the node to just the label.
+ */
+ public void hideChildren() {
+ childrenVisible = false;
+ Dimension size = getFullSize();
+ super.setSizeInLayout(size.width, size.height);
+ this.firePropertyChange(FORCE_REDRAW, null, null);
+ }
+
+ /**
+ * Displays the children and expands the node.
+ */
+ public void showChildren() {
+ childrenVisible = true;
+ Dimension size = getFullSize();
+ super.setSizeInLayout(size.width, size.height);
+ this.firePropertyChange(FORCE_REDRAW, null, null);
+ }
+
+ /**
+ * Gets the full size of the node without scaling and with children shown.
+ *
+ * @return Dimension
+ */
+ public Dimension getFullSize() {
+ if (fullSize == null) {
+ fullSize = calculateMinimumSize();
+ }
+ return fullSize.getCopy();
+ }
+
+ /**
+ * Gets the size of just the plus/minus and icon and text.
+ *
+ * @return Dimension
+ */
+ public Dimension getMinimizedSize() {
+ if (minimizedSize == null) {
+ minimizedSize = calculateMinimumLabelSize();
+ minimizedSize.width = Math.max(minimizedSize.width, getFullSize().width);
+ }
+ return minimizedSize.getCopy();
+ }
+
+ /**
+ * Sets the size of the node. Do not use this method if you want to minize
+ * the node (label only). Call hideChildren() instead. You can also call
+ * showChildren() to show the full size.
+ *
+ * @see org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode#setSizeInLayout(double,
+ * double)
+ */
+ public void setSizeInLayout(double width, double height) {
+ fullSize = new Dimension((int) width, (int) height);
+ super.setSizeInLayout(width, height);
+ }
+
+ /**
+ * Currently this always returns 1
+ *
+ * @return
+ */
+ // public double getScale() {
+ // return (isCurrent() ? 1 : scale);
+ // }
+
+ public double getWidthScale() {
+ return widthScale;
+ }
+
+ public double getHeightScale() {
+ return heightScale;
+ }
+
+ public void setScale(double w, double h) {
+ this.widthScale = w;
+ this.heightScale = h;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see ca.uvic.cs.zest.internal.graphmodel.GraphModelNode#calculateMinimumLabelSize()
+ */
+ public Dimension calculateMinimumLabelSize() {
+ if (labelSize == null) {
+ labelSize = super.calculateMinimumLabelSize();
+ if (hasChildren()) {
+ labelSize.expand(PLUS_SIZE + 4, Math.max(PLUS_SIZE - labelSize.height, 0));
+ }
+ }
+ return labelSize;
+ }
+
+ /**
+ * Calculates the minimum size of this node without taking scaling into
+ * account. This is the full size of the label and the children.
+ *
+ * @see ca.uvic.cs.zest.internal.graphmodel.IGraphModelNode#calculateMinimumSize()
+ */
+ public Dimension calculateMinimumSize() {
+ Dimension labelSize = calculateMinimumLabelSize();
+ Rectangle childSize = calculateMinimumChildrenBounds();
+ int width = childSize.width;
+ int height = childSize.height;
+ width = Math.max(width, labelSize.width);
+ height += labelSize.height;
+ return new Dimension(width, height);
+ }
+
+ /**
+ * Gets the minimum size for the children.
+ *
+ * @return Dimension
+ */
+ public Rectangle calculateMinimumChildrenBounds() {
+ if (childrenBounds == null) {
+ childrenBounds = new Rectangle();
+ for (Iterator iter = getChildren().iterator(); iter.hasNext();) {
+ IGraphModelNode node = (IGraphModelNode) iter.next();
+ double x = node.getXInLayout();
+ double y = node.getYInLayout();
+ if (node instanceof GraphModelNode) {
+ ((GraphModelNode)node).calculateMinimumLabelSize();
+ }
+ double width = x + Math.max(node.getWidthInLayout(), labelSize.width);
+ double height = y + Math.max(node.getHeightInLayout(), labelSize.height);
+ childrenBounds.x = (int) Math.min(childrenBounds.x, x);
+ childrenBounds.y = (int) Math.min(childrenBounds.y, y);
+ childrenBounds.width = (int) Math.max(childrenBounds.width, width);
+ childrenBounds.height = (int) Math.max(childrenBounds.height, height);
+ }
+ }
+ return childrenBounds;
+ }
+
+ public NestedGraphModel getNestedGraphModel() {
+ return (NestedGraphModel) this.getGraphModel();
+ }
+
+ /**
+ * Finds all the nodes that are connected to this node or its children.
+ * @return a list containing NestedGraphModelNodes.
+ */
+// @tag zest(bug(150585-TopArcs(fix)))
+ public List getNodesConnectedTo() {
+ List connections = getConnectionsTo();
+ //@tag zest(bug(unreported(fix))) : there should be only one copy of each node.
+ HashSet targets = new HashSet();
+ for (Iterator i = connections.iterator(); i.hasNext();) {
+ targets.add(((IGraphModelConnection)i.next()).getDestination());
+ }
+ return Arrays.asList(targets.toArray());
+ }
+
+ public List getConnectionsTo() {
+ return recursiveGetConnectionsTo(this, this);
+ }
+ /**
+ * Recursively finds the nodes that are connected to the currentNode and its children.
+ * @param currentNode the current node to check.
+ * @param topNode the top node that all nodes are guaranteed to be children of.
+ * @return the list of nodes.
+ */
+ private List recursiveGetConnectionsTo(NestedGraphModelNode currentNode, NestedGraphModelNode topNode) {
+ List connectedTo = new ArrayList();
+ List sourceConnections = currentNode.getSourceConnections();
+ for (Iterator i = sourceConnections.iterator(); i.hasNext();) {
+ IGraphModelConnection c = (IGraphModelConnection) i.next();
+ //@tag zest(bug(unreported(fix))) : it is possible for a nested node to be connected to a non nested node.
+ if (!(c.getDestination() instanceof NestedGraphModelNode)) continue;
+ NestedGraphModelNode target = (NestedGraphModelNode) c.getDestination();
+ if (topNode.getRelationshipBetweenNodes(target) != NestedGraphModelNode.ANCESTOR)
+ connectedTo.add(c);
+ }
+ for (Iterator i = currentNode.getChildren().iterator(); i.hasNext();) {
+ connectedTo.addAll(recursiveGetConnectionsTo((NestedGraphModelNode) i.next(), topNode));
+ }
+ return connectedTo;
+ }
+
+
+ /**
+ * Finds all the nodes that are connected from this node or its children.
+ * @return a list containing NestedGraphModelNodes.
+ */
+ //@tag zest(bug(150585-TopArcs(fix)))
+ public List getNodesConnectedFrom() {
+ List connections = getConnectionsFrom();
+ //@tag zest(bug(unreported(fix))) : there should only be one copy of each node in the list.
+ HashSet sources = new HashSet();
+ for (Iterator i = connections.iterator(); i.hasNext();) {
+ sources.add(((IGraphModelConnection)i.next()).getSource());
+ }
+ return Arrays.asList(sources.toArray());
+ }
+
+ public List getConnectionsFrom() {
+ return recursiveGetConnectionsFrom(this, this);
+ }
+
+
+ /**
+ * Recursively finds all nodes that are connected "from" the currentNode and its children.
+ * @param currentNode the current node to check.
+ * @param topNode the top node that all nodes are guaranteed to be children of.
+ * @return the list of nodes.
+ */
+ private List recursiveGetConnectionsFrom(NestedGraphModelNode currentNode, NestedGraphModelNode topNode) {
+ List connectedFrom = new ArrayList();
+ List targetConnections = currentNode.getTargetConnections();
+ for (Iterator i = targetConnections.iterator(); i.hasNext();) {
+ IGraphModelConnection c = (IGraphModelConnection) i.next();
+ //@tag zest(bug(unreported(fix))) : it is possible for nested nodes to be connected to non nested nodes.
+ if (!(c.getSource() instanceof NestedGraphModelNode)) continue;
+ NestedGraphModelNode source = (NestedGraphModelNode) c.getSource();
+ if (topNode.getRelationshipBetweenNodes(source) != NestedGraphModelNode.ANCESTOR)
+ connectedFrom.add(c);
+ }
+ for (Iterator i = currentNode.getChildren().iterator(); i.hasNext();) {
+ connectedFrom.addAll(recursiveGetConnectionsFrom((NestedGraphModelNode) i.next(), topNode));
+ }
+ return connectedFrom;
+ }
+
+ /**
+ * Finds the highest common parent between this node and that node.
+ * @param that the node to check against.
+ * @return the highest common parent between the nodes, or null if none.
+ */
+ public NestedGraphModelNode findCommonParent(NestedGraphModelNode that) {
+ if (this == that) return this.getCastedParent();
+
+ LinkedList thisParentQueue = new LinkedList();
+ LinkedList thatParentQueue = new LinkedList();
+ NestedGraphModelNode node = this;
+ while (node != null) {
+ thisParentQueue.addFirst(node);
+ node = node.getCastedParent();
+ }
+ node = that;
+ while (node != null) {
+ thatParentQueue.addFirst(node);
+ node = node.getCastedParent();
+ }
+ int limit = Math.min(thisParentQueue.size(), thatParentQueue.size());
+ int i;
+ for (i = 0; i < limit; i++) {
+ if (thisParentQueue.get(i) != thatParentQueue.get(i)) break;
+ }
+ if (i >= limit) return null; //shouldn't happen.
+ return (NestedGraphModelNode) thisParentQueue.get(i-1);
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelRootNode.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelRootNode.java
new file mode 100644
index 0000000..ad5a648
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedGraphModelRootNode.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel.nested;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * The root model node.
+ *
+ * @author Chris Callendar
+ */
+public class NestedGraphModelRootNode extends NestedGraphModelNode {
+
+ private static final Image IMG_HOME = ImageDescriptor.createFromFile(NestedGraphModelRootNode.class, "/icons/home.gif").createImage();
+
+ /**
+ * Creates a new root node with the text "Root" and the home icon.
+ * The data is set to be null.
+ * @param model the model
+ */
+ public NestedGraphModelRootNode(NestedGraphModel model) {
+ super(model, "Root", IMG_HOME, null);
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedPane.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedPane.java
new file mode 100644
index 0000000..f86fa10
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NestedPane.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel.nested;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelConnection;
+import org.eclipse.mylar.zest.core.internal.graphmodel.NonNestedProxyNode;
+import org.eclipse.mylar.zest.core.internal.graphmodel.ProxyConnection;
+
+
+/**
+ * @author Ian bull
+ */
+//@tag bug(152613-Client-Supplier(fix))
+public class NestedPane {
+
+ public static final int SUPPLIER_PANE = 0;
+ public static final int MAIN_PANE = 1;
+ public static final int CLIENT_PANE = 2;
+ private boolean closedState = false;
+
+ private NestedGraphModel nestedGraphModel = null;
+ private int paneType = 0;
+ //@tag bug(153466-NoNestedClientSupply(fix)) : must cache the children in order to avoid making extra proxies.
+ private List children = null;
+ public NestedPane( int paneType ) {
+ this.paneType = paneType;
+ children = new ArrayList();
+
+ }
+
+ public void setModel( NestedGraphModel model ) {
+// @tag bug(152613-Client-Supplier(fix)) : set the initial closed state based on the model.
+ this.nestedGraphModel = model;
+ if (model == null) return;
+ switch(paneType) {
+ case CLIENT_PANE:
+ closedState = model.isClientClosed();
+ this.children = createProxies(nestedGraphModel.getCurrentNode().getConnectionsTo(), true);
+ break;
+ case SUPPLIER_PANE:
+ closedState = model.isSupplierClosed();
+ this.children = createProxies(nestedGraphModel.getCurrentNode().getConnectionsFrom(), false);
+ break;
+ case MAIN_PANE:
+ this.children = Arrays.asList(new Object[] {nestedGraphModel.getCurrentNode()});
+ break;
+ default:
+ this.children = Collections.EMPTY_LIST;
+ closedState = false;
+ }
+ }
+
+ public NestedGraphModel getModel() {
+ return this.nestedGraphModel;
+ }
+
+ public int getPaneType() {
+ return this.paneType;
+ }
+
+ /**
+ * Refreshes the children from the proxies listed in the model.
+ *
+ */
+ public void refreshChildren() {
+ List proxies = nestedGraphModel.getProxyConnections();
+ NestedGraphModelNode topNode = nestedGraphModel.getCurrentNode();
+ List connections;
+ boolean to = false;
+ switch (getPaneType()) {
+ case CLIENT_PANE :
+ connections = topNode.getConnectionsTo();
+ to = true;
+ break;
+ case SUPPLIER_PANE:
+ connections = topNode.getConnectionsFrom();
+ break;
+ case MAIN_PANE:
+ this.children = Arrays.asList(new Object[] {nestedGraphModel.getCurrentNode()});
+ default: return;
+ }
+ this.children = new ArrayList();
+ Iterator i = proxies.iterator();
+ while (i.hasNext()) {
+ ProxyConnection conn = (ProxyConnection) i.next();
+ IGraphModelConnection referenced = conn.getProxy();
+ Object end = (to) ? conn.getDestination() : conn.getSource();
+ if (end instanceof NonNestedProxyNode) {
+ if (connections.contains(referenced) && ! this.children.contains(end)) {
+ this.children.add(end);
+ }
+ }
+ }
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ /**
+ * Creates proxy nodes for the given list of connections. Visible proxy connections
+ * are also created and added to the nodes.
+ * @param connections the list of connections.
+ * @return The list of proxy nodes and connections
+ */
+ private List createProxies(List connections, boolean to) {
+ LinkedList proxies = new LinkedList();
+ HashMap modelNodes = new HashMap();
+ for (Iterator i = connections.iterator(); i.hasNext();) {
+ IGraphModelConnection conn = (IGraphModelConnection) i.next();
+ NestedGraphModelNode node = null;
+ if (to) {
+ node = (NestedGraphModelNode) conn.getDestination();
+ } else {
+ node = (NestedGraphModelNode) conn.getSource();
+ }
+ NonNestedProxyNode proxy = (NonNestedProxyNode) modelNodes.get(node);
+ if (proxy == null) {
+ proxy = node.getGraphModel().createProxyNode(node);
+ modelNodes.put(node,proxy);
+ proxies.add(proxy);
+ }
+ if (!to) {
+ node.getGraphModel().createProxyConnection(proxy, conn.getDestination(), conn);
+ } else {
+ node.getGraphModel().createProxyConnection(conn.getSource(), proxy, conn);
+ }
+ }
+ return proxies;
+ }
+
+ /**
+ * Removes a proxy node from the list of children.
+ */
+ public void removeProxy(NonNestedProxyNode proxy) {
+ this.children.remove(proxy);
+ }
+
+ /**
+ * @return true iff the state of this panel is closed.
+ */
+ public boolean isClosed() {
+ return closedState;
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NodeChildrenComparator.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NodeChildrenComparator.java
new file mode 100644
index 0000000..fb0f41d
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphmodel/nested/NodeChildrenComparator.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphmodel.nested;
+
+import java.util.Comparator;
+
+
+/**
+ * Compares NestedGraphModelNode objects based on how many children a node has.
+ * Nodes with the most children will be first and nodes with no children will be last.
+ * It also sorts nodes with the same number of children alphabetically.
+ *
+ * @author Chris Callendar
+ */
+public class NodeChildrenComparator implements Comparator {
+
+ public NodeChildrenComparator() {
+ super();
+ }
+
+ /**
+ * Compares two NestedGraphModelNode objects based on the number of
+ * children each one has. The node with more children will be first.
+ * If the nodes have the same number of children then the nodes are sorted
+ * by name (getText()).
+ * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
+ */
+ public int compare(Object o1, Object o2) {
+ if ((o1 instanceof NestedGraphModelNode) && (o2 instanceof NestedGraphModelNode)) {
+ return compare((NestedGraphModelNode)o1, (NestedGraphModelNode)o2);
+ }
+ return 0;
+ }
+
+ private int compare(NestedGraphModelNode node1, NestedGraphModelNode node2) {
+ int rv = 0;
+ int c1 = node1.getChildren().size();
+ int c2 = node2.getChildren().size();
+ if (c1 != c2) {
+ rv = c2 - c1; // put the node with more children first
+ } else {
+ String t1 = node1.getText();
+ String t2 = node2.getText();
+ if (t1 != null) {
+ rv = t1.compareTo(t2);
+ }
+ }
+ return rv;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/SpringGraphViewerImpl.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/SpringGraphViewerImpl.java
new file mode 100644
index 0000000..8d6bf8e
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/SpringGraphViewerImpl.java
@@ -0,0 +1,550 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphviewer;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.KeyHandler;
+import org.eclipse.gef.KeyStroke;
+import org.eclipse.gef.MouseWheelHandler;
+import org.eclipse.gef.MouseWheelZoomHandler;
+import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
+import org.eclipse.gef.editparts.LayerManager;
+import org.eclipse.gef.editparts.ZoomListener;
+import org.eclipse.gef.editparts.ZoomManager;
+import org.eclipse.gef.ui.actions.ZoomInAction;
+import org.eclipse.gef.ui.actions.ZoomOutAction;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+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.mylar.zest.core.ZestStyles;
+import org.eclipse.mylar.zest.core.internal.gefx.GraphRootEditPart;
+import org.eclipse.mylar.zest.core.internal.gefx.IPanningListener;
+import org.eclipse.mylar.zest.core.internal.gefx.ThreadedGraphicalViewer;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelConnection;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IStylingGraphModelFactory;
+import org.eclipse.mylar.zest.core.internal.graphviewer.parts.GraphEditPartFactory;
+import org.eclipse.mylar.zest.core.internal.graphviewer.parts.GraphNodeEditPart;
+import org.eclipse.mylar.zest.core.viewers.IGraphContentProvider;
+import org.eclipse.mylar.zest.core.viewers.SpringGraphViewer;
+import org.eclipse.mylar.zest.layouts.LayoutAlgorithm;
+import org.eclipse.mylar.zest.layouts.LayoutStyles;
+import org.eclipse.mylar.zest.layouts.Stoppable;
+import org.eclipse.mylar.zest.layouts.algorithms.AbstractLayoutAlgorithm;
+import org.eclipse.mylar.zest.layouts.algorithms.FadeLayoutAlgorithm;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Widget;
+
+
+/**
+ * Simple GEF viewer used to display graphical layouts.
+ *
+ * @author Ian Bull
+ * @author Chris Callendar
+ */
+public class SpringGraphViewerImpl extends ThreadedGraphicalViewer implements IPanningListener, ZoomListener {
+
+ private AbstractLayoutAlgorithm layoutAlgorithm = null;
+ private Stoppable layoutThread = null;
+ private GraphModel model = null;
+ private IStylingGraphModelFactory modelFactory = null;
+
+ /** Indicates if marquee (multiple) selection of nodes is allowed. */
+ private boolean allowMarqueeSelection;
+
+ /* irbull: These were the force directed settings
+ private boolean noOverlappingNodes = false;
+ private boolean stabilize = false;
+ private boolean enforceBounds = false;
+ private boolean directedGraph = false;
+ */
+ private int style = ZestStyles.NONE;
+
+ /**
+ * Indicates if panning (moving the canvas essentially) is allowed. This
+ * can only be allowed if marquee selection is not allowed.
+ */
+ private boolean allowPanning;
+
+ /** If the layout algorithm was running before panning started. */
+ private boolean wasRunningBeforePanning = false;
+
+
+ /**
+ * SpringGraphViewerImpl constructor.
+ * @param composite the composite object
+ * @param style The style for this viewer.
+ * Note marquee selection can only be enabled if panning is not enabled.
+ * @see ZestStyles#PANNING
+ * @see ZestStyles#MARQUEE_SELECTION
+ * @see ZestStyles#NO_OVERLAPPING_NODES
+ * @see ZestStyles#STABILIZE
+ * @see ZestStyles#ENFORCE_BOUNDS
+ * @see ZestStyles#DIRECTED_GRAPH
+ */
+ public SpringGraphViewerImpl(Composite composite, int style) {
+ super(composite);
+ this.setStyle( style );
+ this.addSelectionChangedListener( new _selectionChangedListener() );
+ }
+
+ /**
+ * Sets the style on the SpringGraphViewer
+ * @param style The style for this viewer.
+ * Note marquee selection can only be enabled if panning is not enabled.
+ * @see ZestStyles#PANNING
+ * @see ZestStyles#MARQUEE_SELECTION
+ * @see ZestStyles#NO_OVERLAPPING_NODES
+ * @see ZestStyles#STABILIZE
+ * @see ZestStyles#ENFORCE_BOUNDS
+ * @see ZestStyles#DIRECTED_GRAPH
+ */
+ public void setStyle( int style ) {
+ this.style = style;
+ /* irbull: These were the Force Directed Settings
+ this.noOverlappingNodes = ZestStyles.checkStyle(style, ZestStyles.NO_OVERLAPPING_NODES);
+ this.stabilize = ZestStyles.checkStyle(style, ZestStyles.STABILIZE);
+ this.enforceBounds = ZestStyles.checkStyle(style, ZestStyles.ENFORCE_BOUNDS);
+ */
+ this.allowPanning = ZestStyles.checkStyle(style, ZestStyles.PANNING);
+ this.allowMarqueeSelection = !allowPanning && ZestStyles.checkStyle(style, ZestStyles.MARQUEE_SELECTION);
+ }
+
+
+ /**
+ * Gets the style on the SpringGraphViewer
+ * @return
+ */
+ public int getStyle( ) {
+ return this.style;
+ }
+
+ /**
+ * Sets the model and initializes the layout algorithm.
+ * @see org.eclipse.mylar.zest.core.internal.gefx.ThreadedGraphicalViewer#setContents(java.lang.Object)
+ */
+ public void setContents(GraphModel model, IStylingGraphModelFactory modelFactory) {
+ super.setContents(model);
+ this.model = model;
+ this.modelFactory = modelFactory;
+
+ Dimension d = this.getCanvasSize();
+
+ layoutAlgorithm = new FadeLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING);
+ //((FadeLayoutAlgorithm)layoutAlgorithm).setRandom(false);
+ //((FadeLayoutAlgorithm)layoutAlgorithm).setIterations(1000);
+ /* irbull, these were the force directed settings
+ ((SpringLayoutAlgorithm) layoutAlgorithm).setSpringLengthRange(60, 225);
+ ((SpringLayoutAlgorithm) layoutAlgorithm).setStabilize(stabilize);
+ ((SpringLayoutAlgorithm) layoutAlgorithm).setEnforceBounds(enforceBounds);
+ ((SpringLayoutAlgorithm) layoutAlgorithm).setMinimumStabilizeDistance(ForceDirectedAlgorithm.DEFAULT_MIN_STABILIZE_DISTANCE);
+ ((SpringLayoutAlgorithm) layoutAlgorithm).setOverlappingNodesAllowed(!noOverlappingNodes);
+ */
+ layoutThread = layoutAlgorithm.getLayoutThread(model.getNodesArray(), model.getConnectionsArray(), 5D, 5D, (double)d.width,(double) d.height, true);
+
+ this.addThread(layoutThread);
+ //this.addThread(new FreqUpdater());
+ this.addControlListener(new ControlListener() {
+ private boolean minimized = false;
+ private boolean wasRunning = false;
+ public void controlMoved(ControlEvent e) { }
+ public void controlResized(ControlEvent e) {
+ // handle minimized case
+ Dimension d = SpringGraphViewerImpl.this.getCanvasSize();
+ if (d.isEmpty()) {
+ wasRunning = layoutAlgorithm.isRunning() && !layoutAlgorithm.isPaused();
+ layoutAlgorithm.pause();
+ minimized = true;
+ } else {
+ layoutAlgorithm.setLayoutArea(5, 5, Math.max(10, d.width - 60), Math.max(10, d.height - 30));
+ if (minimized) {
+ minimized = false;
+ if (wasRunning) {
+ layoutAlgorithm.resume();
+ }
+ }
+ }
+ }
+ });
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.ui.parts.GraphicalViewerImpl#createDefaultRoot()
+ */
+ //@tag zest.bug.156617ClearViewer.fix : create the correct default root.
+ protected void createDefaultRoot() {
+ GraphRootEditPart root = new GraphRootEditPart(this, allowMarqueeSelection, allowPanning);
+ this.setRootEditPart(root);
+ }
+
+ private GraphRootEditPart getCastedRoot() {
+ return (GraphRootEditPart)getRootEditPart();
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.ui.parts.GraphicalEditor#configureGraphicalViewer()
+ */
+ protected void configureGraphicalViewer() {
+ GraphRootEditPart root = getCastedRoot();
+ //@tag zest.bug.156617ClearViewer.fix : just clear the children.
+ getCastedRoot().clear();
+ //setProperty(MouseWheelHandler.KeyGenerator.getKey(SWT.NONE), this);
+
+ List zoomLevels = new ArrayList(3);
+ zoomLevels.add(ZoomManager.FIT_ALL);
+ zoomLevels.add(ZoomManager.FIT_WIDTH);
+ zoomLevels.add(ZoomManager.FIT_HEIGHT);
+
+ ZoomManager zoomMgr = root.getZoomManager();
+ zoomMgr.setZoomLevelContributions(zoomLevels);
+
+ zoomMgr.addZoomListener(this);
+ IAction zoomIn = new ZoomInAction(zoomMgr);
+ IAction zoomOut = new ZoomOutAction(zoomMgr);
+
+
+ KeyHandler handler = getKeyHandler();
+ if (handler == null) {
+ handler = new KeyHandler();
+ this.setKeyHandler(handler);
+ }
+
+ // so many zoom choices, so little time!
+
+ getKeyHandler().put(KeyStroke.getPressed('+', 43, SWT.CTRL), zoomIn);
+ getKeyHandler().put(KeyStroke.getPressed('+', SWT.KEYPAD_ADD, SWT.CTRL), zoomIn);
+ getKeyHandler().put(KeyStroke.getPressed('=', 61, SWT.CTRL), zoomIn);
+ getKeyHandler().put(KeyStroke.getPressed('-', 45, SWT.CTRL), zoomOut);
+ getKeyHandler().put(KeyStroke.getPressed('-', SWT.KEYPAD_SUBTRACT, SWT.CTRL), zoomOut);
+ this.setProperty(MouseWheelHandler.KeyGenerator.getKey(SWT.CTRL), MouseWheelZoomHandler.SINGLETON);
+ this.setEditPartFactory(new GraphEditPartFactory(root));
+
+ }
+
+ /**
+ * Gets an array of the selected model elements (Widget objects).
+ * @return Widget[]
+ */
+ public Widget[] getSelectedModelElements() {
+ Widget[] items = new Widget[ getSelectedEditParts().size() ];
+ int i = 0;
+ for ( Iterator iterator = getSelectedEditParts().iterator(); iterator.hasNext(); ) {
+ AbstractGraphicalEditPart editPart =(AbstractGraphicalEditPart) iterator.next();
+ Object modelElement = (Object)editPart.getModel();
+ if ( modelElement.equals(LayerManager.ID)) {
+ items[i++] = getControl();
+ } else {
+ items[i++] = (Widget)modelElement;
+ }
+ }
+ return items;
+ }
+
+ public void setSelection(ISelection selection) {
+ if (!(selection instanceof IStructuredSelection))
+ return;
+ List orderedSelection = ((IStructuredSelection)selection).toList();
+ ArrayList editPartSelection = new ArrayList( orderedSelection.size() );
+ for( int i = 0; i < orderedSelection.size(); i++ ) {
+ editPartSelection.add( i, orderedSelection.get(i));
+ }
+ super.setSelection( new StructuredSelection( editPartSelection ) );
+ }
+
+ /**
+ * @see SpringGraphViewerImpl#centerNode(IGraphModelNode)
+ */
+ public void setCenterSelection( IGraphModelNode nodeToCenter, int x, int y ) {
+ //TODO: Really make this the center, note this is harder when we are in a SASHForm
+ nodeToCenter.setLocationInLayout(x,y);
+ StructuredSelection selection = new StructuredSelection( new Object[]{nodeToCenter} );
+ setSelection( selection );
+ }
+
+ /**
+ * This is an alternate way of centering the given node. It
+ * pans the canvas to make the given node be at the center of the viewer.
+ * @param nodeToCenter The node to center (assumed not null).
+ */
+ public void centerNodeInCanvas(IGraphModelNode nodeToCenter) {
+ Dimension dim = getTranslatedCanvasSize();
+ int cx = (dim.width / 2);
+ int cy = (dim.height / 2);
+ int dx = cx - (int)nodeToCenter.getXInLayout();
+ int dy = cy - (int)nodeToCenter.getYInLayout();
+ panningStart();
+ panning(dx, dy);
+ panningEnd();
+ }
+
+ /**
+ * Indicates that panning has started. Pauses the layout algorithm if it is running.
+ * @see org.eclipse.mylar.zest.core.internal.graphviewer.IPanningListener#panningStart()
+ */
+ public void panningStart() {
+ wasRunningBeforePanning = layoutAlgorithm.isRunning() && !layoutAlgorithm.isPaused();
+ if (wasRunningBeforePanning) {
+ layoutAlgorithm.pause();
+ }
+ }
+
+ /**
+ * Handles a change in position due to panning. Tells the {@link SpringGraphViewer}
+ * to move all the nodes.
+ * @param dx the change in x position
+ * @param dy the change in y position
+ * @see org.eclipse.mylar.zest.core.internal.graphviewer.IPanningListener#panning(int, int)
+ */
+ public void panning(int dx, int dy) {
+ // @tag zest(bug(153356)) : Revist panning support for static graph
+ //layoutAlgorithm.moveAllEntities(dx, dy);
+ }
+
+ /**
+ * Indicates that panning has ceased. Resumes the layout algorithm if it was running
+ * before panning started.
+ * @see org.eclipse.mylar.zest.core.internal.graphviewer.IPanningListener#panningEnd()
+ */
+ public void panningEnd() {
+ if (wasRunningBeforePanning) {
+ layoutAlgorithm.resume();
+ }
+ }
+
+ /**
+ * Creates a new relationship between the source node and the destination node.
+ * If either node doesn't exist then it will be created.
+ * @param connection The connection data object.
+ * @param srcNode The source node data object.
+ * @param destNode The destination node data object.
+ */
+ public void addRelationship(Object connection, Object srcNode, Object destNode) {
+ // create the new relationship
+ IGraphModelConnection newConnection = modelFactory.createConnection(model, connection, srcNode, destNode);
+
+ // add it to the layout algorithm
+ layoutAlgorithm.addRelationship(newConnection);
+ }
+
+ /**
+ * Adds a new relationship given the connection. It will use the content provider
+ * to determine the source and destination nodes.
+ * @param connection The connection data object.
+ */
+ public void addRelationship (Object connection) {
+ if (model.getInternalConnection(connection) == null) {
+ if (modelFactory.getContentProvider() instanceof IGraphContentProvider) {
+ IGraphContentProvider content = ((IGraphContentProvider)modelFactory.getContentProvider());
+ Object source = content.getSource(connection);
+ Object dest = content.getDestination(connection);
+ // create the new relationship
+ IGraphModelConnection newConnection = modelFactory.createConnection(model, connection, source, dest);
+ // add it to the layout algorithm
+ layoutAlgorithm.addRelationship(newConnection);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+
+ /**
+ * Updates the connection with the given weight.
+ * The weight should be in {-1, [0-1]}.
+ * A weight of -1 means that there is no force/tension between the nodes.
+ * A weight of 0 results in the maximum spring length being used (farthest apart).
+ * A weight of 1 results in the minimum spring length being used (closest together).
+ * @param connection The connection object.
+ * @param weight The new weight for the connection.
+ */
+ public void updateRelationshipWeight(Object connection, double weight) {
+ IGraphModelConnection relationship = model.getInternalConnection(connection);
+ if (relationship != null) {
+ relationship.setWeightInLayout(weight);
+ }
+ }
+
+ /**
+ * Removes the given connection object from the layout algorithm and the model.
+ * @param connection
+ */
+ public void removeRelationship(Object connection) {
+ IGraphModelConnection relation = model.getInternalConnection(connection);
+
+ if (relation != null) {
+ // remove the relationship from the layout algorithm
+ layoutAlgorithm.removeRelationship(relation);
+
+ // remove the relationship from the model
+ model.removeConnection(relation);
+ }
+ }
+
+
+ /**
+ * Creates a new node and adds it to the graph. If it already exists nothing happens.
+ * @param newNode
+ */
+ public void addNode(Object element) {
+ if (model.getInternalNode(element) == null ) {
+ // create the new node
+ IGraphModelNode newNode = modelFactory.createNode(model, element);
+
+ // add it to the layout algorithm
+ layoutAlgorithm.addEntity(newNode);
+ }
+ }
+
+ /**
+ * Removes the given element from the layout algorithm and the model.
+ * @param element The node element to remove.
+ */
+ public void removeNode(Object element) {
+ IGraphModelNode node = model.getInternalNode(element);
+
+ if (node != null) {
+ // remove the node from the layout algorithm and all the connections
+ layoutAlgorithm.removeEntity(node);
+ layoutAlgorithm.removeRelationships(node.getSourceConnections());
+ layoutAlgorithm.removeRelationships(node.getTargetConnections());
+
+ // remove the node and it's connections from the model
+ model.removeNode(node);
+ }
+ }
+
+ /**
+ * Pauses layout algorithm if it is running not paused.
+ */
+ public void pauseLayoutAlgorithm() {
+ if ((layoutAlgorithm != null) && layoutAlgorithm.isRunning()) {
+ if (!layoutAlgorithm.isPaused()) {
+ layoutAlgorithm.pause();
+ }
+ }
+ }
+
+ /**
+ * Resumes the layout algorithm if it is running and paused.
+ */
+ public void resumeLayoutAlgorithm() {
+ if ((layoutAlgorithm != null) && layoutAlgorithm.isRunning()) {
+ if (layoutAlgorithm.isPaused()) {
+ layoutAlgorithm.resume();
+ }
+ }
+ }
+
+ /**
+ * Stops the layout algorithm if it is running.
+ */
+ public void stopLayoutAlgorithm() {
+ if ((layoutAlgorithm != null) && layoutAlgorithm.isRunning()) {
+ layoutAlgorithm.stop();
+ }
+ }
+
+ /**
+ * Restarts the layout algorithm. This will do nothing
+ * if the algorithm is already running.
+ */
+ public void restartLayoutAlgorithm() {
+ if ((layoutAlgorithm != null) && !layoutAlgorithm.isRunning()) {
+ this.removeThread(layoutThread);
+ this.addThread(layoutThread);
+ }
+ }
+
+ /**
+ * Returns if the layout algorithm is running.
+ * @return boolean if the layout algorithm is running
+ */
+ public boolean isLayoutAlgorithmRunning() {
+ return ((layoutAlgorithm != null) && layoutAlgorithm.isRunning());
+ }
+
+ /**
+ * Returns if the layout algorithm is paused.
+ * @return boolean if the layout algorithm is paused
+ */
+ public boolean isLayoutAlgorithmPaused() {
+ return ((layoutAlgorithm != null) && layoutAlgorithm.isPaused());
+ }
+
+
+ public void zoomIn() {
+ ((GraphRootEditPart)this.getRootEditPart()).getZoomManager().zoomIn();
+ }
+
+ public void zoomOut() {
+ ((GraphRootEditPart)this.getRootEditPart()).getZoomManager().zoomOut();
+ }
+
+
+ /**
+ * Ensures that the selected node is visible (centered).
+ * @see org.eclipse.gef.editparts.ZoomListener#zoomChanged(double)
+ */
+ public void zoomChanged(double zoom) {
+
+ Dimension dim = getCanvasSize();
+ if ((zoom < 1D) && (zoom > 0D)) {
+ layoutAlgorithm.setLayoutArea(10, 10, Math.max(0, (dim.width / zoom) - 10), Math.max(0, (dim.height / zoom) - 10));
+ } else {
+ layoutAlgorithm.setLayoutArea(10, 10, dim.width, dim.height);
+ }
+
+ List selection = getSelectedEditParts();
+ if (selection.size() > 0) {
+ Object obj = selection.get(0);
+ if (obj instanceof GraphNodeEditPart) {
+ //GraphNodeEditPart editPart = (GraphNodeEditPart)obj;
+ //centerNodeInCanvas(editPart.getCastedModel());
+ }
+ }
+ }
+
+ class _selectionChangedListener implements ISelectionChangedListener {
+ public void selectionChanged(SelectionChangedEvent event) {
+ //DebugPrint.println("Impl selection changed called: ");
+ Event e = new Event();
+ ArrayList selectedModelElements = new ArrayList( getSelectedEditParts().size() );
+ for ( Iterator iterator = getSelectedEditParts().iterator(); iterator.hasNext(); ) {
+ Object modelElement = ((EditPart) iterator.next()).getModel();
+ //DebugPrint.println(modelElement);
+
+ selectedModelElements.add( modelElement );
+ }
+ e.data = new String("test");
+
+ getControl().notifyListeners(SWT.Selection, null);
+ }
+ }
+
+ public LayoutAlgorithm getLayoutAlgorithm() {
+ return layoutAlgorithm;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/StaticGraphViewerImpl.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/StaticGraphViewerImpl.java
new file mode 100644
index 0000000..86f3f4d
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/StaticGraphViewerImpl.java
@@ -0,0 +1,428 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphviewer;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.draw2d.Animation;
+import org.eclipse.draw2d.FigureCanvas;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.gef.EditPart;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.mylar.zest.core.ZestStyles;
+import org.eclipse.mylar.zest.core.internal.gefx.IPanningListener;
+import org.eclipse.mylar.zest.core.internal.gefx.NonThreadedGraphicalViewer;
+import org.eclipse.mylar.zest.core.internal.gefx.RevealListener;
+import org.eclipse.mylar.zest.core.internal.gefx.StaticGraphRootEditPart;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphItem;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelConnection;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IZestGraphDefaults;
+import org.eclipse.mylar.zest.core.internal.graphviewer.parts.GraphEditPartFactory;
+import org.eclipse.mylar.zest.core.internal.viewers.NoOverlapLayoutAlgorithm;
+import org.eclipse.mylar.zest.layouts.InvalidLayoutConfiguration;
+import org.eclipse.mylar.zest.layouts.LayoutAlgorithm;
+import org.eclipse.mylar.zest.layouts.LayoutStyles;
+import org.eclipse.mylar.zest.layouts.algorithms.GridLayoutAlgorithm;
+import org.eclipse.mylar.zest.layouts.algorithms.RadialLayoutAlgorithm;
+import org.eclipse.mylar.zest.layouts.algorithms.SpringLayoutAlgorithm;
+import org.eclipse.mylar.zest.layouts.algorithms.TreeLayoutAlgorithm;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ *
+ * @author Ian Bull
+ * @author Chris Callendar
+ */
+public class StaticGraphViewerImpl extends NonThreadedGraphicalViewer implements IPanningListener {
+
+ private static final int ANIMATION_TIME = 500;
+ private LayoutAlgorithm layoutAlgorithm = null;
+ private NoOverlapLayoutAlgorithm noOverlapAlgorithm = null;
+ private GraphModel model = null;
+ private boolean allowMarqueeSelection = false;
+ private boolean allowPanning = false;
+ private boolean noOverlappingNodes = false;
+ private int style = 0;
+ private int nodeStyle;
+ private int connectionStyle;
+ private Dimension preferredSize = new Dimension(-1, -1);
+
+ private boolean hasLayoutRun = false;
+
+
+ /**
+ * Initializes the viewer impl.
+ * @see ZestStyles#PANNING
+ * @see ZestStyles#NO_OVERLAPPING_NODES
+ * @see ZestStyles#NODES_HIGHLIGHT_ADJACENT
+ * @see ZestStyles#DIRECTED_GRAPH
+ * @see ZestStyles#LAYOUT_GRID
+ * @see ZestStyles#LAYOUT_TREE
+ * @see ZestStyles#LAYOUT_RADIAL
+ * @see ZestStyles#LAYOUT_SPRING
+ * @see SWT#V_SCROLL
+ * @see SWT#H_SCROLL
+ */
+ public StaticGraphViewerImpl(Composite composite, int style) {
+ super(composite);
+ this.setStyle(style);
+ setConnectionStyle(IZestGraphDefaults.CONNECTION_STYLE);
+ setNodeStyle(IZestGraphDefaults.NODE_STYLE);
+ this.noOverlapAlgorithm = new NoOverlapLayoutAlgorithm();
+ this.setSelectionManager(new ZestSelectionManager());
+ }
+
+ public boolean hasLayoutRun() {
+ return hasLayoutRun;
+ }
+
+ /**
+ * Gets the style on the SpringGraphViewer
+ * @return int
+ */
+ public int getStyle() {
+ return this.style;
+ }
+
+ /**
+ * Sets the scale for the main canvas
+ * @param x The scale in the X direction
+ * @param y The scale in the y direction
+ */
+ public void setScale( double x, double y ) {
+ StaticGraphRootEditPart root = (StaticGraphRootEditPart)getRootEditPart();
+ root.setScale(x, y);
+
+ }
+
+ /**
+ * Gets the scale in the y Direction
+ */
+ public double getHeightScale() {
+ StaticGraphRootEditPart root = (StaticGraphRootEditPart)getRootEditPart();
+ return root.getYScale();
+ }
+
+ /**
+ * Gets the scale in the X Direction
+ */
+ public double getWidthScale() {
+ StaticGraphRootEditPart root = (StaticGraphRootEditPart)getRootEditPart();
+ return root.getXScale();
+ }
+
+
+ /**
+ * Sets the style for the viewer.
+ * @param style
+ */
+ public void setStyle(int style) {
+ this.style = style;
+ this.noOverlappingNodes = ZestStyles.checkStyle(style, ZestStyles.NO_OVERLAPPING_NODES);
+ this.allowPanning = ZestStyles.checkStyle(style, ZestStyles.PANNING);
+ this.allowMarqueeSelection = !allowPanning && ZestStyles.checkStyle(style, ZestStyles.MARQUEE_SELECTION);
+ (getFigureCanvas()).setScrollBarVisibility(FigureCanvas.AUTOMATIC);
+ setLayoutAlgorithm(new GridLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING), false);
+ }
+
+ /**
+ * Sets the default connection style.
+ * @param connection style the connection style to set
+ * @see org.eclipse.mylar.zest.core.ZestStyles
+ */
+ public void setConnectionStyle(int connectionStyle) {
+ this.connectionStyle = connectionStyle;
+ if (model != null) {
+ model.setConnectionStyle(connectionStyle);
+ }
+ }
+
+ /**
+ * Gets the default connection style.
+ * @return the connection style
+ * @see org.eclipse.mylar.zest.core.ZestStyles
+ */
+ public int getConnectionStyle() {
+ return connectionStyle;
+ }
+
+ /**
+ * Sets the default node style.
+ * @param nodeStyle the node style to set
+ * @see org.eclipse.mylar.zest.core.ZestStyles
+ */
+ public void setNodeStyle(int nodeStyle) {
+ this.nodeStyle = nodeStyle;
+ if (model != null) {
+ model.setNodeStyle(nodeStyle);
+ }
+ }
+
+ /**
+ * Gets the default node style.
+ * @return the node style
+ * @see org.eclipse.mylar.zest.core.ZestStyles
+ */
+ public int getNodeStyle() {
+ return nodeStyle;
+ }
+
+ /**
+ * Sets the preferred size of the layout area. Size of ( -1, -1) uses the current canvas size.
+ * @param width
+ * @param height
+ */
+ public void setPreferredSize(int width, int height) {
+
+ this.preferredSize = new Dimension(width, height);
+ }
+
+
+ /**
+ * Sets the model and initializes the layout algorithm.
+ * @see org.eclipse.mylar.zest.core.internal.gefx.ThreadedGraphicalViewer#setContents(java.lang.Object)
+ */
+ //@tag zest.bug.160367-Refreshing.fix : uses the IStylingGraphModelFactory now
+ public void setContents(Object model) {
+ super.setContents( model );
+ this.model = (GraphModel)model;
+ applyLayout();
+ }
+
+
+ /**
+ * Runs the layout on this graph.
+ * It uses the reveal listner to run the layout only if the view
+ * is visible. Otherwise it will be deferred until after the view
+ * is available.
+ */
+ public void applyLayout() {
+ this.addRevealListener(new RevealListener() {
+ public void revealed(Control c) {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+
+ public void run() {
+ applyLayoutInternal();
+ }
+ });
+ }
+ });
+ }
+
+
+
+ private void applyLayoutInternal() {
+
+ if ((model == null) || (model.getNodes().size() == 0))
+ return;
+
+ if (layoutAlgorithm == null) {
+ layoutAlgorithm = new TreeLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING);
+ }
+
+ // calculate the size for the layout algorithm
+ Dimension d = this.getCanvasSize();
+ Dimension nodeSize = findBiggestNode();
+ d.width = Math.max(0, d.width - nodeSize.width);
+ //@tag zest.bug.159645 : should be d.height - nodeSize.height
+ d.height = Math.max(0, d.height - nodeSize.height);
+
+ if ( this.preferredSize.width >= 0 ) {
+ d.width = preferredSize.width;
+ }
+ if ( this.preferredSize.height >= 0 ) {
+ d.height = preferredSize.height;
+ }
+
+ if (d.isEmpty())
+ return;
+ IGraphModelConnection[] connectionsToLayout = getConnectionsToLayout();
+ IGraphModelNode[] nodesToLayout = getNodesToLayout();
+ // For the spring layout, I think it works a little nicer
+ // if a radial layout is run first first
+ if (layoutAlgorithm instanceof SpringLayoutAlgorithm) {
+
+ try {
+ RadialLayoutAlgorithm radial = new RadialLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING);
+ radial.applyLayout(nodesToLayout, connectionsToLayout, 0, 0, d.width, d.height, false, false);
+ } catch (InvalidLayoutConfiguration e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ try {
+ Animation.markBegin();
+ layoutAlgorithm.applyLayout(nodesToLayout, connectionsToLayout, 0, 0, d.width, d.height, false, false);
+ Animation.run(ANIMATION_TIME);
+ getLightweightSystem().getUpdateManager().performUpdate();
+
+ } catch (InvalidLayoutConfiguration e) {
+ e.printStackTrace();
+ }
+
+ // enforce no overlapping nodes
+ if (noOverlappingNodes) {
+ noOverlapAlgorithm.layout(model.getNodes());
+ }
+
+ hasLayoutRun = true;
+ }
+
+ IGraphModelConnection[] getConnectionsToLayout() {
+// @tag zest.bug.156528-Filters.follows : make sure not to layout filtered connections, if the style says so.
+ IGraphModelConnection[] entities;
+ if (ZestStyles.checkStyle(style, ZestStyles.IGNORE_INVISIBLE_LAYOUT)) {
+ LinkedList nodeList = new LinkedList();
+ for (Iterator i = model.getConnections().iterator(); i.hasNext();) {
+ IGraphItem next = (IGraphItem) i.next();
+ if (next.isVisible())
+ nodeList.add(next);
+ }
+ entities = (IGraphModelConnection[]) nodeList.toArray(new IGraphModelConnection[]{});
+ } else {
+ entities = model.getConnectionsArray();
+ }
+ return entities;
+ }
+
+ IGraphModelNode[] getNodesToLayout(){
+// @tag zest.bug.156528-Filters.follows : make sure not to layout filtered nodes, if the style says so.
+ IGraphModelNode[] entities;
+ if (ZestStyles.checkStyle(style, ZestStyles.IGNORE_INVISIBLE_LAYOUT)) {
+ LinkedList nodeList = new LinkedList();
+ for (Iterator i = model.getNodes().iterator(); i.hasNext();) {
+ IGraphItem next = (IGraphItem) i.next();
+ if (next.isVisible())
+ nodeList.add(next);
+ }
+ entities = (IGraphModelNode[]) nodeList.toArray(new IGraphModelNode[]{});
+ } else {
+ entities = model.getNodesArray();
+ }
+ return entities;
+ }
+
+ private Dimension findBiggestNode() {
+ Dimension dim = new Dimension();
+ if (model != null) {
+ for (Iterator iter = model.getNodes().iterator(); iter.hasNext(); ) {
+ IGraphModelNode node = (IGraphModelNode)iter.next();
+ dim.width = Math.max(dim.width, (int)node.getWidthInLayout());
+ dim.height = Math.max(dim.height, (int)node.getHeightInLayout());
+ }
+ }
+ return dim;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.ui.parts.GraphicalViewerImpl#createDefaultRoot()
+ */
+ //@tag zest.bug.156617ClearViewer.fix : create the correct default root.
+ protected void createDefaultRoot() {
+ StaticGraphRootEditPart root = new StaticGraphRootEditPart();
+ this.setRootEditPart(root);
+ }
+
+// @tag zest.bug.156617ClearViewer.fix : convenience.
+ private StaticGraphRootEditPart getCastedRoot() {
+ return (StaticGraphRootEditPart)getRootEditPart();
+ }
+
+ protected void configureGraphicalViewer() {
+ //@tag zest.bug.156617ClearViewer.fix : just clear the children, don't create a new root.
+ getCastedRoot().clear();
+ getCastedRoot().configure(this, allowMarqueeSelection, allowPanning);
+ this.setEditPartFactory(new GraphEditPartFactory(getCastedRoot()));
+ }
+
+ public void panningStart() {
+ }
+
+ public void panning(int dx, int dy) {
+ // @tag zest(bug(153356)) : Revist panning support for static graph
+ //((AbstractLayoutAlgorithm)layoutAlgorithm).moveAllEntities(dx, dy);
+ }
+
+ public void panningEnd() {
+ }
+
+ /**
+ * @param algorithm
+ */
+ public void setLayoutAlgorithm(LayoutAlgorithm algorithm, boolean applyLayout) {
+ this.layoutAlgorithm = algorithm;
+ if (applyLayout) {
+ applyLayout();
+ }
+ }
+
+
+
+ /**
+ * Updates the connection with the given weight.
+ * The weight should be in {-1, [0-1]}.
+ * A weight of -1 means that there is no force/tension between the nodes.
+ * A weight of 0 results in the maximum spring length being used (farthest apart).
+ * A weight of 1 results in the minimum spring length being used (closest together).
+ * @param connection The connection object.
+ * @param weight The new weight for the connection.
+ */
+ public void updateRelationshipWeight(Object connection, double weight) {
+ IGraphModelConnection relationship = model.getInternalConnection(connection);
+ if (relationship != null) {
+ relationship.setWeightInLayout(weight);
+ applyLayout();
+ }
+ }
+
+
+
+ public void setSelection(List selection) {
+ if (model == null) return;
+ Iterator iterator = selection.iterator();
+ HashMap nodeMap = model.getNodesMap();
+ HashMap connectionMap = model.getConnectionMap();
+ List editPartList = new ArrayList(1);
+ while (iterator.hasNext()) {
+ Object current = iterator.next();
+ Object currentNode = nodeMap.get(current);
+ if ( current != null ) {
+ //@tag zest(bug(153466-NoNestedClientSupply(fix))) : use the edit part registry to avoid having back-links in the model.
+ EditPart part = (EditPart) getEditPartRegistry().get(currentNode);
+ if (part != null)
+ editPartList.add(part);//((GraphModelNode)currentNode).getEditPart() );
+ }
+ else {
+ Object currentConnection = connectionMap.get(current);
+ if ( currentConnection != null ) {
+ // Currenly we cannot select edges
+ }
+ }
+ }
+ setSelection(new StructuredSelection(editPartList));
+ }
+
+ public LayoutAlgorithm getLayoutAlgorithm() {
+ return layoutAlgorithm;
+ }
+
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/ZestSelectionManager.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/ZestSelectionManager.java
new file mode 100644
index 0000000..7ee1297
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/ZestSelectionManager.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphviewer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.gef.SelectionManager;
+import org.eclipse.gef.editparts.AbstractEditPart;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphItem;
+
+/**
+ * This Selection Manager converts edit parts to the user data type and returns
+ * those. If this were not here, Zest would return the Edit parts as selection
+ * and not what the user passed in through the content provider.
+ *
+ * @author Ian Bull
+ *
+ */
+public class ZestSelectionManager extends SelectionManager {
+
+ /**
+ * Gets a list of currently selected nodes. The selection will include the "user objects"
+ * that have been selected.
+ *
+ * @return A List of user objects that has been selected.
+ */
+ public List getSelectedModelElements() {
+ List selections = new ArrayList(1);
+ ISelection selection = super.getSelection();
+
+ IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+ Iterator elements = structuredSelection.iterator();
+
+ // Loop through all selected elements and get the user data for these
+ while (elements.hasNext()) {
+ AbstractEditPart editPart = (AbstractEditPart) elements.next();
+ if (editPart.getModel() == null) {
+ return Collections.EMPTY_LIST;
+ }
+ else if (editPart.getModel() instanceof IGraphItem) {
+ IGraphItem item = (IGraphItem) editPart.getModel();
+ if (!(item instanceof GraphModel)) {
+ selections.add(item.getData());
+ }
+ }
+ }
+ return selections;
+ }
+
+ /**
+ * Gets the current selection in the Zest viewer as an ISelection.
+ */
+ public ISelection getSelection() {
+ List selections = getSelectedModelElements();
+ if ( selections.size() == 0 ) return StructuredSelection.EMPTY;
+ return new StructuredSelection( selections );
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/GraphOverviewerImpl.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/GraphOverviewerImpl.java
new file mode 100644
index 0000000..717af84
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/GraphOverviewerImpl.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphviewer.overview;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.gef.EditDomain;
+import org.eclipse.gef.EditPartViewer;
+import org.eclipse.gef.RootEditPart;
+import org.eclipse.gef.Tool;
+import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
+import org.eclipse.gef.editparts.ScalableRootEditPart;
+import org.eclipse.gef.editparts.ZoomManager;
+import org.eclipse.gef.ui.parts.DomainEventDispatcher;
+import org.eclipse.gef.ui.parts.GraphicalViewerImpl;
+import org.eclipse.mylar.zest.core.internal.gefx.IZestViewerProperties;
+import org.eclipse.mylar.zest.core.internal.graphviewer.overview.parts.OverviewEditPart;
+import org.eclipse.mylar.zest.core.internal.graphviewer.overview.parts.OverviewImageEditPartFactory;
+import org.eclipse.mylar.zest.core.internal.graphviewer.overview.parts.OverviewRootEditPart;
+import org.eclipse.mylar.zest.core.internal.graphviewer.parts.tools.OverviewRectangleTool;
+import org.eclipse.swt.graphics.Cursor;
+
+/**
+ * A viewer that simply draws an "overview" graph of nodes only.
+ * @author Del Myers
+ *
+ */
+public class GraphOverviewerImpl extends GraphicalViewerImpl implements PropertyChangeListener {
+
+
+ private EditPartViewer model;
+
+ /**
+ *
+ */
+ public GraphOverviewerImpl() {
+ setEditPartFactory(new OverviewImageEditPartFactory());
+ EditDomain domain = new EditDomain();
+ //ToolEventDispatcher dispatcher = new ToolEventDispatcher(this);
+ domain.setDefaultTool(new OverviewRectangleTool());
+ domain.setActiveTool(domain.getDefaultTool());
+ DomainEventDispatcher dispatcher = new DomainEventDispatcher(domain, this);
+ setEditDomain(domain);
+ //dispatcher.setTool(new OverviewRectangleTool());
+ getLightweightSystem().setEventDispatcher(dispatcher);
+ }
+
+ protected void createDefaultRoot() {
+ ScalableRootEditPart root = new OverviewRootEditPart();
+ this.setRootEditPart(root);
+ }
+
+ private ScalableRootEditPart getCastedRoot() {
+ return (ScalableRootEditPart)getRootEditPart();
+ }
+
+
+ public void setContents(Object model) {
+ if (model instanceof EditPartViewer) {
+ EditPartViewer newModel = (EditPartViewer) model;
+ if (this.model != model) {
+ if (this.model != null)
+ this.model.removePropertyChangeListener(this);
+ this.model = newModel;
+ super.setContents(newModel);
+ this.model.addPropertyChangeListener(this);
+ Tool tool = getEditDomain().getActiveTool();
+ if (tool instanceof OverviewRectangleTool) {
+ tool.setViewer(this);
+ ((OverviewRectangleTool)tool).setZoomManager(getZoomManager(model));
+ }
+ }
+ } else {
+
+ }
+ }
+
+ /**
+ * @param model2
+ * @return
+ */
+ private ZoomManager getZoomManager(Object model) {
+ if (model instanceof EditPartViewer) {
+ RootEditPart modelRoot = ((EditPartViewer)model).getRootEditPart();
+ if (modelRoot instanceof ScalableFreeformRootEditPart) {
+ return ((ScalableFreeformRootEditPart)modelRoot).getZoomManager();
+ } else if (modelRoot instanceof ScalableRootEditPart) {
+ return ((ScalableRootEditPart)modelRoot).getZoomManager();
+ }
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.ui.parts.GraphicalViewerImpl#setCursor(org.eclipse.swt.graphics.Cursor)
+ */
+ public void setCursor(Cursor newCursor) {
+ if (getControl() != null && !getControl().isDisposed()) {
+ getControl().setCursor(newCursor);
+ }
+ }
+
+ public void setZoom(Rectangle zoom) {
+ OverviewEditPart content = (OverviewEditPart) getCastedRoot().getContents();
+ Rectangle copy = zoom.getCopy();
+ //((GraphicalEditPart)getRootEditPart()).getFigure().translateToRelative(copy);
+ if (content==null) return; //not yet initialized.
+ content.translateToViewer(copy);
+ RootEditPart modelRoot = this.model.getRootEditPart();
+ if (modelRoot instanceof ScalableFreeformRootEditPart) {
+ if (!zoom.isEmpty()) {
+ ((ScalableFreeformRootEditPart)modelRoot).getZoomManager().zoomTo(copy);
+ } else {
+ //ensure that the rectangle has a zero width and height so that
+ //it is centered and the view is not scaled.
+ copy.width = 0;
+ copy.height = 0;
+ ((ScalableFreeformRootEditPart)modelRoot).getZoomManager().zoomTo(copy);
+ }
+ } else if (modelRoot instanceof ScalableRootEditPart) {
+ if (!zoom.isEmpty()) {
+ ((ScalableRootEditPart)modelRoot).getZoomManager().zoomTo(copy);
+ } else {
+// ensure that the rectangle has a zero width and height so that
+ //it is centered and the view is not scaled.
+ copy.width = 0;
+ copy.height = 0;
+ ((ScalableRootEditPart)modelRoot).getZoomManager().zoomTo(copy);
+ }
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.ui.parts.GraphicalViewerImpl#unhookControl()
+ */
+ protected void unhookControl() {
+ Tool tool = getEditDomain().getActiveTool();
+ if (tool instanceof OverviewRectangleTool) {
+ ((OverviewRectangleTool)tool).setZoomManager(null);
+ }
+ super.unhookControl();
+ }
+
+ /* (non-Javadoc)
+ * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (IZestViewerProperties.GRAPH_VIEWER_CONTENTS.equals(evt.getPropertyName())) {
+ //set the zoom to nothing.
+ //setZoom(new Rectangle());
+ //have to refresh the whole thing.
+ //super.setContents(this.model);
+ }
+ }
+
+
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/ToolEventDispatcher.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/ToolEventDispatcher.java
new file mode 100644
index 0000000..a01a29a
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/ToolEventDispatcher.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphviewer.overview;
+
+import org.eclipse.draw2d.InputEvent;
+import org.eclipse.draw2d.SWTEventDispatcher;
+import org.eclipse.gef.EditPartViewer;
+import org.eclipse.gef.Tool;
+import org.eclipse.gef.tools.SelectionTool;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.widgets.Event;
+
+/**
+ * An event dispatcher that simply delegates events to tools.
+ * @author Del Myers
+ *
+ */
+public class ToolEventDispatcher extends SWTEventDispatcher {
+ private EditPartViewer viewer;
+ private Tool tool;
+
+ public ToolEventDispatcher(EditPartViewer viewer) {
+ this.viewer = viewer;
+ setTool(new SelectionTool());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchFocusGained(org.eclipse.swt.events.FocusEvent)
+ */
+ public void dispatchFocusGained(FocusEvent e) {
+ tool.focusGained(e, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchFocusLost(org.eclipse.swt.events.FocusEvent)
+ */
+ public void dispatchFocusLost(FocusEvent e) {
+ tool.focusLost(e, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchKeyPressed(org.eclipse.swt.events.KeyEvent)
+ */
+ public void dispatchKeyPressed(KeyEvent e) {
+ tool.keyDown(e, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchKeyReleased(org.eclipse.swt.events.KeyEvent)
+ */
+ public void dispatchKeyReleased(KeyEvent e) {
+ tool.keyUp(e, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchKeyTraversed(org.eclipse.swt.events.TraverseEvent)
+ */
+ public void dispatchKeyTraversed(TraverseEvent e) {
+ tool.keyTraversed(e, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchMouseDoubleClicked(org.eclipse.swt.events.MouseEvent)
+ */
+ public void dispatchMouseDoubleClicked(MouseEvent me) {
+ tool.mouseDoubleClick(me, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchMouseEntered(org.eclipse.swt.events.MouseEvent)
+ */
+ public void dispatchMouseEntered(MouseEvent me) {
+ tool.viewerEntered(me, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchMouseExited(org.eclipse.swt.events.MouseEvent)
+ */
+ public void dispatchMouseExited(MouseEvent me) {
+ tool.viewerExited(me, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchMouseHover(org.eclipse.swt.events.MouseEvent)
+ */
+ public void dispatchMouseHover(MouseEvent me) {
+ tool.mouseHover(me, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchMouseMoved(org.eclipse.swt.events.MouseEvent)
+ */
+ public void dispatchMouseMoved(MouseEvent me) {
+ if ((me.stateMask & InputEvent.ANY_BUTTON) != 0)
+ tool.mouseDrag(me, viewer);
+ else
+ tool.mouseMove(me, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchMousePressed(org.eclipse.swt.events.MouseEvent)
+ */
+ public void dispatchMousePressed(MouseEvent me) {
+ tool.mouseDown(me, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.SWTEventDispatcher#dispatchMouseReleased(org.eclipse.swt.events.MouseEvent)
+ */
+ public void dispatchMouseReleased(MouseEvent me) {
+ tool.mouseUp(me, viewer);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.EventDispatcher#dispatchMouseWheelScrolled(org.eclipse.swt.widgets.Event)
+ */
+ public void dispatchMouseWheelScrolled(Event event) {
+ tool.mouseWheelScrolled(event, viewer);
+ }
+
+ public void setTool(Tool tool) {
+ this.tool = tool;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewEditPart.java
new file mode 100644
index 0000000..df03579
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewEditPart.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphviewer.overview.parts;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.draw2d.FigureListener;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.XYLayout;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.gef.EditPartViewer;
+import org.eclipse.gef.EditPolicy;
+import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
+import org.eclipse.mylar.zest.core.internal.nestedgraphviewer.policies.NullLayoutEditPolicy;
+
+/**
+ *
+ * @author Del Myers
+ *
+ */
+public class OverviewEditPart extends AbstractGraphicalEditPart implements FigureListener {
+ /* (non-Javadoc)
+ * @see org.eclipse.mylar.zest.core.internal.graphviewer.parts.GraphEditPart#createEditPolicies()
+ */
+ protected void createEditPolicies() {
+ installEditPolicy(EditPolicy.LAYOUT_ROLE, new NullLayoutEditPolicy());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
+ */
+ protected IFigure createFigure() {
+ IFigure figure = new ScalingContainerFigure();
+ figure.setLayoutManager(new XYLayout());
+ return figure;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#activate()
+ */
+ public void activate() {
+ GraphicalEditPart part = (GraphicalEditPart) getCastedModel().getRootEditPart();
+ IFigure parentPane = part.getFigure();
+ parentPane.addFigureListener(this);
+ //getFigure().addFigureListener(this);
+ super.activate();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#deactivate()
+ */
+ public void deactivate() {
+ GraphicalEditPart part = (GraphicalEditPart) getCastedModel().getRootEditPart();
+ IFigure parentPane = part.getFigure();
+ parentPane.removeFigureListener(this);
+ //getFigure().removeFigureListener(this);
+ super.deactivate();
+ }
+
+ private EditPartViewer getCastedModel() {
+ return (EditPartViewer)getModel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#getModelChildren()
+ */
+ protected List getModelChildren() {
+ GraphicalEditPart part = (GraphicalEditPart) getCastedModel().getRootEditPart();
+// List children = part.getChildren();
+ List modelChildren = new LinkedList();
+ modelChildren.add(part);
+// for (Iterator i = children.iterator(); i.hasNext();) {
+// EditPart child = (EditPart) i.next();
+// if (!(child instanceof ConnectionEditPart)) {
+// if (child instanceof GraphicalEditPart) {
+// modelChildren.add(child);
+// }
+// }
+// }
+ return modelChildren;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#refreshVisuals()
+ */
+ protected void refreshVisuals() {
+ super.refreshVisuals();
+
+ Rectangle boundingArea = getBoundingArea();
+ ScalingContainerFigure figure = (ScalingContainerFigure) getFigure();
+
+ if (!figure.getLogicalBounds().equals(boundingArea)) {
+ figure.setLogicalBounds(boundingArea);
+ }
+ //figure.setBounds(boundingArea);
+
+ }
+ /**
+ * @return
+ */
+ private Rectangle getBoundingArea() {
+ GraphicalEditPart part = (GraphicalEditPart) getCastedModel().getRootEditPart().getContents();
+ Rectangle boundingArea = null;
+ IFigure boundingFigure = part.getFigure();
+ for (Iterator i = boundingFigure.getChildren().iterator(); i.hasNext();) {
+ IFigure child = (IFigure) i.next();
+ if (boundingArea == null) {
+ boundingArea = child.getBounds().getCopy();
+ } else {
+ boundingArea.union(child.getBounds());
+ }
+ }
+ if (boundingArea == null) {
+ boundingArea = new Rectangle();
+ }
+ return boundingArea;
+ }
+
+ /**
+ * Translates the rectangle given in absolute coordinates first into coordinates
+ * that correspond to the coordinate system of this part's target viewer.
+ * @param rect
+ */
+ public void translateToViewer(Rectangle rect) {
+ getFigure().translateFromParent(rect);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.FigureListener#figureMoved(org.eclipse.draw2d.IFigure)
+ */
+ public void figureMoved(IFigure source) {
+ refreshVisuals();
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewGraphEditPartFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewGraphEditPartFactory.java
new file mode 100644
index 0000000..e61b52a
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewGraphEditPartFactory.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphviewer.overview.parts;
+
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.EditPartFactory;
+import org.eclipse.gef.EditPartViewer;
+import org.eclipse.gef.GraphicalEditPart;
+
+
+/**
+ * Creates the edit parts associated with the different models.
+ * @deprecated
+ */
+public class OverviewGraphEditPartFactory implements EditPartFactory {
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.EditPartFactory#createEditPart(org.eclipse.gef.EditPart, java.lang.Object)
+ */
+ public EditPart createEditPart(EditPart context, Object model) {
+ EditPart editPart = null;
+ if (model instanceof EditPartViewer) {
+ editPart = new OverviewEditPart();
+ } else if (model instanceof GraphicalEditPart) {
+ editPart = new OverviewNodeEditPart();
+ }
+ editPart.setModel(model);
+
+ return editPart;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewImageEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewImageEditPart.java
new file mode 100644
index 0000000..db180a6
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewImageEditPart.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphviewer.overview.parts;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.ImageFigure;
+import org.eclipse.draw2d.SWTGraphics;
+import org.eclipse.draw2d.UpdateListener;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.RootEditPart;
+import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * An edit part that links itself to a graphical root edit part, and periodically refreshes itself
+ * with the data in that edit part in order to create an overview.
+ * @author Del Myers
+ */
+
+public class OverviewImageEditPart extends AbstractGraphicalEditPart implements UpdateListener {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
+ */
+ Image image;
+ protected IFigure createFigure() {
+ ImageFigure figure = new ImageFigure();
+ return figure;
+ }
+
+ private GraphicalEditPart getCastedModel() {
+ return (GraphicalEditPart)getModel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
+ */
+ protected void createEditPolicies() {
+ // TODO Auto-generated method stub
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#refreshVisuals()
+ */
+ protected void refreshVisuals() {
+
+ IFigure clientFigure = ((GraphicalEditPart)((RootEditPart)getCastedModel()).getContents()).getFigure();
+ Rectangle clientBounds = getBoundingArea();
+ if (clientBounds.isEmpty()) return;
+ if (image == null) {
+ image = new Image(Display.getDefault(), clientBounds.width, clientBounds.height);
+ }
+ Rectangle imageBounds = new Rectangle(image.getBounds().x, image.getBounds().y, image.getBounds().width, image.getBounds().height);
+ if (!imageBounds.getSize().equals(clientBounds.getSize())) {
+ if (!image.isDisposed()) {
+ image.dispose();
+ }
+ image = new Image(Display.getDefault(), clientBounds.width, clientBounds.height);
+ }
+ GC drawer = new GC(image);
+ SWTGraphics graphics = new SWTGraphics(drawer);
+ graphics.translate(-clientBounds.x, -clientBounds.y);
+ clientFigure.paint(graphics);
+ drawer.dispose();
+ getFigure().setBounds(clientBounds);
+ ((ImageFigure)getFigure()).setImage(image);
+ getFigure().invalidate();
+ getFigure().repaint();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#activate()
+ */
+ public void activate() {
+ getCastedModel().getFigure().getUpdateManager().addUpdateListener(this);
+ super.activate();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#deactivate()
+ */
+ public void deactivate() {
+ getCastedModel().getFigure().getUpdateManager().removeUpdateListener(this);
+ super.deactivate();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#unregisterVisuals()
+ */
+ protected void unregisterVisuals() {
+ if (image != null && !image.isDisposed()) {
+ image.dispose();
+ image = null;
+ }
+ super.unregisterVisuals();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.UpdateListener#notifyPainting(org.eclipse.draw2d.geometry.Rectangle, java.util.Map)
+ */
+ public void notifyPainting(Rectangle damage, Map dirtyRegions) {
+ IFigure clientFigure = ((GraphicalEditPart)((RootEditPart)getCastedModel()).getContents()).getFigure();
+ Rectangle clientBounds = getBoundingArea();
+ if (clientBounds.isEmpty()) return;
+ if (image == null) {
+ refreshVisuals();
+ return;
+ }
+ Rectangle imageBounds = new Rectangle(image.getBounds().x, image.getBounds().y, image.getBounds().width, image.getBounds().height);
+ if (!imageBounds.getSize().equals(clientBounds.getSize())) {
+ refreshVisuals();
+ return;
+ }
+ List clientChildren = clientFigure.getChildren();
+ Iterator keys = clientChildren.iterator();
+
+ Rectangle region = (Rectangle) dirtyRegions.get(clientFigure);
+ List dirtyFigures = new LinkedList();
+ if (region != null) {
+ dirtyFigures.add(clientFigure);
+ }
+ while (keys.hasNext()) {
+ IFigure fig = (IFigure) keys.next();
+ region = (Rectangle) dirtyRegions.get(fig);
+ if (region != null) {
+ dirtyFigures.add(fig);
+ }
+ }
+ if (dirtyFigures.size() > clientChildren.size()/2) {
+ refreshVisuals();
+ return;
+ }
+ calculateDirtyArea(clientFigure, dirtyFigures, dirtyRegions);
+ }
+
+ /**
+ * @return
+ */
+ private Rectangle getBoundingArea() {
+ IFigure clientFigure = ((GraphicalEditPart)((RootEditPart)getCastedModel()).getContents()).getFigure();
+ //Dimension logicalSize = part.getFigure().getSize();
+ Rectangle boundingArea = null;//new Rectangle(new Point(0,0), logicalSize);
+ for (Iterator i = clientFigure.getChildren().iterator(); i.hasNext();) {
+ IFigure child = (IFigure) i.next();
+ if (boundingArea == null) {
+ boundingArea = child.getBounds().getCopy();
+ } else {
+ boundingArea.union(child.getBounds());
+ }
+ }
+ if (boundingArea == null) {
+ boundingArea = new Rectangle();
+ }
+// boundingArea.width += Math.abs(boundingArea.x);
+// boundingArea.height += Math.abs(boundingArea.y);
+// boundingArea.x = 0;
+// boundingArea.y = 0;
+ return boundingArea;
+ }
+
+ /**
+ * @param dirtyFigures
+ * @param dirtyRegions
+ * @return
+ */
+ private Rectangle calculateDirtyArea(IFigure clientFigure, List dirtyFigures, Map dirtyRegions) {
+ if (image == null) return null;
+// if (region == null)
+// return null;
+ GC drawer = new GC(image);
+ Graphics graphics = new SWTGraphics(drawer);
+ graphics.setBackgroundColor(clientFigure.getBackgroundColor());
+ Rectangle clientbounds = getBoundingArea();
+ graphics.translate(-clientbounds.x, -clientbounds.y);
+ for (Iterator dirty = dirtyFigures.iterator(); dirty.hasNext();) {
+ IFigure fig = (IFigure) dirty.next();
+ Rectangle r = ((Rectangle) dirtyRegions.get(fig)).getCopy();
+// r.x -= fig.getInsets().left;
+// r.y -= fig.getInsets().top;
+// r.height += fig.getInsets().bottom;
+// r.width += fig.getInsets().right;
+
+ //fig.getParent().translateToAbsolute(r);
+ r = fig.getBounds().getCopy();
+ //r.expand(fig.getInsets());
+ graphics.fillRectangle(r);
+ fig.paint(graphics);
+
+
+// intersectingSet.add(fig);
+// region.union(r);
+// for (Iterator intersectors = childSet.iterator(); intersectors.hasNext();) {
+// IFigure intersect = (IFigure) intersectors.next();
+// if (intersect.getBounds().intersects(region)) {
+// region.union(intersect.getBounds());
+// intersectingSet.add(intersect);
+// intersectors.remove();
+// }
+// }
+ }
+// graphics.fillRectangle(region);
+// for (Iterator figs = intersectingSet.iterator(); figs.hasNext();) {
+// ((IFigure)figs.next()).paint(graphics);
+// }
+ drawer.dispose();
+ getFigure().repaint();
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.UpdateListener#notifyValidating()
+ */
+ public void notifyValidating() {
+ // TODO Auto-generated method stub
+
+ }
+
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewImageEditPartFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewImageEditPartFactory.java
new file mode 100644
index 0000000..1b44c4b
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewImageEditPartFactory.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphviewer.overview.parts;
+
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.EditPartFactory;
+import org.eclipse.gef.EditPartViewer;
+import org.eclipse.gef.GraphicalEditPart;
+
+
+/**
+ * Creates the edit parts associated with the different models.
+ * @author Chris Callendar
+ */
+public class OverviewImageEditPartFactory implements EditPartFactory {
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.EditPartFactory#createEditPart(org.eclipse.gef.EditPart, java.lang.Object)
+ */
+ public EditPart createEditPart(EditPart context, Object model) {
+ EditPart editPart = null;
+ if (model instanceof EditPartViewer) {
+ editPart = new OverviewEditPart();
+ } else if (model instanceof GraphicalEditPart) {
+ editPart = new OverviewImageEditPart();
+ }
+ editPart.setModel(model);
+
+ return editPart;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewNodeEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewNodeEditPart.java
new file mode 100644
index 0000000..ffc6efc
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewNodeEditPart.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphviewer.overview.parts;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+import org.eclipse.draw2d.FigureListener;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.RectangleFigure;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
+
+/**
+ * A simple edit part that displays a rectangle representing a node.
+ * @author Del Myers
+ * @deprecated This class is going to be removed in future versions.
+ */
+public class OverviewNodeEditPart extends AbstractGraphicalEditPart implements FigureListener, PropertyChangeListener {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
+ */
+ protected IFigure createFigure() {
+ Class clazz = getCastedModel().getClass();
+ InvocationHandler handler = new InvocationHandler() {
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return method.invoke(proxy, args);
+ }
+ };
+ try {
+ if (getCastedModel() instanceof AbstractGraphicalEditPart) {
+ Method setModel = clazz.getMethod("setModel", new Class[] {Object.class});
+ Object instance = clazz.newInstance();
+ setModel.invoke(instance, new Object[]{getCastedModel().getModel()});
+ Method method = AbstractGraphicalEditPart.class.getDeclaredMethod("getFigure", new Class[0]);
+ Object result = handler.invoke(instance, method, new Object[0]);
+ return (IFigure)result;
+ }
+ } catch (Throwable e) {
+ //do nothing, just create a rectangle.
+ }
+ return new RectangleFigure();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#activate()
+ */
+ public void activate() {
+ getCastedModel().getFigure().addFigureListener(this);
+ getCastedModel().getFigure().addPropertyChangeListener(this);
+ super.activate();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#deactivate()
+ */
+ public void deactivate() {
+ getCastedModel().getFigure().removeFigureListener(this);
+ getCastedModel().getFigure().removePropertyChangeListener(this);
+ super.deactivate();
+ }
+
+ //@tag zest.overview.todo : the model should actually be a proxy edit part that we can listen to figure changes on, that way our figures could look exactly the same.
+ private GraphicalEditPart getCastedModel() {
+ return (GraphicalEditPart)getModel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
+ */
+ protected void createEditPolicies() {
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#refreshVisuals()
+ */
+ protected void refreshVisuals() {
+ super.refreshVisuals();
+ Rectangle bounds = getCastedModel().getFigure().getBounds().getCopy();
+ getFigure().setForegroundColor(getCastedModel().getFigure().getForegroundColor());
+ getFigure().setBackgroundColor(getCastedModel().getFigure().getBackgroundColor());
+ getFigure().getParent().setConstraint(getFigure(), bounds);
+ getParent().refresh();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.FigureListener#figureMoved(org.eclipse.draw2d.IFigure)
+ */
+ public void figureMoved(IFigure source) {
+ refreshVisuals();
+ }
+
+ /* (non-Javadoc)
+ * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent evt) {
+ refreshVisuals();
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewRootEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewRootEditPart.java
new file mode 100644
index 0000000..d99710f
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/OverviewRootEditPart.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphviewer.overview.parts;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.RangeModel;
+import org.eclipse.draw2d.StackLayout;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.gef.DragTracker;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.Request;
+import org.eclipse.gef.editparts.ScalableRootEditPart;
+import org.eclipse.mylar.zest.core.internal.graphviewer.parts.tools.OverviewRectangleTool;
+
+/**
+ * A simple root edit part that allows the viewport to be sized down.
+ * @author Del Myers
+ *
+ */
+public class OverviewRootEditPart extends ScalableRootEditPart {
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.ScalableRootEditPart#createFigure()
+ */
+ protected IFigure createFigure() {
+ final Viewport figure = (Viewport) super.createFigure();
+ figure.setContentsTracksHeight(true);
+ figure.setContentsTracksWidth(true);
+ figure.getContents().setLayoutManager(new StackLayout(){
+ public Dimension getMinimumSize(IFigure container, int w, int h) {
+ return new Dimension(w,h);
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.StackLayout#layout(org.eclipse.draw2d.IFigure)
+ */
+ public void layout(IFigure parent) {
+ List children = parent.getChildren();
+ RangeModel vertical = figure.getVerticalRangeModel();
+ RangeModel horizontal = figure.getHorizontalRangeModel();
+ for (Iterator i = children.iterator(); i.hasNext();) {
+ IFigure child = (IFigure) i.next();
+ child.setBounds(new Rectangle(0,0,horizontal.getMaximum(), vertical.getMaximum()));
+
+ }
+ }
+ });
+ return figure;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.ScalableRootEditPart#getDragTracker(org.eclipse.gef.Request)
+ */
+ public DragTracker getDragTracker(Request req) {
+ return new OverviewRectangleTool();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#getTargetEditPart(org.eclipse.gef.Request)
+ */
+ public EditPart getTargetEditPart(Request request) {
+ return this;
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/ScalingContainerFigure.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/ScalingContainerFigure.java
new file mode 100644
index 0000000..05832a0
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/overview/parts/ScalingContainerFigure.java
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphviewer.overview.parts;
+
+import java.util.List;
+
+import org.eclipse.draw2d.Figure;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.PrecisionDimension;
+import org.eclipse.draw2d.geometry.PrecisionPoint;
+import org.eclipse.draw2d.geometry.PrecisionRectangle;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.draw2d.geometry.Translatable;
+import org.eclipse.draw2d.text.CaretInfo;
+
+/**
+ * A figure that scales all of its children to always be within its bounds.
+ * @author Del Myers
+ *
+ */
+public class ScalingContainerFigure extends Figure {
+ private static final int DEFAULT_SIZE = 1000; //the "default" size used for scaling.
+ private Rectangle logicalBounds;
+
+ /**
+ *
+ */
+ public ScalingContainerFigure() {
+ setLogicalBounds(new Rectangle(0,0,DEFAULT_SIZE, DEFAULT_SIZE));
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#useLocalCoordinates()
+ */
+ protected boolean useLocalCoordinates() {
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#isCoordinateSystem()
+ */
+ public boolean isCoordinateSystem() {
+ return true;
+ }
+
+ public double getWidthScale() {
+ Dimension size = getSize();
+ Dimension logicalSize = getLogicalSize();
+ double widthScale = size.width/(double)logicalSize.width;
+ return widthScale;
+ }
+
+ public double getHeightScale() {
+ Dimension size = getSize();
+ Dimension logicalSize = getLogicalSize();
+ double heightScale = size.height/(double)logicalSize.height;
+ return heightScale;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#translateFromParent(org.eclipse.draw2d.geometry.Translatable)
+ */
+ public void translateFromParent(Translatable t) {
+ super.translateFromParent(t);
+ Dimension size = getSize();
+ Dimension logicalSize = getLogicalSize();
+ double widthScale = size.width/(double)logicalSize.width;
+ double heightScale = size.height/(double)logicalSize.height;
+
+
+ if ( t instanceof PrecisionRectangle ) {
+ PrecisionRectangle r = (PrecisionRectangle)t;
+ r.preciseX *= 1/widthScale;
+ r.preciseY *= 1/heightScale;
+ r.preciseWidth *= 1/widthScale;
+ r.preciseHeight *= 1/heightScale;
+ r.updateInts();
+ }
+ else if ( t instanceof Rectangle ) {
+ Rectangle r = (Rectangle)t;
+ r.scale(1/widthScale, 1/heightScale);
+ }
+ else if ( t instanceof CaretInfo ) {
+ CaretInfo c = (CaretInfo)t;
+ c.performScale(1/heightScale);
+ }
+ else if ( t instanceof PrecisionDimension ) {
+ PrecisionDimension d = (PrecisionDimension)t;
+ d.preciseWidth *= 1/widthScale;
+ d.preciseHeight *= 1/heightScale;
+ d.updateInts();
+ }
+ else if ( t instanceof Dimension ) {
+ Dimension d = (Dimension) t;
+ d.scale(1/widthScale, 1/heightScale);
+ }
+ else if ( t instanceof PrecisionPoint ) {
+ PrecisionPoint p = (PrecisionPoint) t;
+ p.preciseX *= 1/widthScale;
+ p.preciseY *= 1/heightScale;
+ p.updateInts();
+ }
+ else if ( t instanceof Point ) {
+ Point p = (Point)t;
+ p.scale(1/widthScale, 1/heightScale);
+ }
+ else if ( t instanceof PointList ) {
+ throw new RuntimeException("PointList not supported in AspectRatioScale");
+ }
+ else {
+ throw new RuntimeException( t.toString() + " not supported in AspectRatioScale");
+ }
+ t.performTranslate((int)(getLogicalReference().x), (int)(getLogicalReference().y));
+ //t.performScale(1/widthScale);
+ }
+
+ /**
+ * Sets the logical size to the given width and heigth. This will be used
+ * to determine how children should be scaled.
+ * @param w the logical width.
+ * @param h the logical height.
+ */
+ public final void setLogicalSize(int w, int h) {
+ setLogicalSize(new Dimension(w, h));
+ }
+
+ /**
+ * Sets the logical size to the given dimension. This will be used to determine
+ * how the children should be scaled.
+ * @param size the logical size.
+ */
+ public void setLogicalSize(Dimension size) {
+ setLogicalBounds(new Rectangle(new Point(0,0), size));
+ invalidate();
+ }
+
+ /**
+ * Sets the logical size to the dimensions of the bounds, and allows for translation
+ * based on the position of the given rectangle. The top-left corner of the rectangle
+ * will be translated to 0,0 in this scaling container.
+ * @param bounds
+ */
+ public void setLogicalBounds(Rectangle bounds) {
+ this.logicalBounds = bounds.getCopy();
+ invalidate();
+ }
+
+ /**
+ * Returns the top-left reference point for the bounds of the figures contained within
+ * this container.
+ * @return the logical reference.
+ */
+ public Point getLogicalReference() {
+ return logicalBounds.getTopLeft();
+ }
+
+ /**
+ * @return the logicalSize
+ */
+ public final Dimension getLogicalSize() {
+ return logicalBounds.getSize();
+ }
+
+ /**
+ * @return the logicalBounds
+ */
+ public Rectangle getLogicalBounds() {
+ return logicalBounds;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle)
+ */
+ public void setBounds(Rectangle rect) {
+ super.setBounds(rect);
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#paintClientArea(org.eclipse.draw2d.Graphics)
+ */
+ protected void paintClientArea(Graphics graphics) {
+ List children = getChildren();
+ if (children.isEmpty())
+ return;
+
+ boolean optimizeClip = getBorder() == null || getBorder().isOpaque();
+
+ if (useLocalCoordinates()) {
+ graphics.translate(
+ getBounds().x + getInsets().left,
+ getBounds().y + getInsets().top);
+ if (isCoordinateSystem()) {
+ graphics.scale((float)getWidthScale(), (float)getHeightScale());
+ graphics.translate(getLogicalReference().getNegated());
+ }
+ if (!optimizeClip)
+ graphics.clipRect(getClientArea(new Rectangle()));
+ graphics.pushState();
+ paintChildren(graphics);
+ graphics.popState();
+ graphics.restoreState();
+ } else {
+ if (optimizeClip)
+ paintChildren(graphics);
+ else {
+ graphics.clipRect(getClientArea(new Rectangle()));
+ graphics.pushState();
+ paintChildren(graphics);
+ graphics.popState();
+ graphics.restoreState();
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.Figure#translateToParent(org.eclipse.draw2d.geometry.Translatable)
+ */
+ public void translateToParent(Translatable t) {
+ Dimension size = getBounds().getSize();
+ Dimension logicalSize = getLogicalSize();
+ double widthScale = size.width/(double)logicalSize.width;
+ double heightScale = size.height/(double)logicalSize.height;
+ t.performTranslate(-getLogicalReference().x, -getLogicalReference().y);
+
+ if ( t instanceof PrecisionRectangle ) {
+ PrecisionRectangle r = (PrecisionRectangle)t;
+ r.preciseX *= widthScale;
+ r.preciseY *= heightScale;
+ r.preciseWidth *= widthScale;
+ r.preciseHeight *= heightScale;
+ r.updateInts();
+ }
+ else if ( t instanceof Rectangle ) {
+ Rectangle r = (Rectangle)t;
+ //r.performScale(widthScale);
+ r.scale(widthScale, heightScale);
+ }
+ else if ( t instanceof CaretInfo ) {
+ CaretInfo c = (CaretInfo)t;
+ c.performScale(heightScale);
+ }
+ else if ( t instanceof PrecisionDimension ) {
+ PrecisionDimension d = (PrecisionDimension)t;
+ d.preciseWidth *= widthScale;
+ d.preciseHeight *= heightScale;
+ d.updateInts();
+ }
+ else if ( t instanceof Dimension ) {
+ Dimension d = (Dimension) t;
+ d.scale(widthScale, heightScale);
+ }
+ else if ( t instanceof PrecisionPoint ) {
+ PrecisionPoint p = (PrecisionPoint) t;
+ p.preciseX *= widthScale;
+ p.preciseY *= heightScale;
+ p.updateInts();
+ }
+ else if ( t instanceof Point ) {
+ Point p = (Point)t;
+ p.scale(widthScale, heightScale);
+ }
+ else if ( t instanceof PointList ) {
+ throw new RuntimeException("PointList not supported in AspectRatioScale");
+ }
+ else {
+ throw new RuntimeException( t.toString() + " not supported in AspectRatioScale");
+ }
+
+
+ super.translateToParent(t);
+
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphConnectionEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphConnectionEditPart.java
new file mode 100644
index 0000000..b442959
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphConnectionEditPart.java
@@ -0,0 +1,400 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphviewer.parts;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.draw2d.Connection;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Label;
+import org.eclipse.draw2d.PolygonDecoration;
+import org.eclipse.draw2d.PolylineConnection;
+import org.eclipse.draw2d.PolylineDecoration;
+import org.eclipse.draw2d.RotatableDecoration;
+import org.eclipse.draw2d.Shape;
+import org.eclipse.gef.DragTracker;
+import org.eclipse.gef.EditPolicy;
+import org.eclipse.gef.Request;
+import org.eclipse.gef.editparts.AbstractConnectionEditPart;
+import org.eclipse.mylar.zest.core.ZestStyles;
+import org.eclipse.mylar.zest.core.internal.gefx.AligningBendpointLocator;
+import org.eclipse.mylar.zest.core.internal.gefx.BezierConnection;
+import org.eclipse.mylar.zest.core.internal.gefx.GraphRootEditPart;
+import org.eclipse.mylar.zest.core.internal.gefx.PolylineArcConnection;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphItem;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelConnection;
+import org.eclipse.mylar.zest.core.internal.graphviewer.policies.HighlightConnectionEndpointEditPolicy;
+import org.eclipse.mylar.zest.core.internal.viewers.trackers.SingleSelectionTracker;
+
+
+/**
+ * @author Chris Callendar
+ */
+public class GraphConnectionEditPart extends AbstractConnectionEditPart implements PropertyChangeListener {
+ private RotatableDecoration dec;
+
+
+ /**
+ * The constraint placed on the label for its alignment.
+ */
+ //@tag zest.bug.160368-ConnectionAlign.fix
+ /**
+ *
+ */
+ public GraphConnectionEditPart() {
+ super();
+ }
+
+
+
+
+ /**
+ * Upon activation, attach to the model element as a property change listener.
+ */
+ public void activate() {
+ if (!isActive()) {
+ super.activate();
+ ((IGraphItem)getModel()).addPropertyChangeListener(this);
+ }
+ }
+
+
+ /**
+ * Upon deactivation, detach from the model element as a property change listener.
+ */
+ public void deactivate() {
+ if (isActive()) {
+ super.deactivate();
+ ((IGraphItem) getModel()).removePropertyChangeListener(this);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractConnectionEditPart#removeNotify()
+ */
+ public void removeNotify() {
+ //disconnect
+
+ IFigure edge = getFigure();
+ IFigure layer = getLayer(CONNECTION_LAYER);
+ if (layer != null) {
+ if (edge.getParent() != layer) {
+ edge.getParent().remove(edge);
+ layer.add(edge);
+ }
+ }
+ super.removeNotify();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
+ */
+ protected void createEditPolicies() {
+ installEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE, new HighlightConnectionEndpointEditPolicy());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
+ */
+ protected IFigure createFigure() {
+ Connection connection;
+ IGraphModelConnection model = getCastedModel();
+ int connectionStyle = model.getConnectionStyle();
+
+ //styles should have been set by the GraphItemStyler by this point.
+ if (model.getSource() == model.getDestination()) {
+ //@tag zest(bug(152180-SelfLoops)) : create an arc connection, despite the styles that have been set.
+ //allow for a self-loop.
+ //@tag zest(bug(154391-ArcEnds(fix))) :use a polyline arc connection
+ connection = new PolylineArcConnection();
+ if (model.getCurveDepth() <= 0) {
+ //it has to have a curve.
+ ((PolylineArcConnection)connection).setDepth(10);
+ } else {
+ ((PolylineArcConnection)connection).setDepth(model.getCurveDepth());
+ }
+ } else if (ZestStyles.checkStyle(connectionStyle, ZestStyles.CONNECTIONS_CURVED)) {
+ connection = new PolylineArcConnection();
+ ((PolylineArcConnection)connection).setDepth(getCastedModel().getCurveDepth());
+ } else if (ZestStyles.checkStyle(connectionStyle, ZestStyles.CONNECTIONS_BEZIER)) {
+ connection = new BezierConnection(model.getStartAngle(), model.getStartLength(), model.getEndAngle(), model.getEndLength());
+ } else {
+ connection = (PolylineConnection)super.createFigure();
+ }
+ connection.setForegroundColor(getCastedModel().getLineColor());
+ if (connection instanceof Shape) {
+ ((Shape)connection).setLineWidth(getCastedModel().getLineWidth());
+ ((Shape)connection).setLineStyle(getCastedModel().getLineStyle());
+ }
+
+ AligningBendpointLocator labelLocator = new AligningBendpointLocator(connection);
+ if ( getCastedModel().getText() != null ||
+ getCastedModel().getImage() != null ) {
+ Label l = new Label(getCastedModel().getText(), getCastedModel().getImage());
+ l.setFont(getCastedModel().getFont());
+ connection.add(l,labelLocator);
+ }
+
+ return connection;
+ }
+
+ /**
+ * Gets the drag tracker for the edge. This means only 1 edge can be selected
+ * @tag Selection : this controls single edge selection
+ */
+ public DragTracker getDragTracker(Request req) {
+ return new SingleSelectionTracker(this);
+ }
+
+ /**
+ *
+ */
+ public void highlightEdge() {
+ IFigure thisEdge = getFigure();
+ IFigure layer = getLayer(CONNECTION_LAYER);
+ IFigure feedbackLayer = getLayer(GraphRootEditPart.CONNECTION_FEEDBACK_LAYER );
+ if (thisEdge.getParent() == layer) {
+ layer.remove(thisEdge);
+ feedbackLayer.add(thisEdge);
+ }
+
+ }
+
+ /**
+ *
+ */
+ public void unHighlightEdge() {
+ IFigure thisEdge = getFigure();
+ IFigure layer = getLayer(CONNECTION_LAYER);
+ IFigure feedbackLayer = getLayer(GraphRootEditPart.CONNECTION_FEEDBACK_LAYER );
+ if (thisEdge.getParent() == feedbackLayer) {
+ feedbackLayer.remove(thisEdge);
+ layer.add(thisEdge);
+ }
+
+ }
+
+ /**
+ * Gets the model casted into a GraphModelConnection
+ * @return the model casted into a GraphModelConnection
+ */
+ protected IGraphModelConnection getCastedModel() {
+ return (IGraphModelConnection)getModel();
+ }
+
+ /* (non-Javadoc)
+ * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+
+ String property = event.getPropertyName();
+ IFigure figure = getFigure();
+ if (IGraphModelConnection.HIGHLIGHT_PROP.equals(property)) {
+ //@tag unreported(EdgeHighlight) : respond to model highlight changes.
+ highlightEdge();
+ } else if (IGraphModelConnection.UNHIGHLIGHT_PROP.equals(property)) {
+// @tag unreported(EdgeHighlight) : respond to model highlight changes.
+ unHighlightEdge();
+ } else if (IGraphModelConnection.LINECOLOR_PROP.equals(property)) {
+ figure.setForegroundColor(getCastedModel().getLineColor());
+ } else if (IGraphModelConnection.LINEWIDTH_PROP.equals(property)) {
+ if (figure instanceof Shape)
+ ((Shape)figure).setLineWidth(getCastedModel().getLineWidth());
+ RotatableDecoration dec = getDecoration();
+ if (dec instanceof PolylineDecoration) {
+ ((PolylineDecoration)dec).setScale(getCastedModel().getLineWidth()+2, getCastedModel().getLineWidth()+2);
+ } else if (dec instanceof PolygonDecoration) {
+ ((PolygonDecoration)dec).setScale(getCastedModel().getLineWidth()+2, getCastedModel().getLineWidth()+2);
+ } else if (dec instanceof Shape) {
+ ((Shape)dec).setLineWidth(getCastedModel().getLineWidth());
+ }
+ } else if (IGraphModelConnection.LINESTYLE_PROP.equals(property)) {
+ if (figure instanceof Shape)
+ ((Shape)figure).setLineStyle(getCastedModel().getLineStyle());
+ } else if (IGraphModelConnection.DIRECTED_EDGE_PROP.equals( property )) {
+ boolean directed = isDirected();
+ if (figure instanceof PolylineConnection) {
+ if (directed) {
+ ((PolylineConnection)figure).setTargetDecoration(new PolygonDecoration());
+ }
+ else {
+ ((PolylineConnection)figure).setTargetDecoration( null );
+ }
+ } else if (figure instanceof PolylineArcConnection) {
+ if (directed) {
+ ((PolylineArcConnection)getFigure()).setTargetDecoration(new PolygonDecoration());
+ }
+ else {
+ ((PolylineArcConnection)getFigure()).setTargetDecoration( null );
+ }
+ } else if (figure instanceof BezierConnection) {
+ if (directed) {
+ ((BezierConnection)figure).setTargetDecoration(new PolygonDecoration());
+ }
+ else {
+ ((BezierConnection)figure).setTargetDecoration( null );
+ }
+ }
+ } else if (IGraphItem.VISIBLE_PROP.equals(property)) {
+ getFigure().setVisible(((Boolean)event.getNewValue()).booleanValue());
+ } else if (IGraphModelConnection.CURVE_PROP.equals(property)) {
+ if (figure instanceof PolylineArcConnection) {
+ ((PolylineArcConnection)figure).setDepth(getCastedModel().getCurveDepth());
+ } else if (figure instanceof BezierConnection) {
+ BezierConnection bezier = (BezierConnection) figure;
+ IGraphModelConnection conn = getCastedModel();
+ bezier.setStartAngle((int) Math.round(conn.getStartLength()));
+ bezier.setEndAngle((int) Math.round(conn.getEndAngle()));
+ bezier.setStartLength(conn.getStartLength());
+ bezier.setEndLength(conn.getEndLength());
+ }
+ }
+ }
+
+
+ /**
+ * @return
+ */
+ private boolean isDirected() {
+ int connectionStyle = getCastedModel().getConnectionStyle();
+ return ZestStyles.checkStyle(connectionStyle, ZestStyles.CONNECTIONS_DIRECTED);
+
+ }
+
+ protected RotatableDecoration createDecoration() {
+ RotatableDecoration dec = new PolygonDecoration();
+ if (ZestStyles.checkStyle(getCastedModel().getConnectionStyle(), ZestStyles.CONNECTIONS_OPEN)) {
+ dec = new PolylineDecoration();
+ ((PolylineDecoration)dec).setFill(false);
+ ((PolylineDecoration)dec).setOutline(true);
+ }
+ return dec;
+ }
+
+ public final RotatableDecoration getDecoration() {
+ if (dec == null) {
+ dec = createDecoration();
+ getFigure().add(dec);
+ }
+ return dec;
+ }
+
+ protected void refreshVisuals() {
+ super.refreshVisuals();
+ IFigure figure = getFigure();
+ //@tag zest.bug.167128-EdgeVisibility : make the figue change visibility with model.
+ figure.setVisible(getCastedModel().isVisible());
+ figure.setForegroundColor(getCastedModel().getLineColor());
+ if (figure instanceof Shape) {
+ ((Shape)figure).setLineWidth(getCastedModel().getLineWidth());
+ ((Shape)figure).setLineStyle(getCastedModel().getLineStyle());
+ }
+ //@tag zest(bug(154595(fix))) : change the arrow size based on the line width.
+ boolean directed = isDirected();
+ RotatableDecoration dec = getDecoration();
+ if (dec instanceof PolylineDecoration) {
+ ((PolylineDecoration)dec).setScale(getCastedModel().getLineWidth()+2, getCastedModel().getLineWidth()+2);
+ } else if (dec instanceof PolygonDecoration) {
+ ((PolygonDecoration)dec).setScale(getCastedModel().getLineWidth()+2, getCastedModel().getLineWidth()+2);
+ } else if (dec instanceof Shape) {
+ ((Shape)dec).setLineWidth(getCastedModel().getLineWidth());
+ }
+ if (figure instanceof PolylineArcConnection) {
+ if (directed) {
+ ((PolylineArcConnection)getFigure()).setTargetDecoration(dec);
+ }
+ else {
+ ((PolylineArcConnection)getFigure()).setTargetDecoration( null );
+ }
+ ((PolylineArcConnection)figure).setDepth(getCastedModel().getCurveDepth());
+ } else if (figure instanceof BezierConnection) {
+ if (directed) {
+ ((BezierConnection)figure).setTargetDecoration(dec);
+ }
+ else {
+ ((BezierConnection)figure).setTargetDecoration( null );
+ }
+ } else if (figure instanceof PolylineConnection) {
+ if (directed) {
+ ((PolylineConnection)getFigure()).setTargetDecoration(dec);
+ }
+ else {
+ ((PolylineConnection)getFigure()).setTargetDecoration( null );
+ }
+ }
+ refreshLineStyle();
+ refreshLabelLocation();
+
+ }
+ /**
+ * Refresh the alignment of labels.
+ */
+ private void refreshLabelLocation() {
+ List children = getFigure().getChildren();
+ for (Iterator i = children.iterator(); i.hasNext();) {
+ IFigure child = (IFigure) i.next();
+ int style = getCastedModel().getConnectionStyle();
+ AligningBendpointLocator labelLocator = null;
+ try {
+ Object constraint = getFigure().getLayoutManager().getConstraint(child);
+ if (constraint instanceof AligningBendpointLocator) {
+ labelLocator = (AligningBendpointLocator)constraint;
+ }
+ } catch (Exception e) {
+ return;
+ }
+ if (labelLocator == null) return;
+ if (ZestStyles.checkStyle(style, ZestStyles.CONNECTIONS_VALIGN_TOP)) {
+ labelLocator.setVerticalAlginment(AligningBendpointLocator.ABOVE);
+ } else if (ZestStyles.checkStyle(style, ZestStyles.CONNECTIONS_VALIGN_BOTTOM)) {
+ labelLocator.setVerticalAlginment(AligningBendpointLocator.BELOW);
+ } else {
+ labelLocator.setVerticalAlginment(AligningBendpointLocator.MIDDLE);
+ }
+ if (ZestStyles.checkStyle(style, ZestStyles.CONNECTIONS_HALIGN_START)) {
+ labelLocator.setHorizontalAlignment(AligningBendpointLocator.BEGINNING);
+ } else if (ZestStyles.checkStyle(style, ZestStyles.CONNECTIONS_HALIGN_END)) {
+ labelLocator.setHorizontalAlignment(AligningBendpointLocator.END);
+ } else if (ZestStyles.checkStyle(style, ZestStyles.CONNECTIONS_HALIGN_CENTER_START)) {
+ labelLocator.setHorizontalAlignment(AligningBendpointLocator.CENTER_BEGINNING);
+ } else if (ZestStyles.checkStyle(style, ZestStyles.CONNECTIONS_HALIGN_CENTER_END)) {
+ labelLocator.setHorizontalAlignment(AligningBendpointLocator.CENTER_END);
+ } else {
+ labelLocator.setHorizontalAlignment(AligningBendpointLocator.CENTER);
+ }
+ }
+ }
+
+
+ /**
+ * Refresh the line style for dashes, dots, etc.
+ *
+ */
+ private void refreshLineStyle() {
+ IFigure figure = getFigure();
+ int style = getCastedModel().getLineStyle();
+ if (figure instanceof Shape) {
+ ((Shape)figure).setLineStyle(style);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#unregister()
+ */
+ protected void unregister() {
+ // TODO Auto-generated method stub
+ super.unregister();
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphEditPart.java
new file mode 100644
index 0000000..e921f60
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphEditPart.java
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphviewer.parts;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.LayoutAnimator;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.gef.EditPolicy;
+import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
+import org.eclipse.gef.editparts.ZoomManager;
+import org.eclipse.gef.editpolicies.RootComponentEditPolicy;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphItem;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode;
+import org.eclipse.mylar.zest.core.internal.graphviewer.policies.GraphXYLayoutEditPolicy;
+import org.eclipse.mylar.zest.core.internal.viewers.figures.AspectRatioFreeformLayer;
+
+
+/**
+ * The EditPart associated with the GraphModel. The view creates a FreeformLayer figure.
+ *
+ * @author Chris Callendar
+ */
+public class GraphEditPart extends AbstractGraphicalEditPart implements PropertyChangeListener {
+
+ //@tag zest.bug.156286-Zooming.fix : add a zoom manager to the edit part.
+ ZoomManager zoomer;
+
+ public GraphEditPart() {
+ super();
+ }
+
+ /**
+ * Upon activation, attach to the model element as a property change listener.
+ */
+ public void activate() {
+ if (!isActive()) {
+ super.activate();
+ ((GraphItem) getModel()).addPropertyChangeListener(this);
+ }
+ }
+
+ /**
+ * Upon deactivation, detach from the model element as a property change listener.
+ */
+ public void deactivate() {
+ if (isActive()) {
+ super.deactivate();
+ ((GraphItem) getModel()).removePropertyChangeListener(this);
+ }
+ }
+
+ /**
+ * Sets the scale for the graph
+ * @param x
+ * @param y
+ */
+ public void setScale( double x, double y ) {
+ AspectRatioFreeformLayer aspectRatioFreeformLayer = (AspectRatioFreeformLayer) getFigure();
+ aspectRatioFreeformLayer.setScale( x, y );
+ //aspectRatioFreeformLayer.repaint();
+ getCastedModel().fireAllPropertyChange(IGraphModelNode.FORCE_REDRAW, null, null);
+ }
+
+ /**
+ * Gets the scale in the X Direction
+ * @return
+ */
+ public double getXScale() {
+ AspectRatioFreeformLayer aspectRatioFreeformLayer = (AspectRatioFreeformLayer) getFigure();
+ return aspectRatioFreeformLayer.getWidthScale();
+ }
+
+ /**
+ * Getes the scale in the Y Direction
+ * @return
+ */
+ public double getYScale() {
+ AspectRatioFreeformLayer aspectRatioFreeformLayer = (AspectRatioFreeformLayer) getFigure();
+ return aspectRatioFreeformLayer.getHeightScale();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
+ */
+ protected IFigure createFigure() {
+
+ AspectRatioFreeformLayer aspectRatioScaledFigure = new AspectRatioFreeformLayer("root");
+ aspectRatioScaledFigure.setScale(1.0, 1.0);
+ aspectRatioScaledFigure.addLayoutListener(LayoutAnimator.getDefault());
+ zoomer = new ZoomManager(aspectRatioScaledFigure, (Viewport)((GraphicalEditPart)getRoot()).getFigure());
+ return aspectRatioScaledFigure;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
+ */
+ protected void createEditPolicies() {
+ // disallows the removal of this edit part from its parent
+ installEditPolicy(EditPolicy.COMPONENT_ROLE, new RootComponentEditPolicy());
+ installEditPolicy(EditPolicy.LAYOUT_ROLE, new GraphXYLayoutEditPolicy());
+
+ }
+
+ /* (non-Javadoc)
+ * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent evt) {
+ String prop = evt.getPropertyName();
+ // these properties are fired when Nodes and connections are added into or removed from
+ // the LayoutDiagram instance and must cause a call of refreshChildren()
+ // to update the diagram's contents.
+ if (GraphModel.NODE_ADDED_PROP.equals(prop) || GraphModel.NODE_REMOVED_PROP.equals(prop)) {
+ refreshChildren();
+ } else if ( IGraphModelNode.HIGHLIGHT_PROP.equals( prop ) ) {
+ if (((Boolean)evt.getNewValue()).booleanValue()) {
+ setSelected(1);
+ } else {
+ setSelected(0);
+ }
+ }
+ }
+
+ private GraphModel getCastedModel() {
+ return (GraphModel)getModel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#getModelChildren()
+ */
+ protected List getModelChildren() {
+ return getCastedModel().getNodes();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#register()
+ */
+ protected void register() {
+ super.register();
+ //@tag zest.bug.156286-Scaling.fix : set a property so that this can be zoomed on.
+ getViewer().setProperty(ZoomManager.class.toString(), getZoomManager());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#unregister()
+ */
+ protected void unregister() {
+ super.unregister();
+// @tag zest.bug.156286-Scaling.fix : set a property so that this can be zoomed on.
+ getViewer().setProperty(ZoomManager.class.toString(), null);
+ }
+
+ //@tag zest.bug.156286-Scaling.fix : get the zoom manager
+ public ZoomManager getZoomManager() {
+ return zoomer;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphEditPartFactory.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphEditPartFactory.java
new file mode 100644
index 0000000..e7309bf
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphEditPartFactory.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphviewer.parts;
+
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.EditPartFactory;
+import org.eclipse.mylar.zest.core.internal.gefx.ZestRootEditPart;
+import org.eclipse.mylar.zest.core.internal.graphmodel.GraphModel;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelConnection;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode;
+
+
+/**
+ * Creates the edit parts associated with the different models.
+ * @author Chris Callendar
+ */
+public class GraphEditPartFactory implements EditPartFactory {
+
+ protected ZestRootEditPart graphRootEditPart = null;
+ public GraphEditPartFactory( ZestRootEditPart graphRootEditPart ) {
+ this.graphRootEditPart = graphRootEditPart;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.EditPartFactory#createEditPart(org.eclipse.gef.EditPart, java.lang.Object)
+ */
+ public EditPart createEditPart(EditPart context, Object model) {
+ EditPart editPart = null;
+ if (model instanceof IGraphModelNode) {
+ editPart = new GraphNodeEditPart();
+ } else if (model instanceof IGraphModelConnection) {
+ editPart = new GraphConnectionEditPart();
+ } else if (model instanceof GraphModel) {
+ editPart = new GraphEditPart();
+ graphRootEditPart.setModelRootEditPart( editPart );
+ } else {
+ throw new RuntimeException("Can't create part for model element: " +
+ ((model != null) ? model.getClass().getName() : "null"));
+ }
+ editPart.setModel(model);
+
+ return editPart;
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphNodeEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphNodeEditPart.java
new file mode 100644
index 0000000..52d0962
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/GraphNodeEditPart.java
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ * Copyright 2005, 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 org.eclipse.mylar.zest.core.internal.graphviewer.parts;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.draw2d.ChopboxAnchor;
+import org.eclipse.draw2d.ConnectionAnchor;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Label;
+import org.eclipse.draw2d.LayoutAnimator;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.gef.ConnectionEditPart;
+import org.eclipse.gef.DragTracker;
+import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.NodeEditPart;
+import org.eclipse.gef.Request;
+import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphItem;
+import org.eclipse.mylar.zest.core.internal.graphmodel.IGraphModelNode;
+import org.eclipse.mylar.zest.core.internal.viewers.figures.GraphLabel;
+import org.eclipse.mylar.zest.core.internal.viewers.figures.RoundedChopboxAnchor;
+import org.eclipse.mylar.zest.core.internal.viewers.trackers.SingleSelectionTracker;
+
+
+/**
+ * The edit part for the LayoutNode model.
+ * @author Chris Callendar
+ */
+public class GraphNodeEditPart extends AbstractGraphicalEditPart implements
+ PropertyChangeListener, NodeEditPart {
+ private Label toolTip;
+
+ protected ConnectionAnchor anchor;
+// @tag zest(bug(152180-SelfLoops)) : New anchor required so that the loops aren't hidden by the node.
+ protected ConnectionAnchor loopAnchor;
+ /**
+ * GraphNodeEditPart constructor.
+ */
+ public GraphNodeEditPart() {
+ super();
+ }
+
+ /**
+ * Upon activation, attach to the model element as a property change
+ * listener.
+ */
+ public void activate() {
+ if (!isActive()) {
+ super.activate();
+ ((IGraphItem) getModel()).addPropertyChangeListener(this);
+ }
+ }
+
+ /**
+ * Upon deactivation, detach from the model element as a property change
+ * listener.
+ */
+ public void deactivate() {
+ if (isActive()) {
+ super.deactivate();
+ ((IGraphItem) getModel()).removePropertyChangeListener(this);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
+ */
+ protected void createEditPolicies() {
+ // allow removal of the associated model element
+ // installEditPolicy(EditPolicy.COMPONENT_ROLE, new NodeComponentEditPolicy());
+
+ // allow the creation of connections and the reconnection of connections between nodes
+ //installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new GraphNodeEditPolicy());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
+ */
+ protected IFigure createFigure() {
+ IFigure f = createFigureForModel();
+ f.addLayoutListener(LayoutAnimator.getDefault());
+ return f;
+ }
+
+ /**
+ * Return a IFigure depending on the instance of the current model element.
+ * This allows this EditPart to be used for both sublasses of Shape.
+ */
+ protected IFigure createFigureForModel() {
+ IFigure figure;
+ if (getModel() instanceof IGraphModelNode) {
+ IGraphModelNode node = getCastedModel();
+ boolean cacheLabel = ((IGraphModelNode)getModel()).cacheLabel();
+ GraphLabel label = new GraphLabel(node.getText(), node.getImage(), cacheLabel);
+ label.setForegroundColor(node.getForegroundColor());
+ label.setBackgroundColor(node.getBackgroundColor());
+ label.setFont(node.getFont());
+ Dimension d = label.getSize();
+ node.setSizeInLayout(d.width, d.height);
+ figure = label;
+ toolTip = new Label();
+ toolTip.setText(node.getText());
+ figure.setToolTip(toolTip);
+ } else {
+ // if Shapes gets extended the conditions above must be updated
+ throw new IllegalArgumentException("Unexpected model when creating a figure");
+ }
+ return figure;
+ }
+
+ /**
+ * Gets a drag tracker which only allows one node to be selected at a time.
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#getDragTracker(org.eclipse.gef.Request)
+ */
+ public DragTracker getDragTracker(Request request) {
+ return new SingleSelectionTracker(this);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#performRequest(org.eclipse.gef.Request)
+ */
+ public void performRequest(Request req) {
+ if (REQ_OPEN.equals(req.getType())) {
+ // TODO handle double-click
+ }
+ super.performRequest(req);
+ }
+
+// @tag zest(bug(152180-SelfLoops)) : New anchor required so that the loops aren't hidden by the node.
+ protected ConnectionAnchor getLoopAnchor() {
+ if (loopAnchor == null) {
+ loopAnchor = new LoopAnchor(getFigure());
+ }
+ return loopAnchor;
+ }
+
+// @tag zest(bug(152180-SelfLoops)) : New anchor required so that the loops aren't hidden by the node.
+ protected ConnectionAnchor getDefaultConnectionAnchor() {
+ if (anchor == null) {
+ if (getModel() instanceof IGraphModelNode) {
+ IFigure figure = getFigure();
+ // use a rounded chopbox anchor for graph label figures (rounded corners)
+ if (figure instanceof GraphLabel) {
+ GraphLabel graphFigure = (GraphLabel) figure;
+ anchor = new RoundedChopboxAnchor(graphFigure, graphFigure.getArcWidth() / 2);
+ } else {
+ anchor = new ChopboxAnchor(figure);
+ }
+ } else {
+ throw new IllegalArgumentException("Unexpected model when creating an anchor");
+ }
+ }
+ return anchor;
+ }
+
+
+ /* (non-Javadoc)
+ * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent evt) {
+
+
+ String prop = evt.getPropertyName();
+ if ( IGraphModelNode.FORCE_REDRAW.equals(prop)) {
+ refreshVisuals();
+ refreshChildren();
+ refreshColors();
+ getCastedModel().highlight();
+ getCastedModel().unhighlight();
+ }
+ else if (IGraphModelNode.LOCATION_PROP.equals(prop) || IGraphModelNode.SIZE_PROP.equals(prop)) {
+ refreshVisuals();
+ } else if (IGraphModelNode.SOURCE_CONNECTIONS_PROP.equals(prop)) {
+ refreshSourceConnections();
+ } else if (IGraphModelNode.TARGET_CONNECTIONS_PROP.equals(prop)) {
+ refreshTargetConnections();
+ } else if (IGraphModelNode.HIGHLIGHT_PROP.equals(prop)) {
+ refreshColors();
+ } else if (IGraphModelNode.COLOR_BG_PROP.equals(prop)) {
+ refreshColors();
+ } else if (IGraphModelNode.COLOR_FG_PROP.equals(prop)) {
+ refreshColors();
+ }
+ else if (IGraphModelNode.COLOR_BD_PROP.equals(prop)) {
+ refreshColors();
+ }
+ else if ( IGraphModelNode.BRING_TO_FRONT.equals(prop) ) {
+ IFigure figure = getFigure();
+ IFigure parent = figure.getParent();
+ parent.remove(figure);
+ parent.add(figure);
+
+ } else if (IGraphItem.VISIBLE_PROP.equals(prop)) {
+ getFigure().setVisible(((Boolean)evt.getNewValue()).booleanValue());
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractEditPart#refreshVisuals()
+ */
+ protected void refreshVisuals() {
+ IGraphModelNode node = getCastedModel();
+ Point loc = node.getLocation();
+ Dimension size = node.getSize();
+ Rectangle bounds = new Rectangle(loc, size);
+ ((GraphicalEditPart)getParent()).setLayoutConstraint(this, getFigure(), bounds);
+ refreshColors();
+ }
+
+ /**
+ * Refreshes the figures colors from the model. This includes the border color and width.
+ */
+ protected void refreshColors() {
+ IFigure figure = getFigure();
+ IGraphModelNode model = getCastedModel();
+ figure.setForegroundColor(model.getForegroundColor());
+ figure.setBackgroundColor(model.getBackgroundColor());
+
+ if (figure instanceof GraphLabel) {
+ GraphLabel label = (GraphLabel) figure;
+ label.setBorderColor(model.getBorderColor());
+ label.setBorderWidth(model.getBorderWidth());
+ }
+ }
+
+ /**
+ * Returns the model casted into a GraphModelNode.
+ * @return the casted GraphModelNode model
+ */
+ private IGraphModelNode getCastedModel() {
+ return (IGraphModelNode)getModel();
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#getModelSourceConnections()
+ */
+ protected List getModelSourceConnections() {
+ if (getCastedModel() == null) return Collections.EMPTY_LIST;
+ return getCastedModel().getSourceConnections();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#getModelTargetConnections()
+ */
+ protected List getModelTargetConnections() {
+ if (getCastedModel() == null) return Collections.EMPTY_LIST;
+ return getCastedModel().getTargetConnections();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.NodeEditPart#getSourceConnectionAnchor(org.eclipse.gef.ConnectionEditPart)
+ */
+ public ConnectionAnchor getSourceConnectionAnchor(ConnectionEditPart connection) {
+// @tag zest(bug(152180-SelfLoops)) : New anchor required so that the loops aren't hidden by the node.
+ if (connection.getSource() == connection.getTarget()) {
+ return getLoopAnchor();
+ }
+ return getDefaultConnectionAnchor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.NodeEditPart#getTargetConnectionAnchor(org.eclipse.gef.ConnectionEditPart)
+ */
+ public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart connection) {
+// @tag zest(bug(152180-SelfLoops)) : New anchor required so that the loops aren't hidden by the node.
+ if (connection.getSource() == connection.getTarget()) {
+ return getLoopAnchor();
+ }
+ return getDefaultConnectionAnchor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.NodeEditPart#getSourceConnectionAnchor(org.eclipse.gef.Request)
+ */
+ public ConnectionAnchor getSourceConnectionAnchor(Request request) {
+ return getDefaultConnectionAnchor();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.NodeEditPart#getTargetConnectionAnchor(org.eclipse.gef.Request)
+ */
+ public ConnectionAnchor getTargetConnectionAnchor(Request request) {
+ return getDefaultConnectionAnchor();
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/LoopAnchor.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/LoopAnchor.java
new file mode 100644
index 0000000..6ecb31c
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/LoopAnchor.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphviewer.parts;
+
+import org.eclipse.draw2d.ChopboxAnchor;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.geometry.Point;
+
+public class LoopAnchor extends ChopboxAnchor {
+ public LoopAnchor(IFigure owner) {
+ super(owner);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.ChopboxAnchor#getReferencePoint()
+ */
+ public Point getReferencePoint() {
+ //modification to getReferencePoint. Returns
+ //a point on the outside of the owners box, rather than the
+ //center. Only usefull for self-loops.
+ if (getOwner() == null)
+ return null;
+ else {
+ Point ref = getOwner().getBounds().getCenter();
+ ref.y = getOwner().getBounds().y;
+ getOwner().translateToAbsolute(ref);
+ return ref;
+ }
+ }
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/ProxyConnectionEditPart.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/ProxyConnectionEditPart.java
new file mode 100644
index 0000000..a870b20
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/ProxyConnectionEditPart.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphviewer.parts;
+
+import org.eclipse.mylar.zest.core.internal.graphmodel.ProxyConnection;
+
+/**
+ * An edit part for creating proxy connections.
+ * @author Del Myers
+ *
+ */
+//@tag bug(153466-NoNestedClientSupply(fix))
+public final class ProxyConnectionEditPart extends GraphConnectionEditPart {
+
+ /**
+ *
+ */
+ public ProxyConnectionEditPart() {
+ super();
+ }
+
+
+ public ProxyConnection getProxyModel() {
+ return (ProxyConnection)getModel();
+ }
+
+}
diff --git a/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/tools/OverviewRectangleTool.java b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/tools/OverviewRectangleTool.java
new file mode 100644
index 0000000..92a4a4f
--- /dev/null
+++ b/visualization/plugins/org.eclipse.mylar.zest.core/src/org/eclipse/mylar/zest/core/internal/graphviewer/parts/tools/OverviewRectangleTool.java
@@ -0,0 +1,554 @@
+/*******************************************************************************
+ * Copyright 2005-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 org.eclipse.mylar.zest.core.internal.graphviewer.parts.tools;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.Cursors;
+import org.eclipse.draw2d.FigureListener;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.RectangleFigure;
+import org.eclipse.draw2d.Viewport;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.gef.DragTracker;
+import org.eclipse.gef.EditPartViewer;
+import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.RequestConstants;
+import org.eclipse.gef.editparts.ZoomListener;
+import org.eclipse.gef.editparts.ZoomManager;
+import org.eclipse.gef.tools.AbstractTool;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.MouseEvent;
+
+/**
+ * A tool that is used in the graph overview viewer that allows the user to select an area
+ * of the graph to zoom on.
+ * @author Del Myers
+ *
+ */
+public class OverviewRectangleTool extends AbstractTool implements DragTracker, ControlListener, ZoomListener, PropertyChangeListener, FigureListener {
+ private RectangleFigure focusFigure;
+ private int dragState;
+ private Rectangle startBounds;
+ private TargetShape target;
+ private FigureListener focusFigureListener;
+ private Rectangle referenceBounds;
+ private ZoomManager currentManager;
+
+ private static final int OUTSIDE = 0;
+ private static final int INSIDE = 1<<1;
+ private static final int NORTH = INSIDE | 1<<2;
+ private static final int EAST = INSIDE |1<<3;
+ private static final int SOUTH = INSIDE | 1<<4;
+ private static final int WEST = INSIDE | 1<<5;
+
+ private static final int NORTHWEST = INSIDE | NORTH | WEST;
+ private static final int NORTHEAST = INSIDE | NORTH | EAST;
+ private static final int SOUTHWEST = INSIDE | SOUTH | WEST;
+ private static final int SOUTHEAST = INSIDE | SOUTH | EAST;
+
+ private class FocusMoveListener implements FigureListener {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.draw2d.FigureListener#figureMoved(org.eclipse.draw2d.IFigure)
+ */
+ public void figureMoved(IFigure source) {
+ //move the target to the center of the figure.
+ setTargetCenter(source.getBounds().getCenter());
+ }
+
+ }
+
+ /**
+ *
+ */
+ public OverviewRectangleTool() {
+ setUnloadWhenFinished(false);
+ referenceBounds = new Rectangle();
+ //setEditDomain(new EditDomain());
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.tools.AbstractTool#handleDragStarted()
+ */
+ protected boolean handleDragStarted() {
+ setState(STATE_DRAG);
+ setDragState(getRelativeCursorLocation(getStartLocation()));
+ Rectangle bounds = getFeedBackFigure().getBounds().getCopy();
+ getFeedBackFigure().translateToAbsolute(bounds);
+ setDragStartBounds(bounds);
+ return true;
+ }
+
+ /**
+ * @param bounds
+ */
+ private void setDragStartBounds(Rectangle bounds) {
+ this.startBounds = bounds.getCopy();
+
+ }
+
+ private Rectangle getDragStartBounds() {
+ return startBounds;
+ }
+
+ private void setDragState(int state) {
+ this.dragState = state;
+ }
+
+ /**
+ * @return the dragState
+ */
+ private int getDragState() {
+ return dragState;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.tools.AbstractTool#handleDragInProgress()
+ */
+ protected boolean handleDragInProgress() {
+ setState(STATE_DRAG_IN_PROGRESS);
+ Dimension delta = getDragMoveDelta();
+ Point start = getStartLocation();
+ RectangleFigure feedBack = getFeedBackFigure();
+ boolean moving = false;
+ int state = getDragState();
+ Rectangle oldBounds = getDragStartBounds();
+ Rectangle newBounds = oldBounds.getCopy();
+ if (isNorth(state)) {
+ newBounds.y = oldBounds.y+delta.height;
+ newBounds.height = oldBounds.height-delta.height;
+ } else if (isSouth(state)) {
+ newBounds.height = oldBounds.height+delta.height;
+ }
+ if (isWest(state)) {
+ newBounds.x = oldBounds.x+delta.width;
+ newBounds.width = oldBounds.width-delta.width;
+ } else if (isEast(state)) {
+ newBounds.width = oldBounds.width+delta.width;
+ }
+ if (!(isWest(state) || isEast(state) || isNorth(state) || isSouth(state))) {
+ if (isInside(state)) {
+ moving = true;
+ newBounds.x = oldBounds.x+delta.width;
+ newBounds.y = oldBounds.y+delta.height;
+ } else {
+ //a new rectangle is drawn.
+ newBounds = new Rectangle(start, delta);
+ }
+ }
+ //adjust the bounds for negative widths and heights
+ if (newBounds.width < 0) {
+ newBounds.x += newBounds.width;
+ newBounds.width = -newBounds.width;
+ }
+ if (newBounds.height < 0) {
+ newBounds.y += newBounds.height;
+ newBounds.height = -newBounds.height;
+ }
+ int minX = getCurrentViewer().getControl().getBounds().x;
+ int minY = getCurrentViewer().getControl().getBounds().y;
+ int maxWidth = getCurrentViewer().getControl().getBounds().width;
+ int maxHeight = getCurrentViewer().getControl().getBounds().height;
+ if (newBounds.x < minX || newBounds.y < minY ||
+ newBounds.x + newBounds.width > minX + maxWidth ||
+ newBounds.y + newBounds.height > minY + maxHeight) {
+ if (moving) {
+ if (newBounds.x < minX) {
+ newBounds.x = minX;
+ }
+ if (newBounds.y < minY) {
+ newBounds.y = minY;
+ }
+ if (newBounds.x+newBounds.width > minX+maxWidth) {
+ newBounds.x = maxWidth-newBounds.width;;
+ }
+ if (newBounds.y +newBounds.height > minY+maxHeight) {
+ newBounds.y = maxHeight-newBounds.height;
+ }
+ } else {
+ if (newBounds.x < minX) {
+ newBounds.width = newBounds.x+newBounds.width-minX;
+ newBounds.x = minX;
+ }
+ if (newBounds.y < minY) {
+ newBounds.height = newBounds.y+newBounds.height-minY;
+ newBounds.y = minY;
+ }
+ if (newBounds.x+newBounds.width > minX+maxWidth) {
+ newBounds.width = maxWidth-newBounds.x;
+ }
+ if (newBounds.y +newBounds.height > minY+maxHeight) {
+ newBounds.height = maxHeight-newBounds.y;
+ }
+ }
+ }
+ feedBack.translateToRelative(newBounds);
+ feedBack.setBounds(newBounds);
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.tools.AbstractTool#handleMove()
+ */
+ protected boolean handleMove() {
+ Point location = getLocation();
+ switch (getRelativeCursorLocation(location)) {
+ case NORTH:
+ case SOUTH:
+ setCursor(Cursors.SIZENS);
+ break;
+ case EAST:
+ case WEST:
+ setCursor(Cursors.SIZEWE);
+ break;
+ case NORTHWEST:
+ case SOUTHEAST:
+ setCursor(Cursors.SIZENWSE);
+ break;
+ case NORTHEAST:
+ case SOUTHWEST:
+ setCursor(Cursors.SIZENESW);
+ break;
+ case INSIDE:
+ setCursor(Cursors.SIZEALL);
+ break;
+ default:
+ setCursor(getDefaultCursor());
+
+ }
+ return true;
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.tools.AbstractTool#handleButtonUp(int)
+ */
+ protected boolean handleButtonUp(int button) {
+ handleFinished();
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.tools.AbstractTool#handleFinished()
+ */
+ protected void handleFinished() {
+ if (isInState(STATE_DRAG_IN_PROGRESS) || isInState(STATE_DRAG)) {
+
+ } else {
+ Point location = getLocation();
+ getFeedBackFigure().setBounds(new Rectangle(location.x, location.y, 0,0));
+ }
+ setState(STATE_TERMINAL);
+ resetFlags();
+ zoomTo(getFeedBackFigure().getBounds().getCopy());
+
+ //super.handleFinished();
+ }
+
+ /**
+ * @param copy
+ */
+ private void zoomTo(Rectangle rect) {
+ referenceBounds = rect.getCopy();
+ GraphicalEditPart part = (GraphicalEditPart)getCurrentViewer().getContents();
+ if (part != null) {
+ part.getFigure().translateFromParent(referenceBounds);
+ if (rect.isEmpty()) {
+ //make sure that it didn't scale to a size greater than 0.
+ referenceBounds.setSize(0,0);
+ }
+ if (this.currentManager != null)
+ this.currentManager.zoomTo(referenceBounds);
+ if (referenceBounds.isEmpty()) {
+ //reset the figure so as to not jar the user.
+ getFeedBackFigure().setBounds(rect);
+ }
+ }
+ }
+ boolean isNorth(int loc) {
+ loc = (isInside(loc)) ? loc ^ INSIDE : loc;
+ return (loc & NORTH) != 0;
+ }
+
+ boolean isEast(int loc) {
+ loc = (isInside(loc)) ? loc ^ INSIDE : loc;
+ return (loc & EAST)!= 0;
+ }
+
+ boolean isSouth(int loc) {
+ loc = (isInside(loc)) ? loc ^ INSIDE : loc;
+ return (loc & SOUTH) != 0;
+ }
+
+ boolean isWest(int loc) {
+ loc = (isInside(loc)) ? loc ^ INSIDE : loc;
+ return (loc & WEST) != 0;
+ }
+
+ boolean isInside(int loc) {
+ return (loc & INSIDE) != 0;
+ }
+
+ private int getRelativeCursorLocation(Point location) {
+ RectangleFigure feedBack = getFeedBackFigure();
+ Rectangle bounds = feedBack.getBounds();
+ location = location.getCopy();
+ feedBack.translateToRelative(location);
+ int result = OUTSIDE;
+ if (feedBack.containsPoint(location)) {
+ result |= INSIDE;
+ //check to see the bounds to find out which
+ //cursor to use
+ if (Math.abs(location.y - bounds.y)<=2) {
+ result |= NORTH;
+ } else if (Math.abs(location.y -(bounds.y+bounds.height-1))<=2) {
+ result |= SOUTH;
+ }
+ if (Math.abs(location.x - bounds.x)<=2) {
+ result |= WEST;
+ } else if (Math.abs(location.x - (bounds.x+bounds.width-1))<=2) {
+ result |= EAST;
+ }
+ }
+ return result;
+ }
+
+ private RectangleFigure getFeedBackFigure() {
+ if (focusFigure == null) {
+ focusFigure = new RectangleFigure();
+ focusFigure.setForegroundColor(ColorConstants.red);
+ focusFigure.setFill(false);
+ focusFigure.setLineWidth(2);
+ focusFigureListener = new FocusMoveListener();
+ focusFigure.addFigureListener(focusFigureListener);
+ focusFigure.setBounds(new Rectangle());
+ addFeedback(focusFigure);
+// referenceBounds = focusFigure.getParent().getBounds().getCopy();
+// focusFigure.getParent().addFigureListener(this);
+ }
+ return focusFigure;
+ }
+
+ private TargetShape getTargetFigure() {
+ if (target == null) {
+ target = new TargetShape();
+ target.setForegroundColor(ColorConstants.red);
+ target.setFill(false);
+ target.setBounds(new Rectangle(-10,-10,20,20));
+ target.setLineWidth(2);
+ addFeedback(target);
+ }
+ return target;
+ }
+
+ private void setTargetCenter(Point center) {
+ Point newCenter = center.getCopy();
+ newCenter.translate(-getTargetFigure().getSize().width/2, -getTargetFigure().getSize().height/2);
+ getTargetFigure().setLocation(newCenter);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.gef.tools.AbstractTool#deactivate()
+ */
+ public void deactivate() {
+ if (focusFigure != null) {
+ focusFigure.removeFigureListener(focusFigureListener);