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 || mainTree.getSelectionCount() == 0 )
227 return;
228
229 if( mainTree.getSelectionCount() == 1 )
230 {
231 SoapUITreeNode lastPathComponent = ( SoapUITreeNode )selectionPath.getLastPathComponent();
232 ActionList actions = lastPathComponent.getActions();
233 if( actions != null )
234 {
235 actions.dispatchKeyEvent( e );
236 }
237
238 if( !e.isConsumed() )
239 {
240 KeyStroke ks = KeyStroke.getKeyStrokeForEvent( e );
241 if( ks.equals( UISupport.getKeyStroke( "alt C" ) ) )
242 {
243 mainTree.collapsePath( selectionPath );
244 e.consume();
245 }
246 else if( ks.equals( UISupport.getKeyStroke( "alt E" ) ) )
247 {
248 mainTree.collapsePath( selectionPath );
249 int row = mainTree.getSelectionRows()[0];
250 TreePath nextPath = mainTree.getPathForRow( row + 1 );
251
252 TreePath path = mainTree.getPathForRow( row );
253 while( path != null && !path.equals( nextPath ) )
254 {
255 mainTree.expandRow( row );
256 path = mainTree.getPathForRow( ++row );
257 }
258
259 e.consume();
260 }
261 }
262 }
263 else
264 {
265 TreePath[] selectionPaths = mainTree.getSelectionPaths();
266 List<ModelItem> targets = new ArrayList<ModelItem>();
267 for( TreePath treePath : selectionPaths )
268 {
269 SoapUITreeNode node = ( SoapUITreeNode )treePath.getLastPathComponent();
270 targets.add( node.getModelItem() );
271 }
272
273 if( targets.size() > 0 )
274 {
275 ActionList actions = ActionListBuilder.buildMultiActions( targets
276 .toArray( new ModelItem[targets.size()] ) );
277 if( actions.getActionCount() > 0 )
278 {
279 actions.dispatchKeyEvent( e );
280 }
281 }
282 }
283 }
284 }
285
286 public class InternalTreeSelectionListener implements TreeSelectionListener
287 {
288 public void valueChanged( TreeSelectionEvent e )
289 {
290 Object obj = e.getPath().getLastPathComponent();
291 if( obj instanceof SoapUITreeNode )
292 {
293 SoapUITreeNode treeNode = ( SoapUITreeNode )obj;
294
295 if( !listeners.isEmpty() )
296 {
297 TreePath newPath = e.getNewLeadSelectionPath();
298 NavigatorListener[] array = listeners.toArray( new NavigatorListener[listeners.size()] );
299 for( NavigatorListener listener : array )
300 {
301 listener.nodeSelected( newPath == null ? null : treeNode );
302 }
303 }
304 }
305 }
306 }
307
308 public class TreeMouseListener extends MouseAdapter
309 {
310 private final class CollapseRowAction extends AbstractAction
311 {
312 private final int row;
313
314 public CollapseRowAction( int row )
315 {
316 this.row = row;
317 }
318
319 public void actionPerformed( ActionEvent e )
320 {
321 collapseAll( mainTree.getPathForRow( row ) );
322 mainTree.collapseRow( row );
323 }
324
325 private void collapseAll( TreePath tp )
326 {
327 if( tp == null )
328 return;
329
330 Object node = tp.getLastPathComponent();
331 TreeModel model = mainTree.getModel();
332 if( !model.isLeaf( node ) )
333 {
334 mainTree.collapsePath( tp );
335 for( int i = 0; i < model.getChildCount( node ); i++ )
336 {
337
338 collapseAll( tp.pathByAddingChild( model.getChild( node, i ) ) );
339 }
340 mainTree.collapsePath( tp );
341 }
342 }
343 }
344
345 private final class ExpandRowAction extends AbstractAction
346 {
347 private final int row;
348
349 public ExpandRowAction( int row )
350 {
351 this.row = row;
352 }
353
354 public void actionPerformed( ActionEvent e )
355 {
356 mainTree.expandRow( row );
357 expandAll( mainTree.getPathForRow( row ) );
358 }
359
360 private void expandAll( TreePath tp )
361 {
362 if( tp == null )
363 return;
364
365 Object node = tp.getLastPathComponent();
366 TreeModel model = mainTree.getModel();
367 if( !model.isLeaf( node ) )
368 {
369 mainTree.expandPath( tp );
370 for( int i = 0; i < model.getChildCount( node ); i++ )
371 {
372 expandAll( tp.pathByAddingChild( model.getChild( node, i ) ) );
373 }
374 }
375 }
376
377
378 }
379
380 public void mouseClicked( MouseEvent e )
381 {
382 if( e.isPopupTrigger() )
383 showPopup( e );
384 else if( e.getClickCount() < 2 )
385 return;
386 if( mainTree.getSelectionCount() == 1 )
387 {
388 int row = mainTree.getRowForLocation( e.getX(), e.getY() );
389 TreePath path = mainTree.getSelectionPath();
390 if( path == null && row == -1 )
391 return;
392
393 if( path == null || mainTree.getRowForPath( path ) != row )
394 mainTree.setSelectionRow( row );
395
396 SoapUITreeNode node = ( SoapUITreeNode )path.getLastPathComponent();
397 ActionList actions = node.getActions();
398 if( actions != null )
399 actions.performDefaultAction( new ActionEvent( mainTree, 0, null ) );
400 }
401 }
402
403 public void mousePressed( MouseEvent e )
404 {
405 if( e.isPopupTrigger() )
406 showPopup( e );
407 }
408
409 public void mouseReleased( MouseEvent e )
410 {
411 if( e.isPopupTrigger() )
412 showPopup( e );
413 }
414
415 private void showPopup( MouseEvent e )
416 {
417 if( mainTree.getSelectionCount() < 2 )
418 {
419 TreePath path = mainTree.getPathForLocation( ( int )e.getPoint().getX(), ( int )e.getPoint().getY() );
420 if( path == null )
421 {
422 int row = ( int )e.getPoint().getY() / mainTree.getRowHeight();
423 if( row != -1 )
424 {
425 JPopupMenu collapsePopup = new JPopupMenu();
426 collapsePopup.add( "Collapse" ).addActionListener( new CollapseRowAction( row ) );
427 collapsePopup.add( "Expand" ).addActionListener( new ExpandRowAction( row ) );
428 collapsePopup.show( mainTree, e.getX(), e.getY() );
429 }
430
431 return;
432 }
433 SoapUITreeNode node = ( SoapUITreeNode )path.getLastPathComponent();
434
435 JPopupMenu popupMenu = node.getPopup();
436 if( popupMenu == null )
437 return;
438
439 mainTree.setSelectionPath( path );
440
441 popupMenu.show( mainTree, e.getX(), e.getY() );
442 }
443 else
444 {
445 TreePath[] selectionPaths = mainTree.getSelectionPaths();
446 List<ModelItem> targets = new ArrayList<ModelItem>();
447 for( TreePath treePath : selectionPaths )
448 {
449 SoapUITreeNode node = ( SoapUITreeNode )treePath.getLastPathComponent();
450 targets.add( node.getModelItem() );
451 }
452
453 if( targets.size() > 0 )
454 {
455 ActionList actions = ActionListBuilder.buildMultiActions( targets
456 .toArray( new ModelItem[targets.size()] ) );
457 if( actions.getActionCount() > 0 )
458 {
459 JPopupMenu popup = new JPopupMenu();
460 ActionSupport.addActions( actions, popup );
461 popup.show( mainTree, e.getX(), e.getY() );
462 }
463 }
464 }
465 }
466 }
467
468 public boolean isVisible( TreePath path )
469 {
470 return mainTree.isVisible( path );
471 }
472
473 public boolean isExpanded( TreePath path )
474 {
475 return mainTree.isExpanded( path );
476 }
477
478 private class TogglePropertiesAction extends AbstractAction
479 {
480 public TogglePropertiesAction()
481 {
482 putValue( SMALL_ICON, UISupport.createImageIcon( "/toggle_properties.gif" ) );
483 }
484
485 public void actionPerformed( ActionEvent e )
486 {
487 Enumeration<TreePath> expandedDescendants = mainTree.getExpandedDescendants( getTreePath( workspace ) );
488 TreePath selectionPath = mainTree.getSelectionPath();
489
490 treeModel.setShowProperties( !treeModel.isShowProperties() );
491
492 while( expandedDescendants != null && expandedDescendants.hasMoreElements() )
493 {
494 mainTree.expandPath( expandedDescendants.nextElement() );
495 }
496
497 if( selectionPath != null )
498 mainTree.setSelectionPath( selectionPath );
499 }
500 }
501
502 public class MultiActionInvoker extends AbstractAction
503 {
504 private final SoapUIMultiAction action;
505 private final List<ModelItem> targets;
506
507 public MultiActionInvoker( SoapUIMultiAction action, List<ModelItem> targets )
508 {
509 super( action.getName() );
510
511 this.action = action;
512 this.targets = targets;
513 }
514
515 public void actionPerformed( ActionEvent e )
516 {
517 action.perform( targets.toArray( new ModelItem[targets.size()] ), null );
518 }
519 }
520 }