/* * 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.

* * 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.

* * 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.

* * 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.

* * KVETCHING: Kvetch 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 kvetch is where this code lives * and is intended to check a broad range of conditions.

* * To turn off specific kvetch's, one simply specifies a System property * as in this table:

* * * * * * * * *
Property nameValueDiscussion
javasoft.awtsqe.lw.IGNORE_FOCUS_KVETCHtrue or falseSpecify whether the hasFocus kvetch is checked.

* * XXX To implement - 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.

* * XXX To implement - 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.

*/ 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 shoulds 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 shoulds 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.

* * Child classes of LWComponent are to call {@code restrictGraphicsToClientArea} * at the beginning of their {@code paint} method, and then call * {@code unrestrictGraphicsFromClientArea} afterwards.

* * 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).

* * @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); } }