// BlogBridge -- RSS feed reader, manager, and web based service
// Copyright (C) 2002-2007 by R. Pito Salas
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software Foundation;
// either version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with this program;
// if not, write to the Free Software Foundation, Inc., 59 Temple Place,
// Suite 330, Boston, MA 02111-1307 USA
//
// Contact: R. Pito Salas
// mailto:pitosalas@users.sourceforge.net
// More information: about BlogBridge
// http://www.blogbridge.com
// http://sourceforge.net/projects/blogbridge
//
// $Id: AbstractSelectorComponent.java,v 1.2 2007/06/13 10:49:48 spyromus Exp $
//
package com.salas.bb.views;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.uif.util.SystemUtils;
import com.salas.bb.utils.uif.IconSource;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
* Abstract selector component that knows how to track its state.
*/
public abstract class AbstractSelectorComponent extends JComponent
{
/** None-state for the pressed field. */
private static final int NONE = -1;
/**
* Prepares on-off-pressed icons for all given modes.
*
* @param modeNames names of modes.
*
* @return icons[modes.length][3].
*/
protected static Icon[][] prepareIcons(String[] modeNames)
{
Icon[][] icons = new Icon[modeNames.length][3];
for (int mode = 0; mode < modeNames.length; mode++)
{
String name = modeNames[mode];
for (State state : State.values())
{
icons[mode][state.ordinal()] = getIconFromResources(name, state);
}
}
return icons;
}
/**
* Returns icon from the resources.
*
* @param modeName mode name.
* @param state state.
*
* @return icon.
*/
protected static Icon getIconFromResources(String modeName, State state)
{
return getIcon(modeName + "." + STATES[state.ordinal()]);
}
/** Button / icon state. */
protected enum State { ON, OFF, PRESSED }
/** The names of the states. */
protected static final String[] STATES = { "on", "off", "pressed" };
/** What's pressed at the moment. */
protected int pressed = NONE;
/** <code>TRUE</code> when the mouse is over the component. */
protected boolean mouseOver;
/** A flag to avoid repainting when induced by self. */
protected boolean selfEvent;
/** The model to update. */
protected final ValueModel model;
private static final Icon[] SEPARATOR = new Icon[]
{
ViewTypeSelector.getIcon("separator.on"),
ViewTypeSelector.getIcon("separator.off"),
ViewTypeSelector.getIcon("separator.pressed")
};
/**
* Creates a selector component for a given model.
*
* @param valueModel model.
*/
public AbstractSelectorComponent(ValueModel valueModel)
{
selfEvent = false;
model = valueModel;
model.addValueChangeListener(new ModelChangeListener());
Dimension size = getPreferredDimensions();
setMinimumSize(size);
setMaximumSize(size);
setPreferredSize(size);
enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
/**
* Returns the dimensions for the icon in the given state.
*
* @param mode mode.
* @param state state.
*
* @return dimensions or <code>NULL</code> if the icon isn't found or invalid.
*/
protected Dimension getIconDimension(int mode, State state)
{
Icon icon = getIcon(mode, state);
return getIconDimensions(icon);
}
/**
* Returns the dimensions of the icon.
*
* @param aIcon icon.
*
* @return dimensions or <code>NULL</code> if icon is not specified.
*/
protected static Dimension getIconDimensions(Icon aIcon)
{
return aIcon == null ? null : new Dimension(aIcon.getIconWidth(), aIcon.getIconHeight());
}
/**
* Returns icon for a given mode and state.
*
* @param mode mode.
* @param state state.
*
* @return icon.
*/
protected abstract Icon getIcon(int mode, State state);
/**
* Returns separator icon in the given state.
*
* @param state state.
*
* @return icon.
*/
protected static Icon getSeparatorIcon(State state)
{
return SEPARATOR[state.ordinal()];
}
/**
* Invoked when painting of the component is necessary.
*
* @param g graphics context.
*/
protected abstract void paintComponent(Graphics g);
/**
* Returns the state of the mode button.
*
* @param md mode.
*
* @return state.
*/
protected State getCurrentState(int md)
{
return pressed == md && mouseOver ? State.PRESSED : getMode() == md ? State.ON : State.OFF;
}
/**
* Returns the state of a separator component between two buttons with
* the given states.
*
* @param firstState first state.
* @param secondState second state.
*
* @return separator state.
*/
protected static State getSeparatorState(State firstState, State secondState)
{
return (firstState == State.ON || secondState == State.ON)
? State.ON : (firstState == State.PRESSED || secondState == State.PRESSED)
? State.PRESSED : State.OFF;
}
/**
* Processes mouse events occurring on this component by dispatching them to any registered
* <code>MouseListener</code> objects.
*
* @param e the mouse event
*/
protected void processMouseEvent(MouseEvent e)
{
int id = e.getID();
int md;
switch (id)
{
case MouseEvent.MOUSE_PRESSED:
md = locationToMode(e.getPoint());
if (md != NONE)
{
mouseOver = true;
pressed = md;
repaint();
}
break;
case MouseEvent.MOUSE_RELEASED:
if (pressed != NONE)
{
md = locationToMode(e.getPoint());
if (pressed == md && mouseOver)
{
selfEvent = true;
try
{
setMode(md);
} finally
{
selfEvent = false;
}
}
pressed = NONE;
repaint();
}
break;
case MouseEvent.MOUSE_ENTERED:
mouseOver = true;
repaint();
break;
case MouseEvent.MOUSE_EXITED:
mouseOver = false;
repaint();
break;
}
}
/**
* Processes mouse motion events, such as MouseEvent.MOUSE_DRAGGED.
*
* @param e the <code>MouseEvent</code>
*
* @see java.awt.event.MouseEvent
*/
protected void processMouseMotionEvent(MouseEvent e)
{
int id = e.getID();
if (!contains(e.getPoint())) return;
if (id == MouseEvent.MOUSE_DRAGGED)
{
int md = locationToMode(e.getPoint());
if (pressed != md)
{
if (mouseOver)
{
mouseOver = false;
repaint();
}
} else if (!mouseOver)
{
mouseOver = true;
repaint();
}
}
}
/**
* Converts a point (mouse pointer) within the component coordinates
* into the mode (button).
*
* @param aPoint point.
*
* @return button / mode.
*/
protected abstract int locationToMode(Point aPoint);
/**
* Returns current mode.
*
* @return mode.
*/
private int getMode()
{
return (Integer)model.getValue();
}
/**
* Sets different mode.
*
* @param mode mode.
*/
protected void setMode(int mode)
{
model.setValue(mode);
}
/**
* Reports a desired dimensions for this component.
* Used during the construction (once).
*
* @return dimensions.
*/
protected abstract Dimension getPreferredDimensions();
/**
* Returns icon by its resource key. It automatically appends ".mac" to the
* key on Mac platform.
*
* @param key key of the icon resource.
*
* @return icon.
*/
protected static Icon getIcon(String key)
{
if (SystemUtils.IS_OS_MAC) key += ".mac";
return IconSource.getIcon(key);
}
/**
* Listens for mode changes.
*/
protected class ModelChangeListener implements PropertyChangeListener
{
/**
* Invoked when mode changes.
*
* @param evt event object.
*/
public void propertyChange(PropertyChangeEvent evt)
{
if (!selfEvent) repaint();
}
}
}