diff --git a/api.search/nbproject/project.properties b/api.search/nbproject/project.properties --- a/api.search/nbproject/project.properties +++ b/api.search/nbproject/project.properties @@ -1,3 +1,5 @@ +auxiliary.org-zeroturnaround-jrebel-netbeans.remoting_2e_private=3082025c020100028181008aa7f7b908d077fb77a6d206d73a27f9ec2f105da2a59eca8219b56ae34c52f9690ceb2ef50148b54e4374027a2d385174a045921348bddbfe301eb38025dfbc60dc1db16b026201f43b1a64584412047848e6b6921d42c34aa76b60ccbba6e3b2e1be6d0f1d1f6bd1c2c511a79802363854ea5f694a40d22ffd24dd19631e13020301000102818063c0b5d2d4098ed8465ba8ec2cf974fb33b5245409089835dee57a043d2492e9466f79df9d7a6001b81d851fa720534d1ed79355a534070b7752cd339c7523018207110168e82df66c3e18eb0ae3975c63e5c7d7303c88f829ad9c3d798a4e7568dd461446ab9728726e6f5cff9ebcbbcdd9ae8f7d91c904b7dfe01b87930ea1024100e6d31751fe491a1699d7856793cc99d7d5d60c5cb6b48d2841457bbb8b5f181d5390d5beb3f65ca5894c574ce7613f82603eaef3f468b3aa86f3b1a9a4debd6302410099c76b3dbed24bb8e3a9a23d816c8c9594f1be76fb22b9e9b53dc94bbbbc26b3b2bcf282d6d69c2e4740a0c6fa209de30d1ed8bdc8e9d5711b65b2ed0bbe93910241009f40c4b62d5b0244d59f7ccb198df01699d2347543190ecad73db9408bc7f03b4b8c81ec30fda8ae0d2c3e8f309447dd0bca33aadcd5e01fa73c3b377028bd41024020e8ad498c71a2a4d18bb6dda4b9df280fb4e046c41dbd5d875cce9c3773df706b5ac6273be4b78dabcfe7d84d24f7c4e444eaab4ab8c7e5a9b04ce72fb13c1102404c316288f1a0de9526f6c50539728e313859b072673554866bdaecaf5049469116fbab26906da603895d6101be889dace3b4ed6a2f6c07e62b1afe999901d3c1 +auxiliary.org-zeroturnaround-jrebel-netbeans.remoting_2e_public=308189028181008aa7f7b908d077fb77a6d206d73a27f9ec2f105da2a59eca8219b56ae34c52f9690ceb2ef50148b54e4374027a2d385174a045921348bddbfe301eb38025dfbc60dc1db16b026201f43b1a64584412047848e6b6921d42c34aa76b60ccbba6e3b2e1be6d0f1d1f6bd1c2c511a79802363854ea5f694a40d22ffd24dd19631e130203010001 is.autoload=true javac.source=1.6 javac.compilerargs=-Xlint -Xlint:-serial diff --git a/api.search/src/org/netbeans/modules/search/MatchingObject.java b/api.search/src/org/netbeans/modules/search/MatchingObject.java --- a/api.search/src/org/netbeans/modules/search/MatchingObject.java +++ b/api.search/src/org/netbeans/modules/search/MatchingObject.java @@ -538,14 +538,14 @@ List detailNodes = new ArrayList(textDetails.size()); for (TextDetail txtDetail : textDetails) { - detailNodes.add(new TextDetail.DetailNode(txtDetail, false)); + detailNodes.add(new TextDetail.DetailNode(txtDetail, false, resultModel)); } return detailNodes.toArray(new Node[detailNodes.size()]); } public Children getDetailsChildren(boolean replacing) { - return new DetailsChildren(replacing); + return new DetailsChildren(replacing, resultModel); } /** @@ -1053,15 +1053,17 @@ private class DetailsChildren extends Children.Keys { private boolean replacing; + private final ResultModel model; - public DetailsChildren(boolean replacing) { + public DetailsChildren(boolean replacing, ResultModel model) { this.replacing = replacing; setKeys(getTextDetails()); + this.model = model; } @Override protected Node[] createNodes(TextDetail key) { - return new Node[]{new TextDetail.DetailNode(key, replacing)}; + return new Node[]{new TextDetail.DetailNode(key, replacing, model)}; } } diff --git a/api.search/src/org/netbeans/modules/search/ResultModel.java b/api.search/src/org/netbeans/modules/search/ResultModel.java --- a/api.search/src/org/netbeans/modules/search/ResultModel.java +++ b/api.search/src/org/netbeans/modules/search/ResultModel.java @@ -48,12 +48,16 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.nio.charset.Charset; +import java.util.*; import java.util.List; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.netbeans.modules.search.Constants.Limit; +import org.netbeans.modules.search.ui.*; import org.openide.ErrorManager; +import org.openide.filesystems.*; import org.openide.filesystems.FileObject; +import org.openide.loaders.*; import org.openide.nodes.Node; @@ -65,6 +69,8 @@ */ public final class ResultModel { + public static final String PROP_REMOVED = "remove"; //NOI18N + public static final String PROP_REMOVEDDETAIL = "removeDetail"; //NOI18N public static final String PROP_SELECTION = "selection"; //NOI18N public static final String PROP_VALID = "valid"; //NOI18N public static final String PROP_MATCHING_OBJECTS = @@ -118,6 +124,76 @@ isFullText = (basicCriteria != null) && basicCriteria.isFullText(); startTime = -1; } + + /** + * Remove the {@link MatchingObject} from the model and informs the listeners. + * @param mo + */ + public void remove (MatchingObject mo) { + + matchingObjects.remove(mo); + totalDetailsCount -= getDetailsCount(mo); + + mo.cleanup(); + // inform listeners, old object contains removed object + propertyChangeSupport.firePropertyChange(PROP_REMOVED, Arrays.asList(mo), null); + } + + public void removeDetailMatch (TextDetail txtDetail) { + MatchingObject found = getMatchingObjectFor(txtDetail); + System.out.println("found "+found); + + if (null != found ) { + found.textDetails.remove(txtDetail); + totalDetailsCount--; + + if (found.textDetails.isEmpty()){ + remove(found); + }else{ + propertyChangeSupport.firePropertyChange(PROP_REMOVEDDETAIL, Arrays.asList(), null); + + } + // inform listeners, old object contains removed object + } + + } + + + public void removeFolder (DataObject folder) { + List list = new ArrayList(); + + for (MatchingObject mo : matchingObjects) { + FileObject folderOfFile = mo.getFileObject().getParent(); + FileObject folderToRemove = folder.getPrimaryFile(); + boolean isInFolder = folderOfFile.equals(folderToRemove); + boolean isInSubFolder = FileUtil.isParentOf(folderToRemove, folderOfFile); + if (isInFolder || isInSubFolder){ + list.add(mo); + }; + } + + matchingObjects.removeAll(list); + for (MatchingObject mo : list) { + + totalDetailsCount -= getDetailsCount(mo); + mo.cleanup(); + } + + + // inform listeners, old object contains removed objects + propertyChangeSupport.firePropertyChange(PROP_REMOVED, list, null); + } + + private MatchingObject getMatchingObjectFor (TextDetail txtDetail) { + for (MatchingObject mo : matchingObjects) { + for (TextDetail textDetail : mo.getTextDetails()) { + if (textDetail == txtDetail) { + return mo; + } + } + } + return null; + } /** */ @@ -450,6 +526,7 @@ } } + private class MatchSelectionListener implements ChangeListener { @Override diff --git a/api.search/src/org/netbeans/modules/search/TextDetail.java b/api.search/src/org/netbeans/modules/search/TextDetail.java --- a/api.search/src/org/netbeans/modules/search/TextDetail.java +++ b/api.search/src/org/netbeans/modules/search/TextDetail.java @@ -49,7 +49,7 @@ import java.awt.Toolkit; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; -import java.io.CharConversionException; +import java.io.*; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -451,6 +451,7 @@ /** Cached toString value. */ private String name; private String htmlDisplayName; + private final ResultModel model; /** * Constructs a node representing the specified information about @@ -458,11 +459,12 @@ * * @param txtDetail information to be represented by this node */ - public DetailNode(TextDetail txtDetail, boolean replacing) { + public DetailNode(TextDetail txtDetail, boolean replacing, ResultModel model) { super(Children.LEAF, Lookups.fixed(txtDetail, new ReplaceCheckableNode(txtDetail, replacing))); this.txtDetail = txtDetail; + this.model = model; setShortDescription(DetailNode.getShortDesc(txtDetail)); setValue(SearchDisplayer.ATTR_OUTPUT_LINE, @@ -823,6 +825,20 @@ @Override protected void createPasteTypes(Transferable t, List s) { } + + @Override + public boolean canDestroy () { + return true; + } + + @Override + public void destroy () throws IOException { + this.model.removeDetailMatch(txtDetail); +// super.destroy(); //To change body of generated methods, choose Tools | Templates. + } + + + } // End of DetailNode class. diff --git a/api.search/src/org/netbeans/modules/search/ui/AbstractSearchResultsPanel.java b/api.search/src/org/netbeans/modules/search/ui/AbstractSearchResultsPanel.java --- a/api.search/src/org/netbeans/modules/search/ui/AbstractSearchResultsPanel.java +++ b/api.search/src/org/netbeans/modules/search/ui/AbstractSearchResultsPanel.java @@ -126,6 +126,12 @@ this.searchProviderPresenter = searchProviderPresenter; initComponents(); explorerManager = new ExplorerManager(); + + + ActionMap map = this.getActionMap(); + // map delete key to delete action + map.put("delete", ExplorerUtils.actionDelete(explorerManager, false)); + lookup = ExplorerUtils.createLookup(explorerManager, ResultView.getInstance().getActionMap()); initActions(); diff --git a/api.search/src/org/netbeans/modules/search/ui/BasicAbstractResultsPanel.java b/api.search/src/org/netbeans/modules/search/ui/BasicAbstractResultsPanel.java --- a/api.search/src/org/netbeans/modules/search/ui/BasicAbstractResultsPanel.java +++ b/api.search/src/org/netbeans/modules/search/ui/BasicAbstractResultsPanel.java @@ -45,6 +45,7 @@ import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.beans.*; import java.beans.PropertyVetoException; import java.util.List; import java.util.ResourceBundle; @@ -76,7 +77,7 @@ * @author jhavlin */ public abstract class BasicAbstractResultsPanel - extends AbstractSearchResultsPanel { + extends AbstractSearchResultsPanel implements PropertyChangeListener { @StaticResource private static final String SHOW_DETAILS_ICON = @@ -123,8 +124,14 @@ setRootDisplayName(NbBundle.getMessage(ResultView.class, "TEXT_SEARCHING___")); //NOI18N initAccessibility(); + this.resultModel.addPropertyChangeListener(ResultModel.PROP_REMOVED, this); } + @Override + public void propertyChange (PropertyChangeEvent evt) { + // update the root node after change in model + setFinalRootNodeText(); + } public void update() { if (details && btnExpand.isVisible() && !btnExpand.isEnabled()) { diff --git a/api.search/src/org/netbeans/modules/search/ui/MatchingObjectNode.java b/api.search/src/org/netbeans/modules/search/ui/MatchingObjectNode.java --- a/api.search/src/org/netbeans/modules/search/ui/MatchingObjectNode.java +++ b/api.search/src/org/netbeans/modules/search/ui/MatchingObjectNode.java @@ -54,11 +54,14 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.List; +import javax.swing.*; import javax.swing.AbstractAction; import javax.swing.Action; import org.netbeans.api.annotations.common.StaticResource; +import org.netbeans.modules.search.*; import org.netbeans.modules.search.MatchingObject; import org.netbeans.modules.search.MatchingObject.InvalidityStatus; +import org.openide.actions.*; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; @@ -100,22 +103,24 @@ private boolean valid = true; private PropertyChangeListener validityListener; private PropertyChangeListener selectionListener; + private ResultModel resultModel; PropertySet[] propertySets; public MatchingObjectNode(Node original, org.openide.nodes.Children children, - MatchingObject matchingObject, final boolean replacing) { + MatchingObject matchingObject, final boolean replacing, ResultModel model) { this(original, children, matchingObject, - new ReplaceCheckableNode(matchingObject, replacing)); + new ReplaceCheckableNode(matchingObject, replacing), model); } private MatchingObjectNode(Node original, org.openide.nodes.Children children, final MatchingObject matchingObject, - ReplaceCheckableNode checkableNode) { + ReplaceCheckableNode checkableNode, ResultModel model) { super(children, Lookups.fixed(matchingObject, checkableNode, matchingObject.getFileObject())); Parameters.notNull("original", original); //NOI18N + this.resultModel = model; this.matchingObject = matchingObject; if (matchingObject.isObjectValid()) { this.original = original; @@ -165,7 +170,8 @@ if (!context) { return new Action[]{ SystemAction.get(OpenMatchingObjectsAction.class), - new CopyPathAction() + new CopyPathAction(), + SystemAction.get(DeleteAction.class) }; } else { return new Action[0]; @@ -234,7 +240,7 @@ @Override public boolean canDestroy() { - return false; + return true; } public void clean() { @@ -274,6 +280,12 @@ return propertySets; } + @Override + public void destroy () throws IOException { + // when removing the node, the node's content is removed from model + resultModel.remove(this.matchingObject); + } + /** * Check whether the file object is valid and a valid data object can be * found for it. It should be checked after original node is destroyed. It diff --git a/api.search/src/org/netbeans/modules/search/ui/ResultsOutlineSupport.java b/api.search/src/org/netbeans/modules/search/ui/ResultsOutlineSupport.java --- a/api.search/src/org/netbeans/modules/search/ui/ResultsOutlineSupport.java +++ b/api.search/src/org/netbeans/modules/search/ui/ResultsOutlineSupport.java @@ -52,11 +52,13 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; +import java.util.*; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; +import javax.swing.*; import javax.swing.Action; import javax.swing.JScrollPane; import javax.swing.KeyStroke; @@ -68,10 +70,7 @@ import javax.swing.event.TreeExpansionListener; import javax.swing.table.TableColumn; import org.netbeans.api.annotations.common.StaticResource; -import org.netbeans.modules.search.FindDialogMemory; -import org.netbeans.modules.search.MatchingObject; -import org.netbeans.modules.search.ResultModel; -import org.netbeans.modules.search.Selectable; +import org.netbeans.modules.search.*; import org.netbeans.modules.search.ui.AbstractSearchResultsPanel.RootNode; import org.netbeans.swing.etable.ETableColumnModel; import org.netbeans.swing.outline.Outline; @@ -81,6 +80,7 @@ import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; import org.openide.loaders.DataObjectNotFoundException; +import org.openide.nodes.*; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.FilterNode; @@ -94,7 +94,7 @@ * * @author jhavlin */ -public class ResultsOutlineSupport { +public class ResultsOutlineSupport implements PropertyChangeListener { @StaticResource private static final String ROOT_NODE_ICON = @@ -123,11 +123,26 @@ this.details = details; this.resultModel = resultModel; this.rootFiles = rootFiles; - this.resultsNode = new ResultsNode(); + this.resultsNode = new ResultsNode(resultModel); this.infoNode = infoNode; this.invisibleRoot = new RootNode(resultsNode, infoNode); this.matchingObjectNodes = new LinkedList(); createOutlineView(); + this.resultModel.addPropertyChangeListener(ResultModel.PROP_REMOVED, this); + } + + + @Override + public void propertyChange (PropertyChangeEvent evt) { + List items = (List) evt.getOldValue(); + + //support folders (multiple matching objects) or single matching objects + for (MatchingObject mo : items) { + removedMatchingObject(mo); + } + + // rebuild the tree from the modified model + this.update(); } private void createOutlineView() { @@ -301,10 +316,10 @@ private FolderTreeChildren folderTreeChildren; private String htmlDisplayName = null; - public ResultsNode() { + public ResultsNode(ResultModel model) { super(new FlatChildren()); this.flatChildren = (FlatChildren) this.getChildren(); - this.folderTreeChildren = new FolderTreeChildren(rootPathItem); + this.folderTreeChildren = new FolderTreeChildren(rootPathItem, model); } void update() { @@ -402,7 +417,7 @@ children = key.getDetailsChildren(replacing); } MatchingObjectNode mon = - new MatchingObjectNode(delegate, children, key, replacing); + new MatchingObjectNode(delegate, children, key, replacing, resultModel); matchingObjectNodes.add(mon); return mon; } @@ -422,6 +437,20 @@ addToTreeView(rootPathItem, Collections.singletonList(mo.getFileObject()), mo); } + public synchronized void removedMatchingObject(MatchingObject mo) { + if (closed) { + return; + } + removeFilesFromTreeView(rootPathItem, mo); + removeEmptyFoldersFromTreeView(rootPathItem); + } + public synchronized void removedTextDetail(TextDetail detail) { + if (closed) { + return; + } +// removeFilesFromTreeView(rootPathItem, mo); + removeEmptyFoldersFromTreeView(rootPathItem); + } private List getRelativePath(FileObject parent, FileObject fo) { List l = new LinkedList(); @@ -436,6 +465,41 @@ return l; } + private void removeFilesFromTreeView (FolderTreeItem parentItem, MatchingObject matchingObject) { + List removeableItems = new ArrayList(); + + for (FolderTreeItem pi : parentItem.getChildren()) { + if (!pi.isPathLeaf()) { + //start recursion for folders + removeFilesFromTreeView(pi, matchingObject); + } else { + if (pi.getMatchingObject().equals(matchingObject)) { + // collect all the files which have to be removed + removeableItems.add(pi); + } + } + } + parentItem.removeChildren(removeableItems); + } + + private void removeEmptyFoldersFromTreeView (FolderTreeItem parentItem) { + List emptyFolders = new ArrayList(); + + for (FolderTreeItem pi : parentItem.getChildren()) { + { + // start recursion + removeEmptyFoldersFromTreeView(pi); + + if (pi.getChildren().isEmpty() && null == pi.getMatchingObject()) { + // collect all the empty folders which have to be removed + emptyFolders.add(pi); + } + } + } + // remove empty folders + parentItem.removeChildren(emptyFolders); + } + private void addToTreeView(FolderTreeItem parentItem, List path, MatchingObject matchingObject) { for (FolderTreeItem pi : parentItem.getChildren()) { @@ -508,6 +572,12 @@ public List getChildren() { return children; } + + public boolean removeChildren (Collection list) { + boolean result = children.removeAll(list); + firePropertyChange(PROP_CHILDREN, null, null); + return result; + } public MatchingObject getMatchingObject() { return matchingObject; @@ -563,13 +633,15 @@ } private class FolderTreeNode extends FilterNode { + private ResultModel model; - public FolderTreeNode(FolderTreeItem pathItem) { + public FolderTreeNode(FolderTreeItem pathItem, ResultModel model) { super(pathItem.getFolder().getNodeDelegate(), - new FolderTreeChildren(pathItem), + new FolderTreeChildren(pathItem, model), Lookups.fixed(pathItem, new ReplaceCheckableNode(pathItem, replacing), pathItem.getFolder().getPrimaryFile())); + this.model=model; pathItem.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { @@ -582,7 +654,7 @@ pathItem.getFolder().getPrimaryFile().getPath()); } } - + @Override public PasteType[] getPasteTypes(Transferable t) { return new PasteType[0]; @@ -594,6 +666,21 @@ } @Override + public boolean canDestroy () { + return true; + } + + @Override + public void destroy () throws IOException { + //FIXME alter the model + FolderTreeItem folder = this.getLookup().lookup(FolderTreeItem.class); + this.model.removeFolder(folder.getFolder()); +// this.resultModel.remove(this.getLookup().lookup(FolderTreeItem.class)); + } + + + + @Override public Transferable drag() throws IOException { return UiUtils.DISABLE_TRANSFER; } @@ -607,8 +694,9 @@ private class FolderTreeChildren extends Children.Keys { private FolderTreeItem item = null; + private final ResultModel model; - public FolderTreeChildren(FolderTreeItem pathItem) { + public FolderTreeChildren(FolderTreeItem pathItem, ResultModel model) { this.item = pathItem; pathItem.addPropertyChangeListener(new PropertyChangeListener() { @Override @@ -619,6 +707,7 @@ } } }); + this.model = model; } @Override @@ -636,7 +725,8 @@ if (key.isPathLeaf()) { n = createNodeForMatchingObject(key.getMatchingObject()); } else { - n = new FolderTreeNode(key); + n = new FolderTreeNode(key, model); + } return new Node[]{n}; } diff --git a/api.search/test/unit/src/org/netbeans/modules/search/ui/MatchingObjectNodeTest.java b/api.search/test/unit/src/org/netbeans/modules/search/ui/MatchingObjectNodeTest.java --- a/api.search/test/unit/src/org/netbeans/modules/search/ui/MatchingObjectNodeTest.java +++ b/api.search/test/unit/src/org/netbeans/modules/search/ui/MatchingObjectNodeTest.java @@ -85,7 +85,7 @@ ResultModel rm = SearchTestUtils.createResultModelWithOneMatch(); MatchingObject mo = rm.getMatchingObjects().get(0); MatchingObjectNode mon = new MatchingObjectNode(n, Children.LEAF, mo, - false); + false, rm); mon.addNodeListener(new DisplayNameChangeListener(s)); mon.getDisplayName(); mo.getFileObject().delete(); @@ -105,7 +105,7 @@ Node original = dob.getNodeDelegate(); fob.delete(); // No exception should be thrown from the constructor. - Node n = new MatchingObjectNode(original, Children.LEAF, mo, false); + Node n = new MatchingObjectNode(original, Children.LEAF, mo, false, rm); assertEquals("test.txt", n.getDisplayName()); }