import java.awt.*;
import java.awt.event.*;
/** Because ScrollPane is a PIG on large areas. So, I decided to
* write my own. Strangely enough, though... it seems to still have a slight
* delay at times. Hmmm. Probably to do with me having to use
* setLocation(-offset,-offset). Ah well.
*
* Similarities to ScrollPane:
* Holds a single component
* Has horizontal and vertical scrollbars
*
* Differences:
* Scrollbars are ALWAYS visible
* Has only the subset of functionality that I need RIGHT NOW.
* We dont have an implicit "setSize(100,100)", unless you make us
* visible without a component.
* Otherwise, we ask for the entire size of the component
*
* MANDATORY NOTES:
* * Held component must always pay attention to the clip region of * the Graphics object passed into its paint(Graphics) function. Otherwise, * performance will suck. *
* Held component should NOT calculate anything based on its actual visible * size, but instead on what it returns by its own getPreferredSize() *
* It must IMPLEMENT getPreferredSize() !!! *
* If the value of heldcomponent.getPreferredSize() changes, I think the * "proper" AWT thing you are supposed to do, is call FscrollPaneObj.doLayout() * Or, you can simply call heldcomponent.setSize(). *
* BUGS:
* There is a "bug" in that the scrollbars are "on top" of the contained * object, and an extra piece is visible in the bottom-right. * Right now, this class is not a high priority for me. But it could * probably be fixed up by having the contained component be * in a sub-panel, instead of at the same level as the scrollbars. * That would use up an extra "heavyweight" component, though, for those * people afflicted with an M$-"win"XX operating system. *
* @author Phil Brown, "phil@bolthole.com" * @version 1.0 * * Copyright Phil Brown, 1999 * Free for any use as long as credit to author is given, and mention * that you got the source from www.bolthole.com * I did not look at Sun source code at all to write this class. */ public class FscrollPane extends Container implements AdjustmentListener, ComponentListener { Scrollbar hbar, vbar; Component contained; int containedw,containedh; // cached FULL size of contained obj int viewwidth, viewheight; int yoffset=0, xoffset=0; boolean doClear=false; public FscrollPane(){ contained=null; viewwidth=90; viewheight=90; hbar=new Scrollbar(Scrollbar.HORIZONTAL); vbar=new Scrollbar(Scrollbar.VERTICAL); super.add(hbar); super.add(vbar); setLayout(null); hbar.addAdjustmentListener(this); vbar.addAdjustmentListener(this); } /** overrides Container.doLayout. * Sets position and size of contained components. * Except we dont set the size of 'contained', just position. */ public void doLayout(){ //debug("Fscroll doLayout()"); Dimension mysize=getSize(); if(mysize.width==0) mysize.width=100; if(mysize.height==0) mysize.height=100; int swidth,sheight; swidth=vbar.getPreferredSize().width; sheight=hbar.getPreferredSize().height; viewwidth=mysize.width-swidth; viewheight=mysize.height-sheight; vbar.setBounds(viewwidth,0,swidth,viewheight); hbar.setBounds(0,viewheight,viewwidth,sheight); contained.setLocation(-xoffset,-yoffset); } /** * It seems doing an explicit clearRect after scrolling is not * neccesary. But in case you want an automatic clearscreen * for some reason (bugs), here's a way for you to * set that behaviour */ public void setClear(boolean cv){doClear = cv;} /** When the size of the held component changes, this * sets the size of the scroll thumbs appropriately. * This does nothing outside of adjusting scrollbar appearance. *
* This should be called whenever preferredSize() changes. *
* We assume viewwidth & viewheight have been set properly. */ void adjustScrollsize(){ //debug("FscrollPane.adjustScrollsize called"); int thsize,thpos; if(contained==null) return; Dimension dim=contained.getPreferredSize(); int neww=dim.width,newh=dim.height; //debug(" Looking at values..."); //debug(" xoff, yoff: "+xoffset+","+yoffset); //debug(" contained size: "+dim); //debug(" My viewable size: "+viewwidth+","+viewheight); if(neww != containedw){ thsize=(viewwidth /neww) * 100; thpos=(xoffset/ neww) * 100; hbar.setValues(thpos, thsize, 0, 100); containedw=dim.width; } if(newh!=containedh){ thsize=(viewheight /newh) * 100; thpos=(yoffset / neww) * 100; vbar.setValues(thpos, thsize, 0, 100); containedh=dim.height; } doLayout(); } /** * This is a standard Event callback, for when * the user adjusts a scrollbar. * We adjust the position of the component, and * call contained.paint(); *
* Unfortunatley, we redraw the ENTIRE visible area, for now. *
* MODIFIED: xoffset,yoffset */ public void adjustmentValueChanged(AdjustmentEvent evt){ int val=evt.getValue(); Dimension mydim=getSize(); int newxoff,newyoff; if(evt.getSource().equals(vbar)){ newyoff=(val*containedh)/ 100; if(newyoff==yoffset) return; yoffset=newyoff; } else { newxoff=(val*containedw)/ 100; if(newxoff==xoffset) return; xoffset=newxoff; } //debug("Fscroll: changed location of contained obj"); contained.setLocation(-xoffset,-yoffset); //debug(" to " + contained.getLocation()); Graphics gc = contained.getGraphics(); if(doClear) gc.clearRect(xoffset, yoffset,viewwidth,viewheight); // This doesn't work?!! gc.setClip(xoffset, yoffset,viewwidth,viewheight); //contained.paint(gc); } /** This may or may not get called, depending on what kind * of component you have contained. * Bug in AWT? */ public void componentResized(ComponentEvent evt){ adjustScrollsize(); } public void componentMoved(ComponentEvent evt){} public void componentHidden(ComponentEvent evt){} public void componentShown(ComponentEvent evt){} // WARNING: Race condition here /** Here is where we allow adding of a Component to be scrolled. * this overwrites any prior contained Component */ public Component add(Component obj){ if(contained!=null){ remove(contained); contained=null; } super.add(obj); contained=obj; xoffset=0; yoffset=0; Dimension csize=contained.getPreferredSize(); contained.setSize(csize); // redundant? doLayout(); adjustScrollsize(); return obj; } void debug(String msg){ System.out.println(msg); } public void setSize(int width, int height){ super.setSize(width,height); viewwidth=width; viewheight=height; //debug("done FscrollPage.setSize("+width+","+height+")"); } public Dimension getPreferredSize(){ if(contained==null){ //debug("NUILL IN PREF"); return new Dimension(100,100); } Dimension dim = new Dimension(); if(viewwidth!=-1) dim.width=viewwidth; else dim.width=containedw; if(viewheight!=-1) dim.height=viewheight; else dim.height=containedh; return dim; } public Dimension getMinimumSize(){ return getPreferredSize(); } public Dimension getMaximumSize(){ return getPreferredSize(); } }