/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.
*/
/*
* IntellicutList.java
* Creation date: Dec 10th 2002
* By: Ken Wong
*/
package org.openquark.gems.client;
import java.awt.Container;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JList;
import javax.swing.JViewport;
import javax.swing.ListSelectionModel;
import org.openquark.gems.client.IntellicutListModel.FilterLevel;
import org.openquark.gems.client.IntellicutListModelAdapter.IntellicutListEntry;
import org.openquark.gems.client.IntellicutManager.IntellicutMode;
/**
* A list that encapsulates all the details associated with populating
* a list with the unqualified names of gems.
* @author Ken Wong
* @author Frank Worsley
*/
public class IntellicutList extends JList {
private static final long serialVersionUID = -8515575227984050475L;
/** The last list index that was visible when the state was saved. */
private int savedVisibleIndex = -1;
/** The last list entry that was visible when the state was saved. */
private IntellicutListEntry savedVisibleEntry = null;
/** The list entry that was selected when the state was saved. */
private IntellicutListEntry savedSelectedEntry = null;
/** Whether or not this list should be drawn transparently. */
private boolean transparent = false;
/**
* Constructor for IntellicutList.
*/
public IntellicutList(IntellicutMode mode) {
setName("MatchingGemsList");
setCellRenderer(new IntellicutListRenderer(mode));
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
/**
* Enables the list to be transparent. If the list is transparent non selected list items
* will let the components below them show through. Selected list items will draw a blue
* semi-transparent tint as their background.
* @param value whether or not the list should be transparent
*/
void setTransparent(boolean value) {
this.transparent = value;
updateUI();
}
/**
* @return whether or not the intellicut list is transparent
*/
public boolean isTransparent() {
return transparent;
}
/**
* Saves the currently selected list item and the scroll position of the
* list so that the list state can be restored later.
*/
private void saveListState() {
IntellicutListModel listModel = (IntellicutListModel) getModel();
savedVisibleEntry = null;
savedSelectedEntry = null;
savedVisibleIndex = getLastVisibleIndex();
int selectedIndex = getSelectedIndex();
try {
savedVisibleEntry = listModel.get(savedVisibleIndex);
savedSelectedEntry = listModel.get(selectedIndex);
// We only want to make the selected entry visible again after refreshing
// the list if it was visible before.
if (selectedIndex > getLastVisibleIndex() || selectedIndex < getFirstVisibleIndex()) {
savedSelectedEntry = null;
}
} catch (Exception ex) {
// This might throw a NullPointerException or ArrayIndexOutOfBoundsException
// if the model is not initialized. In that case there is no index we can restore.
}
}
/**
* Restores the previously saved list state as closely as possible.
*/
private void restoreListState() {
IntellicutListModel listModel = (IntellicutListModel) getModel();
int newSelectedIndex = listModel.indexOf(savedSelectedEntry);
int newVisibleIndex = listModel.indexOf(savedVisibleEntry);
if (newVisibleIndex == -1) {
newVisibleIndex = savedVisibleIndex;
}
// We have to add one cell height, otherwise the cell will be just
// outside of the visible range of the list. ensureIndexIsVisible doesn't
// work here since we want the index to appear at the very bottom.
Point newLocation = indexToLocation(newVisibleIndex);
if (newLocation != null) {
newLocation.y += getCellBounds(0, 0).height;
scrollRectToVisible(new Rectangle(newLocation));
}
if (newSelectedIndex != -1) {
setSelectedIndex(newSelectedIndex);
ensureIndexIsVisible(newSelectedIndex);
} else {
setSelectedIndex(0);
}
}
/**
* Refreshes the list while remembering the selected item and scroll position
* as closely as possible. Simply refreshing the list model will not remember
* the list state.
* @param prefix the new prefix for values in the list
*/
public void refreshList(String prefix) {
IntellicutListModel listModel = (IntellicutListModel) getModel();
saveListState();
listModel.refreshList(prefix);
restoreListState();
}
/**
* Finds the JViewport this list is embedded in.
* @return the viewport or null if there is no viewport
*/
private JViewport getViewport() {
Container parent = getParent();
while (parent != null && !(parent instanceof JViewport)) {
parent = parent.getParent ();
}
return (JViewport) parent;
}
/**
* Scrolls the given index as close as possible to the top of the list's viewport.
* @param index index of the item to scroll to the top
*/
void scrollToTop(int index) {
Point location = indexToLocation(index);
if (location == null) {
return;
}
JViewport viewport = getViewport();
// Scroll this element as close to the top as possible.
if (viewport.getViewSize().height - location.y < viewport.getSize().height) {
location.y -= viewport.getSize().height - viewport.getViewSize().height + location.y;
}
viewport.setViewPosition(location);
}
/**
* Sets the current gem filtering level.
*
* The list is automatically refreshed after doing this. This method also saves
* the list's displayed state as opposed to just setting the filter level which
* will not remember the list state.
* @param filterLevel the new level to filter at
*/
void setFilterLevel(FilterLevel filterLevel) {
if (filterLevel == null) {
throw new NullPointerException("filterLevel must not be null in IntellicutList.setFilterLevel");
}
IntellicutListModel listModel = (IntellicutListModel) getModel();
saveListState();
listModel.setFilterLevel(filterLevel);
restoreListState();
}
/**
* Returns the user selected IntellicutListEntry
* @return IntellicutListEntry
*/
IntellicutListEntry getSelected(){
return getModel().getSize() > 0 ? (IntellicutListEntry) getSelectedValue() : null;
}
/**
* We want tooltips to be displayed to the right of an item.
* If there is no item at the coordinates it returns null.
* @param e the mouse event for which to determine the location
* @return the tooltip location for the list item at the coordinates of the mouse event
*/
@Override
public Point getToolTipLocation(MouseEvent e) {
int index = locationToIndex(e.getPoint());
if (index == -1) {
return null;
}
Rectangle cellBounds = getCellBounds(index, index);
// take off 50 and add 5 for good looks
return new Point (cellBounds.x + cellBounds.width - 50, cellBounds.y + 5);
}
/**
* @param e the MouseEvent for which to get the tooltip text
* @return the tooltip text for the list item at the coordinates or null if there is no
* item at the coordinates
*/
@Override
public String getToolTipText(MouseEvent e) {
int index = locationToIndex(e.getPoint());
if (index == -1) {
return null;
}
IntellicutListEntry listEntry = (IntellicutListEntry) getModel().getElementAt(index);
if (listEntry == null) {
return null;
}
IntellicutListModel listModel = (IntellicutListModel) getModel();
return listModel.getAdapter().getToolTipTextForEntry(listEntry, this);
}
}