/*
* Copyright (c) 2010-2016, Sikuli.org, sikulix.com
* Released under the MIT License.
*
*/
package org.sikuli.guide;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import javax.swing.JPanel;
import org.sikuli.guide.Visual.Layout;
import org.sikuli.basics.Debug;
import org.sikuli.util.OverlayTransparentWindow;
import org.sikuli.script.Region;
import org.sikuli.basics.Settings;
import org.sikuli.natives.SysUtil;
// TODO: Automatically move mouse cursor to the click target. The current implementation
// is problematic for non-rectangular clickable widgets, for instance, a round buttone.
// Since the highlighted region is always rectangular and is larger than the area that
// is actually cliable, users may click on the edge of the region and dismiss the window
// errorneously. This needs to be fixed.
public class ClickableWindow extends OverlayTransparentWindow
implements MouseListener, Transition, GlobalMouseMotionListener {
Guide guide;
JPanel jp = null;
ArrayList<SxClickable> clickables = new ArrayList<SxClickable>();
private SxClickable lastClicked;
private Rectangle maxR;
Point clickLocation;
GlobalMouseMotionTracker mouseTracker;
TransitionListener token;
Cursor handCursor = new Cursor(Cursor.HAND_CURSOR);
Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
Cursor hourglassCursor = new Cursor(Cursor.WAIT_CURSOR);
Cursor currentCursor = null;
public ClickableWindow(Guide guide) {
super(new Color(0.1f, 0f, 0f, 0.005f), null);
this.guide = guide;
jp = getJPanel();
mouseTracker = GlobalMouseMotionTracker.getInstance();
mouseTracker.addListener(this);
setVisible(false);
setFocusableWindowState(false);
addMouseListener(this);
addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
// stop the global mouse tracker's timer thread
mouseTracker.stop();
}
});
}
@Override
public void toFront() {
if (Settings.isMac()) {
// this call is necessary to allow clicks to go through the window (ignoreMouse == true)
if (Settings.JavaVersion < 7) {
SysUtil.getOSUtil().bringWindowToFront(this, true);
} else {
}
}
super.toFront();
}
public void addClickable(SxClickable c) {
clickables.add(c);
SxClickable c1 = new SxClickable(null);
c1.setLocationRelativeToComponent(c, Layout.OVER);
jp.add(c1);
}
public void addClickableRegion(Region region, String name) {
SxClickable c = new SxClickable(region);
c.setName(name);
addClickable(c);
}
@Override
public void mouseClicked(MouseEvent e) {
Debug.log("[ClickableWindow] clicked on " + e.getX() + "," + e.getY());
Point p = e.getPoint();
lastClicked = null;
for (SxClickable c : clickables) {
if (c.getActualBounds().contains(p)) {
lastClicked = c;
p.x -= c.getX();
p.y -= c.getY();
c.globalMouseClicked(p);
}
}
if (lastClicked != null) {
if (token != null) {
setVisible(false);
token.transitionOccurred(this);
}
}
}
//<editor-fold defaultstate="collapsed" desc="MouseEvents not used">
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
@Override
public void mousePressed(MouseEvent arg0) {
}
@Override
public void mouseReleased(MouseEvent arg0) {
}
//</editor-fold>
@Override
public String waitForTransition(TransitionListener token) {
this.token = token;
maxR = clickables.get(0).getBounds();
if (clickables.size() > 1) {
for (SxClickable c : clickables.subList(1, clickables.size())) {
maxR = maxR.union(c.getActualBounds());
}
}
setBounds(maxR);
setVisible(true);
mouseTracker.start();
return "Next";
}
@Override
public void globalMouseMoved(int x, int y) {
Point p = new Point(x, y);
SxClickable cc = null;
for (SxClickable c : clickables) {
if (c.getBounds().contains(p)) {
c.setMouseOver(true);
cc = c;
} else {
c.setMouseOver(false);
}
}
//TODO keep moving to (0,0) to nullify the dragged move bug
if (cc != null) {
setLocation(0, 0);
setSize(cc.getActualLocation().x+cc.getActualWidth(), cc.getActualLocation().y+cc.getActualHeight());
} else {
setBounds(maxR);
}
}
@Override
public void globalMouseIdled(int x, int y) {
}
public SxClickable getLastClicked() {
return lastClicked;
}
public ArrayList<SxClickable> getClickables() {
return clickables;
}
public void clear() {
clickables.clear();
getContentPane().removeAll();
}
}