1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.ui;
14
15 import java.awt.BorderLayout;
16 import java.awt.Component;
17 import java.awt.Insets;
18 import java.awt.Point;
19 import java.awt.Rectangle;
20 import java.awt.dnd.Autoscroll;
21 import java.awt.event.ActionEvent;
22 import java.awt.event.KeyAdapter;
23 import java.awt.event.KeyEvent;
24 import java.awt.event.MouseAdapter;
25 import java.awt.event.MouseEvent;
26 import java.util.ArrayList;
27 import java.util.Enumeration;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Set;
31
32 import javax.swing.AbstractAction;
33 import javax.swing.BorderFactory;
34 import javax.swing.JPanel;
35 import javax.swing.JPopupMenu;
36 import javax.swing.JScrollPane;
37 import javax.swing.JToggleButton;
38 import javax.swing.JTree;
39 import javax.swing.KeyStroke;
40 import javax.swing.ToolTipManager;
41 import javax.swing.event.TreeSelectionEvent;
42 import javax.swing.event.TreeSelectionListener;
43 import javax.swing.tree.TreeModel;
44 import javax.swing.tree.TreePath;
45 import javax.swing.tree.TreeSelectionModel;
46
47 import com.eviware.soapui.model.ModelItem;
48 import com.eviware.soapui.model.project.Project;
49 import com.eviware.soapui.model.tree.SoapUITreeModel;
50 import com.eviware.soapui.model.tree.SoapUITreeNode;
51 import com.eviware.soapui.model.tree.SoapUITreeNodeRenderer;
52 import com.eviware.soapui.model.tree.nodes.ProjectTreeNode;
53 import com.eviware.soapui.model.workspace.Workspace;
54 import com.eviware.soapui.support.UISupport;
55 import com.eviware.soapui.support.action.SoapUIMultiAction;
56 import com.eviware.soapui.support.action.swing.ActionList;
57 import com.eviware.soapui.support.action.swing.ActionListBuilder;
58 import com.eviware.soapui.support.action.swing.ActionSupport;
59 import com.eviware.soapui.support.components.JXToolBar;
60
61 /***
62 * The soapUI navigator tree
63 *
64 * @author Ole.Matzura
65 */
66
67 public class Navigator extends JPanel
68 {
69 private Workspace workspace;
70 private JTree mainTree;
71 private SoapUITreeModel treeModel;
72 private Set<NavigatorListener> listeners = new HashSet<NavigatorListener>();
73
74 public Navigator( Workspace workspace )
75 {
76 super( new BorderLayout() );
77 this.workspace = workspace;
78
79 buildUI();
80 }
81
82 private void buildUI()
83 {
84 treeModel = new SoapUITreeModel( workspace );
85
86 mainTree = new NavigatorTree( treeModel );
87 mainTree.setRootVisible( true );
88 mainTree.setExpandsSelectedPaths( true );
89 mainTree.setScrollsOnExpand( true );
90 mainTree.setToggleClickCount( 0 );
91 mainTree.addMouseListener( new TreeMouseListener() );
92 mainTree.addTreeSelectionListener( new InternalTreeSelectionListener() );
93 mainTree.setCellRenderer( new SoapUITreeNodeRenderer() );
94 mainTree.setBorder( null );
95 mainTree.getSelectionModel().setSelectionMode( TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION );
96 mainTree.addKeyListener( new TreeKeyListener() );
97
98 ToolTipManager.sharedInstance().registerComponent( mainTree );
99
100 add( new JScrollPane( mainTree ), BorderLayout.CENTER );
101 add( buildToolbar(), BorderLayout.NORTH );
102 setBorder( BorderFactory.createEmptyBorder( 0, 0, 0, 0 ) );
103 }
104
105 private Component buildToolbar()
106 {
107 JXToolBar toolbar = UISupport.createSmallToolbar();
108
109 JToggleButton toggleButton = new JToggleButton( new TogglePropertiesAction() );
110 toggleButton.setToolTipText( "Toggles displaying of Test Properties in tree" );
111 toggleButton.setSize( 10, 12 );
112 toolbar.addFixed( toggleButton );
113 toolbar.addGlue();
114
115 return toolbar;
116 }
117
118 private static class NavigatorTree extends JTree implements Autoscroll
119 {
120 public NavigatorTree( SoapUITreeModel treeModel )
121 {
122 super( treeModel );
123 }
124
125 private static final int AUTOSCROLL_MARGIN = 12;
126
127 public void autoscroll( Point pt )
128 {
129
130 int nRow = getRowForLocation( pt.x, pt.y );
131
132
133 if( nRow < 0 )
134 return;
135
136 Rectangle raOuter = getBounds();
137
138
139
140
141
142 nRow = ( pt.y + raOuter.y <= AUTOSCROLL_MARGIN )
143
144 ? ( nRow <= 0 ? 0 : nRow - 1 )
145 : ( nRow < getRowCount() - 1 ? nRow + 1 : nRow );
146
147
148
149
150 scrollRowToVisible( nRow );
151 }
152
153
154
155 public Insets getAutoscrollInsets()
156 {
157 Rectangle raOuter = getBounds();
158 Rectangle raInner = getParent().getBounds();
159 return new Insets( raInner.y - raOuter.y + AUTOSCROLL_MARGIN, raInner.x - raOuter.x + AUTOSCROLL_MARGIN,
160 raOuter.height - raInner.height - raInner.y + raOuter.y + AUTOSCROLL_MARGIN, raOuter.width
161 - raInner.width - raInner.x + raOuter.x + AUTOSCROLL_MARGIN );
162 }
163 }
164
165 public Project getCurrentProject()
166 {
167 TreePath path = mainTree.getSelectionPath();
168 if( path == null )
169 return null;
170
171 Object node = ( Object )path.getLastPathComponent();
172 while( node != null && !( node instanceof ProjectTreeNode ) )
173 {
174 path = path.getParentPath();
175 node = ( path == null ? null : path.getLastPathComponent() );
176 }
177
178 if( node == null )
179 return null;
180
181 return ( ( ProjectTreeNode )node ).getProject();
182 }
183
184 public void addNavigatorListener( NavigatorListener listener )
185 {
186 listeners.add( listener );
187 }
188
189 public void removeNavigatorListener( NavigatorListener listener )
190 {
191 listeners.remove( listener );
192 }
193
194 public void selectModelItem( ModelItem modelItem )
195 {
196 TreePath path = treeModel.getPath( modelItem );
197 mainTree.setSelectionPath( path );
198 mainTree.expandPath( path );
199 mainTree.scrollPathToVisible( path );
200 }
201
202 public TreePath getTreePath( ModelItem modelItem )
203 {
204 return treeModel.getPath( modelItem );
205 }
206
207 public JTree getMainTree()
208 {
209 return mainTree;
210 }
211
212 public ModelItem getSelectedItem()
213 {
214 TreePath path = mainTree.getSelectionPath();
215 if( path == null )
216 return null;
217
218 return ( ( SoapUITreeNode )path.getLastPathComponent() ).getModelItem();
219 }
220
221 private final class TreeKeyListener extends KeyAdapter
222 {
223 public void keyPressed( KeyEvent e )
224 {
225 TreePath selectionPath = mainTree.getSelectionPath();
226 if( selectionPath != null )
227 {
228 SoapUITreeNode lastPathComponent = ( SoapUITreeNode )selectionPath.getLastPathComponent();
229 ActionList actions = lastPathComponent.getActions();
230 if( actions != null )
231 {
232 actions.dispatchKeyEvent( e );
233 }
234
235 if( !e.isConsumed() )
236 {
237 KeyStroke ks = KeyStroke.getKeyStrokeForEvent( e );
238 if( ks.equals( UISupport.getKeyStroke( "alt C" ) ) )
239 {
240 mainTree.collapsePath( selectionPath );
241 e.consume();
242 }
243 else if( ks.equals( UISupport.getKeyStroke( "alt E" ) ) )
244 {
245 mainTree.collapsePath( selectionPath );
246 int row = mainTree.getSelectionRows()[0];
247 TreePath nextPath = mainTree.getPathForRow( row + 1 );
248
249 TreePath path = mainTree.getPathForRow( row );
250 while( path != null && !path.equals( nextPath ) )
251 {
252 mainTree.expandRow( row );
253 path = mainTree.getPathForRow( ++row );
254 }
255
256 e.consume();
257 }
258 }
259 }
260 }
261 }
262
263 public class InternalTreeSelectionListener implements TreeSelectionListener
264 {
265 public void valueChanged( TreeSelectionEvent e )
266 {
267 Object obj = e.getPath().getLastPathComponent();
268 if( obj instanceof SoapUITreeNode )
269 {
270 SoapUITreeNode treeNode = ( SoapUITreeNode )obj;
271
272 if( !listeners.isEmpty() )
273 {
274 TreePath newPath = e.getNewLeadSelectionPath();
275 NavigatorListener[] array = listeners.toArray( new NavigatorListener[listeners.size()] );
276 for( NavigatorListener listener : array )
277 {
278 listener.nodeSelected( newPath == null ? null : treeNode );
279 }
280 }
281 }
282 }
283 }
284
285 public class TreeMouseListener extends MouseAdapter
286 {
287 private final class CollapseRowAction extends AbstractAction
288 {
289 private final int row;
290
291 public CollapseRowAction( int row )
292 {
293 this.row = row;
294 }
295
296 public void actionPerformed( ActionEvent e )
297 {
298 collapseAll( mainTree.getPathForRow( row ) );
299 mainTree.collapseRow( row );
300 }
301
302 private void collapseAll( TreePath tp )
303 {
304 if( tp == null )
305 return;
306
307 Object node = tp.getLastPathComponent();
308 TreeModel model = mainTree.getModel();
309 if( !model.isLeaf( node ) )
310 {
311 mainTree.collapsePath( tp );
312 for( int i = 0; i < model.getChildCount( node ); i++ )
313 {
314
315 collapseAll( tp.pathByAddingChild( model.getChild( node, i ) ) );
316 }
317 mainTree.collapsePath( tp );
318 }
319 }
320 }
321
322 private final class ExpandRowAction extends AbstractAction
323 {
324 private final int row;
325
326 public ExpandRowAction( int row )
327 {
328 this.row = row;
329 }
330
331 public void actionPerformed( ActionEvent e )
332 {
333 mainTree.expandRow( row );
334 expandAll( mainTree.getPathForRow( row ) );
335 }
336
337 private void expandAll( TreePath tp )
338 {
339 if( tp == null )
340 return;
341
342 Object node = tp.getLastPathComponent();
343 TreeModel model = mainTree.getModel();
344 if( !model.isLeaf( node ) )
345 {
346 mainTree.expandPath( tp );
347 for( int i = 0; i < model.getChildCount( node ); i++ )
348 {
349 expandAll( tp.pathByAddingChild( model.getChild( node, i ) ) );
350 }
351 }
352 }
353
354
355 }
356
357 public void mouseClicked( MouseEvent e )
358 {
359 if( e.isPopupTrigger() )
360 showPopup( e );
361 else if( e.getClickCount() < 2 )
362 return;
363 if( mainTree.getSelectionCount() == 1 )
364 {
365 int row = mainTree.getRowForLocation( e.getX(), e.getY() );
366 TreePath path = mainTree.getSelectionPath();
367 if( path == null && row == -1 )
368 return;
369
370 if( path == null || mainTree.getRowForPath( path ) != row )
371 mainTree.setSelectionRow( row );
372
373 SoapUITreeNode node = ( SoapUITreeNode )path.getLastPathComponent();
374 ActionList actions = node.getActions();
375 if( actions != null )
376 actions.performDefaultAction( new ActionEvent( mainTree, 0, null ) );
377 }
378 }
379
380 public void mousePressed( MouseEvent e )
381 {
382 if( e.isPopupTrigger() )
383 showPopup( e );
384 }
385
386 public void mouseReleased( MouseEvent e )
387 {
388 if( e.isPopupTrigger() )
389 showPopup( e );
390 }
391
392 private void showPopup( MouseEvent e )
393 {
394 if( mainTree.getSelectionCount() < 2 )
395 {
396 TreePath path = mainTree.getPathForLocation( ( int )e.getPoint().getX(), ( int )e.getPoint().getY() );
397 if( path == null )
398 {
399 int row = ( int )e.getPoint().getY() / mainTree.getRowHeight();
400 if( row != -1 )
401 {
402 JPopupMenu collapsePopup = new JPopupMenu();
403 collapsePopup.add( "Collapse" ).addActionListener( new CollapseRowAction( row ) );
404 collapsePopup.add( "Expand" ).addActionListener( new ExpandRowAction( row ) );
405 collapsePopup.show( mainTree, e.getX(), e.getY() );
406 }
407
408 return;
409 }
410 SoapUITreeNode node = ( SoapUITreeNode )path.getLastPathComponent();
411
412 JPopupMenu popupMenu = node.getPopup();
413 if( popupMenu == null )
414 return;
415
416 mainTree.setSelectionPath( path );
417
418 popupMenu.show( mainTree, e.getX(), e.getY() );
419 }
420 else
421 {
422 TreePath[] selectionPaths = mainTree.getSelectionPaths();
423 List<ModelItem> targets = new ArrayList<ModelItem>();
424 for( TreePath treePath : selectionPaths )
425 {
426 SoapUITreeNode node = ( SoapUITreeNode )treePath.getLastPathComponent();
427 targets.add( node.getModelItem() );
428 }
429
430 if( targets.size() > 0 )
431 {
432 ActionList actions = ActionListBuilder.buildMultiActions( targets
433 .toArray( new ModelItem[targets.size()] ) );
434 if( actions.getActionCount() > 0 )
435 {
436 JPopupMenu popup = new JPopupMenu();
437 ActionSupport.addActions( actions, popup );
438 popup.show( mainTree, e.getX(), e.getY() );
439 }
440 }
441 }
442 }
443 }
444
445 public boolean isVisible( TreePath path )
446 {
447 return mainTree.isVisible( path );
448 }
449
450 public boolean isExpanded( TreePath path )
451 {
452 return mainTree.isExpanded( path );
453 }
454
455 private class TogglePropertiesAction extends AbstractAction
456 {
457 public TogglePropertiesAction()
458 {
459 putValue( SMALL_ICON, UISupport.createImageIcon( "/toggle_properties.gif" ) );
460 }
461
462 public void actionPerformed( ActionEvent e )
463 {
464 Enumeration<TreePath> expandedDescendants = mainTree.getExpandedDescendants( getTreePath( workspace ) );
465 TreePath selectionPath = mainTree.getSelectionPath();
466
467 treeModel.setShowProperties( !treeModel.isShowProperties() );
468
469 while( expandedDescendants != null && expandedDescendants.hasMoreElements() )
470 {
471 mainTree.expandPath( expandedDescendants.nextElement() );
472 }
473
474 if( selectionPath != null )
475 mainTree.setSelectionPath( selectionPath );
476 }
477 }
478
479 public class MultiActionInvoker extends AbstractAction
480 {
481 private final SoapUIMultiAction action;
482 private final List<ModelItem> targets;
483
484 public MultiActionInvoker( SoapUIMultiAction action, List<ModelItem> targets )
485 {
486 super( action.getName() );
487
488 this.action = action;
489 this.targets = targets;
490 }
491
492 public void actionPerformed( ActionEvent e )
493 {
494 action.perform( targets.toArray( new ModelItem[targets.size()] ), null );
495 }
496 }
497 }