Ptidej-v5.2-master/POM Tests/data/ArgoUML_0.28/src/org/argouml/cognitive/ui/ToDoPane.java
// $Id: ToDoPane.java 16299 2008-12-07 21:18:09Z tfmorris $
// Copyright (c) 1996-2008 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies.  This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason.  IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

package org.argouml.cognitive.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import org.apache.log4j.Logger;
import org.argouml.cognitive.Designer;
import org.argouml.cognitive.ToDoItem;
import org.argouml.cognitive.ToDoList;
import org.argouml.cognitive.ToDoListEvent;
import org.argouml.cognitive.ToDoListListener;
import org.argouml.cognitive.Translator;
import org.argouml.ui.DisplayTextTree;
import org.argouml.ui.PerspectiveSupport;
import org.argouml.ui.ProjectBrowser;
import org.argouml.ui.SplashScreen;

/**
 * The lower-left pane of the main ArgoUML window, which shows the list
 * of active critics and todo items. <p>
 *
 * This pane shows a list or tree of all the "to do" items that
 * the designer should consider. <p>
 *
 * This class is similar to the NavigatorPane.
 * It uses the same treemodel class and JTree implementation. <p>
 *
 * Perspectives are now built here. <p>
 *
 * Future plans may involve:<ol>
 * <li> DecisionModelListener implementation
 * <li> GoalListener implementation
 * </ol>
 *
 *<pre>
 * possible future additions:
 *  ToDoPerspective difficulty = new ToDoByDifficulty();
 *  ToDoPerspective skill = new ToDoBySkill();
 *</pre>
 */
