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}