001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.log4j.lf5.viewer.categoryexplorer; 018 019import java.awt.Component; 020import java.awt.event.ActionEvent; 021import java.awt.event.ActionListener; 022import java.awt.event.MouseAdapter; 023import java.awt.event.MouseEvent; 024import java.util.ArrayList; 025import java.util.Enumeration; 026 027import javax.swing.JCheckBox; 028import javax.swing.JMenuItem; 029import javax.swing.JOptionPane; 030import javax.swing.JPopupMenu; 031import javax.swing.JTree; 032import javax.swing.tree.TreePath; 033 034/** 035 * CategoryNodeEditor 036 * 037 * @author Michael J. Sikorsky 038 * @author Robert Shaw 039 */ 040 041// Contributed by ThoughtWorks Inc. 042 043public class CategoryNodeEditor extends CategoryAbstractCellEditor { 044 //-------------------------------------------------------------------------- 045 // Constants: 046 //-------------------------------------------------------------------------- 047 048 //-------------------------------------------------------------------------- 049 // Protected Variables: 050 //-------------------------------------------------------------------------- 051 protected CategoryNodeEditorRenderer _renderer; 052 protected CategoryNode _lastEditedNode; 053 protected JCheckBox _checkBox; 054 protected CategoryExplorerModel _categoryModel; 055 protected JTree _tree; 056 057 //-------------------------------------------------------------------------- 058 // Private Variables: 059 //-------------------------------------------------------------------------- 060 061 //-------------------------------------------------------------------------- 062 // Constructors: 063 //-------------------------------------------------------------------------- 064 065 public CategoryNodeEditor(CategoryExplorerModel model) { 066 _renderer = new CategoryNodeEditorRenderer(); 067 _checkBox = _renderer.getCheckBox(); 068 _categoryModel = model; 069 070 _checkBox.addActionListener(new ActionListener() { 071 public void actionPerformed(ActionEvent e) { 072 _categoryModel.update(_lastEditedNode, _checkBox.isSelected()); 073 stopCellEditing(); 074 } 075 }); 076 077 _renderer.addMouseListener(new MouseAdapter() { 078 public void mousePressed(MouseEvent e) { 079 if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) { 080 showPopup(_lastEditedNode, e.getX(), e.getY()); 081 } 082 stopCellEditing(); 083 } 084 }); 085 } 086 087 //-------------------------------------------------------------------------- 088 // Public Methods: 089 //-------------------------------------------------------------------------- 090 091 public Component getTreeCellEditorComponent(JTree tree, Object value, 092 boolean selected, boolean expanded, 093 boolean leaf, int row) { 094 _lastEditedNode = (CategoryNode) value; 095 _tree = tree; 096 097 return _renderer.getTreeCellRendererComponent(tree, 098 value, selected, expanded, 099 leaf, row, true); 100 // hasFocus ignored 101 } 102 103 public Object getCellEditorValue() { 104 return _lastEditedNode.getUserObject(); 105 } 106 //-------------------------------------------------------------------------- 107 // Protected Methods: 108 //-------------------------------------------------------------------------- 109 110 protected JMenuItem createPropertiesMenuItem(final CategoryNode node) { 111 JMenuItem result = new JMenuItem("Properties"); 112 result.addActionListener(new ActionListener() { 113 public void actionPerformed(ActionEvent e) { 114 showPropertiesDialog(node); 115 } 116 }); 117 return result; 118 } 119 120 protected void showPropertiesDialog(CategoryNode node) { 121 JOptionPane.showMessageDialog( 122 _tree, 123 getDisplayedProperties(node), 124 "Category Properties: " + node.getTitle(), 125 JOptionPane.PLAIN_MESSAGE 126 ); 127 } 128 129 protected Object getDisplayedProperties(CategoryNode node) { 130 ArrayList result = new ArrayList(); 131 result.add("Category: " + node.getTitle()); 132 if (node.hasFatalRecords()) { 133 result.add("Contains at least one fatal LogRecord."); 134 } 135 if (node.hasFatalChildren()) { 136 result.add("Contains descendants with a fatal LogRecord."); 137 } 138 result.add("LogRecords in this category alone: " + 139 node.getNumberOfContainedRecords()); 140 result.add("LogRecords in descendant categories: " + 141 node.getNumberOfRecordsFromChildren()); 142 result.add("LogRecords in this category including descendants: " + 143 node.getTotalNumberOfRecords()); 144 return result.toArray(); 145 } 146 147 protected void showPopup(CategoryNode node, int x, int y) { 148 JPopupMenu popup = new JPopupMenu(); 149 popup.setSize(150, 400); 150 // 151 // Configure the Popup 152 // 153 if (node.getParent() == null) { 154 popup.add(createRemoveMenuItem()); 155 popup.addSeparator(); 156 } 157 popup.add(createSelectDescendantsMenuItem(node)); 158 popup.add(createUnselectDescendantsMenuItem(node)); 159 popup.addSeparator(); 160 popup.add(createExpandMenuItem(node)); 161 popup.add(createCollapseMenuItem(node)); 162 popup.addSeparator(); 163 popup.add(createPropertiesMenuItem(node)); 164 popup.show(_renderer, x, y); 165 } 166 167 protected JMenuItem createSelectDescendantsMenuItem(final CategoryNode node) { 168 JMenuItem selectDescendants = 169 new JMenuItem("Select All Descendant Categories"); 170 selectDescendants.addActionListener( 171 new ActionListener() { 172 public void actionPerformed(ActionEvent e) { 173 _categoryModel.setDescendantSelection(node, true); 174 } 175 } 176 ); 177 return selectDescendants; 178 } 179 180 protected JMenuItem createUnselectDescendantsMenuItem(final CategoryNode node) { 181 JMenuItem unselectDescendants = 182 new JMenuItem("Deselect All Descendant Categories"); 183 unselectDescendants.addActionListener( 184 185 new ActionListener() { 186 public void actionPerformed(ActionEvent e) { 187 _categoryModel.setDescendantSelection(node, false); 188 } 189 } 190 191 ); 192 return unselectDescendants; 193 } 194 195 protected JMenuItem createExpandMenuItem(final CategoryNode node) { 196 JMenuItem result = new JMenuItem("Expand All Descendant Categories"); 197 result.addActionListener(new ActionListener() { 198 public void actionPerformed(ActionEvent e) { 199 expandDescendants(node); 200 } 201 }); 202 return result; 203 } 204 205 protected JMenuItem createCollapseMenuItem(final CategoryNode node) { 206 JMenuItem result = new JMenuItem("Collapse All Descendant Categories"); 207 result.addActionListener(new ActionListener() { 208 public void actionPerformed(ActionEvent e) { 209 collapseDescendants(node); 210 } 211 }); 212 return result; 213 } 214 215 /** 216 * This featured was moved from the LogBrokerMonitor class 217 * to the CategoryNodeExplorer so that the Category tree 218 * could be pruned from the Category Explorer popup menu. 219 * This menu option only appears when a user right clicks on 220 * the Category parent node. 221 * 222 * See removeUnusedNodes() 223 */ 224 protected JMenuItem createRemoveMenuItem() { 225 JMenuItem result = new JMenuItem("Remove All Empty Categories"); 226 result.addActionListener(new ActionListener() { 227 public void actionPerformed(ActionEvent e) { 228 while (removeUnusedNodes() > 0) ; 229 } 230 }); 231 return result; 232 } 233 234 protected void expandDescendants(CategoryNode node) { 235 Enumeration descendants = node.depthFirstEnumeration(); 236 CategoryNode current; 237 while (descendants.hasMoreElements()) { 238 current = (CategoryNode) descendants.nextElement(); 239 expand(current); 240 } 241 } 242 243 protected void collapseDescendants(CategoryNode node) { 244 Enumeration descendants = node.depthFirstEnumeration(); 245 CategoryNode current; 246 while (descendants.hasMoreElements()) { 247 current = (CategoryNode) descendants.nextElement(); 248 collapse(current); 249 } 250 } 251 252 /** 253 * Removes any inactive nodes from the Category tree. 254 */ 255 protected int removeUnusedNodes() { 256 int count = 0; 257 CategoryNode root = _categoryModel.getRootCategoryNode(); 258 Enumeration enumeration = root.depthFirstEnumeration(); 259 while (enumeration.hasMoreElements()) { 260 CategoryNode node = (CategoryNode) enumeration.nextElement(); 261 if (node.isLeaf() && node.getNumberOfContainedRecords() == 0 262 && node.getParent() != null) { 263 _categoryModel.removeNodeFromParent(node); 264 count++; 265 } 266 } 267 268 return count; 269 } 270 271 protected void expand(CategoryNode node) { 272 _tree.expandPath(getTreePath(node)); 273 } 274 275 protected TreePath getTreePath(CategoryNode node) { 276 return new TreePath(node.getPath()); 277 } 278 279 protected void collapse(CategoryNode node) { 280 _tree.collapsePath(getTreePath(node)); 281 } 282 283 //----------------------------------------------------------------------- 284 // Private Methods: 285 //-------------------------------------------------------------------------- 286 287 //-------------------------------------------------------------------------- 288 // Nested Top-Level Classes or Interfaces: 289 //-------------------------------------------------------------------------- 290 291}