1 package de.matthias_burbach.mosaique4eclipse.views;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.LinkedList;
6 import java.util.List;
7
8 import org.eclipse.core.resources.IWorkspaceRoot;
9 import org.eclipse.jface.viewers.TreeViewer;
10 import org.eclipse.swt.SWT;
11 import org.eclipse.swt.events.MouseAdapter;
12 import org.eclipse.swt.events.MouseEvent;
13 import org.eclipse.swt.events.MouseTrackAdapter;
14 import org.eclipse.swt.graphics.Point;
15 import org.eclipse.swt.graphics.Rectangle;
16 import org.eclipse.swt.layout.GridData;
17 import org.eclipse.swt.layout.GridLayout;
18 import org.eclipse.swt.widgets.Composite;
19 import org.eclipse.swt.widgets.Control;
20 import org.eclipse.swt.widgets.Display;
21 import org.eclipse.swt.widgets.Label;
22 import org.eclipse.swt.widgets.Shell;
23 import org.eclipse.swt.widgets.TreeItem;
24
25 import de.matthias_burbach.mosaique.swing.AbstractFileItemNode;
26 import de.matthias_burbach.mosaique.swing.DefinitionNode;
27 import de.matthias_burbach.mosaique.swing.InsertNode;
28 import de.matthias_burbach.mosaique.swing.JspNode;
29 import de.matthias_burbach.mosaique.swing.PutNode;
30
31 /***
32 * Displays the tree of Struts configs, logical pages, Tiles definitions,
33 * JSPs etc. within the Mosaique Eclipse view.
34 *
35 * @author Matthias Burbach
36 */
37 public class MosaiqueTreeViewer extends TreeViewer {
38 /***
39 * Intercepts construction to set up the tool tip handler.
40 *
41 * @param parent The parent composite.
42 */
43 public MosaiqueTreeViewer(final Composite parent) {
44 super(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
45 ToolTipHandler toolTipHandler = new ToolTipHandler(parent.getShell());
46 toolTipHandler.activateHoverHelp(getTree());
47 }
48
49 /***
50 * This tool tip solution is an adapted version of source code from
51 * Andrei Loskutov's JDepend plugin.
52 *
53 * Emulated tooltip handler
54 * Notice that we could display anything in a tooltip besides text and
55 * images.
56 */
57 private final class ToolTipHandler {
58 /***
59 * The shell used to display the tip.
60 */
61 private Shell tipShell;
62
63 /***
64 * The label to display an image as tool tip. Not really used currently.
65 */
66 private Label tipLabelImage;
67
68 /***
69 * The label to display a text as tool tip.
70 */
71 private Label tipLabelText;
72
73 /***
74 * The position being hovered over.
75 */
76 private Point tipPosition;
77
78 /***
79 * Creates a new tooltip handler
80 *
81 * @param parent the parent Shell
82 */
83 public ToolTipHandler(final Shell parent) {
84 final Display display = parent.getDisplay();
85
86 tipShell = new Shell(parent, SWT.ON_TOP);
87 GridLayout gridLayout = new GridLayout();
88 gridLayout.numColumns = 2;
89 gridLayout.marginWidth = 2;
90 gridLayout.marginHeight = 2;
91 tipShell.setLayout(gridLayout);
92
93 tipShell.setBackground(
94 display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
95
96 tipLabelImage = new Label(tipShell, SWT.NONE);
97 tipLabelImage.setForeground(
98 display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
99 tipLabelImage.setBackground(
100 display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
101 tipLabelImage.setLayoutData(
102 new GridData(GridData.FILL_HORIZONTAL
103 | GridData.VERTICAL_ALIGN_CENTER));
104
105 tipLabelText = new Label(tipShell, SWT.NONE);
106 tipLabelText.setForeground(
107 display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
108 tipLabelText.setBackground(
109 display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
110 tipLabelText.setLayoutData(
111 new GridData(GridData.FILL_HORIZONTAL
112 | GridData.VERTICAL_ALIGN_CENTER));
113 }
114
115 /***
116 * Enables customized hover help for a specified control
117 *
118 * @param control the control on which to enable hoverhelp
119 */
120 public void activateHoverHelp(final Control control) {
121
122
123
124
125 control.addMouseListener(new MouseAdapter () {
126 public void mouseDown(final MouseEvent e) {
127 if (tipShell.isVisible()) {
128 tipShell.setVisible(false);
129 }
130 }
131 });
132
133
134
135
136 control.addMouseTrackListener(new MouseTrackAdapter () {
137 public void mouseExit(final MouseEvent e) {
138 if (tipShell.isVisible()) {
139 tipShell.setVisible(false);
140 }
141 }
142 public void mouseHover(final MouseEvent event) {
143 Point pt = new Point (event.x, event.y);
144 TreeItem ti = MosaiqueTreeViewer.this.getTree().getItem(pt);
145 if (ti == null || ti.getText() == null) {
146 return;
147 }
148 String toolTipText = getToolTipText(ti);
149 if (toolTipText == null || toolTipText.length() == 0) {
150 tipShell.setVisible(false);
151 return;
152 }
153 tipPosition = control.toDisplay(pt);
154
155 tipLabelText.setText(toolTipText);
156
157 tipShell.pack();
158 setHoverLocation(tipShell, tipPosition);
159 tipShell.setVisible(true);
160 }
161 });
162 }
163
164 /***
165 * @param treeItem The tree item to get the tool tip text for.
166 * @return The tool tip text.
167 */
168 protected String getToolTipText(final TreeItem treeItem) {
169 String result = null;
170 Object data = treeItem.getData();
171 if (data instanceof AbstractFileItemNode) {
172 AbstractFileItemNode node = (AbstractFileItemNode) data;
173 result = node.getMessage();
174 }
175 if (result == null) {
176 if (data instanceof PutNode) {
177 PutNode node = (PutNode) data;
178 result = "value='" + node.getPut().getValue() + "'";
179 } else if (data instanceof JspNode) {
180 JspNode node = (JspNode) data;
181 result = node.getFileItem().getFilePath();
182 } else if (data instanceof DefinitionNode) {
183 DefinitionNode node = (DefinitionNode) data;
184 result =
185 "Defined in '"
186 + node.getFileItem().getFilePath()
187 + "'";
188 } else if (data instanceof InsertNode) {
189
190 result = null;
191 }
192 }
193 return result;
194 }
195
196 /***
197 * Sets the location for a hovering shell
198 * @param shell the object that is to hover
199 * @param position the position of a widget to hover over
200 */
201 protected void setHoverLocation(
202 final Shell shell, final Point position) {
203 Rectangle displayBounds = shell.getDisplay().getBounds();
204 Rectangle shellBounds = shell.getBounds();
205 final int maxWidth = 8;
206 final int maxHeight = 16;
207 shellBounds.x = Math.max(
208 Math.min(
209 position.x + maxWidth,
210 displayBounds.width - shellBounds.width),
211 0);
212 shellBounds.y = Math.max(
213 Math.min(
214 position.y + maxHeight,
215 displayBounds.height - shellBounds.height),
216 0);
217 shell.setBounds(shellBounds);
218 }
219 }
220
221 /***
222 * @return The list of paths of all elements being currently expanded in the
223 * tree. The paths are of type {@link String}.
224 */
225 public List getExpandedElementPaths() {
226 List result = new ArrayList();
227 Object[] expandedElements = getExpandedElements();
228 for (int i = 0; i < expandedElements.length; i++) {
229 List path = getElementPath(expandedElements[i]);
230 result.add(path);
231 }
232 return result;
233 }
234
235 /***
236 * @param expandedElementPaths The list of paths of all elements to be
237 * expanded in the tree. The paths must be of
238 * type {@link List} of elements of type
239 * {@link String}. The strings are the segments
240 * of the path.
241 */
242 public void setExpandedElementPaths(final List expandedElementPaths) {
243 List expandedElements = new ArrayList();
244 for (Iterator iter = expandedElementPaths.iterator(); iter.hasNext();) {
245 List path = (List) iter.next();
246 Object element = getElementForPath(null, path);
247 if (element != null) {
248 expandedElements.add(element);
249 }
250 }
251 setExpandedElements(expandedElements.toArray());
252 }
253
254 /***
255 * Maps an element displayed in the tree to its path.
256 *
257 * @param element The element to map.
258 * @return The path as list of list of path segments of type {@link String}.
259 */
260 private List getElementPath(final Object element) {
261 LinkedList result = new LinkedList();
262 result.add(element.toString());
263 MosaiqueTreeContentProvider provider = getMosaiqueContentProvider();
264 Object parent = provider.getParent(element);
265 while (parent != null && !(parent instanceof IWorkspaceRoot)) {
266 result.addFirst(parent.toString());
267 parent = provider.getParent(parent);
268 }
269 return result;
270 }
271
272 /***
273 * Maps a path to an element in the tree.
274 *
275 * @param aRoot The ancestor of the element which is the root of the given
276 * path. Defaults to the very root of the tree.
277 * @param path The path as list of segments of type {@link String}.
278 * @return The element found under the path or <code>null</code>.
279 */
280 private Object getElementForPath(final Object aRoot, final List path) {
281 Object result = null;
282 MosaiqueTreeContentProvider provider = getMosaiqueContentProvider();
283 Object root = aRoot;
284 if (root == null) {
285 root = provider.getRoot();
286 }
287 if (path.size() > 0) {
288 String nextChildName = (String) path.get(0);
289 Object nextChild = getChildForName(root, nextChildName);
290 if (nextChild != null && path.size() > 1) {
291 result = getElementForPath(
292 nextChild, path.subList(1, path.size()));
293 } else {
294 result = nextChild;
295 }
296 }
297 return result;
298 }
299
300 /***
301 * Scans all children of the given parent for the child matching the given
302 * name.
303 *
304 * @param parent The parent whose children to scan.
305 * @param childName The name of the child to find.
306 * @return The child with the given name or <code>null</code>.
307 */
308 private Object getChildForName(
309 final Object parent, final String childName) {
310 Object result = null;
311 MosaiqueTreeContentProvider provider = getMosaiqueContentProvider();
312 Object[] children = provider.getChildren(parent);
313 for (int i = 0; i < children.length; i++) {
314 if (children[i].toString().equals(childName)) {
315 result = children[i];
316 break;
317 }
318 }
319 return result;
320 }
321
322 /***
323 * Gives typed access to this tree viewer's content provider.
324 *
325 * @return The MosaiqueTreeContentProvider.
326 */
327 private MosaiqueTreeContentProvider getMosaiqueContentProvider() {
328 MosaiqueTreeContentProvider provider =
329 (MosaiqueTreeContentProvider) getContentProvider();
330 return provider;
331 }
332 }