Package DisplayProject.controls

Source Code of DisplayProject.controls.ListView

/*
Copyright (c) 2003-2009 ITerative Consulting Pty Ltd. All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:

o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
 
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
   
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder

 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


*/
package DisplayProject.controls;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.dnd.Autoscroll;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.tree.TreeNode;

import org.apache.log4j.Logger;

import DisplayProject.ArrayColumn;
import DisplayProject.ArrayColumnModel;
import DisplayProject.ArrayFieldModel;
import DisplayProject.Array_Of_DisplayNode;
import DisplayProject.Array_Of_OutlineColumnDesc;
import DisplayProject.Constants;
import DisplayProject.DisplayNode;
import DisplayProject.DisplayNodeDragListener;
import DisplayProject.OutlineColumnDesc;
import DisplayProject.PopupListener;
import DisplayProject.TableSorter;
import DisplayProject.TreeField;
import DisplayProject.UIutils;
import DisplayProject.WindowSystem;
import DisplayProject.actions.ActionMgr;
import DisplayProject.actions.HeightInPixels;
import DisplayProject.actions.ListStyle;
import DisplayProject.actions.PendingAction;
import DisplayProject.actions.WidthInPixels;
import DisplayProject.binding.ListViewTableModel;
import DisplayProject.events.ClickListener;
import DisplayProject.events.ClientEventManager;
import DisplayProject.events.DoubleClickListener;
import DisplayProject.factory.CompoundFieldFactory;
import DisplayProject.factory.TableFactory;
import DisplayProject.table.AlignedCellRenderer;
import Framework.CloneHelper;
import Framework.DynamicArray;
import Framework.EventManager;
import Framework.ForteKeyboardFocusManager;
import Framework.FrameworkUtils;
import Framework.ImageData;
import Framework.MsgCatalog;
import Framework.ParameterHolder;
import Framework.TextData;

/**
* A ListView is a view that mimics the windows file explorer view.
* Able to show large icons.
* Has windows like selection.
* Has drag and drop ability.
* Has double click and keyboard enter, space, and esc listeners.

* @author Craig Mitchell 22/06/2005.
* @version 1.0
*
* @author Peter
* @version 1.1
* added behaviour to mimic listview in Image Mode
* added forte event AfterCurrentNodeChange
* addeed forte mouse events
*
*
* @author Peter
* @version 1.2
* Added support for the four modes of a list view
*/
@SuppressWarnings("serial")
public class ListView extends JPanel implements KeyListener, TreeField, Autoscroll {
    private DisplayNode oldNode = null;
    private DisplayNode currentNode = null;
    private int oldRow = -1;
    private int currentRow = -1;
    private int listStyle = Constants.LT_IMAGE;
    protected ArrayColumnModel columnModel;
    protected ListViewTableModel tableModel;
    protected TableSorter sorter;
    protected ListViewTable table;
    protected JList list;
    protected JScrollPane scrollPane = null;
    private Color selectionForeground;
    private Color selectionBackground;
    private NodeChangeListener ncl;
    private int oldListStyle = 0;
    protected boolean sortToggle = true;
    protected int sortType = Constants.LT_DESCEND;
    protected volatile DisplayNode rootNode;
    protected Class<?> mappedType;
    protected int visibleLines = -1;
    protected int visibleColumns = -1;
    // TF:7/11/07:Added the row highlights
    private boolean hasRowHighlights = true;
    private boolean isDropHighlightEnabled = true; // CraigM: 04/04/2008
    private boolean isAutoScrollEnabled = true; // CraigM: 10/04/2008
    private int scrollPolicy; // Constants.NP_... CraigM 01/01/2008
    private static final Logger log = Logger.getLogger(ListView.class);
   
    @SuppressWarnings("unused")
  private class cellRenderer extends JLabel implements TableCellRenderer {
        /* (non-Javadoc)
         * @see javax.swing.table.TableCellRenderer#getTableCellRendererComponent(javax.swing.JTable, java.lang.Object, boolean, boolean, int, int)
         */
        public Component getTableCellRendererComponent(JTable pTable, Object pValue, boolean pIsSelected, boolean pHasFocus, int pRow, int pColumn) {
            if (pValue instanceof ImageData) {
                this.setIcon(((ImageData)pValue).asIconImage());
            }
            else if (pValue instanceof Icon) {
                this.setIcon((Icon)pValue);
            }
            else {
                this.setText(pValue.toString());
                this.setBorder(new EmptyBorder(0, 4, 0, 10));
            }
            return this;
        }
    }
    /**
     * Constructor
     */
    public ListView() {
        this("", Constants.LT_SMALLICON, null, Font.PLAIN);
    }

    public ListView(String name) {
        this(name, Constants.LT_SMALLICON, null, Font.PLAIN);
    }

    public ListView(String name, int style, Font font, int headerFontStyle) {
        super();
        this.setLayout(new BorderLayout());
        if (font != null) {
            this.setFont(font);
        }
        else {
          // TF:15/07/2008:Force the explicit setup of the default font
          this.setFont(WindowSystem.getPortableFont(80, Constants.TF_SYSTEM_DEFAULT, false, false));
        }
        this.scrollPane = CompoundFieldFactory.newScrollPane();
        // TF:28/1/08:Changed the scrolling layout to match what Forte had, ie remove the ugly
        // grey square at the top left corner and have the end scrollbar go all the way up.
        this.scrollPane.setLayout(new NonCorneredScrollLayout());
        this.scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        this.scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        // TF:28/1/08:Remove the scrollPane background color. This should only affect the corners
        // of the scroll pane, as the viewport background colour affects the rest of it.
        // this.scrollPane.setOpaque(true);
        // this.scrollPane.setBackground(java.awt.Color.WHITE); // Set the rest of the JTable to have a white background to mirror the Forte one
        this.scrollPane.getViewport().setBackground(java.awt.Color.WHITE); // Set the rest of the JTable to have a white background to mirror the Forte one
        this.scrollPane.setInheritsPopupMenu(true);
        this.add(this.scrollPane);
        this.list = new JList(new DefaultListModel());
        if (font != null)
            this.list.setFont(font);
        this.list.setName("InnerList");
        this.list.setAutoscrolls(true);
//    this.list.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        new DisplayNodeDragListener(this.list);
        this.list.addKeyListener(this);
        this.ncl = new NodeChangeListener(this);
        this.list.addListSelectionListener(this.ncl);
        this.list.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), PsuedoDoubleClickAction.PSUED0_DOUBLE_CLICK);
        this.list.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_MASK),PsuedoDoubleClickAction.PSUED0_DOUBLE_CLICK);
        this.list.getActionMap().put(PsuedoDoubleClickAction.PSUED0_DOUBLE_CLICK, new PsuedoDoubleClickAction());
        this.list.setInheritsPopupMenu(true); //PM:9/10/07 ensure the popup is available in list mode
        this.table = new ListViewTable(this, font);
        // COLET 13/01/2009 : Pseudo Double Click using Enter Key will not work if the table component has focus. Lets register the Action on the Table too.
        this.table.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), PsuedoDoubleClickAction.PSUED0_DOUBLE_CLICK);
        this.table.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_MASK),PsuedoDoubleClickAction.PSUED0_DOUBLE_CLICK);
        this.table.getActionMap().put(PsuedoDoubleClickAction.PSUED0_DOUBLE_CLICK, new PsuedoDoubleClickAction());
        this.list.setSelectionModel(this.table.getSelectionModel());
        this.tableModel = new ListViewTableModel(this);
        this.setTableModel(this.tableModel);
        this.setName(name);


        if (headerFontStyle != Font.PLAIN) {
            JTableHeader header = this.table.getTableHeader();
            final Font fontStyle = header.getFont().deriveFont(headerFontStyle);
            final TableCellRenderer headerRenderer = header.getDefaultRenderer();
            header.setDefaultRenderer( new TableCellRenderer() {
                public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) {
                    Component comp = headerRenderer.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column );
                    comp.setFont( fontStyle );
                    ((JLabel)comp).setHorizontalAlignment(JLabel.LEFT);
                    return comp;
                }
            });

        }

        // Detect the right click when JTable emppty
        this.scrollPane.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)){
                    MouseEvent me = new MouseEvent(ListView.this.table, MouseEvent.MOUSE_CLICKED, e.getWhen(),e.getModifiers(),e.getX(),e.getY(),1,true,1);
                    MouseListener[] ms = ListView.this.table.getMouseListeners();
                    for (int i = 0; i < ms.length; i++){
                        if (ms[i] instanceof PopupListener)
                            ((PopupListener)ms[i]).mousePressed(me);
                    }
                }else{
                    // post "Click" instead of "ChildClick"
                  // TF:19/3/08:Changed to add in the parameters
                    ClientEventManager.postEvent(ListView.this, "Click", ClickListener.makeParamList(e));
                }
            }
        });
       
        // Change the selected row on right click
        this.table.addMouseListener( new MouseAdapter() {
          // CraigM:18/08/2008 - We don't get the mouse released event when there is a popup menu, only the mouse pressed
          @Override
          public void mousePressed(MouseEvent e) {
                if ( SwingUtilities.isRightMouseButton(e)) {
                    int row = table.rowAtPoint( e.getPoint() );
                    int column = table.columnAtPoint( e.getPoint() );
                    //  Only change selection to row, column if the cell isn't selected
                    if (!table.isCellSelected(row, column)) {
                        table.changeSelection(row, column, false, false);          
                    }
                }
            }
        });

        // TF:05/05/2009:Refactored this and made it apply to the list as well as the table
        FocusAdapter fa = new FocusAdapter(){
            public void focusGained(FocusEvent e) {
                setSelectionBackground(TableFactory.HIGHLIGHT_COLOUR_FOCUS);
                setSelectionForeground(Color.WHITE);
               
                // CraigM:15/01/2009 - Forte would not select the first row
                // Set focus to first row
//                if (table.getSelectedRow() == -1 && table.getRowCount() > 0 ) {
//                    UIutils.setCurrentRow(table, 1);
//                }

                // Trigger the AfterFocusGain event.  CraigM 12/07/2007.
                EventManager.startEventChain();
                int reason = ForteKeyboardFocusManager.getTraversalReason();
                if (reason != Constants.FC_SUPRESS) {
                    Hashtable<String, Object> params = new Hashtable<String, Object>();
                    params.put("reason", new ParameterHolder(reason));
                    ClientEventManager.postEvent( ListView.this, "AfterFocusGain", params );
                }
                EventManager.endEventChain();

            }
            public void focusLost(FocusEvent e) {
//        If JTable viewonly or disabled the background colour is set to light grey
//        which == HIGHLIGHT_COLOUR_NO_FOCUS - so if this is the case set the background colour to white
                setSelectionForeground(Color.BLACK);               
                Integer state = (Integer)ListView.this.getClientProperty("qq_state");
                if (!(state == null) &&
                        (state.intValue() == Constants.FS_VIEWONLY || state.intValue() == Constants.FS_DISABLED)) {
                    setSelectionBackground(Color.WHITE);
                } else {
                    setSelectionBackground(TableFactory.HIGHLIGHT_COLOUR_NO_FOCUS);
                }
                table.setBackground(Color.WHITE);

            }
        };
       
        this.table.addFocusListener(fa);
        this.list.addFocusListener(fa);

        this.addFocusListener(new FocusAdapter() {
            /* (non-Javadoc)
             * @see java.awt.event.FocusAdapter#focusGained(java.awt.event.FocusEvent)
             */
            public void focusGained(FocusEvent e) {
                switch (ListView.this.listStyle) {
                case Constants.LT_SMALLICON:
                case Constants.LT_IMAGE:
                case Constants.LT_LIST:
                    ListView.this.list.requestFocus();
                default:
                    ListView.this.table.requestFocus();
                }
            }
        });
