/*
* Licensed Materials - Property of Perforce Software, Inc.
* © Copyright Perforce Software, Inc. 2014, 2021
* © Copyright IBM Corp. 2009, 2014
* © Copyright ILOG 1996, 2009
* All Rights Reserved.
*
* Note to U.S. Government Users Restricted Rights:
* The Software and Documentation were developed at private expense and
* are "Commercial Items" as that term is defined at 48 CFR 2.101,
* consisting of "Commercial Computer Software" and
* "Commercial Computer Software Documentation", as such terms are
* used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
* as applicable.
*/
package ganttviewer.pert;
import ganttviewer.pert.model.GanttToSDMAdapter;
import ganttviewer.util.GanttViewerLogger;
import ganttviewer.util.PERTDiagram;
import ilog.views.IlvApplyObject;
import ilog.views.IlvGrapher;
import ilog.views.IlvGraphic;
import ilog.views.IlvManager;
import ilog.views.IlvManagerViewInteractor;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvTransformer;
import ilog.views.event.InteractorListener;
import ilog.views.event.TransformerListener;
import ilog.views.gantt.IlvActivity;
import ilog.views.gantt.IlvConstraint;
import ilog.views.gantt.IlvGanttModel;
import ilog.views.gantt.IlvGanttModelUtil;
import ilog.views.gantt.event.SelectionEvent;
import ilog.views.gantt.event.SelectionListener;
import ilog.views.gantt.util.event.IlvHashedEventService;
import ilog.views.interactor.IlvPanInteractor;
import ilog.views.interactor.IlvSelectInteractor;
import ilog.views.sdm.IlvSDMEngine;
import ilog.views.sdm.IlvSDMView;
import ilog.views.sdm.event.SDMEngineSelectionEvent;
import ilog.views.sdm.event.SDMEngineSelectionListener;
import ilog.views.sdm.event.SDMGraphLayoutRendererEvent;
import ilog.views.sdm.event.SDMGraphLayoutRendererListener;
import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.swing.IlvToolTipManager;
import ilog.views.symbol.util.IlvSelectableSymbol;
import ilog.views.util.event.IlvEventListenerCollection;
import ilog.views.util.event.IlvEventListenerSet;
import java.awt.AWTEvent;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JScrollBar;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicArrowButton;
/**
* Implementation of the PERT diagram.
*/
SuppressWarnings("serial")
public class PERTDiagramImpl extends JRootPane implements PERTDiagram,
SDMEngineSelectionListener {
private static final Rectangle OVERVIEW_BOUNDS = new Rectangle(10, 10, 200,
100);
/**
* The overview.
*/
private Overview overview;
/**
* The manager view.
*/
private IlvJScrollManagerView scrollView;
/**
* The event service.
*/
private IlvHashedEventService eventService;
/**
* The selection listener(s).
*/
private IlvEventListenerCollection<SelectionListener> selectionListeners;
/**
* Selection listening enabled or disabled.
*/
private boolean listenToSelection = true;
/**
* Layout listener.
*/
private SDMGraphLayoutRendererListener layoutListener;
/**
* Apply collapse action.
*/
private IlvApplyObject applyCollapse;
/**
* Builds a <code>PERTDiagramImpl</code>.
*/
public PERTDiagramImpl() {
initEventService();
// create the PERT diagram
createPERTDiagram();
}
/**
* Creates a PERT diagram.
*/
protected void createPERTDiagram() {
scrollView = new IlvJScrollManagerView(new IlvSDMView());
setContentPane(scrollView);
// overview
overview = new Overview((IlvSDMView) scrollView.getView());
// collapse action
applyCollapse = new IlvApplyObject() {
Override
public void apply(IlvGraphic g, Object arg) {
((IlvGrapher) g).setCollapsed(true);
}
};
// layout listener
layoutListener = new SDMGraphLayoutRendererListener() {
Override
public void performLayoutEnded(SDMGraphLayoutRendererEvent event) {
overview.updateContent();
}
Override
public void performLayoutStarted(SDMGraphLayoutRendererEvent event) {
}
};
// glass pane layer customization
initGlassPane();
}
/**
* Custom glass pane.
*
*/
class CustomGlassPane extends JPanel implements AWTEventListener {
/**
* Builds a <code>CustomGlassPane</code>.
*/
public CustomGlassPane() {
try {
Toolkit.getDefaultToolkit().addAWTEventListener(this,
AWTEvent.MOUSE_EVENT_MASK);
} catch (Exception e) {
// doing nothing, the overview will not be displayed
}
}
/**
* @see javax.swing.JComponent.contains(int, int)
*/
Override
public boolean contains(int x, int y) {
// AWT system shows the cursor of the topmost visible component
// In our case, the topmost visible component is this glass pane
// and we want to display the current cursor of the IlvSDMView
// This fix makes the glass pane completely transparent
return false;
}
/**
* @see java.awt.event.AWTEventListener.eventDispatched(java.awt.AWTEvent)
*/
Override
public void eventDispatched(AWTEvent event) {
if (event instanceof MouseEvent) {
MouseEvent me = (MouseEvent) event;
if (!SwingUtilities.isDescendingFrom(me.getComponent(),
PERTDiagramImpl.this)) {
return;
}
// make the overview visible or not depending on the current interaction
if (isTranslation(me)) {
overview.setVisible(true);
} else if (stopTranslation(me)) {
overview.setVisible(false);
}
}
}
}
/**
* Indicates whether the mouse event is considered as a translation interaction.
* @param me The mouse event.
* @return Whether the mouse event is considered as a translation interaction.
*/
protected boolean isTranslation(MouseEvent me) {
if (me.getSource() instanceof IlvSDMView) {
return (((IlvSDMView) me.getSource()).getInteractor() instanceof IlvPanInteractor)
&& (MouseEvent.MOUSE_PRESSED == me.getID());
}
if ((me.getSource() instanceof JScrollBar)
|| (me.getSource() instanceof BasicArrowButton)) {
return MouseEvent.MOUSE_PRESSED == me.getID();
}
return false;
}
/**
* Indicates whether the mouse event is considered as a stop-translation interaction.
* @param me The mouse event.
* @return Whether the mouse event is considered as a stop-translation interaction.
*/
protected boolean stopTranslation(MouseEvent me) {
return MouseEvent.MOUSE_RELEASED == me.getID();
}
/**
* Customizes the glass pane to display an overview.
*/
protected void initGlassPane() {
JPanel glassPane = new CustomGlassPane();
glassPane.setLayout(null);
glassPane.setOpaque(false);
glassPane.add(overview);
overview.setBounds(PERTDiagramImpl.OVERVIEW_BOUNDS);
overview.setVisible(false);
setGlassPane(glassPane);
glassPane.setVisible(true);
}
/**
* Initializes the event services.
*/
private void initEventService() {
eventService = new IlvHashedEventService();
selectionListeners = new IlvEventListenerSet<SelectionListener>();
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#initialize()
*/
Override
public void initialize() {
// activate the mouse wheel support
scrollView.setWheelScrollingEnabled(true);
IlvSDMView view = (IlvSDMView) scrollView.getView();
// activate tooltips
IlvToolTipManager.registerView(view);
// activate wheel zooming
view.setWheelZoomingEnabled(true);
// selection
IlvSelectInteractor selectInteractor = new IlvSelectInteractor();
selectInteractor.setMoveAllowed(false);
selectInteractor.setMultipleSelectionMode(true);
view.setInteractor(selectInteractor);
// listen to selection
view.getSDMEngine().addSDMEngineSelectionListener(this);
// a way to improve the performances:
// no rendering done on selection or property change
// selection is more reactive
view.getSDMEngine().setRenderingDoneMode(IlvSDMEngine.NEVER);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#attachGanttModel(ilog.views.gantt.IlvGanttModel)
*/
Override
public void attachGanttModel(IlvGanttModel ganttModel) {
IlvSDMEngine engine = getSDMEngine();
engine.setAdjusting(true);
try {
// create the model adapter
GanttToSDMAdapter sdmModel = new GanttToSDMAdapter(ganttModel);
engine.setModel(sdmModel, true);
engine.performNodeLayout();
} catch (Exception e) {
GanttViewerLogger.error(e);
} finally {
engine.setAdjusting(false);
}
}
/**
* Returns the collapsed activities.
* @return The collapsed activities.
*/
protected Set<IlvActivity> getCollapsedNodes() {
IlvSDMEngine engine = getSDMEngine();
Set<IlvActivity> collapsedNodes = new HashSet<IlvActivity>();
// retrieve collapsed nodes
Enumeration<?> objects = engine.getAllObjects();
while (objects.hasMoreElements()) {
Object obj = objects.nextElement();
if (obj instanceof IlvActivity) {
IlvGraphic graphic = engine.getGraphic(obj, false);
if (graphic instanceof IlvGrapher) {
IlvGrapher grapher = (IlvGrapher) graphic;
if (grapher.isCollapsed()) {
collapsedNodes.add((IlvActivity) obj);
}
}
}
}
return collapsedNodes;
}
/**
* Collapses the nodes corresponding to the given activities.
* @param activities The activities.
*/
protected void setCollapsedNodes(Set<IlvActivity> activities) {
if ((activities == null) || (activities.size() == 0)) {
return;
}
IlvSDMEngine engine = getSDMEngine();
Iterator<IlvActivity> it = activities.iterator();
while (it.hasNext()) {
IlvGrapher grapher = (IlvGrapher) engine.getGraphic(it.next(), false);
grapher.getGraphicBag().applyToObject(grapher, applyCollapse, grapher,
false);
}
// perform layout
engine.performNodeLayout();
}
/**
* Gets the selected objects.
* @return The selected objects.
*/
protected Set<Object> getSelectedObjects() {
IlvSDMEngine engine = getSDMEngine();
Set<Object> selectedObjects = new HashSet<Object>();
// retrieve collapsed nodes
Enumeration<?> objects = engine.getSelectedObjects();
while (objects.hasMoreElements()) {
selectedObjects.add(objects.nextElement());
}
return selectedObjects;
}
/**
* Internal selection of the given objects.
* @param selectedObjects The objects to select.
*/
protected void setSelectedObjects(Set<Object> selectedObjects) {
if ((selectedObjects == null) || (selectedObjects.size() == 0)) {
return;
}
listenToSelection = false;
Iterator<Object> objects = selectedObjects.iterator();
while (objects.hasNext()) {
Object obj = objects.next();
if (obj instanceof IlvActivity) {
setSelectedImpl((IlvActivity) obj, true);
} else if (obj instanceof IlvConstraint) {
setSelectedImpl((IlvConstraint) obj, true);
}
}
listenToSelection = true;
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#applyStyle(java.lang.String)
* @proofread
*/
Override
public void applyStyle(String css) throws Exception {
Set<IlvActivity> collapsedNodes = null;
IlvSDMEngine engine = getSDMEngine();
// if this is not the first stylesheet:
if (engine.getNodeLayoutRenderer() != null) {
// remove the previous layout listener
engine.getNodeLayoutRenderer().removeGraphLayoutRendererListener(
layoutListener);
// retrieve the collapsed nodes to set them collapsed after applying the new stylesheet
collapsedNodes = getCollapsedNodes();
}
// get selection
Set<Object> selectedObjects = getSelectedObjects();
// apply stylesheet
getSDMView().setStyleSheets(0, css);
// restaure collapsed nodes
setCollapsedNodes(collapsedNodes);
// restaure selection
setSelectedObjects(selectedObjects);
// add the layout listener again
engine.getNodeLayoutRenderer().addGraphLayoutRendererListener(
layoutListener);
engine.performNodeLayout();
}
/**
* Returns the SDM view.
* @return The SDM view.
*/
public IlvSDMView getSDMView() {
return (IlvSDMView) scrollView.getView();
}
/**
* Returns the SDM engine.
* @return The SDM engine.
*/
public IlvSDMEngine getSDMEngine() {
return getSDMView().getSDMEngine();
}
/**
* Returns the loaded Gantt data model.
* @return The Gantt data model.
*/
Override
public IlvGanttModel getGanttModel() {
GanttToSDMAdapter sdmModel = (GanttToSDMAdapter) this.getSDMEngine()
.getModel();
return sdmModel.getGanttModel();
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#isSelected(ilog.views.gantt.IlvActivity)
*/
Override
public boolean isSelected(IlvActivity activity) {
return getSDMEngine().isSelected(activity);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#deselectAll()
*/
Override
public void deSelectAll() {
getSDMEngine().deselectAllObjects();
}
/**
* Selects or deselects the given activity.
* @param activity The activity.
* @param selected The desired selection status.
*/
protected void setSelectedImpl(IlvActivity activity, boolean selected) {
GanttToSDMAdapter model = (GanttToSDMAdapter) getSDMEngine().getModel();
if (model.isNode(activity) && getGanttModel().contains(activity)
&& !IlvGanttModelUtil.hasChildren(getGanttModel(), activity)) {
getSDMEngine().setSelected(activity, selected);
}
// select or deselect the activity
IlvGraphic activityGraphic = getSDMEngine().getGraphic(activity, false);
if (activityGraphic instanceof IlvSelectableSymbol) {
((IlvSelectableSymbol) activityGraphic).selectionStateChanged();
}
}
/**
* Selects or deselects the given constraint.
* @param constraint The constraint.
* @param selected The desired selection status.
*/
protected void setSelectedImpl(IlvConstraint constraint, boolean selected) {
GanttToSDMAdapter model = (GanttToSDMAdapter) getSDMEngine().getModel();
if (model.isLink(constraint) && getGanttModel().contains(constraint)) {
getSDMEngine().setSelected(constraint, selected);
}
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#select(ilog.views.gantt.IlvActivity, boolean)
*/
Override
public void setSelected(IlvActivity activity, boolean selected) {
setSelectedImpl(activity, selected);
}
/**
* Listens to selection changes.
* @param event The selection event.
* @proofread
*/
Override
public void selectionChanged(SDMEngineSelectionEvent event) {
if (!listenToSelection) {
return;
}
if (event.getDataObject() instanceof IlvActivity) {
IlvActivity activity = (IlvActivity) event.getDataObject();
boolean selected = event.getEngine().isSelected(activity);
// select or deselect the activity
IlvGraphic activityGraphic = event.getEngine()
.getGraphic(activity, false);
if (activityGraphic instanceof IlvSelectableSymbol) {
((IlvSelectableSymbol) activityGraphic).selectionStateChanged();
}
// forward the event only if this is an activity
fireSelectionEvent(event);
// get selected constraints
Enumeration<?> e = event.getEngine().getSelectedObjects();
Set<Object> selectedConstraints = new HashSet<Object>();
while (e.hasMoreElements()) {
Object obj = e.nextElement();
if (((GanttToSDMAdapter) getSDMEngine().getModel()).isLink(obj)) {
selectedConstraints.add(obj);
}
}
if (!selected) {
// we only deselect constraints that are not connected to a selected activity
Iterator<?> constraints = selectedConstraints.iterator();
while (constraints.hasNext()) {
IlvConstraint constraint = (IlvConstraint) constraints.next();
if ((constraint.getFromActivity().equals(activity) && !event
.getEngine().isSelected(constraint.getToActivity()))
|| (constraint.getToActivity().equals(activity) && !event
.getEngine().isSelected(constraint.getFromActivity()))) {
setSelectedImpl(constraint, false);
}
}
} else {
// if an activity has been selected, highlight or unhighlight related constraints
IlvGanttModel model = getGanttModel();
Iterator<?> constraints = model.constraintIteratorFromActivity(activity);
while (constraints.hasNext()) {
IlvConstraint constraint = (IlvConstraint) constraints.next();
setSelectedImpl(constraint, true);
}
constraints = model.constraintIteratorToActivity(activity);
while (constraints.hasNext()) {
IlvConstraint constraint = (IlvConstraint) constraints.next();
setSelectedImpl(constraint, true);
}
}
}
}
/**
* Fires a <code>SelectionEvent</code> event.
* @param event An SDM engine event that will be transformed into a Gantt selection event.
*/
private void fireSelectionEvent(SDMEngineSelectionEvent event) {
// Create a Gantt selection event.
SelectionEvent e = new SelectionEvent(event.getDataObject(), getSDMEngine()
.isSelected(event.getDataObject()));
eventService.publish(e);
for (Iterator<SelectionListener> i = selectionListeners.getListeners(); i.hasNext();) {
SelectionListener listener = i.next();
listener.selectionChanged(e);
}
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#addInteractorListener(ilog.views.InteractorListener)
*/
Override
public void addInteractorListener(InteractorListener listener) {
scrollView.getView().addInteractorListener(listener);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#addSelectionListener(ilog.views.gantt.event.SelectionListener)
*/
Override
public void addSelectionListener(SelectionListener listener) {
selectionListeners.addListener(listener);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#addTransformerListener(ilog.views.TransformerListener)
*/
Override
public void addTransformerListener(TransformerListener listener) {
scrollView.getView().addTransformerListener(listener);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#removeInteractorListener(ilog.views.event.InteractorListener)
*/
Override
public void removeInteractorListener(InteractorListener listener) {
scrollView.getView().removeInteractorListener(listener);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#removeSelectionListener(ilog.views.gantt.event.SelectionListener)
*/
Override
public void removeSelectionListener(SelectionListener listener) {
selectionListeners.removeListener(listener);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#removeTransformerListener(ilog.views.event.TransformerListener)
*/
Override
public void removeTransformerListener(TransformerListener listener) {
scrollView.getView().removeTransformerListener(listener);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#zoom(ilog.views.IlvPoint, double, double)
*/
Override
public void zoom(IlvPoint point, double sx, double sy) {
scrollView.getView().zoom(point, sx, sy, true);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#zoomToFit()
*/
Override
public void zoomToFit() {
scrollView.getView().fitTransformerToContent();
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#getInteractor()
*/
Override
public IlvManagerViewInteractor getInteractor() {
return scrollView.getView().getInteractor();
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#setInteractor(ilog.views.IlvManagerViewInteractor)
*/
Override
public void setInteractor(IlvManagerViewInteractor interactor) {
scrollView.getView().setInteractor(interactor);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#getTransformer()
*/
Override
public IlvTransformer getTransformer() {
return scrollView.getView().getTransformer();
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#setTransformer(ilog.views.IlvTransformer)
*/
Override
public void setTransformer(IlvTransformer t) {
scrollView.getView().setTransformer(t);
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#boundingBox()
*/
Override
public IlvRect boundingBox() {
return getSDMView().getSDMEngine().getGrapher().boundingBox();
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#boundingBox(IlvActivity)
*/
Override
public IlvRect boundingBox(IlvActivity activity) {
IlvGraphic graphic = getSDMEngine().getGraphic(activity, false);
if (graphic == null) {
return new IlvRect();
}
return graphic.boundingBox(((IlvManager) graphic.getGraphicBag())
.getTopLevelTransformer());
}
/* (non-Javadoc)
* @see ganttviewer.pert.PERTDiagram#visibleRect()
*/
Override
public IlvRect visibleRect() {
Rectangle visibleRect = getSDMView().visibleRect();
return new IlvRect(visibleRect.x, visibleRect.y, visibleRect.width,
visibleRect.height);
}
/**
* Overview panel.
*/
class Overview extends JPanel {
/**
* Cached image to render.
*/
private BufferedImage cachedImage = null;
/**
* Transparency.
*/
private AlphaComposite alpha;
/**
* The associated view.
*/
private IlvSDMView view;
/**
* Visible area color
*/
private final Color visibleAreaColor = new Color(100, 100, 255, 100);
/**
* Internal transformer.
*/
private IlvTransformer transformer;
/**
* Builds an <code>Overview</code>
* @param view The view from which we want to create an overview.
*/
public Overview(IlvSDMView view) {
this.view = view;
// alpha transparency management
alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f);
// internal transformer
transformer = new IlvTransformer();
// Transparent panel
setOpaque(false);
}
/**
* Recreates the cached image.
*/
protected void rebuildCachedImage() {
Rectangle bounds = getBounds();
// create a new background image
BufferedImage snapshot = new BufferedImage(bounds.width, bounds.height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = snapshot.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// source and destination rectangles
IlvRect srcRect = boundingBox();
IlvRect dstRect = new IlvRect(0, 0, bounds.width, bounds.height);
// fit to content
double sx = dstRect.width / srcRect.width;
double sy = dstRect.height / srcRect.height;
double scale = Math.min(sx, sy);
double x0 = -scale * srcRect.x
+ ((sx > sy) ? (dstRect.width - scale * srcRect.width) / 2 : 0);
double y0 = -scale * srcRect.y
+ ((sx <= sy) ? (dstRect.height - scale * srcRect.height) / 2 : 0);
transformer.setValues(scale, 0., 0., scale, x0, y0);
// draw
getSDMView().getSDMEngine().getGrapher().print(g2d, srcRect, view,
transformer, false);
g2d.dispose();
// create the image to be rendered
cachedImage = new BufferedImage(bounds.width, bounds.height,
BufferedImage.TYPE_INT_ARGB);
g2d = cachedImage.createGraphics();
g2d.setComposite(alpha);
g2d.setColor(Color.LIGHT_GRAY);
g2d.fillRect(0, 0, bounds.width, bounds.height);
g2d.drawImage(snapshot, 0, 0, bounds.width, bounds.height, null);
// dispose resources
g2d.dispose();
snapshot.flush();
}
/**
* Refreshes the overview.
*/
public void updateContent() {
Rectangle bounds = getBounds();
if ((bounds.width > 0) && (bounds.height > 0)) {
rebuildCachedImage();
}
}
/**
* Rendering.
* @param g Graphic context.
*/
Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Rectangle bounds = getBounds();
super.paint(g2d);
if (cachedImage == null) {
return;
}
// draw
g2d.drawImage(cachedImage, 0, 0, bounds.width, bounds.height, null);
// highlight visible area
displayVisibleArea(g2d);
}
/**
* Displays the visible area.
* @param g Graphic context.
*/
protected void displayVisibleArea(Graphics g) {
// apply transformer on visible area
IlvRect visibleRect = visibleRect();
// transformation
view.getTransformer().inverse(visibleRect);
transformer.applyFloor(visibleRect);
// draw the rectangle
g.setColor(visibleAreaColor);
g.fillRect((int) visibleRect.x, (int) visibleRect.y,
(int) visibleRect.width + 1, (int) visibleRect.height + 1);
}
}
}