View Javadoc

1   /*
2    * $Id: ResultPanel.java,v 1.20 2007/07/04 19:42:48 edankert Exp $
3    *
4    * The contents of this file are subject to the Mozilla Public License 
5    * Version 1.1 (the "License"); you may not use this file except in 
6    * compliance with the License. You may obtain a copy of the License at 
7    * http://www.mozilla.org/MPL/ 
8    *
9    * Software distributed under the License is distributed on an "AS IS" basis, 
10   * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
11   * for the specific language governing rights and limitations under the License.
12   *
13   * The Original Code is XML Hammer code. (org.xmlhammer.*)
14   *
15   * The Initial Developer of the Original Code is Edwin Dankert. Portions created 
16   * by the Initial Developer are Copyright (C) 2002 - 2006 the Initial Developer. 
17   * All Rights Reserved.
18   *
19   * Contributor(s): Edwin Dankert <edankert@gmail.com>
20   */
21  
22  package org.xmlhammer.gui.output;
23  
24  import java.awt.BorderLayout;
25  import java.awt.Toolkit;
26  import java.awt.datatransfer.Clipboard;
27  import java.awt.datatransfer.StringSelection;
28  import java.awt.event.ActionEvent;
29  import java.awt.event.KeyEvent;
30  import java.awt.event.MouseEvent;
31  import java.net.URI;
32  
33  import javax.swing.AbstractAction;
34  import javax.swing.JFrame;
35  import javax.swing.JPopupMenu;
36  import javax.swing.JScrollPane;
37  import javax.swing.JTree;
38  import javax.swing.KeyStroke;
39  import javax.swing.ToolTipManager;
40  import javax.swing.border.EmptyBorder;
41  import javax.swing.event.TreeModelEvent;
42  import javax.swing.event.TreeModelListener;
43  import javax.swing.tree.DefaultMutableTreeNode;
44  import javax.swing.tree.TreePath;
45  
46  import org.bounce.RunnableAction;
47  import org.bounce.MenuUtilities;
48  import org.bounce.event.DoubleClickListener;
49  import org.bounce.event.PopupListener;
50  import org.xmlhammer.gui.output.actions.CopyAction;
51  import org.xmlhammer.model.project.Output;
52  
53  /***
54   * Shows the result tree.
55   *
56   * @version	$Revision: 1.20 $, $Date: 2007/07/04 19:42:48 $
57   * @author Edwin Dankert <edankert@gmail.com>
58   */
59  public class ResultPanel extends SearchablePanel implements TreeModelListener {
60  	private static final long serialVersionUID = 5439830288476866693L;
61  
62      private boolean foundSelectedNode = false;
63  	private JTree tree = null;
64      private JFrame parent = null;
65      private ResultTreeModel model = null;
66      private CopyAction copyAction = null;
67  
68  	public ResultPanel( JFrame parent) {
69  		super( new BorderLayout());
70  		
71          this.parent = parent;
72          
73  		Output output = null;
74  		
75  //		if ( output != null && output.getResult().size() > 0) {
76  //			result = (Result)output.getResult().get( output.getResult().size() - 1);
77  //		}
78  		
79  		model = new ResultTreeModel( output);
80  		
81  		model.addTreeModelListener( this);
82  
83  		tree = new JTree( model);
84          tree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false), "copy-selection");
85          tree.getActionMap().put("copy-selection", new CopyNodeAction());
86  
87          tree.setRootVisible( false);
88  		tree.setShowsRootHandles( true);
89          tree.expandPath( new TreePath( ((DefaultMutableTreeNode)model.getRoot()).getPath()));
90          tree.setCellRenderer( new ResultCellRenderer());
91  		ToolTipManager.sharedInstance().registerComponent( tree);
92  		
93  		JScrollPane scrollPane = new JScrollPane(	tree,
94  										JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
95  										JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
96  
97  		this.setBorder( new EmptyBorder( 0, 0, 0, 0));
98  		this.add( scrollPane, BorderLayout.CENTER);
99  
100 		tree.addMouseListener(new DoubleClickListener() {
101             public void doubleClicked(MouseEvent arg0) {
102                 runDefaultAction();
103             }
104 		});
105         
106         tree.addMouseListener(new PopupListener() {
107             public void popupTriggered(MouseEvent event) {
108                 showPopup(event);
109             }
110         });
111 	}
112 	
113 	public ResultTreeModel createModel(URI base) {
114         if (base != null && !base.isAbsolute()) {
115             throw new IllegalArgumentException("The URI \""+base+"\" has to be absolute.");
116         }
117 		Output output = new Output();
118 //		output.getResult().add( result);
119 		
120 		model = new ResultTreeModel(output, base);
121 		
122 		model.addTreeModelListener(this);
123 		
124 		tree.setModel(model);
125 		
126 		return model;
127 	}
128     
129     public ResultNode getSelectedNode() {
130         TreePath path = tree.getSelectionPath();
131         
132         if (path != null) {
133             Object object = path.getLastPathComponent();
134             
135             if (object instanceof ResultNode) {
136                 return (ResultNode)object;
137             }
138         }
139         
140         return null;
141     }
142     
143     public void setSelectedNode(ResultNode node) {
144         TreePath path = new TreePath(node.getPath());
145         
146         tree.setSelectionPath(path);
147         tree.expandPath(path);
148         tree.scrollPathToVisible(path);
149     }
150 
151     public ResultTreeModel getModel() {
152         return model;
153     }
154 
155     private void runDefaultAction() {
156         TreePath path = tree.getSelectionPath();
157         if (path != null) {
158             Object object = path.getLastPathComponent();
159             
160             if ( object instanceof ResultNode && ((ResultNode)object).getChildCount() == 0) {
161                 RunnableAction action = ((ResultNode)object).getDefaultAction(parent);
162                 action.run();
163             }
164         }
165     }
166 	
167     private void showPopup(MouseEvent event) {
168         JPopupMenu popup = null;
169         
170         if (tree.getSelectionCount() == 1) {
171             TreePath path = tree.getSelectionPath();
172             Object object = path.getLastPathComponent();
173             
174             if (object instanceof ResultNode) {
175                 popup = ((ResultNode)object).getPopupMenu(parent);
176             }
177         } else if (tree.getSelectionCount() > 1) {
178             CopyAction action = getCopyAction();
179             action.setText(getCopyValue());
180 
181             popup = new JPopupMenu();
182             popup.add(action);
183             MenuUtilities.alignMenu(popup);
184         }
185 
186         if (popup != null) {
187             popup.show(tree, event.getX(), event.getY());
188         }
189     }
190 
191     private String getCopyValue() {
192         TreePath[] paths = tree.getSelectionPaths();
193         StringBuilder builder = new StringBuilder();
194         
195         int baseLevel = 1024; // arbritrary high number
196         for (TreePath path : paths) {
197             baseLevel = Math.min(path.getPathCount(), baseLevel);
198         }
199         
200         for (TreePath path : paths) {
201             Object object = path.getLastPathComponent();
202             
203             if (object instanceof ResultNode) {
204                 int level = path.getPathCount() - baseLevel;
205 
206                 for (int i = 0; i < level; i++) {
207                     builder.append("\t");
208                 }
209 
210                 builder.append(((ResultNode)object).getCopyValue());
211                 
212                 if (path != paths[paths.length-1]) {
213                     builder.append(System.getProperty("line.separator"));
214                 }
215             }
216         }
217         
218         return builder.toString();
219     }
220     
221     protected CopyAction getCopyAction() {
222         if (copyAction == null) {
223             copyAction = new CopyAction();
224         }
225         
226         return copyAction;
227     }
228     
229     public void treeNodesInserted( TreeModelEvent e) {
230         Object[] children = e.getChildren();
231         
232         if ( children.length > 0) {
233             if ( children[0] instanceof DirectoryNode) {
234                 // don't do anything ...
235             } else if ( children[0] instanceof URINode) {
236                 tree.expandPath( e.getTreePath());
237             } else {
238                 Object[] path = e.getPath();
239 
240                 tree.scrollPathToVisible( e.getTreePath());
241                 model.nodeChanged( (URINode)path[ path.length-1]);
242             }
243         }
244     }
245     
246     public void copySelectedNodes() {
247         Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
248         clipboard.setContents(new StringSelection(getCopyValue()), null);
249     }
250 
251     public void treeNodesChanged( TreeModelEvent e) {}
252     public void treeNodesRemoved( TreeModelEvent e) {}
253     public void treeStructureChanged( TreeModelEvent e) {}
254     
255     public class CopyNodeAction extends AbstractAction {
256         private static final long serialVersionUID = 2045980274706285174L;
257 
258         public void actionPerformed( ActionEvent event) {
259             copySelectedNodes();
260       }
261     }
262 
263     @Override
264     public boolean search(String search, boolean matchCase, boolean forward) {
265         foundSelectedNode = false;
266 
267         ResultTreeModel model = getModel();
268         ResultNode selected = getSelectedNode();
269         RootNode root = (RootNode)model.getRoot();
270         
271         if (selected == null) {
272             selected = root;
273         }
274 
275         ResultNode node = find(selected, root, search, forward, matchCase);
276         
277         if (node == null) {
278             if (forward) {
279                 selected = root;
280             } else {
281                 selected = getLastNode(root);
282             }
283 
284             node = find(selected, root, search, forward, matchCase);
285         }
286 
287         if (node != null) {
288             setSelectedNode(node);
289             return true;
290         }
291 
292         return false;
293     }
294     
295     private ResultNode getLastNode(ResultNode node) {
296         if (node.getChildCount() > 0) {
297             return getLastNode((ResultNode)node.getChildAt(node.getChildCount()-1));
298         }
299         
300         return node;
301     }
302     
303     private ResultNode find(ResultNode start, ResultNode node, String search, boolean forward, boolean matchCase) {
304         if (foundSelectedNode && matches(node.getValue(), search, matchCase) && forward) {
305             return node;
306         } 
307 
308         if (node == start && forward) {
309             foundSelectedNode = true;
310         }
311         
312         if (forward) {
313             for (int i = 0; i < node.getChildCount(); i++) {
314                 ResultNode result = find(start, (ResultNode)node.getChildAt(i), search, forward, matchCase);
315                 if (result != null) {
316                     return result;
317                 }
318             }
319         } else {
320             for (int i = node.getChildCount()-1; i >= 0; i--) {
321                 ResultNode result = find(start, (ResultNode)node.getChildAt(i), search, forward, matchCase);
322                 if (result != null) {
323                     return result;
324                 }
325             }
326         }
327         
328         if (foundSelectedNode && matches(node.getValue(), search, matchCase) && !forward) {
329             return node;
330         }
331         
332         if (node == start && !forward) {
333             foundSelectedNode = true;
334         }
335 
336         return null;
337     }
338     
339     public boolean matches(String value, String search, boolean matchCase) {
340         if (matchCase) {
341             return value.indexOf(search) != -1;
342         }
343 
344         return value.toLowerCase().indexOf(search.toLowerCase()) != -1;
345     }
346 }