//    this.setSelectionBackground(Color.BLUE);
//    this.setSelectionForeground(Color.WHITE);
        this.setListStyle(Constants.LT_SMALLICON);
        // TF:18/09/2009:Force the GUI actions to be processed here, otherwise we might not pick up the proper definitions
        UIutils.processGUIActions();
        this.setListStyle(style);
        /*
         * PM:10/9/07
         * Added for DnD support
         */
        this.table.putClientProperty("qq_ListView", this);
        this.list.putClientProperty("qq_ListView", this);
       
      // TF:15/12/2009:DET-141:Set this up to allow inheriting of popup menu
      this.setInheritsPopupMenu(true);
    }
   
    private StringBuilder keyScrollerKeysPressed = new StringBuilder(5); // Remember the keys pressed
    private long keyScrollerLastKeyPressTime = 0; // Remember when the last key was pressed

    /**
     * When the user presses a key, if we are in details mode, then we should jump to the appropriate row.
     * Written by CraigM.
     *
     * @param ke
     * @since 02/04/2008
     */
    public void doKeyScroller(KeyEvent ke) {
      // Only applicable for details view
      if (this.isViewTable(this.getListStyle()) == false) {
        return;
      }

      if (ke.getID() == KeyEvent.KEY_PRESSED) {

        // Jump to the top on HOME key
        if (ke.getKeyCode() == KeyEvent.VK_HOME) {
            Array_Of_DisplayNode<DisplayNode> nodes = this.getViewNodes();
            if (nodes.size() > 0) {
              this.selectNode(nodes.get(0));
              this.requestScroll(0, Constants.AF_AUTOMATIC);
            }
        }
       
        // Jump to the bottom on END key
        else if (ke.getKeyCode() == KeyEvent.VK_END) {
            Array_Of_DisplayNode<DisplayNode> nodes = this.getViewNodes();
            if (nodes.size() > 0) {
              this.selectNode(nodes.get(nodes.size()-1));
              this.requestScroll(nodes.size(), Constants.AF_AUTOMATIC);
            }
        }
      }

      // If they type a key we want to match based on the nodes toString
      else if (ke.getID() == KeyEvent.KEY_TYPED) {
        Array_Of_DisplayNode<DisplayNode> nodes = this.getViewNodes();
        long currentTime = System.currentTimeMillis();
        int currentIndex = Math.max(this.getSelectedRow()-1, 0);
        DisplayNode nodeToSelect = null;
        int indexToSelect = 0;
        String strToMatch;

        // If more then 0.7 seconds has passed since the last key press.  Clear out the keys pressed.
        if (currentTime - keyScrollerLastKeyPressTime > 700) {
          keyScrollerKeysPressed = new StringBuilder(5);
        }
       
        // Record the current time and key press
        keyScrollerLastKeyPressTime = currentTime;
        keyScrollerKeysPressed.append(ke.getKeyChar());

        // Check if they keep pressing the same key
        boolean isSameKeyPress = true;
        for (int i=0; i<keyScrollerKeysPressed.length(); i++) {
          if (keyScrollerKeysPressed.charAt(i) != ke.getKeyChar()) {
            isSameKeyPress = false;
            break;
          }
        }

        // If they keep pressing the same key, then only match on the first character
        if (isSameKeyPress) {
          strToMatch = String.valueOf(ke.getKeyChar());
        }

        // Otherwise match on everything they have typed
        else {
          strToMatch = keyScrollerKeysPressed.toString();
        }

        // Start searching from our current location
        for (int i=currentIndex+1; i<nodes.size(); i++) {
          DisplayNode aNode = nodes.get(i);
          int length = strToMatch.length();
          String nodeStr = aNode.toString();

          if nodeStr != null &&
              nodeStr.length() >= length &&
              nodeStr.substring(0, length).equalsIgnoreCase(strToMatch.toString())) {
            nodeToSelect = aNode;
            indexToSelect = i+1;
            break;
          }
        }
       
        // If no match, continue searching from the start
        if (nodeToSelect == null) {
          for (int i=0; i<currentIndex; i++) {
            DisplayNode aNode = nodes.get(i);
            int length = strToMatch.length();
            String nodeStr = (aNode == null) ? null : aNode.toString();

            if nodeStr != null &&
                nodeStr.length() >= length &&
                nodeStr.substring(0, length).equalsIgnoreCase(strToMatch.toString())) {
              nodeToSelect = aNode;
              indexToSelect = i+1;
              break;
            }
          }
        }

        // If we found a match, select it and scroll to it
        if (nodeToSelect != null) {
          this.selectNode(nodeToSelect);
          this.requestScroll(indexToSelect, Constants.AF_AUTOMATIC);
        }
      }
    }
   
    public void addMouseListener(MouseListener ml){
        this.list.addMouseListener(ml);
        this.table.addMouseListener(ml);
    }

    /**
     * When a user double clicks or presses enter, this method is called.
     * @param pItem = The item that was selected.
     */
    public void itemSelected(DisplayNode pItem){
        //setCurrentNode(pItem);
    }

    /**
     * This method is called when a key is pressed that is not an arrow key,
     * enter, space, or the escape key.
     * @param pKey = The key event that was received.
     */
    public void keyHandler(KeyEvent pKey) {
    }

    /**
     * Force a refresh of the table
     */
    public final void updateFieldFromData() {
        this.tableModel.fireTableDataChanged();
    }

    /**
     * Set the data for the list.
     * @param pData = The data which must be of type DisplayNode[]
     */
    public void setListData(Object[] pData) {
        list.getSelectionModel().clearSelection();
        DefaultListModel dlm = (DefaultListModel)list.getModel();
        dlm.clear();
        if (pData != null) {
            for (int i = 0; i < pData.length; i++) {
                dlm.addElement(pData[i]);
            }
        }
    }
    /**
     * Set the View Nodes for the list.
     * @param pData = The data which must be of type DynamicArray
     */
    public void setViewNodes(Array_Of_DisplayNode<? extends DisplayNode> pData) {
        // TF:8/10/07:Cloned the array nodes to make sure they're not cleared
        // TF:3/2/08:Changed the clone to be a shallow clone after observing forte behaviour
        final Array_Of_DisplayNode<? extends DisplayNode> nodes = (Array_Of_DisplayNode<? extends DisplayNode>)CloneHelper.clone(pData, false);
        ActionMgr.addAction(new PendingAction(null) {
            @Override
            public String toString() {
                return "ListView.setViewNodes(" + nodes + ")";
            }
            @Override
            public void performAction() {
                synchronized(ListView.this) {
                    // Disassociate any listeners in the existing view nodes with this list view
                    if (rootNode != null) {
                        rootNode.removePropertyChangeListener(nodeRefreshListener);
                        int maxIndex = rootNode.getChildCount();
                        for (int i = 0; i < maxIndex; i++) {
                            TreeNode node = rootNode.getChildAt(i);
                            if (node instanceof DisplayNode) {
                                ((DisplayNode)node).removePropertyChangeListener(nodeRefreshListener);
                            }
                        }
                    }
                    if (nodes == null) {
                        setRootNode(null);
                    }
                    else {
                        try {
                            rootNode = (DisplayNode)FrameworkUtils.getArrayType(mappedType).newInstance();
                        }
                        catch (InstantiationException e) {
                            rootNode = new DisplayNode();
                        }
                        catch (IllegalAccessException e) {
                            rootNode = new DisplayNode();
                        }
                        rootNode.setIsFolder(true);
                        rootNode.setIsFilled(true);
                        rootNode.setUserObject(ListView.this);
                        tableModel.setViewNodes(nodes);
                        setListData(nodes.toArray());

                        // We also have to reparent all the nodes in the array to this new node. It won't
                        // actually matter for display purposes, but maintains it in case they investigate
                        // the list via the node hierarchy.
                        for (DisplayNode node : nodes) {
                            rootNode.add(node);
                            node.addPropertyChangeListener(nodeRefreshListener);
                        }
                    }
                    invalidate();
                    scrollPane.invalidate();
                    /*
                     * this horrible code is to cause the correct
                     * repaint of the table header. This is a long
                     * standing bug open with Sun.
                     *
                     * Bug ID: 4473075
                     * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4473075
                     *
                     * It fixes most of it but there are still some
                     * cases where the table header is not repainted 
                     *
                     * TF:21/11/07:Removed this code because it doesn't always solve
                     * the table header problem, but ensuring the preferred size is
                     * never set seems to solve this properly, even at the expense
                     * of having a header that is slightly too large.
                     */
//                    Dimension d = table.getPreferredSize();
//                    JTableHeader header = table.getTableHeader();
//                    if (header != null){
//                        d.width += 1;
//                        d.height = header.getPreferredSize().height;
//                        header.setPreferredSize(d);
//                    }
                }
            }
        });
    }

    @SuppressWarnings("unchecked")
  public Array_Of_DisplayNode<DisplayNode> getViewNodes() {
        //PM:15/11/07 change processGUIActions to be specific to this object
        ActionMgr.processGUIActions();
        // MI2007: Made sure this instantiated the correct class type
        Array_Of_DisplayNode<DisplayNode> nodes = null;
        DynamicArray da = null;
        synchronized(this) {
            if (this.tableModel != null) {
                da = this.tableModel.getData();

                Class<?> clazz;
                if (da != null) {
                    clazz = da.getClass();
                }
                else {
                    clazz = this.getMappedType();
                }
                if (clazz != null) {
                    if (Array_Of_DisplayNode.class.isAssignableFrom(clazz)) {
                        try {
                            nodes = (Array_Of_DisplayNode<DisplayNode>)clazz.newInstance();
                        }
                        catch (Exception e) {
                            // Something went wrong, just use the default
                        }
                    }
                }
            }
        }
        if (nodes == null) {
            nodes = new Array_Of_DisplayNode<DisplayNode>();
        }
        if (da != null) {
            nodes.addAll(da);
        }
        return nodes;
    }

    /**
     * Select a given node in the ListView, if it exists. If this is a single selection list, the selection
     * will replace any existing selection, or clear the selection if the node is not in the list. If the
     * list is in multiple selection mode, the node is selected if it is present
     * @param pNode
     */
    public void selectNode(DisplayNode pNode) {
        int index = this.tableModel.getData().indexOf(pNode);
        if (index >= 0) {
            if (this.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION) {
                this.getSelectionModel().setSelectionInterval(index, index);
            }
            else {
                this.getSelectionModel().addSelectionInterval(index, index);
            }
        }
        else {
            if (this.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)
                this.getSelectionModel().clearSelection();
        }
    }
    /**
     * Returns a boolean indicating if the node is selected
     * @param node
     * @return
     */
    @SuppressWarnings("unchecked")
  public boolean isSelected(DisplayNode node){
        int index = this.tableModel.getData().indexOf(node);
        if (index < 0) return  false;

        /*
         *  06/03/07
         * If sort order has been changed, we must adjust the array according
         * to the sorting column.
         */
        //  Some test code to see if code below could be simplified
        if (this.getSorter().isSorting())  {
            int column = this.getSorter().getSortColumn();
            ArrayColumnModel columnModel = this.getColumnModel();
            ArrayColumn arrayColumn = columnModel.getRealColumn(column);
            TextData name = new TextData();
            name.setValue(arrayColumn.getName());
            if (name.moveToString("Item")) {
                name = name.copyRange(name.getOffset());
            }
            boolean ascending = (this.getSorter().getSortDirection() == TableSorter.ASCENDING);

            DynamicArray<Object> theArray = (DynamicArray<Object>)(this.tableModel.getData().clone());
            theArray.sort(name.asString(),false,ascending);
            index = theArray.indexOf(node);
        }
        return this.getSelectionModel().isSelectedIndex(index);
    }

    /**
     * Deselectes a given node
     * @param pNode
     */
    public void deselectNode(DisplayNode pNode) {
        int index = this.tableModel.getData().indexOf(pNode);
        if (index >= 0) {
            this.getSelectionModel().removeSelectionInterval(index, index);
        }
    }

    public DisplayNode findRelated(Object pRelated) {
        DynamicArray<Object> data = this.tableModel.getData();
        for (Object o : data) {
            if ((o instanceof DisplayNode) && ((DisplayNode)o).getRelated() == pRelated) {
                return (DisplayNode)o;
            }
        }
        return null;
    }

    public TextData getValueAt(int pLine, int pColumn) {
        Object o = tableModel.getValueAt(pLine, pColumn);
        if (o instanceof String)
            return new TextData((String)o);
        else if (o instanceof TextData)
            return (TextData)o;
        else
            return null;
    }

    public final void keyPressed(KeyEvent e) {
        int aKey = e.getKeyCode();

        if (aKey == KeyEvent.VK_ENTER || aKey == KeyEvent.VK_SPACE) {
            Object aSelected = this.list.getSelectedValue();

            if (aSelected != null && aSelected instanceof DisplayNode) {
                this.itemSelected((DisplayNode)aSelected);
                /*
                 * fire the doubleClick mouse event
                 */

            }
        }
        else if (aKey == KeyEvent.VK_ESCAPE) {
            this.list.clearSelection();
        }
        this.keyHandler(e);
    }

    public final void keyReleased(KeyEvent e) {
    }

    public final void keyTyped(KeyEvent e) {
    }


    public DisplayNode getCurrentNode() {
        switch (this.listStyle) {
        case Constants.LT_SMALLICON:
        case Constants.LT_IMAGE:
        case Constants.LT_LIST:
            return (DisplayNode)this.list.getSelectedValue();
        default:
            return this.tableModel.getCurrentRow();
        }
    }


    public int getListStyle() {
        return this.listStyle;
    }

    /**
     * @return How many lines there are in the list view.
     * NOTE:  Forte seems to have a bug where it always returns 1 more then there actually are, so this is
     * done here too.
     * Written by Craig Mitchell
     * @since 23/10/2007
     */
    public int getTotalLines() {
        return this.list.getModel().getSize() + 1// +1 to reproduce Fortes bug.
    }

    public void setListStyle(int listStyle) {
      // TF:05/05/2009:Added conditional test
      if (this.listStyle != listStyle) {
          if (SwingUtilities.isEventDispatchThread()) {
            // TF:05/05/2009:If we were in the detail style and we're changing to one of the other styles,
            // the current selection will be in terms of the view, not the model, so we must change the
            // selection to be in model terms. Similarly, if we're changing back to a detail, we must change
            // from the model selection to the view selections.
            if (listStyle == Constants.LT_DETAIL) {
              // Must map the selection, we know we're not currently in a detail style.
              int selectedIndex = this.list.getSelectedIndex();
              if (selectedIndex >= 0) {
                int newSelection = this.sorter.viewIndex(selectedIndex);
                this.getSelectionModel().setSelectionInterval(newSelection, newSelection);
              }
            }
            else if (this.listStyle == Constants.LT_DETAIL) {
              // Going from a DETAIL to a LIST style, change from the view to the model selection.
              int selectedIndex = this.table.getSelectedRow();
              if (selectedIndex >= 0) {
                int newSelection = this.sorter.modelIndex(selectedIndex);
                this.getSelectionModel().setSelectionInterval(newSelection, newSelection);
              }
            }
            this.listStyle = listStyle;
             
              switch (this.listStyle) {
              case Constants.LT_SMALLICON:
                  this.list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
                  this.list.setVisibleRowCount(0);
                  // TF:17/8/08:Adjusted these numbers so they match the forte one very closely
                  this.list.setFixedCellWidth(115);
                  this.list.setFixedCellHeight(18);
                  this.list.setCellRenderer(new SmallIconDisplayNodeRenderer(this.tableModel));
                  if (isViewTable(oldListStyle) || (oldListStyle == 0)) {
                      this.scrollPane.setViewportView(list);
                      this.list.requestFocus();
                  }
                  break;
              case Constants.LT_IMAGE:
                  this.list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
                  this.list.setVisibleRowCount(0);
                  this.list.setFixedCellWidth(76);
                  this.list.setFixedCellHeight(58);
                  this.list.setCellRenderer(new ImageDisplayNodeRenderer(this.tableModel));
                  if (isViewTable(oldListStyle) || (oldListStyle == 0)) {
                      this.scrollPane.setViewportView(list);
                      this.list.requestFocus();
                  }
                  break;
              case Constants.LT_LIST:
                  this.list.setLayoutOrientation(JList.VERTICAL_WRAP);
                  this.list.setVisibleRowCount(0);
                  this.list.setFixedCellWidth(115);
                  this.list.setFixedCellHeight(18);
                  this.list.setCellRenderer(new ListDisplayNodeRenderer(this.tableModel));
                  if (isViewTable(oldListStyle) || (oldListStyle == 0)) {
                      this.scrollPane.setViewportView(list);
                      this.list.requestFocus();
                  }
                  break;
 
              default:
                  if (!(isViewTable(oldListStyle) || (oldListStyle == 0))){
                      this.scrollPane.setViewportView(table);
                      this.table.requestFocus();
                  }
              }
              this.oldListStyle = this.listStyle;
              invalidate();
              this.scrollPane.invalidate();
          }
          else {
              ListStyle.set(this, listStyle);
          }
      }
    }

    public boolean isViewTable() {
      return this.isViewTable(this.getListStyle());
    }
   
    private boolean isViewTable(int listStyle){
        return ((listStyle == Constants.LT_DETAIL) ||
                (listStyle == Constants.LT_DEFAULT));

    }
    public ListSelectionModel getSelectionModel(){
        return this.list.getSelectionModel();
    }
    public ListViewTableModel getListViewTableModel() {
      return this.tableModel;
    }
    public void setSelectionModel(ListSelectionModel selectionModel){
        this.list.setSelectionModel(selectionModel);
        this.table.setSelectionModel(selectionModel);
    }
    public ArrayColumnModel getColumnModel() {
        return columnModel;
    }

    public void setColumnModel(ArrayColumnModel columnModel) {
        this.columnModel = columnModel;
        this.table.setColumnModel(this.columnModel);

//    this.columnModel.getSelectionModel().addListSelectionListener(this.ncl);

    }

    public OutlineColumnDesc getDescByName(String pName) {
        if (columnModel != null) {
          // TF:19/06/2008:Changed this to use the internal method as it's more accurate
            return getOutlineColumn(columnModel.findColumn(pName), true);
        }
        else {
            return null;
        }
    }

    public OutlineColumnDesc getDescByName(TextData pName) {
        return this.getDescByName(pName.toString());
    }

    public OutlineColumnDesc getDescByHeader(String pHeader) {
        if (columnModel != null) {
          // TF:19/06/2008:Changed this to use the internal method as it's more accurate
            return getOutlineColumn(columnModel.findColumnByHeader(pHeader), true);
        }
        else {
            return null;
        }
    }

    public OutlineColumnDesc getDescByHeader(TextData pHeader) {
        return this.getDescByHeader(pHeader.toString());
    }

    public int getCurrentRow() {
        return currentRow;
    }

    public void setCurrentRow(int currentRow) {
        this.currentRow = currentRow;
    }

    public TableSorter getSorter() {
        return sorter;
    }

    public void setSorter(TableSorter sorter) {
        this.sorter = sorter;
    }
   
    /**
     * Enables/Disables Autoscroll when doing drag and drop.
     * Default is enabled.
     *
     * CraigM: 10/04/2008
     *
     * @see Autoscroll
     *
     * @param pIsAutoScrollEnabled
     */
    public void setIsAutoScrollEnabled(boolean pIsAutoScrollEnabled) {
      this.isAutoScrollEnabled = pIsAutoScrollEnabled;
    }

    public boolean getIsAutoScrollEnabled() {
      return this.isAutoScrollEnabled;
    }

    public ArrayFieldModel getTableModel() {
        return tableModel;
    }

    public void setTableModel(ListViewTableModel tableModel) {
        this.tableModel = tableModel;
        this.sorter = new TableSorter(this.tableModel);
        this.sorter.setTableHeader(this.table.getTableHeader());
        this.sorter.setParent(this);
        this.table.setModel(this.sorter);
    }

    public void setCurrentNode(final DisplayNode pCurrentNode) {
      // TF:26/07/2009:forced this onto the GUI thread
      ActionMgr.addAction(new PendingAction(null) {
        @Override
        public void performAction() {
              try {
                DisplayNode nodeToSet = pCurrentNode;
                // TF:29/07/2010:Fixed an issue from DaveC if the passed node is not found in the listview then the
                // first row in the table should be selected
                if (nodeToSet != null && tableModel.getRowIndex(nodeToSet) < 0 && tableModel.getSize() > 0) {
                  nodeToSet = tableModel.getRow(0);
                }
                  tableModel.setCurrentRow(nodeToSet);
                  if (list.getModel().getSize() != 0) {
                      list.setSelectedValue(nodeToSet, true);

                      // Scroll to the node based on the scroll policy.  CraigM 01/01/2008
                      if (scrollPolicy != Constants.NP_NOSCROLL) {
                          for (int i=0; i<list.getModel().getSize(); i++) {
                              if (list.getModel().getElementAt(i) == nodeToSet) {
                                  switch (scrollPolicy) {
                                  case Constants.NP_TOP:
                                      requestScroll(i+1, Constants.AF_TOP);
                                      break;
                                  case Constants.NP_BOTTOM:
                                      requestScroll(i+1, Constants.AF_BOTTOM);
                                      break;
                                  case Constants.NP_MIDDLE:
                                      requestScroll(i+1, Constants.AF_MIDDLE);
                                      break;
                                  default:
                                      requestScroll(i+1, Constants.AF_DEFAULT);
                                      break;
                                  }
                              }
                          }
                      }         
                  }
                  currentNode = nodeToSet;
              } catch (NullPointerException e){
                  /*
                   * this exception should only be thrown during a clone
                   * in which case it can be ignored
                   */
                  log.error("This exception should only be thrown during a clone", e);
              }
        }
      });
        UIutils.processGUIActions();
    }

    /**
     * The GetColumnCoordinates method returns the coordinates of the rectangle
     * around a column within an outline field.
     *
     * You use GetColumnCoordinates to get the specific coordinates, relative
     * to the OutlineField, of the rectangle that surrounds the column
     * specified with the column parameter.
     *
     * Since the columns may be scrolled off the current display, they can be
     * negative if the column is scrolled off to the left, or they can be
     * greater than the size of the outline field if scrolled off to the right.
     *
     * @param column number of the column within the OutlineField, numbered
     * from 1 as the first column on the left.
     * @param xLeft integer value measured in mils relative to the top left
     * @param yTop integer value measured in mils relative to the top left
     * @param xRight integer value measured in mils relative to the top left
     * @param yBottom integer value measured in mils relative to the top left
     * @return TRUE if any part of the column is currently visible, or a value
     * of FALSE, if it is not currently visible.
     *
     * NOTE: This method doesn't currently support hidden columns.
     *
     * Written by Craig Mitchell
     * @since 16/01/2008
     */
    public boolean getColumnCoordinates(int column, ParameterHolder xLeft, ParameterHolder yTop, ParameterHolder xRight, ParameterHolder yBottom) {
        column--; // Swap from Fortes 1 based to Javas 0 based

        if (this.isViewTable(this.getListStyle())) {
            if (column >= 0 && column < this.table.getColumnCount()) {
                for (int i=0; i<column; i++) {
                    xLeft.setInt(xLeft.getInt() + this.table.getColumnModel().getColumn(i).getWidth());
                }
                xRight.setInt(xLeft.getInt() + this.table.getColumnModel().getColumn(column).getWidth());
                yTop.setInt(this.table.getTableHeader().getHeight());
                yBottom.setInt(yTop.getInt() + this.table.getVisibleRect().height);

                // Convert to mils
                xLeft.setInt(UIutils.pixelsToMils(xLeft.getInt()));
                xRight.setInt(UIutils.pixelsToMils(xRight.getInt()));
                yTop.setInt(UIutils.pixelsToMils(yTop.getInt()));
                yBottom.setInt(UIutils.pixelsToMils(yBottom.getInt()));

                return true;
            }
        }
        return false;
    }

    public JList getList() {
        return list;
    }

    public void setList(JList list) {
        this.list = list;
    }

    public ListViewTable getTable() {
        return table;
    }

    public void setTable(ListViewTable table) {
        this.table = table;
    }

    public void sortColumn(int column, int status){
        this.sorter.sortColumn(column, status);
    }
    public void setSelectionMode(int SelectionMode){
        this.list.setSelectionMode(SelectionMode);

    }
    public int getSelectionMode(){
        return this.list.getSelectionMode();
    }

    public Color getSelectionBackground() {
        if (hasRowHighlights) {
            return selectionBackground;
        }
        else {
            return this.table.getBackground();
        }
    }

    public void setSelectionBackground(Color selectionBackground) {
        try {
            this.selectionBackground = selectionBackground;
            // TF:05/05/2009:Changed this method to ignore hasSelectionHighlights as this is taken
            // care of in the renderer now.
            this.table.setSelectionBackground(this.selectionBackground);
            this.list.setSelectionBackground(this.selectionBackground);
        } catch (NullPointerException e){
            /*
             * this exception should only be thrown during a clone
             * in which case it can be ignored
             */
                log.debug("This exception should only be thrown during a clone", e);
        }
    }

    public Color getSelectionForeground() {
        return selectionForeground;
    }

    public void setSelectionForeground(Color selectionForeground) {
        try {
          // TF:05/05/2009:Changed this method to ignore hasSelectionHighlights as this is taken
          // care of in the renderer now.
            this.selectionForeground = selectionForeground;
            this.table.setSelectionForeground(this.selectionForeground);
            this.list.setSelectionForeground(this.selectionForeground);
        } catch (NullPointerException e){
            /*
             * this exception should only be thrown during a clone
             * in which case it can be ignored
             */
            log.debug("This exception should only be thrown during a clone", e);
        }

    }
    private class NodeChangeListener implements ListSelectionListener{
        ListView listview;
        public NodeChangeListener(ListView lv){
            this.listview = lv;
        }
        public void valueChanged(ListSelectionEvent ev) {
            if (!ev.getValueIsAdjusting() && EventManager.isPostingEnabled()){
                oldNode = this.listview.currentNode;
                oldRow = this.listview.currentRow;
                currentRow = this.listview.list.getSelectedIndex() + 1; //PM:16/4/08 get the correct index
                Array_Of_DisplayNode<DisplayNode> nodes = this.listview.getSelectedNodes();
                if ((nodes != null) && (nodes.size() > 0))
                    this.listview.currentNode = (DisplayNode)this.listview.getSelectedNodes().get(0);
                else
                    this.listview.currentNode = null;
                Hashtable<String, Object> qq_Params = new Hashtable<String,Object>();
                qq_Params.put( "OldNode", new ParameterHolder(oldNode) );
                qq_Params.put( "NewNode", new ParameterHolder( getCurrentNode()));
                qq_Params.put( "OldRowNumber", new ParameterHolder(oldRow) );
                qq_Params.put( "NewRowNumber", new ParameterHolder(currentRow) );
                ClientEventManager.postEvent( this.listview, "AfterCurrentNodeChange", qq_Params );
            }
        }

    }

    private abstract static class DisplayNodeRenderer extends JPanel implements ListCellRenderer {
        protected JLabel labelText;
        protected JLabel labelIcon;
        protected int imageSize = 32;
        protected ArrayFieldModel model;
        protected String firstColumn;

        // ------------------------------------------------------------------------
        // Constructor
        // ------------------------------------------------------------------------
        public DisplayNodeRenderer(ArrayFieldModel model ) {
            this.model = model;
            this.labelText = new JLabel();
            // Leave the labelText_Line2 as null, as only one renderer needs it.
            this.labelIcon = new JLabel();
            this.setOpaque(false);
        }

        // ------------------------------------------------------------------------
        // Created so different renderers can use different text
        // ------------------------------------------------------------------------
        public String getNodeText(DisplayNode node) {
          if (this.model != null && (this.model instanceof ListViewTableModel)) {
            Object objValue = ((ListViewTableModel)this.model).getValue(node, 0);
            if (objValue != null) {
              return objValue.toString();
            }
            else {
              return "";
            }
          }
          else {
            return node.toString();
          }
        }

        // ------------------------------------------------------------------------
        // Render the JPanel
        // ------------------------------------------------------------------------
        public Component getListCellRendererComponent(
            JList list, // the list being redrawn
            Object value, // value to display
            int index, // cell index
            boolean isSelected, // is the cell selected
            boolean cellHasFocus) // the list and the cell have the focus
        {
          // TF:27/03/2009:Removed lots of extra casting which is unnecessary and adds runtime overhead
          DisplayNode node = (DisplayNode)value;
          // TF:28/04/2009:The list vell renderer should always refer to the first column in the list, not the DVNodeText
          // The DVNodeText is initialised in the constructor, so there's no case where this would be null (unless explicitly
          // set later) so this code stops a valid value being displayed.
/*     
         if (node.getDVNodeText()!= null){
            String text = this.getNodeText(node);
            this._LabelText.setText(text);
          }
          // this is a unique feature of Forte ListView, if the DVNodeText is null than the 1st column is used as the node text
          // So, for example, if the node is mapped to a DisplayNode subclass which has "Name" as it's first column, then the
          // Name will appear in the text next to the icon
          else*/
          this.labelText.setText(getNodeText(node));
         
          UIutils.labelWidth(this.labelText);
          // Set float over text as the label is often truncated.  ISIS-349881.
          this.setToolTipText(this.labelText.getText());

          ImageIcon image = null;
          if (isSelected) {

            if (list.hasFocus()) {
              this.labelText.setBackground(list.getSelectionBackground());
              this.labelText.setForeground(list.getSelectionForeground());
            }
            else {
              this.labelText.setBackground(java.awt.Color.lightGray);
              this.labelText.setForeground(java.awt.Color.black);
            }
            // TF:15/8/07:Moved the selection of the icon here, because it doesn't matter
            // if we have focus or not for which icon we display
            // TF:27/03/2009:Changed this to use the inbuilt method "asIconImage" and removed obsolete logic
            if (node.getDVSelectedIcon() != null) {
              image = node.getDVSelectedIcon().asIconImage();
            }
            else if (node.getDVLargeIcon() != null) {
              image = node.getDVLargeIcon().asIconImage();
            }
            else if (node.getDVSmallIcon() != null) {
              image = node.getDVSmallIcon().asIconImage();
            }
          }
          else {
            this.labelText.setBackground(list.getBackground());
            this.labelText.setForeground(list.getForeground());
           
            if (node.getDVLargeIcon() != null) {
              image = node.getDVLargeIcon().asIconImage();
            }
            else if (node.getDVSmallIcon() != null) {
              image = node.getDVSmallIcon().asIconImage();
            }
          }
          if (image != null){
            image = new ImageIcon(image.getImage().getScaledInstance(this.imageSize, this.imageSize, Image.SCALE_DEFAULT));
            this.labelIcon.setIcon(image);
          }
          else {
            // TF:05/05/2009:Added else check
            this.labelIcon.setIcon(null);
          }
          this.setEnabled(list.isEnabled());
          this.labelText.setFont(list.getFont());
          this.labelText.setOpaque(true);

          return this;
        }
    }
   
    private static class SmallIconDisplayNodeRenderer extends ListView.DisplayNodeRenderer{
        public SmallIconDisplayNodeRenderer(ArrayFieldModel alm){
            super(alm);
            this.setLayout(new BorderLayout(0,0));
            this.add(Box.createRigidArea(new Dimension(2,2)), java.awt.BorderLayout.WEST);
            this.add(Box.createRigidArea(new Dimension(2,2)), java.awt.BorderLayout.NORTH);
            this.add(this.labelIcon, java.awt.BorderLayout.WEST);
            this.add(this.labelText, java.awt.BorderLayout.CENTER);
            this.labelIcon.setHorizontalAlignment(JLabel.LEFT);
            this.labelText.setHorizontalAlignment(JLabel.LEFT);
            this.imageSize = 16;

        }
    }
    private static class ListDisplayNodeRenderer extends ListView.DisplayNodeRenderer{
        public ListDisplayNodeRenderer(ArrayFieldModel alm){
          // TF:05/05/2009:Fixed up this layout to mirror Forte's (In the majority of cases.
          // The layout Forte used depended on whether the list view had ever shown an icon
          // or not, and we don't want to mirror this uncommon situation)
            super(alm);
            this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
            this.add(this.labelIcon);
            this.add(Box.createRigidArea(new Dimension(2,2)));
            this.add(this.labelText);
            this.add(Box.createHorizontalGlue());
            this.labelIcon.setHorizontalAlignment(JLabel.LEFT);
            this.labelText.setHorizontalAlignment(JLabel.LEFT);
            this.imageSize = 16;
        }
    }

    private static class ImageDisplayNodeRenderer extends ListView.DisplayNodeRenderer{
      // This renderer actually has 2 lines of text in Forte, but if there is more text
      // then Forte would magically float the full text over the icon further down if there
      // was too much text. Java cannot do this, so we give it 3 lines of text.
        private JLabel labelText_TextLine;
     
        public ImageDisplayNodeRenderer(ArrayFieldModel alm){
            super(alm);
//            this.labelText = new TallLabel();
//
//            this.setLayout(new BorderLayout(0,0));
//            this.add(Box.createRigidArea(new Dimension(2,2)), java.awt.BorderLayout.WEST);
//            this.add(Box.createRigidArea(new Dimension(2,2)), java.awt.BorderLayout.NORTH);
//            this.add(this.labelIcon, java.awt.BorderLayout.CENTER);
//            this.add(this.labelText, java.awt.BorderLayout.SOUTH);
//            this.labelIcon.setHorizontalAlignment(JLabel.CENTER);
//            this.labelText.setVerticalAlignment(JLabel.TOP);
//            this.labelText.setHorizontalAlignment(JLabel.CENTER);
//            this.imageSize = 32;
            this.setLayout(new GridLayout(2,1));
            Box b1 = Box.createHorizontalBox();
            b1.add(Box.createHorizontalGlue());
            b1.add(this.labelIcon);
            b1.add(Box.createHorizontalGlue());
            Box b2 = Box.createHorizontalBox();
            b2.add(Box.createHorizontalGlue());
            JPanel linePanel = new JPanel();
            linePanel.setLayout(new BorderLayout());
            this.labelText_TextLine = new JLabel();
            linePanel.add(this.labelText_TextLine, BorderLayout.CENTER);
//            linePanel.add(this.labelText_Line2, BorderLayout.SOUTH);
            b2.add(linePanel);
          this.labelText_TextLine.setOpaque(true);
          this.labelText_TextLine.setHorizontalAlignment(SwingUtilities.CENTER);
          this.labelText_TextLine.setVerticalAlignment(SwingUtilities.TOP);
            b2.add(Box.createHorizontalGlue());
            this.add(b1);
            this.add(b2);
            this.imageSize = 32;
        }

        @Override
        public Component getListCellRendererComponent(
            JList list, // the list being redrawn
            Object value, // value to display
            int index, // cell index
            boolean isSelected, // is the cell selected
            boolean cellHasFocus) // the list and the cell have the focus
        {
          // Format the text as HTML. Note that we can't just use the label text
          // as this is use for other things such as the float-over help
          Component result = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
          String text = this.labelText.getText();
          this.labelText_TextLine.setText("<html><center>" + text + "</center></html>");
          this.labelText_TextLine.setForeground(this.labelText.getForeground());
          this.labelText_TextLine.setBackground(this.labelText.getBackground());
          this.labelText_TextLine.setFont(list.getFont());

          return result;
        }
    }
   
    private class PsuedoDoubleClickAction extends AbstractAction {
        public static final String PSUED0_DOUBLE_CLICK = "psuedoDoubleClick";

        public void actionPerformed(ActionEvent e) {
            MouseEvent me = new MouseEvent((JComponent)e.getSource(), e.getID(),
                    e.getWhen(), e.getModifiers(),
                    1, 1, 2, false, 1);
            new DoubleClickListener().mouseClicked(me);
        }
    }
    /**
     * for backward compatibility only
     * @deprecated
     * @param header
     */
    public void setTableHeader(JTableHeader header){
        this.table.setTableHeader(header);
    }
   
    @SuppressWarnings("unchecked")
  public Array_Of_DisplayNode<DisplayNode> getSelectedNodes() {
      // CraigM:28/08/2008 - Never return a null array
      Array_Of_DisplayNode<DisplayNode> nodes = new Array_Of_DisplayNode<DisplayNode>();

      switch (listStyle) {
      case Constants.LT_SMALLICON:
      case Constants.LT_IMAGE:
      case Constants.LT_LIST:
        if (this.list.getSelectedIndices().length > 0)
          nodes = new Array_Of_DisplayNode(DisplayNode.class, this.list.getSelectedValues())
        break;
      default:
        int count = table.getSelectedRowCount();
        if (count > 0) {
          int[] rows = table.getSelectedRows();
         
          for (int i = 0; i < count; i++) {
            // adjust index to model index in case it is sorted
            if (sorter != null && tableModel != null){
              int row = sorter.modelIndex(rows[i]);
              Object rowData = tableModel.getData().get(row);
              nodes.add((DisplayNode)rowData);
            }
          }
        }
      }
      return nodes;
    }
   
    public int getSelectedCount(){
      switch (listStyle) {
      case Constants.LT_SMALLICON:
        case Constants.LT_IMAGE:
        case Constants.LT_LIST:
            return this.list.getSelectedIndices().length;
        default:
            return this.table.getSelectedRowCount();
        }

    }
    public void clearSelectedNodes(){
        //  Fix clearing of selection - do it in the selection model
//    this.list.setSelectedIndex(-1);
        list.getSelectionModel().clearSelection();
    }
    /**
     * The SortToggle attribute (boolean) indicates whether or not the direction of the sort (ascending or descending) changes when the user clicks a column header.
     * @return
     */
    public boolean isSortToggle() {
        return sortToggle;
    }

    /**
     * The SortToggle attribute (boolean) indicates whether or not the direction of the sort (ascending or descending) changes when the user clicks a column header.
     * @param sortToggle
     */
    public void setSortToggle(boolean sortToggle) {
        try {
            this.sortToggle = sortToggle;
            this.sorter.setSortToggle( sortToggle );
        } catch (NullPointerException e){
            /*
             * this exception should only be thrown during a clone
             * in whic case it can be ignored
             */
            log.debug("This exception should only be thrown during a clone", e);
        }

    }
    /**
     * The SortType attribute (integer) indicates the kind of sort to perform when the user clicks the header of a column. It can take one of the following values
     * @return
     */
    public int getSortType() {
        return sortType;
    }
    /**
     * The SortType attribute (integer) indicates the kind of sort to perform when the user clicks the header of a column. It can take one of the following values
     * @param sortType
     */
    public void setSortType(int sortType) {
        this.sortType = sortType;
        log.error("ListView.setSortType() is a stub implemetation only");
    }
    /**
     * gets the roon node bound to the ListView
     * @return
     */
    public DisplayNode getRootNode() {
        return rootNode;
    }
    /**
     * sets the root node bound to the ListView
     * @param rootNode
     */
    @SuppressWarnings("unchecked")
  public void setRootNode(final DisplayNode pRootNode) {
        if (rootNode == pRootNode)
            return;
        rootNode = pRootNode;
        // TF:30/04/2009:Forced this to be on the EDT to stop an EDT violation.
        ActionMgr.addAction(new PendingAction(null) {
          @Override
          public void performAction() {
                if (rootNode == null || rootNode.getChildCount() == 0) {
                    ListView.this.setListData((Object [])null);
                    tableModel.setViewNodes(null);
                }
                if (rootNode != null) {
                    rootNode.setUserObject(ListView.this);

                    Enumeration kids = rootNode.children();
                    Array_Of_DisplayNode data = new Array_Of_DisplayNode(rootNode.getClass(), kids);

                    ListView.this.setListData(data.toArray());
                    ListView.this.tableModel.setViewNodes(data);
                }
          }
          @Override
          public String toString() {
            return "SetRootNode(" + pRootNode + ") on " + ListView.this;
          }
        });
    }

    /**
     * The nodeRefreshListener is a static property change listener that listens for any node
     * mapped to this table to be changed, and updates the underlying table model.
     */
    private PropertyChangeListener nodeRefreshListener = new PropertyChangeListener() {
        public void propertyChange(final PropertyChangeEvent evt) {
            ActionMgr.addAction(new PendingAction(null) {
                @Override
                public void performAction() {
                    if (evt.getSource() instanceof DisplayNode && rootNode != null) {
                        int index = rootNode.getIndex((DisplayNode)evt.getSource());
                        if (index >= 0) {
                            ListView.this.table.tableChanged(new TableModelEvent(ListView.this.tableModel, index));
                        }
                    }
                }
                @Override
                public String toString() {
                    return "refreshing data on list view";
                }
            });
        }
    };

    /**
     * inserts a DisplayNode at the specified index
     * @param index
     * @param node
     */
    public void insertNode(int index, DisplayNode node){
        if (node != null){
            DefaultListModel model = (DefaultListModel) this.list.getModel();
            model.ensureCapacity(index+1);
            model.insertElementAt(node, index);
            if (index >= this.tableModel.getRowCount()){
                this.tableModel.addRow(node);
            }
            else {
                this.tableModel.insertRow(index, node);
            }
            node.addPropertyChangeListener(nodeRefreshListener);
        }
    }

    /**
     * Removes a display node from the list view. This method simply updates the underlying model, and does
     * not affect the parentage of the node, assuming this will have already been done. This method should not
     * be called by any classes not a part of the DisplayProject helper classes
     * @param node
     */
    public void removeNode(DisplayNode node) {
        if (node != null) {
            DefaultListModel model = (DefaultListModel)this.list.getModel();
            model.removeElement(node);
            int index = this.tableModel.getRowIndex(node);
            if (index >= 0) {
                this.tableModel.removeRow(index);
                this.tableModel.fireTableRowsDeleted(index, index);
            }
//            node.removeFromParent();
            node.removePropertyChangeListener(nodeRefreshListener);
        }
    }

    /**
     * Scroll to pRow
     * @param pRow 0 based
     * Written by Craig Mitchell
     * @since 02/01/2008
     */
    private void scrollRowToVisible(int pRow) {
        if (!(table.getParent() instanceof JViewport))
            return;
        JViewport viewport = (JViewport)table.getParent();
        java.awt.Rectangle rect = table.getCellRect(pRow, 0, true);
        java.awt.Rectangle viewRect = viewport.getViewRect();
        rect.setLocation(rect.x - viewRect.x, rect.y - viewRect.y);
        viewport.scrollRectToVisible(rect);
    }

    /**
     * The RequestScroll method initiates a sequence of events to scroll an ListView field line to a specified position in the ListView field display area, moving other lines into and out of the display area as necessary.
     * @param pLine desired line
     * @param pScrollPolicy see setScrollPolicy
     */
    public void requestScroll(final int pLine, final int pScrollPolicy) {
        ActionMgr.addAction(new PendingAction(null) {
            @Override
            public void performAction() {
                if (pScrollPolicy==Constants.AF_TOP) {
                    ListView.this.scrollRowToVisible(ListView.this.getTotalLines()-1);
                    ListView.this.scrollRowToVisible(pLine-1);
                }
                else if (pScrollPolicy==Constants.AF_BOTTOM) {
                    ListView.this.scrollRowToVisible(0);
                    ListView.this.scrollRowToVisible(pLine-1);
                }
                else {
                    ListView.this.scrollRowToVisible(pLine-1);
                }
            }

            @Override
            public String toString() {
                return "ListView.requestScroll(" + pLine + ", " + pScrollPolicy + ")";
            }
        });
    }

    /**
     * The ScrollPolicy attribute (boolean) determines where to scroll the current node in an outline field when an application sets it as the current node. The current node always appears in view.<br>
     * <br>
     * ScrollPolicy uses the following values:<br>
     * Value    Definition<br>
     * NP_AUTOMATIC  Automatic scrolling - puts the node somewhere in view.<br>
     * NP_BOTTOM  Scrolls current node to the bottom of outline field display.<br>
     * NP_DEFAULT  Automatic scrolling - puts the node somewhere in view.<br>
     * NP_MIDDLE  Scrolls current node to the middle of outline field display.<br>
     * NP_NOSCROLL  Does not scroll specified node.<br>
     * NP_TOP    Scrolls current node to the top of outline field display.<br>
     *
     * @param pScrollPolicy
     * Written by Craig Mitchell
     * @since 1/1/2008
     */
    public void setScrollPolicy(int pScrollPolicy) {
        this.scrollPolicy = pScrollPolicy;
    }

    public int getScrollPolicy() {
        return this.scrollPolicy;
    }

    public void setRootVisible(boolean visible){

    }

    /**
     * Indicates whether or not the ListView highlights the node under the cursor when
     * the user is doing a drag and drop operation and drags the cursor over a node in
     * the list.  Default is set to true.  CraigM: 04/04/2008.
     */
    public void setIsDropHighlightEnabled(boolean enabled) {
      this.isDropHighlightEnabled = enabled;
    }
   
    /**
     * Indicates whether or not the ListView highlights the node under the cursor when
     * the user is doing a drag and drop operation and drags the cursor over a node in
     * the list.  Default is set to true.  CraigM: 04/04/2008.
     */
    public boolean getIsDropHighlightEnabled() {
      return this.isDropHighlightEnabled;
    }

    public JPopupMenu getPopupMenu() {
        switch (listStyle) {
        case Constants.LT_SMALLICON:
        case Constants.LT_IMAGE:
        case Constants.LT_LIST:
            return this.list.getComponentPopupMenu();
        default:
            return this.table.getComponentPopupMenu();
        }
    }

    /**
     * A magic constant that forte seems to tack onto the size of a listview
     */
    private static final int LIST_VIEW_MARGIN = 18;

    /**
     * A magic constant that Forte seems to use to scale the average character by
     */
    private static final double LIST_VIEW_AVG_CHAR_SCALING_FACTOR = 0.75;

    /**
     * Set the size of the list view based on the number of visible columns it has
     * @param pCols
     */
    public void setVisibleColumns(int pCols) {
      // TF:15/07/2008:Believe it or not, this seems to be the formula that Forte used to determine size
        this.visibleColumns = pCols;
        Insets ins = getInsets();
        double charWidth = UIutils.averageCharacter(this) * LIST_VIEW_AVG_CHAR_SCALING_FACTOR;
        double size = pCols * charWidth;
        WidthInPixels.set(this, (int)(size + ins.left + ins.right + LIST_VIEW_MARGIN + charWidth));
    }

    public int getVisibleColumns() {
        if (this.visibleColumns == -1) {
          // TF:15/07/2008:Reverse the above calculation to get the number of columns
            // this.visibleColumns = UIutils.pixelsToCols(WidthInPixels.get(this), this);
            Insets ins = getInsets();
            double charWidth = UIutils.averageCharacter(this) * LIST_VIEW_AVG_CHAR_SCALING_FACTOR;
            int size = WidthInPixels.get(this);
            return (int)((size - ins.left - ins.right - LIST_VIEW_MARGIN - charWidth) / charWidth);
        }
        return this.visibleColumns;
    }

    public void setHasTitles(boolean pTitles) {
        switch (listStyle) {
        case Constants.LT_SMALLICON:
        case Constants.LT_IMAGE:
        case Constants.LT_LIST:
            // No effect, lists don't have headers
        default:
            if (!pTitles) {
              // CraigM:21/11/2008 - Hide the header
                this.table.getTableHeader().setVisible(false);
                this.table.getTableHeader().setPreferredSize(new Dimension(0,0));
            }
            else {
              // CraigM:21/11/2008 - Show the header
                this.table.getTableHeader().setVisible(true);
                this.table.getTableHeader().setPreferredSize(null);
            }
        }
    }
   
    /**
     * CraigM:13/11/2008.
     * @return if titles are showing or not
     */
    public boolean getHasTitles() {
      return this.table.getTableHeader().isVisible();
    }
   
    /**
     * NOT IMPLEMENTED.  This method didn't do anything in Forte on a ListView.  CraigM:13/11/2008.
     * @param rows
     */
    @Deprecated
    public void setMinHeightInRows(int rows) {
    }

    /**
     * Return the first row displayed in the list view
     */
    public int getTopLine() {
        switch (listStyle) {
        case Constants.LT_SMALLICON:
        case Constants.LT_IMAGE:
        case Constants.LT_LIST:
            return this.list.getFirstVisibleIndex();     
        default:
            Point p = this.table.getVisibleRect().getLocation();
        return this.table.rowAtPoint(p) + 1 ;
        }
    }

    public void setHasVertScrollbar(boolean pHasVertScrollbar) {
        this.scrollPane.setVerticalScrollBarPolicy(pHasVertScrollbar ? ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED : ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
    }

    public void setHasHorzScrollbar(boolean pHasHorzScrollbar) {
        this.scrollPane.setHorizontalScrollBarPolicy(pHasHorzScrollbar ? ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED : ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    }
    public int getSelectedRow(){
        switch (listStyle) {
        case Constants.LT_SMALLICON:
        case Constants.LT_IMAGE:
        case Constants.LT_LIST:
            return this.list.getSelectedIndex() + 1;
        default:
            return this.table.getSelectedRow() + 1;
        }
    }


    /**
     * Focus is applied to the constituent JList or JTable depending on which is being displayed.
     * @see javax.swing.JComponent#requestFocusInWindow()
     */
    public boolean requestFocusInWindow() {
        boolean success = false;
        if (isViewTable(getListStyle())) {
            if (getTable() != null) {
                success = getTable().requestFocusInWindow();
            }
        }
        else {
            if (getList() != null) {
                success = getList().requestFocusInWindow();
            }
        }
        return success;
    }

    public void setBounds(int x, int y, int width, int height) {
        super.setBounds(x, y, width, height);
        this.table.setSize(width, height);
        this.list.setSize(width, height);
        this.scrollPane.setSize(width, height);
    }

    public void setBackground(Color color){
        super.setBackground(Color.WHITE);
        if (this.list != null)
            this.list.setBackground(Color.WHITE);
        if (this.table != null)
            this.table.setBackground(Color.WHITE);
    }

    @Override
    public void setName(String name) {
        super.setName(name);
        if (this.list != null)
            this.list.setName(name);
        if (this.table != null)
            this.table.setName(name);
    }
   
    /**
     * Get the outline column which is associated with the passed array column
     * @param col
     * @return
     */
    public OutlineColumnDesc getOutlineColumn(ArrayColumn col, boolean pSetArrayColLink) {
      int state = col.isVisible() ? Constants.FS_VISIBLE : Constants.FS_INVISIBLE;

      // CraigM:22/08/2008 - Only set to drag if column is visible
      if (this.table.getDragEnabled() && col.isVisible()) {
        state = Constants.FS_DRAG;
      }

      // TF:19/06/2008:Split this out to a separate method
        OutlineColumnDesc desc = new OutlineColumnDesc(col.getAlignment(),
                false,
                false,
                // TF:19/06/2008:The max characters actually reflects the current width
                //col.getMaxCharacters(),
                UIutils.pixelsToCols((int)(col.getWidth() / UIutils.FORTE_COLUMNS_SCALING_FACTOR), this.table),
                col.getName(),
                col.getSizePolicy(),
                state,
                (String)col.getHeaderValue(),
                col.getTitleMsgNumber());
        desc.setActualWidth(col.getWidth());
       
        if (pSetArrayColLink) {
          desc.setArrayColumn(col);
        }
       
        return desc;
    }
    /**
     * The GetColumnList method returns a copy of an outline field�s array of outline column descriptors
     * @return
     */
    public Array_Of_OutlineColumnDesc<OutlineColumnDesc> getColumnList(){
        Array_Of_OutlineColumnDesc<OutlineColumnDesc> columns = new Array_Of_OutlineColumnDesc<OutlineColumnDesc>();
        ArrayColumnModel acm = (ArrayColumnModel)this.table.getColumnModel();
       
        // CraigM:22/08/2008 - Get the real columns
        /*
        if (acm.getRealColumnCount() > 0) {
            columns = new Array_Of_OutlineColumnDesc<OutlineColumnDesc>();
        }
        */
        for (int i = 0; i < acm.getRealColumnCount(); i++){
            ArrayColumn col = (ArrayColumn)acm.getRealColumn(i);
            columns.add(this.getOutlineColumn(col, false));
        }
        return columns;
    }

    /**
     * You use setColumnList to produce the column descriptors for outline fields for code generation; you cannot use it to dynamically change the outline column descriptors for a ListView field displaying data.
     * @param columns
     */
    public void setColumnList(Array_Of_OutlineColumnDesc<OutlineColumnDesc> columns){
        //  === Column model setup ===
        ArrayColumnModel cm = new ArrayColumnModel();
        int colNum = 0;
        // TF:14 Jul 2009:Placing a check if columns are null
        if (columns == null) {
          return;
       
        String[] propNames = new String[columns.size()];
        boolean isDraggable = false;

        for (OutlineColumnDesc col : (List<OutlineColumnDesc>)columns){
            int alignment = JLabel.LEFT;
            switch (col.getAlignment()){
            case Constants.TA_LEFT:
                alignment = JLabel.LEFT;
                break;
            case Constants.TA_RIGHT:
                alignment = JLabel.RIGHT;
                break;
            case Constants.TA_CENTER:
                alignment = JLabel.CENTER;
                break;
            }
            int colWidth = 0;
            if (col.getArrayColumn() != null) {
                colWidth = col.getArrayColumn().getMinWidth();
            }
            else if (col.getActualWidth() >= 0) {
              colWidth = col.getActualWidth();
            }
            else {
              // TF:19/3/08: Added in a factor to make the columns approximately as wide as they are in Forte.
                colWidth = (int)(UIutils.colsToPixels(col.getMaxCharacters(), this) * UIutils.FORTE_COLUMNS_SCALING_FACTOR);
            }
            AlignedCellRenderer renderer = new AlignedCellRenderer(alignment, col.getName().toString());
            renderer.setInsets(0, 2, 0, 2);
            ArrayColumn ac = new ArrayColumn(col.getName().toString(),
                    colNum,
                    colWidth,
                    renderer,
                    false,
                    new Integer(col.getTitleMsgNum()));
           
            // CraigM:18/07/2008 - We need to set the size policy as it is used in ArrayFieldModel.resizeColumnIfNecessary()
            ac.setSizePolicy(col.getSizePolicy());
           
            // TF:19/06/2008:In Forte, the user could always adjust the widths of the columns in a list view. If we use
            // setMinWidth (as the ArrayColumn constructor does) then the user cannot reduce the columns smaller than
            // their initial size. Hence we use setMinWidth(0) and setPreferredWith. This is done here rather than in
            // the array column constructor as this rule applies only for list views, not array fields.
            ac.setMinWidth(0);
            ac.setPreferredWidth(colWidth);
           
            col.setArrayColumn(ac);
            cm.addColumn(ac, col.getTitle().toString(), col.getState() != Constants.FS_INVISIBLE);

            propNames[colNum] = col.getName().toString();
            colNum++;
           
            if (col.getState() == Constants.FS_DRAG) {
              isDraggable = true;
            }
        }

        // Set if this list views nodes are draggable.  CraigM: 24/03/2008
        if (isDraggable) {
            this.table.setDragEnabled(true);
            this.list.setDragEnabled(true);
        }
        else {
            this.table.setDragEnabled(false);
            this.list.setDragEnabled(false);
        }
       
        // Always set up the transfer handlers.  CraigM: 24/03/2008
        this.table.setTransferHandler(ClientEventManager.getObjectDropTransferHandler());
        this.list.setTransferHandler(ClientEventManager.getObjectDropTransferHandler());
       
        // By default, disallow the list view to be a drop target.  Will be set to a drop target in the ForteTransferHandler.  CraigM: 24/03/2008
        this.table.setDropTarget(null);
        this.list.setDropTarget(null);

        this.tableModel.setColumnList(propNames);
        setColumnModel(cm);
    }

    public ListView cloneComponent(){
        ListView clone = TableFactory.newListView(this.getName(), this.getListStyle(), this.getFont(), this.getFont().getStyle());
        Array_Of_OutlineColumnDesc<OutlineColumnDesc> cols = this.getColumnList();
        clone.setMappedType(this.getMappedType());
        clone.setColumnList(cols);
        CloneHelper.cloneComponent(this, clone, new String[]{"UI", // class javax.swing.plaf.PanelUI
                "UIClassID", // class java.lang.String
                "accessibleContext", // class javax.accessibility.AccessibleContext
                "actionMap", // class javax.swing.ActionMap
                //"alignmentX", // float
                //"alignmentY", // float
                "ancestorListeners", // class [Ljavax.swing.event.AncestorListener;
                //"autoscrolls", // boolean
                "background", // class java.awt.Color
                "border", // interface javax.swing.border.Border
                "columnList", // class DisplayProject.Array_Of_OutlineColumnDesc
                "columnModel", // class DisplayProject.ArrayColumnModel
                "component", // null
                "componentCount", // int
                //"componentPopupMenu", // class javax.swing.JPopupMenu
                "components", // class [Ljava.awt.Component;
                "containerListeners", // class [Ljava.awt.event.ContainerListener;
                "currentNode", // class DisplayProject.DisplayNode
                "currentRow", // int
                "debugGraphicsOptions", // int
                //"doubleBuffered", // boolean
                //"enabled", // boolean
                "focusCycleRoot", // boolean
                "focusTraversalKeys", // null
                "focusTraversalPolicy", // class java.awt.FocusTraversalPolicy
                "focusTraversalPolicyProvider", // boolean
                "focusTraversalPolicySet", // boolean
                //"focusable", // boolean
                //"font", // class java.awt.Font
                //"foreground", // class java.awt.Color
                "graphics", // class java.awt.Graphics
                //"hasHorzScrollbar", // boolean
                //"hasTitles", // boolean
                //"hasVertScrollbar", // boolean
                //"height", // int
                "inheritsPopupMenu", // boolean
                "inputMap", // null
                "inputVerifier", // class javax.swing.InputVerifier
                "insets", // class java.awt.Insets
                "layout", // interface java.awt.LayoutManager
                "list", // class javax.swing.JList
                "listData", // class [Ljava.lang.Object;
                //"listStyle", // int
                "managingFocus", // boolean
                //"maximumSize", // class java.awt.Dimension
                //"minimumSize", // class java.awt.Dimension
                "name", // class java.lang.String
                "nextFocusableComponent", // class java.awt.Component
                //"opaque", // boolean
                //"optimizedDrawingEnabled", // boolean
                "paintingTile", // boolean
                //"popupMenu", // class javax.swing.JPopupMenu
                "preferredSize", // class java.awt.Dimension
                "registeredKeyStrokes", // class [Ljavax.swing.KeyStroke;
                "requestFocusEnabled", // boolean
                "rootNode", // class DisplayProject.DisplayNode
                "rootPane", // class javax.swing.JRootPane
                "rootVisible", // boolean
                "selectedCount", // int
                "selectedNodes", // class DisplayProject.Array_Of_DisplayNode
                "selectedRow", // int
                "selectionBackground", // class java.awt.Color
                "selectionForeground", // class java.awt.Color
                "selectionMode", // int
                "selectionModel", // interface javax.swing.ListSelectionModel
                "sortToggle", // boolean
                "sortType", // int
                "sorter", // class DisplayProject.TableSorter
                "table", // class javax.swing.JTable
                "tableHeader", // class javax.swing.table.JTableHeader
                "tableModel", // class DisplayProject.ArrayListModel
                //"toolTipText", // class java.lang.String
                "topLevelAncestor", // class java.awt.Container
                "topLine", // int
                "transferHandler", // class javax.swing.TransferHandler
                "validateRoot", // boolean
                "verifyInputWhenFocusTarget", // boolean
                "vetoableChangeListeners", // class [Ljava.beans.VetoableChangeListener;
                "viewNodes", // class DisplayProject.Array_Of_DisplayNode
                //"visible", // boolean
                //"visibleColumns", // int
                "visibleRect", // class java.awt.Rectangle
                //"width", // int
                //"x", // int
                //"y" // int
        });
        return clone;
    }
    /**
     * Return the type to which this array is mapped
     * @return
     */
    public Class<?> getMappedType() {
        return mappedType;
    }

    /**
     * Set the mapped type of the list view. This muct be a sub-class of Array_Of_DisplayNode
     */
    public void setMappedType(Class<?> mappedType) {
        if (Array_Of_DisplayNode.class.isAssignableFrom(mappedType)) {
            Class<?> oldType = this.mappedType;
            this.mappedType = mappedType;
            this.firePropertyChange("mappedType", oldType, this.mappedType);
        }
    }

    public int getVisibleLines() {
        if (this.visibleLines == -1)
            this.visibleLines = UIutils.pixelsToRows(HeightInPixels.get(this), this);
        return visibleLines;
    }

    public void setVisibleLines(int visibleLines) {
        this.visibleLines = visibleLines;
        Insets ins = this.scrollPane.getInsets();
        int rowTotal = this.table.getRowHeight() * this.visibleLines;
        int headerHeight = (this.table.getTableHeader() == null) ? 0 : this.table.getTableHeader().getPreferredSize().height + 2;
        int rowSpacing = this.table.getIntercellSpacing().height * this.visibleLines;
        int height = rowTotal + headerHeight + rowSpacing + ins.top + ins.bottom;
        HeightInPixels.set(this, height);
    }

    public boolean getHasRowHighlights() {
        return hasRowHighlights;
    }

    public void setHasRowHighlights(final boolean hasHighlights) {
        if (hasHighlights != hasRowHighlights) {
            boolean oldValue = hasRowHighlights;
            this.hasRowHighlights = hasHighlights;

            // TF:05/05/2009:Changed this to make the cell renderers aware of this property, and hence behave properly.
            // Note that in Forte, the use of the row highlights only pertained to details mode. This repaint will force
            // the AlignedCellRenderer to use the new value of this property.
            ListView.this.table.repaint();
            this.firePropertyChange("hasRowHighlights", oldValue, hasHighlights);
        }
    }

  /* (non-Javadoc)
   * @see javax.swing.JComponent#setFont(java.awt.Font)
   */
  @Override
  public void setFont(Font font) {
    super.setFont(font);
   
    // JCT-496 Make sure the list and Table have the same font
    if (this.table != null) {
      this.table.setFont(font);
      this.table.getTableHeader().setFont(font);
    }
    if (this.list != null) {
      this.list.setFont(font);
    }
   
    // CraigM:18/07/2008 - After changing the font, we need to recalculate the size
    if (this.visibleColumns > 0) {
      this.setVisibleColumns(this.visibleColumns);
    }
    if (this.visibleLines > 0) {
      this.setVisibleLines(this.visibleLines);
    }
  }

    /**
     * Undocumented Forte method
     * @deprecated use {@link #getColumnList()}
     * @return
     */
  public Array_Of_OutlineColumnDesc<OutlineColumnDesc> getDynamicColumnList() {
    return this.getColumnList();
  }

  /*
   * Allow auto scrolling up and down when doing drag and drop.  CraigM: 10/04/2008
   *
   * @see java.awt.dnd.Autoscroll#getAutoscrollInsets()
   */
  public void autoscroll(Point cursorLocn) {
    // Scroll up
    if (cursorLocn.y < this.getHeight()/2) {
          this.scrollRowToVisible(this.getTopLine()-2);
    }
    // Scroll down
    else {
          this.scrollRowToVisible(this.getVisibleLines()-1 + this.getTopLine());
    }
  }

  /*
   * Set the trigger points when using auto scrolling when doing drag and drop.  CraigM: 10/04/2008
   *
   * @see java.awt.dnd.Autoscroll#getAutoscrollInsets()
   */
  public Insets getAutoscrollInsets() {
    if (this.isAutoScrollEnabled) {
          JTableHeader header = this.getTable().getTableHeader();
        if (header != null) {
          return new Insets(10+header.getHeight(),0,10,0);
        }
      return new Insets(15,0,15,0);
    }
    else {
      return new Insets(0,0,0,0);
    }
  }
  public boolean isSuppressSort() {
    return this.sorter.isSuppressSort();
  }

  public void setSuppressSort(boolean suppressSort) {
    this.sorter.setSuppressSort(suppressSort);
  }

  /**
   * We still want to get the RequestSort event, however, we don't want the sorter to do anything.
   * This will be called automatically when someone registers for the RequestSort event.  It will
   * be triggered by the RequestSortListener.
   * CraigM:20/08/2008.
   */
  public void setOverrideSort() {
    this.sorter.setOverrideSort();
  }
  /**
   * Loads the colum titles from a given Message Catalog
   *
   */
  //PM:23/4/08
  public void loadColumnHeadings() {
    int setNumber = getTitleSetNum();
    loadColumnHeadings(setNumber, MsgCatalog.getInstance());
  }
  /**
   * Loads the colum titles from a given Message Catalog
   * @param mcat
   *
   */
  //PM:23/4/08
  public void loadColumnHeadings(MsgCatalog mcat) {
    int setNumber = getTitleSetNum();
    loadColumnHeadings(setNumber, mcat);
  }
 
  /**
   * Loads the colum titles from a given Message Catalog
   *
   * @param setNumber
   * @param mcat
   */
  //PM:23/4/08
  public void loadColumnHeadings(int setNumber, MsgCatalog mcat) {
    ArrayColumnModel acm = (ArrayColumnModel)this.table.getColumnModel();
   
    // CraigM:01/05/2008:Use the total array collection (including invisible columns)
    ArrayList<ArrayColumn> allColumns = acm.getColumnList();

    for (int i = 0; i < allColumns.size(); i++){
            ArrayColumn col = allColumns.get(i);
            if (col.getTitleMsgNumber() > 0){
              String titleString = mcat.getString(setNumber, col.getTitleMsgNumber());
              col.setHeaderValue(titleString);
            }
        }
  }
 
  public void setTitleSetNum(int num) {
    putClientProperty("qq_TitleSetNum", new Integer(num));
    loadColumnHeadings(num, MsgCatalog.getInstance());
  }
 
  public int getTitleSetNum() {
    //PM:10/08/2008:fixed null pointer
    Integer setNum = (Integer)getClientProperty("qq_TitleSetNum");
    return (setNum == null) ? -1 : setNum;
  }
 
  /**
   * Allow the column to be reordered by dragging it
   *
   * CraigM:16/06/2008
   * @param draggable
   */
  public void setIsColDraggable(boolean draggable) {
    if (draggable) {
      this.table.getTableHeader().setReorderingAllowed(true);
    } else {
      this.table.getTableHeader().setReorderingAllowed(true);
    }
  }
 
  /**
   * Convert the view row index (0 based) into the model row index (0 based).  Will only be different if we are sorting.
   * CraigM:20/08/2008.
   * @param viewRowIndex
   * @return
   */
  public int getModelRowIndex(int viewRowIndex) {
    if (this.getSorter().isSorting()) {
      if (viewRowIndex >= 0) {
        return this.getSorter().modelIndex(viewRowIndex);
      }
    }
    return viewRowIndex;
  }
 
  public int getViewRowIndex(int modelRowIndex) {
    if (this.getSorter().isSorting()) {
      return this.getSorter().viewIndex(modelRowIndex);
    }
    return modelRowIndex;
  }
 
  /**
   * Return the display node corresponding to the number of the first line displayed. This was an undocumented
   * method in Forte, and is reproduced here for completeness. This method does not need to
   * be invoked from the EDT
   * @return The DisplayNode corresponding to the first visible row
   */
  public DisplayNode getTopNode() {
    // If we're not on the EDT, there may be pending actions which will affect this, so process them first
    UIutils.processGUIActions();
    if (this.getParent() != null && this.getParent().getParent() instanceof JScrollPane) {
      JScrollPane sp = (JScrollPane)this.getParent().getParent();
      Point p = sp.getViewport().getViewPosition();
      Insets i = sp.getInsets();
      int row = table.rowAtPoint(new Point(p.x + i.left, p.y + i.top));
      return tableModel.getRow(row);
    }
    else {
      Insets i = this.getInsets();
      int row = table.rowAtPoint(new Point(i.left, i.top));
      return tableModel.getRow(row);
    }
  }

  public DisplayNode getFirstVisibleThreadedNode() {
    return getTopNode();
  }
 
    /**
     * Determine the index of the first visible column. If no visible column is found, this method returns -1
     * @return integer index of the first visible column.
     */
    public int getFirstVisibleColumnIndex() {
        ArrayColumnModel acm = (ArrayColumnModel)this.table.getColumnModel();
        for (int i = 0; i < acm.getRealColumnCount(); i++){
            ArrayColumn col = (ArrayColumn)acm.getRealColumn(i);
          if (col.isVisible()) {
            return i;
          }
        }
    return -1;
  }


}
TOP

Related Classes of DisplayProject.controls.ListView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.