public class ToDoPane extends JPanel
    implements ItemListener,
        TreeSelectionListener,
        MouseListener,
        ToDoListListener {
    /**
     * Logger.
     */
    private static final Logger LOG = Logger.getLogger(ToDoPane.class);

    private static final int WARN_THRESHOLD = 50;
    private static final int ALARM_THRESHOLD = 100;
    private static final Color WARN_COLOR = Color.yellow;
    private static final Color ALARM_COLOR = Color.pink;

    private static int clicksInToDoPane;
    private static int dblClicksInToDoPane;
    private static int toDoPerspectivesChanged;

    private JTree tree;
    private JComboBox combo;

    /**
     * List of TreeModels.
     */
    private List<ToDoPerspective> perspectives;
    private ToDoPerspective curPerspective;

    private ToDoList root;
    private JLabel countLabel;
    private Object lastSel;


    /**
     * Construct the ToDoPane.
     *
     * @param splash if not null, then we have to show progress in the splash
     */
    // TODO: This should take a ProgressMonitor or something more generic
    // (or nothing at all since it doesn't do that much work)
    public ToDoPane(SplashScreen splash) {

        setLayout(new BorderLayout());

        combo = new JComboBox();
        tree = new DisplayTextTree();

        perspectives = new ArrayList<ToDoPerspective>();

        countLabel = new JLabel(formatCountLabel(999));
        countLabel.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));

        JPanel toolbarPanel = new JPanel(new BorderLayout());
        toolbarPanel.add(countLabel, BorderLayout.EAST);
        toolbarPanel.add(combo, BorderLayout.CENTER);
        add(toolbarPanel, BorderLayout.NORTH);

        add(new JScrollPane(tree), BorderLayout.CENTER);

        combo.addItemListener(this);

        tree.addTreeSelectionListener(this);
        tree.setCellRenderer(new ToDoTreeRenderer());
        tree.addMouseListener(this);

        // next line coming from projectbrowser
        setRoot(Designer.theDesigner().getToDoList());
        Designer.theDesigner().getToDoList().addToDoListListener(this);

        if (splash != null) {
            splash.getStatusBar().showStatus(
	            Translator.localize("statusmsg.bar.making-todopane"));
            splash.getStatusBar().showProgress(25);
        }

        setPerspectives(buildPerspectives());

        setMinimumSize(new Dimension(120, 100));

        Dimension preferredSize = getPreferredSize();
        preferredSize.height = 120;
        setPreferredSize(preferredSize);
    }


    /**
     * @param r the root
     */
    public void setRoot(ToDoList r) {
        root = r;
        updateTree();
    }

    /**
     * @return the root
     */
    public ToDoList getRoot() {
        return root;
    }


    /**
     * @return the perspectives treemodels
     */
    public List<ToDoPerspective> getPerspectiveList() {
        return perspectives;
    }
    
    /**
     * @param pers the perspectives
     */
    public void setPerspectives(List<ToDoPerspective> pers) {
        perspectives = pers;
        if (pers.isEmpty()) {
            curPerspective = null;
        } else {
            curPerspective = pers.get(0);
        }

        for (ToDoPerspective tdp : perspectives) {
            combo.addItem(tdp);
        }

        if (pers.isEmpty()) {
            curPerspective = null;
        } else if (pers.contains(curPerspective)) {
            setCurPerspective(curPerspective);
        } else {
            setCurPerspective(perspectives.get(0));
        }
        updateTree();
    }
    
    /**
     * @return the current perspectives
     */
    public ToDoPerspective getCurPerspective() {
        return curPerspective;
    }

    /**
     * @param per the current perspective
     */
    public void setCurPerspective(TreeModel per) {
        if (perspectives == null || !perspectives.contains(per)) {
	    return;
	}
        combo.setSelectedItem(per);
        toDoPerspectivesChanged++;
    }

    /**
     * @return the last <code>Object</code> in the first selected node's
     *      <code>TreePath</code>,
     *      or <code>null</code> if nothing is selected
     */
    public Object getSelectedObject() {
        return tree.getLastSelectedPathComponent();
    }

    /**
     * @param item the item to be selected
     */
    public void selectItem(ToDoItem item) {
        Object[] path = new Object[3];
        Object category = null;
        int size = curPerspective.getChildCount(root);
        for (int i = 0; i < size; i++) {
            category = curPerspective.getChild(root, i);
            if (curPerspective.getIndexOfChild(category, item) != -1) {
                break;
	    }
        }
        if (category == null) {
	    return;
	}
        path[0] = root;
        path[1] = category;
        path[2] = item;
        TreePath trPath = new TreePath(path);
        tree.expandPath(trPath);
        tree.scrollPathToVisible(trPath);
        tree.setSelectionPath(trPath);
    }

    // ------------ ItemListener implementation ----------------------

    /**
     * Called when the user selects a perspective from the perspective
     * combo. <p>
     * {@inheritDoc}
     */
    public void itemStateChanged(ItemEvent e) {
        if (e.getSource() == combo) {
	    updateTree();
	}
    }

    // -------------TreeSelectionListener implementation -----------

    /*
     * @see javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event.TreeSelectionEvent)
     */
    public void valueChanged(TreeSelectionEvent e) {
        LOG.debug("ToDoPane valueChanged");
        //TODO: should fire its own event and ProjectBrowser
        //should register a listener - tfm
        Object sel = getSelectedObject();
        ProjectBrowser.getInstance().setToDoItem(sel);
        LOG.debug("lastselection: " + lastSel);
	LOG.debug("sel: " + sel);
        if (lastSel instanceof ToDoItem) {
	    ((ToDoItem) lastSel).deselect();
	}
        if (sel instanceof ToDoItem) {
	    ((ToDoItem) sel).select();
	}
        lastSel = sel;
    }

    // ------------- MouseListener implementation ---------------

    /*
     * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
     */
    public void mousePressed(MouseEvent e) { 
        // Empty implementation.
    }

    /*
     * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
     */
    public void mouseReleased(MouseEvent e) {  
        // Empty implementation.
    }

    /*
     * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
     */
    public void mouseEntered(MouseEvent e) {  
        // Empty implementation.
    }

    /*
     * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
     */
    public void mouseExited(MouseEvent e) { 
        // Empty implementation.
    }

    /*
     * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
     */
    public void mouseClicked(MouseEvent e) {
        int row = tree.getRowForLocation(e.getX(), e.getY());
        TreePath path = tree.getPathForLocation(e.getX(), e.getY());
        if (row != -1) {
            if (e.getClickCount() >= 2) {
                myDoubleClick(row, path);
            } else {
                mySingleClick(row, path);
            }
        }
        e.consume();
    }

    ////////////////////////////////////////////////////////////////
    // ToDoListListener implementation

    /**
     * Invoke a task on the Swing thread. If we are running on the Swing
     * thread, this happens immediately. Otherwise the task is queued for later
     * execution using SwingUtilities.invokeLater (actually for the moment
     * invokeAndWait but this should be resolved in future).
     * <p>
     * This is necessary because event notification of ToDoListener events is
     * likely to be coming from the ToDo Validity Checker thread running in the
     * background.
     * 
     * @param task a Runnable task who's run() method will be invoked
     */
    private void swingInvoke(Runnable task) {
        if (SwingUtilities.isEventDispatchThread()) {
            task.run();
        } else {
            SwingUtilities.invokeLater(task);
        }
    }
    
    /*
     * @see org.argouml.cognitive.ToDoListListener#toDoItemsChanged(org.argouml.cognitive.ToDoListEvent)
     */
    public void toDoItemsChanged(final ToDoListEvent tde) {
        swingInvoke(new Runnable() {
            public void run() {
                if (curPerspective instanceof ToDoListListener) {
                    ((ToDoListListener) curPerspective).toDoItemsChanged(tde);
                }
            }
        });
    }


    /*
     * @see org.argouml.cognitive.ToDoListListener#toDoItemsAdded(org.argouml.cognitive.ToDoListEvent)
     */
    public void toDoItemsAdded(final ToDoListEvent tde) {
        swingInvoke(new Runnable() {
            public void run() {
                if (curPerspective instanceof ToDoListListener) {
                    ((ToDoListListener) curPerspective).toDoItemsAdded(tde);
                }
                List<ToDoItem> items = tde.getToDoItemList();
                for (ToDoItem todo : items) {
                    if (todo.getPriority() 
                            >= ToDoItem.INTERRUPTIVE_PRIORITY) {
                        // keep nagging until the user solves the problem:
                        // This seems a nice way to nag:
                        selectItem(todo);
                        break; // Only interrupt for one todoitem
                    }
                }
                updateCountLabel();
            }
        });
    }
    
    /*
     * @see org.argouml.cognitive.ToDoListListener#toDoItemsRemoved(org.argouml.cognitive.ToDoListEvent)
     */
    public void toDoItemsRemoved(final ToDoListEvent tde) {
        swingInvoke(new Runnable() {
            public void run() {
                if (curPerspective instanceof ToDoListListener) {
                    ((ToDoListListener) curPerspective).toDoItemsRemoved(tde);
                }
                updateCountLabel();
            }
        });
    }

    /*
     * @see org.argouml.cognitive.ToDoListListener#toDoListChanged(org.argouml.cognitive.ToDoListEvent)
     */
    public void toDoListChanged(final ToDoListEvent tde) {
        swingInvoke(new Runnable() {
            public void run() {
                if (curPerspective instanceof ToDoListListener) {
                    ((ToDoListListener) curPerspective).toDoListChanged(tde);
                }
                updateCountLabel();
            }
        });
    }

    /* TODO: Indicate the direction! */
    private static String formatCountLabel(int size) {
        switch (size) {
	case 0:
	    return Translator.localize("label.todopane.no-items");
	case 1:
	    return MessageFormat.
		format(Translator.localize("label.todopane.item"),
		       new Object[] {
			   Integer.valueOf(size),
		       });
	default:
	    return MessageFormat.
		format(Translator.localize("label.todopane.items"),
		       new Object[] {
			   Integer.valueOf(size),
		       });
        }
    }

    /**
     * Update the count label.
     */
    public void updateCountLabel() {
        int size = Designer.theDesigner().getToDoList().size();
        countLabel.setText(formatCountLabel(size));
        countLabel.setOpaque(size > WARN_THRESHOLD);
        countLabel.setBackground((size >= ALARM_THRESHOLD) ? ALARM_COLOR
				  : WARN_COLOR);
    }

    /**
     * Update the todo tree.
     */
    protected void updateTree() {
        ToDoPerspective tm = (ToDoPerspective) combo.getSelectedItem();
        curPerspective = tm;
        if (curPerspective == null) {
            tree.setVisible(false);
	} else {
            LOG.debug("ToDoPane setting tree model");
            curPerspective.setRoot(root);
            tree.setShowsRootHandles(true);
            tree.setModel(curPerspective);
            tree.setVisible(true); // blinks?
        }
    }

    /**
     * Called when the user clicks once on an item in the tree. <p>
     *
     * Q: What should the difference be between a single
     * and double click? <p>
     * A: A single click selects the todo item in the tree,
     * shows the red indication on the diagram,
     * and selects the todo tab in the details panel.
     * A double click additionally
     * selects the offender in the explorer,
     * and selects the offender in the diagram (blue selection),
     * and selects the properties tab in the details panel.
     * In both cases, the focus (for keyboard actions) remains in the todo tree.
     *
     * @param row the selected row in the tree
     * @param path the path in the tree of the selected item
     */
    public static void mySingleClick(
            @SuppressWarnings("unused") int row, 
            @SuppressWarnings("unused") TreePath path) {
        clicksInToDoPane++;
    }

    /**
     * Called when the user clicks twice on an item in the tree.
     * myDoubleClick will invoke the action() on the ToDoItem.
     *
     * @param row the selected row in the tree
     * @param path the path in the tree of the selected item
     */
    public void myDoubleClick(
            @SuppressWarnings("unused") int row, 
            @SuppressWarnings("unused") TreePath path) {
        dblClicksInToDoPane++;
        if (getSelectedObject() == null) {
	    return;
	}
        Object sel = getSelectedObject();
        if (sel instanceof ToDoItem) {
            ((ToDoItem) sel).action();
        }

        //TODO: should fire its own event and ProjectBrowser
        //TODO: should register a listener
        LOG.debug("2: " + getSelectedObject().toString());
    }

    /**
     * The perspectives to be chosen in the combobox are built here.
     */
    private static List<ToDoPerspective> buildPerspectives() {

        ToDoPerspective priority = new ToDoByPriority();
        ToDoPerspective decision = new ToDoByDecision();
        ToDoPerspective goal = new ToDoByGoal();
        ToDoPerspective offender = new ToDoByOffender();
        ToDoPerspective poster = new ToDoByPoster();
        ToDoPerspective type = new ToDoByType();

        // add the perspectives to a list for the combobox
        List<ToDoPerspective> perspectives = new ArrayList<ToDoPerspective>();

        perspectives.add(priority);
        perspectives.add(decision);
        perspectives.add(goal);
        perspectives.add(offender);
        perspectives.add(poster);
        perspectives.add(type);

        PerspectiveSupport.registerRule(new GoListToDecisionsToItems());
        PerspectiveSupport.registerRule(new GoListToGoalsToItems());
        PerspectiveSupport.registerRule(new GoListToPriorityToItem());
        PerspectiveSupport.registerRule(new GoListToTypeToItem());
        PerspectiveSupport.registerRule(new GoListToOffenderToItem());
        PerspectiveSupport.registerRule(new GoListToPosterToItem());

        return perspectives;
    }

    /**
     * The UID.
     */
    private static final long serialVersionUID = 1911401582875302996L;
}
