/*
* ScreenMarkerLine.java
*
* Created on 29. maj 2007, 16:37
*
* To change this template, choose Tools | Options and locate the template under
* the Source Creation and Management node. Right-click the template and choose
* Open. You can then make changes to the template in the Source Editor.
*/
package krut.KRUT_GUI;
/**
*
* @author Jonas
*/
/** This is a class to draw lines that usually look like they are drawn
* directly on the screen. It can be used to mark an area of the screen.
* At the moment, only straight lines can be drawn.
*
* The drawing is performed very crudly by using an undecorated JFrame
* to draw a series of pixels, lines, or rectangular shapes over a
* snapshot that has been taken of the background. These drawings are aligned
* and added up to a line of the specified dimensions.
*
* This class is almost, but not entirely, finished. It is not
* currently used in the Krut program.
*/
public class ScreenMarkerLine {
/** The robot used to take the snapshots of the background.
*/
private java.awt.Robot robot = null;
/** The JPanel class that is used to draw the ScreenMarkerLine.
*/
private class LinePanel extends javax.swing.JPanel {
java.awt.Color backupColor;
public void paintComponent(java.awt.Graphics g) {
java.awt.Graphics2D g2 = (java.awt.Graphics2D) g;
if (!segmentHidden) {
super.paintComponent(g);
g2.drawImage(line, 0, 0, this);
backupColor = g2.getColor();
g2.setColor(safeColor);
g2.drawLine(0, 0, 0, 0);
g2.setColor(backupColor);
System.out.println("painted picture");
}
else {
super.paintComponent(g);
backupColor = g2.getColor();
g2.setColor(marker);
g2.drawLine(0, 0, 0, 0);
g2.setColor(backupColor);
System.out.println("painted marker");
}
}
}
public java.awt.Color marker = java.awt.Color.CYAN;
public java.awt.Color safeColor = java.awt.Color.BLACK;
private javax.swing.JFrame segment;
private LinePanel fill;
/** This flag is true if the segment is hidden,
* and false if it is visible.
*/
private boolean segmentHidden = false;
/** The color used to paint the border of the segments
* of the line.
*/
private java.awt.Color segmentBorderColor = java.awt.Color.LIGHT_GRAY;
/** The color used to paint the segments of the line.
*/
private java.awt.Color segmentColor = java.awt.Color.WHITE;
/** The BufferedImage that is drawn on to the ScreenMarkerLine.
*/
private java.awt.image.BufferedImage line;
/** The size of each segment of the line.
* The length in pixels.
*/
private int segmentSize = 0;
/** The gap in each segment. The length in
* pixels. The gap is a part of the
* segment, so obviously the gap can not
* be bigger than the segment size.
*/
private int gapSize = 0;
/** The bounds of this line. If the width or the
* height of the line are 0, the line will never
* be drawn.
*/
private java.awt.Rectangle bounds = new java.awt.Rectangle(0, 0, 0, 0);
/** The horizontal orientation for this class. */
public final static int HORIZONTAL = 0;
/** The vertical orientation for this class. */
public final static int VERTICAL = 1;
/** The orientation for this line.
* This is changed in the setOrientation method,
* and read through the getOrientation method.
*/
private int orientation = HORIZONTAL;
/** This is used to tell the paintThread when it
* should start launching events that look for a
* marker.
*/
private boolean waitForMarker = false;
/** This is used to tell the paintThread when it
* should take a snapshot.
*/
private boolean takeSnapshot = false;
/** The time in ms that the paintThread holds between
* each attempt to find a marker.
*/
private int holdTime = 20;
private Thread paintThread = new Thread(new Runnable(){
public void run() {
while (true) {
// System.out.println("Waiting to check for marker");
while (!waitForMarker) hold(0);
waitForMarker = false;
// System.out.println("Checking for marker");
do {
hold(holdTime);
} while (!checkForMarker());
// System.out.println("Updating graphics");
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
updateLineGraphics();
showSegment();
/** We need an extra one of these here in
* case the graphics have been moved again.
*/
waitForMarker = false;
}
});
}
}
});
/** Creates a new instance of ScreenMarkerLine with
* no size and orientation HORIZONTAL.
*/
public ScreenMarkerLine() {
this(new java.awt.Rectangle (0, 0, 0, 0), HORIZONTAL);
}
/** Creates a new instance of ScreenMarkerLine with
* the given bounds and orientation, provided they are valid,
* with one segment and no gap size.
*
* @param bounds Should be a Rectangle with only
* positive integer values.
* @param orientation Should be either HORIZONTAL or VERTICAL.
*/
public ScreenMarkerLine(java.awt.Rectangle bounds, int orientation) {
this(bounds, orientation,
(orientation == HORIZONTAL) ? bounds.width : bounds.height,
(orientation == HORIZONTAL) ? bounds.width : bounds.height);
}
/** Creates a new instance of ScreenMarkerLine with
* the given parameters, provided they are valid.
*
* @param bounds Should be a Rectangle with only
* positive integer values.
* @param orientation Should be either HORIZONTAL or VERTICAL.
* @param segmentSize Should be a positive integer larger than 0.
* @param gapSize Should be a positive integer no larger than
* the orientation.
*/
public ScreenMarkerLine(java.awt.Rectangle bounds, int orientation,
int segmentSize, int gapSize) {
setBounds(bounds);
setOrientation(orientation);
setSegmentSize(segmentSize);
setGapSize(gapSize);
try {
robot = new java.awt.Robot();
} catch (Exception e) {
System.out.println("e");
}
paintThread.setPriority(Thread.MIN_PRIORITY);
paintThread.start();
System.out.println("made ScreenMarkerLine");
}
/** Used as a place to wait for sync in the updating
* of the graphics.
*
* @param ms The time to wait in ms. If 0 is given,
* the wait will be indefinite (until a
* notifyAll).
*/
public synchronized void hold(long ms) {
try {
System.out.println("waiting " + ms + "ms.");
wait(ms);
} catch (InterruptedException ie) {
System.err.println(ie);
}
}
/** Called by the user to start capturing images.
*
* Also wakes up users waiting for the grabber to finish.
* This method is called once when the last frame is captured,
* and once more when all video data are written to the
* temp file.
*/
public synchronized void wakeUp() {
notifyAll();
}
/** Returns the length of the line. */
public int getLength() {
if (orientation == HORIZONTAL)
return bounds.width;
else
return bounds.height;
}
/** Returns the width of the line. */
public int getWidth() {
if (orientation == VERTICAL)
return bounds.width;
else
return bounds.height;
}
/** Sets the segment size in the orientation of
* the line. If the requested segment size is
* 0 or negative, the line length
* will be set as the new segment size.
* The segment size perpendicular to
* the orientation of the line will always be
* the same as the line width.
*
* @param size The requested segment size
* in the orientation of the line.
*
* @return The segment size that was set.
*/
public int setSegmentSize(int size) {
if (0 < size)
segmentSize = size;
else
segmentSize = getLength();
return segmentSize;
}
/** Gives the segment size in the orientation of
* the line.
*
* @return The segment size of the line.
*/
public int getSegmentSize() {
return segmentSize;
}
/** Sets the gap size in the orientation of the
* line. If the gap size is larger than the
* segment size, the gap size will be set to
* the same as the segment size.
* The gap size perpendicular to
* the orientation of the line will always
* be zero.
*
* @param size The requested gap size in
* the orientation of the line.
*
* @return The gap size that was set.
*/
public int setGapSize(int size) {
int tempSize = getSegmentSize();
if (tempSize < size)
gapSize = size;
else
gapSize = tempSize;
return gapSize;
}
/** Gives the gap size in the orientation of
* the line.
*
* @return The gap size of the line.
*/
public int getGapSize() {
return gapSize;
}
/** Moves the top left corner of the line
* to the position (x, y) and updates
* the line once.
*
* @param x The new x position of the
* line. Should be a positive
* integer.
* @param y The new y position of the
* line. Should be a positive
* integer.
*
* @return 0 if the move was succesful,
* -1 if the move was not succesful.
*/
public int moveLine(int x, int y) {
if ((0 <= x) && (0 <= y)) {
bounds.x = x;
bounds.y = y;
segment.setLocation(x, y);
System.out.println("pos:" + bounds.x + " " + bounds.y);
segmentHide();
return 0;
} else
return -1;
}
/** Sets the bounds of this line. The bounds should
* all be positive integers, otherwise they will be
* changed to 0.
*
* @param bounds The requested new bounds for the line.
*
* @return 0 if everything was fine, -1 if one
* or more of the bounds were changed.
*/
public int setBounds(java.awt.Rectangle bounds) {
int returnVal = 0;
if (bounds.x < 0) {
bounds.x = 0;
returnVal = -1;
}
if (bounds.y < 0) {
bounds.y = 0;
returnVal = -1;
}
if (bounds.width < 0) {
bounds.width = 0;
returnVal = -1;
}
if (bounds.height < 0) {
bounds.height = 0;
returnVal = -1;
}
this.bounds.setBounds(bounds);
return returnVal;
}
/** Get the bounds for this line.
*
* @return A Rectangle containting the bounds for
* this ScreenMarkerLine.
*/
public java.awt.Rectangle getBounds() {
return bounds;
}
/** Sets the orientation of this line.
* The orientation should be one of
* HORIZONTAL or VERTICAL, otherwise
* the orientation will be set to VERTICAL.
* Changing the orientation will remove the
* current segment size and gap size.
*
* @param orientation The requested orientation.
*
* @return The orientation that was set.
*/
public int setOrientation(int orientation) {
segmentSize = getLength();
gapSize = 0;
if (orientation == HORIZONTAL)
this.orientation = orientation;
else
this.orientation = VERTICAL;
return this.orientation;
}
/** This takes a new snapshot of the
* background, and fills in the line segments.
*/
public void updateLineGraphics() {
// System.out.println("getting snapshot");
line = robot.createScreenCapture(bounds);
// System.out.println("got snapshot");
java.awt.Graphics2D lineGraphics = line.createGraphics();
int segments = getLength() / getSegmentSize();
java.awt.Rectangle fillRect;
if (orientation == HORIZONTAL) {
fillRect = new java.awt.Rectangle(
0, 0, getSegmentSize() - getGapSize(), getWidth());
} else {
fillRect = new java.awt.Rectangle(
0, 0, getWidth(), getSegmentSize() - getGapSize());
}
int linePos = 0;
while (0 < segments--) {
// System.out.print("adding segment");
linePos += getGapSize() / 2 + 1;
if (orientation == HORIZONTAL)
fillRect.setLocation(linePos, 0);
else
fillRect.setLocation(0, linePos);
if (2 < getWidth()) {
lineGraphics.setColor(segmentBorderColor);
lineGraphics.drawRect(fillRect.x, fillRect.y,
fillRect.width, fillRect.height);
lineGraphics.setColor(segmentColor);
lineGraphics.fillRect(fillRect.x + 1, fillRect.y + 1,
fillRect.width - 2, fillRect.height - 2);
} else {
lineGraphics.setColor(segmentBorderColor);
if (orientation == HORIZONTAL) {
lineGraphics.drawLine(fillRect.x,
fillRect.y,
fillRect.x,
fillRect.y + fillRect.height);
lineGraphics.drawLine(fillRect.x + fillRect.width,
fillRect.y,
fillRect.x + fillRect.width,
fillRect.y + fillRect.height);
lineGraphics.setColor(segmentColor);
lineGraphics.fillRect(fillRect.x + 1, fillRect.y,
fillRect.width - 2,
fillRect.height);
} else {
lineGraphics.drawLine(fillRect.x,
fillRect.y,
fillRect.x + fillRect.width,
fillRect.y);
lineGraphics.drawLine(fillRect.x,
fillRect.y + fillRect.height,
fillRect.x + fillRect.width,
fillRect.y + fillRect.height);
lineGraphics.setColor(segmentColor);
lineGraphics.fillRect(fillRect.x, fillRect.y + 1,
fillRect.width,
fillRect.height - 2);
}
}
}
//segment.repaint();
// System.out.println("");
}
/** This is just for testing. */
public void setFrame(javax.swing.JFrame inFrame) {
segment = inFrame;
// segment.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
fill = new LinePanel();
fill.setPreferredSize(bounds.getSize());
segment.getContentPane().add(fill, java.awt.BorderLayout.CENTER);
segment.setLocation(new java.awt.Point(bounds.getLocation()));
segment.pack();
System.out.println("set frame");
}
/** The action that is performed everytime the segment
* is hidden. The correct way to update the graphics
* of the segment is to hide it, and then this method
* will update the graphics and display the segment again.
*/
private void segmentHide() {
segmentHidden = true;
// fill.setPreferredSize(new java.awt.Dimension(1,1));
// segment.setPreferredSize(new java.awt.Dimension(1,1));
segment.setSize(1,1);
// System.out.println("Segment was hidden");
waitForMarker = true;
segment.repaint();
wakeUp();
}
private void showSegment() {
// segment.setPreferredSize(bounds.getSize());
// fill.setPreferredSize(bounds.getSize());
segment.setSize(bounds.getSize());
segmentHidden = false;
segment.repaint();
}
private boolean checkForMarker() {
return robot.getPixelColor(bounds.x, bounds.y).equals(marker);
}
/** Create the JFrame that the line is drawn in.
*
*/
public void createLine() {
segment = new javax.swing.JFrame();
segment.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
segment.setUndecorated(true);
// segment.addComponentListener(new java.awt.event.ComponentAdapter() {
// public void componentHidden(java.awt.event.ComponentEvent evt) {
// segmentHidden();
// }
// });
fill = new LinePanel();
fill.setPreferredSize(bounds.getSize());
fill.setOpaque(true);
segment.setContentPane(fill);
// segment.getContentPane().add(fill, java.awt.BorderLayout.CENTER);
segment.setLocation(bounds.getLocation());
segment.pack();
segment.setVisible(true);
}
}