Pan chart using mouse - Jfreechart - java

Can we implement the pan functionality as a mouse drag event in JfreeChart? Right now I press CTRL and drag my mouse to pan a chart. I want to implement the pan functionality just by dragging the mouse. Is that possible ?

It is apparently impossible to change the modifier key with the current JFreeChart API, as discussed here (but it is in the pipeline).
However, everything is there to pan a chart programmatically, so you could try the following:
Add a MouseMotionListener to your ChartPanel to track mouseDragged() events.
From these events, compute the requested movement of the chart.
Call directly XYPlot.panDomainAxes() and XYPlot.panRangeAxis() (API here).
Take inspiration from ChartPanel source code:
/**
* Temporary storage for the width and height of the chart
* drawing area during panning.
*/
private double panW, panH;
/** The last mouse position during panning. */
private Point panLast;
/**
* The mask for mouse events to trigger panning.
*
* #since 1.0.13
*/
private int panMask = InputEvent.CTRL_MASK;
...
/**
* Handles a 'mouse pressed' event.
* <P>
* This event is the popup trigger on Unix/Linux. For Windows, the popup
* trigger is the 'mouse released' event.
*
* #param e The mouse event.
*/
#Override
public void mousePressed(MouseEvent e) {
if (this.chart == null) {
return;
}
Plot plot = this.chart.getPlot();
int mods = e.getModifiers();
if ((mods & this.panMask) == this.panMask) {
// can we pan this plot?
if (plot instanceof Pannable) {
Pannable pannable = (Pannable) plot;
if (pannable.isDomainPannable() || pannable.isRangePannable()) {
Rectangle2D screenDataArea = getScreenDataArea(e.getX(),
e.getY());
if (screenDataArea != null && screenDataArea.contains(
e.getPoint())) {
this.panW = screenDataArea.getWidth();
this.panH = screenDataArea.getHeight();
this.panLast = e.getPoint();
setCursor(Cursor.getPredefinedCursor(
Cursor.MOVE_CURSOR));
}
}
// the actual panning occurs later in the mouseDragged()
// method
}
}
else if (this.zoomRectangle == null) {
...
}
}
...
/**
* Handles a 'mouse dragged' event.
*
* #param e the mouse event.
*/
#Override
public void mouseDragged(MouseEvent e) {
// if the popup menu has already been triggered, then ignore dragging...
if (this.popup != null && this.popup.isShowing()) {
return;
}
// handle panning if we have a start point
if (this.panLast != null) {
double dx = e.getX() - this.panLast.getX();
double dy = e.getY() - this.panLast.getY();
if (dx == 0.0 && dy == 0.0) {
return;
}
double wPercent = -dx / this.panW;
double hPercent = dy / this.panH;
boolean old = this.chart.getPlot().isNotify();
this.chart.getPlot().setNotify(false);
Pannable p = (Pannable) this.chart.getPlot();
if (p.getOrientation() == PlotOrientation.VERTICAL) {
p.panDomainAxes(wPercent, this.info.getPlotInfo(),
this.panLast);
p.panRangeAxes(hPercent, this.info.getPlotInfo(),
this.panLast);
}
else {
p.panDomainAxes(hPercent, this.info.getPlotInfo(),
this.panLast);
p.panRangeAxes(wPercent, this.info.getPlotInfo(),
this.panLast);
}
this.panLast = e.getPoint();
this.chart.getPlot().setNotify(old);
return;
}
...
}
...
/**
* Handles a 'mouse released' event. On Windows, we need to check if this
* is a popup trigger, but only if we haven't already been tracking a zoom
* rectangle.
*
* #param e information about the event.
*/
#Override
public void mouseReleased(MouseEvent e) {
// if we've been panning, we need to reset now that the mouse is
// released...
if (this.panLast != null) {
this.panLast = null;
setCursor(Cursor.getDefaultCursor());
}
...
}
EDIT: Noticing that the only problem with the current API is that panMask is private, why don't you try to hack the field with reflection:
Field mask = ChartPanel.class.getDeclaredField("panMask");
mask.setAccessible(true);
mask.set(yourChartPanel, Integer.valueOf(0)); // The "0" mask is equivalent to no mask. You could also set a different modifier.

