blob: 2209d0c107a9a21f51e65cfcd61a7fcbcdd8fef0 [file] [log] [blame]
/*
* Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package test.java.awt.event.helpers.lwcomponents;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
/**
* This is experimental - The idea is to subclass all the LW components
* from LWComponent to provide for some common capabilities. The main
* capability to be provided is the status rectangles as done for LWButton.
* In particular the Focus and MouseOver rectangles are generically
* useful, while other rectangles might be useful to other components.<p>
*
* To implement that, here is the idea ... borrowed from Win32 ... Each
* of the LW components has both a client and non-client region. We
* call paintNC to paint the non-client region (Focus and MouseOver
* rectangles), and the subclass might be permitted to implement paintNC
* but for now they aren't.<p>
*
* Then the paint{Enabled,Disabled} methods are called as appropriate.
* Note that paintDisabled is implemented in LWComponent to call paintEnabled
* then stipple over the top of it.<p>
*
* So it is paintEnabled that the component should implement. This method
* needs to know the dimensions of the client area (getClientRegion?) and
* the Graphics needs to have it's clip region set appropriately.<p>
*
* <b>KVETCHING</b>: <i>Kvetch</i> is a Yiddish word which means, basically,
* to complain very precisely. The LWComponent family tracks various pieces
* of information over time that are used to check closely for correct behavior
* in some circumstances. The method <i>kvetch</i> is where this code lives
* and is intended to check a broad range of conditions.<p>
*
* To turn off specific kvetch's, one simply specifies a System property
* as in this table:<p>
*
* <table border="1">
* <tr><th>Property name</th><th>Value</th><th>Discussion</th></tr>
* <tr>
* <th>javasoft.awtsqe.lw.IGNORE_FOCUS_KVETCH</th>
* <th>true or false</th>
* <td>Specify whether the <i>hasFocus</i> kvetch is checked.</td>
* </tr>
* </table><p>
*
* <b>XXX To implement</b> - specifying colors. NCBackground,
* FocusRectColor, MouseOverColor are the threee colors. paintNC
* fills the NC region with NCBackground, and then pains the two
* colors as appropriate. There needs to be methods to get/specify
* these colors.<p>
*
* <b>XXX To implement</b> - Specifying the component name and toString().
* The subclass should only give the base class name, and a method
* in LWComponent should construct a name from that. For toString()
* there needs to be a small amount of infrastructure built.<p>
*/
public abstract class LWComponent extends Component {
protected static Color ncBackgroundColor;
protected static Color focusColor;
protected static Color focusWrongColor;
protected static Color mouseOverColor;
static {
ncBackgroundColor = Color.white;
focusColor = Color.black;
focusWrongColor = Color.magenta;
mouseOverColor = Color.blue;
}
/**
* Flag indicating whether our records indicate that the component
* should have focus.
*/
protected boolean _shouldHaveFocus = false;
protected boolean _shouldBeShowing = false;
protected boolean mouseB1Pressed = false;
protected boolean mouseB2Pressed = false;
protected boolean mouseB3Pressed = false;
protected boolean mouseInside = false;
protected static boolean tracingOn = false;
protected static PrintStream traceOutput = null;
// Uncommenting these lines turns on tracing for the package.
// static {
// tracingOn = true;
// traceOutput = System.err;
// }
public LWComponent() {
enableEvents(AWTEvent.MOUSE_EVENT_MASK
/*| AWTEvent.MOUSE_MOTION_EVENT_MASK*/
| AWTEvent.FOCUS_EVENT_MASK
| AWTEvent.COMPONENT_EVENT_MASK);
}
/**
* Print out an error message.
* @param msg the message
*/
public static void errorMsg(String msg) {
System.err.println("ERROR: " + msg);
}
/**
* Print out a tracing message
* @param msg the message
*/
public static void traceMsg(String msg) {
if (LWComponent.tracingOn) {
LWComponent.traceOutput.println(msg);
}
}
/////////////////////////////////////////////
/////// FLAGS FOR IGNORING KVETCH's /////////
/////////////////////////////////////////////
static boolean bIgnFocus = false;
static {
// Initialize the kvetch ignoring flags here.
String ignFocus = System.getProperty("javasoft.awtsqe.lw.IGNORE_FOCUS_KVETCH",
"false");
bIgnFocus = ignFocus.trim().toLowerCase().equals("true");
}
/**
* Check the <i>shoulds</i> and return a string indicating which
* do not match the components actual state.
*
* @return the string indicating which do not match the components actual state
*/
public String kvetch() {
String ret = this.toString();
boolean errors = false;
if (!bIgnFocus) {
if (hasFocus()) {
if (!shouldHaveFocus()) {
ret += "\nERROR: hasFocus indicates we have Focus, when we shouldn't.";
errors = true;
}
} else {
if (shouldHaveFocus()) {
ret += "\nERROR: (see bug#4233658) hasFocus does not indicate we have Focus, when we should.";
errors = true;
}
}
}
if (errors) {
return ret;
} else {
return null;
}
}
/**
* Check the <i>shoulds</i> and return a string indicating which
* do not match the components actual state. Prints the output
* to the given PrintStream.
* @param out The PrintStream to print to.
*/
public void kvetch(PrintStream out) {
if (out != null) {
String s = kvetch();
if (s != null) {
LWComponent.errorMsg(s);
}
}
}
/**
* Turn on tracing for the LWComponent family.
* @param out the output stream
*/
public static void startTracing(PrintStream out) {
tracingOn = true;
traceOutput = out;
}
/**
* Turn off tracing for the LWComponent family.
*/
public static void stopTracing() { tracingOn = false; traceOutput = null; }
/**
* Indicate whether it is believed the component should have focus.
* @return {@code true} if the component should have focus
*/
public boolean shouldHaveFocus() { return _shouldHaveFocus; }
/**
* Indicate whether it is believed the component should be showing.
* @return {@code true} if the component should be showing
*/
public boolean shouldBeShowing() { return _shouldBeShowing; }
@Override
protected void processFocusEvent(FocusEvent e) {
super.processFocusEvent(e);
LWComponent.traceMsg("processFocusEvent " + e.toString());
switch (e.getID()) {
case FocusEvent.FOCUS_GAINED:
_shouldHaveFocus = true;
repaint();
break;
case FocusEvent.FOCUS_LOST:
_shouldHaveFocus = false;
repaint();
break;
}
}
@Override
protected void processComponentEvent(ComponentEvent e) {
super.processComponentEvent(e);
LWComponent.traceMsg("processComponentEvent " + e.toString());
switch (e.getID()) {
case ComponentEvent.COMPONENT_MOVED: break;
case ComponentEvent.COMPONENT_RESIZED: break;
case ComponentEvent.COMPONENT_SHOWN: _shouldBeShowing = true; break;
case ComponentEvent.COMPONENT_HIDDEN: _shouldBeShowing = false; break;
}
}
@Override
protected void processMouseEvent(MouseEvent e) {
int mod = e.getModifiers();
super.processMouseEvent(e);
LWComponent.traceMsg("processMouseEvent " + e.toString());
switch (e.getID()) {
case MouseEvent.MOUSE_PRESSED:
if ((mod & MouseEvent.BUTTON1_MASK) != 0) {
if (mouseB1Pressed) {
errorMsg("ERROR: MOUSE_PRESSED for B1 when already pressed, on "
+ this.toString());
}
mouseB1Pressed = true;
break;
}
if ((mod & MouseEvent.BUTTON2_MASK) != 0) {
if (mouseB2Pressed) {
errorMsg("ERROR: MOUSE_PRESSED for B2 when already pressed, on "
+ this.toString());
}
mouseB2Pressed = true;
break;
}
if ((mod & MouseEvent.BUTTON3_MASK) != 0) {
if (mouseB3Pressed) {
errorMsg("ERROR: MOUSE_PRESSED for B3 when already pressed, on "
+ this.toString());
}
mouseB3Pressed = true;
break;
}
repaint();
break;
case MouseEvent.MOUSE_RELEASED:
if ((mod & MouseEvent.BUTTON1_MASK) != 0) {
if (!mouseB1Pressed) {
errorMsg("ERROR: MOUSE_RELEASED for B1 when not pressed, on "
+ this.toString());
}
mouseB1Pressed = false;
break;
}
if ((mod & MouseEvent.BUTTON2_MASK) != 0) {
if (!mouseB2Pressed) {
errorMsg("ERROR: MOUSE_RELEASED for B2 when not pressed, on "
+ this.toString());
}
mouseB2Pressed = false;
break;
}
if ((mod & MouseEvent.BUTTON3_MASK) != 0) {
if (!mouseB3Pressed) {
errorMsg("ERROR: MOUSE_RELEASED for B3 when not pressed, on "
+ this.toString());
}
mouseB3Pressed = false;
break;
}
repaint();
break;
case MouseEvent.MOUSE_CLICKED:
break;
case MouseEvent.MOUSE_ENTERED:
if (mouseInside) {
errorMsg("ERROR: MOUSE_ENTERED when mouse already inside component, on "
+ this.toString());
}
mouseInside = true;
repaint();
break;
case MouseEvent.MOUSE_EXITED:
if (!mouseInside) {
errorMsg("ERROR: MOUSE_EXITED when mouse not inside component, on "
+ this.toString());
}
mouseInside = false;
repaint();
break;
case MouseEvent.MOUSE_MOVED:
break;
case MouseEvent.MOUSE_DRAGGED:
break;
}
}
public Point getClientLocation() {
return new Point(5, 5);
}
public Dimension getClientSize() {
Dimension dim = getSize();
dim.width -= 10;
dim.height -= 10;
return dim;
}
public Rectangle getClientBounds() {
Dimension dim = getClientSize();
return new Rectangle(5, 5, dim.width, dim.height);
}
public int getClientX() { return 5; }
public int getClientY() { return 5; }
/**
* Set the color used for painting the non-client area of the component.
* The default for this is Color.white.
*
* @param c The new color to use.
*/
public void setNonClientColor(Color c) {
LWComponent.ncBackgroundColor = c;
}
/**
* Handle painting for the component.
*/
@Override
public void paint(Graphics g) {
Dimension dim = getSize();
kvetch(System.err);
Color saveColor = g.getColor();
super.paint(g);
// ------------------- Paint the background -----------------
// In jdk 1.2 (pre-release) there was a bug using clearRect
// to paint the background of a lightweight.
//g.clearRect(0, 0, dim.width, dim.height);
g.setColor(getBackground());
g.fillRect(0, 0, dim.width, dim.height);
// ------------------- Paint the non-client area ------------
g.setColor(ncBackgroundColor);
// x y width height
g.fillRect(0, 0, dim.width, 5);
g.fillRect(0, 5, 5, dim.height - 10);
g.fillRect(dim.width - 5, 5, 5, dim.height - 10);
g.fillRect(0, dim.height - 5, dim.width, 5);
if (shouldHaveFocus() || hasFocus()) {
g.setColor(shouldHaveFocus() && hasFocus()
? focusColor
: focusWrongColor);
g.drawRect(1, 1, dim.width - 3, dim.height - 3);
}
if (mouseInside) {
g.setColor(mouseOverColor);
g.drawRect(3, 3, dim.width - 7, dim.height - 7);
}
// ------------------- Paint disabledness, if true -----------
if (!isEnabled()) {
g.setColor(getBackground());
Dimension size = getSize();
int borderThickness = 0;
int startX = borderThickness;
int startY = borderThickness;
int endX = startX + size.width - 2 * borderThickness - 2;
int endY = startY + size.height - 2 * borderThickness - 2;
int x, y;
for (y = startY; y <= endY; y += 1) {
for (x = startX + (y % 2); x <= endX; x += 2) {
g.fillRect(x, y, 1, 1);
} // x
} // y
}
g.setColor(saveColor);
}
/**
* Restricts the Graphics to be within the "client area" of the
* component. Recall that the LWComponent series of components has
* a "non-client area" of 5 pixels wide in which it draws two
* status rectangles showing mouse-over and has-focus status. <p>
*
* Child classes of LWComponent are to call {@code restrictGraphicsToClientArea}
* at the beginning of their {@code paint} method, and then call
* {@code unrestrictGraphicsFromClientArea} afterwards.<p>
*
* In order to make those paint methods as convenient as possible, these
* two methods make it appear as if the Graphics available to the
* component is slightly smaller than it really is, by the amount
* used in the non-client area (5 pixel wide border).<p>
*
* @param g The Graphics to restrict.
*/
public void restrictGraphicsToClientArea(Graphics g) {
Dimension dim = getSize();
g.translate(5, 5);
g.setClip(0, 0, dim.width - 10, dim.height - 10);
}
/**
* Undo the restriction done in restrictGraphicsToClientArea.
*
* @param g The Graphics to unrestrict.
*/
public void unrestrictGraphicsFromClientArea(Graphics g) {
g.translate(-5, -5);
Dimension dim = getSize();
g.setClip(0, 0, dim.width, dim.height);
}
}