/*
* funCKit - functional Circuit Kit
* Copyright (C) 2013 Lukas Elsner <open@mindrunner.de>
* Copyright (C) 2013 Peter Dahlberg <catdog2@tuxzone.org>
* Copyright (C) 2013 Julian Stier <mail@julian-stier.de>
* Copyright (C) 2013 Sebastian Vetter <mail@b4sti.eu>
* Copyright (C) 2013 Thomas Poxrucker <poxrucker_t@web.de>
* Copyright (C) 2013 Alexander Treml <alex.treml@directbox.com>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package de.sep2011.funckit.view;
import de.sep2011.funckit.controller.listener.editpanel.EditPanelScrollPaneHorizontalScrollbarBoundedRangeModelChangeListener;
import de.sep2011.funckit.controller.listener.editpanel.EditPanelScrollPaneVerticalScrollbarBoundedRangeModelChangeListener;
import de.sep2011.funckit.model.graphmodel.Circuit;
import de.sep2011.funckit.model.sessionmodel.EditPanelModel;
import de.sep2011.funckit.observer.EditPanelModelInfo;
import de.sep2011.funckit.observer.EditPanelModelObserver;
import de.sep2011.funckit.observer.GraphModelInfo;
import de.sep2011.funckit.observer.GraphModelObserver;
import de.sep2011.funckit.util.Log;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import net.miginfocom.swing.MigLayout;
import java.awt.Adjustable;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
/**
* A Scrollpane for a {@link EditPanel}. It implements ComponentListener and
* {@link EditPanelModelObserver} to adjust the scrollbars appropriately when
* the {@link EditPanel} or the associated {@link EditPanelModel} change.
*/
public class EditPanelScrollPane extends JPanel implements ComponentListener,
EditPanelModelObserver, GraphModelObserver {
/**
*
*/
private static final long serialVersionUID = 3881046211805115478L;
private JScrollBar southScrollBar;
private JScrollBar eastScrollBar;
private EditPanel editPanel;
private static final int SCROLLBAR_UNIT_INCREMENT = 14;
private boolean scrollbarsAdjustingNotFromUser = false;
/**
* Caches the Bounding rect of the circuit. Set it to null if it changed
*/
private Rectangle graphRectangleCache;
/**
* Specifies additional pixels the scrollbars should scroll in every
* direction (additional to the Circuit bounding rect).
*/
private static final int ADDITIONAL_SCROLLBAR_SPACE = 15;
/**
* Create a new {@link EditPanelScrollPane} with no {@link EditPanel} set.
*
* @param view
* the associated {@link View} object, should not be null
*/
public EditPanelScrollPane(View view) {
super();
initialize(null, view);
}
@Override
@SuppressWarnings("deprecation")
public void reshape(int x, int y, int width, int height) {
super.reshape(x, y, width, height);
adjustScrollbarsRange();
}
/**
* Create a new {@link EditPanelScrollPane}.
*
* @param panel
* The {@link EditPanel} which will be in the center of this
* ScrollPane
* @param view
* the associated {@link View} object, should not be null
*/
public EditPanelScrollPane(View view, EditPanel panel) {
super();
initialize(panel, view);
}
/**
* Create a new {@link EditPanelScrollPane}.
*
* @param panel
* The {@link EditPanel} which will be in the center of this
* ScrollPane, can be null
* @param isDoubleBuffered
* see {@link JPanel#JPanel(boolean)}
* @param view
* the associated {@link View} object, should not be null
*/
public EditPanelScrollPane(View view, EditPanel panel, boolean isDoubleBuffered) {
super(isDoubleBuffered);
initialize(panel, view);
}
private void initialize(EditPanel panel, View view) {
southScrollBar = new JScrollBar(Adjustable.HORIZONTAL);
southScrollBar.setUnitIncrement(SCROLLBAR_UNIT_INCREMENT);
southScrollBar.getModel().addChangeListener(
new EditPanelScrollPaneHorizontalScrollbarBoundedRangeModelChangeListener(view,
view.getController(), this));
eastScrollBar = new JScrollBar(Adjustable.VERTICAL);
eastScrollBar.setUnitIncrement(SCROLLBAR_UNIT_INCREMENT);
eastScrollBar.getModel().addChangeListener(
new EditPanelScrollPaneVerticalScrollbarBoundedRangeModelChangeListener(view, view
.getController(), this));
this.setLayout(new MigLayout("fill, insets 0"));
add(southScrollBar, "dock south, growx");
add(eastScrollBar, "dock east, growy");
if (panel != null) {
setEditPanel(panel);
}
}
/**
* Set a {@link EditPanel} to be in the center of this
* {@link EditPanelScrollPane}. This Scrollpane Automatically registers
* itself as observer of the {@link EditPanel} and the
* {@link EditPanelModel} and unregisters from the old.
*
* @param c
* {@link EditPanel} to manage, null to unset the current set one
*/
public void setEditPanel(EditPanel c) {
if (editPanel != null) {
editPanel.removeComponentListener(this);
editPanel.getPanelModel().deleteObserver(this);
editPanel.getPanelModel().getCircuit().deleteObserver(this);
remove(editPanel);
}
if (c != null) {
editPanel = c;
editPanel.addComponentListener(this);
editPanel.getPanelModel().addObserver(this);
editPanel.getPanelModel().getCircuit().addObserver(this);
add(c, "dock center, grow");
graphRectangleCache = null; // clear cache
adjustScrollbarsRange();
}
}
/**
* Get the set {@link EditPanel}.
*
* @return the {@link EditPanel} or null if unset
*/
public EditPanel getEditPanel() {
return editPanel;
}
@Override
public void editPanelModelChanged(EditPanelModel source, EditPanelModelInfo i) {
if (i.isTransformChanged()) {
adjustScrollbarsRange();
}
}
@Override
public void componentHidden(ComponentEvent e) {
// no interest
}
@Override
public void componentMoved(ComponentEvent e) {
// no interest
}
@Override
public void componentResized(ComponentEvent e) {
adjustScrollbarsRange();
}
@Override
public void componentShown(ComponentEvent e) {
adjustScrollbarsRange();
}
private void adjustScrollbarsRange() {
scrollbarsAdjustingNotFromUser = true;
AffineTransform tr = editPanel.getPanelModel().getTransformation();
if (graphRectangleCache == null) {
graphRectangleCache = editPanel.getPanelModel().getCircuit().getBoundingRectangle();
}
Point viewPos1 = new Point();
Point viewPos2 = new Point(editPanel.getSize().width, editPanel.getSize().height);
try {
tr.inverseTransform(viewPos1, viewPos1);
tr.inverseTransform(viewPos2, viewPos2);
} catch (NoninvertibleTransformException e1) {
e1.printStackTrace();
Log.gl().error(e1);
}
Rectangle viewRect = new Rectangle(viewPos1.x, viewPos1.y, viewPos2.x - viewPos1.x,
viewPos2.y - viewPos1.y);
Rectangle boundingRect = graphRectangleCache.createUnion(viewRect).getBounds();
boundingRect.grow(ADDITIONAL_SCROLLBAR_SPACE, ADDITIONAL_SCROLLBAR_SPACE);
double southMin = boundingRect.x;
double southMax = boundingRect.x + boundingRect.width;
double eastMin = boundingRect.y;
double eastMax = boundingRect.y + boundingRect.height;
int southValue = viewPos1.x;
int eastValue = viewPos1.y;
southScrollBar.getModel().setRangeProperties(southValue, viewRect.width, (int) southMin,
(int) southMax, southScrollBar.getModel().getValueIsAdjusting());
eastScrollBar.getModel().setRangeProperties(eastValue, viewRect.height, (int) eastMin,
(int) eastMax, eastScrollBar.getModel().getValueIsAdjusting());
scrollbarsAdjustingNotFromUser = false;
}
@Override
public void graphModelChanged(Circuit source, GraphModelInfo i) {
graphRectangleCache = null; // clear cache
adjustScrollbarsRange();
}
/**
* Return true if the Scrollbars are adjusted internally. (observers,...)
*
* @return true if the Scrollbars are adjusted internally
* @since implementation
*/
public boolean isScrollbarsAdjustingNotFromUser() {
return scrollbarsAdjustingNotFromUser;
}
}