Solution still not provided with new version so i put what works flawlessly for me, it also auto zoom range axis when you scroll:
ChartPanel panel = new ChartPanel(chart);
panel.setMouseZoomable(false);
panel.setMouseWheelEnabled(true);
panel.setDomainZoomable(true);
panel.setRangeZoomable(false);
panel.setPreferredSize(new Dimension(1680, 1100));
panel.setZoomTriggerDistance(Integer.MAX_VALUE);
panel.setFillZoomRectangle(false);
panel.setZoomOutlinePaint(new Color(0f, 0f, 0f, 0f));
panel.setZoomAroundAnchor(true);
try {
Field mask = ChartPanel.class.getDeclaredField("panMask");
mask.setAccessible(true);
mask.set(panel, 0);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
panel.addMouseWheelListener(arg0 -> panel.restoreAutoRangeBounds());
Now JFreeChart behaves like any other professional charting software.

Here is my solution.
Regards
TA
ChartPanel cp = new ChartPanel(chart)
{
/**
* A hack to change the zoom and panning function.
* With this override we can pan around by dragging.
* If SHIFT is pressed we get the zoom rectangle.
* #param e
*/
#Override
public void mousePressed(MouseEvent e)
{
int mods = e.getModifiers();
int panMask = MouseEvent.BUTTON1_MASK;
if (mods == MouseEvent.BUTTON1_MASK+MouseEvent.SHIFT_MASK)
{
panMask = 255; //The pan test will match nothing and the zoom rectangle will be activated.
}
try
{
Field mask = ChartPanel.class.getDeclaredField("panMask");
mask.setAccessible(true);
mask.set(this, panMask);
}
catch (Exception ex)
{
ex.printStackTrace();
}
super.mousePressed(e);
}
};

Related

How to find and write the final coordinates(x,y) of the all the components created in to a text file by clicking a write button

This program is about creating components and moving them randomly in JApplet and removing the components if required. Every mouse click creates a new component. After creating component we can click on it and drag it where ever we want. Double clicking on the component removes it. I was successful in creating, moving and removing components.
For example i created three components on the JApplet by clicking three times randomly at different places on the JApplet.Please click here to view created components I moved them randomly around the screen. Please click here to view JApplet after randomly moving them around screen My goal is to write the final coordinates(x,y) of their position on the screen for all components created in to a text file by clicking a write button. I have no idea about mouseReleased method. Please help me with this. Thank you. Your help is very much appreciated.
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.awt.geom.*;
import javax.swing.*;
This is my main class
public class MouseTest extends JApplet
{
public void init()
{
EventQueue.invokeLater(new Runnable() {
public void run()
{
MousePanel panel = new MousePanel();
add(panel);
}
});
}
public static void main(String[] args)
{
EventQueue.invokeLater(new mainThread());
}
}
class mainThread implements Runnable
{
public void run()
{
JPanel panel = new MousePanel();
JFrame frame = new JFrame("MouseTest");
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300,200);
frame.setVisible(true);
}
}
class MousePanel extends JPanel
{
public MousePanel()
{
squares = new ArrayList<Rectangle2D>();
current = null;
addMouseListener(new MouseHandler());
addMouseMotionListener(new MouseMotionHandler());
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// draw all squares
for (Rectangle2D r : squares)
g2.draw(r);
}
/**
* Finds the first square containing a point.
* #param p a point
* #return the first square that contains p
*/
public Rectangle2D find(Point2D p)
{
for (Rectangle2D r : squares)
{
if (r.contains(p)) return r;
}
return null;
}
/**
* Adds a square to the collection.
* #param p the center of the square
*/
public void add(Point2D p)
{
double x = p.getX();
double y = p.getY();
current = new Rectangle2D.Double(x - SIDELENGTH / 2, y - SIDELENGTH / 2, SIDELENGTH,
SIDELENGTH);
squares.add(current);
repaint();
}
/**
* Removes a square from the collection.
* #param s the square to remove
*/
public void remove(Rectangle2D s)
{
if (s == null) return;
if (s == current) current = null;
squares.remove(s);
repaint();
}
private static final int SIDELENGTH = 10;
private ArrayList<Rectangle2D> squares;
private Rectangle2D current;
// the square containing the mouse cursor
these are methods for defining and dragging and removing the object
private class MouseHandler extends MouseAdapter
{
public void mousePressed(MouseEvent event)
{
// add a new square if the cursor isn't inside a square
current = find(event.getPoint());
if (current == null) add(event.getPoint());
}
public void mouseClicked(MouseEvent event)
{
// remove the current square if double clicked
current = find(event.getPoint());
if (current != null && event.getClickCount() >= 2) remove(current);
}
}
private class MouseMotionHandler implements MouseMotionListener
{
public void mouseMoved(MouseEvent event)
{
// set the mouse cursor to cross hairs if it is inside
// a rectangle
if (find(event.getPoint()) == null) setCursor(Cursor.getDefaultCursor());
else setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
}
public void mouseDragged(MouseEvent event)
{
if (current != null)
{
int x = event.getX();
int y = event.getY();
// drag the current rectangle to center it at (x, y)
current.setFrame(x - SIDELENGTH / 2, y - SIDELENGTH / 2, SIDELENGTH, SIDELENGTH);
repaint();
}
}
}
}
Why do you keep changing the requirement???? See original question: How to find final coordinates(x & y) of the objects position in JApplet and write them to a text file
First you wanted to write out the location of the moved component after the drag was finished.
Then you wanted to write out the location of "all" components after the drag was finished.
Now you want to write out the location of all the components after a "button" is clicked.
I have no idea about mouseReleased method.
What does this have to do with anything? That was the suggestion for your requirement in your last question. Since your requirement has changed it is no longer needed.
My goal is to write the final coordinates(x,y) of their position on the screen for all components created in to a text file by clicking a write button.
Then you need to add an ActionListener to the button. You have already been given a link to the Swing tutorial on How to Write an ActionListener so I guess the question is what code should be included in the ActionListener?
In you last question you stated:
I know how to get the location of a component
I know how to write data to a file
So the only remaining problem is how to get all the components on the panel? To do this you can use the Container.getComponents() method on your panel containing all the dragged components. The basic code for your ActionListener would be something like:
for (Component component: panel.getComponents())
{
Point location = component.getLocation();
// write the location to the file
}
I'll let you add the code for opening/closing the file.

