org.htmlparser.lexerapplications.thumbelina.PicturePanel Maven / Gradle / Ivy
Show all versions of bboss-htmlparser Show documentation
// HTMLParser Library $Name: v1_5 $ - A java-based parser for HTML
// http://sourceforge.org/projects/htmlparser
// Copyright (C) 2003 Derrick Oswald
//
// Revision Control Information
//
// $Source: /cvsroot/htmlparser/htmlparser/src/org/htmlparser/lexerapplications/thumbelina/PicturePanel.java,v $
// $Author: derrickoswald $
// $Date: 2005/04/12 11:27:41 $
// $Revision: 1.2 $
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
package org.htmlparser.lexerapplications.thumbelina;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import javax.swing.JPanel;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.border.BevelBorder;
/**
* Hold and display a group of pictures.
* @author derrick
*/
public class PicturePanel
extends
JPanel
implements
MouseListener,
Scrollable,
ComponentListener,
HierarchyListener
{
/**
* Scrolling unit increment (both directions).
*/
protected static final int UNIT_INCREMENT = 10;
/**
* Scrolling block increment (both directions).
*/
protected static final int BLOCK_INCREMENT = 100;
/**
* The thumbelina object in use.
*/
protected Thumbelina mThumbelina;
/**
* The display mosaic.
*/
protected TileSet mMosaic;
/**
* The preferred size of this component.
* null
initially, caches the results of
* calculatePreferredSize ()
.
*/
protected Dimension mPreferredSize;
/**
* Creates a new instance of PicturePanel
* @param thumbelina The Thumeblina
this panel is associated
* with.
*/
public PicturePanel (final Thumbelina thumbelina)
{
mThumbelina = thumbelina;
mMosaic = new TileSet ();
mPreferredSize = null;
setBorder (new BevelBorder (BevelBorder.LOWERED));
addMouseListener (this);
addHierarchyListener (this);
}
/**
* Clears the panel, discarding any existing images.
*/
public void reset ()
{
mMosaic = new TileSet ();
repaint ();
}
/**
* Move the given picture to the top of the Z order.
* Adds it, even it if it doesn't exist.
* Also puts the URL in the url text of the status bar.
* @param picture The picture being brought forward.
*/
public void bringToTop (final Picture picture)
{
picture.reset ();
mMosaic.bringToTop (picture);
repaint (picture.x, picture.y, picture.width, picture.height);
mThumbelina.mUrlText.setText (picture.getURL ().toExternalForm ());
}
/**
* Find a picture with the given URL in the panel.
* This should really only be used to discover if the picture is still
* visible. There could be more than one picture with the given URL
* because it may be partially obscured by another picture, in which
* case the pieces are each given their own picture object, but all
* point at the same URL
and Image
.
* @param url The url to locate.
* @return The first picture encountered in the panel,
* or null if the picture was not found.
*/
public Picture find (final String url)
{
Iterator enumeration;
Picture picture;
Picture ret;
ret = null;
enumeration = mMosaic.getPictures ();
while ((null == ret) && enumeration.hasNext())
{
picture = (Picture)enumeration.next ();
if (url.equals (picture.getURL ().toExternalForm ()))
ret = picture;
}
return (ret);
}
/**
* Draw an image on screen.
* @param picture The picture to draw.
* @param add If true
, the picture is added to the history.
*/
protected void draw (final Picture picture, final boolean add)
{
Component parent;
boolean dolayout;
Dimension before;
Dimension after;
parent = getParent ();
dolayout = false;
synchronized (mMosaic)
{
if (parent instanceof JViewport)
{
before = getPreferredSize ();
mMosaic.add (picture);
after = calculatePreferredSize ();
if (after.width > before.width)
dolayout = true;
else
after.width = before.width;
if (after.height > before.height)
dolayout = true;
else
after.height = before.height;
if (dolayout)
mPreferredSize = after;
}
else
mMosaic.add (picture);
}
if (dolayout)
revalidate ();
repaint (picture.x, picture.y, picture.width, picture.height);
if (add)
mThumbelina.addHistory (picture.getURL ().toExternalForm ());
}
/**
* Updates this component.
* @param graphics The graphics context in which to update the component.
*/
public void update (final Graphics graphics)
{
paint (graphics);
}
/**
* Adjust the graphics clip region to account for insets.
* @param graphics The graphics object to set the clip region for.
*/
public void adjustClipForInsets (final Graphics graphics)
{
Dimension dim;
Insets insets;
Rectangle clip;
dim = getSize ();
insets = getInsets ();
clip = graphics.getClipBounds ();
if (clip.x < insets.left)
clip.x = insets.left;
if (clip.y < insets.top)
clip.y = insets.top;
if (clip.x + clip.width > dim.width - insets.right)
clip.width = dim.width - insets.right - clip.x;
if (clip.y + clip.height > dim.height - insets.bottom)
clip.height = dim.height - insets.bottom - clip.y;
graphics.setClip (clip.x, clip.y, clip.width, clip.height);
}
/**
* Paints this component.
* Runs through the list of tiles and for every one that intersects
* the clip region performs a drawImage()
.
* @param graphics The graphics context used to paint with.
*/
public void paint (final Graphics graphics)
{
Rectangle clip;
Iterator enumeration;
HashSet set; // just so we don't draw things twice
Picture picture;
Image image;
Point origin;
int width;
int height;
adjustClipForInsets (graphics);
clip = graphics.getClipBounds ();
synchronized (mMosaic)
{
if (0 == mMosaic.getSize ())
super.paint (graphics);
else
{
super.paint (graphics);
enumeration = mMosaic.getPictures ();
set = new HashSet ();
while (enumeration.hasNext())
{
picture = (Picture)enumeration.next ();
if ((null == clip) || (clip.intersects (picture)))
{
image = picture.getImage ();
if (!set.contains (image))
{
origin = picture.getOrigin ();
width = image.getWidth (this);
height = image.getHeight (this);
graphics.drawImage (picture.getImage (),
origin.x, origin.y,
origin.x + width, origin.y + height,
0, 0, width, height,
this);
set.add (image);
}
}
}
}
}
}
/**
* Get the preferred size of the component.
* @return The dimension of this component.
*/
public Dimension getPreferredSize ()
{
if (null == mPreferredSize)
setPreferredSize (calculatePreferredSize ());
else
if ((0 == mPreferredSize.width) || (0 == mPreferredSize.height))
setPreferredSize (calculatePreferredSize ());
return (mPreferredSize);
}
/**
* Sets the preferred size of this component.
* @param dimension The new value to use for
* getPreferredSize()
until recalculated.
*/
public void setPreferredSize (final Dimension dimension)
{
mPreferredSize = dimension;
}
/**
* Compute the preferred size of the component.
* Computes the minimum bounding rectangle covering all the pictures in
* the panel. It then does some funky stuff to handle
* embedding in the view port of a scroll pane, basically asking
* up the ancestor heirarchy what size is available, and filling it.
* @return The optimal dimension for this component.
*/
protected Dimension calculatePreferredSize ()
{
Iterator enumeration;
int x;
int y;
Picture picture;
Component parent;
Insets insets;
Dimension ret;
enumeration = mMosaic.getPictures ();
x = 0;
y = 0;
picture = null;
while (enumeration.hasNext ())
{
picture = (Picture)enumeration.next ();
if (picture.x + picture.width > x)
x = picture.x + picture.width;
if (picture.y + picture.height > y)
y = picture.y + picture.height;
}
parent = getParent ();
if (parent instanceof JViewport)
{
ret = parent.getSize ();
insets = ((JViewport)parent).getInsets ();
ret.width -= insets.left + insets.right;
ret.height -= insets.top + insets.bottom;
if ((0 != ret.width) || (0 != ret.height))
ret.width -= 2; // ... I dunno why, it just needs it
if (ret.width < x)
ret.width = x;
if (ret.height < y)
ret.height = y;
}
else
{
insets = getInsets ();
x += insets.left + insets.right;
y += insets.top + insets.bottom;
ret = new Dimension (x, y);
}
return (ret);
}
//
// MouseListener Interface
//
/**
* Invoked when the mouse button has been clicked
* (pressed and released) on a component.
* Not used.
* @param event The object providing details of the mouse event.
*/
public void mouseClicked (final MouseEvent event)
{
}
/**
*Invoked when a mouse button has been released on a component.
* Not used.
* @param event The object providing details of the mouse event.
*/
public void mouseReleased (final MouseEvent event)
{
}
/**
* Invoked when the mouse enters a component.
* Not used.
* @param event The object providing details of the mouse event.
*/
public void mouseEntered (final MouseEvent event)
{
}
/**
* Invoked when the mouse exits a component.
* Not used.
* @param event The object providing details of the mouse event.
*/
public void mouseExited (final MouseEvent event)
{
}
/**
* Handle left click on a picture by bringing it to the top.
* @param event The object providing details of the mouse event.
*/
public void mousePressed (final MouseEvent event)
{
Picture picture;
if (!event.isMetaDown ())
{
picture = mMosaic.pictureAt (event.getX (), event.getY ());
if (null != picture)
bringToTop (picture);
}
}
//
// Scrollable interface
//
/**
* Returns the preferred size of the viewport for a view component.
* For example the preferredSize of a JList component is the size
* required to accommodate all of the cells in its list however the
* value of preferredScrollableViewportSize is the size required for
* JList.getVisibleRowCount() rows. A component without any properties
* that would effect the viewport size should just return
* getPreferredSize() here.
*
* @return The preferredSize of a JViewport whose view is this Scrollable.
* @see JViewport#getPreferredSize
*/
public Dimension getPreferredScrollableViewportSize ()
{
return (getPreferredSize ());
}
/**
* Components that display logical rows or columns should compute
* the scroll increment that will completely expose one new row
* or column, depending on the value of orientation. Ideally,
* components should handle a partially exposed row or column by
* returning the distance required to completely expose the item.
*
* Scrolling containers, like JScrollPane, will use this method
* each time the user requests a unit scroll.
*
* @param visibleRect The view area visible within the viewport
* @param orientation Either SwingConstants.VERTICAL or
* SwingConstants.HORIZONTAL.
* @param direction Less than zero to scroll up/left,
* greater than zero for down/right.
* @return The "unit" increment for scrolling in the specified direction.
* This value should always be positive.
*/
public int getScrollableUnitIncrement (
final Rectangle visibleRect,
final int orientation,
final int direction)
{
return (UNIT_INCREMENT);
}
/**
* Components that display logical rows or columns should compute
* the scroll increment that will completely expose one block
* of rows or columns, depending on the value of orientation.
*
* Scrolling containers, like JScrollPane, will use this method
* each time the user requests a block scroll.
*
* @param visibleRect The view area visible within the viewport
* @param orientation Either SwingConstants.VERTICAL or
* SwingConstants.HORIZONTAL.
* @param direction Less than zero to scroll up/left,
* greater than zero for down/right.
* @return The "block" increment for scrolling in the specified direction.
* This value should always be positive.
*/
public int getScrollableBlockIncrement (
final Rectangle visibleRect,
final int orientation,
final int direction)
{
return (BLOCK_INCREMENT);
}
/**
* Return true if a viewport should always force the width of this
* Scrollable
to match the width of the viewport.
* For example a normal
* text view that supported line wrapping would return true here, since it
* would be undesirable for wrapped lines to disappear beyond the right
* edge of the viewport. Note that returning true for a Scrollable
* whose ancestor is a JScrollPane effectively disables horizontal
* scrolling.
*
* Scrolling containers, like JViewport, will use this method each
* time they are validated.
*
* @return true
if a viewport should force the Scrollables
* width to match its own.
*/
public boolean getScrollableTracksViewportWidth ()
{
return (false);
}
/**
* Return true if a viewport should always force the height of this
* Scrollable to match the height of the viewport. For example a
* columnar text view that flowed text in left to right columns
* could effectively disable vertical scrolling by returning
* true here.
*
* Scrolling containers, like JViewport, will use this method each
* time they are validated.
*
* @return true
if a viewport should force the Scrollables
* height to match its own.
*/
public boolean getScrollableTracksViewportHeight ()
{
return (false);
}
//
// ComponentListener interface
//
/**
* Invoked when the container's size changes.
* Un-caches the preferred size.
* @param event The resize event.
*/
public void componentResized (final ComponentEvent event)
{
setPreferredSize (null);
}
/**
* Invoked when the component's position changes.
* Not used.
* @param event The component event.
*/
public void componentMoved (final ComponentEvent event)
{
}
/**
* Invoked when the component has been made visible.
* Not used.
* @param event The component event.
*/
public void componentShown (final ComponentEvent event)
{
}
/**
* Invoked when the component has been made invisible.
* Not used.
* @param event The component event.
*/
public void componentHidden (final ComponentEvent event)
{
}
//
// HierarchyListener interface
//
/**
* Handles this components ancestor being added to a container.
* Registers this component as a listener for size changes on the
* ancestor so that we may un-cache the prefereed size and force
* a recalculation.
* @param event The heirarchy event.
*/
public void hierarchyChanged (final HierarchyEvent event)
{
if (0 != (event.getChangeFlags () & HierarchyEvent.PARENT_CHANGED))
{
Component dad = event.getChanged ();
Component parent = getParent ();
if ((null != parent) && (parent.getParent () == dad))
dad.addComponentListener (this);
}
}
}
/*
* Revision Control Modification History
*
* $Log: PicturePanel.java,v $
* Revision 1.2 2005/04/12 11:27:41 derrickoswald
* Documentation revamp part two.
*
* Revision 1.1 2003/09/21 18:20:56 derrickoswald
* Thumbelina
* Created a lexer GUI application to extract images behind thumbnails.
* Added a task in the ant build script - thumbelina - to create the jar file.
* You need JDK 1.4.x to build it. It can be run on JDK 1.3.x in crippled mode.
* Usage: java -Xmx256M thumbelina.jar [URL]
*
*
*/