Logo Search packages:      
Sourcecode: cacao-oj6 version File versions

PathPanel.java

/*
 * $Id$
 *
 * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.interview.wizard;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import javax.accessibility.AccessibleContext;
import javax.swing.AbstractListModel;
import javax.swing.Icon;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.Scrollable;
import javax.swing.UIManager;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.metal.MetalLookAndFeel;

import com.sun.interview.ErrorQuestion;
import com.sun.interview.FinalQuestion;
import com.sun.interview.Interview;
import com.sun.interview.NullQuestion;
import com.sun.interview.Question;

class PathPanel extends JPanel 
    implements Scrollable 
{
    public PathPanel(QuestionPanel questionPanel, Interview interview) {
      this.questionPanel = questionPanel;  //uugh; but needed for autosaving answers before changing questions
      this.interview = interview;
      moreText = i18n.getString("path.more");
      initGUI();
    }

    // ---------- Component stuff ---------------------------------------

    public Dimension getPreferredSize() {
      return list.getPreferredSize(); // should not be necessary
    }

    // ---------- Scrollable stuff ---------------------------------------

    public Dimension getPreferredScrollableViewportSize() {
      return list.getPreferredScrollableViewportSize();
    }
    
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
      return list.getScrollableBlockIncrement(visibleRect, orientation, direction);
    }
    
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
      return list.getScrollableUnitIncrement(visibleRect, orientation, direction);
    }

    public boolean getScrollableTracksViewportHeight() {
      if (getParent() instanceof JViewport) {
          return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
      }
      return false;
    }

    public boolean getScrollableTracksViewportWidth() {
      return true;
    }

    // ---------- end of Scrollable stuff -----------------------------------

    boolean getMarkersEnabled() {
      return markersEnabled;
    }
    
    void setMarkersEnabled(boolean on) {
      if (on != markersEnabled) {
          markersEnabled = on;
          pathList.update();
      }
    }

    boolean getMarkersFilterEnabled() {
      return markersFilterEnabled;
    }
    
    void setMarkersFilterEnabled(boolean on) {
      if (on != markersFilterEnabled) {
          markersFilterEnabled = on;
          pathList.update();
      }
    }

    Question getNextVisible() {
      return pathList.getNextVisible();
    }

    Question getPrevVisible() {
      return pathList.getPrevVisible();
    }

    Question getLastVisible() {
      return pathList.getLastVisible();
    }

    JMenu getMarkerMenu() {
      return createMenu();
    }

    private void initGUI() {
      setName("path");
      setFocusable(false);
      setLayout(new BorderLayout());
      pathList = new PathList();
      list = new JList(pathList);
      setInfo(list, "path.list", true);
      list.setCellRenderer(pathList);
      KeyStroke enterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
      list.registerKeyboardAction(pathList, enterKey, JComponent.WHEN_FOCUSED);
      list.addListSelectionListener(pathList);
      list.addMouseListener(pathList);
      //list.setPrototypeCellValue("What is a good default to use?");
      
      // would be better if this were configurable
      list.setFixedCellWidth(2 * DOTS_PER_INCH);

      list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      list.setVisibleRowCount(5);
      pathList.currentQuestionChanged(interview.getCurrentQuestion());
      addAncestorListener(pathList);
      add(list);
    }
    
    private void setInfo(JComponent jc, String uiKey, boolean addToolTip) {
      jc.setName(uiKey);
      AccessibleContext ac = jc.getAccessibleContext();
      ac.setAccessibleName(i18n.getString(uiKey + ".name"));
      if (addToolTip) {
          String tip = i18n.getString(uiKey + ".tip");
          jc.setToolTipText(tip);
          ac.setAccessibleDescription(tip);
      }
      else
          ac.setAccessibleDescription(i18n.getString(uiKey + ".desc"));
    }

    private QuestionPanel questionPanel;
    private Interview interview;
    private PathList pathList;
    private JList list;
    private String moreText;

    // client parameters
    private boolean markersEnabled;
    private boolean markersFilterEnabled;
    private String markerName = null; // theoretically settable

    private static final I18NResourceBundle i18n = I18NResourceBundle.getDefaultBundle();
    private static Color INVALID_VALUE_COLOR = i18n.getErrorColor();
    private static final int DOTS_PER_INCH = Toolkit.getDefaultToolkit().getScreenResolution();

    private class PathList
            extends AbstractListModel 
            implements ActionListener, AncestorListener, 
                     ListCellRenderer, ListSelectionListener,
                     MouseListener,
                     Interview.Observer 
    {
      //----- navigation support for WizPane -----------------------

      Question getNextVisible() {
          for (int i = currIndex + 1; i < currEntries.length; i++) {
            Object e = currEntries[i];
            if (e instanceof Question)
                return ((Question) e);
          }
          return null;
      }

      Question getPrevVisible() {
          for (int i = currIndex - 1; i >= 0; i--) {
            Object e = currEntries[i];
            if (e instanceof Question)
                return ((Question) e);
          }
          return null;
      }

      Question getLastVisible() {
          for (int i = currEntries.length - 1; i >= 0; i--) {
            Object e = currEntries[i];
            if (e instanceof Question)
                return ((Question) e);
          }
          return null;
      }

      //----- state support for menus -----------------------------

      boolean isQuestionVisible(Question q) {
          for (int i = 0; i < currEntries.length; i++) {
            Object e = currEntries[i];
            if (e instanceof Question && ((Question) e) == q)
                return true;
            else if (e instanceof List && ((List) e).contains(q))
                return false;
          }
          return false;
      }

      boolean isQuestionAutoOpened(Question q) {
          // only return true if the preceding marked question is in the autoOpen set
          boolean autoOpened = autoOpenSet.contains(null);
          for (int i = 0; i < currEntries.length; i++) {
            Object e = currEntries[i];
            if (e instanceof Question) {
                Question qe = (Question) e;
                if (qe.hasMarker(markerName)) {
                  if (qe == q)
                      return false;
                  autoOpened = autoOpenSet.contains(qe);
                }
                else if (qe == q)
                  return autoOpened;
            } 
            else if (e instanceof List && ((List) e).contains(q))
                return false;
          }
          return false;
      }

      //----- actions ------------------------

      void markCurrentQuestion() {
          setQuestionMarked(interview.getCurrentQuestion(), true);
      }

      void unmarkCurrentQuestion() {
          setQuestionMarked(interview.getCurrentQuestion(), false);
      }

      private void setQuestionMarked(Question q, boolean on) {
          questionPanel.saveCurrentResponse();
          if (on)
            q.addMarker(markerName);
          else
            q.removeMarker(markerName);

          pathList.update(q);
      }

      void openCurrentEntry() {
          openEntry(currIndex);
      }
       
      void openEntry(int index) {
          Object o = currEntries[index];

          // only a list can be opened
          if (!(o instanceof List))
            return;

          // the marker question for a List is the question in the preceding entry
          // find that marker question and add it to the autoOpenSet
          for (int i = 1; i < currEntries.length; i++) {
            if (currEntries[i] == o) {
                Object m = currEntries[i - 1];
                if (m instanceof Question)
                  autoOpenSet.add(m);
                update();
            }
          }
      }

      void closeCurrentEntry() {
          closeEntry(currIndex);
      }

      void closeEntry(int index) {
          Object o = currEntries[index];

          // only a question can be closed
          if (!(o instanceof Question))
            return;

          // need to figure out the autoOpenSet entry
          // scan back through entries looking for a marked question
          // or the first question
          Question marker = null;
          for (int i = index; i >= 0; i--) {
            Object ei = currEntries[i];
            if (ei instanceof Question) {
                Question qi = (Question) ei;
                if (i == 0 || qi.hasMarker(markerName)) {
                  marker = qi;
                  break;
                }
            }
          }
          
          // can't close the marker question itself:
          // must close a question after the marker
          if (marker == o)
            return;

          autoOpenSet.remove(marker);
          update();
      }
      
      //----- from AbstractListModel -----------

      public int getSize() {
          return (currEntries == null ? 0 : currEntries.length);
      }

      public Object getElementAt(int index) {
          return (index < currEntries.length ? currEntries[index] : null);
      }

      //----- from ListCellRenderer -----------
      
      private JLabel sample = new JLabel() {
          public Dimension getMaximumSize() {
            return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
          }
      };

      public Component getListCellRendererComponent(JList list, Object o, int index, boolean isSelected, boolean cellHasFocus) {
          if (o instanceof Question) {
            Question q = (Question)o;
            Font f;
            String s;
            Color c;
            Color bg = null;        // null is default
            if (q instanceof ErrorQuestion) {
                f = list.getFont().deriveFont(Font.BOLD);
                s = "   " + q.getSummary();
                c = INVALID_VALUE_COLOR;
            }
            else if (q instanceof NullQuestion) {
                    int level = ((NullQuestion)q).getLevel();

                    switch (level) {
                    case NullQuestion.LEVEL_NONE:
                        f = list.getFont();
                        s = " " + q.getSummary();
                        c = list.getForeground();
                        bg = null;
                        break;
                    case NullQuestion.LEVEL_1:
                        f = list.getFont().deriveFont(Font.BOLD,
                                        list.getFont().getSize() +3);
                        s = q.getSummary();
                        c = new Color(0x63,0x82,0xBF);
                        bg = new Color(0xDD,0xDD,0xDD);
                        break;
                    case NullQuestion.LEVEL_2:
                        f = list.getFont().deriveFont(Font.BOLD);
                        s = q.getSummary();
                        c = new Color(0x63,0x82,0xBF);
                        break;
                    case NullQuestion.LEVEL_3:
                        f = list.getFont().deriveFont(Font.PLAIN);
                        s = "  " + q.getSummary();
                        c = list.getForeground();
                        break;
                    default:        // LEVEL_LEGACY handled here
                        f = list.getFont().deriveFont(Font.BOLD);
                        s = " " + q.getSummary();
                        c = list.getForeground();
                    }   // switch
            }
            else {
                f = list.getFont().deriveFont(Font.PLAIN);
                s = "   " + q.getSummary();
                c = list.getForeground();
            }
            sample.setText(s);
            sample.setFont(f);
            sample.setForeground(c);
                if (bg != null)
                    sample.setBackground(bg);
                else
                    sample.setBackground(list.getBackground());

            if (markersEnabled)
                sample.setIcon(q.hasMarker(markerName) ? markerIcon : noMarkerIcon);
            else 
                sample.setIcon(null);
          }
          else if (o instanceof List) {
            sample.setText(null);
            sample.setFont(list.getFont());
            sample.setForeground(list.getForeground());
            sample.setIcon(ellipsisIcon);
          }
          else if (o instanceof String) {
            // prototype value or more...
            sample.setText(" " + (String)o);
            sample.setFont(list.getFont().deriveFont(Font.ITALIC));
            sample.setForeground(list.getForeground());
            sample.setIcon(markersEnabled ? noMarkerIcon : null);
          }
          else 
            throw new IllegalArgumentException();

          // rest of method based on javax.swing.DefaultListCellRenderer
          if (isSelected) {
            sample.setBackground(list.getSelectionBackground());
            //sample.setForeground(list.getSelectionForeground());
          }
          else {
            //sample.setBackground(list.getBackground());
            //sample.setForeground(list.getForeground());
          }
          sample.setOpaque(true);
          sample.setEnabled(list.isEnabled());
          sample.setBorder((cellHasFocus) ? UIManager.getBorder("List.focusCellHighlightBorder") : null);
          
          return sample;
      }

      //----- from ActionListener -----------

      // invoked by keyboard "enter"
      public void actionPerformed(ActionEvent e) {
          //System.err.println("PP.actionPerformed");
          JList list = (JList)(e.getSource());
          Object o = list.getSelectedValue();
          if (o != null && o instanceof Question) {
            Question q = (Question)o;
            if (q == interview.getCurrentQuestion())
                return;

            //System.err.println("PP.actionPerformed saveCurrentResponse");
            questionPanel.saveCurrentResponse();

            try {
                //System.err.println("PP.actionPerformed setCurrentQuestion");
                interview.setCurrentQuestion(q);
            }
            catch (Interview.Fault ex) {
                // ignore, should never happen; FLW
            }
          }
      }

      //----- from ListSelectionListener -----------
      
      // invoked by mouse selection (or by list.setSelectedXXX ??)
      public void valueChanged(ListSelectionEvent e) {
          JList list = (JList) (e.getSource());
          Object o = list.getSelectedValue();
          if (o == null)
            return;

          // make sure the interview's current question is synchronized with 
          // the list selection
          if (o instanceof Question) {
            Question q = (Question) o;
            if (q == interview.getCurrentQuestion())
                return;

            questionPanel.saveCurrentResponse();

            try {
                interview.setCurrentQuestion(q);
            }
            catch (Interview.Fault ex) {
                // ignore, should never happen; FLW
            }
          }
          else if (o instanceof List) {
            List l = (List) o;
            if (l.contains(interview.getCurrentQuestion()))
                return;

            questionPanel.saveCurrentResponse();
            
            try {
                Question q = (Question) (l.get(0));
                interview.setCurrentQuestion(q);
            }
            catch (Interview.Fault ex) {
                // ignore, should never happen; FLW
            }
                
          }
          else {
            // if the user tries to select the More string,
            // reject the request by resetting to the currIndex
            list.setSelectedIndex(currIndex);
          }
      }

      // ---------- from AncestorListener -----------

      public void ancestorAdded(AncestorEvent e) {
          interview.addObserver(this);
          pathUpdated();
      }

      public void ancestorMoved(AncestorEvent e) { }

      public void ancestorRemoved(AncestorEvent e) {
          interview.removeObserver(this);
      }

      //----- from MouseListener -----------

      public void mouseEntered(MouseEvent e) { }

      public void mouseExited(MouseEvent e) { }

      public void mousePressed(MouseEvent e) { 
          if (markersEnabled && e.isPopupTrigger() && isOverSelection(e))
            showPopupMenu(e);
      }

      public void mouseReleased(MouseEvent e) { 
          if (markersEnabled && e.isPopupTrigger() && isOverSelection(e))
            showPopupMenu(e);
      }

      private boolean isOverSelection(MouseEvent e) {
          JList l = (JList) (e.getComponent());
          Rectangle r = l.getCellBounds(currIndex, currIndex);
          return (r.contains(e.getX(), e.getY()));
      }

      private void showPopupMenu(MouseEvent e) {
          if (popupMenu == null)
            popupMenu = createPopupMenu();
          popupMenu.show(e.getComponent(), e.getX(), e.getY());
      }

      public void mouseClicked(MouseEvent e) {
          if (!markersEnabled)
            return;

          Point p = e.getPoint();
          int index = list.locationToIndex(p);
          if (index == -1)
            return;
          Object entry = currEntries[index];

          switch (e.getClickCount()) {
          case 1:
            if (p.x < markerIcon.getIconWidth()) {
                if (entry instanceof Question) {
                  Question q = (Question) entry;
                  setQuestionMarked(q, !q.hasMarker(markerName));
                }
            }
            break;

          case 2:
            if (markersFilterEnabled) {
                if (entry instanceof List)
                  openEntry(index);
                else
                  closeEntry(index);
            }
            break;
          }
      }

      //----- from Interview.Observer -----------

      public void pathUpdated() {
          update(interview.getPath(), interview.getCurrentQuestion());
      }

      public void currentQuestionChanged(Question q) {
          int prevIndex = currIndex;
          currQuestion = q;
          for (int i = 0; i < currEntries.length; i++) {
            Object o = currEntries[i];
            if (o == q || (o instanceof List && ((List) o).contains(q))) {
                currIndex = i;
                break;
            }
          }
          fireContentsChanged(this, prevIndex, currIndex);
          
          list.setSelectedIndex(currIndex);
          list.ensureIndexIsVisible(currIndex);
      }

      public void finished() {
      }

      void update() {
          update(currPath, currQuestion);
      }

      void update(Question q) {
          if (markersFilterEnabled) 
            update();
          else {
            for (int i = 0; i < currEntries.length; i++) {
                Object e = currEntries[i];
                if (e == q) {
                  currMarks[i] = (markersEnabled && q.hasMarker(markerName));
                  fireContentsChanged(this, i, i);
                  break;
                }
            }
          }
      }
 
      private void update(Question[] newPath, Question newCurrQuestion) {
          boolean oldEnabled = currEnabled;
          Object[] oldEntries = currEntries;
          boolean[] oldMarks = currMarks;

          if (!markersFilterEnabled)
            autoOpenSet.clear();

          Object[] newEntries = getEntries(newPath);
          boolean[] newMarks = new boolean[newEntries.length];
          if (markersEnabled) {
            for (int i = 0; i < newEntries.length; i++) {
                newMarks[i] = (newEntries[i] instanceof Question
                           && ((Question) newEntries[i]).hasMarker(markerName));
            }
          }
          
          currPath = newPath;
          currEntries = newEntries;
          currEnabled = markersEnabled;
          currMarks = newMarks;

          int shorterEntriesLength = Math.min(oldEntries.length, newEntries.length);

          int firstDiff = 0;
          if (currEnabled == oldEnabled) {
            // optimize firstDiff when icons are not changing
            while (firstDiff < shorterEntriesLength) {
                Object oldObj = oldEntries[firstDiff];
                boolean oldMark = oldMarks[firstDiff];
                Object newObj = newEntries[firstDiff];
                boolean newMark = newMarks[firstDiff];
                if (oldObj instanceof Question ? oldObj == newObj && oldMark == newMark
                  : oldObj instanceof List ? newObj instanceof List
                  : false ) {
                  firstDiff++;
                }
                else
                  break;
            }
          }

          if (firstDiff != oldEntries.length || firstDiff != newEntries.length) {
            if (firstDiff != shorterEntriesLength) {
                //System.err.println("PP.update: change[" + firstDiff + "," + (shorterEntriesLength-1) + "/" + oldEntries.length + "," + newEntries.length + "]" + interview);
                fireContentsChanged(this, firstDiff, shorterEntriesLength-1);
            }
            
            if (shorterEntriesLength != oldEntries.length) {
                //System.err.println("PP.update: remove[" + shorterEntriesLength + "," + (oldEntries.length-1) + "/" + oldEntries.length + "," + newEntries.length + "]" + interview);
                fireIntervalRemoved(this, shorterEntriesLength, oldEntries.length-1);
            }
            if (shorterEntriesLength != newEntries.length) {
                //System.err.println("PP.update: add[" + shorterEntriesLength + "," + (newEntries.length-1) + "/" + oldEntries.length + "," + newEntries.length + "]" + interview);
                fireIntervalAdded(this, shorterEntriesLength, newEntries.length-1);
            }
          }
          
          currQuestion = newCurrQuestion;
          for (int i = 0; i < currEntries.length; i++) {
            Object o = currEntries[i];
            if (o == currQuestion 
                || (o instanceof List && ((List) o).contains(currQuestion))) {
                currIndex = i;
                break;
            }
          }
          
          list.setSelectedIndex(currIndex);
          list.ensureIndexIsVisible(currIndex);
          //System.err.println("PP.update: sel:" + currIndex + " " + currQuestion);
      }
      
      private Object[] getEntries(Question[] path) {
          if (path.length == 0)  // transient startup condition
              return path;

          Question last = path[path.length - 1];
          boolean needMore = !(last instanceof ErrorQuestion || last instanceof FinalQuestion);
          // quick check to see if we can simply use the path as is
          if ( (!markersEnabled || !markersFilterEnabled) && !needMore)
              return path;

          Vector v = new Vector();
          Question lastMarker = null;
          for (int i = 0; i < path.length; i++) {
            Question q = path[i];
            if (!markersEnabled || !markersFilterEnabled) {
                v.add(q);
            }
            else if (q.hasMarker(markerName)
                   || i == 0
                   || (i == path.length - 1 && q instanceof FinalQuestion)) {
                lastMarker = q;
                v.add(q);
            }
            else if (autoOpenSet.contains(lastMarker)) {
                v.add(q);
            }
            else {
                List l;
                Object o = v.lastElement();
                if (o == null || o instanceof Question) {
                  l = new Vector();
                  v.add(l);
                }
                else
                  l = (List) o;
                l.add(q);
            }
          }

          // auto-expand the final section if it doesn't end in FinalQuestion
          if (!(last instanceof FinalQuestion) && v.lastElement() instanceof List) {
            List l = (List) (v.lastElement());
            v.setSize(v.size() - 1);
            v.addAll(l);
          }

          if (needMore)
              v.add(moreText);
          
          Object[] a = new Object[v.size()];
          v.copyInto(a);
          return a;
      }


      
      // currPath and currQuestion give info as obtained from the interview
      private Question[] currPath = new Question[0];
      private Question currQuestion;

      // currEntries and currIndex give displayable entries
      // entries may be Question, List<Question>, or String
      private Object[] currEntries = new Object[0];
      private int currIndex;

      // currEnabled gives state of markersEnabled as used to construct list
      private boolean currEnabled;

      // currMarks gives which questions are currently showing a mark
      private boolean[] currMarks;

      // autoOpenSet gives which non-markered questions should be displayed
      private Set autoOpenSet = new HashSet();

      private Icon markerIcon = new MarkerIcon(true);
      private Icon noMarkerIcon = new MarkerIcon(false);
      private Icon ellipsisIcon = new EllipsisIcon();

      private JPopupMenu popupMenu;
      
    }

    //-----------------------------------------------------------------------

    private JMenu createMenu() {
      return (JMenu) (new Menu(Menu.JMENU).getComponent());
    }
    
    private JPopupMenu createPopupMenu() {
      return (JPopupMenu) (new Menu(Menu.JPOPUPMENU).getComponent());
    }

    private class Menu 
      implements ActionListener, ChangeListener, MenuListener, PopupMenuListener
    {
      
      static final int JMENU = 0, JPOPUPMENU = 1;

      Menu(int type) {
          this.type = type;

          // check box items (JMenu only)
          if (type == JMENU) {
            enableItem = createCheckBoxItem(ENABLE);
            filterItem = createCheckBoxItem(FILTER);
          }

          // question items (JMenu and JPopupMenu)
          markItem = createItem(MARK);
          unmarkItem = createItem(UNMARK);
          clearItem = createItem(CLEAR);

          // group items (JMenu and JPopupMenu)
          openGroupItem = createItem(OPEN_GROUP);
          closeGroupItem = createItem(CLOSE_GROUP);

          // interview items (JMenu only)
          if (type == JMENU) {
            clearMarkedItem = createItem(CLEAR_MARKED);
            removeAllItem = createItem(REMOVE_MARKERS);
          } 

          if (type == JMENU) {
            JMenu m = new JMenu(i18n.getString("path.mark.menu"));
            m.setName("path.mark.menu");
            m.getAccessibleContext().setAccessibleDescription(i18n.getString("path.mark.desc"));
            int mne = i18n.getString("path.mark.mne").charAt(0);
            m.setMnemonic(mne);
            m.add(enableItem);
            m.add(filterItem);
            m.addSeparator();
            m.add(markItem);
            m.add(unmarkItem);
            m.add(clearItem);
            m.addSeparator();
            m.add(openGroupItem);
            m.add(closeGroupItem);
            m.addSeparator();
            m.add(clearMarkedItem);
            m.add(removeAllItem);
            m.addMenuListener(this);
            comp = m;
          }
          else {
            JPopupMenu m = new JPopupMenu();
            m.add(markItem);
            m.add(unmarkItem);
            m.add(clearItem);
            // don't put a separator because often all the items above 
            // or all the items below will not be visible
            m.add(openGroupItem);
            m.add(closeGroupItem);
            m.addPopupMenuListener(this);
            comp = m;
          }
      }

      JComponent getComponent() {
          return comp;
      }

      private JMenuItem createItem(String name) {
          JMenuItem mi = new JMenuItem(i18n.getString("path.mark." + name + ".mit"));
          mi.setName(name);
          mi.setActionCommand(name);
          mi.addActionListener(this);
          setMnemonic(mi, name);
          return mi;
      }

      private JCheckBoxMenuItem createCheckBoxItem(String name) {
          JCheckBoxMenuItem mi = new JCheckBoxMenuItem(i18n.getString("path.mark." + name + ".ckb"));
          mi.setName(name);
          mi.addChangeListener(this);
          setMnemonic(mi, name);
          return mi;
      }

      private void updateItems() {

          Question q = interview.getCurrentQuestion();

          boolean marked = q.hasMarker(markerName);   
          boolean visible = pathList.isQuestionVisible(q);
          boolean autoOpened = pathList.isQuestionAutoOpened(q);

          if (type == JMENU) {
            // rules for a menu-bar menu:
            // keep things more visible, but disable as necessary

            enableItem.setSelected(markersEnabled);

            filterItem.setSelected(markersFilterEnabled);
            filterItem.setEnabled(markersEnabled);

            markItem.setVisible(!marked);
            markItem.setEnabled(markersEnabled);

            unmarkItem.setVisible(marked);
            unmarkItem.setEnabled(markersEnabled);

            clearItem.setVisible(true);
            clearItem.setEnabled(markersEnabled && !(q instanceof NullQuestion));

            openGroupItem.setVisible(!markersFilterEnabled || !visible);
            openGroupItem.setEnabled(markersEnabled && markersFilterEnabled);

            closeGroupItem.setVisible(markersFilterEnabled && visible);
            closeGroupItem.setEnabled(markersEnabled && markersFilterEnabled);

            clearMarkedItem.setVisible(true);
            clearMarkedItem.setEnabled(markersEnabled);

            removeAllItem.setVisible(true);
            removeAllItem.setEnabled(markersEnabled);
          }
          else {
            // rules for a popup menu:
            // hide inappropriate items, but always enabled
            // note popup menu only active if markersEnabled

            markItem.setVisible(visible && !marked);

            unmarkItem.setVisible(visible && marked);

            clearItem.setVisible(visible && !(q instanceof NullQuestion));

            openGroupItem.setVisible(markersFilterEnabled && !visible);
            closeGroupItem.setVisible(markersFilterEnabled && autoOpened);
          }
      }

      private void setMnemonic(JMenuItem mi, String name) {
          int mne = i18n.getString("path.mark." + name + ".mne").charAt(0);
          mi.setMnemonic(mne);
      }

      // ---------- from ActionListener -----------

      public void actionPerformed(ActionEvent e) {
          String cmd = e.getActionCommand();
          if (cmd.equals(MARK)) {
            pathList.markCurrentQuestion();
          }
          else if (cmd.equals(UNMARK)) {
            pathList.unmarkCurrentQuestion();
          }
          else if (cmd.equals(CLEAR)) {
            Question q = interview.getCurrentQuestion();
            q.clear();
            // have to redisplay question explicitly,
            // because there is no notification that the 
            // value of the current question has been changed.
            questionPanel.showQuestion(q); 
          }
          else if (cmd.equals(OPEN_GROUP)) {
            pathList.openCurrentEntry();
          }
          else if (cmd.equals(CLOSE_GROUP)) {
            pathList.closeCurrentEntry();
          }
          else if (cmd.equals(CLEAR_MARKED)) {
            Question q = interview.getCurrentQuestion();
            interview.clearMarkedResponses(markerName);
            // If the previously current question is still current,
            // we need to redisplay it to make sure that it shows
            // the updated value
            if (q == interview.getCurrentQuestion())
                questionPanel.showQuestion(q); 
          }
          else if (cmd.equals(REMOVE_MARKERS)) {
            // show a confirm dialog?
            interview.removeMarkers(markerName);
            if (getMarkersFilterEnabled() == true)
                setMarkersFilterEnabled(false); // will cause update
            else
                pathList.update();
          }
      }

      // ---------- from ChangeListener -----------

      public void stateChanged(ChangeEvent e) {
          Object src = e.getSource();
          if (src == enableItem) {
            questionPanel.saveCurrentResponse();
            boolean on = enableItem.isSelected();
            setMarkersEnabled(on);
          }
          else if (src == filterItem) {
            questionPanel.saveCurrentResponse();
            boolean on = filterItem.isSelected();
            setMarkersFilterEnabled(on);
          }
      }

      // ---------- from MenuListener -----------

      public void menuSelected(MenuEvent e) {
          updateItems();
      }

      public void menuDeselected(MenuEvent e) {
      }

      public void menuCanceled(MenuEvent e) {
      }

      // ---------- from PopupMenuListener -----------

      public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
          updateItems();
      }

      public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
      }

      public void popupMenuCanceled(PopupMenuEvent e) {
      }

      private int type;
      private JComponent comp;
      private JCheckBoxMenuItem enableItem;
      private JCheckBoxMenuItem filterItem;
      private JMenuItem markItem;
      private JMenuItem unmarkItem;
      private JMenuItem clearItem;
      private JMenuItem openGroupItem;
      private JMenuItem closeGroupItem;
      private JMenuItem clearMarkedItem;
      private JMenuItem removeAllItem;

      private static final String ENABLE = "enable";
      private static final String FILTER = "filter";
      private static final String MARK = "mark";
      private static final String UNMARK = "unmark";
      private static final String CLEAR = "clear";
      private static final String OPEN_GROUP = "open";
      private static final String CLOSE_GROUP = "close";
      
      private static final String CLEAR_MARKED = "clearMarked";
      private static final String REMOVE_MARKERS = "remove";
    }

    //-----------------------------------------------------------------------

    private static class MarkerIcon implements Icon
    {
      MarkerIcon(boolean on) {
          this.on = on;
      }

      public int getIconWidth() { 
          return iconWidth; 
      }

      public int getIconHeight() { 
          return iconHeight; 
      }

      public void paintIcon(Component c, Graphics g, int x, int y) {
          if (on) {
            if (image == null) {
                image = new BufferedImage(getIconWidth(), getIconHeight(),
                                    BufferedImage.TYPE_INT_ARGB);
                paintMe(image);
            }
            g.drawImage(image, x, y, null);
          }
      }

      private void paintMe(BufferedImage image) {
          Graphics g = image.getGraphics();     
          
          int x0 = 0;
          int y0 = 0;

          int x1 = Math.min(iconWidth, iconHeight);
          int y1 = x1;

          int[] xx = {
            x0 + iconIndent,
            x1,
            x1,
            x1 - iconIndent,
            x0,
            x0 + iconIndent,
          };

          int[] yy = {
            y0,
            y1 - iconIndent,
            y1,
            y1,
            y0 + iconIndent,
            y0 + iconIndent
          };
          
          g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
          g.fillPolygon(xx, yy, xx.length);
      }

      private boolean on;
      private BufferedImage image;
      private static final int iconWidth = 8;
      private static final int iconHeight = 16;
      private static final int iconIndent = 3;
    }

    //-----------------------------------------------------------------------

    private static class EllipsisIcon implements Icon
    {
      public int getIconWidth() { 
          return iconWidth; 
      }

      public int getIconHeight() { 
          return iconHeight; 
      }

      public void paintIcon(Component c, Graphics g, int x, int y) {
          if (image == null) {
            image = new BufferedImage(getIconWidth(), getIconHeight(),
                                BufferedImage.TYPE_INT_ARGB);
            paintMe(image);
          }
          g.drawImage(image, x, y, null);
      }

      private void paintMe(BufferedImage image) {
          Graphics g = image.getGraphics();     
          g.setColor(Color.black);

          for (int iy = 0; iy < dotHeight; iy++) {
            int y = (iconHeight - dotHeight) / 2 + iy;
            for (int ix = 0; ix < dots; ix++) {
                int x = dotIndent + ix * (dotWidth + dotSep);
                g.drawLine(x, y, x + dotWidth - 1, y);
            }
          }
      }

      private BufferedImage image;
      private static final int iconWidth = 48;
      private static final int iconHeight = 6;
      private static final int dots = 3;
      private static final int dotWidth = 2;
      private static final int dotHeight = 1;
      private static final int dotSep = 4;
      private static final int dotIndent = 20;
    }

}

Generated by  Doxygen 1.6.0   Back to index