Swing events not passed to upwards to base component

I have a class called ComponentMover that allows you to drag around any object that's registered with it. This works if you pass it bare JPanels, but it doesn't work if the JPanel has anything inside of it, and it just stays put.
This is ComponentMover:
public class ComponentMover extends MouseAdapter {
private Insets dragInsets = new Insets(0, 0, 0, 0);
private Dimension snapSize = new Dimension(1, 1);
private Insets edgeInsets = new Insets(0, 0, 0, 0);
private boolean changeCursor = true;
private boolean autoLayout = false;
private Class<?> destinationClass;
private Component destinationComponent;
private Component destination;
private Component source;
private Point pressed;
private Point location;
private Cursor originalCursor;
private boolean autoscrolls;
private boolean potentialDrag;
private boolean shouldLock = true;
/**
* Constructor for moving individual components. The components must be
* regisetered using the registerComponent() method.
*/
public ComponentMover() {
}
/**
* Constructor to specify a Class of Component that will be moved when drag
* events are generated on a registered child component. The events will be
* passed to the first ancestor of this specified class.
*
* #param destinationClass
* the Class of the ancestor component
* #param component
* the Components to be registered for forwarding drag events to
* the ancestor Component.
*/
public ComponentMover(Class<?> destinationClass, JComponent... components) {
this.destinationClass = destinationClass;
registerComponent(components);
}
/**
* Constructor to specify a parent component that will be moved when drag
* events are generated on a registered child component.
*
* #param destinationComponent
* the component drage events should be forwareded to
* #param components
* the Components to be registered for forwarding drag events to
* the parent component to be moved
*/
public ComponentMover(JComponent destinationComponent,
JComponent... components) {
this.destinationComponent = destinationComponent;
registerComponent(components);
}
public void setLock(boolean shouldLock) {
this.shouldLock = shouldLock;
}
/**
* Get the auto layout property
*
* #return the auto layout property
*/
public boolean isAutoLayout() {
return autoLayout;
}
/**
* Set the auto layout property
*
* #param autoLayout
* when true layout will be invoked on the parent container
*/
public void setAutoLayout(boolean autoLayout) {
this.autoLayout = autoLayout;
}
/**
* Get the change cursor property
*
* #return the change cursor property
*/
public boolean isChangeCursor() {
return changeCursor;
}
/**
* Set the change cursor property
*
* #param changeCursor
* when true the cursor will be changed to the Cursor.MOVE_CURSOR
* while the mouse is pressed
*/
public void setChangeCursor(boolean changeCursor) {
this.changeCursor = changeCursor;
}
/**
* Get the drag insets
*
* #return the drag insets
*/
public Insets getDragInsets() {
return dragInsets;
}
/**
* Set the drag insets. The insets specify an area where mouseDragged events
* should be ignored and therefore the component will not be moved. This
* will prevent these events from being confused with a MouseMotionListener
* that supports component resizing.
*
* #param dragInsets
*/
public void setDragInsets(Insets dragInsets) {
this.dragInsets = dragInsets;
}
/**
* Get the bounds insets
*
* #return the bounds insets
*/
public Insets getEdgeInsets() {
return edgeInsets;
}
/**
* Set the edge insets. The insets specify how close to each edge of the
* parent component that the child component can be moved. Positive values
* means the component must be contained within the parent. Negative values
* means the component can be moved outside the parent.
*
* #param edgeInsets
*/
public void setEdgeInsets(Insets edgeInsets) {
this.edgeInsets = edgeInsets;
}
/**
* Remove listeners from the specified component
*
* #param component
* the component the listeners are removed from
*/
public void deregisterComponent(JComponent... components) {
for (JComponent component : components)
component.removeMouseListener(this);
}
/**
* Add the required listeners to the specified component
*
* #param component
* the component the listeners are added to
*/
public void registerComponent(JComponent... components) {
for (JComponent component : components){
component.addMouseListener(this);
}
}
/**
* Get the snap size
*
* #return the snap size
*/
public Dimension getSnapSize() {
return snapSize;
}
/**
* Set the snap size. Forces the component to be snapped to the closest grid
* position. Snapping will occur when the mouse is dragged half way.
*/
public void setSnapSize(Dimension snapSize) {
if (snapSize.width < 1 || snapSize.height < 1)
throw new IllegalArgumentException(
"Snap sizes must be greater than 0");
this.snapSize = snapSize;
}
/**
* Setup the variables used to control the moving of the component:
*
* source - the source component of the mouse event destination - the
* component that will ultimately be moved pressed - the Point where the
* mouse was pressed in the destination component coordinates.
*/
#Override
public void mousePressed(MouseEvent e) {
source = e.getComponent();
int width = source.getSize().width - dragInsets.left - dragInsets.right;
int height = source.getSize().height - dragInsets.top
- dragInsets.bottom;
Rectangle r = new Rectangle(dragInsets.left, dragInsets.top, width,
height);
if (r.contains(e.getPoint()))
setupForDragging(e);
}
private void setupForDragging(MouseEvent e) {
source.addMouseMotionListener(this);
potentialDrag = true;
// Determine the component that will ultimately be moved
if (destinationComponent != null) {
destination = destinationComponent;
} else if (destinationClass == null) {
destination = source;
} else // forward events to destination component
{
destination = SwingUtilities.getAncestorOfClass(destinationClass,
source);
}
pressed = e.getLocationOnScreen();
location = destination.getLocation();
if (changeCursor) {
originalCursor = source.getCursor();
source.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
}
// Making sure autoscrolls is false will allow for smoother dragging of
// individual components
if (destination instanceof JComponent) {
JComponent jc = (JComponent) destination;
autoscrolls = jc.getAutoscrolls();
jc.setAutoscrolls(false);
}
}
/**
* Move the component to its new location. The dragged Point must be in the
* destination coordinates.
*/
#Override
public void mouseDragged(MouseEvent e) {
Point dragged = e.getLocationOnScreen();
int dragX = getDragDistance(dragged.x, pressed.x, snapSize.width);
int dragY = getDragDistance(dragged.y, pressed.y, snapSize.height);
int locationX = location.x + dragX;
int locationY = location.y + dragY;
// Mouse dragged events are not generated for every pixel the mouse
// is moved. Adjust the location to make sure we are still on a
// snap value.
if (shouldLock) {
while (locationX < edgeInsets.left)
locationX += snapSize.width;
while (locationY < edgeInsets.top)
locationY += snapSize.height;
Dimension d = getBoundingSize(destination);
while (locationX + destination.getSize().width + edgeInsets.right > d.width)
locationX -= snapSize.width;
while (locationY + destination.getSize().height + edgeInsets.bottom > d.height)
locationY -= snapSize.height;
}
// Adjustments are finished, move the component
destination.setLocation(locationX, locationY);
}
/*
* Determine how far the mouse has moved from where dragging started (Assume
* drag direction is down and right for positive drag distance)
*/
private int getDragDistance(int larger, int smaller, int snapSize) {
int halfway = snapSize / 2;
int drag = larger - smaller;
drag += (drag < 0) ? -halfway : halfway;
drag = (drag / snapSize) * snapSize;
return drag;
}
/*
* Get the bounds of the parent of the dragged component.
*/
private Dimension getBoundingSize(Component source) {
if (source instanceof Window) {
GraphicsEnvironment env = GraphicsEnvironment
.getLocalGraphicsEnvironment();
Rectangle bounds = env.getMaximumWindowBounds();
return new Dimension(bounds.width, bounds.height);
} else {
return source.getParent().getSize();
}
}
/**
* Restore the original state of the Component
*/
#Override
public void mouseReleased(MouseEvent e) {
if (!potentialDrag)
return;
source.removeMouseMotionListener(this);
potentialDrag = false;
if (changeCursor)
source.setCursor(originalCursor);
if (destination instanceof JComponent) {
((JComponent) destination).setAutoscrolls(autoscrolls);
}
// Layout the components on the parent container
if (autoLayout) {
if (destination instanceof JComponent) {
((JComponent) destination).revalidate();
} else {
destination.validate();
}
}
}
}
What should I do with this code (or the other GUI's building code) to make me able to drag and drop components with children?
You have an adaptation of a ComponentMover by #camickr. The original and your version work fine with components that have children. Perhaps the problem is elsewhere. Consider posting an MCVE that illustrates the problem.
Here is a simple demo:
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestMove extends JPanel{
public TestMove() {
setLayout(null);
JPanel panel = new JPanel();
panel.add(new JLabel("label"));
panel.add(new JButton("button"));
panel.setBorder(BorderFactory.createLineBorder(Color.GREEN));
panel.setBounds(50, 50, 200, 50);
add(panel);
ComponentMover cm = new ComponentMover();
cm.registerComponent(panel);
}
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
TestMove panel = new TestMove();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
});
}
}

