PageRenderTime 222ms CodeModel.GetById 181ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/jedit/search/HyperSearchResults.java

#
Java | 551 lines | 405 code | 73 blank | 73 comment | 54 complexity | eecba8a4cf991f34e234138cfd0f4712 MD5 | raw file
  1/*
  2 * HyperSearchResults.java - HyperSearch results
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 1998, 1999, 2000, 2001 Slava Pestov
  7 * Portions copyright (C) 2002 Peter Cox
  8 *
  9 * This program is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU General Public License
 11 * as published by the Free Software Foundation; either version 2
 12 * of the License, or any later version.
 13 *
 14 * This program is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 17 * GNU General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU General Public License
 20 * along with this program; if not, write to the Free Software
 21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 22 */
 23
 24package org.gjt.sp.jedit.search;
 25
 26//{{{ Imports
 27import javax.swing.*;
 28import javax.swing.event.*;
 29import javax.swing.tree.*;
 30import java.awt.*;
 31import java.awt.event.*;
 32import org.gjt.sp.jedit.io.VFSManager;
 33import org.gjt.sp.jedit.gui.DefaultFocusComponent;
 34import org.gjt.sp.jedit.gui.RolloverButton;
 35import org.gjt.sp.jedit.msg.*;
 36import org.gjt.sp.jedit.textarea.*;
 37import org.gjt.sp.jedit.*;
 38//}}}
 39
 40/**
 41 * HyperSearch results window.
 42 * @author Slava Pestov
 43 * @version $Id: HyperSearchResults.java 4965 2004-02-04 00:07:34Z spestov $
 44 */
 45public class HyperSearchResults extends JPanel implements EBComponent,
 46	DefaultFocusComponent
 47{
 48	public static final String NAME = "hypersearch-results";
 49
 50	//{{{ HyperSearchResults constructor
 51	public HyperSearchResults(View view)
 52	{
 53		super(new BorderLayout());
 54
 55		this.view = view;
 56
 57		caption = new JLabel();
 58
 59		Box toolBar = new Box(BoxLayout.X_AXIS);
 60		toolBar.add(caption);
 61		toolBar.add(Box.createGlue());
 62
 63		ActionHandler ah = new ActionHandler();
 64
 65		clear = new RolloverButton(GUIUtilities.loadIcon("Clear.png"));
 66		clear.setToolTipText(jEdit.getProperty(
 67			"hypersearch-results.clear.label"));
 68		clear.addActionListener(ah);
 69		toolBar.add(clear);
 70
 71		multi = new RolloverButton();
 72		multi.setToolTipText(jEdit.getProperty(
 73			"hypersearch-results.multi.label"));
 74		multi.addActionListener(ah);
 75		toolBar.add(multi);
 76
 77		add(BorderLayout.NORTH, toolBar);
 78
 79		resultTreeRoot = new DefaultMutableTreeNode();
 80		resultTreeModel = new DefaultTreeModel(resultTreeRoot);
 81		resultTree = new JTree(resultTreeModel);
 82		resultTree.setCellRenderer(new ResultCellRenderer());
 83		resultTree.setVisibleRowCount(16);
 84		resultTree.setRootVisible(false);
 85		resultTree.setShowsRootHandles(true);
 86
 87		// looks bad with the OS X L&F, apparently...
 88		if(!OperatingSystem.isMacOSLF())
 89			resultTree.putClientProperty("JTree.lineStyle", "Angled");
 90
 91		resultTree.setEditable(false);
 92
 93		resultTree.addKeyListener(new KeyHandler());
 94		resultTree.addMouseListener(new MouseHandler());
 95
 96		JScrollPane scrollPane = new JScrollPane(resultTree);
 97		Dimension dim = scrollPane.getPreferredSize();
 98		dim.width = 400;
 99		scrollPane.setPreferredSize(dim);
100		add(BorderLayout.CENTER, scrollPane);
101	} //}}}
102
103	//{{{ focusOnDefaultComponent() method
104	public void focusOnDefaultComponent()
105	{
106		resultTree.requestFocus();
107	} //}}}
108
109	//{{{ addNotify() method
110	public void addNotify()
111	{
112		super.addNotify();
113		EditBus.addToBus(this);
114		multiStatus = jEdit.getBooleanProperty(
115			"hypersearch-results.multi");
116		updateMultiStatus();
117	} //}}}
118
119	//{{{ removeNotify() method
120	public void removeNotify()
121	{
122		super.removeNotify();
123		EditBus.removeFromBus(this);
124		jEdit.setBooleanProperty("hypersearch-results.multi",multiStatus);
125	} //}}}
126
127	//{{{ handleMessage() method
128	public void handleMessage(EBMessage msg)
129	{
130		if(msg instanceof BufferUpdate)
131		{
132			BufferUpdate bmsg = (BufferUpdate)msg;
133			Buffer buffer = bmsg.getBuffer();
134			Object what = bmsg.getWhat();
135			if(what == BufferUpdate.LOADED ||
136				what == BufferUpdate.CLOSED)
137			{
138				ResultVisitor visitor = null;
139				if (what == BufferUpdate.LOADED)
140				{
141					visitor = new BufferLoadedVisitor();
142				}
143				else // BufferUpdate.CLOSED
144				{
145					visitor = new BufferClosedVisitor();
146				}
147				// impl note: since multiple searches now allowed,
148				// extra level in hierarchy
149				for(int i = resultTreeRoot.getChildCount() - 1; i >= 0; i--)
150				{
151					DefaultMutableTreeNode searchNode = (DefaultMutableTreeNode)
152						resultTreeRoot.getChildAt(i);
153					for(int j = searchNode.getChildCount() - 1;
154						j >= 0; j--)
155					{
156
157						DefaultMutableTreeNode bufferNode = (DefaultMutableTreeNode)
158							searchNode.getChildAt(j);
159
160						for(int k = bufferNode.getChildCount() - 1;
161							k >= 0; k--)
162						{
163							Object userObject =
164								((DefaultMutableTreeNode)bufferNode
165								.getChildAt(k)).getUserObject();
166							HyperSearchResult result = (HyperSearchResult)
167									userObject;
168
169							if(buffer.getPath().equals(result.path))
170								visitor.visit(buffer,result);
171						}
172					}
173				}
174			}
175		}
176	} //}}}
177
178	//{{{ getTreeModel() method
179	public DefaultTreeModel getTreeModel()
180	{
181		return resultTreeModel;
182	} //}}}
183
184	//{{{ getTree() method
185	/**
186	 * Returns the result tree.
187	 * @since jEdit 4.1pre9
188	 */
189	public JTree getTree()
190	{
191		return resultTree;
192	} //}}}
193
194	//{{{ searchStarted() method
195	public void searchStarted()
196	{
197		caption.setText(jEdit.getProperty("hypersearch-results.searching"));
198	} //}}}
199
200	//{{{ searchFailed() method
201	public void searchFailed()
202	{
203		caption.setText(jEdit.getProperty("hypersearch-results.no-results"));
204
205		// collapse all nodes, as suggested on user mailing list...
206		for(int i = 0; i < resultTreeRoot.getChildCount(); i++)
207		{
208			DefaultMutableTreeNode node = (DefaultMutableTreeNode)
209				resultTreeRoot.getChildAt(i);
210			resultTree.collapsePath(new TreePath(new Object[] {
211				resultTreeRoot, node }));
212		}
213	} //}}}
214
215	//{{{ searchDone() method
216	public void searchDone(final DefaultMutableTreeNode searchNode)
217	{
218		final int nodeCount = searchNode.getChildCount();
219		if (nodeCount < 1)
220		{
221			searchFailed();
222			return;
223		}
224
225		caption.setText(jEdit.getProperty("hypersearch-results.done"));
226
227		SwingUtilities.invokeLater(new Runnable()
228		{
229			public void run()
230			{
231				if(!multiStatus)
232				{
233					for(int i = 0; i < resultTreeRoot.getChildCount(); i++)
234					{
235						resultTreeRoot.remove(0);
236					}
237				}
238
239				resultTreeRoot.add(searchNode);
240				resultTreeModel.reload(resultTreeRoot);
241
242				TreePath lastNode = null;
243
244				for(int i = 0; i < nodeCount; i++)
245				{
246					lastNode = new TreePath(
247						((DefaultMutableTreeNode)
248						searchNode.getChildAt(i))
249						.getPath());
250
251					resultTree.expandPath(lastNode);
252				}
253
254				resultTree.scrollPathToVisible(
255					new TreePath(new Object[] {
256					resultTreeRoot,searchNode }));
257			}
258		});
259	} //}}}
260
261	//{{{ Private members
262	private View view;
263
264	private JLabel caption;
265	private JTree resultTree;
266	private DefaultMutableTreeNode resultTreeRoot;
267	private DefaultTreeModel resultTreeModel;
268
269	private RolloverButton clear;
270	private RolloverButton multi;
271	private boolean multiStatus;
272
273	//{{{ updateMultiStatus() method
274	private void updateMultiStatus()
275	{
276		if(multiStatus)
277			multi.setIcon(GUIUtilities.loadIcon("MultipleResults.png"));
278		else
279			multi.setIcon(GUIUtilities.loadIcon("SingleResult.png"));
280	} //}}}
281
282	//{{{ goToSelectedNode() method
283	private void goToSelectedNode()
284	{
285		TreePath path = resultTree.getSelectionPath();
286		if(path == null)
287			return;
288
289		DefaultMutableTreeNode node = (DefaultMutableTreeNode)path
290			.getLastPathComponent();
291		Object value = node.getUserObject();
292
293		if(node.getParent() == resultTreeRoot)
294		{
295			// do nothing if clicked "foo (showing n occurrences in m files)"
296		}
297		else if(value instanceof String)
298		{
299			Buffer buffer = jEdit.openFile(view,(String)value);
300			if(buffer == null)
301				return;
302
303			view.goToBuffer(buffer);
304
305			// fuck me dead
306			SwingUtilities.invokeLater(new Runnable()
307			{
308				public void run()
309				{
310					resultTree.requestFocus();
311				}
312			});
313		}
314		else if (value instanceof HyperSearchResult)
315		{
316			((HyperSearchResult)value).goTo(view);
317		}
318	} //}}}
319
320	//}}}
321
322	//{{{ ActionHandler class
323	public class ActionHandler implements ActionListener
324	{
325		public void actionPerformed(ActionEvent evt)
326		{
327			Object source = evt.getSource();
328			if(source == clear)
329			{
330				resultTreeRoot.removeAllChildren();
331				resultTreeModel.reload(resultTreeRoot);
332			}
333			else if(source == multi)
334			{
335				multiStatus = !multiStatus;
336				updateMultiStatus();
337
338				if(!multiStatus)
339				{
340					for(int i = resultTreeRoot.getChildCount() - 2; i >= 0; i--)
341					{
342						resultTreeModel.removeNodeFromParent(
343							(MutableTreeNode)resultTreeRoot
344							.getChildAt(i));
345					}
346				}
347			}
348		}
349	} //}}}
350
351	//{{{ KeyHandler class
352	class KeyHandler extends KeyAdapter
353	{
354		public void keyPressed(KeyEvent evt)
355		{
356			if(evt.getKeyCode() == KeyEvent.VK_ENTER)
357			{
358				goToSelectedNode();
359
360				// fuck me dead
361				SwingUtilities.invokeLater(new Runnable()
362				{
363					public void run()
364					{
365						resultTree.requestFocus();
366					}
367				});
368
369				evt.consume();
370			}
371		}
372	} //}}}
373
374	//{{{ MouseHandler class
375	class MouseHandler extends MouseAdapter
376	{
377		//{{{ mousePressed() method
378		public void mousePressed(MouseEvent evt)
379		{
380			if(evt.isConsumed())
381				return;
382
383			TreePath path1 = resultTree.getPathForLocation(
384				evt.getX(),evt.getY());
385			if(path1 == null)
386				return;
387
388			resultTree.setSelectionPath(path1);
389			if (GUIUtilities.isPopupTrigger(evt))
390				showPopupMenu(evt);
391			else
392			{
393				goToSelectedNode();
394
395				view.toFront();
396				view.requestFocus();
397				view.getTextArea().requestFocus();
398			}
399		} //}}}
400
401		//{{{ Private members
402		private JPopupMenu popupMenu;
403
404		//{{{ showPopupMenu method
405		private void showPopupMenu(MouseEvent evt)
406		{
407			if (popupMenu == null)
408			{
409				popupMenu = new JPopupMenu();
410				popupMenu.add(new RemoveTreeNodeAction());
411			}
412
413			GUIUtilities.showPopupMenu(popupMenu,evt.getComponent(),
414				evt.getX(),evt.getY());
415			evt.consume();
416		} //}}}
417
418		//}}}
419	} //}}}
420
421	//{{{ RemoveTreeNodeAction class
422	class RemoveTreeNodeAction extends AbstractAction
423	{
424		public RemoveTreeNodeAction()
425		{
426			super(jEdit.getProperty("hypersearch-results.remove-node"));
427		}
428
429		public void actionPerformed(ActionEvent evt)
430		{
431			TreePath path = resultTree.getSelectionPath();
432			if(path == null)
433				return;
434
435			MutableTreeNode value = (MutableTreeNode)path
436				.getLastPathComponent();
437			resultTreeModel.removeNodeFromParent(value);
438		}
439	}//}}}
440
441	//{{{ RemoveAllTreeNodesAction class
442	class RemoveAllTreeNodesAction extends AbstractAction
443	{
444		public RemoveAllTreeNodesAction()
445		{
446			super(jEdit.getProperty("hypersearch-results.remove-all-nodes"));
447		}
448
449		public void actionPerformed(ActionEvent evt)
450		{
451			resultTreeRoot = new DefaultMutableTreeNode();
452			resultTreeModel = new DefaultTreeModel(resultTreeRoot);
453			resultTree.setModel(resultTreeModel);
454		}
455	}//}}}
456
457	//{{{ ResultCellRenderer class
458	class ResultCellRenderer extends DefaultTreeCellRenderer
459	{
460		Font plainFont, boldFont;
461
462		//{{{ ResultCellRenderer constructor
463		ResultCellRenderer()
464		{
465			plainFont = UIManager.getFont("Tree.font");
466			if(plainFont == null)
467				plainFont = jEdit.getFontProperty("metal.secondary.font");
468			boldFont = new Font(plainFont.getName(),Font.BOLD,
469				plainFont.getSize());
470		} //}}}
471
472		//{{{ getTreeCellRendererComponent() method
473		public Component getTreeCellRendererComponent(JTree tree,
474			Object value, boolean sel, boolean expanded,
475			boolean leaf, int row, boolean hasFocus)
476		{
477			Component comp = super.getTreeCellRendererComponent(tree,value,sel,
478				expanded,leaf,row,hasFocus);
479			setIcon(null);
480			DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
481
482			if (node.getParent() == resultTreeRoot)
483			{
484				ResultCellRenderer.this.setFont(boldFont);
485				int bufferCount = node.getChildCount();
486				int resultCount = 0;
487				for (int i = 0; i < bufferCount; i++)
488				{
489					resultCount += node.getChildAt(i).getChildCount();
490				}
491				Object[] pp = { node.toString(), new Integer(resultCount), new Integer(bufferCount) };
492				setText(jEdit.getProperty("hypersearch-results.result-caption",pp));
493			}
494			else if(node.getUserObject() instanceof String)
495			{
496				// file name
497				ResultCellRenderer.this.setFont(boldFont);
498				int count = node.getChildCount();
499				if(count == 1)
500				{
501					setText(jEdit.getProperty("hypersearch-results"
502						+ ".file-caption1",new Object[] {
503						node.getUserObject()
504						}));
505				}
506				else
507				{
508					setText(jEdit.getProperty("hypersearch-results"
509						+ ".file-caption",new Object[] {
510						node.getUserObject(),
511						new Integer(count)
512						}));
513				}
514			}
515			else
516			{
517				ResultCellRenderer.this.setFont(plainFont);
518			}
519
520			return this;
521		} //}}}
522	} //}}}
523
524	// these are used to eliminate code duplication. i don't normally use
525	// the visitor or "template method" pattern, but this code was contributed
526	// by Peter Cox and i don't feel like changing it around too much.
527
528	//{{{ ResultVisitor interface
529	interface ResultVisitor
530	{
531		public void visit(Buffer buffer, HyperSearchResult result);
532	} //}}}
533
534	//{{{ BufferLoadedVisitor class
535	class BufferLoadedVisitor implements ResultVisitor
536	{
537		public void visit(Buffer buffer, HyperSearchResult result)
538		{
539			result.bufferOpened(buffer);
540		}
541	} //}}}
542
543	//{{{ BufferClosedVisitor class
544	class BufferClosedVisitor implements ResultVisitor
545	{
546		public void visit(Buffer buffer, HyperSearchResult result)
547		{
548			result.bufferClosed();
549		}
550	} //}}}
551}