/*********************************************************************************
* TotalCross Software Development Kit *
* Copyright (C) 1998, 1999 Wabasoft <www.wabasoft.com> *
* Copyright (C) 2000-2012 SuperWaba Ltda. *
* All Rights Reserved *
* *
* This library and virtual machine 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. *
* *
* This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 *
* A copy of this license is located in file license.txt at the root of this *
* SDK or can be downloaded here: *
* http://www.gnu.org/licenses/lgpl-3.0.txt *
* *
*********************************************************************************/
package totalcross.ui;
import totalcross.sys.*;
import totalcross.ui.event.*;
import totalcross.ui.font.*;
import totalcross.ui.gfx.*;
import totalcross.ui.image.*;
import totalcross.util.*;
/**
* Control is the base class for user-interface objects.
*/
public class Control extends GfxSurface
{
/** The type of surface. */
int surfaceType; // don't move from here! must be at position 0
/** The control's x location */
protected int x; // guich@200: VERY IMPORTANT: DONT CHANGE THE LOCATION OF THIS VARIABLE! it must be the # 1
/** The control's y location */
protected int y; // guich@200: VERY IMPORTANT: DONT CHANGE THE LOCATION OF THIS VARIABLE! it must be the # 2
/** The control's width */
protected int width; // guich@200: VERY IMPORTANT: DONT CHANGE THE LOCATION OF THIS VARIABLE! it must be the # 3
/** The control's height */
protected int height; // guich@200: VERY IMPORTANT: DONT CHANGE THE LOCATION OF THIS VARIABLE! it must be the # 4
/** The parent of the control. */
protected Container parent;
/** The control's next sibling. */
Control next;
/** The control's previous sibling. */
Control prev;
/** True if the control is enabled (accepts events) or false if not */
private boolean enabled=true;
/** The font used by the control. */
protected Font font;
/** The fontMetrics corresponding to the controls font. */
public FontMetrics fm;
/** True if the control is visible, false otherwise */
protected boolean visible=true;
/** Foreground color of this control. When the control is added, its foreground is set to be the same of the parent's. */
protected int foreColor=-1;
/** Background color of this control. When the control is added, its background is set to be the same of the parent's. */
protected int backColor=-1;
/** Application defined constant. This constant is not used; its only a placeholder for the application so it can set to any value it wants */
public int appId;
/** Application defined object. This field is not used; its only a placeholder for the application so it can set to any value it wants */
public Object appObj; // guich@580_38
/** Default value when calling clear. When the control will use a numeric value or a String, depends on the type of control. Defaults to an empty string. */
public String clearValueStr = ""; // guich@572_19
/** Default value when calling clear. When the control will use a numeric value or a String, depends on the type of control. Defaults to zero. */
public int clearValueInt; // guich@572_19
public static final int RANGE = 10000000;
private static final int UICONST = RANGE*2+1000000;
/** Constant used in params width and height in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: PREFERRED+2 or PREFERRED-1. */
public static final int PREFERRED = 1*UICONST;
/** Constant used in param x in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: LEFT+2 or LEFT-1. */
public static final int LEFT = 2*UICONST;
/** Constant used in params x and y in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: CENTER+2 or CENTER-1. */
public static final int CENTER = 3*UICONST;
/** Constant used in param x in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: RIGHT+2 or RIGHT-1. */
public static final int RIGHT = 4*UICONST;
/** Constant used in param y in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: TOP+2 or TOP-1. */
public static final int TOP = 5*UICONST;
/** Constant used in param y in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: BOTTOM+2 or BOTTOM-1. */
public static final int BOTTOM = 6*UICONST;
/** Constant used in params width and height in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: FILL+2 or FILL-1. Note that FILL cannot be used with other x/y positioning constants. */
public static final int FILL = 7*UICONST;
/** Constant used in param x/y in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: BEFORE+2 or BEFORE-1. */
public static final int BEFORE = 8*UICONST; // guich@200b4_100
/** Constant used in params x/y/width/height in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: SAME+2 or SAME-1. */
public static final int SAME = 9*UICONST; // guich@200b4_100
/** Constant used in param x/y in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: AFTER+2 or AFTER-1. */
public static final int AFTER = 10*UICONST; // guich@200b4_100
/** Constant used in params width and height in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: FIT+2 or FIT-1. Note that FIT cannot be used with other x/y positioning constants. FIT will make the control fit between the desired position and the last added control position. */
public static final int FIT = 11*UICONST; // guich@330_4
/** Constant used in param x/y in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: CENTER_OF+2 or CENTER_OF-1. CENTER_OF is related to a control, while CENTER is related to the screen. CENTER_OF cannot be used with FILL/FIT in the widths. */
public static final int CENTER_OF = 12*UICONST; // guich@tc110_88
/** Constant used in param x/y in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: RIGHT_OF+2 or RIGHT_OF-1. RIGHT_OF is related to a control, while RIGHT is related to the screen. RIGHT_OF cannot be used with FILL/FIT in the widths. */
public static final int RIGHT_OF = 13*UICONST; // guich@tc110_97
/** Constant used in param x/y in setRect. You can use this constant added to a number to specify a increment/decrement to the calculated size. EG: BOTTOM_OF+2 or BOTTOM_OF-1. BOTTOM_OF is related to a control, while BOTTOM is related to the screen. BOTTOM_OF cannot be used with FILL/FIT in the widths. */
public static final int BOTTOM_OF = 14*UICONST; // guich@tc110_97
/** Constant used in param width (will use screen's width) and height (will use screen's height) in setRect.
* You can use this constant added or subtracted to a number to specify a increment to the calculated size.
* There are two ways to use it:<br>
* 1. SCREENSIZE + constant: it will use as a PERCENTAGE of the screen's size. For example, SCREENSIZE+20 in width will result in 20% of screen's size.<br>
* 2. SCREENSIZE - constant: it will use as a FRACTION of the screen's size. For example, SCREENSIZE-4 in width will result in 1/4 of screen's size.<br>
*
* If there are no constant number, size will be 100% of the screen's width/height.
* @since TotalCross 1.3
*/
public static final int SCREENSIZE = 15*UICONST;
/** Constant used in params x/y/width/height in setRect. It keeps the current value. Note that it does NOT support increment nor decrement.
* KEEP differs from SAME in the manner that KEEP applies to the coordinates of this control, while SAME applies
* to the coordinates of another control.
* @since TotalCross 1.14
*/
public static final int KEEP = 16*UICONST; // guich@tc114_68
/** Constant used in param width (will use parent's width) and height (will use parent's height) in setRect.
* You can use this constant added or subtracted to a number to specify a increment to the calculated size.
* There are two ways to use it:<br>
* 1. PARENTSIZE + constant: it will use as a PERCENTAGE of the parent's size. For example, PARENTSIZE+20 in width will result in 20% of parent's size.<br>
* 2. PARENTSIZE - constant: it will use as a FRACTION of the parent's size. For example, PARENTSIZE-4 in width will result in 1/4 of parent's size.<br>
*
* If there are no constant number, size will be 100% of the parent's width/height.
*
* If the parent is unknown, the screen size will be used instead.
* @since TotalCross 1.52
*/
public static final int PARENTSIZE = 17*UICONST;
/** Constant used in param width or height (will use screen's minimum size between width and height) in setRect.
* You can use this constant added or subtracted to a number to specify a increment to the calculated size.
* There are two ways to use it:<br>
* 1. SCREENSIZEMIN + constant: it will use as a PERCENTAGE of the screen's size. For example, SCREENSIZEMIN+20 in width will result in 20% of screen's minimum size.<br>
* 2. SCREENSIZEMIN - constant: it will use as a FRACTION of the screen's size. For example, SCREENSIZEMIN-4 in width will result in 1/4 of screen's minimum size.<br>
*
* If there are no constant number, size will be 100% of the screen's width/height.
* @since TotalCross 2.0
*/
public static final int SCREENSIZEMIN = 18*UICONST;
/** Constant used in param width or height (will use screen's maximum size between width and height) in setRect.
* You can use this constant added or subtracted to a number to specify a increment to the calculated size.
* There are two ways to use it:<br>
* 1. SCREENSIZEMAX + constant: it will use as a PERCENTAGE of the screen's size. For example, SCREENSIZEMAX+20 in width will result in 20% of screen's maximum size.<br>
* 2. SCREENSIZEMAX - constant: it will use as a FRACTION of the screen's size. For example, SCREENSIZEMAX-4 in width will result in 1/4 of screen's maximum size.<br>
*
* If there are no constant number, size will be 100% of the screen's width/height.
* @since TotalCross 2.0
*/
public static final int SCREENSIZEMAX = 19*UICONST;
/** Constant used in param width or height (will use parent's minimum size between width and height) in setRect.
* You can use this constant added or subtracted to a number to specify a increment to the calculated size.
* There are two ways to use it:<br>
* 1. PARENTSIZEMIN + constant: it will use as a PERCENTAGE of the parent's size. For example, PARENTSIZEMIN+20 in width will result in 20% of parent's size.<br>
* 2. PARENTSIZEMIN - constant: it will use as a FRACTION of the parent's size. For example, PARENTSIZEMIN-4 in width will result in 1/4 of parent's size.<br>
*
* If there are no constant number, size will be 100% of the parent's width/height.
*
* If the parent is unknown, the screen size will be used instead.
* @since TotalCross 2.0
*/
public static final int PARENTSIZEMIN = 20*UICONST;
/** Constant used in param width or height (will use parent's maximum size between width and height) in setRect.
* You can use this constant added or subtracted to a number to specify a increment to the calculated size.
* There are two ways to use it:<br>
* 1. PARENTSIZEMAX + constant: it will use as a PERCENTAGE of the parent's size. For example, PARENTSIZEMAX+20 in width will result in 20% of parent's size.<br>
* 2. PARENTSIZEMAX - constant: it will use as a FRACTION of the parent's size. For example, PARENTSIZEMAX-4 in width will result in 1/4 of parent's size.<br>
*
* If there are no constant number, size will be 100% of the parent's width/height.
*
* If the parent is unknown, the screen size will be used instead.
* @since TotalCross 2.0
*/
public static final int PARENTSIZEMAX = 21*UICONST;
/** Constant used in params width/height in setRect. It informs that the parent's last width/height should not be updated now, because it will be resized later. Note that it does NOT support increment nor decrement.
* Sample:
* <pre>
* Container c;
* add(c = new Container(), LEFT+5, AFTER+5, FILL-5, WILL_RESIZE);
* // add controls to c
* c.resizeHeight();
* </pre>
* @since TotalCross 1.14
*/
public static final int WILL_RESIZE = RANGE/3; // guich@tc114_68
private static final int MAXABSOLUTECOORD = PREFERRED - RANGE;
/** Set to true to ignore parent's insets when placing this control on screen. */
public boolean ignoreInsets;
private ControlEvent pressedEvent; // guich@tc100: share the same event across all controls - guich@tc114_42: no longer share
/** Allows to disable the ui adjustments based on font height for a single control.
* Set this flag in the constructor. It is propagated to all child controls.
* @since TotalCross 1.3
*/
public boolean uiAdjustmentsBasedOnFontHeightIsSupported=true;
/** Set to false to disallow the screen update. */
public static boolean enableUpdateScreen = true;
/** Stores the height of the current font. The same of <code>fm.height</code>. */
protected int fmH;
/** Stores the control that should handle the focus change key for this control */
protected boolean focusHandler; // kmeehl@tc100
/** If true, the keyboard arrows will be used to highlight the controls until one is selected.
* This will cause the KeyEvent to be intercepted and handled by the method changeHighlighted.
* When the user press the ACTION (or ENTER) key to use the control, this flag is set to false
* and the focus will be set to the control, so it be able to use the arrows to navigate
* inside it. The control must then set this to true when the finish using it
* or press the ACTION button again (which then sets the flag to true).
* @since SuperWaba 5.5
*/
protected static boolean isHighlighting = true;
/** Defines if this control can receive focus by using the arrow keys.
* @since SuperWaba 5.5
*/
public boolean focusTraversable = true;
/** Shortcuts to test the UI style. Use the setUIStyle method to change them accordingly. */
protected static boolean uiFlat,uiVista=true,uiAndroid;
/** If true, this control will receive pen and key events but will never gain focus.
* This is useful to create keypads. See totalcross.ui.Calculator.
*/
protected boolean focusLess;
protected EnabledStateChangeEvent esce = new EnabledStateChangeEvent();
public boolean eventsEnabled = true;
static Rect cli = new Rect();
/** Specifies if this device is a tablet, computing the number of text lines.
*/
public static boolean isTablet;
static final int SETX_NOT_SET = -100000000;
protected int setX = SETX_NOT_SET, setY, setW, setH;
protected Font setFont;
protected Control setRel;
protected boolean repositionAllowed;
protected int tempW; // used in flowContainer
/** The shadow color to be applied to this control. */
public int textShadowColor; // guich@tc126_26
/** Set to true to call onEvent before calling the event listeners.
* By default, the event listener is called before the onEvent. */
public boolean onEventFirst;
/** READ-ONLY property which is not null if this control is a container */
protected Container asContainer; // speedup some instance_of Container
/** READ-ONLY property which is not null if this control is a window */
protected Window asWindow; // speedup some instance_of Window
/** The cached Graphics objects for this control. */
Graphics gfx;
static ToolTip uitip;
/** To be used in the setTextShadowColor method. */
public static final int BRIGHTER_BACKGROUND = -2;
/** To be used in the setTextShadowColor method. */
public static final int DARKER_BACKGROUND = -3;
private Vector listeners;
private static boolean callingUpdScr,callingRepNow;
/** Set the background to be transparent, by not filling the control's area with the background color.
* @since TotalCross 1.0
*/
public boolean transparentBackground;
/** True means this control expects to get focus on a PEN_DOWN event. If focusOnPenDown is false, focus will be set on PEN_UP instead.
* This can be used for things like drag-scrollable list controls that contain controls as list items, to avoid setting focus to an
* item (and therefore changing the selection) during a drag-scroll. */
public boolean focusOnPenDown = true;
/** True means that EventListeners for PenEvent and KeyEvent will be called without verifying that the event target is this. */
public boolean callListenersOnAllTargets;
private Control dragTarget; // holds the Control that handled the last dragEvent sent to this control.
/** The offscreen image taken with takeScreenShot. The onPaint will use this shot until the user calls releaseScreenShot.
*/
public Image offscreen;
/** Keep the control disabled even if enabled is true. */
public boolean keepDisabled;
/** creates the font for this control as the same font of the MainWindow. */
protected Control()
{
if (MainWindow.defaultFont == null) // guich@tc130: moved from static block at MainWindow to here, so user can define if he wants to use the old or new font
{
MainWindow.defaultFont = Font.getFont(Font.DEFAULT, false, Font.NORMAL_SIZE);
if (Settings.onJavaSE && !Font.DEFAULT.equals("TCFont"))
Vm.warning("You're using the old font. Consider porting your program to the new font. See Settings.useNewFont javadocs.");
isTablet = Math.max(Settings.screenWidth,Settings.screenHeight)/Font.NORMAL_SIZE > 30;
}
font = MainWindow.defaultFont;
fm = font.fm; // guich@450_36: new way of getting the fontMetrics.
fmH = fm.height;
gfx = new Graphics(this); // guich@tc100
textShadowColor = UIColors.textShadowColor;
}
/** Take a screen shot of this container and stores it in <code>offscreen</code>.
*/
public void takeScreenShot()
{
try
{
offscreen = null;
Image offscreen = new Image(width,height);
paint2shot(offscreen.getGraphics(),false);
this.offscreen = offscreen;
this.offscreen.applyChanges();
}
catch (Throwable t)
{
this.offscreen = null;
}
}
void paint2shot(Graphics g, boolean shift)
{
if (!transparentBackground && parent != null && !(parent.parent != null && parent.parent instanceof ScrollContainer && parent.parent.transparentBackground)) // last clause prevents a white background on SAV's menu
{
g.backColor = parent.backColor;
g.fillRect(0,0,width,height);
}
g.setFont(font);
if (asWindow != null)
asWindow.paintWindowBackground(g);
paint2shot(g,this,shift);
}
/** Releases the screen shot. */
public void releaseScreenShot()
{
offscreen = null;
Window.needsPaint = true;
}
void paint2shot(Graphics g, Control top, boolean shift)
{
// if (asContainer != null || asWindow != null)
// this.refreshGraphics(g,0,top);
if (this.asWindow == null)
this.onPaint(g);
Window w = getParentWindow();
int x0 = shift ? w.x : 0;
int y0 = shift ? w.y : 0;
Rect rtop = top.getRect();
if (asContainer != null)
for (Control child = asContainer.children; child != null; child = child.next)
if (child.visible)
{
Rect r = child.getAbsoluteRect();
if (rtop.intersects(r))
{
child.refreshGraphics(g,0,top,x0,y0);
child.onPaint(g);
if (child.asContainer != null)
child.asContainer.paint2shot(g,top,shift);
}
}
}
/** Call to set the color value to place a shadow around the control's text. The shadow is made
* drawing the button in (x-1,y-1), (x+1,y-1), (x-1,y+1), (x+1,y+1) positions.
* Defaults to -1, which means no shadow.
* You can set pass BRIGHTER_BACKGROUND or DARKER_BACKGROUND as parameter, AFTER calling setBackColor or setForeColor,
* to compute the color based on the background.
* <br><br> Example:
* <pre>
* c = new Label(....);
* c.setBackColor(Color.BLUE);
* c.setTextShadowColor(DARKER_BACKGROUND);
* // you may also set it directly to a color: c.setTextShadowColor(Color.BLACK);
* </pre>
*
* @see #BRIGHTER_BACKGROUND
* @see #DARKER_BACKGROUND
* @see UIColors#textShadowColor
* @since TotalCross 1.27
*/
public void setTextShadowColor(int color)
{
this.textShadowColor = color == BRIGHTER_BACKGROUND ? Color.brighter(backColor) : color == DARKER_BACKGROUND ? Color.darker(backColor) : color;
}
/** Returns the textShadowColor of this control.
* @since TotalCross 1.27
*/
public int getTextShadowColor()
{
return textShadowColor;
}
/** Returns true if the point lies inside this control.
* If it don't lies, but the device is a finger touch one,
* checks if the distance is below the touch tolerance.
* @see totalcross.sys.Settings#touchTolerance
* @see totalcross.sys.Settings#fingerTouch
* @since TotalCross 1.2
*/
public boolean isInsideOrNear(int x, int y) // guich@tc120_48
{
if (0 <= x && x < width && 0 <= y && y < height)
return true;
if (!Settings.fingerTouch)
return false;
int d = (int)(Convert.getDistancePoint2Rect(x,y, 0,0,width,height)+0.5);
return d <= Settings.touchTolerance;
}
/** Shows a message using a global tip shared by all controls. */
static void showTip(Control c, String s, int duration, int y) // guich@tc100b4_27
{
uitip.millisDisplay = duration;
uitip.setText(s);
Window w = c.getParentWindow(); // guich@tc114_59: exclude window position
Rect r = c.getAbsoluteRect();
if (y >= 0)
{
r.y += y;
r.height = 0;
}
else
{
r.x -= w.x;
r.y -= w.y;
}
uitip.setControlRect(r);
uitip.show();
}
/** Posts a ControlEvent.PRESSED event with this control as target.
* @since TotalCross 1.14
*/
public void postPressedEvent()
{
postEvent(getPressedEvent(this));
}
/** Creates a ControlEvent.PRESSED if not yet created and returns it.
* @since TotalCross 1.14
*/
protected ControlEvent getPressedEvent(Control target)
{
if (pressedEvent == null)
pressedEvent = new ControlEvent(ControlEvent.PRESSED, null);
return pressedEvent.update(target);
}
/**
* Adds a timer to a control. Each time the timer ticks, a TIMER
* event will be posted to the control. The timer does
* not interrupt the program during its execution at the timer interval,
* it is scheduled along with application events. The timer object
* returned from this method can be passed to removeTimer() to
* remove the timer. Under Windows, the timer has a minimum resolution
* of 55ms due to the native Windows system clock resolution of 55ms. Under
* Palm OS and other platforms, the minimum timer resolution is 10ms.
* <p>
* If the control that holds the timer is removed from screen, the
* timer is also disabled. Consider using the dispatch-listener event
* model (addTimerListener) instead of creating a control just to catch
* the event (if this is the case).
*
* @param millis the timer tick interval in milliseconds
* @see totalcross.ui.event.TimerEvent
*/
public TimerEvent addTimer(int millis)
{
return MainWindow.mainWindowInstance.addTimer(this, millis);
}
/**
* Add a timer to a control. This method allows you to create an instance
* TimerEvent (or any descendant) ahead of time and add it to the control.
* @param t the TimerEvent instance
* @param millis the timer tick interval in milliseconds
* @see totalcross.ui.event.TimerEvent
*/
public void addTimer(TimerEvent t, int millis)
{
MainWindow.mainWindowInstance.addTimer(t, this, millis);
}
/**
* Removes a timer from a control. True is returned if the timer was
* found and removed and false is returned if the timer could not be
* found (meaning it was not active).
*/
public boolean removeTimer(TimerEvent timer)
{
return MainWindow.mainWindowInstance.removeTimer(timer);
}
/** Sets the font of this control. */
public void setFont(Font font)
{
this.setFont = this.font = font;
this.fm = font.fm;
this.fmH = fm.height;
onFontChanged();
}
/** Gets the font of this control. */
public Font getFont()
{
return this.font;
}
/** Returns the preferred width of this control. */
public int getPreferredWidth()
{
return 30;
}
/** Returns the preferred height of this control. */
public int getPreferredHeight()
{
return fmH;
}
/** Sets or changes a control's position and size.
* <pre>
* setRect(r.x,r.y,r.width,r.height,null,false)
* </pre>
* @see #setRect(int, int, int, int, Control, boolean)
*/
public void setRect(Rect r)
{
setRect(r.x,r.y,r.width,r.height,null,false);
}
/** Sets or changes a control's position and size. Same of
* <pre>
* setRect(x,y,width,height,null,false)
* </pre>
* @see #setRect(int, int, int, int, Control, boolean)
*/
public void setRect(int x, int y, int width, int height)
{
setRect(x,y,width,height,null,false);
}
/** Sets or changes a control's position and size.
* <pre>
* setRect(x,y,width,height,relative,false)
* </pre>
* @see #setRect(int, int, int, int, Control, boolean)
*/
public void setRect(int x, int y, int width, int height, Control relative)
{
setRect(x,y,width,height, relative, false);
}
/** The relative positioning will be made with the given control (relative).
* Note that in this case, only the SAME,BEFORE,AFTER are affected by the given control.
* Here is an example of relative positioning:
* <p>
* Important note: you can't use FILL/FIT with BEFORE/RIGHT/BOTTOM (for x,y).
* <pre>
* add(new Label("1"),CENTER,CENTER);
* add(new Label("2"),AFTER,SAME);
* add(new Label("3"),SAME,AFTER);
* add(new Label("4"),BEFORE,SAME);
* add(new Label("5"),BEFORE,BEFORE);
* </pre>
* You will see this on screen:
* <pre>
* 512
* 43
* </pre>
* Note: add(control, x,y) does: <code>add(control); control.setRect(x,y,PREFERRED,PREFERRED);</code>
* <p>
* <b>Important! Always add the control to the container before doing a setRect.</b>
* <p>The relative positioning does not work well if the control is placed outside screen bounds.
* @param x One of the relative positioning constants: LEFT, RIGHT, SAME, BEFORE, AFTER, CENTER, with a small adjustment. You can also use an absolute value, but this is strongly discouraged.
* @param y One of the relative positioning constants: TOP, BOTTOM, SAME, BEFORE, AFTER, CENTER, with a small adjustment. You can also use an absolute value, but this is strongly discouraged.
* @param width One of the relative positioning constants: PREFERRED, FILL, FIT, SAME. You can also use an absolute value, but this is strongly discouraged.
* @param height One of the relative positioning constants: PREFERRED, FILL, FIT, SAME. You can also use an absolute value, but this is strongly discouraged.
* @param relative To whom the position should be relative to; or null to be relative to the last control.
* @param screenChanged Indicates that a screen change (resize, collapse) occured and the <code>reposition</code> method is calling this method. Set by the system. If you call this method directly, always pass false to it.
* @see #LEFT
* @see #TOP
* @see #RIGHT
* @see #BOTTOM
* @see #BEFORE
* @see #AFTER
* @see #CENTER
* @see #SAME
* @see #FILL
* @see #PREFERRED
* @see #FIT
* @see #CENTER_OF
* @see #RIGHT_OF
* @see #BOTTOM_OF
* @see #SCREENSIZE
* @see #SCREENSIZEMIN
* @see #SCREENSIZEMAX
* @see #PARENTSIZE
* @see #PARENTSIZEMIN
* @see #PARENTSIZEMAX
* @see Container#add(Control, int, int)
* @see Container#add(Control, int, int, Control)
*/
public void setRect(int x, int y, int width, int height, Control relative, boolean screenChanged)
{
if (setX == SETX_NOT_SET) {setX = x; setY = y; setW = width; setH = height; setRel = relative; setFont = this.font;}
if (x+y+width+height >= MAXABSOLUTECOORD) // are there any relative coords?
{
if (x == KEEP) x = this.x;
if (y == KEEP) y = this.y;
if (width == KEEP) width = this.width;
if (height == KEEP) height = this.height;
repositionAllowed = true;
int lpx=0,lpy=0;
Container parent = this.parent; // guich@450_36: use local var instead of field
Rect cli = Control.cli; // guich@450_36: avoid recreating Rects
// relative placement
if (parent != null)
{
if (!ignoreInsets)
parent.getClientRect(cli);
else
{
cli.x = cli.y = 0;
cli.width = parent.width;
cli.height = parent.height;
}
lpx = parent.lastX;
lpy = parent.lastY;
if (relative != null)
{
// use the given control's coords instead of parent's ones.
parent.lastX = relative.x;
parent.lastY = relative.y;
parent.lastW = relative.width;
parent.lastH = relative.height;
}
else
if (parent.lastX == -999999) // first control being added? - guich@450_36: only one check is enough
{
parent.lastX = cli.x;
parent.lastY = cli.y;
}
}
else
{
cli.y = cli.x = 0; // guich@450a_40
cli.width = Settings.screenWidth;
cli.height = Settings.screenHeight;
}
if (Settings.uiAdjustmentsBasedOnFontHeight && uiAdjustmentsBasedOnFontHeightIsSupported)
{
// non-dependant width
if (width < MAXABSOLUTECOORD) {} else
if ((PREFERRED-RANGE) <= width && width <= (PREFERRED+RANGE)) width = getPreferredWidth() + (width-PREFERRED)*fmH/100; else // guich@450_36: changed order to be able to put an else here
if ((SAME -RANGE) <= width && width <= (SAME +RANGE) && parent != null) width = parent.lastW +(width-SAME)*fmH/100; else // can't be moved from here!
if ((SCREENSIZE-RANGE) <= width && width <= (SCREENSIZE+RANGE)) {width -= SCREENSIZE; if (width < 0) width = Settings.screenWidth / -width; else if (width == 0) width = Settings.screenWidth; else width = width * Settings.screenWidth / 100;} else
if ((SCREENSIZEMIN-RANGE) <= width && width <= (SCREENSIZEMIN+RANGE)) {width -= SCREENSIZEMIN; if (width < 0) width = Math.min(Settings.screenWidth,Settings.screenHeight) / -width; else if (width == 0) width = Math.min(Settings.screenWidth,Settings.screenHeight); else width = width * Math.min(Settings.screenWidth,Settings.screenHeight) / 100;} else
if ((SCREENSIZEMAX-RANGE) <= width && width <= (SCREENSIZEMAX+RANGE)) {width -= SCREENSIZEMAX; if (width < 0) width = Math.max(Settings.screenWidth,Settings.screenHeight) / -width; else if (width == 0) width = Math.max(Settings.screenWidth,Settings.screenHeight); else width = width * Math.max(Settings.screenWidth,Settings.screenHeight) / 100;} else
if ((PARENTSIZE-RANGE) <= width && width <= (PARENTSIZE+RANGE)) {width -= PARENTSIZE; if (width < 0) width = cli.width / -width; else if (width == 0) width = cli.width; else width = width * cli.width / 100;} else
if ((PARENTSIZEMIN-RANGE) <= width && width <= (PARENTSIZEMIN+RANGE)) {width -= PARENTSIZEMIN; if (width < 0) width = Math.min(cli.width,cli.height) / -width; else if (width == 0) width = Math.min(cli.width,cli.height); else width = width * Math.min(cli.width,cli.height) / 100;} else
if ((PARENTSIZEMAX-RANGE) <= width && width <= (PARENTSIZEMAX+RANGE)) {width -= PARENTSIZEMAX; if (width < 0) width = Math.max(cli.width,cli.height) / -width; else if (width == 0) width = Math.max(cli.width,cli.height); else width = width * Math.max(cli.width,cli.height) / 100;}
tempW = width;
// non-dependant height
if (height < MAXABSOLUTECOORD) {} else
if ((PREFERRED-RANGE) <= height && height <= (PREFERRED+RANGE)) height = getPreferredHeight() +(height-PREFERRED)*fmH/100; else
if ((SAME -RANGE) <= height && height <= (SAME +RANGE) && parent != null) height = parent.lastH +(height-SAME)*fmH/100; // can't be moved from here!
if ((SCREENSIZE-RANGE) <= height && height <= (SCREENSIZE+RANGE)) {height -= SCREENSIZE; if (height < 0) height = Settings.screenHeight / -height; else if (height == 0) height = Settings.screenHeight; else height = height * Settings.screenHeight / 100;} else
if ((SCREENSIZEMIN-RANGE) <= height && height <= (SCREENSIZEMIN+RANGE)) {height -= SCREENSIZEMIN; if (height < 0) height = Math.min(Settings.screenWidth,Settings.screenHeight) / -height; else if (height == 0) height = Math.min(Settings.screenWidth,Settings.screenHeight); else height = height * Math.min(Settings.screenWidth,Settings.screenHeight) / 100;} else
if ((SCREENSIZEMAX-RANGE) <= height && height <= (SCREENSIZEMAX+RANGE)) {height -= SCREENSIZEMAX; if (height < 0) height = Math.max(Settings.screenWidth,Settings.screenHeight) / -height; else if (height == 0) height = Math.max(Settings.screenWidth,Settings.screenHeight); else height = height * Math.max(Settings.screenWidth,Settings.screenHeight) / 100;} else
if ((PARENTSIZE-RANGE) <= height && height <= (PARENTSIZE+RANGE)) {height -= PARENTSIZE; if (height < 0) height = cli.height / -height; else if (height == 0) height = cli.height; else height = height * cli.height / 100;} else
if ((PARENTSIZEMIN-RANGE) <= height && height <= (PARENTSIZEMIN+RANGE)) {height -= PARENTSIZEMIN; if (height < 0) height = Math.min(cli.width,cli.height) / -height; else if (height == 0) height = Math.min(cli.width,cli.height); else height = height * Math.min(cli.width,cli.height) / 100;} else
if ((PARENTSIZEMAX-RANGE) <= height && height <= (PARENTSIZEMAX+RANGE)) {height -= PARENTSIZEMAX; if (height < 0) height = Math.max(cli.width,cli.height) / -height; else if (height == 0) height = Math.max(cli.width,cli.height); else height = height * Math.max(cli.width,cli.height) / 100;}
// x
if (x < MAXABSOLUTECOORD) {} else
if ((AFTER -RANGE) <= x && x <= (AFTER +RANGE) && parent != null) x = parent.lastX + parent.lastW +(x-AFTER)*fmH/100; else // guich@450_36: test parent only after testing the relative type
if ((BEFORE -RANGE) <= x && x <= (BEFORE +RANGE) && parent != null) x = parent.lastX - width +(x-BEFORE)*fmH/100; else
if ((SAME -RANGE) <= x && x <= (SAME +RANGE) && parent != null) x = parent.lastX +(x-SAME)*fmH/100; else
if ((LEFT -RANGE) <= x && x <= (LEFT +RANGE)) x = cli.x +(x-LEFT)*fmH/100; else
if ((RIGHT -RANGE) <= x && x <= (RIGHT +RANGE)) x = cli.x + cli.width-width +(x-RIGHT)*fmH/100; else
if ((CENTER -RANGE) <= x && x <= (CENTER +RANGE)) x = cli.x + ((cli.width-width) >> 1) +(x-CENTER)*fmH/100; else
if ((CENTER_OF-RANGE) <= x && x <= (CENTER_OF+RANGE)) x = parent.lastX + (parent.lastW - width)/2 +(x-CENTER_OF)*fmH/100; else // guich@tc110_88
if ((RIGHT_OF-RANGE) <= x && x <= (RIGHT_OF+RANGE)) x = parent.lastX + (parent.lastW - width) +(x-RIGHT_OF)*fmH/100; // guich@tc110_97
// y
if (y <= MAXABSOLUTECOORD) {} else
if ((AFTER -RANGE) <= y && y <= (AFTER +RANGE) && parent != null) y = parent.lastY + parent.lastH +(y-AFTER)*fmH/100; else // guich@450_36: test parent only after testing the relative type
if ((BEFORE -RANGE) <= y && y <= (BEFORE +RANGE) && parent != null) y = parent.lastY - height +(y-BEFORE)*fmH/100; else
if ((SAME -RANGE) <= y && y <= (SAME +RANGE) && parent != null) y = parent.lastY +(y-SAME)*fmH/100; else
if ((TOP -RANGE) <= y && y <= (TOP +RANGE)) y = cli.y +(y-TOP)*fmH/100; else
if ((BOTTOM -RANGE) <= y && y <= (BOTTOM +RANGE)) y = cli.y + cli.height-height +(y-BOTTOM)*fmH/100; else
if ((CENTER -RANGE) <= y && y <= (CENTER +RANGE)) y = cli.y + ((cli.height-height) >> 1) +(y-CENTER)*fmH/100; else
if ((CENTER_OF-RANGE) <= y && y <= (CENTER_OF+RANGE)) y = parent.lastY + (parent.lastH - height)/2 +(y-CENTER_OF)*fmH/100; else // guich@tc110_88
if ((BOTTOM_OF-RANGE) <= y && y <= (BOTTOM_OF+RANGE)) y = parent.lastY + (parent.lastH - height) +(y-BOTTOM_OF)*fmH/100; // guich@tc110_97
// width that depends on x
if (width > MAXABSOLUTECOORD)
{
if ((FILL-RANGE) <= width && width <= (FILL+RANGE)) width = cli.width - x + cli.x +(width-FILL)*fmH/100; else
if ((FIT -RANGE) <= width && width <= (FIT +RANGE) && parent != null) width = lpx - x +(width-FIT)*fmH/100;
tempW = width;
}
// height that depends on y
if (height > MAXABSOLUTECOORD)
{
if ((FILL-RANGE) <= height && height <= (FILL+RANGE)) height = cli.height - y + cli.y +(height-FILL)*fmH/100; else
if ((FIT -RANGE) <= height && height <= (FIT +RANGE) && parent != null) height = lpy - y +(height-FIT)*fmH/100;
}
}
else
{
// non-dependant width
if (width < MAXABSOLUTECOORD) {} else
if ((PREFERRED-RANGE) <= width && width <= (PREFERRED+RANGE)) width += getPreferredWidth() -PREFERRED; else // guich@450_36: changed order to be able to put an else here
if ((SAME -RANGE) <= width && width <= (SAME +RANGE) && parent != null) width += parent.lastW - SAME; // can't be moved from here!
if ((SCREENSIZE-RANGE) <= width && width <= (SCREENSIZE+RANGE)) {width -= SCREENSIZE; if (width < 0) width = Settings.screenWidth / -width; else if (width == 0) width = Settings.screenWidth; else width = width * Settings.screenWidth / 100;} else
if ((SCREENSIZEMIN-RANGE) <= width && width <= (SCREENSIZEMIN+RANGE)) {width -= SCREENSIZEMIN; if (width < 0) width = Math.min(Settings.screenWidth,Settings.screenHeight) / -width; else if (width == 0) width = Math.min(Settings.screenWidth,Settings.screenHeight); else width = width * Math.min(Settings.screenWidth,Settings.screenHeight) / 100;} else
if ((SCREENSIZEMAX-RANGE) <= width && width <= (SCREENSIZEMAX+RANGE)) {width -= SCREENSIZEMAX; if (width < 0) width = Math.max(Settings.screenWidth,Settings.screenHeight) / -width; else if (width == 0) width = Math.max(Settings.screenWidth,Settings.screenHeight); else width = width * Math.max(Settings.screenWidth,Settings.screenHeight) / 100;} else
if ((PARENTSIZE-RANGE) <= width && width <= (PARENTSIZE+RANGE)) {width -= PARENTSIZE; if (width < 0) width = cli.width / -width; else if (width == 0) width = cli.width; else width = width * cli.width / 100;} else
if ((PARENTSIZEMIN-RANGE) <= width && width <= (PARENTSIZEMIN+RANGE)) {width -= PARENTSIZEMIN; if (width < 0) width = Math.min(cli.width,cli.height) / -width; else if (width == 0) width = Math.min(cli.width,cli.height); else width = width * Math.min(cli.width,cli.height) / 100;} else
if ((PARENTSIZEMAX-RANGE) <= width && width <= (PARENTSIZEMAX+RANGE)) {width -= PARENTSIZEMAX; if (width < 0) width = Math.max(cli.width,cli.height) / -width; else if (width == 0) width = Math.max(cli.width,cli.height); else width = width * Math.max(cli.width,cli.height) / 100;}
tempW = width;
// non-dependant height
if (height < MAXABSOLUTECOORD) {} else
if ((PREFERRED-RANGE) <= height && height <= (PREFERRED+RANGE)) height += getPreferredHeight() -PREFERRED; else
if ((SAME -RANGE) <= height && height <= (SAME +RANGE) && parent != null) height += parent.lastH -SAME; // can't be moved from here!
if ((SCREENSIZE-RANGE) <= height && height <= (SCREENSIZE+RANGE)) {height -= SCREENSIZE; if (height < 0) height = Settings.screenHeight / -height; else if (height == 0) height = Settings.screenHeight; else height = height * Settings.screenHeight / 100;} else
if ((SCREENSIZEMIN-RANGE) <= height && height <= (SCREENSIZEMIN+RANGE)) {height -= SCREENSIZEMIN; if (height < 0) height = Math.min(Settings.screenWidth,Settings.screenHeight) / -height; else if (height == 0) height = Math.min(Settings.screenWidth,Settings.screenHeight); else height = height * Math.min(Settings.screenWidth,Settings.screenHeight) / 100;} else
if ((SCREENSIZEMAX-RANGE) <= height && height <= (SCREENSIZEMAX+RANGE)) {height -= SCREENSIZEMAX; if (height < 0) height = Math.max(Settings.screenWidth,Settings.screenHeight) / -height; else if (height == 0) height = Math.max(Settings.screenWidth,Settings.screenHeight); else height = height * Math.max(Settings.screenWidth,Settings.screenHeight) / 100;} else
if ((PARENTSIZE-RANGE) <= height && height <= (PARENTSIZE+RANGE)) {height -= PARENTSIZE; if (height < 0) height = cli.height / -height; else if (height == 0) height = cli.height; else height = height * cli.height / 100;} else
if ((PARENTSIZEMIN-RANGE) <= height && height <= (PARENTSIZEMIN+RANGE)) {height -= PARENTSIZEMIN; if (height < 0) height = Math.min(cli.width,cli.height) / -height; else if (height == 0) height = Math.min(cli.width,cli.height); else height = height * Math.min(cli.width,cli.height) / 100;} else
if ((PARENTSIZEMAX-RANGE) <= height && height <= (PARENTSIZEMAX+RANGE)) {height -= PARENTSIZEMAX; if (height < 0) height = Math.max(cli.width,cli.height) / -height; else if (height == 0) height = Math.max(cli.width,cli.height); else height = height * Math.max(cli.width,cli.height) / 100;}
// x
if (x < MAXABSOLUTECOORD) {} else
if ((AFTER -RANGE) <= x && x <= (AFTER +RANGE) && parent != null) x += parent.lastX + parent.lastW -AFTER; else // guich@450_36: test parent only after testing the relative type
if ((LEFT -RANGE) <= x && x <= (LEFT +RANGE)) x += cli.x -LEFT; else
if ((BEFORE -RANGE) <= x && x <= (BEFORE +RANGE) && parent != null) x += parent.lastX - width -BEFORE; else
if ((SAME -RANGE) <= x && x <= (SAME +RANGE) && parent != null) x += parent.lastX -SAME; else
if ((RIGHT -RANGE) <= x && x <= (RIGHT +RANGE)) x += cli.x + cli.width-width -RIGHT; else
if ((CENTER -RANGE) <= x && x <= (CENTER +RANGE)) x += cli.x + ((cli.width-width) >> 1) -CENTER; else
if ((CENTER_OF-RANGE) <= x && x <= (CENTER_OF+RANGE)) x += parent.lastX + (parent.lastW - width)/2 -CENTER_OF; else // guich@tc110_88
if ((RIGHT_OF-RANGE) <= x && x <= (RIGHT_OF+RANGE)) x += parent.lastX + (parent.lastW - width) -RIGHT_OF; // guich@tc110_97
// y
if (y <= MAXABSOLUTECOORD) {} else
if ((AFTER -RANGE) <= y && y <= (AFTER +RANGE) && parent != null) y += parent.lastY + parent.lastH -AFTER; else // guich@450_36: test parent only after testing the relative type
if ((BEFORE -RANGE) <= y && y <= (BEFORE +RANGE) && parent != null) y += parent.lastY - height -BEFORE; else
if ((SAME -RANGE) <= y && y <= (SAME +RANGE) && parent != null) y += parent.lastY -SAME; else
if ((TOP -RANGE) <= y && y <= (TOP +RANGE)) y += cli.y -TOP; else
if ((BOTTOM -RANGE) <= y && y <= (BOTTOM +RANGE)) y += cli.y + cli.height-height -BOTTOM; else
if ((CENTER -RANGE) <= y && y <= (CENTER +RANGE)) y += cli.y + ((cli.height-height) >> 1) -CENTER; else
if ((CENTER_OF-RANGE) <= y && y <= (CENTER_OF+RANGE)) y += parent.lastY + (parent.lastH - height)/2 -CENTER_OF; else // guich@tc110_88
if ((BOTTOM_OF-RANGE) <= y && y <= (BOTTOM_OF+RANGE)) y += parent.lastY + (parent.lastH - height) -BOTTOM_OF; // guich@tc110_97
// width that depends on x
if (width > MAXABSOLUTECOORD)
{
if ((FILL-RANGE) <= width && width <= (FILL+RANGE)) width += cli.width - x + cli.x -FILL; else
if ((FIT -RANGE) <= width && width <= (FIT +RANGE) && parent != null) width += lpx - x -FIT;
tempW = width;
}
// height that depends on y
if (height > MAXABSOLUTECOORD)
{
if ((FILL-RANGE) <= height && height <= (FILL+RANGE)) height += cli.height - y + cli.y -FILL; else
if ((FIT -RANGE) <= height && height <= (FIT +RANGE) && parent != null) height += lpy - y -FIT;
}
}
// quick check to see if all bounds were set.
if (Settings.onJavaSE) // guich@450_36: do these checks only if running on desktop
{
if (cli.width == 0 || cli.height == 0)
throw new RuntimeException(parent+" must have its bounds set before calling "+this+".setRect"); // guich@300_28
else
if (x+y+width+height > RANGE)
{
x=y=0;
width=height=10;
throw new RuntimeException("To use AFTER/BEFORE/SAME you must add first the control "+toString()+" to the parent container.");
} else
if (x+y < -RANGE) // guich@300_27
{
if (x < -RANGE)
throw new RuntimeException("You can't use FILL with BEFORE, CENTER or RIGHT for control "+toString());
else
throw new RuntimeException("You can't use FILL with BEFORE, CENTER or BOTTOM for control "+toString());
}
if (height < 0 || width < 0)
throw new RuntimeException("Invalid resulting values in width,height for control "+toString()+": "+width+","+height);
}
}
this.x = x;
this.y = y;
this.width = width;
this.height = height;
if (parent != null)
updateTemporary();
Window.needsPaint = true;
onBoundsChanged(screenChanged);
if (asContainer != null)
{
if (!asContainer.started) // guich@340_15
{
asContainer.started = true;
asContainer.initUI();
asContainer.finishedStart = true;
}
asContainer.lastScreenWidth = Settings.screenWidth; // save the last screen resolution so we can be repositioned if a rotation occured at a time that the container was not on screen
}
}
/** Resets the original points that are set by the first setRect, so if you call setRect again, the
* old positions are replaced by the new ones. The set positions are used when a rotation occurs.
* @since TotalCross 1.25
*/
public void resetSetPositions()
{
setX = SETX_NOT_SET;
}
/** Used internally. */
public void setSet(int x, int y)
{
setX = x;
setY = y;
}
protected void updateTemporary() // guich@tc114_68
{
if (parent != null)
{
if (x != WILL_RESIZE) parent.lastX = x; // guich@200b4_100: save last positions
if (y != WILL_RESIZE) parent.lastY = y;
if (width != WILL_RESIZE) parent.lastW = width;
if (height != WILL_RESIZE) parent.lastH = height;
}
}
/** Returns the current size (width,height) of this control */
public Coord getSize()
{
return new Coord(width,height);
}
/** Returns the current position (x,y) of this control */
public Coord getPos()
{
return new Coord(x,y);
}
/** Shows or "hides" this control. Note that it remains attached to its container.
* Calls repaint. */
public void setVisible(boolean visible)
{
if (visible != this.visible)
{
this.visible = visible;
if (!visible)
{
Window w = getParentWindow();
if (w != null)
for (Control c = w._focus; c != null; c = c.parent) // kmeehl@tc100: make sure the focused control is not hidden now
if (!c.visible)
{
w.removeFocus();
break;
}
}
Window.needsPaint = true;
}
}
/** Returns true if this control is visible, false otherwise */
public boolean isVisible()
{
return visible;
}
/**
* Returns a copy of the control's rectangle, relative to its parent. A control's rectangle
* defines its location and size.
*/
public Rect getRect()
{
return new Rect(this.x, this.y, this.width, this.height);
}
/** Returns the absolute coordinates of this control relative to the MainWindow. */
public Rect getAbsoluteRect() // guich@102: changed name from getRelativeRect to getAbsoluteRect.
{
Rect r = getRect();
Control c = parent;
while (c != null)
{
r.x += c.x;
r.y += c.y;
c = c.parent;
}
return r;
}
/** Returns the control's parent Window or null if there's no parent
* (eg: the control still not added to any container). If this control is a window, will return itself.
*/
public Window getParentWindow()
{
if (asWindow != null) // guich@tc100b5_14: the parent window of a window should return itself
return asWindow;
Container c = parent;
// guich@200final_17: using again the old algorithm
while (c != null && c.asWindow == null) // guich@320_2: changed instanceof to .asWindow
c = c.parent;
return c != null ? (Window)c : null;
}
/** Returns the control's parent container. */
public Container getParent()
{
return parent;
}
/** Returns the next child in the parent's list of controls. */
public Control getNext()
{
return next;
}
/** Returns the previous child in the parent's list of controls. */
public Control getPrev()
{
return prev;
}
/**
* Returns true if the given x and y coordinate in the parent's
* coordinate system is contained within this control.
*/
public boolean contains(int x, int y)
{
return this.x <= x && x < (this.x+this.width) && this.y <= y && y < (this.y+this.height);
}
/** Marks all controls in the screen for repaint.
* Important note: when you call repaint, a flag is set indicating that the screen
* must be repainted; then, the next time a event (a keypress, a timer, a pen event)
* occurs, the screen is updated. If you call repaint and the control isn't
* effectively repainted, you can use the Control.repaintNow method.
* <br><br>
* If you want to avoid a method call, you can do
* <pre>
* Window.needsPaint = true;
* </pre>
* @see #repaintNow()
*/
public static void repaint()
{
Window.needsPaint = true;
}
/** Redraws the control immediately. If this control is a Window, the whole window area is
* marked for repaint (useful if you're removing some controls from a container).
* This method affects only this control, while the repaint method affects the whole screen.
*
* If Window.enableUpdateScreen is true, the method returns immediately.
* @since SuperWaba 2.0 beta 4 release 3
* @see #repaint()
*/
public void repaintNow()
{
if (!Window.enableUpdateScreen)
return;
Window w = asWindow != null ? asWindow : getParentWindow();
if (w != null && Window.zStack.indexOf(w,0) >= 0) // guich@560_12: if we're not visible, this is nonsense
{
if (Settings.isOpenGL)
{
Window.needsPaint = true; // make sure the whole area is marked to be repainted
if (MainWindow.isMainThread())
Window.repaintActiveWindows();
else
{
MainWindow.mainWindowInstance.setTimerInterval(1);
Vm.safeSleep(1);
}
}
else
if (asWindow != null) // guich@200b4: if this is a Window, paint everything
{
Window.needsPaint = true; // make sure the whole area is marked to be repainted
asWindow._doPaint(); // doPaint already calls updateScreen
}
else
if (transparentBackground)
parent.repaintNow(); // guich@tc100: for transparent backgrounds we have to force paint everything
else
{
Graphics g = refreshGraphics(gfx, 0, null,0,0);
if (g != null)
{
onPaint(g);
if (asContainer != null) // else, if this is a Container, be sure to repaint all its children
asContainer.paintChildren();
safeUpdateScreen();
}
}
}
}
/** Sets the given Point to the absolute coordinate relative to the origin Window.
* @since SuperWaba 5.5
*/
public void translateFromOrigin(Coord z) // guich@550_31
{
z.x = z.y = 0;
Control c = this;
while (c != null)
{
if (c.asWindow != null)
break;
z.x += c.x;
z.y += c.y;
c = c.parent;
}
}
/**
* Returns a Graphics object which can be used to draw in the control.
* This method updates the single Graphics object with the
* current control font and bounds.
* It sets a clipping rectangle on the graphics, clipping it against all parent areas.
*/
public Graphics getGraphics()
{
return refreshGraphics(gfx, 0, null,0,0);
}
Graphics refreshGraphics(Graphics g, int expand, Control topParent, int tx0, int ty0)
{
if (asWindow == null && parent == null) // if we're not added to a Container, return null (windows are never added to a Container!)
return null;
int sw = this.width;
int sh = this.height;
int sx = this.x, sy = this.y, cx, cy, delta, tx = sx, ty = sy;
if (this != topParent)
for (Container c = parent; c != topParent; c = c.parent)
{
cx = c.x; cy = c.y;
tx += cx; ty += cy;
sx += cx; sy += cy;
// before?
delta = sx - cx;
if (delta < 0) {sw += delta; sx = cx;}
delta = sy - cy;
if (delta < 0) {sh += delta; sy = cy;}
// after?
delta = (sx+sw)-(cx+c.width);
if (delta > 0) sw -= delta;
delta = (sy+sh)-(cy+c.height);
if (delta > 0) sh -= delta;
}
g.refresh(sx+tx0-expand,sy-expand+ty0,sw+expand+expand,sh+expand+expand, tx+tx0, ty+ty0, font);
return g;
}
/**
* Posts an event. The event pass will be posted to this control
* and all the parent controls of this control (all the containers
* this control is within).
* @see totalcross.ui.event.Event
*/
public void postEvent(Event event)
{
// we can now go back to highlighting mode, the control had being pressed
if (Settings.keyboardFocusTraversable && event.type == ControlEvent.PRESSED && !(event.target instanceof Edit)) // guich@tc113_1: not on PRESSED of Edits
(asContainer!=null?asContainer:parent.asContainer).setHighlighting();
// don't dispatch events when disabled except TIMER events or (fingertouch and pen events)
if ((!isEnabled() && (!Settings.fingerTouch || event.type == PenEvent.PEN_DOWN)) || (!eventsEnabled && event.type != TimerEvent.TRIGGERED))
return;
boolean dragTargetCalled = false;
if (dragTarget != null) // improve drag performance by sending the event directly to the drag handler control
{
if (event.type == PenEvent.PEN_DOWN)
dragTarget = null;
else if (event.type == PenEvent.PEN_DRAG || event.type == PenEvent.PEN_UP)
{
dragTarget._onEvent(event);
dragTargetCalled = true;
}
}
if (!event.consumed)
{
boolean eventTargetCalled = false;
for (Control c = this; c != null; c = c.parent)
{
if (c == event.target)
eventTargetCalled = true;
if (!dragTargetCalled || c != dragTarget)
{
Control cp = c.parent;
c._onEvent(event);
if (event.consumed && event.type == PenEvent.PEN_DRAG)
dragTarget = c;
if (event.consumed || cp != c.parent) // guich@200b4_132: if the control consumed the event, stop propagation. - guich@200b4: has the parent changed? if yes, dont broadcast the event anymore.
break;
}
}
if (!eventTargetCalled && event.target instanceof Control) // guich@tc110_52: call any listeners of the target control - guich@tc112_3: if not yet called
{
Control target = (Control)event.target;
if (target.listeners != null)
target.callEventListeners(event);
}
}
if (event.type == PenEvent.PEN_UP) // release drag handler at PEN_UP
dragTarget = null;
event.consumed = false; // set to false again bc some controls reuse event objects
}
/** Sets if this control can or not accept events.
* It changes the appearance of many controls to indicate they are disabled.
*/
public void setEnabled(boolean enabled)
{
internalSetEnabled(enabled, true);
}
/** For internal use only. Used by derived controls to set the enabled flag. */
public boolean internalSetEnabled(boolean enabled, boolean post)
{
if (enabled != this.enabled)
{
this.enabled = enabled;
onColorsChanged(false);
if (post)
post();
Window.needsPaint = true; // now the controls have different l&f for disabled states
return true;
}
return false;
}
/** Posts the enable state change event. */
public void post()
{
esce.update(this);
postEvent(esce);
}
/** Returns if this control can or not accept events */
public boolean isEnabled()
{
return this.enabled && !keepDisabled;
}
/**
* Called to process key, pen, control and other posted events.
* @param event the event to process
* @see totalcross.ui.event.Event
* @see totalcross.ui.event.KeyEvent
* @see totalcross.ui.event.PenEvent
*/
public void onEvent(Event event)
{
}
/**
* Called to draw the control. When this method is called, the graphics
* object passed has been translated into the coordinate system of the
* control and the area behind the control has
* already been painted.
* @param g the graphics object for drawing
* @see totalcross.ui.gfx.Graphics
*/
public void onPaint(Graphics g)
{
}
/** Called after a setRect.
* @param screenChanged If the bounds were changed due to a screen change (rotation, collapse)
*/
protected void onBoundsChanged(boolean screenChanged)
{
}
/** Called after a setEnabled, setForeColor and setBackColor and when a control has
* been added to a Container. If colorsChanged
* is true, it was called from setForeColor/setBackColor/Container.add; otherwise, it was
* called from setEnabled
*/
protected void onColorsChanged(boolean colorsChanged) // guich@200b4_152
{
}
/** Called after the window has finished a paint. Only called to the focused control and the parent's window. */
protected void onWindowPaintFinished()
{
}
/** Called after a setFont */
protected void onFontChanged() // guich@200b4_153
{
}
/** Set the background and foreground colors at once.
* Calling this method is faster than calling setBackColor and setForeColor separately.
*/
public void setBackForeColors(int back, int fore) // guich@200b4_170
{
this.backColor = back;
this.foreColor = fore;
onColorsChanged(true);
}
/** Set the foreground color of this control.
@since SuperWaba 2.0 */
public void setForeColor(int c)
{
this.foreColor = c;
onColorsChanged(true);
}
/** Set the background color of this control.
@since SuperWaba 2.0 */
public void setBackColor(int c)
{
this.backColor = c;
onColorsChanged(true);
}
/** Get the desired foreground color of this control.
@since SuperWaba 2.0 */
public int getForeColor()
{
return isEnabled()?foreColor:Color.brighter(foreColor);
}
/** Get the desired background color of this control.
@since SuperWaba 2.0 */
public int getBackColor()
{
// note: if were in a white back color, return the color without darking
return (isEnabled() || parent == null)?backColor:Color.darker(backColor);
}
/** Return true if the parent of this Control is added to somewhere.
* Some containers, like the TabPanel, has n child containers, but only one is added
* at a time. With this method, you can discover if your container is the one being shown.
*/
public boolean isDisplayed()
{
Control c = this;
while (c.asWindow == null)
{
c = c.parent;
if (c == null)
return false; // not added yet
}
return c.asWindow.popped || (c instanceof MainWindow);
}
/** Sets the focus to this control. Note that in penless devices its also needed
* to set <code>isHighlighting = false</code>. */
public void requestFocus()
{
Window w = getParentWindow();
if (w != null) w.setFocus(this);
// does not work - test on UIGadgets for example - isHighlighting = false; // guich@570_39: if the user decided to place the focus here, we should disable highlighting traversals
}
/** Sets this control to be focusless. If this control is a container, sets
* all its children to be focusless too.
* A focusless control can receive and dispatch events, but cannot receive focus.
* Here's an example of how to use it to create a keypad:
* <pre>
// class fields
private PushButtonGroup numericPad;
private KeyEvent ke = new KeyEvent();
// in the initUI method:
String []numerics = {"1","2","3","4","5","6","7","8","9","0",".","-"};
add(numericPad=new PushButtonGroup(numerics, false, -1, -1, 6, 4, true,
PushButtonGroup.BUTTON), RIGHT-2, TOP+2);
numericPad.setFocusLess(true);
// in the onEvent method
Control focus;
if (e.target == numericPad && (focus=getParentWindow().getFocus()) instanceof Edit)
{
String s = numericPad.getSelectedCaption();
if (s != null)
{
ke.key = s.charAt(0);
ke.target = focus;
focus.onEvent(ke);
}
}
* </pre>
*/
public void setFocusLess(boolean on)
{
Container p = parent;
if (p != null)
if (on)
p.tabOrder.removeElement(this);
else
p.tabOrder.addElement(this);
this.focusLess = on;
if (asContainer != null)
{
for (Control child = asContainer.children; child != null; child = child.next)
if (child.asContainer != null)
child.setFocusLess(on);
else
child.focusLess = on;
}
}
private static boolean uiStyleAlreadyChanged;
/** Internal use only */
public static void uiStyleChanged()
{
if (!uiStyleAlreadyChanged)
{
uiFlat = Settings.uiStyle == Settings.Flat;
uiAndroid = Settings.uiStyle == Settings.Android;
uiVista = Settings.uiStyle == Settings.Vista || uiAndroid;
uiStyleAlreadyChanged = true;
}
else throw new RuntimeException("The user interface style can be changed only once, in the MainWindow's constructor.");
}
/** Returns the next/previous control that can be highlighted */
private Control getNextHighlighted(Container p, boolean forward)
{
Vector v = p.tabOrder;
int idx = v.indexOf(this);
int last = v.size()-1;
if (last == -1)
return null;
if (p == this) // changing for the first time into this container?
idx = forward?-1:last+1; // return (Control)v.items[forward?0:last]; // return the first/last control - guich@573_22: can't just go to first or last - they may be disabled!
else
if (idx == -1) // control not found?
return null;
int limit = forward ? last : 0;
int inc = forward ? 1 :-1;
while (true)
{
if (idx == limit) // reached the limits?
return null;
idx += inc;
Control c = (Control)v.items[idx];
if (c.visible && c.isEnabled() && !c.focusLess) // kmeehl@tc100: do not traverse through focusless controls
if (c.focusTraversable) // guich@580_56: added focusTraversable test.
return c;
else
if (c.asContainer != null) // kmeehl@tc100: look through child containers for the next focusable control
{
c = c.getNextHighlighted(c.asContainer, forward);
if (c != null)
return c;
}
}
}
/** Transfers the focus to the next or previous control.
* @since SuperWaba 5.5
*/
public void changeHighlighted(Container p, boolean forward)
{
Control c = getNextHighlighted(p, forward); // allow the control to find the next control to highlight
if (c != null)
{
Window w = getParentWindow();
if (w != null) w.setHighlighted(c);
}
else
if (parent != null)
if (this == p)
changeHighlighted(parent, forward); // look in parent for the next focusable control
else
parent.changeHighlighted(parent.parent != null ? parent.parent : parent, forward); // return to the parent of the parent and continues - p.p!=null: if our parent is a window (E.G. UIGadgets' scrollbar), his parent is null; so we restart from the window
else
if (p != this.asWindow) // fdie@ prevent infinite loop
changeHighlighted(this.asWindow, forward); // we're the window - restart everything
}
/** Placeholder for the clear method. The class that inherits control is responsible for cleaning it up. */
public void clear()
{
}
/**
* This method causes the immediate screen update. The screen
* is saved in a buffer and, when this method is called,
* the buffer is transfered to the screen, using the
* nextTransitionEffect set.
* NOTE: for a thread-safe version, use safeUpdateScreen
* @see #safeUpdateScreen()
* @see Container#nextTransitionEffect
* @since SuperWaba 5.0
*/
public static void updateScreen()
{
if (enableUpdateScreen)
{
totalcross.Launcher.instance.updateScreen();
Graphics.needsUpdate = false;
}
}
/** This method causes the screen's update. If called at the main thread,
* the screen is updated immediatly. If not, the updated is schedulled to occur as
* soon as possible.
* @see #updateScreen()
* @since TotalCross 3.1
*/
public static void safeUpdateScreen()
{
if (MainWindow.isMainThread())
updateScreen();
else
if (!callingUpdScr)
{
callingUpdScr = true;
MainWindow.getMainWindow().runOnMainThread(new Runnable()
{
public void run()
{
updateScreen();
Vm.sleep(1);
callingUpdScr = false;
}
});
}
}
native public static void updateScreen4D();
/** Returns the control's width. */
public int getWidth()
{
return width;
}
/** Returns the control's height. */
public int getHeight()
{
return height;
}
/** Returns the control's x position. */
public int getX()
{
return x;
}
/** Returns the control's y position. */
public int getY()
{
return y;
}
/** Returns x+width-1
* @since TotalCross 1.14
*/
public int getX2()
{
return x+width-1;
}
/** Returns y+height-1
* @since TotalCross 1.14
*/
public int getY2()
{
return y+height-1;
}
/** Repositions this control, and dives into other controls if this is a container and recursive is true. */
protected void reposition(boolean recursive)
{
if (setX != SETX_NOT_SET) // bounds already set?
{
if (repositionAllowed)
{
Font current = this.font;
font = setFont; fm = font.fm; fmH = font.fm.height;
setRect(setX, setY, setW, setH, setRel, true);
font = current; fm = font.fm; fmH = font.fm.height;
refreshGraphics(gfx, 0, null,0,0);
}
if (recursive && asContainer != null)
{
asContainer.lastX = -999999; asContainer.lastY = asContainer.lastW = asContainer.lastH = 0;
for (Control child = asContainer.children; child != null; child = child.next)
child.reposition();
}
}
if (asContainer != null) asContainer.lastScreenWidth = Settings.screenWidth; // save the last screen resolution so we can be repositioned if a rotation occured at a time that the container was not on screen
}
/** Reposition this control, calling again setRect with the original parameters. */
public void reposition()
{
reposition(true);
}
/** Calls the event listeners and the onEvent method for this control. */
public void _onEvent(Event e)
{
if (!e.consumed && onEventFirst)
onEvent(e);
if (!e.consumed && listeners != null && (callListenersOnAllTargets || e.target == this))
callEventListeners(e);
if (!e.consumed && !onEventFirst)
onEvent(e);
}
private void addListener(int type, Object listener)
{
if (listeners == null) listeners = new Vector(1);
Listener l = new Listener(this, type, listener);
if (listeners.indexOf(l) == -1)
listeners.addElement(l);
}
/** Removes the given listener from the list of listeners of this control.
*/
private void removeListener(int type, Object listener)
{
if (listeners != null && listeners.removeElement(new Listener(this, type, listener)) && listeners.size() == 0)
listeners = null;
}
/** Adds a listener for Pen events.
* @see totalcross.ui.event.PenListener
*/
public void addPenListener(PenListener listener)
{
addListener(Listener.PEN, listener);
}
/** Adds a listener for MultiTouch events.
* @see totalcross.ui.event.MultiTouchListener
*/
public void addMultiTouchListener(MultiTouchListener listener)
{
addListener(Listener.MULTITOUCH, listener);
}
/** Adds a listener for mouse events.
* @see totalcross.ui.event.MouseListener
*/
public void addMouseListener(MouseListener listener)
{
addListener(Listener.MOUSE, listener);
}
/** Adds a listener for Window events.
* @see totalcross.ui.event.WindowListener
*/
public void addWindowListener(WindowListener listener)
{
addListener(Listener.WINDOW, listener);
}
/** Adds a listener for Grid events.
* @see totalcross.ui.event.GridListener
*/
public void addGridListener(GridListener listener)
{
addListener(Listener.GRID, listener);
}
/** Adds a listener for ListContainer events.
* @see totalcross.ui.event.ListContainerListener
*/
public void addListContainerListener(ListContainerListener listener)
{
addListener(Listener.LISTCONTAINER, listener);
}
/** Adds a listener for Focus events.
* @see totalcross.ui.event.FocusListener
*/
public void addFocusListener(FocusListener listener)
{
addListener(Listener.FOCUS, listener);
}
/** Adds a listener for Press events.
* @see totalcross.ui.event.PressListener
*/
public void addPressListener(PressListener listener)
{
addListener(Listener.PRESS, listener);
}
/** Adds a listener for Timer events.
* @see totalcross.ui.event.TimerListener
*/
public void addTimerListener(TimerListener listener)
{
addListener(Listener.TIMER, listener);
}
/** Adds a listener for Key events.
* @see totalcross.ui.event.KeyListener
*/
public void addKeyListener(KeyListener listener)
{
addListener(Listener.KEY, listener);
}
/** Adds a listener for Highlight events.
* @see totalcross.ui.event.HighlightListener
*/
public void addHighlightListener(HighlightListener listener)
{
addListener(Listener.HIGHLIGHT, listener);
}
/** Adds a listener for enabled state changes.
*/
public void addEnabledStateListener(EnabledStateChangeListener listener)
{
addListener(Listener.ENABLED, listener);
}
/** Removes a listener for enabled state changes.
* @since TotalCross 1.67
*/
public void removeEnabledStateListener(EnabledStateChangeListener listener)
{
removeListener(Listener.ENABLED, listener);
}
/** Removes a listener for MultiTouch events.
* @see totalcross.ui.event.MultiTouchListener
* @since TotalCross 1.22
*/
public void removeMultiTouchListener(MultiTouchListener listener)
{
removeListener(Listener.MULTITOUCH, listener);
}
/** Removes a listener for Pen events.
* @see totalcross.ui.event.PenListener
* @since TotalCross 1.22
*/
public void removePenListener(PenListener listener)
{
removeListener(Listener.PEN, listener);
}
/** Removes a listener for mouse events.
* @see totalcross.ui.event.MouseListener
* @since TotalCross 1.22
*/
public void removeMouseListener(MouseListener listener)
{
removeListener(Listener.MOUSE, listener);
}
/** Removes a listener for Window events.
* @see totalcross.ui.event.WindowListener
* @since TotalCross 1.22
*/
public void removeWindowListener(WindowListener listener)
{
removeListener(Listener.WINDOW, listener);
}
/** Removes a listener for Grid events.
* @see totalcross.ui.event.GridListener
* @since TotalCross 1.22
*/
public void removeGridListener(GridListener listener)
{
removeListener(Listener.GRID, listener);
}
/** Removes a listener for ListContainer events.
* @see totalcross.ui.event.ListContainerListener
* @since TotalCross 1.22
*/
public void removeListContainerListener(ListContainerListener listener)
{
removeListener(Listener.LISTCONTAINER, listener);
}
/** Removes a listener for Focus events.
* @see totalcross.ui.event.FocusListener
* @since TotalCross 1.22
*/
public void removeFocusListener(FocusListener listener)
{
removeListener(Listener.FOCUS, listener);
}
/** Removes a listener for Press events.
* @see totalcross.ui.event.PressListener
* @since TotalCross 1.22
*/
public void removePressListener(PressListener listener)
{
removeListener(Listener.PRESS, listener);
}
/** Removes a listener for Timer events.
* @see totalcross.ui.event.TimerListener
* @since TotalCross 1.22
*/
public void removeTimerListener(TimerListener listener)
{
removeListener(Listener.TIMER, listener);
}
/** Removes a listener for Key events.
* @see totalcross.ui.event.KeyListener
* @since TotalCross 1.22
*/
public void removeKeyListener(KeyListener listener)
{
removeListener(Listener.KEY, listener);
}
/** Removes a listener for Highlight events.
* @see totalcross.ui.event.HighlightListener
* @since TotalCross 1.22
*/
public void removeHighlightListener(HighlightListener listener)
{
removeListener(Listener.HIGHLIGHT, listener);
}
private void callEventListeners(Event e)
{
// although this code is not much eficient, the number of listeners for a single control will be only one, most of the times.
for (int i = 0; listeners != null && i < listeners.size() && !e.consumed; i++) // size may change during loop
try
{
Listener l = (Listener)listeners.items[i];
if (e.target == l.target || (callListenersOnAllTargets && (e instanceof KeyEvent || e instanceof PenEvent))) // guich@tc152: fixed problem of a PRESS on a Button inside a TabbedContainer calling the press listener of the TabbedContainer.
switch (e.type)
{
case MouseEvent.MOUSE_MOVE: if (l.type == Listener.MOUSE) ((MouseListener )l.listener).mouseMove((MouseEvent)e); break;
case MouseEvent.MOUSE_IN: if (l.type == Listener.MOUSE) ((MouseListener )l.listener).mouseIn((MouseEvent)e); break;
case MouseEvent.MOUSE_OUT: if (l.type == Listener.MOUSE) ((MouseListener )l.listener).mouseOut((MouseEvent)e); break;
case MouseEvent.MOUSE_WHEEL: if (l.type == Listener.MOUSE) ((MouseListener )l.listener).mouseWheel((MouseEvent)e); break;
case MultiTouchEvent.SCALE: if (l.type == Listener.MULTITOUCH)((MultiTouchListener)l.listener).scale((MultiTouchEvent)e); break;
case PenEvent.PEN_DOWN: if (l.type == Listener.PEN) ((PenListener )l.listener).penDown((PenEvent)e); break;
case PenEvent.PEN_UP: if (l.type == Listener.PEN) ((PenListener )l.listener).penUp((PenEvent)e); break;
case PenEvent.PEN_DRAG: if (l.type == Listener.PEN) ((PenListener )l.listener).penDrag((DragEvent)e); break;
case PenEvent.PEN_DRAG_START: if (l.type == Listener.PEN) ((PenListener )l.listener).penDragStart((DragEvent)e); break;
case PenEvent.PEN_DRAG_END: if (l.type == Listener.PEN) ((PenListener )l.listener).penDragEnd((DragEvent)e); break;
case ControlEvent.PRESSED: if (l.type == Listener.PRESS) ((PressListener )l.listener).controlPressed((ControlEvent)e); break;
case ControlEvent.FOCUS_IN: if (l.type == Listener.FOCUS) ((FocusListener )l.listener).focusIn((ControlEvent)e); break;
case ControlEvent.FOCUS_OUT: if (l.type == Listener.FOCUS) ((FocusListener )l.listener).focusOut((ControlEvent)e); break;
case ControlEvent.HIGHLIGHT_IN: if (l.type == Listener.HIGHLIGHT) ((HighlightListener)l.listener).highlightIn((ControlEvent)e); break;
case ControlEvent.HIGHLIGHT_OUT: if (l.type == Listener.HIGHLIGHT) ((HighlightListener)l.listener).highlightOut((ControlEvent)e); break;
case ControlEvent.WINDOW_CLOSED: if (l.type == Listener.WINDOW) ((WindowListener )l.listener).windowClosed((ControlEvent)e); break;
case GridEvent.SELECTED_EVENT: if (l.type == Listener.GRID) ((GridListener )l.listener).gridSelected((GridEvent)e); break;
case GridEvent.CHECK_CHANGED_EVENT:if (l.type == Listener.GRID) ((GridListener )l.listener).gridCheckChanged((GridEvent)e); break;
case GridEvent.TEXT_CHANGED_EVENT: if (l.type == Listener.GRID) ((GridListener )l.listener).gridTextChanged((GridEvent)e); break;
case TimerEvent.TRIGGERED: if (l.type == Listener.TIMER) ((TimerListener )l.listener).timerTriggered((TimerEvent)e); break;
case KeyEvent.KEY_PRESS: if (l.type == Listener.KEY) ((KeyListener )l.listener).keyPressed((KeyEvent)e); break;
case KeyEvent.ACTION_KEY_PRESS: if (l.type == Listener.KEY) ((KeyListener )l.listener).actionkeyPressed((KeyEvent)e); break;
case KeyEvent.SPECIAL_KEY_PRESS: if (l.type == Listener.KEY) ((KeyListener )l.listener).specialkeyPressed((KeyEvent)e); break;
case ListContainerEvent.ITEM_SELECTED_EVENT: if (l.type == Listener.LISTCONTAINER) ((ListContainerListener)l.listener).itemSelected((ListContainerEvent)e); break;
case ListContainerEvent.LEFT_IMAGE_CLICKED_EVENT: if (l.type == Listener.LISTCONTAINER) ((ListContainerListener)l.listener).leftImageClicked((ListContainerEvent)e); break;
case ListContainerEvent.RIGHT_IMAGE_CLICKED_EVENT: if (l.type == Listener.LISTCONTAINER) ((ListContainerListener)l.listener).rightImageClicked((ListContainerEvent)e); break;
case EnabledStateChangeEvent.ENABLED_STATE_CHANGE: if (l.type == Listener.ENABLED) ((EnabledStateChangeListener)l.listener).enabledStateChange((EnabledStateChangeEvent)e); break;
}
}
catch (ClassCastException ee) // prevent totalcross.ui.event.PenEvent is not compatible with totalcross.ui.event.DragEvent
{
if (Settings.onJavaSE) throw ee;
}
}
/**
* Used by the main event loop to give the currently focused control an opportunity to act directly on
* the KeyEvent.
* @param ke The KeyEvent to be processed
* @return The control that should get focus as a result of this KeyEvent. Null if this control did not
* handle the KeyEvent.
* @see totalcross.sys.Settings#geographicalFocus
*/
public Control handleGeographicalFocusChangeKeys(KeyEvent ke) // kmeehl@tc100
{
return null;
}
/** Returns the event listeners array.
* Note that each element is an instance of Control.Listener.
* @see Listener
*/
public Vector getEventListeners()
{
return listeners;
}
/** Returns true of this control is visible and inside these bounds
* @since TotalCross 1.15
*/
public boolean isVisibleAndInside(int x0, int y0, int xf, int yf) // guich@tc115_40
{
return this.visible && this.y < yf && (this.y+this.height) > y0 && this.x < xf && (this.x+this.width) > x0; // guich@200: ignore hidden controls - note: a window added to a container may not be painted correctly
}
boolean isVisibleAndInside(int y0, int yf) // guich@tc115_40
{
return this.visible && this.y < yf && (this.y+this.height) > y0; // guich@200: ignore hidden controls - note: a window added to a container may not be painted correctly
}
public int getGap(int gap)
{
return Settings.uiAdjustmentsBasedOnFontHeight && uiAdjustmentsBasedOnFontHeightIsSupported ? gap * fmH / 100 : gap;
}
/**
* Send this control to the top of the parent's.
* @since TotalCross 1.3
*/
public void bringToFront()
{
if (parent != null && parent.tail != this)
{
if (parent.children == this)
parent.children = this.next;
if (this.prev != null)
this.prev.next = this.next;
if (this.next != null)
this.next.prev = this.prev;
this.prev = parent.tail;
this.next = null;
parent.tail.next = this;
parent.tail = this;
Window.needsPaint = true;
}
}
/**
* Send this control to the last place of the parent's.
* @since TotalCross 1.3
*/
public void sendToBack()
{
if (parent != null && parent.children != this)
{
if (parent.tail == this)
parent.tail = this.prev;
if (this.prev != null)
this.prev.next = this.next;
if (this.next != null)
this.next.prev = this.prev;
this.next = parent.children;
this.prev = null;
parent.children.prev = this;
parent.children = this;
Window.needsPaint = true;
}
}
/** Returns true if the parent of this control is a Scrollable and had scrolled since the last
* pen down.
*
* A scroll occurs before a flick is started.
* @since TotalCross 1.3
*/
public boolean hadParentScrolled()
{
for (Container c = parent; c != null; c = c.parent)
if (c instanceof Scrollable && ((Scrollable)c).wasScrolled())
return true;
return false;
}
/** Returns true if this is a MultiEdit or an Edit that has a standard keyboard.
*/
protected boolean willOpenKeyboard()
{
return false;
}
/** Returns if this event should be handled as an action. */
protected boolean isActionEvent(Event event)
{
return (Settings.fingerTouch && event.type == PenEvent.PEN_UP && !hadParentScrolled()) ||
(!Settings.fingerTouch && event.type == PenEvent.PEN_DOWN);
}
/** Returns true if this control is added to the given container at some higher level.
* @since TotalCross 1.53
*/
public boolean isChildOf(Container p)
{
for (Control c = this; c != null; c = c.parent)
if (c == p)
return true;
return false;
}
/** Returns true of the parent window is the top most one.
* @since TotalCross 1.66
*/
public boolean isTopMost()
{
Window w = getParentWindow();
return w != null && w == Window.topMost;
}
/** Returns true if this control is obscured by the topmost window.
* Note: parentWindow is retrieved with getParentWindow.
* @since TotalCross 2.1
*/
public boolean isObscured(Window parentWindow)
{
if (parentWindow == Window.topMost)
return false;
int cx1 = Window.topMost.x, cy1 = Window.topMost.y, cx2 = cx1 + Window.topMost.width, cy2 = cy1 + Window.topMost.height;
int x1 = this.x, y1 = this.y, x2,y2;
for (Control c = parent; c != null; c = c.parent)
{
x1 += c.x;
y1 += c.y;
}
x2 = x1 + this.width; y2 = y1 + this.height;
return x1 >= cx1 && x2 < cx2 && y1 >= cy1 && y2 < cy2;
}
/** Called by code that runs on threads to safely repaint now.
* @since TotalCross 3.1
*/
protected void safeRepaintNow()
{
if (Settings.isOpenGL || MainWindow.isMainThread())
repaintNow();
else
if (!callingRepNow)
{
Window.needsPaint = true;
callingRepNow = true;
MainWindow.getMainWindow().runOnMainThread(new Runnable()
{
public void run()
{
repaintNow();
callingRepNow = false;
}
});
}
}
// for internal use only. Used by Tree
public void intXYWH(int x, int y, int w, int h)
{
this.x = x;
this.y = y;
if (w != 0) this.width = w;
if (h != 0) this.height = h;
}
}