Flickering XYImageAnnotation on JFreeChart with timer

I have difficulties drawing an image on a JFreeChart - XYLineChart. The main problem is the x and y coordinates of the annotation is updated dynamically in real time.So with my code adding the annotation and clearing it for the new one to be drawn causes flickering which is annoying for the user.
I have checked some examples of flickering problems on JAVA using update() , paint () or repaint() methods using graphics but seems not implementable on a JFreeChart.
Do you have any ideas how to get rid of the flicker or a workaround to use one bufferedImage on the JFreeChart instead of an annotation ?
To be more specific here is the drawn line and the image :
Screenshot
So this cross hair (as the buffered image) should go on the plot line up and down with the updated values of x and y axis.But this motion causes the flickering unfortunately.
Here is the part of my code where I draw the image - I cannot provide SSCCE I guess since there are more than 15 classes and 5k of written code :
// After a button clicked on panel
SomeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
// The chart and XYPlot is already drawn at this point
// Reading the image
try {
myPicture = ImageIO
.read(new File("\\\\Users2\\blabla\\Data\\MyPictures\\x.png"));
} catch (IOException e) {
e.printStackTrace();
}
// Setting up a timer
timer2 = new java.util.Timer();
Object source = event.getSource();
if (source == SomeButton) {
// Setting up a task
task2 = new java.util.TimerTask() {
#Override
public void run() {
double x1;
double y1;
try {
// Getting different x and y values from a microcontroller instantaneously
if (microContConnected()) {
x1 = microCont.getX();
y1 = microCont.getY();
// creating the annotation
XYImageAnnotation ImageAnn = new XYImageAnnotation(x1, y1, myPicture);
// Here is the drawing and clearing made !
plot.addAnnotation(ImageAnn);
pause(50);
plot.clearAnnotations();
}
} catch (SerialPortException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
timer2.scheduleAtFixedRate(task2, 50, 50);
}
}
});
It seems I found a solution myself ; instead of adding the image to plot I use the renderer and there is no pause function between adding and removing the picture with new coordinates.. also sequence of adding and removed are reversed. Surprising for me to work this way I must say. There is no flickering left; it's as smooth as a clipped graphics or double buffered. :) Here is the new code :
// renderer
final XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
// Reading the image
try {
myPicture = ImageIO.read(new File("\\\\Users2\\blabla\\Data\\MyPictures\\x.png"));
} catch (IOException e) {
e.printStackTrace();
}
// Setting up a timer
timer2 = new java.util.Timer();
Object source = event.getSource();
if (source == someButton) {
task2 = new java.util.TimerTask() {
#Override
public void run() {
if (check == true) {
if (microContConnected()) {
x1 = microCont.getX();
y1 = microCont.getY();
renderer.removeAnnotations();
XYImageAnnotation img2 = new XYImageAnnotation(
x1, y1, myPicture);
renderer.addAnnotation(img2,
Layer.FOREGROUND);
}
}
}
};
timer2.scheduleAtFixedRate(task2, 50, 50);
}

