/**
* ScrollPane, bei welcher der Inhalt durch Anfassen mit der Maus verschoben werden kann.
*
*/
public class DraggableScrollPane extends JScrollPane {
/**
* Diese Komponente wird ueber den Viewport gelegt und faengt die Mausereignisse zunaechst ab.
* Koennen die Mausereignisse interpretiert werden als Anweisung, den Inhalt zu verschieben, wird
* selbiges getan. Ansonsten werden die Ereignisse weiter an den Inhalt gereicht.
*
*/
class MyGlassPane extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener {
final static int MOUSEPRESSDELAY = 100; // ein Mausdruck wird nach soviel Millisekunden an den Inhalt weitergereicht,
// sofern nichts anderes passiert
protected javax.swing.Timer MousePressTimer;
protected Point pressedPoint; // nach jedem mousePressed stehen hier die Koordinaten drin
protected Point pressedViewPos; // und hier, bei welcher Position der Inhalt stand.
protected boolean dragging = false; // ist true, wenn der Benutzer den Inhalt gerade verschiebt
public MyGlassPane() {
super();
addMouseListener(this);
addMouseMotionListener(this);
addMouseWheelListener(this);
setFocusable(false);
}
boolean MouseLocked = false;
@Override
/**
* Wenn MouseLocked true ist, dann wird hier false zurueckgegeben und so sichergestellt,
* dass die GlassPane bei der Zuordnung von MouseEvents nicht beruecksichtigt wird
*/
public boolean contains(int x, int y) {
return !MouseLocked && super.contains(x,y);
}
/**
* Uebergibt ein MouseWheelEvent an die JScrollPane.
*/
protected void delegateMouseEvent(MouseWheelEvent e) {
getParent().dispatchEvent(e);
}
/**
* Uebergibt ein MouseEvent an darunterliegende Komponenten
*/
protected void delegateMouseEvent(MouseEvent e) {
Container container = getParent();
Point containerPoint = SwingUtilities.convertPoint(this, e.getPoint(), container);
MouseLocked = true; // damit nicht die GlassPane gefunden wird
Component C = container.findComponentAt(containerPoint.x, containerPoint.y); // jetzt die Komponente suchen, die den Punkt beinhaltet
MouseLocked = false; // jetzt wieder anschalten
if (C != null && C != this) { // C != this zur Sicherheit
Point destP = SwingUtilities.convertPoint(this, containerPoint, C); // Punkt konvertieren in Koordinaten der Komponente
MouseEvent newe = new MouseEvent(C, e.getID(), e.getWhen(), e.getModifiers()+e.getModifiersEx(),
destP.x, destP.y, e.getClickCount(), e.isPopupTrigger(), e.getButton()); // neues MouseEvent erzeugen
C.dispatchEvent(newe); // und der Komponente uebergeben
}
}
public void startTimer(final MouseEvent EventToDelegate) {
MousePressTimer = new Timer(MOUSEPRESSDELAY, new ActionListener() {
public void actionPerformed(ActionEvent e) {
delegateMouseEvent(EventToDelegate);
}
});
MousePressTimer.setRepeats(false);
MousePressTimer.start();
}
public void stopTimer() {
if (MousePressTimer != null && MousePressTimer.isRunning()) {
MousePressTimer.stop();
}
}
public boolean isTimerRunning() {
return MousePressTimer != null && MousePressTimer.isRunning();
}
public void scrollTo(Point p) {
JViewport v = ((JScrollPane) getParent()).getViewport();
Dimension ext = v.getExtentSize();
Dimension D = v.getView().getSize();
if (p.x < 0) p.x = 0;
if (p.y < 0) p.y = 0;
if (p.x > D.width-ext.width) p.x = D.width-ext.width;
if (p.y > D.height-ext.height) p.y = D.height-ext.height;
v.setViewPosition(p);
}
public Point getViewPos() {
return ((JScrollPane) getParent()).getViewport().getViewPosition();
}
public void mouseClicked(MouseEvent e) {
delegateMouseEvent(e);
}
public void mouseEntered(MouseEvent e) {
delegateMouseEvent(e);
}
public void mouseExited(MouseEvent e) {
delegateMouseEvent(e);
}
public void mousePressed(MouseEvent e) {
pressedPoint = e.getPoint();
pressedViewPos = getViewPos();
startTimer(e);
}
public void mouseReleased(MouseEvent e) {
if (dragging) { // lief gerade ein Dragging-Vorgang
dragging = false; // dann beenden
} else {
delegateMouseEvent(e); // ansonsten das Event weiterreichen
}
}
public void mouseDragged(MouseEvent e) {
if (isTimerRunning()) { // ist die Zeit noch nicht abgelaufen
stopTimer(); // dann Timer abbrechen
dragging = true; // und denn Dragging-Vorgang starten
}
if (dragging) { // wenn Dragging-Vorgang laeuft
Point sp = new Point(pressedPoint.x-e.getX()+pressedViewPos.x,pressedPoint.y-e.getY()+pressedViewPos.y);
scrollTo(sp); // an entsprechende Stelle scrollen
} else {
delegateMouseEvent(e); // ansonsten Event weiterreichen
}
}
public void mouseMoved(MouseEvent e) {
delegateMouseEvent(e);
}
public void mouseWheelMoved(MouseWheelEvent e) {
delegateMouseEvent(e);
}
}
/**
* Dieses Layout erweitert das normale ScrollPaneLayout und sorgt dafuer,
* dass meine GlassPane ueber den Viewport gelegt wird.
*/
class MyScrollPaneLayout extends ScrollPaneLayout {
MyGlassPane GP;
final static String GLASSPANE = "GlassPane";
@Override
public void addLayoutComponent(String s, Component c) {
if (s.equals(GLASSPANE)) {
GP = (MyGlassPane) addSingletonComponent(GP, c);
} else {
super.addLayoutComponent(s, c);
}
}
@Override
public void layoutContainer(Container parent) {
super.layoutContainer(parent);
JViewport v = getViewport();
if (v != null && GP != null) {
GP.setBounds(v.getBounds()); // passgenaue Position und Groesse setzen
parent.setComponentZOrder(GP, 0); // und sicherstellen, dass die GlassPane obendrauf liegt
}
}
}
/********** DraggableScrollPane-Konstruktoren ********************/
public DraggableScrollPane() {
super();
addmythings();
}
public DraggableScrollPane(Component view) {
super(view);
addmythings();
}
public DraggableScrollPane(int vsbPolicy, int hsbPolicy) {
super(vsbPolicy, hsbPolicy);
addmythings();
}
public DraggableScrollPane(Component view, int vsbPolicy, int hsbPolicy) {
super(view, vsbPolicy, hsbPolicy);
addmythings();
}
/**
* Setzt mein eigenes Layout ein und fuegt meine GlassPane hinzu
*/
protected void addmythings() {
setLayout(new MyScrollPaneLayout());
add(new MyGlassPane(), MyScrollPaneLayout.GLASSPANE);
doLayout();
}
}