i have created the wizard, i want it to position always at the center, can any one help me?

private Point getFirstMonitorSize() { // Here Point is org.eclipse.swt.graphics.Point
Display display = Display.getDefault();
if (display != null) {
Monitor[] monitors = display.getMonitors();
if (monitors.length == 1) {
Rectangle clientArea = monitors[0].getClientArea();
return new Point(clientArea.width / 2, clientArea.height / 2);
}
}
return null;
}
I found out this for positioning but i don't know how to use in wizard dialog?
For Eclipse E4, you can add this method in your E4LifeCycle class.
This will make your application centre of primary monitor.
This will make your application shell location in centre of screen, this if you open the WizardDialog where you set the proper parent shell then every thing will fall into place
#Inject
#Optional
public void receiveActiveShell(#Named(IServiceConstants.ACTIVE_SHELL) final Shell shell) {
try {
if (shell != null) {
final Display display = shell.getDisplay();
final Monitor primary = display.getPrimaryMonitor();
final Rectangle displayBounds = primary.getBounds();
shell.setSize(displayBounds.width - 100, displayBounds.height - 100);
final Point size = shell.getSize();
Point defaultLocation = new Point((int) (displayBounds.width - size.x) / 2, (int) (displayBounds.height - size.y) / 2);
shell.setLocation(this.defaultLocation);
}
} catch (Exception e) {
LOGGER.error("Execption ocuured in Active Shell", e); //$NON-NLS-1$
}
}
You can position your wizard dialog by calling wizardDialog.getShell().setLocation(..).

Java Translucent Selection Box

For custom rendering, I've created a class that extends JPanel and overrides the paintComponent method. In the custom paintComponent I rendering multiple shape objects held in a array. What I would like to add is the ability to drag and select 1 or more of the shapes. While dragging I would like to show a translucent rectangle defining the selection region akin to what is seen in Windows Explorer. Can any provide a starting point for accomplishing this?
Thanks.
I saw an interesting way of doing this in JFreeChart's source code. You can draw a marquee over a section of the chart, and when you release the mouse the chart zooms in on the selected are. Re-rending the chart is expensive and unfortunately JFreeChart doesn't support partial paints of a chart. So to draw the marquee they do some sort of bitwise operation to the colors of the component, in a reversible fashion. Every time the mouse moves while selecting a marquee, you reverse the previous bitwise operation on the old coordinates, then redo it on the new coordinates.
Take a look at ChartPanel.java in JFreeChart
private void drawZoomRectangle(Graphics2D g2) {
// Set XOR mode to draw the zoom rectangle
g2.setXORMode(Color.gray);
if (this.zoomRectangle != null) {
if (this.fillZoomRectangle) {
g2.fill(this.zoomRectangle);
}
else {
g2.draw(this.zoomRectangle);
}
}
// Reset to the default 'overwrite' mode
g2.setPaintMode();
}
You could use JXLayer. Span it across the whole form and do the painting in a custom LayerUI.
All,
Thanks for the suggestions. Ended up resolving this by adapting some the code used in this rather clever demo. http://forums.sun.com/thread.jspa?threadID=5299064&start=19
public class ExamplePanel extends JPanel
{
Rectangle2D.Double selectionRect;
Point mouseDown, mouseHere;
...
protected void paintComponent(Graphics g)
{
AlphaComposite ta = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f);
g2d.setComposite(ta);
g2d.setColor(Color.BLUE);
g2d.fill(selectionRect);
g2d.setComposite(AlphaComposite.SrcOver);
g2d.setColor(Color.BLACK);
g2d.draw(selectionRect);
}
}
public class ExammpleMouseListener extends MouseAdapter
{
#Override
public void mousePressed(MouseEvent e)
{
super.mousePressed(e);
// store the mouse down location
pnl.mouseDown = e.getPoint();
}
/**
* #see java.awt.event.MouseAdapter#mouseDragged(java.awt.event.MouseEvent)
*/
#Override
public void mouseDragged(MouseEvent e)
{
super.mouseDragged(e);
// check for left mouse button
if ((e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) == 0)
{
return;
}
// store the current location
pnl.mouseHere = e.getPoint();
// calculate the size of the selection rectangle
double downX = pnl.mouseDown.getX();
double downY = pnl.mouseDown.getY();
double hereX = pnl.mouseHere.getX();
double hereY = pnl.mouseHere.getY();
double l = Math.min(downX, hereX);
double t = Math.min(downY, hereY);
double w = Math.abs(downX - hereX);
double h = Math.abs(downY - hereY);
pnl.selectionRect = new Rectangle2D.Double(l, t, w, h);
// queue a repaint of the panel
pnl.repaint();
}
#Override
public void mouseReleased(MouseEvent e)
{
super.mouseReleased(e);
// clear the selection rectangle
pnl.selectionRect = null;
// queue a repaint of the panel
pnl.repaint();
}
}
}
Sure, here's an simple example with methods for creating and moving the shape.
class MyShape implements Shape {
private Shape shape;
public void createShape(Point p1, Point p2, ShapeType t) {
switch(t) {
case RECTANGLE: {
shape = new Rectangle2D.Double(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y);
break;
}
... (other shapes)
}
}
public void moveShape(Point lastPoint, Point newPoint, ShapeType t) {
int xOffset = newPoint.x - lastPoint.x;
int yOffset = newPoint.y - lastPoint.y;
switch(t) {
case RECTANGLE: {
double x1 = shape.getBounds().getX() + xOffset;
double y1 = shape.getBounds().getY() + yOffset;
double w = shape.getBounds().getWidth();
double h = shape.getBounds().getHeight();
shape = new Rectangle2D.Double(x1, y1, w, h);
break;
}
... (other shapes)
}
}
}
For some components ( I don't know if this apply to your components while being dragged ) you can set the background and use a "transparent color"
The Color class does implements Transparency.
To use it you may specify the alpha value in the Color constructor.
For instance this is a semi transparent black background:
// 0: totally transparent
// 255: totally opaque,
// 192 semy transparent.
this.setBackground(new Color( 0, 0, 0, 192 ));
See the [constructor][1]
Again, I'm not sure if this applies to you. Give it a try
[1]: http://java.sun.com/javase/6/docs/api/java/awt/Color.html#Color(int, int, int, int)

Categories